cocoapods-core 1.8.4 → 1.9.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 306a8c96eeccd74ea026697148fe4b1cb29336e906a448f24ab7c1d254f0d9f8
4
- data.tar.gz: f5d4447b0d9016de3c8d122ad6f2472d41c3df2fad6a96a0d58d82d3f06b2c4e
3
+ metadata.gz: 767b9681ebb1b555c9750c0fb6ceafa498dd5193a5a509e7560e3c395ef7093e
4
+ data.tar.gz: ad41c838f0582b8ae978b180f35f2cfc32c321dcb47f8f8591abd3843f61598c
5
5
  SHA512:
6
- metadata.gz: d9c8236b2c02a23d7d70495bff802a2fe46c061a8ecc4a51fd012c7f717cdde113a227b60e1b4b857cd6f74b954b48d3fc11ec4a1cdfae62b8d80be0249711f0
7
- data.tar.gz: d8472bbcbcd8b00a673e7e42f61827d35b887f796b66b16e7ee922dfed4ab7b254e3176e09b27234bd8bf3ea4693c9d8823d752e6eff581941758abdf75b035f
6
+ metadata.gz: a6e61b8cd239ea526a301f6a340b043ecc4fa02be1e6985e728bbff0376aea1389f45e689b6bcef170300e80fdde978c48d20392fafa30d9902443e157b42ec9
7
+ data.tar.gz: 4eb1ba7d93173e723207bfdfb9488d5fda243cdf2c7cba14b76e96a7a46929747f80ed6d7fd5efd6d707faee99433b9846d5d567b290fa52899755e0d4196c93
@@ -32,6 +32,7 @@ module Pod
32
32
  autoload :Specification, 'cocoapods-core/specification'
33
33
  autoload :StandardError, 'cocoapods-core/standard_error'
34
34
  autoload :YAMLHelper, 'cocoapods-core/yaml_helper'
35
+ autoload :BuildType, 'cocoapods-core/build_type'
35
36
 
36
37
  # TODO: Fix
37
38
  #
@@ -0,0 +1,121 @@
1
+ module Pod
2
+ class BuildType
3
+ # @return [Array<Symbol>] known packaging options.
4
+ #
5
+ KNOWN_PACKAGING_OPTIONS = %i(library framework).freeze
6
+
7
+ # @return [Array<Symbol>] known linking options.
8
+ #
9
+ KNOWN_LINKAGE_OPTIONS = %i(static dynamic).freeze
10
+
11
+ # @return [Symbol] the packaging for this build type, one of #KNOWN_PACKAGING_OPTIONS
12
+ #
13
+ attr_reader :packaging
14
+
15
+ # @return [Symbol] the linkage for this build type, one of #KNOWN_LINKAGE_OPTIONS
16
+ #
17
+ attr_reader :linkage
18
+
19
+ attr_reader :hash
20
+
21
+ def initialize(linkage: :static, packaging: :library)
22
+ unless KNOWN_LINKAGE_OPTIONS.include?(linkage)
23
+ raise ArgumentError, "Invalid linkage option #{linkage.inspect}, valid options are #{KNOWN_LINKAGE_OPTIONS.inspect}"
24
+ end
25
+ unless KNOWN_PACKAGING_OPTIONS.include?(packaging)
26
+ raise ArgumentError, "Invalid packaging option #{packaging.inspect}, valid options are #{KNOWN_PACKAGING_OPTIONS.inspect}"
27
+ end
28
+ @packaging = packaging
29
+ @linkage = linkage
30
+ @hash = packaging.hash ^ linkage.hash
31
+ end
32
+
33
+ # @return [BuildType] the build type for a dynamic library
34
+ def self.dynamic_library
35
+ new(:linkage => :dynamic, :packaging => :library)
36
+ end
37
+
38
+ # @return [BuildType] the build type for a static library
39
+ #
40
+ def self.static_library
41
+ new(:linkage => :static, :packaging => :library)
42
+ end
43
+
44
+ # @return [BuildType] the build type for a dynamic framework
45
+ #
46
+ def self.dynamic_framework
47
+ new(:linkage => :dynamic, :packaging => :framework)
48
+ end
49
+
50
+ # @return [BuildType] the build type for a static framework
51
+ #
52
+ def self.static_framework
53
+ new(:linkage => :static, :packaging => :framework)
54
+ end
55
+
56
+ # @return [Boolean] whether the target is built dynamically
57
+ #
58
+ def dynamic?
59
+ linkage == :dynamic
60
+ end
61
+
62
+ # @return [Boolean] whether the target is built statically
63
+ #
64
+ def static?
65
+ linkage == :static
66
+ end
67
+
68
+ # @return [Boolean] whether the target is built as a framework
69
+ #
70
+ def framework?
71
+ packaging == :framework
72
+ end
73
+
74
+ # @return [Boolean] whether the target is built as a library
75
+ #
76
+ def library?
77
+ packaging == :library
78
+ end
79
+
80
+ # @return [Boolean] whether the target is built as a dynamic framework
81
+ #
82
+ def dynamic_framework?
83
+ dynamic? && framework?
84
+ end
85
+
86
+ # @return [Boolean] whether the target is built as a dynamic library
87
+ #
88
+ def dynamic_library?
89
+ dynamic? && library?
90
+ end
91
+
92
+ # @return [Boolean] whether the target is built as a static framework
93
+ #
94
+ def static_framework?
95
+ static? && framework?
96
+ end
97
+
98
+ # @return [Boolean] whether the target is built as a static library
99
+ #
100
+ def static_library?
101
+ static? && library?
102
+ end
103
+
104
+ def to_s
105
+ "#{linkage} #{packaging}"
106
+ end
107
+
108
+ def to_hash
109
+ { :linkage => linkage, :packaging => packaging }
110
+ end
111
+
112
+ def inspect
113
+ "#<#{self.class} linkage=#{linkage} packaging=#{packaging}>"
114
+ end
115
+
116
+ def ==(other)
117
+ linkage == other.linkage &&
118
+ packaging == other.packaging
119
+ end
120
+ end
121
+ end
@@ -1,13 +1,20 @@
1
1
  require 'cocoapods-core/source'
