cocoapods 1.11.3 → 1.12.0

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -2
  3. data/README.md +7 -8
  4. data/lib/cocoapods/command/lib/lint.rb +3 -0
  5. data/lib/cocoapods/command/repo/push.rb +3 -0
  6. data/lib/cocoapods/command/setup.rb +2 -2
  7. data/lib/cocoapods/command/spec/lint.rb +3 -0
  8. data/lib/cocoapods/config.rb +5 -5
  9. data/lib/cocoapods/executable.rb +1 -1
  10. data/lib/cocoapods/external_sources/abstract_external_source.rb +1 -1
  11. data/lib/cocoapods/external_sources/path_source.rb +1 -1
  12. data/lib/cocoapods/gem_version.rb +1 -1
  13. data/lib/cocoapods/generator/acknowledgements.rb +12 -0
  14. data/lib/cocoapods/generator/copy_resources_script.rb +1 -1
  15. data/lib/cocoapods/generator/info_plist_file.rb +1 -1
  16. data/lib/cocoapods/installer/analyzer/analysis_result.rb +3 -3
  17. data/lib/cocoapods/installer/analyzer/pod_variant.rb +1 -1
  18. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +6 -6
  19. data/lib/cocoapods/installer/analyzer.rb +8 -4
  20. data/lib/cocoapods/installer/base_install_hooks_context.rb +18 -3
  21. data/lib/cocoapods/installer/installation_options.rb +11 -0
  22. data/lib/cocoapods/installer/pod_source_downloader.rb +159 -0
  23. data/lib/cocoapods/installer/pod_source_installer.rb +10 -36
  24. data/lib/cocoapods/installer/project_cache/project_cache_analyzer.rb +2 -2
  25. data/lib/cocoapods/installer/project_cache/target_cache_key.rb +1 -1
  26. data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +37 -3
  27. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +6 -0
  28. data/lib/cocoapods/installer/xcode/pods_project_generator/project_generator.rb +1 -1
  29. data/lib/cocoapods/installer/xcode/pods_project_generator.rb +36 -1
  30. data/lib/cocoapods/installer.rb +102 -34
  31. data/lib/cocoapods/native_target_extension.rb +1 -1
  32. data/lib/cocoapods/open-uri.rb +1 -1
  33. data/lib/cocoapods/project.rb +8 -8
  34. data/lib/cocoapods/resolver/resolver_specification.rb +1 -1
  35. data/lib/cocoapods/resolver.rb +3 -3
  36. data/lib/cocoapods/sandbox/file_accessor.rb +1 -1
  37. data/lib/cocoapods/sandbox/path_list.rb +1 -1
  38. data/lib/cocoapods/sandbox.rb +48 -12
  39. data/lib/cocoapods/sources_manager.rb +1 -1
  40. data/lib/cocoapods/target/build_settings.rb +6 -1
  41. data/lib/cocoapods/target/pod_target.rb +2 -2
  42. data/lib/cocoapods/user_interface.rb +2 -2
  43. data/lib/cocoapods/validator.rb +19 -17
  44. data/lib/cocoapods/xcode/xcframework/xcframework_slice.rb +12 -6
  45. data/lib/cocoapods/xcode/xcframework.rb +1 -1
  46. metadata +13 -12
