cocoapods 0.37.2 → 0.38.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +136 -1
  3. data/lib/cocoapods.rb +0 -5
  4. data/lib/cocoapods/command.rb +3 -0
  5. data/lib/cocoapods/command/cache.rb +28 -0
  6. data/lib/cocoapods/command/cache/clean.rb +90 -0
  7. data/lib/cocoapods/command/cache/list.rb +69 -0
  8. data/lib/cocoapods/command/lib.rb +11 -4
  9. data/lib/cocoapods/command/list.rb +4 -4
  10. data/lib/cocoapods/command/outdated.rb +1 -10
  11. data/lib/cocoapods/command/project.rb +3 -2
  12. data/lib/cocoapods/command/spec.rb +0 -17
  13. data/lib/cocoapods/command/spec/cat.rb +1 -1
  14. data/lib/cocoapods/command/spec/create.rb +1 -0
  15. data/lib/cocoapods/command/spec/edit.rb +1 -1
  16. data/lib/cocoapods/command/spec/lint.rb +10 -4
  17. data/lib/cocoapods/config.rb +6 -0
  18. data/lib/cocoapods/downloader/cache.rb +48 -1
  19. data/lib/cocoapods/executable.rb +27 -6
  20. data/lib/cocoapods/gem_version.rb +1 -1
  21. data/lib/cocoapods/generator/copy_resources_script.rb +1 -0
  22. data/lib/cocoapods/generator/embed_frameworks_script.rb +23 -28
  23. data/lib/cocoapods/generator/header.rb +5 -1
  24. data/lib/cocoapods/generator/umbrella_header.rb +1 -1
  25. data/lib/cocoapods/generator/xcconfig/aggregate_xcconfig.rb +139 -33
  26. data/lib/cocoapods/generator/xcconfig/private_pod_xcconfig.rb +2 -2
  27. data/lib/cocoapods/generator/xcconfig/xcconfig_helper.rb +3 -3
  28. data/lib/cocoapods/installer.rb +64 -109
  29. data/lib/cocoapods/installer/analyzer.rb +167 -336
  30. data/lib/cocoapods/installer/analyzer/analysis_result.rb +46 -0
  31. data/lib/cocoapods/installer/analyzer/specs_state.rb +76 -0
  32. data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +41 -0
  33. data/lib/cocoapods/installer/analyzer/target_inspector.rb +203 -0
  34. data/lib/cocoapods/installer/file_references_installer.rb +48 -13
  35. data/lib/cocoapods/installer/podfile_validator.rb +86 -0
  36. data/lib/cocoapods/installer/{hooks_context.rb → post_install_hooks_context.rb} +3 -3
  37. data/lib/cocoapods/installer/pre_install_hooks_context.rb +41 -0
  38. data/lib/cocoapods/installer/target_installer.rb +1 -7
  39. data/lib/cocoapods/installer/target_installer/aggregate_target_installer.rb +15 -17
  40. data/lib/cocoapods/installer/target_installer/pod_target_installer.rb +4 -4
  41. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +16 -16
  42. data/lib/cocoapods/sandbox/file_accessor.rb +20 -2
  43. data/lib/cocoapods/sandbox/path_list.rb +15 -13
  44. data/lib/cocoapods/sandbox/podspec_finder.rb +1 -0
  45. data/lib/cocoapods/sources_manager.rb +2 -0
  46. data/lib/cocoapods/target.rb +7 -37
  47. data/lib/cocoapods/target/aggregate_target.rb +25 -1
  48. data/lib/cocoapods/target/pod_target.rb +106 -10
  49. data/lib/cocoapods/user_interface.rb +26 -0
  50. data/lib/cocoapods/user_interface/error_report.rb +6 -0
  51. data/lib/cocoapods/validator.rb +22 -0
  52. metadata +21 -16
  53. data/lib/cocoapods/generator/target_environment_header.rb +0 -171
  54. data/lib/cocoapods/hooks/installer_representation.rb +0 -133
  55. data/lib/cocoapods/hooks/library_representation.rb +0 -93
  56. data/lib/cocoapods/hooks/pod_representation.rb +0 -70
@@ -60,13 +60,13 @@ module Pod
60
60
  # 'USE_HEADERMAP' => 'NO'
61
61
  }
62
62
 
63
- if target.requires_frameworks?
63
+ if target.requires_frameworks? && target.scoped?
64
64
  # Only quote the FRAMEWORK_SEARCH_PATHS entry, because it’s a setting that takes multiple values.
65
65
  # In addition, quoting CONFIGURATION_BUILD_DIR would make it be interpreted as a relative path.
66
66
  build_settings = {
67
67
  'PODS_FRAMEWORK_BUILD_PATH' => target.configuration_build_dir,
68
- 'CONFIGURATION_BUILD_DIR' => '$PODS_FRAMEWORK_BUILD_PATH',
69
68
  'FRAMEWORK_SEARCH_PATHS' => '"$PODS_FRAMEWORK_BUILD_PATH"',
69
+ 'CONFIGURATION_BUILD_DIR' => '$PODS_FRAMEWORK_BUILD_PATH',
70
70
  }
71
71
  config.merge!(build_settings)
72
72
  end
@@ -33,7 +33,7 @@ module Pod
33
33
  #
34
34
  def self.default_ld_flags(target)
35
35
  ld_flags = '-ObjC'
36
- if target.target_definition.podfile.set_arc_compatibility_flag? &&
36
+ if target.podfile.set_arc_compatibility_flag? &&
37
37
  target.spec_consumers.any?(&:requires_arc?)
38
38
  ld_flags << ' -fobjc-arc'
39
39
  end
@@ -70,7 +70,7 @@ module Pod
70
70
  # The xcconfig to edit.
71
71
  #
72
72
  def self.add_spec_build_settings_to_xcconfig(consumer, xcconfig)