2
2
  require 'rest'
3
3
  require 'concurrent'
4
+ require 'typhoeus'
5
+ require 'netrc'
4
6
 
5
7
  module Pod
6
8
  # Subclass of Pod::Source to provide support for CDN-based Specs repositories
7
9
  #
8
10
  class CDNSource < Source
9
- MAX_CDN_NETWORK_THREADS = (ENV['MAX_CDN_NETWORK_THREADS'] || 50).to_i
11
+ include Concurrent
12
+
10
13
  MAX_NUMBER_OF_RETRIES = (ENV['COCOAPODS_CDN_MAX_NUMBER_OF_RETRIES'] || 5).to_i
14
+ # Single thread executor for all network activity.
15
+ HYDRA_EXECUTOR = Concurrent::SingleThreadExecutor.new
16
+
17
+ private_constant :HYDRA_EXECUTOR
11
18
 
12
19
  # @param [String] repo The name of the repository
13
20
  #
@@ -18,12 +25,6 @@ module Pod
18
25
  # after the source was initialized, is considered fresh enough.
19
26
  @startup_time = Time.new
20
27
 
21
- @executor = Concurrent::ThreadPoolExecutor.new(
22
- :min_threads => 5,
23
- :max_threads => MAX_CDN_NETWORK_THREADS,
24
- :max_queue => 0 # unbounded work queue
25
- )
26
-
27
28
  @version_arrays_by_fragment_by_name = {}
28
29
 
29
30
  super(repo)
@@ -58,14 +59,14 @@ module Pod
58
59
  def preheat_existing_files
59
60
  files_to_update = files_definitely_to_update + deprecated_local_podspecs - ['deprecated_podspecs.txt']
60
61
  debug "CDN: #{name} Going to update #{files_to_update.count} files"
61
- loaders = files_to_update.map do |file|
62
- Concurrent::Promises.future_on(@executor) do
63
- download_file(file)
64
- end
65
- end
66
62
 
67
- catching_concurrent_errors do
68
- Concurrent::Promises.zip(*loaders).wait!
63
+ concurrent_requests_catching_errors do
64
+ # Queue all tasks first
65
+ loaders = files_to_update.map do |file|
66
+ download_file_async(file)
67
+ end
68
+ # Block and wait for all to complete running on Hydra
69
+ Promises.zip_futures_on(HYDRA_EXECUTOR, *loaders).wait!
69
70
  end
70
71
  end
71
72
 
@@ -117,27 +118,30 @@ module Pod
117
118
 
118
119
  return nil if @version_arrays_by_fragment_by_name[fragment][name].nil?
119
120
 
120
- loaders = []
121
- @versions_by_name[name] ||= @version_arrays_by_fragment_by_name[fragment][name].map do |version|
122
- # Optimization: ensure all the podspec files at least exist. The correct one will get refreshed
123
- # in #specification_path regardless.
124
- podspec_version_path_relative = Pathname.new(version).join("#{name}.podspec.json")
125
- unless pod_path_actual.join(podspec_version_path_relative).exist?
126
- loaders << Concurrent::Promises.future_on(@executor) do
127
- download_file(pod_path_relative.join(podspec_version_path_relative).to_s)
121
+ concurrent_requests_catching_errors do
122
+ loaders = []
123
+
124
+ @versions_by_name[name] ||= @version_arrays_by_fragment_by_name[fragment][name].map do |version|
125
+ # Optimization: ensure all the podspec files at least exist. The correct one will get refreshed
126
+ # in #specification_path regardless.
127
+ podspec_version_path_relative = Pathname.new(version).join("#{name}.podspec.json")
128
+
129
+ unless pod_path_actual.join(podspec_version_path_relative).exist?
130
+ # Queue all podspec download tasks first
131
+ loaders << download_file_async(pod_path_relative.join(podspec_version_path_relative).to_s)
128
132
  end
