cocoapods-core 0.30.0 → 1.15.2

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 (50) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +7 -10
  3. data/lib/cocoapods-core/build_type.rb +121 -0
  4. data/lib/cocoapods-core/cdn_source.rb +501 -0
  5. data/lib/cocoapods-core/core_ui.rb +4 -3
  6. data/lib/cocoapods-core/dependency.rb +100 -73
  7. data/lib/cocoapods-core/gem_version.rb +1 -2
  8. data/lib/cocoapods-core/github.rb +32 -5
  9. data/lib/cocoapods-core/http.rb +86 -0
  10. data/lib/cocoapods-core/lockfile.rb +161 -56
  11. data/lib/cocoapods-core/metrics.rb +47 -0
  12. data/lib/cocoapods-core/platform.rb +99 -11
  13. data/lib/cocoapods-core/podfile/dsl.rb +623 -124
  14. data/lib/cocoapods-core/podfile/target_definition.rb +662 -109
  15. data/lib/cocoapods-core/podfile.rb +138 -65
  16. data/lib/cocoapods-core/requirement.rb +37 -8
  17. data/lib/cocoapods-core/source/acceptor.rb +16 -13
  18. data/lib/cocoapods-core/source/aggregate.rb +79 -103
  19. data/lib/cocoapods-core/source/health_reporter.rb +9 -18
  20. data/lib/cocoapods-core/source/manager.rb +488 -0
  21. data/lib/cocoapods-core/source/metadata.rb +79 -0
  22. data/lib/cocoapods-core/source.rb +241 -70
  23. data/lib/cocoapods-core/specification/consumer.rb +187 -47
  24. data/lib/cocoapods-core/specification/dsl/attribute.rb +49 -85
  25. data/lib/cocoapods-core/specification/dsl/attribute_support.rb +6 -8
  26. data/lib/cocoapods-core/specification/dsl/deprecations.rb +9 -126
  27. data/lib/cocoapods-core/specification/dsl/platform_proxy.rb +30 -20
  28. data/lib/cocoapods-core/specification/dsl.rb +943 -296
  29. data/lib/cocoapods-core/specification/json.rb +64 -23
  30. data/lib/cocoapods-core/specification/linter/analyzer.rb +218 -0
  31. data/lib/cocoapods-core/specification/linter/result.rb +128 -0
  32. data/lib/cocoapods-core/specification/linter.rb +310 -309
  33. data/lib/cocoapods-core/specification/root_attribute_accessors.rb +90 -39
  34. data/lib/cocoapods-core/specification/set/presenter.rb +35 -71
  35. data/lib/cocoapods-core/specification/set.rb +42 -96
  36. data/lib/cocoapods-core/specification.rb +368 -130
  37. data/lib/cocoapods-core/standard_error.rb +45 -24
  38. data/lib/cocoapods-core/trunk_source.rb +14 -0
  39. data/lib/cocoapods-core/vendor/requirement.rb +133 -53
  40. data/lib/cocoapods-core/vendor/version.rb +197 -156
  41. data/lib/cocoapods-core/vendor.rb +1 -5
  42. data/lib/cocoapods-core/version.rb +137 -42
  43. data/lib/cocoapods-core/yaml_helper.rb +334 -0
  44. data/lib/cocoapods-core.rb +10 -4
  45. metadata +100 -27
  46. data/lib/cocoapods-core/source/abstract_data_provider.rb +0 -71
  47. data/lib/cocoapods-core/source/file_system_data_provider.rb +0 -150
  48. data/lib/cocoapods-core/source/github_data_provider.rb +0 -143
  49. data/lib/cocoapods-core/specification/set/statistics.rb +0 -266
  50. data/lib/cocoapods-core/yaml_converter.rb +0 -192
@@ -1,12 +1,18 @@
1
1
  module Pod
2
2
  class Specification
3
3
  module JSONSupport
4
-
5
4
  # @return [String] the json representation of the specification.
6
5
  #
7
6
  def to_json(*a)
8
7
  require 'json'
9
- to_hash.to_json(*a)
8
+ JSON.dump(to_hash, *a) << "\n"
9
+ end
10
+
11
+ # @return [String] the pretty json representation of the specification.
12
+ #
13
+ def to_pretty_json(*a)
14
+ require 'json'
15
+ JSON.pretty_generate(to_hash, *a) << "\n"
10
16
  end