73
- xcconfig.merge!(consumer.xcconfig)
73
+ xcconfig.merge!(consumer.pod_target_xcconfig)
74
74
  xcconfig.libraries.merge(consumer.libraries)
75
75
  xcconfig.frameworks.merge(consumer.frameworks)
76
76
  xcconfig.weak_frameworks.merge(consumer.weak_frameworks)
@@ -189,7 +189,7 @@ module Pod
189
189
  search_paths = xcconfig.attributes['FRAMEWORK_SEARCH_PATHS'] ||= ''
190
190
  search_paths_to_add = []
191
191
  search_paths_to_add << '$(inherited)'
192
- if platform == :ios
192
+ if platform == :ios || platform == :watchos
193
193
  search_paths_to_add << '"$(SDKROOT)/Developer/Library/Frameworks"'
194
194
  else
195
195
  search_paths_to_add << '"$(DEVELOPER_LIBRARY_DIR)/Frameworks"'
@@ -31,8 +31,10 @@ module Pod
31
31
  autoload :AggregateTargetInstaller, 'cocoapods/installer/target_installer/aggregate_target_installer'
32
32
  autoload :Analyzer, 'cocoapods/installer/analyzer'
33
33
  autoload :FileReferencesInstaller, 'cocoapods/installer/file_references_installer'
34
- autoload :HooksContext, 'cocoapods/installer/hooks_context'
34
+ autoload :PostInstallHooksContext, 'cocoapods/installer/post_install_hooks_context'
35
+ autoload :PreInstallHooksContext, 'cocoapods/installer/pre_install_hooks_context'
35
36
  autoload :Migrator, 'cocoapods/installer/migrator'
37
+ autoload :PodfileValidator, 'cocoapods/installer/podfile_validator'
36
38
  autoload :PodSourceInstaller, 'cocoapods/installer/pod_source_installer'
37
39
  autoload :PodSourcePreparer, 'cocoapods/installer/pod_source_preparer'
38
40
  autoload :PodTargetInstaller, 'cocoapods/installer/target_installer/pod_target_installer'
@@ -106,12 +108,19 @@ module Pod
106
108
  sandbox.prepare
107
109
  ensure_plugins_are_installed!
108
110
  Migrator.migrate(sandbox)
111
+ run_plugins_pre_install_hooks
109
112
  end
110
113
  end
111
114
 
112
115
  def resolve_dependencies
116
+ analyzer = create_analyzer
117
+
118
+ UI.section 'Updating local specs repositories' do
119
+ analyzer.update_repositories
120
+ end unless config.skip_repo_update?
121
+
113
122
  UI.section 'Analyzing dependencies' do
114
- analyze
123
+ analyze(analyzer)
115
124
  validate_build_configurations
116
125
  prepare_for_legacy_compatibility
117
126
  clean_sandbox
@@ -122,7 +131,7 @@ module Pod
122
131
  UI.section 'Downloading dependencies' do
123
132
  create_file_accessors
124
133
  install_pod_sources
125
- run_pre_install_hooks
134
+ run_podfile_pre_install_hooks
126
135
  clean_pod_sources
127
136
  lock_pod_sources
128
137
  end
@@ -160,11 +169,19 @@ module Pod
160
169
  #
161
170
  attr_reader :names_of_pods_to_install
162
171
 
163
- # @return [Array<AggregateTarget>] The Podfile targets containing library
164
- # dependencies.
172
+ # @return [Array<AggregateTarget>] The model representations of an
173
+ # aggregation of pod targets generated for a target definition
174
+ # in the Podfile as result of the analyzer.
165
175
  #
166
176
  attr_reader :aggregate_targets
167
177
 
178
+ # @return [Array<PodTarget>] The model representations of pod targets
179
+ # generated as result of the analyzer.
180
+ #
181
+ def pod_targets
182
+ aggregate_targets.map(&:pod_targets).flatten.uniq
183
+ end
184
+
168
185
  # @return [Array<Specification>] The specifications that where installed.
169
186
  #
170
187
  attr_accessor :installed_specs
@@ -179,33 +196,27 @@ module Pod
179
196
  #
180
197
  # @return [void]
181
198
  #
182
- # @note The warning about the version of the Lockfile doesn't use the
183
- # `UI.warn` method because it prints the output only at the end
184
- # of the installation. At that time CocoaPods could have crashed.
185
- #
186
- def analyze
187
- if lockfile && lockfile.cocoapods_version > Version.new(VERSION)
188
- STDERR.puts '[!] The version of CocoaPods used to generate ' \
189
- "the lockfile (#{lockfile.cocoapods_version}) is "\
190
- "higher than the version of the current executable (#{VERSION}). " \
191
- 'Incompatibility issues may arise.'.yellow
192
- end
193
-
194
- analyzer = Analyzer.new(sandbox, podfile, lockfile)
199
+ def analyze(analyzer = create_analyzer)
195
200
  analyzer.update = update
196
201
  @analysis_result = analyzer.analyze
197
202
  @aggregate_targets = analyzer.result.targets
198
203
  end
199
204
 
205
+ def create_analyzer
206
+ Analyzer.new(sandbox, podfile, lockfile)
207
+ end
208
+
200
209
  # Ensures that the white-listed build configurations are known to prevent
201
210
  # silent typos.
202
211
  #
203
212
  # @raise If an unknown user configuration is found.
204
213
  #
205
214
  def validate_build_configurations
206
- whitelisted_configs = pod_targets.map do |target|
207
- target.target_definition.all_whitelisted_configurations.map(&:downcase)
208
- end.flatten.uniq
215
+ whitelisted_configs = pod_targets.
216
+ flat_map(&:target_definitions).
217
+ flat_map(&:all_whitelisted_configurations).
218
+ map(&:downcase).
219
+ uniq
209
220
  all_user_configurations = analysis_result.all_user_build_configurations.keys.map(&:downcase)