129
- end
130
- begin
131
- Version.new(version) if version[0, 1] != '.'
132
- rescue ArgumentError
133
- raise Informative, 'An unexpected version directory ' \
134
- "`#{version}` was encountered for the " \
135
- "`#{pod_path_actual}` Pod in the `#{name}` repository."
136
- end
137
- end.compact.sort.reverse
138
133
 
139
- catching_concurrent_errors do
140
- Concurrent::Promises.zip(*loaders).wait!
134
+ begin
135
+ Version.new(version) if version[0, 1] != '.'
136
+ rescue ArgumentError
137
+ raise Informative, 'An unexpected version directory ' \
138
+ "`#{version}` was encountered for the " \
139
+ "`#{pod_path_actual}` Pod in the `#{name}` repository."
140
+ end
141
+ end.compact.sort.reverse
142
+
143
+ # Block and wait for all to complete running on Hydra
144
+ Promises.zip_futures_on(HYDRA_EXECUTOR, *loaders).wait!
141
145
  end
142
146
 
143
147
  @versions_by_name[name]
@@ -323,18 +327,25 @@ module Pod
323
327
  end
324
328
 
325
329
  def download_file(partial_url)
330
+ # Block the main thread waiting for Hydra to finish
331
+ #
332
+ # Used for single-file downloads
333
+ download_file_async(partial_url).wait!
334
+ end
335
+
336
+ def download_file_async(partial_url)
326
337
  file_remote_url = URI.encode(url + partial_url.to_s)
327
338
  path = repo + partial_url
328
339
 
329
340
  if File.exist?(path)
330
341
  if @startup_time < File.mtime(path)
331
342
  debug "CDN: #{name} Relative path: #{partial_url} modified during this run! Returning local"
332
- return partial_url
343
+ return Promises.fulfilled_future(partial_url, HYDRA_EXECUTOR)
333
344
  end
334
345
 
335
346
  unless @check_existing_files_for_update
336
347
  debug "CDN: #{name} Relative path: #{partial_url} exists! Returning local because checking is only perfomed in repo update"
337
- return partial_url
348
+ return Promises.fulfilled_future(partial_url, HYDRA_EXECUTOR)
338
349
  end
339
350
  end
340
351
 
@@ -345,46 +356,68 @@ module Pod
345
356
  etag = File.read(etag_path) if File.exist?(etag_path)
346
357
  debug "CDN: #{name} Relative path: #{partial_url}, has ETag? #{etag}" unless etag.nil?
347
358
 
348
- download_retrying_retryable_errors(partial_url, file_remote_url, etag)
359
+ download_and_save_with_retries_async(partial_url, file_remote_url, etag)
349
360
  end
350
361
 
351
- def download_retrying_retryable_errors(partial_url, file_remote_url, etag, retries = MAX_NUMBER_OF_RETRIES)
362
+ def download_and_save_with_retries_async(partial_url, file_remote_url, etag, retries = MAX_NUMBER_OF_RETRIES)
352
363
  path = repo + partial_url
353
364
  etag_path = path.sub_ext(path.extname + '.etag')
354
365
 
