cocoapods 0.37.2 → 0.38.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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