@@ -0,0 +1,159 @@
1
+
2
+ module Pod
3
+ class Installer
4
+ # Controller class responsible for downloading the activated specifications
5
+ # of a single Pod.
6
+ #
7
+ # @note This class needs to consider all the activated specs of a Pod.
8
+ #
9
+ class PodSourceDownloader
10
+ UNENCRYPTED_PROTOCOLS = %w(http git).freeze
11
+
12
+ # @return [Sandbox] The installation target.
13
+ #
14
+ attr_reader :sandbox
15
+
16
+ # @return [Podfile] the podfile that should be integrated with the user
17
+ # projects.
18
+ #
19
+ attr_reader :podfile
20
+
21
+ # @return [Hash{Symbol=>Array}] The specifications that need to be
22
+ # installed grouped by platform.
23
+ #
24
+ attr_reader :specs_by_platform
25
+
26
+ # @return [Boolean] Whether the installer is allowed to touch the cache.
27
+ #
28
+ attr_reader :can_cache
29
+ alias can_cache? can_cache
30
+
31
+ # Initialize a new instance
32
+ #
33
+ # @param [Sandbox] sandbox @see #sandbox
34
+ # @param [Podfile] podfile @see #podfile
35
+ # @param [Hash{Symbol=>Array}] specs_by_platform @see #specs_by_platform
36
+ # @param [Boolean] can_cache @see #can_cache
37
+ #
38
+ def initialize(sandbox, podfile, specs_by_platform, can_cache: true)
39
+ @sandbox = sandbox
40
+ @podfile = podfile
41
+ @specs_by_platform = specs_by_platform
42
+ @can_cache = can_cache
43
+ end
44
+
45
+ # @return [String] A string suitable for debugging.
46
+ #
47
+ def inspect
48
+ "<#{self.class} sandbox=#{sandbox.root} pod=#{root_spec.name}"
49
+ end
50
+
51
+ # @return [String] The name of the pod this downloader is downloading.
52
+ #
53
+ def name
54
+ root_spec.name
55
+ end
56
+
57
+ #-----------------------------------------------------------------------#
58
+
59
+ public
60
+
61
+ # @!group Downloading
62
+
63
+ # Creates the target in the Pods project and the relative support files.
64
+ #
65
+ # @return [void]
66
+ #
67
+ def download!
68
+ verify_source_is_secure(root_spec)
69
+ download_result = Downloader.download(download_request, root, :can_cache => can_cache?)
70
+
71
+ if (specific_source = download_result.checkout_options) && specific_source != root_spec.source
72
+ sandbox.store_checkout_source(root_spec.name, specific_source)
73
+ end
74
+
75
+ sandbox.store_downloaded_pod(root_spec.name)
76
+ end
77
+
78
+ #-----------------------------------------------------------------------#
79
+
80
+ private
81
+
82
+ # @!group Download Steps
83
+
84
+ # Verify the source of the spec is secure, which is used to show a warning to the user if that isn't the case
85
+ # This method doesn't verify all protocols, but currently only prohibits unencrypted 'http://' and 'git://''
86
+ # connections.
87
+ #
88
+ # @return [void]
89
+ #
90
+ def verify_source_is_secure(root_spec)
91
+ return if root_spec.source.nil? || (root_spec.source[:http].nil? && root_spec.source[:git].nil?)
92
+ source = if !root_spec.source[:http].nil?
93
+ URI(root_spec.source[:http].to_s)
94
+ elsif !root_spec.source[:git].nil?
95
+ git_source = root_spec.source[:git].to_s
96
+ return unless git_source =~ /^#{URI::DEFAULT_PARSER.make_regexp}$/
97
+ URI(git_source)
98
+ end
99
+ if UNENCRYPTED_PROTOCOLS.include?(source.scheme) && source.host != 'localhost'
100
+ UI.warn "'#{root_spec.name}' uses the unencrypted '#{source.scheme}' protocol to transfer the Pod. " \
101
+ 'Please be sure you\'re in a safe network with only trusted hosts. ' \
102
+ 'Otherwise, please reach out to the library author to notify them of this security issue.'
103
+ end
104
+ end
105
+
106
+ def download_request
107
+ Downloader::Request.new(
108
+ :spec => root_spec,
109
+ :released => released?,
110
+ )
111
+ end
112
+
113
+ #-----------------------------------------------------------------------#
114
+
115
+ private
116
+
117
+ # @!group Convenience methods.
118
+
119
+ # @return [Array<Specifications>] the specification of the Pod used in
120
+ # this installation.
121
+ #
122
+ def specs
123
+ specs_by_platform.values.flatten
124
+ end
125
+
126
+ # @return [Specification] the root specification of the Pod.
127
+ #
128
+ def root_spec
129
+ specs.first.root
130
+ end
131
+
132
+ # @return [Pathname] the folder where the source of the Pod is located.
133
+ #
134
+ def root
135
+ sandbox.pod_dir(root_spec.name)
136
+ end
137
+
138
+ # @return [Boolean] whether the source has been pre downloaded in the
139
+ # resolution process to retrieve its podspec.
140
+ #
141
+ def predownloaded?
142
+ sandbox.predownloaded_pods.include?(root_spec.name)
143
+ end
144
+
145
+ # @return [Boolean] whether the pod uses the local option and thus
146
+ # CocoaPods should not interfere with the files of the user.
147
+ #
148
+ def local?
149
+ sandbox.local?(root_spec.name)
150
+ end
151
+
152
+ def released?
153
+ sandbox.specification(root_spec.name) != root_spec
154
+ end
155
+
156
+ #-----------------------------------------------------------------------#
157
+ end
158
+ end
159
+ end
@@ -8,8 +8,6 @@ module Pod
8
8
  # @note This class needs to consider all the activated specs of a Pod.