355
- response = download_retrying_connection_errors(partial_url, file_remote_url, etag, retries)
356
-
357
- case response.status_code
358
- when 301
359
- redirect_location = response.headers['location'].first
360
- debug "CDN: #{name} Redirecting from #{file_remote_url} to #{redirect_location}"
361
- download_retrying_retryable_errors(partial_url, redirect_location, etag)
362
- when 304
363
- debug "CDN: #{name} Relative path not modified: #{partial_url}"
364
- # We need to update the file modification date, as it is later used for freshness
365
- # optimization. See #initialize for more information.
366
- FileUtils.touch path
367
- partial_url
368
- when 200
369
- File.open(path, 'w') { |f| f.write(response.body) }
370
-
371
- etag_new = response.headers['etag'].first if response.headers.include?('etag')
372
- debug "CDN: #{name} Relative path downloaded: #{partial_url}, save ETag: #{etag_new}"
373
- File.open(etag_path, 'w') { |f| f.write(etag_new) } unless etag_new.nil?
374
- partial_url
375
- when 404
376
- debug "CDN: #{name} Relative path couldn't be downloaded: #{partial_url} Response: #{response.status_code}"
377
- nil
378
- when 502, 503, 504
379
- if retries <= 1
380
- raise Informative, "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.status_code}"
366
+ download_task = download_typhoeus_impl_async(file_remote_url, etag).then do |response|
367
+ case response.response_code
368
+ when 301
369
+ redirect_location = response.headers['location']
370
+ debug "CDN: #{name} Redirecting from #{file_remote_url} to #{redirect_location}"
371
+ download_and_save_with_retries_async(partial_url, redirect_location, etag)
372
+ when 304
373
+ debug "CDN: #{name} Relative path not modified: #{partial_url}"
374
+ # We need to update the file modification date, as it is later used for freshness
375
+ # optimization. See #initialize for more information.
376
+ FileUtils.touch path
377
+ partial_url
378
+ when 200
379
+ File.open(path, 'w') { |f| f.write(response.response_body) }
380
+
381
+ etag_new = response.headers['etag']
382
+ debug "CDN: #{name} Relative path downloaded: #{partial_url}, save ETag: #{etag_new}"
383
+ File.open(etag_path, 'w') { |f| f.write(etag_new) } unless etag_new.nil?
384
+ partial_url
385
+ when 404
386
+ debug "CDN: #{name} Relative path couldn't be downloaded: #{partial_url} Response: #{response.response_code}"
387
+ nil
388
+ when 502, 503, 504
389
+ # Retryable HTTP errors, usually related to server overloading
390
+ if retries <= 1
391
+ raise Informative, "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.response_code} #{response.response_body}"
392
+ else
393
+ debug "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.response_code} #{response.response_body}, retries: #{retries - 1}"
394
+ exponential_backoff_async(retries).then do
395
+ download_and_save_with_retries_async(partial_url, file_remote_url, etag, retries - 1)
396
+ end
397
+ end
398
+ when 0
399
+ # Non-HTTP errors, usually network layer
400
+ if retries <= 1
401
+ raise Informative, "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.return_message}"
402
+ else
403
+ debug "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.return_message}, retries: #{retries - 1}"
404
+ exponential_backoff_async(retries).then do
405
+ download_and_save_with_retries_async(partial_url, file_remote_url, etag, retries - 1)
406
+ end
407
+ end
381
408
  else
382
- sleep_for(backoff_time(retries))
383
- download_retrying_retryable_errors(partial_url, file_remote_url, etag, retries - 1)
409
+ raise Informative, "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.response_code} #{response.response_body}"
384
410
  end
385
- else
386
- raise Informative, "CDN: #{name} URL couldn't be downloaded: #{file_remote_url} Response: #{response.status_code}"
387
411
  end
412
+
413
+ # Calling `Future#run` flattens the chained futures created by retries or redirects
414
+ #
415
+ # Does not, in fact, run the task - that is already happening in Hydra at this point
416
+ download_task.run
417
+ end
418
+
419
+ def exponential_backoff_async(retries)
420
+ sleep_async(backoff_time(retries))
388
421
  end
389
422
 
390
423
  def backoff_time(retries)
@@ -392,19 +425,38 @@ module Pod
392
425
  4 * 2**current_retry
393
426
  end
394
427
 
395
- def sleep_for(seconds)
396
- sleep(seconds)
397
- end
428
+ def sleep_async(seconds)
429
+ # Async sleep to avoid blocking either the main or the Hydra thread
430
+ Promises.schedule_on(HYDRA_EXECUTOR, seconds)
431
+ end
432
+
433
+ def download_typhoeus_impl_async(file_remote_url, etag)
434
+ # Create a prefereably HTTP/2 request - the protocol is ultimately responsible for picking
435
+ # the maximum supported protocol
436
+ # When debugging with proxy, use the following extra options:
437
+ # :proxy => 'http://localhost:8888',
438
+ # :ssl_verifypeer => false,
439
+ # :ssl_verifyhost => 0,
440
+ request = Typhoeus::Request.new(
441
+ file_remote_url,
442
+ :method => :get,
443
+ :http_version => :httpv2_0,
444
+ :timeout => 10,
445
+ :connecttimeout => 10,
446
+ :accept_encoding => 'gzip',
447
+ :netrc => :optional,
448
+ :netrc_file => Netrc.default_path,
449
+ :headers => etag.nil? ? {} : { 'If-None-Match' => etag },
450
+ )
398
451
 
399
- def download_retrying_connection_errors(partial_url, file_remote_url, etag, retries)
400
- etag.nil? ? REST.get(file_remote_url) : REST.get(file_remote_url, 'If-None-Match' => etag)
401
- rescue REST::Error => e
402
- if retries <= 1
403
- raise Informative, "CDN: #{name} URL couldn't be downloaded: #{file_remote_url}, error: #{e}"
404
- else
405
- debug "CDN: #{name} Relative path: #{partial_url} error: #{e} - retrying"
406
- download_retrying_connection_errors(partial_url, file_remote_url, etag, retries - 1)
452
+ future = Promises.resolvable_future_on(HYDRA_EXECUTOR)
453
+ queue_request(request)
454
+ request.on_complete do |response|
455
+ future.fulfill(response)
407
456
  end
