cocoapods 0.34.4 → 0.35.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +108 -1
  3. data/bin/pod +20 -2
  4. data/lib/cocoapods.rb +1 -0
  5. data/lib/cocoapods/command.rb +17 -11
  6. data/lib/cocoapods/command/repo/push.rb +1 -1
  7. data/lib/cocoapods/command/search.rb +1 -0
  8. data/lib/cocoapods/gem_version.rb +1 -1
  9. data/lib/cocoapods/generator/acknowledgements/plist.rb +4 -10
  10. data/lib/cocoapods/generator/header.rb +75 -0
  11. data/lib/cocoapods/generator/prefix_header.rb +15 -34
  12. data/lib/cocoapods/generator/xcconfig/aggregate_xcconfig.rb +2 -2
  13. data/lib/cocoapods/generator/xcconfig/private_pod_xcconfig.rb +4 -1
  14. data/lib/cocoapods/hooks/library_representation.rb +1 -1
  15. data/lib/cocoapods/installer.rb +3 -3
  16. data/lib/cocoapods/installer/analyzer.rb +35 -37
  17. data/lib/cocoapods/installer/analyzer/locking_dependency_analyzer.rb +72 -0
  18. data/lib/cocoapods/installer/file_references_installer.rb +10 -9
  19. data/lib/cocoapods/installer/target_installer.rb +21 -21
  20. data/lib/cocoapods/installer/target_installer/aggregate_target_installer.rb +17 -17
  21. data/lib/cocoapods/installer/target_installer/pod_target_installer.rb +19 -19
  22. data/lib/cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +7 -3
  23. data/lib/cocoapods/project.rb +1 -1
  24. data/lib/cocoapods/resolver.rb +235 -98
  25. data/lib/cocoapods/resolver/lazy_specification.rb +60 -0
  26. data/lib/cocoapods/sandbox.rb +2 -1
  27. data/lib/cocoapods/sandbox/file_accessor.rb +26 -19
  28. data/lib/cocoapods/sandbox/headers_store.rb +12 -7
  29. data/lib/cocoapods/sources_manager.rb +6 -6
  30. data/lib/cocoapods/target.rb +1 -1
  31. data/lib/cocoapods/target/pod_target.rb +2 -2
  32. data/lib/cocoapods/user_interface.rb +1 -1
  33. data/lib/cocoapods/user_interface/error_report.rb +3 -0
  34. data/lib/cocoapods/validator.rb +2 -2
  35. metadata +21 -26
  36. data/lib/cocoapods/command/push.rb +0 -21
@@ -9,7 +9,7 @@ module Pod
9
9
  # @return [void]
10
10
  #
11
11
  def install!
12
- UI.message "- Installing target `#{library.name}` #{library.platform}" do
12
+ UI.message "- Installing target `#{target.name}` #{target.platform}" do
13
13
  add_target
14
14
  create_support_files_dir
15
15
  add_files_to_build_phases
@@ -33,15 +33,15 @@ module Pod
33
33
  # @return [void]
34
34
  #
35
35
  def add_files_to_build_phases
36
- library.file_accessors.each do |file_accessor|
36
+ target.file_accessors.each do |file_accessor|
37
37
  consumer = file_accessor.spec_consumer
38
38
  flags = compiler_flags_for_consumer(consumer)
39
39
  all_source_files = file_accessor.source_files
40
40
  regular_source_files = all_source_files.reject { |sf| sf.extname == '.d' }
41
41
  regular_file_refs = regular_source_files.map { |sf| project.reference_for_path(sf) }
42
- target.add_file_references(regular_file_refs, flags)
42
+ native_target.add_file_references(regular_file_refs, flags)
43
43
  other_file_refs = (all_source_files - regular_source_files).map { |sf| project.reference_for_path(sf) }
44
- target.add_file_references(other_file_refs, nil)
44
+ native_target.add_file_references(other_file_refs, nil)
45
45
  end
46
46
  end
47
47
 
@@ -53,22 +53,22 @@ module Pod
53
53
  # @return [void]
54
54
  #
55
55
  def add_resources_bundle_targets
56
- library.file_accessors.each do |file_accessor|
56
+ target.file_accessors.each do |file_accessor|
57
57
  file_accessor.resource_bundles.each do |bundle_name, paths|