9
9
  #
10
10
  class PodSourceInstaller
11
- UNENCRYPTED_PROTOCOLS = %w(http git).freeze
12
-
13
11
  # @return [Sandbox] The installation target.
14
12
  #
15
13
  attr_reader :sandbox
@@ -114,43 +112,12 @@ module Pod
114
112
  # @return [void]
115
113
  #
116
114
  def download_source
117
- verify_source_is_secure(root_spec)
118
- download_result = Downloader.download(download_request, root, :can_cache => can_cache?)
119
-
120
- if (specific_source = download_result.checkout_options) && specific_source != root_spec.source
121
- sandbox.store_checkout_source(root_spec.name, specific_source)
115
+ unless downloaded?
116
+ downloader = PodSourceDownloader.new(sandbox, podfile, specs_by_platform, :can_cache => can_cache?)
117
+ downloader.download!
122
118
  end
123
119
  end
124
120
 
125
- # Verify the source of the spec is secure, which is used to show a warning to the user if that isn't the case
126
- # This method doesn't verify all protocols, but currently only prohibits unencrypted 'http://' and 'git://''
127
- # connections.
128
- #
129
- # @return [void]
130
- #
131
- def verify_source_is_secure(root_spec)
132
- return if root_spec.source.nil? || (root_spec.source[:http].nil? && root_spec.source[:git].nil?)
133
- source = if !root_spec.source[:http].nil?
134
- URI(root_spec.source[:http].to_s)
135
- elsif !root_spec.source[:git].nil?
136
- git_source = root_spec.source[:git].to_s
137
- return unless git_source =~ /^#{URI.regexp}$/
138
- URI(git_source)
139
- end
140
- if UNENCRYPTED_PROTOCOLS.include?(source.scheme) && source.host != 'localhost'
141
- UI.warn "'#{root_spec.name}' uses the unencrypted '#{source.scheme}' protocol to transfer the Pod. " \
142
- 'Please be sure you\'re in a safe network with only trusted hosts. ' \
143
- 'Otherwise, please reach out to the library author to notify them of this security issue.'
144
- end
145
- end
146
-
147
- def download_request
148
- Downloader::Request.new(
149
- :spec => root_spec,
150
- :released => released?,
151
- )
152
- end
153
-
154
121
  #-----------------------------------------------------------------------#
155
122
 
156
123
  private
@@ -186,6 +153,13 @@ module Pod
186
153
  sandbox.pod_dir(root_spec.name)
187
154
  end
188
155
 
156
+ # @return [Boolean] whether the source has already been downloaded prior
157
+ # to the installation process.
158
+ #
159
+ def downloaded?
160
+ sandbox.downloaded_pods.include?(root_spec.name)
161
+ end
162
+
189
163
  # @return [Boolean] whether the source has been pre downloaded in the
190
164
  # resolution process to retrieve its podspec.
191
165
  #
@@ -38,7 +38,7 @@ module Pod
38
38
  #
39
39
  attr_reader :installation_options
40
40
 
41
- # @return [Bool] Flag indicating if we want to ignore the cache and force a clean installation.
41
+ # @return [Boolean] Flag indicating if we want to ignore the cache and force a clean installation.
42
42
  #
43
43
  attr_reader :clean_install
44
44
 
@@ -52,7 +52,7 @@ module Pod
52
52
  # @param [Array<PodTarget>] pod_targets @see #pod_targets
53
53
  # @param [Array<AggregateTarget>] aggregate_targets @see #aggregate_targets
54
54
  # @param [Hash<Symbol, Object>] installation_options @see #installation_options
55
- # @param [Bool] clean_install @see #clean_install
55
+ # @param [Boolean] clean_install @see #clean_install
56
56
  #
57
57
  def initialize(sandbox, cache, build_configurations, project_object_version, podfile_plugins, pod_targets, aggregate_targets, installation_options,
58
58
  clean_install: false)