457
+
458
+ # This `Future` should never reject, network errors are exposed on `Typhoeus::Response`
459
+ future
408
460
  end
409
461
 
410
462
  def debug(message)
@@ -415,11 +467,27 @@ module Pod
415
467
  end
416
468
  end
417
469
 
418
- def catching_concurrent_errors
470
+ def concurrent_requests_catching_errors
419
471
  yield
420
- rescue Concurrent::MultipleErrors => e
472
+ rescue MultipleErrors => e
473
+ # aggregated error message from `Concurrent`
421
474
  errors = e.errors
422
475
  raise Informative, "CDN: #{name} Repo update failed - #{e.errors.size} error(s):\n#{errors.join("\n")}"
423
476
  end
477
+
478
+ def queue_request(request)
479
+ @hydra ||= Typhoeus::Hydra.new
480
+
481
+ # Queue the request into the Hydra (libcurl reactor).
482
+ @hydra.queue(request)
483
+
484
+ # Cycle the reactor on a separate thread
485
+ #
486
+ # The way it works is that if more requests are queued while Hydra is in the `#run`
487
+ # method, it will keep executing them
488
+ #
489
+ # The upcoming calls to `#run` will simply run empty.
490
+ HYDRA_EXECUTOR.post(@hydra, &:run)
491
+ end
424
492
  end
425
493
  end
@@ -1,5 +1,5 @@
1
1
  module Pod
2
2
  # The version of the cocoapods-core.
3
3
  #
4
- CORE_VERSION = '1.8.4'.freeze unless defined? Pod::CORE_VERSION
4
+ CORE_VERSION = '1.9.0.beta.1'.freeze unless defined? Pod::CORE_VERSION
5
5
  end
@@ -45,6 +45,10 @@ module Pod
45
45
  @symbolic_name = input.name
46
46
  @deployment_target = input.deployment_target
47
47
  else
48
+ # Allow `Platform.new('macos')` to be equivalent to `Platform.macos`
49
+ if input == 'macos'
50
+ input = 'osx'
51
+ end
48
52
  @symbolic_name = input.to_sym
49
53
  target = target[:deployment_target] if target.is_a?(Hash)
50
54
  @deployment_target = Version.create(target)
@@ -422,6 +422,9 @@ module Pod
422
422
  # specifies the position of which this script phase should be executed. The currently supported values are:
423
423
  # `:before_compile`, `:after_compile` and `:any` which is the default.
424
424
  #
425
+ # @option options [String] :dependency_file
426
+ # specifies the dependency file to use for this script phase.
427
+ #
425
428
  # @return [void]
426
429
  #
427
430
  def script_phase(options)
@@ -677,14 +680,39 @@ module Pod
677
680
  current_target_definition.use_modular_headers_for_all_pods = true
678
681
  end
679
682
 
680
- # Use frameworks instead of static libraries for Pods.
683
+ # Use frameworks instead of static libraries for Pods. When using frameworks, you may also specify the `:linkage`
684
+ # style to use, either `:static` or `:dynamic`.
681
685
  #
682
686
  # ------
683
687
  #
684
688
  # This attribute is inherited by child target definitions.
685
689
  #
686
- def use_frameworks!(flag = true)
687
- current_target_definition.use_frameworks!(flag)
690
+ # @param [Boolean, Hash] option
691
+ # The option to use for configuring packaging and linkage style.
692
+ #
693
+ # @example
694
+ #
695
+ # target 'MyApp' do
696
+ # use_frameworks!
697
+ # pod 'AFNetworking', '~> 1.0'
698
+ # end
699
+ #
700
+ # @example
701
+ #
702
+ # target 'MyApp' do
703
+ # use_frameworks! :linkage => :dynamic
704
+ # pod 'AFNetworking', '~> 1.0'
705
+ # end
706
+ #
707
+ # target 'ZipApp' do
708
+ # use_frameworks! :linkage => :static
709
+ # pod 'SSZipArchive'
710
+ # end
711
+ #
712
+ # @return [void]
713
+ #
714
+ def use_frameworks!(option = true)
715
+ current_target_definition.use_frameworks!(option)
688
716
  end
689
717
 
690
718
  # Specifies the Swift version requirements this target definition supports.
@@ -365,25 +365,54 @@ module Pod
365
365
 
366
366
  #--------------------------------------#
367
367
 
368
- # Sets whether the target definition should build a framework.
368
+ # The (desired) build type for the pods integrated in this target definition. Defaults to static libraries and can
369
+ # only be overridden through Pod::Podfile::DSL#use_frameworks!.
370
+ #
371
+ # @return [BuildType]
372
+ #
373
+ def build_type
374
+ value = get_hash_value('uses_frameworks', root? ? BuildType.static_library : parent.build_type)
375
+ case value
376
+ when true, false
377
+ value ? BuildType.dynamic_framework : BuildType.static_library
378
+ when Hash
379
+ BuildType.new(:linkage => value.fetch(:linkage), :packaging => value.fetch(:packaging))
380
+ when BuildType
381
+ value
382
+ else
383
+ raise ArgumentError, "Got `#{value.inspect}`, should be a boolean, hash or BuildType."
384
+ end
385
+ end
386
+
387
+ # Sets whether the target definition's pods should be built as frameworks.
369
388
  #