210
221
 
211
222
  remainder = whitelisted_configs - all_user_configurations
@@ -251,16 +262,14 @@ module Pod
251
262
  # created by the Pod source installer as well.
252
263
  #
253
264
  def create_file_accessors
254
- aggregate_targets.each do |target|
255
- target.pod_targets.each do |pod_target|
256
- pod_root = sandbox.pod_dir(pod_target.root_spec.name)
257
- path_list = Sandbox::PathList.new(pod_root)
258
- file_accessors = pod_target.specs.map do |spec|
259
- Sandbox::FileAccessor.new(path_list, spec.consumer(pod_target.platform))
260
- end
261
- pod_target.file_accessors ||= []
262
- pod_target.file_accessors.concat(file_accessors)
265
+ pod_targets.each do |pod_target|
266
+ pod_root = sandbox.pod_dir(pod_target.root_spec.name)
267
+ path_list = Sandbox::PathList.new(pod_root)
268
+ file_accessors = pod_target.specs.map do |spec|
269
+ Sandbox::FileAccessor.new(path_list, spec.consumer(pod_target.platform))
263
270
  end
271
+ pod_target.file_accessors ||= []
272
+ pod_target.file_accessors.concat(file_accessors)
264
273
  end
265
274
  end
266
275
 
@@ -342,7 +351,7 @@ module Pod
342
351
  def determine_dependency_product_types
343
352
  aggregate_targets.each do |aggregate_target|
344
353
  aggregate_target.pod_targets.each do |pod_target|
345
- pod_target.host_requires_frameworks = aggregate_target.requires_frameworks?
354
+ pod_target.host_requires_frameworks ||= aggregate_target.requires_frameworks?
346
355
  end
347
356
  end
348
357
  end
@@ -371,7 +380,7 @@ module Pod
371
380
  aggregate_target.user_build_configurations.keys.each do |config|
372
381
  pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
373
382
 
374
- dependencies = pod_targets.flat_map(&:dependencies)
383
+ dependencies = pod_targets.select(&:should_build?).flat_map(&:dependencies)
375
384
  dependended_upon_targets = pod_targets.select { |t| dependencies.include?(t.pod_name) && !t.should_build? }
376
385
 
377
386
  static_libs = dependended_upon_targets.flat_map(&:file_accessors).flat_map do |fa|
@@ -403,6 +412,15 @@ module Pod
403
412
  end
404
413
  end
405
414
 
415
+ # Runs the registered callbacks for the plugins pre install hooks.
416
+ #
417
+ # @return [void]
418
+ #
419
+ def run_plugins_pre_install_hooks
420
+ context = PreInstallHooksContext.generate(sandbox, podfile, lockfile)
421
+ HooksManager.run(:pre_install, context, podfile.plugins)
422
+ end
423
+
406
424
  # Performs any post-installation actions
407
425
  #
408
426
  # @return [void]
@@ -415,7 +433,7 @@ module Pod
415
433
  # Runs the registered callbacks for the plugins post install hooks.
416
434
  #
417
435
  def run_plugins_post_install_hooks
418
- context = HooksContext.generate(sandbox, aggregate_targets)
436
+ context = PostInstallHooksContext.generate(sandbox, aggregate_targets)
419
437
  HooksManager.run(:post_install, context, podfile.plugins)
420
438
  end
421
439
 
@@ -491,9 +509,11 @@ module Pod
491
509
  platforms = aggregate_targets.map(&:platform)
492
510
  osx_deployment_target = platforms.select { |p| p.name == :osx }.map(&:deployment_target).min
493
511
  ios_deployment_target = platforms.select { |p| p.name == :ios }.map(&:deployment_target).min
512
+ watchos_deployment_target = platforms.select { |p| p.name == :watchos }.map(&:deployment_target).min
494
513
  @pods_project.build_configurations.each do |build_configuration|
495
514
  build_configuration.build_settings['MACOSX_DEPLOYMENT_TARGET'] = osx_deployment_target.to_s if osx_deployment_target
496
515
  build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = ios_deployment_target.to_s if ios_deployment_target
516
+ build_configuration.build_settings['WATCHOS_DEPLOYMENT_TARGET'] = watchos_deployment_target.to_s if watchos_deployment_target
497
517
  build_configuration.build_settings['STRIP_INSTALLED_PRODUCT'] = 'NO'
498
518
  build_configuration.build_settings['CLANG_ENABLE_OBJC_ARC'] = 'YES'
499
519
  end
@@ -519,7 +539,7 @@ module Pod
519
539
  def install_libraries
520
540
  UI.message '- Installing targets' do
521
541
  pod_targets.sort_by(&:name).each do |pod_target|
522
- next if pod_target.target_definition.dependencies.empty?
542
+ next if pod_target.target_definitions.flat_map(&:dependencies).empty?
523
543
  target_installer = PodTargetInstaller.new(sandbox, pod_target)
524
544
  target_installer.install!
525
545
  end
@@ -610,12 +630,8 @@ module Pod
610
630
  # @return [void]
611
631
  #
612
632
  def share_development_pod_schemes
