cocoapods-dykit 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pod/command.rb +2 -0
- data/lib/pod/command/dyinstall.rb +51 -0
- data/lib/pod/command/dyupdate.rb +106 -0
- data/lib/pod/command/fmwk.rb +4 -0
- data/lib/pod/command/lib/dylint.rb +1 -0
- data/lib/pod/gem_version.rb +1 -1
- data/lib/pod/installer.rb +715 -0
- data/lib/pod/installer/analyzer.rb +934 -0
- data/lib/pod/installer/analyzer/analysis_result.rb +57 -0
- data/lib/pod/installer/analyzer/locking_dependency_analyzer.rb +95 -0
- data/lib/pod/installer/analyzer/pod_variant.rb +68 -0
- data/lib/pod/installer/analyzer/pod_variant_set.rb +157 -0
- data/lib/pod/installer/analyzer/podfile_dependency_cache.rb +54 -0
- data/lib/pod/installer/analyzer/sandbox_analyzer.rb +251 -0
- data/lib/pod/installer/analyzer/specs_state.rb +84 -0
- data/lib/pod/installer/analyzer/target_inspection_result.rb +45 -0
- data/lib/pod/installer/analyzer/target_inspector.rb +254 -0
- data/lib/pod/installer/installation_options.rb +158 -0
- data/lib/pod/installer/pod_source_installer.rb +214 -0
- data/lib/pod/installer/pod_source_preparer.rb +77 -0
- data/lib/pod/installer/podfile_validator.rb +139 -0
- data/lib/pod/installer/post_install_hooks_context.rb +107 -0
- data/lib/pod/installer/pre_install_hooks_context.rb +42 -0
- data/lib/pod/installer/source_provider_hooks_context.rb +32 -0
- data/lib/pod/installer/user_project_integrator.rb +253 -0
- data/lib/pod/installer/user_project_integrator/target_integrator.rb +462 -0
- data/lib/pod/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +146 -0
- data/lib/pod/installer/xcode.rb +8 -0
- data/lib/pod/installer/xcode/pods_project_generator.rb +353 -0
- data/lib/pod/installer/xcode/pods_project_generator/aggregate_target_installer.rb +172 -0
- data/lib/pod/installer/xcode/pods_project_generator/file_references_installer.rb +367 -0
- data/lib/pod/installer/xcode/pods_project_generator/pod_target_installer.rb +718 -0
- data/lib/pod/installer/xcode/pods_project_generator/pod_target_integrator.rb +111 -0
- data/lib/pod/installer/xcode/pods_project_generator/target_installer.rb +265 -0
- data/lib/pod/installer/xcode/target_validator.rb +141 -0
- data/lib/pod/resolver.rb +632 -0
- metadata +34 -2
@@ -0,0 +1,57 @@
|
|
1
|
+
module Pod
|
2
|
+
class DyInstaller
|
3
|
+
class Analyzer
|
4
|
+
class AnalysisResult
|
5
|
+
# @return [SpecsState] the states of the Podfile specs.
|
6
|
+
#
|
7
|
+
attr_accessor :podfile_state
|
8
|
+
|
9
|
+
# @return [Hash{TargetDefinition => Array<Specification>}] the
|
10
|
+
# specifications grouped by target.
|
11
|
+
#
|
12
|
+
attr_accessor :specs_by_target
|
13
|
+
|
14
|
+
# @return [Hash{Source => Array<Specification>}] the
|
15
|
+
# specifications grouped by spec repo source.
|
16
|
+
#
|
17
|
+
attr_accessor :specs_by_source
|
18
|
+
|
19
|
+
# @return [Array<Specification>] the specifications of the resolved
|
20
|
+
# version of Pods that should be installed.
|
21
|
+
#
|
22
|
+
attr_accessor :specifications
|
23
|
+
|
24
|
+
# @return [SpecsState] the states of the {Sandbox} respect the resolved
|
25
|
+
# specifications.
|
26
|
+
#
|
27
|
+
attr_accessor :sandbox_state
|
28
|
+
|
29
|
+
# @return [Array<AggregateTarget>] The aggregate targets created for each
|
30
|
+
# {TargetDefinition} from the {Podfile}.
|
31
|
+
#
|
32
|
+
attr_accessor :targets
|
33
|
+
|
34
|
+
# @return [Hash{TargetDefinition => Array<TargetInspectionResult>}] the
|
35
|
+
# results of inspecting the user targets
|
36
|
+
#
|
37
|
+
attr_accessor :target_inspections
|
38
|
+
|
39
|
+
# @return [PodfileDependencyCache] the cache of all dependencies in the
|
40
|
+
# podfile.
|
41
|
+
#
|
42
|
+
attr_accessor :podfile_dependency_cache
|
43
|
+
|
44
|
+
# @return [Hash{String=>Symbol}] A hash representing all the user build
|
45
|
+
# configurations across all integration targets. Each key
|
46
|
+
# corresponds to the name of a configuration and its value to
|
47
|
+
# its type (`:debug` or `:release`).
|
48
|
+
#
|
49
|
+
def all_user_build_configurations
|
50
|
+
targets.reduce({}) do |result, target|
|
51
|
+
result.merge(target.user_build_configurations)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'molinillo/dependency_graph'
|
2
|
+
|
3
|
+
module Pod
|
4
|
+
class DyInstaller
|
5
|
+
class Analyzer
|
6
|
+
# Generates dependencies that require the specific version of the Pods
|
7
|
+
# that haven't changed in the {Lockfile}.
|
8
|
+
module LockingDependencyAnalyzer
|
9
|
+
# Generates dependencies that require the specific version of the Pods
|
10
|
+
# that haven't changed in the {Lockfile}.
|
11
|
+
#
|
12
|
+
# These dependencies are passed to the {Resolver}, unless the installer
|
13
|
+
# is in update mode, to prevent it from upgrading the Pods that weren't
|
14
|
+
# changed in the {Podfile}.
|
15
|
+
#
|
16
|
+
# @param [Lockfile] lockfile the lockfile containing dependency constraints
|
17
|
+
#
|
18
|
+
# @param [Array<String>] pods_to_update
|
19
|
+
# List of pod names which needs to be updated because installer is
|
20
|
+
# in update mode for these pods. Pods in this list and all their recursive dependencies
|
21
|
+
# will not be included in generated dependency graph
|
22
|
+
#
|
23
|
+
# @param [Array<String>] pods_to_unlock
|
24
|
+
# List of pod names whose version constraints will be removed from the generated dependency graph.
|
25
|
+
# Recursive dependencies of the pods won't be affected. This is currently used to force local pods
|
26
|
+
# to be evaluated again whenever checksum of the specification of the local pods changes.
|
27
|
+
#
|
28
|
+
# @return [Molinillo::DependencyGraph<Dependency>] the dependencies
|
29
|
+
# generated by the lockfile that prevent the resolver to update
|
30
|
+
# a Pod.
|
31
|
+
#
|
32
|
+
def self.generate_version_locking_dependencies(lockfile, pods_to_update, pods_to_unlock = [])
|
33
|
+
dependency_graph = Molinillo::DependencyGraph.new
|
34
|
+
|
35
|
+
if lockfile
|
36
|
+
explicit_dependencies = lockfile.dependencies
|
37
|
+
explicit_dependencies.each do |dependency|
|
38
|
+
dependency_graph.add_vertex(dependency.name, dependency, true)
|
39
|
+
end
|
40
|
+
|
41
|
+
pods = lockfile.to_hash['PODS'] || []
|
42
|
+
pods.each do |pod|
|
43
|
+
add_to_dependency_graph(pod, [], dependency_graph, pods_to_unlock)
|
44
|
+
end
|
45
|
+
|
46
|
+
pods_to_update = pods_to_update.flat_map do |u|
|
47
|
+
root_name = Specification.root_name(u).downcase
|
48
|
+
dependency_graph.vertices.each_key.select { |n| Specification.root_name(n).downcase == root_name }
|
49
|
+
end
|
50
|
+
|
51
|
+
pods_to_update.each do |u|
|
52
|
+
dependency_graph.detach_vertex_named(u)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
dependency_graph
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generates a completely 'unlocked' dependency graph.
|
60
|
+
#
|
61
|
+
# @return [Molinillo::DependencyGraph<Dependency>] an empty dependency
|
62
|
+
# graph
|
63
|
+
#
|
64
|
+
def self.unlocked_dependency_graph
|
65
|
+
Molinillo::DependencyGraph.new
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def self.add_child_vertex_to_graph(dependency_string, parents, dependency_graph, pods_to_unlock)
|
71
|
+
dependency = Dependency.from_string(dependency_string)
|
72
|
+
if pods_to_unlock.include?(dependency.root_name)
|
73
|
+
dependency = Dependency.new(dependency.name)
|
74
|
+
end
|
75
|
+
vertex = dependency_graph.add_child_vertex(dependency.name, nil, parents, nil)
|
76
|
+
dependency = vertex.payload.merge(dependency) if vertex.payload
|
77
|
+
vertex.payload = dependency
|
78
|
+
dependency
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.add_to_dependency_graph(object, parents, dependency_graph, pods_to_unlock)
|
82
|
+
case object
|
83
|
+
when String
|
84
|
+
add_child_vertex_to_graph(object, parents, dependency_graph, pods_to_unlock)
|
85
|
+
when Hash
|
86
|
+
object.each do |key, value|
|
87
|
+
dependency = add_child_vertex_to_graph(key, parents, dependency_graph, pods_to_unlock)
|
88
|
+
value.each { |v| add_to_dependency_graph(v, [dependency.name], dependency_graph, pods_to_unlock) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Pod
|
2
|
+
class DyInstaller
|
3
|
+
class Analyzer
|
4
|
+
# Bundles the information needed to setup a {PodTarget}.
|
5
|
+
class PodVariant
|
6
|
+
# @return [Array<Specification>] the spec and subspecs for the target
|
7
|
+
#
|
8
|
+
attr_reader :specs
|
9
|
+
|
10
|
+
# @return [Array<Specification>] the test specs for the target
|
11
|
+
#
|
12
|
+
attr_reader :test_specs
|
13
|
+
|
14
|
+
# @return [Platform] the platform
|
15
|
+
#
|
16
|
+
attr_reader :platform
|
17
|
+
|
18
|
+
# @return [Bool] whether this pod should be built as framework
|
19
|
+
#
|
20
|
+
attr_reader :requires_frameworks
|
21
|
+
alias_method :requires_frameworks?, :requires_frameworks
|
22
|
+
|
23
|
+
# @return [Specification] the root specification
|
24
|
+
#
|
25
|
+
def root_spec
|
26
|
+
specs.first.root
|
27
|
+
end
|
28
|
+
|
29
|
+
# Initialize a new instance from its attributes.
|
30
|
+
#
|
31
|
+
# @param [Array<Specification>] specs @see #specs
|
32
|
+
# @param [Array<Specification>] test_specs @see #test_specs
|
33
|
+
# @param [Platform] platform @see #platform
|
34
|
+
# @param [Bool] requires_frameworks @see #requires_frameworks?
|
35
|
+
#
|
36
|
+
def initialize(specs, test_specs, platform, requires_frameworks = false)
|
37
|
+
@specs = specs
|
38
|
+
@test_specs = test_specs
|
39
|
+
@platform = platform
|
40
|
+
@requires_frameworks = requires_frameworks
|
41
|
+
end
|
42
|
+
|
43
|
+
# @note Test specs are intentionally not included as part of the equality for pod variants since a
|
44
|
+
# pod variant should not be affected by the number of test specs included.
|
45
|
+
#
|
46
|
+
# @return [Bool] whether the {PodVariant} is equal to another taking all
|
47
|
+
# all their attributes into account
|
48
|
+
#
|
49
|
+
def ==(other)
|
50
|
+
self.class == other.class &&
|
51
|
+
specs == other.specs &&
|
52
|
+
platform == other.platform &&
|
53
|
+
requires_frameworks == other.requires_frameworks
|
54
|
+
end
|
55
|
+
alias_method :eql?, :==
|
56
|
+
|
57
|
+
# Hashes the instance by all its attributes.
|
58
|
+
#
|
59
|
+
# This adds support to make instances usable as Hash keys.
|
60
|
+
#
|
61
|
+
# @!visibility private
|
62
|
+
def hash
|
63
|
+
[specs, platform, requires_frameworks].hash
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Pod
|
4
|
+
class DyInstaller
|
5
|
+
class Analyzer
|
6
|
+
# Collects all {PodVariant}.
|
7
|
+
class PodVariantSet
|
8
|
+
# @return [Array<PodVariant>] the different variants within this set.
|
9
|
+
#
|
10
|
+
attr_reader :variants
|
11
|
+
|
12
|
+
# Initialize a new instance.
|
13
|
+
#
|
14
|
+
# @param [Array<PodVariant>] variants @see #variants
|
15
|
+
#
|
16
|
+
def initialize(variants)
|
17
|
+
@variants = variants
|
18
|
+
end
|
19
|
+
|
20
|
+
# Describes what makes each {PodVariant} distinct among the others.
|
21
|
+
#
|
22
|
+
# @return [Hash<PodVariant, String>]
|
23
|
+
#
|
24
|
+
def scope_suffixes
|
25
|
+
return { variants.first => nil } if variants.count == 1
|
26
|
+
Hash[scope_by_specs.map do |variant, scope|
|
27
|
+
require 'digest'
|
28
|
+
scope = Digest::MD5.hexdigest(scope)[0..7] if !scope.nil? && scope.length >= 50
|
29
|
+
[variant, scope]
|
30
|
+
end]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Groups the collection by result of the block.
|
34
|
+
#
|
35
|
+
# @param [Block<Variant, #hash>] block
|
36
|
+
# @return [Array<PodVariantSet>]
|
37
|
+
#
|
38
|
+
def group_by(&block)
|
39
|
+
variants.group_by(&block).map { |_, v| PodVariantSet.new(v) }
|
40
|
+
end
|
41
|
+
|
42
|
+
# @private
|
43
|
+
#
|
44
|
+
# Prepends the given scoped {PodVariant}s with another scoping label, if there
|
45
|
+
# was more than one group of {PodVariant}s given.
|
46
|
+
#
|
47
|
+
# @param [Array<Hash<PodVariant, String>>] scoped_variants
|
48
|
+
# {PodVariant}s, which where grouped on base of a criteria, which is used
|
49
|
+
# in the block argument to generate a descriptive label.
|
50
|
+
#
|
51
|
+
# @param [Block<PodVariant, String>] block
|
52
|
+
# takes a {PodVariant} and returns a scope suffix which is prepended, if
|
53
|
+
# necessary.
|
54
|
+
#
|
55
|
+
# @return [Hash<PodVariant, String>]
|
56
|
+
#
|
57
|
+
def scope_if_necessary(scoped_variants, &block)
|
58
|
+
if scoped_variants.count == 1
|
59
|
+
return scoped_variants.first
|
60
|
+
end
|
61
|
+
Hash[scoped_variants.flat_map do |variants|
|
62
|
+
variants.map do |variant, suffix|
|
63
|
+
prefix = block.call(variant)
|
64
|
+
scope = [prefix, suffix].compact.join('-')
|
65
|
+
[variant, !scope.empty? ? scope : nil]
|
66
|
+
end
|
67
|
+
end]
|
68
|
+
end
|
69
|
+
|
70
|
+
# @private
|
71
|
+
# @return [Hash<PodVariant, String>]
|
72
|
+
#
|
73
|
+
def scope_by_build_type
|
74
|
+
scope_if_necessary(group_by(&:requires_frameworks).map(&:scope_by_platform)) do |variant|
|
75
|
+
variant.requires_frameworks? ? 'framework' : 'library'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# @private
|
80
|
+
# @return [Hash<PodVariant, String>]
|
81
|
+
#
|
82
|
+
def scope_by_platform
|
83
|
+
grouped_variants = group_by { |v| v.platform.name }
|
84
|
+
if grouped_variants.all? { |set| set.variants.count == 1 }
|
85
|
+
# => Platform name
|
86
|
+
platform_name_proc = proc { |v| Platform.string_name(v.platform.symbolic_name).tr(' ', '') }
|
87
|
+
else
|
88
|
+
grouped_variants = group_by(&:platform)
|
89
|
+
# => Platform name + SDK version
|
90
|
+
platform_name_proc = proc { |v| v.platform.to_s.tr(' ', '') }
|
91
|
+
end
|
92
|
+
scope_if_necessary(grouped_variants.map(&:scope_without_suffix), &platform_name_proc)
|
93
|
+
end
|
94
|
+
|
95
|
+
# @private
|
96
|
+
# @return [Hash<PodVariant, String>]
|
97
|
+
#
|
98
|
+
def scope_by_specs
|
99
|
+
root_spec = variants.first.root_spec
|
100
|
+
specs = [root_spec]
|
101
|
+
specs += if root_spec.default_subspecs.empty?
|
102
|
+
root_spec.subspecs.compact
|
103
|
+
else
|
104
|
+
root_spec.default_subspecs.map do |subspec_name|
|
105
|
+
root_spec.subspec_by_name("#{root_spec.name}/#{subspec_name}")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
default_specs = Set.new(specs)
|
109
|
+
grouped_variants = group_by(&:specs)
|
110
|
+
all_spec_variants = grouped_variants.map { |set| set.variants.first.specs }
|
111
|
+
common_specs = all_spec_variants.map(&:to_set).flatten.inject(&:&)
|
112
|
+
omit_common_specs = common_specs.any? && common_specs.proper_superset?(default_specs)
|
113
|
+
scope_if_necessary(grouped_variants.map(&:scope_by_build_type)) do |variant|
|
114
|
+
specs = variant.specs.to_set
|
115
|
+
|
116
|
+
# The current variant contains all default specs
|
117
|
+
omit_default_specs = default_specs.any? && default_specs.subset?(specs)
|
118
|
+
if omit_default_specs
|
119
|
+
specs -= default_specs
|
120
|
+
end
|
121
|
+
|
122
|
+
# There are common specs, which are different from the default specs
|
123
|
+
if omit_common_specs
|
124
|
+
specs -= common_specs
|
125
|
+
end
|
126
|
+
|
127
|
+
spec_names = specs.map do |spec|
|
128
|
+
spec.root? ? '.root' : spec.name.split('/')[1..-1].join('_')
|
129
|
+
end.sort
|
130
|
+
if spec_names.empty?
|
131
|
+
omit_common_specs ? '.common' : nil
|
132
|
+
else
|
133
|
+
if omit_common_specs
|
134
|
+
spec_names.unshift('.common')
|
135
|
+
elsif omit_default_specs
|
136
|
+
spec_names.unshift('.default')
|
137
|
+
end
|
138
|
+
spec_names.reduce('') do |acc, name|
|
139
|
+
"#{acc}#{acc.empty? || name[0] == '.' ? '' : '-'}#{name}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# @private
|
146
|
+
#
|
147
|
+
# Helps to define scope suffixes recursively.
|
148
|
+
#
|
149
|
+
# @return [Hash<PodVariant, String>]
|
150
|
+
#
|
151
|
+
def scope_without_suffix
|
152
|
+
Hash[variants.map { |v| [v, nil] }]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Pod
|
2
|
+
class DyInstaller
|
3
|
+
class Analyzer
|
4
|
+
# Caches podfile & target definition dependencies, so they do not need to be re-computed
|
5
|
+
# from the internal hash on each access
|
6
|
+
#
|
7
|
+
class PodfileDependencyCache
|
8
|
+
# @return [Array<Pod::Dependency>]
|
9
|
+
# All the dependencies in the podfile
|
10
|
+
#
|
11
|
+
attr_reader :podfile_dependencies
|
12
|
+
|
13
|
+
def initialize(podfile_dependencies, dependencies_by_target_definition)
|
14
|
+
@podfile_dependencies = podfile_dependencies
|
15
|
+
@dependencies_by_target_definition = dependencies_by_target_definition
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the dependencies for the given target definition
|
19
|
+
#
|
20
|
+
def target_definition_dependencies(target_definition)
|
21
|
+
@dependencies_by_target_definition[target_definition] ||
|
22
|
+
raise(ArgumentError, "dependencies for #{target_definition.inspect} do not exist in the cache")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a list of all of the target definitions in the Podfile
|
26
|
+
#
|
27
|
+
def target_definition_list
|
28
|
+
@dependencies_by_target_definition.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates a {PodfileDependencyCache} from the given {Podfile}
|
32
|
+
#
|
33
|
+
# @param [Podfile] podfile
|
34
|
+
# The {Podfile} from which dependencies should be cached
|
35
|
+
#
|
36
|
+
# @return [PodfileDependencyCache]
|
37
|
+
# A warmed, immutable cache of all the dependencies in the {Podfile}
|
38
|
+
#
|
39
|
+
def self.from_podfile(podfile)
|
40
|
+
podfile_dependencies = []
|
41
|
+
dependencies_by_target_definition = {}
|
42
|
+
podfile.target_definition_list.each do |target_definition|
|
43
|
+
deps = target_definition.dependencies.freeze
|
44
|
+
podfile_dependencies.concat deps
|
45
|
+
dependencies_by_target_definition[target_definition] = deps
|
46
|
+
end
|
47
|
+
podfile_dependencies.uniq!
|
48
|
+
|
49
|
+
new(podfile_dependencies.freeze, dependencies_by_target_definition.freeze)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
module Pod
|
2
|
+
class DyInstaller
|
3
|
+
class Analyzer
|
4
|
+
# Analyze the sandbox to detect which Pods should be removed, and which
|
5
|
+
# ones should be reinstalled.
|
6
|
+
#
|
7
|
+
# The logic is the following:
|
8
|
+
#
|
9
|
+
# Added
|
10
|
+
# - If not present in the sandbox lockfile.
|
11
|
+
# - The directory of the Pod doesn't exits.
|
12
|
+
#
|
13
|
+
# Changed
|
14
|
+
# - The version of the Pod changed.
|
15
|
+
# - The SHA of the specification file changed.
|
16
|
+
# - The specific installed (sub)specs of the same Pod changed.
|
17
|
+
# - The specification is from an external source and the
|
18
|
+
# installation process is in update mode.
|
19
|
+
# - The directory of the Pod is empty.
|
20
|
+
# - The Pod has been pre-downloaded.
|
21
|
+
#
|
22
|
+
# Removed
|
23
|
+
# - If a specification is present in the lockfile but not in the resolved
|
24
|
+
# specs.
|
25
|
+
#
|
26
|
+
# Unchanged
|
27
|
+
# - If none of the above conditions match.
|
28
|
+
#
|
29
|
+
class SandboxAnalyzer
|
30
|
+
# @return [Sandbox] The sandbox to analyze.
|
31
|
+
#
|
32
|
+
attr_reader :sandbox
|
33
|
+
|
34
|
+
# @return [Array<Specifications>] The specifications returned by the
|
35
|
+
# resolver.
|
36
|
+
#
|
37
|
+
attr_reader :specs
|
38
|
+
|
39
|
+
# @return [Bool] Whether the installation is performed in update mode.
|
40
|
+
#
|
41
|
+
attr_reader :update_mode
|
42
|
+
|
43
|
+
alias_method :update_mode?, :update_mode
|
44
|
+
|
45
|
+
# @return [Lockfile] The lockfile of the installation as a fall-back if
|
46
|
+
# there is no sandbox manifest. This is indented as a temporary
|
47
|
+
# solution to prevent the full re-installation from users which
|
48
|
+
# are upgrading from CP < 0.17.
|
49
|
+
#
|
50
|
+
# @todo Remove for CP 0.18.
|
51
|
+
#
|
52
|
+
attr_reader :lockfile
|
53
|
+
|
54
|
+
# Init a new SandboxAnalyzer
|
55
|
+
#
|
56
|
+
# @param [Sandbox] sandbox @see sandbox
|
57
|
+
# @param [Array<Specifications>] specs @see specs
|
58
|
+
# @param [Bool] update_mode @see update_mode
|
59
|
+
# @param [Lockfile] lockfile @see lockfile
|
60
|
+
#
|
61
|
+
def initialize(sandbox, specs, update_mode, lockfile = nil)
|
62
|
+
@sandbox = sandbox
|
63
|
+
@specs = specs
|
64
|
+
@update_mode = update_mode
|
65
|
+
@lockfile = lockfile
|
66
|
+
end
|
67
|
+
|
68
|
+
# Performs the analysis to the detect the state of the sandbox respect
|
69
|
+
# to the resolved specifications.
|
70
|
+
#
|
71
|
+
# @return [void]
|
72
|
+
#
|
73
|
+
def analyze
|
74
|
+
state = SpecsState.new
|
75
|
+
if sandbox_manifest
|
76
|
+
all_names = (resolved_pods + sandbox_pods).uniq.sort
|
77
|
+
all_names.sort.each do |name|
|
78
|
+
state.add_name(name, pod_state(name))
|
79
|
+
end
|
80
|
+
else
|
81
|
+
state.added.merge(resolved_pods)
|
82
|
+
end
|
83
|
+
state
|
84
|
+
end
|
85
|
+
|
86
|
+
#---------------------------------------------------------------------#
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# @!group Pod state
|
91
|
+
|
92
|
+
# Returns the state of the Pod with the given name.
|
93
|
+
#
|
94
|
+
# @param [String] pod
|
95
|
+
# the name of the Pod.
|
96
|
+
#
|
97
|
+
# @return [Symbol] The state
|
98
|
+
#
|
99
|
+
def pod_state(pod)
|
100
|
+
return :added if pod_added?(pod)
|
101
|
+
return :deleted if pod_deleted?(pod)
|
102
|
+
return :changed if pod_changed?(pod)
|
103
|
+
:unchanged
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns whether the Pod with the given name should be installed.
|
107
|
+
#
|
108
|
+
# @note A Pod whose folder doesn't exists is considered added.
|
109
|
+
#
|
110
|
+
# @param [String] pod
|
111
|
+
# the name of the Pod.
|
112
|
+
#
|
113
|
+
# @return [Bool] Whether the Pod is added.
|
114
|
+
#
|
115
|
+
def pod_added?(pod)
|
116
|
+
return true if resolved_pods.include?(pod) && !sandbox_pods.include?(pod)
|
117
|
+
return true unless folder_exist?(pod)
|
118
|
+
false
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns whether the Pod with the given name should be removed from
|
122
|
+
# the installation.
|
123
|
+
#
|
124
|
+
# @param [String] pod
|
125
|
+
# the name of the Pod.
|
126
|
+
#
|
127
|
+
# @return [Bool] Whether the Pod is deleted.
|
128
|
+
#
|
129
|
+
def pod_deleted?(pod)
|
130
|
+
return true if !resolved_pods.include?(pod) && sandbox_pods.include?(pod)
|
131
|
+
false
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns whether the Pod with the given name should be considered
|
135
|
+
# changed and thus should be reinstalled.
|
136
|
+
#
|
137
|
+
# @note In update mode, as there is no way to know if a remote source
|
138
|
+
# hash changed the Pods from external
|
139
|
+
# sources are always marked as changed.
|
140
|
+
#
|
141
|
+
# @note A Pod whose folder is empty is considered changed.
|
142
|
+
#
|
143
|
+
# @param [String] pod
|
144
|
+
# the name of the Pod.
|
145
|
+
#
|
146
|
+
# @return [Bool] Whether the Pod is changed.
|
147
|
+
#
|
148
|
+
def pod_changed?(pod)
|
149
|
+
spec = root_spec(pod)
|
150
|
+
return true if spec.version != sandbox_version(pod)
|
151
|
+
return true if spec.checksum != sandbox_checksum(pod)
|
152
|
+
return true if resolved_spec_names(pod) != sandbox_spec_names(pod)
|
153
|
+
return true if sandbox.predownloaded?(pod)
|
154
|
+
return true if folder_empty?(pod)
|
155
|
+
false
|
156
|
+
end
|
157
|
+
|
158
|
+
#---------------------------------------------------------------------#
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
# @!group Private helpers
|
163
|
+
|
164
|
+
# @return [Lockfile] The manifest to use for the sandbox.
|
165
|
+
#
|
166
|
+
def sandbox_manifest
|
167
|
+
sandbox.manifest || lockfile
|
168
|
+
end
|
169
|
+
|
170
|
+
#--------------------------------------#
|
171
|
+
|
172
|
+
# @return [Array<String>] The name of the resolved Pods.
|
173
|
+
#
|
174
|
+
def resolved_pods
|
175
|
+
specs.map { |spec| spec.root.name }.uniq
|
176
|
+
end
|
177
|
+
|
178
|
+
# @return [Array<String>] The name of the Pods stored in the sandbox
|
179
|
+
# manifest.
|
180
|
+
#
|
181
|
+
def sandbox_pods
|
182
|
+
sandbox_manifest.pod_names.map { |name| Specification.root_name(name) }.uniq
|
183
|
+
end
|
184
|
+
|
185
|
+
# @return [Array<String>] The name of the resolved specifications
|
186
|
+
# (includes subspecs).
|
187
|
+
#
|
188
|
+
# @param [String] pod
|
189
|
+
# the name of the Pod.
|
190
|
+
#
|
191
|
+
def resolved_spec_names(pod)
|
192
|
+
specs.select { |s| s.root.name == pod }.map(&:name).uniq.sort
|
193
|
+
end
|
194
|
+
|
195
|
+
# @return [Array<String>] The name of the specifications stored in the
|
196
|
+
# sandbox manifest (includes subspecs).
|
197
|
+
#
|
198
|
+
# @param [String] pod
|
199
|
+
# the name of the Pod.
|
200
|
+
#
|
201
|
+
def sandbox_spec_names(pod)
|
202
|
+
sandbox_manifest.pod_names.select { |name| Specification.root_name(name) == pod }.uniq.sort
|
203
|
+
end
|
204
|
+
|
205
|
+
# @return [Specification] The root specification for the Pod with the
|
206
|
+
# given name.
|
207
|
+
#
|
208
|
+
# @param [String] pod
|
209
|
+
# the name of the Pod.
|
210
|
+
#
|
211
|
+
def root_spec(pod)
|
212
|
+
specs.find { |s| s.root.name == pod }.root
|
213
|
+
end
|
214
|
+
|
215
|
+
#--------------------------------------#
|
216
|
+
|
217
|
+
# @return [Version] The version of Pod with the given name stored in
|
218
|
+
# the sandbox.
|
219
|
+
#
|
220
|
+
# @param [String] pod
|
221
|
+
# the name of the Pod.
|
222
|
+
#
|
223
|
+
def sandbox_version(pod)
|
224
|
+
sandbox_manifest.version(pod)
|
225
|
+
end
|
226
|
+
|
227
|
+
# @return [String] The checksum of the specification of the Pod with
|
228
|
+
# the given name stored in the sandbox.
|
229
|
+
#
|
230
|
+
# @param [String] pod
|
231
|
+
# the name of the Pod.
|
232
|
+
#
|
233
|
+
def sandbox_checksum(pod)
|
234
|
+
sandbox_manifest.checksum(pod)
|
235
|
+
end
|
236
|
+
|
237
|
+
#--------------------------------------#
|
238
|
+
|
239
|
+
def folder_exist?(pod)
|
240
|
+
sandbox.pod_dir(pod).exist?
|
241
|
+
end
|
242
|
+
|
243
|
+
def folder_empty?(pod)
|
244
|
+
Dir.glob(sandbox.pod_dir(pod) + '*').empty?
|
245
|
+
end
|
246
|
+
|
247
|
+
#---------------------------------------------------------------------#
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|