cocoapods 1.11.3 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
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 ||