@@ -109,7 +109,7 @@ module Pod
109
109
  # @param [PodTarget] pod_target
110
110
  # The pod target used to construct a TargetCacheKey object.
111
111
  #
112
- # @param [Bool] is_local_pod
112
+ # @param [Boolean] is_local_pod
113
113
  # Used to also include its local files in the cache key.
114
114
  #
115
115
  # @param [Hash] checkout_options
@@ -21,7 +21,7 @@ module Pod
21
21
  #
22
22
  attr_reader :pods_project
23
23
 
24
- # @return [Bool] add support for preserving the file structure of externally sourced pods, in addition to local pods.
24
+ # @return [Boolean] add support for preserving the file structure of externally sourced pods, in addition to local pods.
25
25
  #
26
26
  attr_reader :preserve_pod_file_structure
27
27
 
@@ -30,7 +30,7 @@ module Pod
30
30
  # @param [Sandbox] sandbox @see #sandbox
31
31
  # @param [Array<PodTarget>] pod_targets @see #pod_targets
32
32
  # @param [Project] pods_project @see #pods_project
33
- # @param [Bool] preserve_pod_file_structure @see #preserve_pod_file_structure
33
+ # @param [Boolean] preserve_pod_file_structure @see #preserve_pod_file_structure
34
34
  #
35
35
  def initialize(sandbox, pod_targets, pods_project, preserve_pod_file_structure = false)
36
36
  @sandbox = sandbox
@@ -207,7 +207,7 @@ module Pod
207
207
  # @param [Symbol] group_key
208
208
  # The key of the group of the Pods project.
209
209
  #
210
- # @param [Bool] reflect_file_system_structure
210
+ # @param [Boolean] reflect_file_system_structure
211
211
  # Whether organizing a local pod's files in subgroups inside
212
212
  # the pod's group is allowed.
213
213
  #
@@ -243,6 +243,10 @@ module Pod
243
243
  def allowable_project_paths(paths)
244
244
  lproj_paths = Set.new
245
245
  lproj_paths_with_files = Set.new
246
+
247
+ # Remove all file ref under .docc folder, but preserve the .docc folder
248
+ paths = merge_to_docc_folder(paths)
249
+
246
250
  allowable_paths = paths.select do |path|
247
251
  path_str = path.to_s
248
252
 
@@ -327,3 +331,33 @@ module Pod
327
331
  end
328
332
  end
329
333
  end
334
+
335
+ # If we have an non-empty .docc folder, remove all paths under the folder
336
+ # but keep the folder itself
337
+ #
338
+ # @param [Array<Pathname>] paths the paths to inspect
339
+ #
340
+ # @return [Array<Pathname>] The resulted list of paths.
341
+ #
342
+ def merge_to_docc_folder(paths)
343
+ docc_paths_with_files = Set.new
344
+ allowable_paths = paths.select do |path|
345
+ path_str = path.to_s
346
+
347
+ if path_str =~ /\.docc(\/|$)/i
348
+
349
+ # we want folder with files
350
+ next if path.directory?
351
+
352
+ # remove everything after ".docc", but keep ".docc"
353
+ folder_path = path_str.split("\.docc")[0] + "\.docc"
354
+
355
+ docc_paths_with_files << Pathname(folder_path)
356
+ next
357
+
358
+ end
359
+ true
360
+ end
361
+
362
+ allowable_paths + docc_paths_with_files.to_a
363
+ end
@@ -600,6 +600,9 @@ module Pod
600
600
  # classes from the parent module.
601
601
  configuration.build_settings['IBSC_MODULE'] = target.product_module_name
602
602
 
603
+ # Xcode 14.x throws an error about code signing on resource bundles, turn it off for now.
604
+ configuration.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
605
+
603
606
  # Set the `SWIFT_VERSION` build setting for resource bundles that could have resources that get
604
607
  # compiled such as an `xcdatamodeld` file which has 'Swift' as its code generation language.
605
608
  if contains_compile_phase_refs && file_accessors.any? { |fa| target.uses_swift_for_spec?(fa.spec) }
@@ -1010,6 +1013,9 @@ module Pod
1010
1013
  error_message_for_missing_reference = lambda do |sf, target|
1011
1014
  "Unable to find #{file_type} ref for `#{sf.basename}` for target `#{target.name}`."
