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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pod/command.rb +2 -0
  3. data/lib/pod/command/dyinstall.rb +51 -0
  4. data/lib/pod/command/dyupdate.rb +106 -0
  5. data/lib/pod/command/fmwk.rb +4 -0
  6. data/lib/pod/command/lib/dylint.rb +1 -0
  7. data/lib/pod/gem_version.rb +1 -1
  8. data/lib/pod/installer.rb +715 -0
  9. data/lib/pod/installer/analyzer.rb +934 -0
  10. data/lib/pod/installer/analyzer/analysis_result.rb +57 -0
  11. data/lib/pod/installer/analyzer/locking_dependency_analyzer.rb +95 -0
  12. data/lib/pod/installer/analyzer/pod_variant.rb +68 -0
  13. data/lib/pod/installer/analyzer/pod_variant_set.rb +157 -0
  14. data/lib/pod/installer/analyzer/podfile_dependency_cache.rb +54 -0
  15. data/lib/pod/installer/analyzer/sandbox_analyzer.rb +251 -0
  16. data/lib/pod/installer/analyzer/specs_state.rb +84 -0
  17. data/lib/pod/installer/analyzer/target_inspection_result.rb +45 -0
  18. data/lib/pod/installer/analyzer/target_inspector.rb +254 -0
  19. data/lib/pod/installer/installation_options.rb +158 -0
  20. data/lib/pod/installer/pod_source_installer.rb +214 -0
  21. data/lib/pod/installer/pod_source_preparer.rb +77 -0
  22. data/lib/pod/installer/podfile_validator.rb +139 -0
  23. data/lib/pod/installer/post_install_hooks_context.rb +107 -0
  24. data/lib/pod/installer/pre_install_hooks_context.rb +42 -0
  25. data/lib/pod/installer/source_provider_hooks_context.rb +32 -0
  26. data/lib/pod/installer/user_project_integrator.rb +253 -0
  27. data/lib/pod/installer/user_project_integrator/target_integrator.rb +462 -0
  28. data/lib/pod/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +146 -0
  29. data/lib/pod/installer/xcode.rb +8 -0
  30. data/lib/pod/installer/xcode/pods_project_generator.rb +353 -0
  31. data/lib/pod/installer/xcode/pods_project_generator/aggregate_target_installer.rb +172 -0
  32. data/lib/pod/installer/xcode/pods_project_generator/file_references_installer.rb +367 -0
  33. data/lib/pod/installer/xcode/pods_project_generator/pod_target_installer.rb +718 -0
  34. data/lib/pod/installer/xcode/pods_project_generator/pod_target_integrator.rb +111 -0
  35. data/lib/pod/installer/xcode/pods_project_generator/target_installer.rb +265 -0
  36. data/lib/pod/installer/xcode/target_validator.rb +141 -0
  37. data/lib/pod/resolver.rb +632 -0
  38. 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