370
- # @param [Bool] flag
371
- # Whether a framework should be built.
389
+ # @param [Boolean, Hash] option
390
+ # Whether pods that are integrated in this target should be built as frameworks. If the option is a
391
+ # boolean then the value affects both packaging and linkage styles. If set to true, then dynamic frameworks
392
+ # are used and if it's set to false, then static libraries are used. If the option is a hash then
393
+ # `:framework` packaging is implied and the user configures the `:linkage` style to use.
372
394
  #
373
395
  # @return [void]
374
396
  #
375
- def use_frameworks!(flag = true)
376
- set_hash_value('uses_frameworks', flag)
397
+ def use_frameworks!(option = true)
398
+ value = case option
399
+ when true, false
400
+ option ? BuildType.dynamic_framework : BuildType.static_library
401
+ when Hash
402
+ BuildType.new(:linkage => option.fetch(:linkage), :packaging => :framework)
403
+ else
404
+ raise ArgumentError, "Got `#{option.inspect}`, should be a boolean or hash."
405
+ end
406
+ set_hash_value('uses_frameworks', value.to_hash)
377
407
  end
378
408
 
379
- # @return [Bool] whether the target definition should build
380
- # a framework.
409
+ # @return [Bool] whether the target definition pods should be built as frameworks.
381
410
  #
382
411
  def uses_frameworks?
383
412
  if internal_hash['uses_frameworks'].nil?
384
413
  root? ? false : parent.uses_frameworks?
385
414
  else
386
- get_hash_value('uses_frameworks')
415
+ build_type.framework?
387
416
  end
388
417
  end
389
418
 
@@ -737,7 +766,8 @@ module Pod
737
766
  # @param [Hash] options
738
767
  # The options to use for this script phase. The required keys
739
768
  # are: `:name`, `:script`, while the optional keys are:
740
- # `:shell_path`, `:input_files`, `:output_files`, `:show_env_vars_in_log` and `:execution_position`.
769
+ # `:shell_path`, `:input_files`, `:output_files`, `:show_env_vars_in_log`, `:execution_position` and
770
+ # `:dependency_file`.
741
771
  #
742
772
  # @return [void]
743
773
  #
@@ -346,11 +346,17 @@ module Pod
346
346
  end
347
347
  end
348
348
 
349
- # @return [Array<String>] the name of the default subspecs if provided.
349
+ # @return [Array<String>, Symbol] the name(s) of the default subspecs if provided or :none for no default subspecs.
350
350
  #
351
351
  def default_subspecs
352
352
  # TODO: remove singular form and update the JSON specs.
353
- Array(attributes_hash['default_subspecs'] || attributes_hash['default_subspec'])
353
+ value = Array(attributes_hash['default_subspecs'] || attributes_hash['default_subspec'])
354
+ first = value.first
355
+ if first == :none || first == 'none'
356
+ first.to_sym
357
+ else
358
+ value
359
+ end
354
360
  end
355
361
 
356
362
  # Returns the dependencies on subspecs.
@@ -367,6 +373,8 @@ module Pod
367
373
  def subspec_dependencies(platform = nil)
368
374
  specs = if default_subspecs.empty?
369
375
  subspecs.compact.reject(&:non_library_specification?)
376
+ elsif default_subspecs == :none
377
+ []
370
378
  else
371
379
  default_subspecs.map do |subspec_name|
372
380
  root.subspec_by_name("#{name}/#{subspec_name}")
@@ -403,10 +411,29 @@ module Pod
403
411
  dependencies(platform) + subspec_dependencies(platform)
404
412
  end
405
413
 
414
+ # Returns whether a dependency is whitelisted for the given configuration.
415
+ #
416
+ # @param [Pod::Dependency] dependency
417
+ # the dependency verify.
418
+ #
419
+ # @param [Symbol, String] configuration
420
+ # the configuration to check against.
421
+ #
422
+ # @return [Bool] whether the dependency is whitelisted or not.
423
+ #
424
+ def dependency_whitelisted_for_configuration?(dependency, configuration)
425
+ inherited = -> { root? ? true : parent.dependency_whitelisted_for_configuration?(dependency, configuration) }
426
+
427
+ return inherited[] unless configuration_whitelist = attributes_hash['configuration_pod_whitelist']
428
+ return inherited[] unless whitelist_for_pod = configuration_whitelist[dependency.name]
429
+
430
+ whitelist_for_pod.include?(configuration.to_s.downcase)
431
+ end
432
+
406
433
  # Returns a consumer to access the multi-platform attributes.