1012
1015
  end
1016
+
1017
+ # Remove all file ref under .docc folder, but preserve the .docc folder
1018
+ files = merge_to_docc_folder(files)
1013
1019
  files.map do |sf|
1014
1020
  begin
1015
1021
  project.reference_for_path(sf).tap do |ref|
@@ -38,7 +38,7 @@ module Pod
38
38
  #
39
39
  attr_reader :podfile_path
40
40
 
41
- # @return [Bool] Bool indicating if this project is a pod target subproject.
41
+ # @return [Boolean] Bool indicating if this project is a pod target subproject.
42
42
  # Used by `generate_multiple_pod_projects` installation option.
43
43
  #
44
44
  attr_reader :pod_target_subproject
@@ -195,7 +195,7 @@ module Pod
195
195
 
196
196
  # @param [String] pod The root name of the development pod.
197
197
  #
198
- # @return [Bool] whether the scheme for the given development pod should be
198
+ # @return [Boolean] whether the scheme for the given development pod should be
199
199
  # shared.
200
200
  #
201
201
  def share_scheme_for_development_pod?(pod)
@@ -265,6 +265,10 @@ module Pod
265
265
  if scheme_configuration.key?(:code_coverage)
266
266
  scheme.test_action.code_coverage_enabled = scheme_configuration[:code_coverage]
267
267
  end
268
+ if scheme_configuration.key?(:parallelizable)
269
+ scheme.test_action.testables.each { |testable| testable.parallelizable = scheme_configuration[:parallelizable] }
270
+ end
271
+ set_scheme_build_configurations(scheme, scheme_configuration.fetch(:build_configurations, {}))
268
272
 
269
273
  hosted_test_specs_by_host[spec].each do |hosted_spec|
270
274
  # We are an app spec which hosts this test spec.
@@ -285,6 +289,37 @@ module Pod
285
289
  Xcodeproj::XCScheme.share_scheme(project.path, scheme_name) if share_scheme
286
290
  end
287
291
  end
292
+
293
+ # @param [Xcodeproj::XCSheme] scheme
294
+ # scheme to apply configuration to
295
+ #
296
+ # @param [Hash{String => String}] configuration
297
+ # action => build configuration to use for the action
298
+ #
299
+ # @return [void]
300
+ #
301
+ def set_scheme_build_configurations(scheme, configuration)
302
+ configuration.each do |k, v|
303
+ unless @build_configurations.include?(v)
304
+ raise Informative, "Unable to set `#{v}` as a build configuration as " \
305
+ "it doesn't match with any of your projects build configurations."
306
+ end
307
+
308
+ case k
309
+ when 'Run'
310
+ scheme.launch_action.build_configuration = v
311
+ when 'Test'
312
+ scheme.test_action.build_configuration = v
313
+ when 'Analyze'
314
+ scheme.analyze_action.build_configuration = v
315
+ when 'Archive'
316
+ scheme.archive_action.build_configuration = v
317
+ else
318
+ raise Informative, "#{k} is not a valid scheme action " \
319
+ "only one of ['run', 'test', 'analyze', 'archive'] is available"
320
+ end
321
+ end
322
+ end
288
323
  end
289
324
  end
290
325
  end
@@ -39,6 +39,7 @@ module Pod
39
39
  autoload :PreIntegrateHooksContext, 'cocoapods/installer/pre_integrate_hooks_context'
40
40
  autoload :SourceProviderHooksContext, 'cocoapods/installer/source_provider_hooks_context'
41
41
  autoload :PodfileValidator, 'cocoapods/installer/podfile_validator'
42
+ autoload :PodSourceDownloader, 'cocoapods/installer/pod_source_downloader'
42
43
  autoload :PodSourceInstaller, 'cocoapods/installer/pod_source_installer'
43
44
  autoload :PodSourcePreparer, 'cocoapods/installer/pod_source_preparer'
44
45
  autoload :UserProjectIntegrator, 'cocoapods/installer/user_project_integrator'
@@ -161,8 +162,10 @@ module Pod
161
162
  resolve_dependencies
162
163
  download_dependencies
163
164
  validate_targets
165
+ clean_sandbox
164
166
  if installation_options.skip_pods_project_generation?
165
167
  show_skip_pods_project_generation_message
