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,632 @@
1
+ require 'molinillo'
2
+ require 'cocoapods/resolver/lazy_specification'
3
+
4
+ module Pod
5
+ class NoSpecFoundError < Informative
6
+ def exit_status
7
+ @exit_status ||= 31
8
+ end
9
+ end
10
+
11
+ # The resolver is responsible of generating a list of specifications grouped
12
+ # by target for a given Podfile.
13
+ #
14
+ class DyResolver
15
+ # A small container that wraps a resolved specification for a given target definition. Additional metadata
16
+ # is included here such as if the specification is only used by tests.
17
+ #
18
+ class ResolverSpecification
19
+ # @return [Specification] the specification that was resolved
20
+ #
21
+ attr_reader :spec
22
+
23
+ # @return [Source] the spec repo source the specification came from
24
+ #
25
+ attr_reader :source
26
+
27
+ # @return [Bool] whether this resolved specification is only used by tests.
28
+ #
29
+ attr_reader :used_by_tests_only
30
+ alias used_by_tests_only? used_by_tests_only
31
+
32
+ def initialize(spec, used_by_tests_only, source)
33
+ @spec = spec
34
+ @used_by_tests_only = used_by_tests_only
35
+ @source = source
36
+ end
37
+
38
+ def name
39
+ spec.name
40
+ end
41
+
42
+ def root
43
+ spec.root
44
+ end
45
+
46
+ def ==(other)
47
+ self.class == other &&
48
+ spec == other.spec &&
49
+ used_by_tests_only == other.test_only
50
+ end
51
+ end
52
+
53
+ include Pod::Installer::InstallationOptions::Mixin
54
+
55
+ delegate_installation_options { podfile }
56
+
57
+ # @return [Sandbox] the Sandbox used by the resolver to find external
58
+ # dependencies.
59
+ #
60
+ attr_reader :sandbox
61
+
62
+ # @return [Podfile] the Podfile used by the resolver.
63
+ #
64
+ attr_reader :podfile
65
+
66
+ # @return [Array<Dependency>] the list of dependencies locked to a specific
67
+ # version.
68
+ #
69
+ attr_reader :locked_dependencies
70
+
71
+ # @return [Array<Source>] The list of the sources which will be used for
72
+ # the resolution.
73
+ #
74
+ attr_reader :sources
75
+
76
+ # @return [Bool] Whether the resolver has sources repositories up-to-date.
77
+ #
78
+ attr_reader :specs_updated
79
+ alias specs_updated? specs_updated
80
+
81
+ # Init a new Resolver
82
+ #
83
+ # @param [Sandbox] sandbox @see sandbox
84
+ # @param [Podfile] podfile @see podfile
85
+ # @param [Array<Dependency>] locked_dependencies @see locked_dependencies
86
+ # @param [Array<Source>, Source] sources @see sources
87
+ # @param [Boolean] specs_updated @see specs_updated
88
+ # @param [PodfileDependencyCache] podfile_dependency_cache the podfile dependency cache to use
89
+ # within this Resolver.
90
+ #
91
+ def initialize(sandbox, podfile, locked_dependencies, sources, specs_updated,
92
+ podfile_dependency_cache: Installer::Analyzer::PodfileDependencyCache.from_podfile(podfile))
93
+ @sandbox = sandbox
94
+ @podfile = podfile
95
+ @locked_dependencies = locked_dependencies
96
+ @sources = Array(sources)
97
+ @specs_updated = specs_updated
98
+ @podfile_dependency_cache = podfile_dependency_cache
99
+ @platforms_by_dependency = Hash.new { |h, k| h[k] = [] }
100
+ @cached_sets = {}
101
+ end
102
+
103
+ #-------------------------------------------------------------------------#
104
+
105
+ public
106
+
107
+ # @!group Resolution
108
+
109
+ # Identifies the specifications that should be installed.
110
+ #
111
+ # @return [Hash{TargetDefinition => Array<ResolverSpecification>}] resolver_specs_by_target
112
+ # the resolved specifications that need to be installed grouped by target
113
+ # definition.
114
+ #
115
+ def resolve
116
+ puts "before resolve...#{Time.now}"
117
+ dependencies = @podfile_dependency_cache.target_definition_list.flat_map do |target|
118
+ @podfile_dependency_cache.target_definition_dependencies(target).each do |dep|
119
+ next unless target.platform
120
+ @platforms_by_dependency[dep].push(target.platform)
121
+ end
122
+ end
123
+ puts "after flat map...#{Time.now}"
124
+ @platforms_by_dependency.each_value(&:uniq!)
125
+ puts "after each...#{Time.now}"
126
+ @activated = Molinillo::Resolver.new(self, self).resolve(dependencies, locked_dependencies)
127
+ puts "after resolve...#{Time.now}"
128
+ resolver_specs_by_target
129
+ rescue Molinillo::ResolverError => e
130
+ handle_resolver_error(e)
131
+ end
132
+
133
+ # @return [Hash{Podfile::TargetDefinition => Array<ResolverSpecification>}]
134
+ # returns the resolved specifications grouped by target.
135
+ #
136
+ # @note The returned specifications can be subspecs.
137
+ #
138
+ def resolver_specs_by_target
139
+ puts "before resolver_specs_by_target...#{Time.now}"
140
+ @resolver_specs_by_target ||= {}.tap do |resolver_specs_by_target|
141
+ dependencies = {}
142
+ puts "#{resolver_specs_by_target} before each...#{Time.now}"
143
+ @podfile_dependency_cache.target_definition_list.each do |target|
144
+ # 读取 Podfile 中每个 target 的 dependency 信息,每个 pod 对应一个 dep
145
+ specs = @podfile_dependency_cache.target_definition_dependencies(target).flat_map do |dep|
146
+ name = dep.name
147
+ node = @activated.vertex_named(name)
148
+ before = Time.now
149
+ noded = valid_dependencies_for_target_from_node(target, dependencies, node) << node
150
+ # puts " - [resolver][resolver_specs_by_target][#{name}] dependencies : #{dependencies.count}, valid_dependencies_for_specs : #{noded.count}, node : #{node.name}, outgoing_edges : #{node.outgoing_edges.count}"
151
+ # puts " - #{noded}"
152
+ # puts " - #{node.payload}"
153
+ # puts " - #{dependencies}"
154
+ after_noded = Time.now
155
+ result = noded.map { |s| [s, node.payload.test_specification?] }
156
+ after = Time.now
157
+ if after - before > 1
158
+ puts " - [resolver][resolver_specs_by_target][#{target}] : #{after - after_noded} : #{after - before} #{name} #{result.count} "
159
+ end
160
+ result
161
+ end
162
+
163
+ grouped = specs.
164
+ group_by(&:first)
165
+ # resolver_specs_by_target[target] = specs.
166
+ # group_by(&:first).
167
+ resolver_specs_by_target[target] = grouped.
168
+ map do |vertex, spec_test_only_tuples|
169
+ # puts " - #{vertex.name}\t#{spec_test_only_tuples.count}\t#{vertex.recursive_predecessors.count}" if vertex.name == 'SDWebImage/Core'
170
+ test_only = spec_test_only_tuples.all? { |tuple| tuple[1] }
171
+ payload = vertex.payload
172
+ spec_source = payload.respond_to?(:spec_source) && payload.spec_source
173
+ result = ResolverSpecification.new(payload, test_only, spec_source)
174
+ result
175
+ end.
176
+ sort_by(&:name)
177
+ end
178
+ end
179
+ end
180
+
181
+ #-------------------------------------------------------------------------#
182
+
183
+ public
184
+
185
+ # @!group Specification Provider
186
+
187
+ include Molinillo::SpecificationProvider
188
+
189
+ # Returns (and caches) the specification that satisfy the given dependency.
190
+ #
191
+ # @return [Array<Specification>] the specifications that satisfy the given
192
+ # `dependency`.
193
+ #
194
+ # @param [Dependency] dependency the dependency that is being searched for.
195
+ #
196
+ def search_for(dependency)
197
+ @search ||= {}
198
+ @search[dependency] ||= begin
199
+ locked_requirement = requirement_for_locked_pod_named(dependency.name)
200
+ additional_requirements = Array(locked_requirement)
201
+ specifications_for_dependency(dependency, additional_requirements)
202
+ end
203
+ @search[dependency].dup
204
+ end
205
+
206
+ # Returns the dependencies of `specification`.
207
+ #
208
+ # @return [Array<Specification>] all dependencies of `specification`.
209
+ #
210
+ # @param [Specification] specification the specification whose own
211
+ # dependencies are being asked for.
212
+ #
213
+ def dependencies_for(specification)
214
+ specification.all_dependencies.map do |dependency|
215
+ if dependency.root_name == Specification.root_name(specification.name)
216
+ dependency.dup.tap { |d| d.specific_version = specification.version }
217
+ else
218
+ dependency
219
+ end
220
+ end
221
+ end
222
+
223
+ # Returns the name for the given `dependency`.
224
+ #
225
+ # @return [String] the name for the given `dependency`.
226
+ #
227
+ # @param [Dependency] dependency the dependency whose name is being
228
+ # queried.
229
+ #
230
+ def name_for(dependency)
231
+ dependency.name
232
+ end
233
+
234
+ # @return [String] the user-facing name for a {Podfile}.
235
+ #
236
+ def name_for_explicit_dependency_source
237
+ 'Podfile'
238
+ end
239
+
240
+ # @return [String] the user-facing name for a {Lockfile}.
241
+ #
242
+ def name_for_locking_dependency_source
243
+ 'Podfile.lock'
244
+ end
245
+
246
+ # Determines whether the given `requirement` is satisfied by the given
247
+ # `spec`, in the context of the current `activated` dependency graph.
248
+ #
249
+ # @return [Boolean] whether `requirement` is satisfied by `spec` in the
250
+ # context of the current `activated` dependency graph.
251
+ #
252
+ # @param [Dependency] requirement the dependency in question.
253
+ #
254
+ # @param [Molinillo::DependencyGraph] activated the current dependency
255
+ # graph in the resolution process.
256
+ #
257
+ # @param [Specification] spec the specification in question.
258
+ #
259
+ def requirement_satisfied_by?(requirement, activated, spec)
260
+ version = spec.version
261
+ return false unless requirement.requirement.satisfied_by?(version)
262
+ shared_possibility_versions, prerelease_requirement = possibility_versions_for_root_name(requirement, activated)
263
+ return false if !shared_possibility_versions.empty? && !shared_possibility_versions.include?(version)
264
+ return false if version.prerelease? && !prerelease_requirement
265
+ return false unless spec_is_platform_compatible?(activated, requirement, spec)
266
+ true
267
+ end
268
+
269
+ def possibility_versions_for_root_name(requirement, activated)
270
+ prerelease_requirement = requirement.prerelease? || requirement.external_source
271
+ existing = activated.vertices.values.flat_map do |vertex|
272
+ next unless vertex.payload
273
+ next unless Specification.root_name(vertex.name) == requirement.root_name
274
+
275
+ prerelease_requirement ||= vertex.requirements.any? { |r| r.prerelease? || r.external_source }
276
+
277
+ if vertex.payload.respond_to?(:possibilities)
278
+ vertex.payload.possibilities.map(&:version)
279
+ else
280
+ vertex.payload.version
281
+ end
282
+ end.compact
283
+
284
+ [existing, prerelease_requirement]
285
+ end
286
+ private :possibility_versions_for_root_name
287
+
288
+ # Sort dependencies so that the ones that are easiest to resolve are first.
289
+ # Easiest to resolve is (usually) defined by:
290
+ # 1) Is this dependency already activated?
291
+ # 2) How relaxed are the requirements?
292
+ # 3) Are there any conflicts for this dependency?
293
+ # 4) How many possibilities are there to satisfy this dependency?
294
+ #
295
+ # @return [Array<Dependency>] the sorted dependencies.
296
+ #
297
+ # @param [Array<Dependency>] dependencies the unsorted dependencies.
298
+ #
299
+ # @param [Molinillo::DependencyGraph] activated the dependency graph of
300
+ # currently activated specs.
301
+ #
302
+ # @param [{String => Array<Conflict>}] conflicts the current conflicts.
303
+ #
304
+ def sort_dependencies(dependencies, activated, conflicts)
305
+ dependencies.sort_by do |dependency|
306
+ name = name_for(dependency)
307
+ [
308
+ activated.vertex_named(name).payload ? 0 : 1,
309
+ dependency.external_source ? 0 : 1,
310
+ dependency.prerelease? ? 0 : 1,
311
+ conflicts[name] ? 0 : 1,
312
+ search_for(dependency).count,
313
+ ]
314
+ end
315
+ end
316
+
317
+ #-------------------------------------------------------------------------#
318
+
319
+ public
320
+
321
+ # @!group Resolver UI
322
+
323
+ include Molinillo::UI
324
+
325
+ # The UI object the resolver should use for displaying user-facing output.
326
+ #
327
+ # @return [UserInterface] the normal CocoaPods UI object.
328
+ #
329
+ def output
330
+ UI
331
+ end
332
+
333
+ # Called before resolution starts.
334
+ #
335
+ # Completely silence this, as we show nothing.
336
+ #
337
+ # @return [Void]
338
+ #
339
+ def before_resolution
340
+ end
341
+
342
+ # Called after resolution ends.
343
+ #
344
+ # Completely silence this, as we show nothing.
345
+ #
346
+ # @return [Void]
347
+ #
348
+ def after_resolution
349
+ end
350
+
351
+ # Called during resolution to indicate progress.
352
+ #
353
+ # Completely silence this, as we show nothing.
354
+ #
355
+ # @return [Void]
356
+ #
357
+ def indicate_progress
358
+ end
359
+
360
+ #-------------------------------------------------------------------------#
361
+
362
+ private
363
+
364
+ # !@ Resolution context
365
+
366
+ # @return [Hash<String => Set>] A cache that keeps tracks of the sets
367
+ # loaded by the resolution process.
368
+ #
369
+ # @note Sets store the resolved dependencies and return the highest
370
+ # available specification found in the sources. This is done
371
+ # globally and not per target definition because there can be just
372
+ # one Pod installation, so different version of the same Pods for
373
+ # target definitions are not allowed.
374
+ #
375
+ attr_reader :cached_sets
376
+
377
+ #-------------------------------------------------------------------------#
378
+
379
+ private
380
+
381
+ # @!group Private helpers
382
+
383
+ # Returns available specifications which satisfy requirements of given dependency
384
+ # and additional requirements.
385
+ #
386
+ # @param [Dependency] dependency
387
+ # The dependency whose requirements will be satisfied.
388
+ #
389
+ # @param [Array<Requirement>] additional_requirements
390
+ # List of additional requirements which should also be satisfied.
391
+ #
392
+ # @return [Array<Specification>] List of specifications satisfying given requirements.
393
+ #
394
+ def specifications_for_dependency(dependency, additional_requirements = [])
395
+ requirement = Requirement.new(dependency.requirement.as_list + additional_requirements.flat_map(&:as_list))
396
+ find_cached_set(dependency).
397
+ all_specifications(installation_options.warn_for_multiple_pod_sources).
398
+ select { |s| requirement.satisfied_by? s.version }.
399
+ map { |s| s.subspec_by_name(dependency.name, false, true) }.
400
+ compact
401
+ end
402
+
403
+ # @return [Set] Loads or returns a previously initialized set for the Pod
404
+ # of the given dependency.
405
+ #
406
+ # @param [Dependency] dependency
407
+ # The dependency for which the set is needed.
408
+ #
409
+ # @return [Set] the cached set for a given dependency.
410
+ #
411
+ def find_cached_set(dependency)
412
+ name = dependency.root_name
413
+ unless cached_sets[name]
414
+ if dependency.external_source
415
+ spec = sandbox.specification(name)
416
+ unless spec
417
+ raise StandardError, '[Bug] Unable to find the specification ' \
418
+ "for `#{dependency}`."
419
+ end
420
+ set = Specification::Set::External.new(spec)
421
+ else
422
+ set = create_set_from_sources(dependency)
423
+ end
424
+ cached_sets[name] = set
425
+ unless set
426
+ raise Molinillo::NoSuchDependencyError.new(dependency) # rubocop:disable Style/RaiseArgs
427
+ end
428
+ end
429
+ cached_sets[name]
430
+ end
431
+
432
+ # @return [Requirement, Nil]
433
+ # The {Requirement} that locks the dependency with name `name` in
434
+ # {#locked_dependencies}.
435
+ #
436
+ def requirement_for_locked_pod_named(name)
437
+ if vertex = locked_dependencies.vertex_named(name)
438
+ if dependency = vertex.payload
439
+ dependency.requirement
440
+ end
441
+ end
442
+ end
443
+
444
+ # @return [Set] Creates a set for the Pod of the given dependency from the
445
+ # sources. The set will contain all versions from all sources that
446
+ # include the Pod.
447
+ #
448
+ # @param [Dependency] dependency
449
+ # The dependency for which the set is needed.
450
+ #
451
+ def create_set_from_sources(dependency)
452
+ aggregate_for_dependency(dependency).search(dependency)
453
+ end
454
+
455
+ # @return [Source::Aggregate] The aggregate of the {#sources}.
456
+ #
457
+ def aggregate_for_dependency(dependency)
458
+ if dependency && dependency.podspec_repo
459
+ return Config.instance.sources_manager.aggregate_for_dependency(dependency)
460
+ else
461
+ @aggregate ||= Source::Aggregate.new(sources)
462
+ end
463
+ end
464
+
465
+ # Ensures that a specification is compatible with the platform of a target.
466
+ #
467
+ # @raise If the specification is not supported by the target.
468
+ #
469
+ # @return [void]
470
+ #
471
+ def validate_platform(spec, target)
472
+ return unless target_platform = target.platform
473
+ unless spec.available_platforms.any? { |p| target_platform.to_sym == p.to_sym }
474
+ raise Informative, "The platform of the target `#{target.name}` " \
475
+ "(#{target.platform}) is not compatible with `#{spec}`, which does " \
476
+ "not support `#{target.platform.name}`."
477
+ end
478
+ end
479
+
480
+ # Handles errors that come out of a {Molinillo::Resolver}.
481
+ #
482
+ # @todo The check for version conflicts coming from the {Lockfile}
483
+ # requiring a pre-release version can be deleted for version 1.0,
484
+ # as it is a migration step for Lockfiles coming from CocoaPods
485
+ # versions before `0.35.0`.
486
+ #
487
+ # @return [void]
488
+ #
489
+ # @param [Molinillo::ResolverError] error
490
+ #
491
+ def handle_resolver_error(error)
492
+ message = error.message
493
+ type = Informative
494
+ case error
495
+ when Molinillo::VersionConflict
496
+ message = error.message_with_trees(
497
+ :solver_name => 'CocoaPods',
498
+ :possibility_type => 'pod',
499
+ :version_for_spec => lambda(&:version),
500
+ :additional_message_for_conflict => lambda do |o, name, conflict|
501
+ local_pod_parent = conflict.requirement_trees.flatten.reverse.find(&:local?)
502
+ lockfile_reqs = conflict.requirements[name_for_locking_dependency_source]
503
+ if lockfile_reqs && lockfile_reqs.last && lockfile_reqs.last.prerelease? && !conflict.existing
504
+ o << "\nDue to the previous naïve CocoaPods resolver, " \
505
+ "you were using a pre-release version of `#{name}`, " \
506
+ 'without explicitly asking for a pre-release version, which now leads to a conflict. ' \
507
+ 'Please decide to either use that pre-release version by adding the ' \
508
+ 'version requirement to your Podfile ' \
509
+ "(e.g. `pod '#{name}', '#{lockfile_reqs.map(&:requirement).join("', '")}'`) " \
510
+ "or revert to a stable version by running `pod update #{name}`."
511
+ elsif local_pod_parent && !specifications_for_dependency(conflict.requirement).empty? && !conflict.possibility && conflict.locked_requirement
512
+ # Conflict was caused by a requirement from a local dependency.
513
+ # Tell user to use `pod update`.
514
+ o << "\nIt seems like you've changed the constraints of dependency `#{name}` " \
515
+ "inside your development pod `#{local_pod_parent.name}`.\nYou should run `pod update #{name}` to apply " \
516
+ "changes you've made."
517
+ elsif (conflict.possibility && conflict.possibility.version.prerelease?) &&
518
+ (conflict.requirement && !(
519
+ conflict.requirement.prerelease? ||
520
+ conflict.requirement.external_source)
521
+ )
522
+ # Conflict was caused by not specifying an explicit version for the requirement #[name],
523
+ # and there is no available stable version satisfying constraints for the requirement.
524
+ o << "\nThere are only pre-release versions available satisfying the following requirements:\n"
525
+ conflict.requirements.values.flatten.uniq.each do |r|
526
+ unless search_for(r).empty?
527
+ o << "\n\t'#{name}', '#{r.requirement}'\n"
528
+ end
529
+ end
530
+ o << "\nYou should explicitly specify the version in order to install a pre-release version"
531
+ elsif !conflict.existing
532
+ conflicts = conflict.requirements.values.flatten.uniq
533
+ found_conflicted_specs = conflicts.reject { |c| search_for(c).empty? }
534
+ if found_conflicted_specs.empty?
535
+ # There are no existing specification inside any of the spec repos with given requirements.
536
+ type = NoSpecFoundError
537
+ dependencies = conflicts.count == 1 ? 'dependency' : 'dependencies'
538
+ o << "\nNone of your spec sources contain a spec satisfying "\
539
+ "the #{dependencies}: `#{conflicts.join(', ')}`." \
540
+ "\n\nYou have either:"
541
+ unless specs_updated?
542
+ o << "\n * out-of-date source repos which you can update with `pod repo update` or with `pod install --repo-update`."
543
+ end
544
+ o << "\n * mistyped the name or version." \
545
+ "\n * not added the source repo that hosts the Podspec to your Podfile." \
546
+ "\n\nNote: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by default."
547
+
548
+ else
549
+ o << "\nSpecs satisfying the `#{conflicts.join(', ')}` dependency were found, " \
550
+ 'but they required a higher minimum deployment target.'
551
+ end
552
+ end
553
+ end,
554
+ )
555
+ end
556
+ raise type.new(message).tap { |e| e.set_backtrace(error.backtrace) }
557
+ end
558
+
559
+ # Returns whether the given spec is platform-compatible with the dependency
560
+ # graph, taking into account the dependency that has required the spec.
561
+ #
562
+ # @param [Molinillo::DependencyGraph] dependency_graph
563
+ #
564
+ # @param [Dependency] dependency
565
+ #
566
+ # @param [Specification] specification
567
+ #
568
+ # @return [Bool]
569
+ def spec_is_platform_compatible?(dependency_graph, dependency, spec)
570
+ # puts "spec_is_platform_compatible -- #{dependency.name}"
571
+ vertex = dependency_graph.vertex_named(dependency.name)
572
+ before_predecessor = Time.now
573
+ predecessors = vertex.recursive_predecessors.select(&:root)
574
+ after_predecessor = Time.now
575
+ # if after_predecessor - before_predecessor > 1000
576
+ # puts " - predecessor : #{after_predecessor - before_predecessor}"
577
+ # end
578
+ predecessors << vertex if vertex.root?
579
+ platforms_to_satisfy = predecessors.flat_map(&:explicit_requirements).flat_map { |r| @platforms_by_dependency[r] }.uniq
580
+
581
+ available_platforms = spec.available_platforms
582
+
583
+ result = platforms_to_satisfy.all? do |platform_to_satisfy|
584
+ available_platforms.all? do |spec_platform|
585
+ next true unless spec_platform.name == platform_to_satisfy.name
586
+ platform_to_satisfy.supports?(spec_platform)
587
+ end
588
+ end
589
+ # end_of_spec_is_platform_compatible = Time.now
590
+ # puts " - end : #{end_of_spec_is_platform_compatible - after_predecessor}"
591
+ result
592
+ end
593
+
594
+ # Returns the target-appropriate nodes that are `successors` of `node`,
595
+ # rejecting those that are scoped by target platform and have incompatible
596
+ # targets.
597
+ #
598
+ # @return [Array<Molinillo::DependencyGraph::Vertex>]
599
+ # An array of target-appropriate nodes whose `payload`s are
600
+ # dependencies for `target`.
601
+ #
602
+ def valid_dependencies_for_target_from_node(target, dependencies, node)
603
+ # 已记录则直接返回,否则
604
+ dependencies[[node.name, target.platform]] ||= begin
605
+ validate_platform(node.payload, target)
606
+ dependency_nodes = []
607
+ node.outgoing_edges.each do |edge|
608
+ next unless edge_is_valid_for_target_platform?(edge, target.platform)
609
+ dependency_nodes << edge.destination
610
+ end
611
+
612
+ result = dependency_nodes.flat_map do |item|
613
+ valid_dependencies_for_target_from_node(target, dependencies, item)
614
+ end | dependency_nodes
615
+ result
616
+ end
617
+ end
618
+
619
+ # Whether the given `edge` should be followed to find dependencies for the
620
+ # given `target_platform`.
621
+ #
622
+ # @return [Bool]
623
+ #
624
+ def edge_is_valid_for_target_platform?(edge, target_platform)
625
+ requirement_name = edge.requirement.name
626
+
627
+ edge.origin.payload.all_dependencies(target_platform).any? do |dep|
628
+ dep.name == requirement_name
629
+ end
630
+ end
631
+ end
632
+ end