407
434
  #
408
435
  # @param [String, Symbol, Platform] platform
409
- # he platform of the consumer
436
+ # the platform of the consumer
410
437
  #
411
438
  # @return [Specification::Consumer] the consumer for the given platform
412
439
  #
@@ -653,6 +653,12 @@ module Pod
653
653
  # spec.dependency 'AFNetworking', '~> 1.0'
654
654
  #
655
655
  # @example
656
+ # spec.dependency 'AFNetworking', '~> 1.0', :configurations => ['Debug']
657
+ #
658
+ # @example
659
+ # spec.dependency 'AFNetworking', '~> 1.0', :configurations => :debug
660
+ #
661
+ # @example
656
662
  # spec.dependency 'RestKit/CoreData', '~> 0.20.0'
657
663
  #
658
664
  # @example
@@ -676,16 +682,22 @@ module Pod
676
682
  end
677
683
  end
678
684
  end
679
- unless version_requirements.all? { |req| req.is_a?(String) }
680
- version_requirements.each do |requirement|
681
- if requirement.is_a?(Hash)
682
- if !requirement[:path].nil?
683
- raise Informative, 'Podspecs cannot specify the source of dependencies. The `:path` option is not supported.'\
684
- ' `:path` can be used in the Podfile instead to override global dependencies.'
685
- elsif !requirement[:git].nil?
686
- raise Informative, 'Podspecs cannot specify the source of dependencies. The `:git` option is not supported.'\
687
- ' `:git` can be used in the Podfile instead to override global dependencies.'
688
- end
685
+
686
+ configurations_option = version_requirements.find { |option| option.is_a?(Hash) && option.key?(:configurations) }
687
+ whitelisted_configurations = if configurations_option
688
+ version_requirements.delete(configurations_option)
689
+ Array(configurations_option.delete(:configurations)).map { |c| c.to_s.downcase }
690
+ end
691
+
692
+ dependency_options = version_requirements.reject { |req| req.is_a?(String) }
693
+ dependency_options.each do |dependency_option|
694
+ if dependency_option.is_a?(Hash)
695
+ if !dependency_option[:path].nil?
696
+ raise Informative, 'Podspecs cannot specify the source of dependencies. The `:path` option is not supported.'\
697
+ ' `:path` can be used in the Podfile instead to override global dependencies.'
698
+ elsif !dependency_option[:git].nil?
699
+ raise Informative, 'Podspecs cannot specify the source of dependencies. The `:git` option is not supported.'\
700
+ ' `:git` can be used in the Podfile instead to override global dependencies.'
689
701
  end
690
702
  end
691
703
 
@@ -694,6 +706,15 @@ module Pod
694
706
 
695
707
  attributes_hash['dependencies'] ||= {}
696
708
  attributes_hash['dependencies'][name] = version_requirements
709
+
710
+ unless whitelisted_configurations.nil?
711
+ if (extras = whitelisted_configurations - %w(debug release)) && !extras.empty?
712
+ raise Informative, "Only `Debug` & `Release` are allowed under configurations for dependency on `#{name}`. " \
713
+ "Found #{extras.map { |configuration| "`#{configuration}`" }.to_sentence}."
714
+ end
715
+ attributes_hash['configuration_pod_whitelist'] ||= {}
716
+ attributes_hash['configuration_pod_whitelist'][name] = whitelisted_configurations
717
+ end
697
718
  end
698
719
 
699
720
  def dependency=(args)
@@ -1024,7 +1045,7 @@ module Pod
1024
1045
  SCRIPT_PHASE_REQUIRED_KEYS = [:name, :script].freeze
1025
1046
 
1026
1047
  SCRIPT_PHASE_OPTIONAL_KEYS = [:shell_path, :input_files, :output_files, :input_file_lists, :output_file_lists,
1027
- :show_env_vars_in_log, :execution_position].freeze
1048
+ :show_env_vars_in_log, :execution_position, :dependency_file].freeze
1028
1049
 
1029
1050
  EXECUTION_POSITION_KEYS = [:before_compile, :after_compile, :any].freeze
1030
1051
 
@@ -1575,7 +1596,7 @@ module Pod
1575
1596
  :types => [String],
1576
1597
  :spec_types => [:test]
1577
1598
 
1578
- SCHEME_KEYS = [:launch_arguments, :environment_variables].freeze
1599
+ SCHEME_KEYS = [:launch_arguments, :environment_variables, :code_coverage].freeze
1579
1600
 
1580
1601
  # @!method scheme=(flag)
1581
1602
  #
@@ -1648,9 +1669,12 @@ module Pod
1648
1669
  # @!method default_subspecs=(subspec_array)
1649
1670
  #
1650
1671
  # An array of subspecs names that should be used as preferred dependency.
1651
- # If not specified a specifications requires all its subspecs as
1672
+ # If not specified, a specification requires all of its subspecs as
1652
1673
  # dependencies.