58
58
  # Add a dependency on an existing Resource Bundle target if possible
59
59
  if bundle_target = project.targets.find { |target| target.name == bundle_name }
60
- target.add_dependency(bundle_target)
60
+ native_target.add_dependency(bundle_target)
61
61
  next
62
62
  end
63
63
  file_references = paths.map { |sf| project.reference_for_path(sf) }
64
64
  bundle_target = project.new_resources_bundle(bundle_name, file_accessor.spec_consumer.platform_name)
65
65
  bundle_target.add_resources(file_references)
66
66
 
67
- library.user_build_configurations.each do |bc_name, type|
67
+ target.user_build_configurations.each do |bc_name, type|
68
68
  bundle_target.add_build_configuration(bc_name, type)
69
69
  end
70
70
 
71
- target.add_dependency(bundle_target)
71
+ native_target.add_dependency(bundle_target)
72
72
  end
73
73
  end
74
74
  end
@@ -78,17 +78,17 @@ module Pod
78
78
  # @return [void]
79
79
  #
80
80
  def create_xcconfig_file
81
- path = library.xcconfig_path
82
- public_gen = Generator::XCConfig::PublicPodXCConfig.new(library)
81
+ path = target.xcconfig_path
82
+ public_gen = Generator::XCConfig::PublicPodXCConfig.new(target)
83
83
  public_gen.save_as(path)
84
84
  add_file_to_support_group(path)
85
85
 
86
- path = library.xcconfig_private_path
87
- private_gen = Generator::XCConfig::PrivatePodXCConfig.new(library, public_gen.xcconfig)
86
+ path = target.xcconfig_private_path
87
+ private_gen = Generator::XCConfig::PrivatePodXCConfig.new(target, public_gen.xcconfig)
88
88
  private_gen.save_as(path)
89
89
  xcconfig_file_ref = add_file_to_support_group(path)
90
90
 
91
- target.build_configurations.each do |c|
91
+ native_target.build_configurations.each do |c|
92
92
  c.base_configuration_reference = xcconfig_file_ref
93
93
  end
94
94
  end
@@ -100,13 +100,13 @@ module Pod
100
100
  # @return [void]
101
101
  #
102
102
  def create_prefix_header
103
- path = library.prefix_header_path
104
- generator = Generator::PrefixHeader.new(library.file_accessors, library.platform)
105
- generator.imports << library.target_environment_header_path.basename
103
+ path = target.prefix_header_path
104
+ generator = Generator::PrefixHeader.new(target.file_accessors, target.platform)
105
+ generator.imports << target.target_environment_header_path.basename
106
106
  generator.save_as(path)
107
107
  add_file_to_support_group(path)
108
108
 
109
- target.build_configurations.each do |c|
109
+ native_target.build_configurations.each do |c|
110
110
  relative_path = path.relative_path_from(project.path.dirname)
111
111
  c.build_settings['GCC_PREFIX_HEADER'] = relative_path.to_s
112
112
  end
@@ -176,8 +176,8 @@ module Pod
176
176
  # @return [PBXFileReference] the file reference of the added file.
177
177
  #
178
178
  def add_file_to_support_group(path)
179
- pod_name = library.pod_name
180
- dir = library.support_files_dir
179
+ pod_name = target.pod_name
180
+ dir = target.support_files_dir
181
181
  group = project.pod_support_files_group(pod_name, dir)
182
182
  group.new_file(path)
183
183
  end
@@ -83,22 +83,26 @@ module Pod
83
83
  # @param [Xcodeproj::XCBuildConfiguration] config
84
84
  # The build configuration.
85
85
  #
86
+ # @return [Boolean] Indicates whether or not any changes were made.
87
+ #
86
88
  def self.set_target_xcconfig(pod_bundle, target, config)
87
89
  path = pod_bundle.xcconfig_relative_path(config.name)
88
90
  group = config.project['Pods'] || config.project.new_group('Pods')
89
91
  file_ref = group.files.find { |f| f.path == path }
90
- if config.base_configuration_reference != file_ref
92
+ if config.base_configuration_reference &&
93
+ config.base_configuration_reference != file_ref
91
94
  UI.warn 'CocoaPods did not set the base configuration of your ' \
92
95
  'project because your project already has a custom ' \
93
96
  'config set. In order for CocoaPods integration to work at ' \