11
17
 
12
18
  #-----------------------------------------------------------------------#
@@ -16,17 +22,29 @@ module Pod
16
22
  #
17
23
  def to_hash
18
24
  hash = attributes_hash.dup
19
- unless subspecs.empty?
20
- hash["subspecs"] = subspecs.map { |spec| spec.to_hash }
25
+ if root? || available_platforms != parent.available_platforms
26
+ platforms = Hash[available_platforms.map { |p| [p.name.to_s, p.deployment_target && p.deployment_target.to_s] }]
27
+ hash['platforms'] = platforms
21
28
  end
22
- hash
23
- end
29
+ specs_by_type = subspecs.group_by(&:spec_type)
30
+ all_appspecs = specs_by_type[:app] || []
31
+ all_testspecs = specs_by_type[:test] || []
32
+ all_subspecs = specs_by_type[:library] || []
24
33
 
25
- # @return [Bool] Whether the specification can be converted to a hash
26
- # without loss of information.
27
- #
28
- def safe_to_hash?
29
- pre_install_callback.nil? && post_install_callback.nil?
34
+ hash.delete('testspecs')
35
+ hash['testspecs'] = all_testspecs.map(&:to_hash) unless all_testspecs.empty?
36
+ hash.delete('appspecs')
37
+ hash['appspecs'] = all_appspecs.map(&:to_hash) unless all_appspecs.empty?
38
+ hash.delete('subspecs')
39
+ hash['subspecs'] = all_subspecs.map(&:to_hash) unless all_subspecs.empty?
40
+
41
+ # Since CocoaPods 1.7 version the DSL has changed to be pluralized. When we serialize a podspec to JSON with
42
+ # 1.7, ensure that we also include the singular version in the hash to maintain backwards compatibility with
43
+ # < 1.7 versions. We also delete this key and re-add it to ensure it gets added at the end.
44
+ hash.delete('swift_version')
45
+ hash['swift_version'] = swift_version.to_s unless swift_version.nil?
46
+
47
+ hash
30
48
  end
31
49
  end
32
50
 
@@ -38,33 +56,56 @@ module Pod
38
56
  #
39
57
  # @return [Specification] the specification
40
58
  #
41
- def self.from_json(json)
59
+ def self.from_json(json, path="")
42
60
  require 'json'
43
- hash = JSON.parse(json)
44
- from_hash(hash)
61
+ begin
62
+ hash = JSON.parse(json)
63
+ from_hash(hash)
64
+ rescue JSON::ParserError => e
65
+ if path != ""
66
+ raise e.class, "Failed to parse JSON at file: '#{path}'.\n\n#{e.message}"
67
+ else raise
68
+ end
69
+ end
45
70
  end
46
71
 
47
72
  # Configures a new specification from the given hash.
48
73
  #
49
- # @param [Hash] the hash which contains the information of the
74
+ # @param [Hash] hash the hash which contains the information of the
50
75
  # specification.
51
76
  #
77
+ # @param [Specification] parent the parent of the specification unless the
78
+ # specification is a root.
79
+ #
52
80
  # @return [Specification] the specification
53
81
  #
54
- def self.from_hash(hash, parent = nil)
55
- spec = Spec.new(parent)
82
+ def self.from_hash(hash, parent = nil, test_specification: false, app_specification: false)
56
83
  attributes_hash = hash.dup
84
+ spec = Spec.new(parent, nil, test_specification, :app_specification => app_specification)
57
85
  subspecs = attributes_hash.delete('subspecs')
86
+ testspecs = attributes_hash.delete('testspecs')
87
+ appspecs = attributes_hash.delete('appspecs')
88
+
89
+ ## backwards compatibility with 1.3.0
90
+ spec.test_specification = !attributes_hash['test_type'].nil?
91
+
58
92
  spec.attributes_hash = attributes_hash
59
- if subspecs
60
- spec.subspecs = subspecs.map do |s_hash|
61
- Specification.from_hash(s_hash, spec)
62
- end
63
- end
93
+ spec.subspecs.concat(subspecs_from_hash(spec, subspecs, false, false))
94
+ spec.subspecs.concat(subspecs_from_hash(spec, testspecs, true, false))
95
+ spec.subspecs.concat(subspecs_from_hash(spec, appspecs, false, true))
96
+
64
97
  spec