168
+ run_podfile_post_install_hooks
166
169
  else
167
170
  integrate
168
171
  end
@@ -296,7 +299,10 @@ module Pod
296
299
  pod_targets_to_generate = cache_analysis_result.pod_targets_to_generate
297
300
  aggregate_targets_to_generate = cache_analysis_result.aggregate_targets_to_generate
298
301
 
299
- clean_sandbox(pod_targets_to_generate)
302
+ pod_targets_to_generate.each do |pod_target|
303
+ pod_target.build_headers.implode_path!(pod_target.headers_sandbox)
304
+ sandbox.public_headers.implode_path!(pod_target.headers_sandbox)
305
+ end
300
306
 
301
307
  create_and_save_projects(pod_targets_to_generate, aggregate_targets_to_generate,
302
308
  cache_analysis_result.build_configurations, cache_analysis_result.project_object_version)
@@ -443,23 +449,30 @@ module Pod
443
449
  end
444
450
  end
445
451
 
446
- # @return [void] In this step we clean all the header folders for pod targets that will be
447
- # regenerated from scratch and cleanup any pods that have been removed.
452
+ # @return [void] Performs a general clean up of the sandbox related to the sandbox state that was
453
+ # calculated. For example, pods that were marked for deletion are removed.
448
454
  #
449
- def clean_sandbox(pod_targets)
450
- pod_targets.each do |pod_target|
451
- pod_target.build_headers.implode_path!(pod_target.headers_sandbox)
452
- sandbox.public_headers.implode_path!(pod_target.headers_sandbox)
453
- end
454
-
455
+ def clean_sandbox
455
456
  unless sandbox_state.deleted.empty?
456
457
  title_options = { :verbose_prefix => '-> '.red }
457
458
  sandbox_state.deleted.each do |pod_name|
458
459
  UI.titled_section("Removing #{pod_name}".red, title_options) do
459
- sandbox.clean_pod(pod_name)
460
+ root_name = Specification.root_name(pod_name)
461
+ pod_dir = sandbox.local?(root_name) ? nil : sandbox.pod_dir(root_name)
462
+ sandbox.clean_pod(pod_name, pod_dir)
460
463
  end
461
464
  end
462
465
  end
466
+
467
+ # Check any changed pods that became local pods and used to be remote pods and
468
+ # ensure the sandbox is cleaned up.
469
+ unless sandbox_state.changed.empty?
470
+ sandbox_state.changed.each do |pod_name|
471
+ previous_spec_repo = sandbox.manifest.spec_repo(pod_name)
472
+ should_clean = !previous_spec_repo.nil? && sandbox.local?(pod_name)
473
+ sandbox.clean_pod(pod_name, sandbox.sources_root + Specification.root_name(pod_name)) if should_clean
474
+ end
475
+ end
463
476
  end
464
477
 
465
478
  # @raise [Informative] If there are any Podfile changes
@@ -490,26 +503,38 @@ module Pod
490
503
  # @return [void]
491
504
  #
492
505
  def install_pod_sources
506
+ @downloaded_specs = []
493
507
  @installed_specs = []
494
508
  pods_to_install = sandbox_state.added | sandbox_state.changed
495
509
  title_options = { :verbose_prefix => '-> '.green }