94
97
  'all, please either set the base configurations of the target ' \
95
98
  "`#{target.name}` to `#{path}` or include the `#{path}` in your " \
96
99
  'build configuration.'
97
- false
98
- elsif !file_ref
100
+ elsif config.base_configuration_reference.nil? || file_ref.nil?
99
101
  file_ref ||= group.new_file(path)
100
102
  config.base_configuration_reference = file_ref
103
+ return true
101
104
  end
105
+ false
102
106
  end
103
107
 
104
108
  private
@@ -209,7 +209,7 @@ module Pod
209
209
  #
210
210
  def add_build_configuration(name, type)
211
211
  build_configuration = super
212
- values = ["#{name.gsub(/[^a-zA-Z0-9_]/, '_').upcase}=1"]
212
+ values = ["#{name.gsub(/[^a-zA-Z0-9_]/, '_').sub(/(^[0-9])/, '_\1').upcase}=1"]
213
213
  settings = build_configuration.build_settings
214
214
  definitions = Array(settings['GCC_PREPROCESSOR_DEFINITIONS'])
215
215
  values.each do |value|
@@ -1,18 +1,10 @@
1
+ require 'molinillo'
2
+ require 'cocoapods/resolver/lazy_specification'
3
+
1
4
  module Pod
2
5
  # The resolver is responsible of generating a list of specifications grouped
3
6
  # by target for a given Podfile.
4
7
  #
5
- # @todo Its current implementation is naive, in the sense that it can't do full
6
- # automatic resolves like Bundler:
7
- # [how-does-bundler-bundle](http://patshaughnessy.net/2011/9/24/how-does-bundler-bundle)
8
- #
9
- # @todo Another limitation is that the order of the dependencies matter. The
10
- # current implementation could create issues, for example, if a
11
- # specification is loaded for a target definition and later for another
12
- # target is set in head mode. The first specification will not be in head
13
- # mode.
14
- #
15
- #
16
8
  class Resolver
17
9
  # @return [Sandbox] the Sandbox used by the resolver to find external
18
10
  # dependencies.
@@ -58,23 +50,12 @@ module Pod
58
50
  # definition.
59
51
  #
60
52
  def resolve
61
- @cached_sets = {}
62
- @cached_specs = {}
63
- @specs_by_target = {}
64
-
65
- target_definitions = podfile.target_definition_list
66
- target_definitions.each do |target|
67
- title = "Resolving dependencies for target `#{target.name}' " \
68
- "(#{target.platform})"
69
- UI.section(title) do
70
- @loaded_specs = []
71
- find_dependency_specs(podfile, target.dependencies, target)
72
- specs = cached_specs.values_at(*@loaded_specs).sort_by(&:name)
73
- specs_by_target[target] = specs
74
- end
75
- end
76
-
53
+ dependencies = @podfile.target_definition_list.map(&:dependencies).flatten
54
+ @cached_sets = {}
55
+ @activated = Molinillo::Resolver.new(self, self).resolve(dependencies, locked_dependencies)
77
56
  specs_by_target
57
+ rescue Molinillo::ResolverError => e
58
+ raise Informative, e.message
78
59
  end
79
60
 
80
61
  # @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
@@ -82,113 +63,237 @@ module Pod
82
63
  #
83
64
  # @note The returned specifications can be subspecs.
84
65
  #
85
- attr_reader :specs_by_target
66
+ def specs_by_target
67
+ @specs_by_target ||= begin
68
+ specs_by_target = {}
69
+ podfile.target_definition_list.each do |target|
70
+ specs = target.dependencies.map(&:name).map do |name|
71
+ node = @activated.vertex_named(name)
72
+ valid_dependencies_for_target_from_node(target, node) << node
73
+ end
74
+
75
+ specs_by_target[target] = specs.
76
+ flatten.
77
+ map(&:payload).
78
+ uniq.
79
+ sort_by(&:name).
80
+ each do |spec|
81
+ validate_platform(spec, target)
82
+ sandbox.store_head_pod(spec.name) if spec.version.head
83
+ end
84
+ end
85
+ specs_by_target
86
+ end
87
+ end
86
88
 
87
89
  #-------------------------------------------------------------------------#
88
90
 
89
- private
91
+ public
90
92
 