613
- development_pod_targets = sandbox.development_pods.keys.map do |pod|
614
- pods_project.targets.select { |target| target.name =~ /^Pods-.*-#{pod}$/ }
615
- end.flatten
616
-
617
- development_pod_targets.each do |pod_target|
618
- Xcodeproj::XCScheme.share_scheme(pods_project.path, pod_target.name)
633
+ development_pod_targets.select(&:should_build?).each do |pod_target|
634
+ Xcodeproj::XCScheme.share_scheme(pods_project.path, pod_target.label)
619
635
  end
620
636
  end
621
637
 
@@ -671,7 +687,7 @@ module Pod
671
687
  #
672
688
  # @return [void]
673
689
  #
674
- def run_pre_install_hooks
690
+ def run_podfile_pre_install_hooks
675
691
  UI.message '- Running pre install hooks' do
676
692
  executed = run_podfile_pre_install_hook
677
693
  UI.message '- Podfile' if executed
@@ -685,7 +701,7 @@ module Pod
685
701
  # @return [Bool] Whether the hook was run.
686
702
  #
687
703
  def run_podfile_pre_install_hook
688
- podfile.pre_install!(installer_rep)
704
+ podfile.pre_install!(self)
689
705
  rescue => e
690
706
  raise Informative, 'An error occurred while processing the pre-install ' \
691
707
  'hook of the Podfile.' \
@@ -713,7 +729,7 @@ module Pod
713
729
  # @return [Bool] Whether the hook was run.
714
730
  #
715
731
  def run_podfile_post_install_hook
716
- podfile.post_install!(installer_rep)
732
+ podfile.post_install!(self)
717
733
  rescue => e
718
734
  raise Informative, 'An error occurred while processing the post-install ' \
719
735
  'hook of the Podfile.' \
@@ -724,76 +740,15 @@ module Pod
724
740
 
725
741
  public
726
742
 
727
- # @!group Hooks Data
728
-
729
- # Creates a hook representation for this installer.
730
- #
731
- # @return [InstallerRepresentation]
732
- #
733
- def installer_rep
734
- Hooks::InstallerRepresentation.new(self)
735
- end
736
-
737
- # Creates a hook representation for a Pod.
743
+ # @return [Array<Library>] The targets of the development pods generated by
744
+ # the installation process.
738
745
  #
739
- # @param [String] pod
740
- # The name of the pod.
741
- #
742
- # @return [PodRepresentation]
743
- #
744
- def pod_rep(pod)
745
- all_file_accessors = pod_targets.map(&:file_accessors).flatten.compact
746
- file_accessors = all_file_accessors.select { |fa| fa.spec.root.name == pod }
747
- Hooks::PodRepresentation.new(pod, file_accessors)
748
- end
749
-
750
- # Creates a hook representation for a given aggregate target.
751
- #
752
- # @param [AggregateTarget] aggregate_target
753
- # the aggregate target
754
- #
755
- # @return [LibraryRepresentation]
756
- #
757
- def library_rep(aggregate_target)
758
- Hooks::LibraryRepresentation.new(sandbox, aggregate_target)
759
- end
760
-
761
- # Creates hook representations for all aggregate targets.
762
- #
763
- # @return [Array<LibraryRepresentation>]
764
- #
765
- def library_reps
766
- @library_reps ||= aggregate_targets.map { |lib| library_rep(lib) }
767
- end
768
-
769
- # Creates hook representations for all #root_specs.
770
- #
771
- # @return [Array<PodRepresentation>]
772
- #
773
- def pod_reps
774
- root_specs.sort_by(&:name).map { |spec| pod_rep(spec.name) }
775
- end
776
-
777
- # Returns the libraries which use the given specification.
778
- #
779
- # @param [Specification] spec
780
- # The specification for which the client libraries are needed.
781
- #
782
- # @return [Array<Library>] The library.
783
- #
784
- def libraries_using_spec(spec)
785
- aggregate_targets.select do |aggregate_target|
786
- aggregate_target.pod_targets.any? { |pod_target| pod_target.specs.include?(spec) }
746
+ def development_pod_targets
747
+ pod_targets.select do |pod_target|
748
+ sandbox.development_pods.keys.include?(pod_target.pod_name)
787
749
  end
788
750
  end
789
751
 
790
- # @return [Array<Library>] The libraries generated by the installation
791
- # process.
792
- #
793
- def pod_targets
794
- aggregate_targets.map(&:pod_targets).flatten
795
- end
796
-
797
752
  #-------------------------------------------------------------------------#
798
753
 
799
754
  private
@@ -6,9 +6,12 @@ module Pod
6
6
  class Analyzer
7
7
  include Config::Mixin
8
8
 
9
- autoload :SandboxAnalyzer, 'cocoapods/installer/analyzer/sandbox_analyzer'
10
-
9
+ autoload :AnalysisResult, 'cocoapods/installer/analyzer/analysis_result'
10
+ autoload :SandboxAnalyzer, 'cocoapods/installer/analyzer/sandbox_analyzer'
11
+ autoload :SpecsState, 'cocoapods/installer/analyzer/specs_state'
11
12
  autoload :LockingDependencyAnalyzer, 'cocoapods/installer/analyzer/locking_dependency_analyzer'
13
+ autoload :TargetInspectionResult, 'cocoapods/installer/analyzer/target_inspection_result'
14
+ autoload :TargetInspector, 'cocoapods/installer/analyzer/target_inspector'
12
15
 
13
16
  # @return [Sandbox] The sandbox where the Pods should be installed.
14
17
  #
@@ -37,7 +40,6 @@ module Pod
37
40
 
38
41
  @update = false
39
42
  @allow_pre_downloads = true
40
- @archs_by_target_def = {}
41
43
  end
42
44
 
43
45
  # Performs the analysis.
@@ -52,9 +54,14 @@ module Pod
52
54
  # @return [AnalysisResult]
53
55
  #
54
56
  def analyze(allow_fetches = true)
55
- update_repositories_if_needed if allow_fetches
57
+ validate_podfile!
58
+ validate_lockfile_version!
56
59
  @result = AnalysisResult.new
57
- compute_target_platforms
60
+ if config.integrate_targets?
61
+ @result.target_inspections = inspect_targets_to_integrate
62
+ else
63
+ verify_platforms_specified!
64
+ end
58
65
  @result.podfile_state = generate_podfile_state
59
66
  @locked_dependencies = generate_version_locking_dependencies
60
67
 
@@ -143,8 +150,30 @@ module Pod
143
150
 
144
151
  private
145
152
 
153
+ def validate_podfile!
154
+ validator = Installer::PodfileValidator.new(podfile)
155
+ validator.validate
156
+
157
+ unless validator.valid?
158
+ raise Informative, validator.message
159
+ end
160
+ end
161
+
146
162
  # @!group Analysis steps
147
163
 
164
+ # @note The warning about the version of the Lockfile doesn't use the
165
+ # `UI.warn` method because it prints the output only at the end
166
+ # of the installation. At that time CocoaPods could have crashed.
167
+ #
168
+ def validate_lockfile_version!
169
+ if lockfile && lockfile.cocoapods_version > Version.new(VERSION)
170
+ STDERR.puts '[!] The version of CocoaPods used to generate ' \
171
+ "the lockfile (#{lockfile.cocoapods_version}) is "\
172
+ "higher than the version of the current executable (#{VERSION}). " \
173
+ 'Incompatibility issues may arise.'.yellow
174
+ end
175
+ end
176
+
148
177
  # Compares the {Podfile} with the {Lockfile} in order to detect which
149
178
  # dependencies should be locked.
150
179
  #
@@ -174,32 +203,31 @@ module Pod
174
203
  end
175
204
  end
176
205
 
206
+ public
207
+
177
208
  # Updates the git source repositories unless the config indicates to skip it.
178
209
  #
179
- def update_repositories_if_needed
180
- unless config.skip_repo_update?
181
- UI.section 'Updating spec repositories' do
182
- sources.each do |source|
183
- if SourcesManager.git_repo?(source.repo)
184
- SourcesManager.update(source.name)
185
- else
186
- UI.message "Skipping `#{source.name}` update because the repository is not a git source repository."
187
- end
188
- end
210
+ def update_repositories
211
+ sources.each do |source|
212
+ if SourcesManager.git_repo?(source.repo)
213
+ SourcesManager.update(source.name)
214
+ else
215
+ UI.message "Skipping `#{source.name}` update because the repository is not a git source repository."
189
216
  end
190
217
  end
191
218
  end
192
219
 
193
- # Creates the models that represent the libraries generated by CocoaPods.
220
+ private
221
+
222
+ # Creates the models that represent the targets generated by CocoaPods.
194
223
  #
195
- # @return [Array<Target>] the generated libraries.
224
+ # @return [Array<AggregateTarget>]
196
225
  #
197
226
  def generate_targets
198
- targets = []
199
- result.specs_by_target.each do |target_definition, specs|
200
- targets << generate_target(target_definition, specs)
227
+ pod_targets = generate_pod_targets(result.specs_by_target)
228
+ result.specs_by_target.map do |target_definition, _|
229
+ generate_target(target_definition, pod_targets)
201
230
  end
202
- targets
203
231
  end
204
232
 
205
233
  # Setup the aggregate target for a single user target
@@ -207,26 +235,22 @@ module Pod
207
235
  # @param [TargetDefinition] target_definition
208
236
  # the target definition for the user target.
209
237
  #
210
- # @param [Array<Specification>] specs
211
- # the specifications that need to be installed grouped by the
212
- # given target definition.
238
+ # @param [Array<PodTarget>] pod_targets
239
+ # the pod targets, which were generated.
213
240
  #
214
241
  # @return [AggregateTarget]
215
242
  #
216
- def generate_target(target_definition, specs)
243
+ def generate_target(target_definition, pod_targets)
217
244
  target = AggregateTarget.new(target_definition, sandbox)
218
245
  target.host_requires_frameworks |= target_definition.uses_frameworks?
219
246
 
220
247
  if config.integrate_targets?
221
- project_path = compute_user_project_path(target_definition)
222
- user_project = Xcodeproj::Project.open(project_path)
223
- native_targets = compute_user_project_targets(target_definition, user_project)
224
-
225
- target.user_project_path = project_path
226
- target.client_root = project_path.dirname
227
- target.user_target_uuids = native_targets.map(&:uuid)
228
- target.user_build_configurations = compute_user_build_configurations(target_definition, native_targets)
229
- target.archs = @archs_by_target_def[target_definition]
248
+ target_inspection = result.target_inspections[target_definition]
249
+ target.user_project_path = target_inspection.project_path
250
+ target.client_root = target.user_project_path.dirname
251
+ target.user_target_uuids = target_inspection.project_target_uuids
252
+ target.user_build_configurations = target_inspection.build_configurations
253
+ target.archs = target_inspection.archs
230
254
  else
231
255
  target.client_root = config.installation_root
232
256
  target.user_target_uuids = []
@@ -236,48 +260,119 @@ module Pod
236
260
  end
237
261
  end
238
262
 
239
- target.pod_targets = generate_pod_targets(target, specs)
240
-
263
+ target.pod_targets = pod_targets.select do |pod_target|
264
+ pod_target.target_definitions.include?(target_definition)
265
+ end
241
266
  target
242
267
  end
243
268
 
244
- # Setup the pod targets for an aggregate target. Group specs and subspecs
245
- # by their root to create a {PodTarget} for each spec.
269
+ # Setup the pod targets for an aggregate target. Deduplicates resulting
270
+ # targets by grouping by grouping by platform and subspec by their root
271
+ # to create a {PodTarget} for each spec.
246
272
  #
247
- # @param [AggregateTarget] target
248
- # the aggregate target
273
+ # @param [Hash{Podfile::TargetDefinition => Array<Specification>}] specs_by_target
274
+ # the resolved specifications grouped by target.
249
275
  #
250
- # @param [Array<Specification>] specs
251
- # the specifications that need to be installed.
276
+ # @return [Array<PodTarget>]
277
+ #
278
+ def generate_pod_targets(specs_by_target)
279
+ if config.deduplicate_targets?
280
+ all_specs = specs_by_target.flat_map do |target_definition, dependent_specs|
281
+ dependent_specs.group_by(&:root).map do |root_spec, specs|
282
+ [root_spec, specs, target_definition]
283
+ end
284
+ end
285
+
286
+ distinct_targets = all_specs.each_with_object({}) do |dependency, hash|
287
+ root_spec, specs, target_definition = *dependency
288
+ hash[root_spec] ||= {}
289
+ (hash[root_spec][[specs, target_definition.platform]] ||= []) << target_definition
290
+ end
291
+
292
+ pod_targets = distinct_targets.flat_map do |_, targets_by_distinctors|
293
+ if targets_by_distinctors.count > 1
294
+ # There are different sets of subspecs or the spec is used across different platforms
295
+ targets_by_distinctors.map do |distinctor, target_definitions|
296
+ specs, _ = *distinctor
297
+ generate_pod_target(target_definitions, specs, :scoped => true)
298
+ end
299
+ else
300
+ (specs, _), target_definitions = targets_by_distinctors.first
301
+ generate_pod_target(target_definitions, specs)
302
+ end
303
+ end
304
+
305
+ # A `PodTarget` can't be deduplicated if any of its
306
+ # transitive dependencies can't be deduplicated.
307
+ pod_targets.flat_map do |target|
308
+ dependent_targets = transitive_dependencies_for_pod_target(target, pod_targets)
309
+ if dependent_targets.any?(&:scoped?)
310
+ target.scoped
311
+ else
312
+ target
313
+ end
314
+ end
315
+ else
316
+ specs_by_target.flat_map do |target_definition, specs|
317
+ grouped_specs = specs.group_by.group_by(&:root).values.uniq
318
+ grouped_specs.flat_map do |pod_specs|
319
+ generate_pod_target([target_definition], pod_specs, :scoped => true)
320
+ end
321
+ end
322
+ end
323
+ end
324
+
325
+ # Finds the names of the Pods upon which the given target _transitively_
326
+ # depends.
327
+ #
328
+ # @note: This is implemented in the analyzer, because we don't have to
329
+ # care about the requirements after dependency resolution.
330
+ #
331
+ # @param [PodTarget] pod_target
332
+ # The pod target, whose dependencies should be returned.
333
+ #
334
+ # @param [Array<PodTarget>] targets
335
+ # All pod targets, which are integrated alongside.
252
336
  #
253
337
  # @return [Array<PodTarget>]
254
338
  #
255
- def generate_pod_targets(target, specs)
256
- grouped_specs = specs.group_by(&:root).values.uniq
257
- grouped_specs.map do |pod_specs|
258
- generate_pod_target(target, pod_specs)
339
+ def transitive_dependencies_for_pod_target(pod_target, targets)
340
+ if targets.any?
341
+ dependent_targets = pod_target.dependencies.flat_map do |dep|
342
+ targets.select { |t| t.pod_name == dep }
343
+ end
344
+ remaining_targets = targets - dependent_targets
345
+ dependent_targets + dependent_targets.flat_map do |target|
346
+ transitive_dependencies_for_pod_target(target, remaining_targets)
347
+ end
348
+ else
349
+ []
259
350
  end
260
351
  end
261
352
 
262
- # Create a target for each spec group and add it to the aggregate target
353
+ # Create a target for each spec group
263
354
  #
264
- # @param [AggregateTarget] target
355
+ # @param [TargetDefinitions] target_definitions
265
356
  # the aggregate target
266
357
  #
267
358
  # @param [Array<Specification>] specs
268
359
  # the specifications of an equal root.
269
360
  #
361
+ # @param [Bool] scoped
362
+ # whether the pod target should be scoped
363
+ #
270
364
  # @return [PodTarget]
271
365
  #
272
- def generate_pod_target(target, pod_specs)
273
- pod_target = PodTarget.new(pod_specs, target.target_definition, sandbox)
366
+ def generate_pod_target(target_definitions, pod_specs, scoped: false)
367
+ pod_target = PodTarget.new(pod_specs, target_definitions, sandbox, scoped)
274
368
 
275
369
  if config.integrate_targets?
276
- pod_target.user_build_configurations = target.user_build_configurations
277
- pod_target.archs = @archs_by_target_def[target.target_definition]
370
+ target_inspections = result.target_inspections.select { |t, _| target_definitions.include?(t) }.values
371
+ pod_target.user_build_configurations = target_inspections.map(&:build_configurations).reduce({}, &:merge)
372
+ pod_target.archs = target_inspections.flat_map(&:archs).compact.uniq.sort
278
373
  else
279
374
  pod_target.user_build_configurations = {}
280
- if target.platform.name == :osx
375
+ if target_definitions.first.platform.name == :osx
281
376
  pod_target.archs = '$(ARCHS_STANDARD_64_BIT)'
282
377
  end
283
378
  end
@@ -438,6 +533,7 @@ module Pod
438
533
  UI.section "Resolving dependencies of #{UI.path podfile.defined_in_file}" do
439
534
  resolver = Resolver.new(sandbox, podfile, locked_dependencies, sources)
440
535
  specs_by_target = resolver.resolve
536
+ specs_by_target.values.flatten(1).each(&:validate_cocoapods_version)
441
537
  end
442
538
  specs_by_target
443
539
  end
@@ -529,305 +625,40 @@ module Pod
529
625
 
530
626
  # @!group Analysis sub-steps
531
627
 
532
- # Returns the path of the user project that the {TargetDefinition}
533
- # should integrate.
534
- #
535
- # @raise If the project is implicit and there are multiple projects.
536
- #
537
- # @raise If the path doesn't exits.
538
- #
539
- # @return [Pathname] the path of the user project.
540
- #
541
- def compute_user_project_path(target_definition)
542
- if target_definition.user_project_path
543
- path = config.installation_root + target_definition.user_project_path
544
- path = "#{path}.xcodeproj" unless File.extname(path) == '.xcodeproj'
545
- path = Pathname.new(path)
546
- unless path.exist?
547
- raise Informative, 'Unable to find the Xcode project ' \
548
- "`#{path}` for the target `#{target_definition.label}`."
549
- end
550
- else
551
- xcodeprojs = config.installation_root.children.select { |e| e.fnmatch('*.xcodeproj') }
552
- if xcodeprojs.size == 1
553
- path = xcodeprojs.first
554
- else
555
- raise Informative, 'Could not automatically select an Xcode project. ' \
556
- "Specify one in your Podfile like so:\n\n" \
557
- " xcodeproj 'path/to/Project.xcodeproj'\n"
558
- end
559
- end
560
- path
561
- end
562
-
563
- # Returns a list of the targets from the project of {TargetDefinition}
564
- # that needs to be integrated.
628
+ # Checks whether the platform is specified if not integrating
565
629
  #
566
- # @note The method first looks if there is a target specified with
567
- # the `link_with` option of the {TargetDefinition}. Otherwise
568
- # it looks for the target that has the same name of the target
569
- # definition. Finally if no target was found the first
570
- # encountered target is returned (it is assumed to be the one
571
- # to integrate in simple projects).
572
- #
573
- # @note This will only return targets that do **not** already have
574
- # the Pods library in their frameworks build phase.
575
- #
576
- #
577
- def compute_user_project_targets(target_definition, user_project)
578
- if link_with = target_definition.link_with
579
- targets = native_targets(user_project).select { |t| link_with.include?(t.name) }
580
- raise Informative, "Unable to find the targets named `#{link_with.to_sentence}` to link with target definition `#{target_definition.name}`" if targets.empty?
581
- elsif target_definition.link_with_first_target?
582
- targets = [native_targets(user_project).first].compact
583
- raise Informative, 'Unable to find a target' if targets.empty?
584
- else
585
- target = native_targets(user_project).find { |t| t.name == target_definition.name.to_s }
586
- targets = [target].compact
587
- raise Informative, "Unable to find a target named `#{target_definition.name}`" if targets.empty?
588
- end
589
- targets
590
- end
591
-
592
- # @return [Array<PBXNativeTarget>] Returns the user’s targets, excluding
593
- # aggregate targets.
594
- #
595
- def native_targets(user_project)
596
- user_project.targets.reject do |target|
597
- target.is_a? Xcodeproj::Project::Object::PBXAggregateTarget
598
- end
599
- end
600
-
601
- # Checks if any of the targets for the {TargetDefinition} computed before
602
- # by #compute_user_project_targets require to be build as a framework due
603
- # the presence of Swift source code in any of the source build phases.
604
- #
605
- # @param [TargetDefinition] target_definition
606
- # the target definition
607
- #
608
- # @param [Array<PBXNativeTarget>] native_targets
609
- # the targets which are checked for presence of Swift source code
610
- #
611
- # @return [Boolean] Whether the user project targets to integrate into
612
- # uses Swift
613
- #
614
- def compute_user_project_targets_require_framework(target_definition, native_targets)
615
- file_predicate = nil
616
- file_predicate = proc do |file_ref|
617
- if file_ref.respond_to?(:last_known_file_type)
618
- file_ref.last_known_file_type == 'sourcecode.swift'
619
- elsif file_ref.respond_to?(:files)
620
- file_ref.files.any?(&file_predicate)
621
- else
622
- false
623
- end
624
- end
625
- target_definition.platform.supports_dynamic_frameworks? || native_targets.any? do |target|
626
- target.source_build_phase.files.any? do |build_file|
627
- file_predicate.call(build_file.file_ref)
628
- end
629
- end
630
- end
631
-
632
- # @return [Hash{String=>Symbol}] A hash representing the user build
633
- # configurations where each key corresponds to the name of a
634
- # configuration and its value to its type (`:debug` or `:release`).
635
- #
636
- def compute_user_build_configurations(target_definition, user_targets)
637
- if user_targets
638
- user_targets.map { |t| t.build_configurations.map(&:name) }.flatten.reduce({}) do |hash, name|
639
- hash[name] = name == 'Debug' ? :debug : :release
640
- hash
641
- end.merge(target_definition.build_configurations || {})
642
- else
643
- target_definition.build_configurations || {}
644
- end
645
- end
646
-
647
- # @return [Platform] The platform for the library.
648
- #
649
- # @note This resolves to the lowest deployment target across the user
650
- # targets.
651
- #
652
- # @todo Is assigning the platform to the target definition the best way
653
- # to go?
630
+ # @return [void]
654
631
  #
655
- def compute_platform_for_target_definition(target_definition, user_targets)
656
- return target_definition.platform if target_definition.platform
657
- name = nil
658
- deployment_target = nil
659
-
660
- user_targets.each do |target|
661
- name ||= target.platform_name
662
- raise Informative, 'Targets with different platforms' unless name == target.platform_name
663
- if !deployment_target || deployment_target > Version.new(target.deployment_target)
664
- deployment_target = Version.new(target.deployment_target)
632
+ def verify_platforms_specified!
633
+ unless config.integrate_targets?
634
+ podfile.target_definition_list.each do |target_definition|
635
+ unless target_definition.platform
636
+ raise Informative, 'It is necessary to specify the platform in the Podfile if not integrating.'
637
+ end
665
638
  end
666
639
  end
667
-
668
- target_definition.set_platform(name, deployment_target)
669
- Platform.new(name, deployment_target)
670
- end
671
-
672
- # @return [Platform] The platform for the library.
673
- #
674
- # @note This resolves to the lowest deployment target across the user
675
- # targets.
676
- #
677
- # @todo Is assigning the platform to the target definition the best way
678
- # to go?
679
- #
680
- def compute_archs_for_target_definition(target_definition, user_targets)
681
- archs = []
682
- user_targets.each do |target|
683
- target_archs = target.common_resolved_build_setting('ARCHS')
684
- archs.concat(Array(target_archs))
685
- end
686
-
687
- archs = archs.compact.uniq.sort
688
- UI.message('Using `ARCHS` setting to build architectures of ' \
689
- "target `#{target_definition.label}`: " \
690
- "(`#{archs.join('`, `')}`)")
691
- archs.length > 1 ? archs : archs.first
692
640
  end
693
641
 
694
- # Precompute the platforms for each target_definition in the Podfile
642
+ # Precompute information for each target_definition in the Podfile
695
643
  #
696
644
  # @note The platforms are computed and added to each target_definition
697
645
  # because it might be necessary to infer the platform from the
698
646
  # user targets.
699
647
  #
700
- # @return [void]
648
+ # @return [Hash{TargetDefinition => TargetInspectionResult}]
701
649
  #
702
- def compute_target_platforms
650
+ def inspect_targets_to_integrate
651
+ inspection_result = {}
703
652
  UI.section 'Inspecting targets to integrate' do
704
653
  podfile.target_definition_list.each do |target_definition|
705
- if config.integrate_targets?
706
- project_path = compute_user_project_path(target_definition)
707
- user_project = Xcodeproj::Project.open(project_path)
708
- targets = compute_user_project_targets(target_definition, user_project)
709
- compute_platform_for_target_definition(target_definition, targets)
710
- archs = compute_archs_for_target_definition(target_definition, targets)
711
- @archs_by_target_def[target_definition] = archs
712
- else
713
- unless target_definition.platform
714
- raise Informative, 'It is necessary to specify the platform in the Podfile if not integrating.'
715
- end
716
- end
654
+ inspector = TargetInspector.new(target_definition, config.installation_root)
655
+ results = inspector.compute_results
656
+ inspection_result[target_definition] = results
657
+ UI.message('Using `ARCHS` setting to build architectures of ' \
658
+ "target `#{target_definition.label}`: (`#{results.archs.join('`, `')}`)")
717
659
  end
718
660
  end
719
- end
720
-
721
- #-----------------------------------------------------------------------#
722
-
723
- class AnalysisResult
724
- # @return [SpecsState] the states of the Podfile specs.
725
- #
726
- attr_accessor :podfile_state
727
-
728
- # @return [Hash{TargetDefinition => Array<Spec>}] the specifications
729
- # grouped by target.
730
- #
731
- attr_accessor :specs_by_target
732
-
733
- # @return [Array<Specification>] the specifications of the resolved
734
- # version of Pods that should be installed.
735
- #
736
- attr_accessor :specifications
737
-
738
- # @return [SpecsState] the states of the {Sandbox} respect the resolved
739
- # specifications.
740
- #
741
- attr_accessor :sandbox_state
742
-
743
- # @return [Array<Target>] The Podfile targets containing library
744
- # dependencies.
745
- #
746
- attr_accessor :targets
747
-
748
- # @return [Hash{String=>Symbol}] A hash representing all the user build
749
- # configurations across all integration targets. Each key
750
- # corresponds to the name of a configuration and its value to
751
- # its type (`:debug` or `:release`).
752
- #
753
- def all_user_build_configurations
754
- targets.reduce({}) do |result, target|
755
- result.merge(target.user_build_configurations)
756
- end
757
- end
758
- end
759
-
760
- #-----------------------------------------------------------------------#
761
-
762
- # This class represents the state of a collection of Pods.
763
- #
764
- # @note The names of the pods stored by this class are always the **root**
765
- # name of the specification.
766
- #
767
- # @note The motivation for this class is to ensure that the names of the
768
- # subspecs are added instead of the name of the Pods.
769
- #
770
- class SpecsState
771
- # Initialize a new instance
772
- #
773
- # @param [Hash{Symbol=>String}] pods_by_state
774
- # The name of the pods grouped by their state
775
- # (`:added`, `:removed`, `:changed` or `:unchanged`).
776
- #
777
- def initialize(pods_by_state = nil)
778
- @added = []
779
- @deleted = []
780
- @changed = []
781
- @unchanged = []
782
-
783
- if pods_by_state
784
- @added = pods_by_state[:added] || []
785
- @deleted = pods_by_state[:removed] || []
786
- @changed = pods_by_state[:changed] || []
787
- @unchanged = pods_by_state[:unchanged] || []
788
- end
789
- end
790
-
791
- # @return [Array<String>] the names of the pods that were added.
792
- #
793
- attr_accessor :added
794
-
795
- # @return [Array<String>] the names of the pods that were changed.
796
- #
797
- attr_accessor :changed
798
-
799
- # @return [Array<String>] the names of the pods that were deleted.
800
- #
801
- attr_accessor :deleted
802
-
803
- # @return [Array<String>] the names of the pods that were unchanged.
804
- #
805
- attr_accessor :unchanged
806
-
807
- # Displays the state of each pod.
808
- #
809
- # @return [void]
810
- #
811
- def print
812
- added .sort.each { |pod| UI.message('A'.green + " #{pod}", '', 2) }
813
- deleted .sort.each { |pod| UI.message('R'.red + " #{pod}", '', 2) }
814
- changed .sort.each { |pod| UI.message('M'.yellow + " #{pod}", '', 2) }
815
- unchanged.sort.each { |pod| UI.message('-' + " #{pod}", '', 2) }
816
- end
817
-
818
- # Adds the name of a Pod to the give state.
819
- #
820
- # @param [String] name
821
- # the name of the Pod.
822
- #
823
- # @param [Symbol] state
824
- # the state of the Pod.
825
- #
826
- # @return [void]
827
- #
828
- def add_name(name, state)
829
- send(state) << name
830
- end
661
+ inspection_result
831
662
  end
832
663
  end
833
664
  end