496
- root_specs.sort_by(&:name).each do |spec|
497
- if pods_to_install.include?(spec.name)
498
- if sandbox_state.changed.include?(spec.name) && sandbox.manifest
499
- current_version = spec.version
500
- previous_version = sandbox.manifest.version(spec.name)
501
- has_changed_version = current_version != previous_version
502
- current_repo = analysis_result.specs_by_source.detect { |key, values| break key if values.map(&:name).include?(spec.name) }
503
- current_repo &&= (Pod::TrunkSource::TRUNK_REPO_NAME if current_repo.name == Pod::TrunkSource::TRUNK_REPO_NAME) || current_repo.url || current_repo.name
504
- previous_spec_repo = sandbox.manifest.spec_repo(spec.name)
505
- has_changed_repo = !previous_spec_repo.nil? && current_repo && !current_repo.casecmp(previous_spec_repo).zero?
506
- title = "Installing #{spec.name} #{spec.version}"
507
- title << " (was #{previous_version} and source changed to `#{current_repo}` from `#{previous_spec_repo}`)" if has_changed_version && has_changed_repo
508
- title << " (was #{previous_version})" if has_changed_version && !has_changed_repo
509
- title << " (source changed to `#{current_repo}` from `#{previous_spec_repo}`)" if !has_changed_version && has_changed_repo
510
- else
511
- title = "Installing #{spec}"
510
+
511
+ sorted_root_specs = root_specs.sort_by(&:name)
512
+
513
+ # Download pods in parallel before installing if the option is set
514
+ if installation_options.parallel_pod_downloads
515
+ require 'concurrent/executor/fixed_thread_pool'
516
+ thread_pool_size = installation_options.parallel_pod_download_thread_pool_size
517
+ thread_pool = Concurrent::FixedThreadPool.new(thread_pool_size, :idletime => 300)
518
+
519
+ sorted_root_specs.each do |spec|
520
+ if pods_to_install.include?(spec.name)
521
+ title = section_title(spec, 'Downloading')
522
+ UI.titled_section(title.green, title_options) do
523
+ thread_pool.post do
524
+ download_source_of_pod(spec.name)
525
+ end
526
+ end
512
527
  end
528
+ end
529
+
530
+ thread_pool.shutdown
531
+ thread_pool.wait_for_termination
532
+ end
533
+
534
+ # Install pods, which includes downloading only if parallel_pod_downloads is set to false
535
+ sorted_root_specs.each do |spec|
536
+ if pods_to_install.include?(spec.name)
537
+ title = section_title(spec, 'Installing')
513
538
  UI.titled_section(title.green, title_options) do
514
539
  install_source_of_pod(spec.name)
515
540
  end
@@ -521,13 +546,32 @@ module Pod
521
546
  end
522
547
  end
523
548
 
549
+ def section_title(spec, current_action)
550
+ if sandbox_state.changed.include?(spec.name) && sandbox.manifest
551
+ current_version = spec.version
552
+ previous_version = sandbox.manifest.version(spec.name)
553
+ has_changed_version = current_version != previous_version
554
+ current_repo = analysis_result.specs_by_source.detect { |key, values| break key if values.map(&:name).include?(spec.name) }
555
+ current_repo &&= (Pod::TrunkSource::TRUNK_REPO_NAME if current_repo.name == Pod::TrunkSource::TRUNK_REPO_NAME) || current_repo.url || current_repo.name
556
+ previous_spec_repo = sandbox.manifest.spec_repo(spec.name)
557
+ has_changed_repo = !previous_spec_repo.nil? && current_repo && !current_repo.casecmp(previous_spec_repo).zero?
558
+ title = "#{current_action} #{spec.name} #{spec.version}"
559
+ title << " (was #{previous_version} and source changed to `#{current_repo}` from `#{previous_spec_repo}`)" if has_changed_version && has_changed_repo
560
+ title << " (was #{previous_version})" if has_changed_version && !has_changed_repo
561
+ title << " (source changed to `#{current_repo}` from `#{previous_spec_repo}`)" if !has_changed_version && has_changed_repo
562
+ else
563
+ title = "#{current_action} #{spec}"
564
+ end
565
+ title
566
+ end
567
+
524
568
  def create_pod_installer(pod_name)
525
569
  specs_by_platform = specs_for_pod(pod_name)
526
570
 
527
571
  if specs_by_platform.empty?
528
572
  requiring_targets = pod_targets.select { |pt| pt.recursive_dependent_targets.any? { |dt| dt.pod_name == pod_name } }
529
573
  message = "Could not install '#{pod_name}' pod"
530
- message += ", dependended upon by #{requiring_targets.to_sentence}" unless requiring_targets.empty?
574
+ message += ", depended upon by #{requiring_targets.to_sentence}" unless requiring_targets.empty?
531
575
  message += '. There is either no platform to build for, or no target to build.'
532
576
  raise StandardError, message
533
577
  end
@@ -537,6 +581,20 @@ module Pod
537
581
  pod_installer
538
582
  end
539
583
 