91
- # !@ Resolution context
93
+ # @!group Specification Provider
92
94
 
93
- # @return [Hash<String => Set>] A cache that keeps tracks of the sets
94
- # loaded by the resolution process.
95
+ include Molinillo::SpecificationProvider
96
+
97
+ # Returns (and caches) the specification that satisfy the given dependency.
95
98
  #
96
- # @note Sets store the resolved dependencies and return the highest
97
- # available specification found in the sources. This is done
98
- # globally and not per target definition because there can be just
99
- # one Pod installation, so different version of the same Pods for
100
- # target definitions are not allowed.
99
+ # @return [Array<Specification>] the specifications that satisfy the given
100
+ # `dependency`.
101
101
  #
102
- attr_accessor :cached_sets
102
+ # @param [Dependency] dependency the dependency that is being searched for.
103
+ #
104
+ def search_for(dependency)
105
+ @search ||= {}
106
+ @search[dependency] ||= begin
107
+ specs = find_cached_set(dependency).
108
+ all_specifications.
109
+ select { |s| dependency.requirement.satisfied_by? s.version }.
110
+ map { |s| s.subspec_by_name(dependency.name, false) }.
111
+ compact
112
+
113
+ specs.
114
+ reverse.
115
+ each { |s| s.version.head = dependency.head? }
116
+ end
117
+ @search[dependency].dup
118
+ end
103
119
 
104
- # @return [Hash<String => Specification>] The loaded specifications grouped
105
- # by name.
120
+ # Returns the dependencies of `specification`.
106
121
  #
107
- attr_accessor :cached_specs
122
+ # @return [Array<Specification>] all dependencies of `specification`.
123
+ #
124
+ # @param [Specification] specification the specification whose own
125
+ # dependencies are being asked for.
126
+ #
127
+ def dependencies_for(specification)
128
+ specification.all_dependencies.map do |dependency|
129
+ if dependency.root_name == Specification.root_name(specification.name)
130
+ dependency.dup.tap { |d| d.specific_version = specification.version }
131
+ else
132
+ dependency
133
+ end
134
+ end
135
+ end
108
136
 
109
- #-------------------------------------------------------------------------#
137
+ # Returns the name for the given `dependency`.
138
+ #
139
+ # @return [String] the name for the given `dependency`.
140
+ #
141
+ # @param [Dependency] dependency the dependency whose name is being
142
+ # queried.
143
+ #
144
+ def name_for(dependency)
145
+ dependency.name
146
+ end
110
147
 
111
- private
148
+ # @return [String] the user-facing name for a {Podfile}.
149
+ #
150
+ def name_for_explicit_dependency_source
151
+ 'Podfile'
152
+ end
112
153
 
113
- # @!group Private helpers
154
+ # @return [String] the user-facing name for a {Lockfile}.
155
+ #
156
+ def name_for_locking_dependency_source
157
+ 'Podfile.lock'
158
+ end
114
159
 
115
- # Resolves recursively the dependencies of a specification and stores them
116
- # in the @cached_specs ivar.
160
+ # Determines whether the given `requirement` is satisfied by the given
161
+ # `spec`, in the context of the current `activated` dependency graph.
162
+ #
163
+ # @return [Boolean] whether `requirement` is satisfied by `spec` in the
164
+ # context of the current `activated` dependency graph.
117
165
  #
118
- # @param [Podfile, Specification, #to_s] dependent_spec
119
- # the specification whose dependencies are being resolved. Used
120
- # only for UI purposes.
166
+ # @param [Dependency] requirement the dependency in question.
121
167
  #
122
- # @param [Array<Dependency>] dependencies
123
- # the dependencies of the specification.
168
+ # @param [Molinillo::DependencyGraph] activated the current dependency
169
+ # graph in the resolution process.
124
170
  #