65
98
  end
66
99
 
67
- #-----------------------------------------------------------------------#
100
+ def self.subspecs_from_hash(spec, subspecs, test_specification, app_specification)
101
+ return [] if subspecs.nil?
102
+ subspecs.map do |s_hash|
103
+ Specification.from_hash(s_hash, spec,
104
+ :test_specification => test_specification,
105
+ :app_specification => app_specification)
106
+ end
107
+ end
68
108
 
109
+ #-----------------------------------------------------------------------#
69
110
  end
70
111
  end
@@ -0,0 +1,218 @@
1
+ require 'cocoapods-core/specification/linter/result'
2
+
3
+ module Pod
4
+ class Specification
5
+ class Linter
6
+ class Analyzer
7
+ def initialize(consumer, results)
8
+ @consumer = consumer
9
+ @results = results
10
+ @results.consumer = @consumer
11
+ end
12
+
13
+ # Analyzes the consumer adding a {Result} for any failed check to
14
+ # the {#results} object.
15
+ #
16
+ # @return [Results] the results of the analysis.
17
+ #
18
+ def analyze
19
+ check_attributes
20
+ validate_file_patterns
21
+ check_if_spec_is_empty
22
+ results
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :consumer
28
+
29
+ attr_reader :results
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
+
39
+ # Checks the attributes hash for any unknown key which might be the
40
+ # result of a misspelling in a JSON file.
41
+ #
42
+ # @note Sub-keys are not checked per-platform as
43
+ # there is no attribute supporting this combination.
44
+ #
45
+ # @note The keys of sub-keys are not checked as they are only used by
46
+ # the `source` attribute and they are subject
47
+ # to change according to the support in the
48
+ # `cocoapods-downloader` gem.
49
+ #
50
+ def check_attributes
51
+ attributes_keys = Pod::Specification::DSL.attributes.keys.map(&:to_s)
52
+ platform_keys = Specification::DSL::PLATFORMS.map(&:to_s)
53
+ valid_keys = attributes_keys + platform_keys + DEPRECATED_KEYS + INTERNAL_KEYS
54
+ attributes_hash = consumer.spec.attributes_hash
55
+ keys = attributes_hash.keys
56
+ Specification::DSL::PLATFORMS.each do |platform|
57
+ if attributes_hash[platform.to_s]
58
+ keys += attributes_hash[platform.to_s].keys
59
+ end
60
+ end
61
+ unknown_keys = keys - valid_keys
62
+
63
+ unknown_keys.each do |key|
64
+ results.add_warning('attributes', "Unrecognized `#{key}` key.")
65
+ end
66
+
67
+ Pod::Specification::DSL.attributes.each do |_key, attribute|
68
+ declared_value = consumer.spec.attributes_hash[attribute.name.to_s]
69
+ validate_attribute_occurrence(attribute, declared_value)
70
+ validate_attribute_type(attribute, declared_value)
71
+ if attribute.name != :platforms
72
+ value = value_for_attribute(attribute)
73
+ validate_attribute_value(attribute, value) if value
74
+ end
75
+ end
76
+ end
77
+
78
+ # Checks the attributes that represent file patterns.
79
+ #
80
+ # @todo Check the attributes hash directly.
81
+ #
82
+ def validate_file_patterns
83
+ attributes = DSL.attributes.values.select(&:file_patterns?)
84
+ attributes.each do |attrb|
85
+ patterns = consumer.send(attrb.name)
86
+
87
+ if patterns.is_a?(Hash)
88
+ patterns = patterns.values.flatten(1)
89
+ end
90
+
91
+ if patterns.respond_to?(:each)
92
+ patterns.each do |pattern|
93
+ pattern = pattern[:paths].join if attrb.name == :on_demand_resources
94
+ if pattern.respond_to?(:start_with?) && pattern.start_with?('/')
95
+ results.add_error('File Patterns', 'File patterns must be ' \
96
+ "relative and cannot start with a slash (#{attrb.name}).")
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ # Check empty subspec attributes
104
+ #
105
+ def check_if_spec_is_empty
106
+ methods = %w( source_files on_demand_resources resources resource_bundles preserve_paths
107
+ dependencies vendored_libraries vendored_frameworks )
108
+ empty_patterns = methods.all? { |m| consumer.send(m).empty? }
109
+ empty = empty_patterns && consumer.spec.subspecs.empty?
110
+ if empty
111
+ results.add_error('File Patterns', "The #{consumer.spec} spec is " \
112
+ 'empty (no source files, resources, resource_bundles, ' \
113
+ 'preserve paths, vendored_libraries, vendored_frameworks, ' \
114
+ 'dependencies, nor subspecs).')
115
+ end
116
+ end
117
+
118
+ private
119
+
120
+ # Returns the own or inherited (if applicable) value of the
121
+ # given attribute.
122
+ #
123
+ # @param [Spec::DSL::Attribute] attribute
124
+ # The attribute.
125
+ #
126
+ # @return [mixed]
127
+ #
128
+ def value_for_attribute(attribute)
129
+ if attribute.root_only?
130
+ consumer.spec.send(attribute.name)
131
+ else
132
+ consumer.send(attribute.name) if consumer.respond_to?(attribute.name)
133
+ end
134
+ rescue => e
135
+ results.add_error('attributes', "Unable to validate `#{attribute.name}` (#{e}).")
136
+ nil
137
+ end
138
+
139
+ # Validates that root attributes don't occur in subspecs.
140
+ #
141
+ # @param [Spec::DSL::Attribute] attribute
142
+ # The attribute.
143
+
144
+ # @param [Object] value
145
+ # The value of the attribute.
146
+ #
147
+ def validate_attribute_occurrence(attribute, value)
148
+ if attribute.root_only? && !value.nil? && !consumer.spec.root?
149
+ results.add_error('attributes', "Can't set `#{attribute.name}` attribute for " \
150
+ "subspecs (in `#{consumer.spec.name}`).")
151
+ end
152
+ if attribute.test_only? && !value.nil? && !consumer.spec.test_specification?
153
+ results.add_error('attributes', "Attribute `#{attribute.name}` can only be set " \
154
+ "within test specs (in `#{consumer.spec.name}`).")
155
+ end
156
+ end
157
+
158
+ # Validates the given value for the given attribute.
159
+ #
160
+ # @param [Spec::DSL::Attribute] attribute
161
+ # The attribute.
162
+ #
163
+ # @param [Object] value
164
+ # The value of the attribute.
165
+ #
166
+ def validate_attribute_value(attribute, value)
167
+ if attribute.keys.is_a?(Array)
168
+ validate_attribute_array_keys(attribute, value)
169
+ elsif attribute.keys.is_a?(Hash)
170
+ validate_attribute_hash_keys(attribute, value)
171
+ end
172
+ end
173
+
174
+ def validate_attribute_type(attribute, value)
175
+ return unless value
176
+ types = attribute.supported_types
177
+ if types.none? { |klass| value.class == klass }
178
+ results.add_error('attributes', 'Unacceptable type ' \
179
+ "`#{value.class}` for `#{attribute.name}`. Allowed values: `#{types.inspect}`.")
180
+ end
181
+ end
182
+
183
+ def validate_attribute_array_keys(attribute, value)
184
+ unknown_keys = value.keys.map(&:to_s) - attribute.keys.map(&:to_s)
185
+ unknown_keys.each do |unknown_key|
186
+ results.add_warning('keys', "Unrecognized `#{unknown_key}` key for " \
187
+ "`#{attribute.name}` attribute.")
188
+ end
189
+ end
190
+
191
+ def validate_attribute_hash_keys(attribute, value)
192
+ unless value.is_a?(Hash)
193
+ results.add_error(attribute.name, "Unsupported type `#{value.class}`, expected `Hash`")
194
+ return
195
+ end
196
+ major_keys = value.keys & attribute.keys.keys
197
+ if major_keys.count.zero?
198
+ results.add_warning('keys', "Missing primary key for `#{attribute.name}` " \
199
+ 'attribute. The acceptable ones are: ' \
200
+ "`#{attribute.keys.keys.map(&:to_s).sort.join(', ')}`.")
201
+ elsif major_keys.count == 1
202
+ acceptable = attribute.keys[major_keys.first] || []
203
+ unknown = value.keys - major_keys - acceptable
204
+ unless unknown.empty?
205
+ results.add_warning('keys', "Incompatible `#{unknown.sort.join(', ')}` " \
206
+ "key(s) with `#{major_keys.first}` primary key for " \
207
+ "`#{attribute.name}` attribute.")
208
+ end
209
+ else
210
+ sorted_keys = major_keys.map(&:to_s).sort
211
+ results.add_warning('keys', "Incompatible `#{sorted_keys.join(', ')}` " \
212
+ "keys for `#{attribute.name}` attribute.")
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,128 @@
1
+ module Pod
2
+ class Specification
3
+ class Linter
4
+ class Results
5
+ public
6
+
7
+ class Result
8
+ # @return [Symbol] the type of result.
9
+ #
10
+ attr_reader :type
11
+
12
+ # @return[String] the name of the attribute associated with result.
13
+ #
14
+ attr_reader :attribute_name
15
+
16
+ # @return [String] the message associated with result.
17
+ #
18
+ attr_reader :message
19
+
20
+ # @return [Boolean] whether the result only applies to public specs.
21
+ #
22
+ attr_reader :public_only
23
+ alias_method :public_only?, :public_only
24
+
25
+ # @param [Symbol] type @see type
26
+ # @param [String] message @see message
27
+ #
28
+ def initialize(type, attribute_name, message, public_only = false)
29
+ @type = type
30
+ @attribute_name = attribute_name
31
+ @message = message
32
+ @public_only = public_only
33
+ @platforms = []
34
+ end
35
+
36
+ # @return [Array<Platform>] the platforms where this result was
37
+ # generated.
38
+ #
39
+ attr_reader :platforms
40
+
41
+ # @return [String] a string representation suitable for UI output.
42
+ #
43
+ def to_s
44
+ r = "[#{type.to_s.upcase}] [#{attribute_name}] #{message}"
45
+ if platforms != Specification::PLATFORMS
46
+ platforms_names = platforms.uniq.map do |p|
47
+ Platform.string_name(p)
48
+ end
49
+ r << " [#{platforms_names * ' - '}]" unless platforms.empty?
50
+ end
51
+ r
52
+ end
53
+ end
54
+
55
+ def initialize
56
+ @results = []
57
+ @consumer = nil
58
+ end
59
+
60
+ include Enumerable
61
+
62
+ def each
63
+ results.each { |r| yield r }
64
+ end
65
+
66
+ def empty?
67
+ results.empty?
68
+ end
69
+
70
+ # @return [Specification::Consumer] the current consumer.
71
+ #
72
+ attr_accessor :consumer
73
+
74
+ # Adds an error result with the given message.
75
+ #
76
+ # @param [String] message
77
+ # The message of the result.
78
+ #
79
+ # @return [void]
80
+ #
81
+ def add_error(attribute_name, message, public_only = false)
82
+ add_result(:error, attribute_name, message, public_only)
83
+ end
84
+
85
+ # Adds a warning result with the given message.
86
+ #
87
+ # @param [String] message
88
+ # The message of the result.
89
+ #
90
+ # @return [void]
91
+ #
92
+ def add_warning(attribute_name, message, public_only = false)
93
+ add_result(:warning, attribute_name, message, public_only)
94
+ end
95
+
96
+ private
97
+
98
+ # @return [Array<Result>] all of the generated results.
99
+ #
100
+ attr_reader :results
101
+
102
+ # Adds a result of the given type with the given message. If there is a
103
+ # current platform it is added to the result. If a result with the same
104
+ # type and the same message is already available the current platform is
105
+ # added to the existing result.
106
+ #
107
+ # @param [Symbol] type
108
+ # The type of the result (`:error`, `:warning`).
109
+ #
110
+ # @param [String] message
111
+ # The message of the result.
112
+ #
113
+ # @return [void]
114
+ #
115
+ def add_result(type, attribute_name, message, public_only)
116
+ result = results.find do |r|
117
+ r.type == type && r.attribute_name == attribute_name && r.message == message && r.public_only? == public_only
118
+ end
119
+ unless result
120
+ result = Result.new(type, attribute_name, message, public_only)
121
+ results << result
122
+ end
123
+ result.platforms << @consumer.platform_name if @consumer
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end