584
+ def create_pod_downloader(pod_name)
585
+ specs_by_platform = specs_for_pod(pod_name)
586
+
587
+ if specs_by_platform.empty?
588
+ requiring_targets = pod_targets.select { |pt| pt.recursive_dependent_targets.any? { |dt| dt.pod_name == pod_name } }
589
+ message = "Could not download '#{pod_name}' pod"
590
+ message += ", depended upon by #{requiring_targets.to_sentence}" unless requiring_targets.empty?
591
+ message += '. There is either no platform to build for, or no target to build.'
592
+ raise StandardError, message
593
+ end
594
+
595
+ PodSourceDownloader.new(sandbox, podfile, specs_by_platform, :can_cache => installation_options.clean?)
596
+ end
597
+
540
598
  # The specifications matching the specified pod name
541
599
  #
542
600
  # @param [String] pod_name the name of the pod
@@ -564,8 +622,19 @@ module Pod
564
622
  @installed_specs.concat(pod_installer.specs_by_platform.values.flatten.uniq)
565
623
  end
566
624
 
567
- # Cleans the sources of the Pods if the config instructs to do so.
625
+ # Download the pod unless it is local or has been predownloaded from an
626
+ # external source.
627
+ #
628
+ # @return [void]
568
629
  #
630
+ def download_source_of_pod(pod_name)
631
+ return if sandbox.local?(pod_name) || sandbox.predownloaded?(pod_name)
632
+
633
+ pod_downloader = create_pod_downloader(pod_name)
634
+ pod_downloader.download!
635
+ end
636
+
637
+ # Cleans the sources of the Pods if the config instructs to do so.
569
638
  #
570
639
  def clean_pod_sources
571
640
  return unless installation_options.clean?
@@ -633,7 +702,7 @@ module Pod
633
702
  #
634
703
  def run_plugins_pre_integrate_hooks
635
704
  if any_plugin_pre_integrate_hooks?
636
- context = PreIntegrateHooksContext.generate(sandbox, pods_project, aggregate_targets)
705
+ context = PreIntegrateHooksContext.generate(sandbox, pods_project, pod_target_subprojects, aggregate_targets)
637
706
  HooksManager.run(:pre_integrate, context, plugins)
638
707
  end
639
708
  end
@@ -645,7 +714,7 @@ module Pod
645
714
  if any_plugin_post_install_hooks?
646
715
  unlock_pod_sources
647
716
 
648
- context = PostInstallHooksContext.generate(sandbox, pods_project, aggregate_targets)
717
+ context = PostInstallHooksContext.generate(sandbox, pods_project, pod_target_subprojects, aggregate_targets)
649
718
  HooksManager.run(:post_install, context, plugins)
650
719
  end
651
720
 
@@ -656,7 +725,7 @@ module Pod
656
725
  #
657
726
  def run_plugins_post_integrate_hooks
658
727
  if any_plugin_post_integrate_hooks?
659
- context = PostIntegrateHooksContext.generate(sandbox, pods_project, aggregate_targets)
728
+ context = PostIntegrateHooksContext.generate(sandbox, pods_project, pod_target_subprojects, aggregate_targets)
660
729
  HooksManager.run(:post_integrate, context, plugins)
661
730
  end
662
731
  end
@@ -893,10 +962,9 @@ module Pod
893
962
  "\n\n#{e.message}\n\n#{e.backtrace * "\n"}"
894
963
  end
895
964
 
896
- # Runs the post integrate hooks of the installed specs and of the Podfile.
965
+ # Runs the pre integrate hooks of the installed specs and of the Podfile.
897
966
  #
898
- # @note Post integrate hooks run _after_ saving of project, so that they
899
- # can alter it after it is written to the disk.
967
+ # @note Pre integrate hooks run _before_ generation of the Pods project.
900
968
  #
901
969
  # @return [void]
902
970
  #
@@ -43,7 +43,7 @@ module Pod
43
43
  # @param [TargetMetadata] cached_target
44
44
  # the target to search for.
45
45
  #
46
- # @return [Bool]
46
+ # @return [Boolean]
47
47
  #
48
48
  def self.dependency_for_cached_target?(sandbox, target, cached_target)
49
49
  target.dependencies.find do |dep|
@@ -24,7 +24,7 @@ module OpenURI
24
24
  # @param [URI::Generic] uri2
25
25
  # the target uri where to where the redirect points to
26
26
  #
27
- # @return [Bool]
27
+ # @return [Boolean]
28
28
  #
29
29
  def self.redirectable?(uri1, uri2)
30
30
  uri1.scheme.downcase == uri2.scheme.downcase ||