125
- # @param [TargetDefinition] target_definition
126
- # the target definition that owns the specification.
171
+ # @param [Specification] spec the specification in question.
172
+ #
173
+ def requirement_satisfied_by?(requirement, activated, spec)
174
+ existing_vertices = activated.vertices.values.select do |v|
175
+ Specification.root_name(v.name) == requirement.root_name
176
+ end
177
+ existing = existing_vertices.map(&:payload).compact.first
178
+ requirement_satisfied =
179
+ if existing
180
+ existing.version == spec.version && requirement.requirement.satisfied_by?(spec.version)
181
+ else
182
+ requirement.requirement.satisfied_by? spec.version
183
+ end
184
+ requirement_satisfied && !(spec.version.prerelease? && existing_vertices.flat_map(&:requirements).none?(&:prerelease?))
185
+ end
186
+
187
+ # Sort dependencies so that the ones that are easiest to resolve are first.
188
+ # Easiest to resolve is (usually) defined by:
189
+ # 1) Is this dependency already activated?
190
+ # 2) How relaxed are the requirements?
191
+ # 3) Are there any conflicts for this dependency?
192
+ # 4) How many possibilities are there to satisfy this dependency?
127
193
  #
128
- # @note If there is a locked dependency with the same name of a
129
- # given dependency the locked one is used in place of the
130
- # dependency of the specification. In this way it is possible to
131
- # prevent the update of the version of installed pods not changed
132
- # in the Podfile.
194
+ # @return [Array<Dependency>] the sorted dependencies.
133
195
  #
134
- # @note The recursive process checks if a dependency has already been
135
- # loaded to prevent an infinite loop.
196
+ # @param [Array<Dependency>] dependencies the unsorted dependencies.
136
197
  #
137
- # @note The set class merges all (of all the target definitions) the
138
- # dependencies and thus it keeps track of whether it is in head
139
- # mode or from an external source because {Dependency#merge}
140
- # preserves this information.
198
+ # @param [Molinillo::DependencyGraph] activated the dependency graph of
199
+ # currently activated specs.
141
200
  #
142
- # @return [void]
201
+ # @param [{String => Array<Conflict>}] conflicts the current conflicts.
143
202
  #
144
- def find_dependency_specs(dependent_spec, dependencies, target_definition)
145
- dependencies.each do |dependency|
146
- locked_dep = locked_dependencies.find { |ld| ld.name == dependency.name }
147
- dependency = locked_dep if locked_dep
203
+ def sort_dependencies(dependencies, activated, conflicts)
204
+ dependencies.sort_by do |dependency|
205
+ name = name_for(dependency)
206
+ [
207
+ activated.vertex_named(name).payload ? 0 : 1,
208
+ dependency.prerelease? ? 0 : 1,
209
+ conflicts[name] ? 0 : 1,
210
+ search_for(dependency).count,
211
+ ]
212
+ end
213
+ end
214
+
215
+ #-------------------------------------------------------------------------#
148
216
 
149
- UI.message("- #{dependency}", '', 2) do
150
- set = find_cached_set(dependency, dependent_spec)
151
- set.required_by(dependency, dependent_spec.to_s)
217
+ public
152
218
 
153
- if (paths = set.specification_paths_for_version(set.required_version)).length > 1
154
- UI.warn "Found multiple specifications for #{dependency}:\n" \
155
- "- #{paths.join("\n")}"
156
- end
219
+ # @!group Resolver UI
157
220
 
158
- unless @loaded_specs.include?(dependency.name)
159
- spec = set.specification.subspec_by_name(dependency.name)
160
- @loaded_specs << spec.name
161
- cached_specs[spec.name] = spec
162
- validate_platform(spec, target_definition)
163
- if dependency.head? || sandbox.head_pod?(spec.name)
164
- spec.version.head = true
165
- sandbox.store_head_pod(spec.name)
166
- end
221
+ include Molinillo::UI
167
222
 
168
- spec_dependencies = spec.all_dependencies(target_definition.platform)
169
- find_dependency_specs(spec, spec_dependencies, target_definition)
170
- end
171
- end
172
- end
223
+ include Config::Mixin
224
+
225
+ # The UI object the resolver should use for displaying user-facing output.
226
+ #
227
+ # @return [UserInterface] the normal CocoaPods UI object.
228
+ #
229
+ def output
230
+ UI
173
231
  end
174
232
 