1653
1674
  #
1675
+ # You may use the value `:none` to specify that none of the subspecs are
1676
+ # required to compile this pod and that all subspecs are optional.
1677
+ #
1654
1678
  # ---
1655
1679
  #
1656
1680
  # A Pod should make available the full library by default. Users can
@@ -1666,17 +1690,21 @@ module Pod
1666
1690
  # spec.default_subspec = 'Core'
1667
1691
  #
1668
1692
  # @example
1693
+ #
1669
1694
  # spec.default_subspecs = 'Core', 'UI'
1670
1695
  #
1671
- # @param [Array<String>] subspec_names
1696
+ # @example
1697
+ #
1698
+ # spec.default_subspecs = :none
1699
+ #
1700
+ # @param [Array<String>, String, Symbol] subspec_names
1672
1701
  # An array of subspec names that should be inherited as
1673
1702
  # dependency.
1674
1703
  #
1675
- attribute :default_subspecs,
1676
- :container => Array,
1677
- :singularize => true,
1678
- :multi_platform => false,
1679
- :root_only => true
1704
+ root_attribute :default_subspecs,
1705
+ :container => Array,
1706
+ :types => [Array, String, Symbol],
1707
+ :singularize => true
1680
1708
 
1681
1709
  #-----------------------------------------------------------------------#
1682
1710
 
@@ -17,7 +17,7 @@ module Pod
17
17
  # multi-platform, they don't have inheritance, and they never have a
18
18
  # default value.
19
19
  #
20
- # @param [String] name
20
+ # @param [Symbol, String] name
21
21
  # The name of the attribute.
22
22
  #
23
23
  # @param [Hash] options
@@ -36,7 +36,7 @@ module Pod
36
36
  # Regular attributes in general support inheritance and multi-platform
37
37
  # values, so resolving them for a given specification is not trivial.
38
38
  #
39
- # @param [String] name
39
+ # @param [Symbol, String] name
40
40
  # The name of the attribute.
41
41
  #
42
42
  # @param [Hash] options
@@ -450,6 +450,9 @@ module Pod
450
450
  if s.key?(:environment_variables) && !s[:environment_variables].is_a?(Hash)
451
451
  results.add_error('scheme', 'Expected a hash for key `environment_variables`.')
452
452
  end
453
+ if s.key?(:code_coverage) && ![true, false].include?(s[:code_coverage])
454
+ results.add_error('scheme', 'Expected a boolean for key `code_coverage`.')
455
+ end
453
456
  end
454
457
  end
455
458
 
@@ -28,6 +28,14 @@ module Pod
28
28
 
29
29
  attr_reader :results
30
30
 
31
+ # @return [Array<String>] Keys that are valid but have been deprecated.
32
+ #
33
+ DEPRECATED_KEYS = ['swift_version'].freeze
34
+
35
+ # @return [Array<String>] Keys that are only used for internal purposes.
36
+ #
37
+ INTERNAL_KEYS = ['configuration_pod_whitelist'].freeze
38
+
31
39
  # Checks the attributes hash for any unknown key which might be the
32
40
  # result of a misspelling in a JSON file.
33
41
  #
@@ -42,8 +50,7 @@ module Pod
42
50
  def check_attributes
43
51
  attributes_keys = Pod::Specification::DSL.attributes.keys.map(&:to_s)
44
52
  platform_keys = Specification::DSL::PLATFORMS.map(&:to_s)
45
- deprecated_keys = ['swift_version']
46
- valid_keys = attributes_keys + platform_keys + deprecated_keys
53
+ valid_keys = attributes_keys + platform_keys + DEPRECATED_KEYS + INTERNAL_KEYS
47
54
  attributes_hash = consumer.spec.attributes_hash
48
55
  keys = attributes_hash.keys
49
56
  Specification::DSL::PLATFORMS.each do |platform|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocoapods-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.4
4
+ version: 1.9.0.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eloy Duran
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-10-16 00:00:00.000000000 Z
12
+ date: 2019-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -87,6 +87,34 @@ dependencies:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
89
  version: '1.1'
90
+ - !ruby/object:Gem::Dependency
91
+ name: typhoeus
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.0'
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ - !ruby/object:Gem::Dependency
105
+ name: netrc
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.11'
111
+ type: :runtime
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.11'
90
118
  - !ruby/object:Gem::Dependency
91
119
  name: bacon
92
120
  requirement: !ruby/object:Gem::Requirement
@@ -115,6 +143,7 @@ files:
115
143
  - LICENSE
116
144
  - README.md
117
145
  - lib/cocoapods-core.rb
146
+ - lib/cocoapods-core/build_type.rb
118
147
  - lib/cocoapods-core/cdn_source.rb
119
148
  - lib/cocoapods-core/core_ui.rb
120
149
  - lib/cocoapods-core/dependency.rb