233
+ # Called before resolution starts. We print out `Resolving dependencies` in
234
+ # the analyzer, so here we just want to print out a starting `.` in verbose
235
+ # mode.
236
+ #
237
+ # @return [Void]
238
+ #
239
+ def before_resolution
240
+ UI.print '.' if config.verbose
241
+ end
242
+
243
+ # Called after resolution ends. We don't want to {#indicate_progress}
244
+ # unless in verbose mode, so we only use the default implementation then.
245
+ #
246
+ # @return [Void]
247
+ #
248
+ def after_resolution
249
+ super if config.verbose
250
+ end
251
+
252
+ # Called during resolution to indicate progress.
253
+ # We only use the default implementation in verbose mode.
254
+ #
255
+ # @return [Void]
256
+ #
257
+ def indicate_progress
258
+ super if config.verbose
259
+ end
260
+
261
+ #-------------------------------------------------------------------------#
262
+
263
+ private
264
+
265
+ # !@ Resolution context
266
+
267
+ # @return [Hash<String => Set>] A cache that keeps tracks of the sets
268
+ # loaded by the resolution process.
269
+ #
270
+ # @note Sets store the resolved dependencies and return the highest
271
+ # available specification found in the sources. This is done
272
+ # globally and not per target definition because there can be just
273
+ # one Pod installation, so different version of the same Pods for
274
+ # target definitions are not allowed.
275
+ #
276
+ attr_accessor :cached_sets
277
+
278
+ #-------------------------------------------------------------------------#
279
+
280
+ private
281
+
282
+ # @!group Private helpers
283
+
175
284
  # @return [Set] Loads or returns a previously initialized set for the Pod
176
285
  # of the given dependency.
177
286
  #
178
287
  # @param [Dependency] dependency
179
288
  # The dependency for which the set is needed.
180
289
  #
181
- # @param [#to_s] dependent_spec
182
- # the specification whose dependencies are being resolved. Used
183
- # only for UI purposes.
184
- #
185
290
  # @return [Set] the cached set for a given dependency.
186
291
  #
187
- def find_cached_set(dependency, dependent_spec)
292
+ def find_cached_set(dependency)
188
293
  name = dependency.root_name
189
294
  unless cached_sets[name]
190
295
  if dependency.external_source
191
- spec = sandbox.specification(dependency.root_name)
296
+ spec = sandbox.specification(name)
192
297
  unless spec
193
298
  raise StandardError, '[Bug] Unable to find the specification ' \
194
299
  "for `#{dependency}`."
@@ -199,8 +304,7 @@ module Pod
199
304
  end
200
305
  cached_sets[name] = set
201
306
  unless set
202
- raise Informative, 'Unable to find a specification for ' \
203
- "`#{dependency}` depended upon by #{dependent_spec}."
307
+ raise Molinillo::NoSuchDependencyError.new(dependency) # rubocop:disable Style/RaiseArgs
204
308
  end
205
309
  end
206
310
  cached_sets[name]
@@ -239,5 +343,38 @@ module Pod
239
343
  "a minimum requirement of #{spec.available_platforms.join(' - ')}."
240
344
  end
241
345
  end
346
+
347
+ # Returns the target-appropriate nodes that are `successors` of `node`,
348
+ # rejecting those that are {Dependency#from_subspec_dependency?} and have
349
+ # and incompatible platform.
350
+ #
351
+ # @return [Array<Molinillo::DependencyGraph::Vertex>]
352
+ # An array of target-appropriate nodes whose `payload`s are
353
+ # dependencies for `target`.
354
+ #
355
+ def valid_dependencies_for_target_from_node(target, node)
356
+ dependency_nodes = node.outgoing_edges.select do |edge|
357
+ edge_is_valid_for_target?(edge, target)
358
+ end.map(&:destination)
359
+
360
+ dependency_nodes + dependency_nodes.flat_map { |n| valid_dependencies_for_target_from_node(target, n) }
361
+ end
362
+
363
+ # Whether the given `edge` should be followed to find dependencies for the
364
+ # given `target`.
365
+ #
366
+ # @note At the moment, this method only checks whether the edge's
367
+ # requirements are normal dependencies _or_ whether they are
368
+ # dependencies that come from {Specification#subspec_dependencies}
369
+ # and, if so, that their platforms are compatible with the target's.
370
+ #
371
+ # @return [Bool]
372
+ #
373
+ def edge_is_valid_for_target?(edge, target)
374
+ edge.requirements.any? do |dependency|
375
+ !dependency.from_subspec_dependency? ||
376
+ edge.destination.payload.available_platforms.any? { |p| target.platform.supports?(p) }
377
+ end
378
+ end
242
379
  end
243
380
  end