cocoapods-core 0.17.0.rc1

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 (36) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +36 -0
  4. data/lib/cocoapods-core/core_ui.rb +19 -0
  5. data/lib/cocoapods-core/dependency.rb +295 -0
  6. data/lib/cocoapods-core/gem_version.rb +6 -0
  7. data/lib/cocoapods-core/lockfile.rb +440 -0
  8. data/lib/cocoapods-core/platform.rb +171 -0
  9. data/lib/cocoapods-core/podfile/dsl.rb +459 -0
  10. data/lib/cocoapods-core/podfile/target_definition.rb +503 -0
  11. data/lib/cocoapods-core/podfile.rb +345 -0
  12. data/lib/cocoapods-core/requirement.rb +15 -0
  13. data/lib/cocoapods-core/source/validator.rb +183 -0
  14. data/lib/cocoapods-core/source.rb +284 -0
  15. data/lib/cocoapods-core/specification/consumer.rb +356 -0
  16. data/lib/cocoapods-core/specification/dsl/attribute.rb +245 -0
  17. data/lib/cocoapods-core/specification/dsl/attribute_support.rb +76 -0
  18. data/lib/cocoapods-core/specification/dsl/deprecations.rb +47 -0
  19. data/lib/cocoapods-core/specification/dsl/platform_proxy.rb +67 -0
  20. data/lib/cocoapods-core/specification/dsl.rb +1110 -0
  21. data/lib/cocoapods-core/specification/linter.rb +436 -0
  22. data/lib/cocoapods-core/specification/root_attribute_accessors.rb +152 -0
  23. data/lib/cocoapods-core/specification/set/presenter.rb +229 -0
  24. data/lib/cocoapods-core/specification/set/statistics.rb +277 -0
  25. data/lib/cocoapods-core/specification/set.rb +171 -0
  26. data/lib/cocoapods-core/specification/yaml.rb +60 -0
  27. data/lib/cocoapods-core/specification.rb +592 -0
  28. data/lib/cocoapods-core/standard_error.rb +84 -0
  29. data/lib/cocoapods-core/vendor/dependency.rb +264 -0
  30. data/lib/cocoapods-core/vendor/requirement.rb +208 -0
  31. data/lib/cocoapods-core/vendor/version.rb +333 -0
  32. data/lib/cocoapods-core/vendor.rb +56 -0
  33. data/lib/cocoapods-core/version.rb +99 -0
  34. data/lib/cocoapods-core/yaml_converter.rb +202 -0
  35. data/lib/cocoapods-core.rb +23 -0
  36. metadata +154 -0
@@ -0,0 +1,436 @@
1
+ module Pod
2
+ class Specification
3
+
4
+ # The Linter check specifications for errors and warnings.
5
+ #
6
+ # It is designed not only to guarantee the formal functionality of a
7
+ # specification, but also to support the maintenance of sources.
8
+ #
9
+ class Linter
10
+
11
+ # @return [Specification] the specification to lint.
12
+ #
13
+ attr_reader :spec
14
+
15
+ # @return [Pathname] the path of the `podspec` file where {#spec} is
16
+ # defined.
17
+ #
18
+ attr_reader :file
19
+
20
+ # @param [Specification, Pathname, String] spec_or_path
21
+ # the Specification or the path of the `podspec` file to lint.
22
+ #
23
+ def initialize(spec_or_path)
24
+ if spec_or_path.is_a?(Specification)
25
+ @spec = spec_or_path
26
+ @file = @spec.defined_in_file
27
+ else
28
+ @file = Pathname.new(spec_or_path)
29
+ begin
30
+ @spec = Specification.from_file(@file)
31
+ rescue Exception => e
32
+ @spec = nil
33
+ @raise_message = e.message
34
+ end
35
+ end
36
+ end
37
+
38
+ # Lints the specification adding a {Result} for any failed check to the
39
+ # {#results} list.
40
+ #
41
+ # @return [Bool] whether the specification passed validation.
42
+ #
43
+ def lint
44
+ @results = []
45
+ if spec
46
+ perform_textual_analysis
47
+ check_required_root_attributes
48
+ run_root_validation_hooks
49
+ perform_all_specs_ananlysis
50
+ else
51
+ error "The specification defined in `#{file}` could not be loaded." \
52
+ "\n\n#{@raise_message}"
53
+ end
54
+ results.empty?
55
+ end
56
+
57
+ #-----------------------------------------------------------------------#
58
+
59
+ # !@group Lint results
60
+
61
+ public
62
+
63
+ # @return [Array<Result>] all the results generated by the Linter.
64
+ #
65
+ attr_reader :results
66
+
67
+ # @return [Array<Result>] all the errors generated by the Linter.
68
+ #
69
+ def errors
70
+ @errors ||= results.select { |r| r.type == :error }
71
+ end
72
+
73
+ # @return [Array<Result>] all the warnings generated by the Linter.
74
+ #
75
+ def warnings
76
+ @warnings ||= results.select { |r| r.type == :warning }
77
+ end
78
+
79
+ #-----------------------------------------------------------------------#
80
+
81
+ private
82
+
83
+ # !@group Lint steps
84
+
85
+ # It reads a podspec file and checks for strings corresponding
86
+ # to features that are or will be deprecated
87
+ #
88
+ # @return [void]
89
+ #
90
+ def perform_textual_analysis
91
+ return unless @file
92
+ text = @file.read
93
+ error "`config.ios?' and `config.osx?' are deprecated." if text =~ /config\..?os.?/
94
+ error "clean_paths are deprecated (use preserve_paths)." if text =~ /clean_paths/
95
+ warning "Comments must be deleted." if text.scan(/^\s*#/).length > 24
96
+ end
97
+
98
+ # Checks that every root only attribute which is required has a value.
99
+ #
100
+ # @return [void]
101
+ #
102
+ def check_required_root_attributes
103
+ attributes = DSL.attributes.values.select(&:root_only?)
104
+ attributes.each do |attr|
105
+ # if spec.respond_to?(attr.name)
106
+ value = spec.send(attr.name)
107
+ next unless attr.required?
108
+ unless value && (!value.respond_to?(:empty?) || !value.empty?)
109
+ if attr.name == :license
110
+ warning("Missing required attribute `#{attr.name}`.")
111
+ else
112
+ error("Missing required attribute `#{attr.name}`.")
113
+ end
114
+ end
115
+ end
116
+ # end
117
+ end
118
+
119
+ # Runs the validation hook for root only attributes.
120
+ #
121
+ # @return [void]
122
+ #
123
+ def run_root_validation_hooks
124
+ attributes = DSL.attributes.values.select(&:root_only?)
125
+ attributes.each do |attr|
126
+ validation_hook = "_validate_#{attr.name}"
127
+ next unless respond_to?(validation_hook, true)
128
+ value = spec.send(attr.name)
129
+ next unless value
130
+ send(validation_hook, value)
131
+ end
132
+ end
133
+
134
+ # Run validations for multi-platform attributes activating .
135
+ #
136
+ # @return [void]
137
+ #
138
+ def perform_all_specs_ananlysis
139
+ all_specs = [ spec, *spec.recursive_subspecs ]
140
+ all_specs.each do |current_spec|
141
+ current_spec.available_platforms.each do |platform|
142
+ @consumer = Specification::Consumer.new(current_spec, platform)
143
+ run_all_specs_valudation_hooks
144
+ validate_file_patterns
145
+ check_tmp_arc_not_nil
146
+ check_if_spec_is_empty
147
+ @consumer = nil
148
+ end
149
+ end
150
+ end
151
+
152
+ # @return [Specification::Consumer] the current consumer.
153
+ #
154
+ attr_accessor :consumer
155
+
156
+ # Runs the validation hook for the attributes that are not root only.
157
+ #
158
+ # @return [void]
159
+ #
160
+ def run_all_specs_valudation_hooks
161
+ attributes = DSL.attributes.values.reject(&:root_only?)
162
+ attributes.each do |attr|
163
+ validation_hook = "_validate_#{attr.name}"
164
+ next unless respond_to?(validation_hook, true)
165
+ value = consumer.send(attr.name)
166
+ next unless value
167
+ send(validation_hook, value)
168
+ end
169
+ end
170
+
171
+ # Runs the validation hook for each attribute.
172
+ #
173
+ # @note Hooks are called only if there is a value for the attribute as
174
+ # required attributes are already checked by the
175
+ # {#check_required_root_attributes} step.
176
+ #
177
+ # @return [void]
178
+ #
179
+ def run_validation_hooks(attributes)
180
+
181
+ end
182
+
183
+ #-----------------------------------------------------------------------#
184
+
185
+ private
186
+
187
+ # @!group Root spec validation helpers
188
+
189
+ # Performs validations related to the `name` attribute.
190
+ #
191
+ def _validate_name(n)
192
+ if spec.name && file
193
+ names_match = (file.basename.to_s == spec.root.name + '.podspec')
194
+ unless names_match
195
+ error "The name of the spec should match the name of the file."
196
+ end
197
+ end
198
+ end
199
+
200
+ # Performs validations related to the `summary` attribute.
201
+ #
202
+ def _validate_summary(s)
203
+ warning "The summary should be short use `description` (max 140 characters)." if s.length > 140
204
+ warning "The summary is not meaningful." if s =~ /A short description of/
205
+ warning "The summary should end with proper punctuation." if s !~ /(\.|\?|!)$/
206
+ end
207
+
208
+ # Performs validations related to the `description` attribute.
209
+ #
210
+ def _validate_description(d)
211
+ warning "The description is not meaningful." if d =~ /An optional longer description of/
212
+ warning "The description should end with proper punctuation." if d !~ /(\.|\?|!)$/
213
+ warning "The description is equal to the summary." if d == spec.summary
214
+ warning "The description is shorter than the summary." if d.length < spec.summary.length
215
+ end
216
+
217
+ # Performs validations related to the `license` attribute.
218
+ #
219
+ def _validate_license(l)
220
+ type = l[:type]
221
+ warning "Missing license type." if type.nil?
222
+ warning "Sample license type." if type && type =~ /\(example\)/
223
+ warning "Invalid license type." if type && type.gsub(' ', '').gsub("\n", '').empty?
224
+ end
225
+
226
+ # Performs validations related to the `source` attribute.
227
+ #
228
+ def _validate_source(s)
229
+ if git = s[:git]
230
+ tag, commit = s.values_at(:tag, :commit)
231
+ github = git.include?('github.com')
232
+ version = spec.version.to_s
233
+
234
+ error "Example source." if git =~ /http:\/\/EXAMPLE/
235
+ error 'The commit of a Git source cannot be `HEAD`.' if commit && commit.downcase =~ /head/
236
+ warning 'The version should be included in the Git tag.' if tag && !tag.include?(version)
237
+ warning "Github repositories should end in `.git`." if github && !git.end_with?('.git')
238
+ warning "Github repositories should use `https` link." if github && !git.start_with?('https://github.com') && !git.start_with?('git://gist.github.com')
239
+
240
+ if version == '0.0.1'
241
+ error 'Git sources should specify either a commit or a tag.' if commit.nil? && tag.nil?
242
+ else
243
+ warning 'Git sources should specify a tag.' if tag.nil?
244
+ end
245
+ end
246
+ end
247
+
248
+ #-----------------------------------------------------------------------#
249
+
250
+ # @!group All specs validation helpers
251
+
252
+ private
253
+
254
+ # Performs validations related to the `compiler_flags` attribute.
255
+ #
256
+ def _validate_compiler_flags(flags)
257
+ if flags.join(' ').split(' ').any? { |flag| flag.start_with?('-Wno') }
258
+ warning "Warnings must not be disabled (`-Wno' compiler flags)."
259
+ end
260
+ end
261
+
262
+ # Checks the attributes that represent file patterns.
263
+ #
264
+ # @todo Check the attributes hash directly.
265
+ #
266
+ def validate_file_patterns
267
+ attributes = DSL.attributes.values.select(&:file_patterns?)
268
+ attributes.each do |attrb|
269
+ patterns = consumer.send(attrb.name)
270
+ if patterns.is_a?(Hash)
271
+ patterns = patterns.values.flatten(1)
272
+ end
273
+ patterns.each do |pattern|
274
+ if pattern.is_a?(Rake::FileList)
275
+ error "Rake::FileList is deprecated, use `exclude_files` (#{attrb.name})."
276
+ else
277
+ if pattern.start_with?('/')
278
+ error "File patterns must be relative and cannot start with a slash (#{attrb.name})."
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ # @todo remove in 0.18 and switch the default to true.
286
+ #
287
+ def check_tmp_arc_not_nil
288
+ if consumer.requires_arc.nil?
289
+ warning "A value for `requires_arc` should be specified until the migration to a `true` default."
290
+ end
291
+ end
292
+
293
+ # Check empty subspec attributes
294
+ #
295
+ def check_if_spec_is_empty
296
+ methods = %w[ source_files resources preserve_paths subspecs ]
297
+ empty = methods.all? { |m| consumer.send(m).empty? }
298
+ if empty
299
+ error "The spec appears to be empty (no source files, resources, or preserve paths)."
300
+ end
301
+ end
302
+
303
+ #-----------------------------------------------------------------------#
304
+
305
+ # !@group Result Helpers
306
+
307
+ private
308
+
309
+ # Adds an error result with the given message.
310
+ #
311
+ # @param [String] message
312
+ # The message of the result.
313
+ #
314
+ # @return [void]
315
+ #
316
+ def error(message)
317
+ add_result(:error, message)
318
+ end
319
+
320
+ # Adds an warning result with the given message.
321
+ #
322
+ # @param [String] message
323
+ # The message of the result.
324
+ #
325
+ # @return [void]
326
+ #
327
+ def warning(message)
328
+ add_result(:warning, message)
329
+ end
330
+
331
+ # Adds a result of the given type with the given message. If there is a
332
+ # current platform it is added to the result. If a result with the same
333
+ # type and the same message is already available the current platform is
334
+ # added to the existing result.
335
+ #
336
+ # @param [Symbol] type
337
+ # The type of the result (`:error`, `:warning`).
338
+ #
339
+ # @param [String] message
340
+ # The message of the result.
341
+ #
342
+ # @return [void]
343
+ #
344
+ def add_result(type, message)
345
+ result = results.find { |r| r.type == type && r.message == message }
346
+ unless result
347
+ result = Result.new(type, message)
348
+ results << result
349
+ end
350
+ result.platforms << consumer.platform_name if consumer
351
+ end
352
+
353
+ #-----------------------------------------------------------------------#
354
+
355
+ class Result
356
+
357
+ # @return [Symbol] the type of result.
358
+ #
359
+ attr_reader :type
360
+
361
+ # @return [String] the message associated with result.
362
+ #
363
+ attr_reader :message
364
+
365
+ # @param [Symbol] type @see type
366
+ # @param [String] message @see message
367
+ #
368
+ def initialize(type, message)
369
+ @type = type
370
+ @message = message
371
+ @platforms = []
372
+ end
373
+
374
+ # @return [Array<Platform>] the platforms where this result was
375
+ # generated.
376
+ #
377
+ attr_reader :platforms
378
+
379
+ # @return [String] a string representation suitable for UI output.
380
+ #
381
+ def to_s
382
+ r = "[#{type.to_s.upcase}] #{message}"
383
+ if platforms != Specification::PLATFORMS
384
+ platforms_names = platforms.uniq.map { |p| Platform.string_name(p) }
385
+ r << " [#{platforms_names * ' - '}]" unless platforms.empty?
386
+ end
387
+ r
388
+ end
389
+ end
390
+
391
+ #-----------------------------------------------------------------------#
392
+
393
+ end
394
+ end
395
+ end
396
+
397
+ # # TODO
398
+ # # Converts the resources file patterns to a hash defaulting to the
399
+ # # resource key if they are defined as an Array or a String.
400
+ # #
401
+ # # @param [String, Array, Hash] value.
402
+ # # The value of the attribute as specified by the user.
403
+ # #
404
+ # # @return [Hash] the resources.
405
+ # #
406
+ # def _prepare_deployment_target(deployment_target)
407
+ # unless @define_for_platforms.count == 1
408
+ # raise StandardError, "The deployment target must be defined per platform like `s.ios.deployment_target = '5.0'`."
409
+ # end
410
+ # Version.new(deployment_target)
411
+ # end
412
+
413
+ # # TODO
414
+ # # Converts the resources file patterns to a hash defaulting to the
415
+ # # resource key if they are defined as an Array or a String.
416
+ # #
417
+ # # @param [String, Array, Hash] value.
418
+ # # The value of the attribute as specified by the user.
419
+ # #
420
+ # # @return [Hash] the resources.
421
+ # #
422
+ # def _prepare_platform(name_and_deployment_target)
423
+ # return nil if name_and_deployment_target.nil?
424
+ # if name_and_deployment_target.is_a?(Array)
425
+ # name = name_and_deployment_target.first
426
+ # deployment_target = name_and_deployment_target.last
427
+ # else
428
+ # name = name_and_deployment_target
429
+ # deployment_target = nil
430
+ # end
431
+ # unless PLATFORMS.include?(name)
432
+ # raise StandardError, "Unsupported platform `#{name}`. The available " \
433
+ # "names are `#{PLATFORMS.inspect}`"
434
+ # end
435
+ # Platform.new(name, deployment_target)
436
+ # end
@@ -0,0 +1,152 @@
1
+ module Pod
2
+ class Specification
3
+ module DSL
4
+
5
+ # Provides the accessors methods for the root attributes. Root attributes
6
+ # do not support multiplatform values and inheritance.
7
+ #
8
+ module RootAttributesAccessors
9
+
10
+ # @return [String] The name of the specification *not* including the
11
+ # names of the parents, in case of ‘sub-specifications’.
12
+ #
13
+ def base_name
14
+ attributes_hash["name"]
15
+ end
16
+
17
+ # @return [String] The name of the specification including the names of
18
+ # the parents, in case of ‘sub-specifications’.
19
+ #
20
+ def name
21
+ parent ? "#{parent.name}/#{base_name}" : base_name
22
+ end
23
+
24
+ # @return [Version] The version of the Pod.
25
+ #
26
+ # @todo The version is memoized because the Resolvers sets the head
27
+ # state on it. This information should be stored in the
28
+ # specification instance and the lockfile should have a more
29
+ # robust handling of head versions (like a dedicated section).
30
+ #
31
+ def version
32
+ @version ||= root? ? Version.new(attributes_hash["version"]) : root.version
33
+ end
34
+
35
+ # @return [Hash] a hash containing the authors as the keys and their
36
+ # email address as the values.
37
+ #
38
+ # @note The value is coherced to a hash with a nil email if needed.
39
+ #
40
+ # @example Possible values
41
+ #
42
+ # { 'Author' => 'email@host.com' }
43
+ # [ 'Author', { 'Author_2' => 'email@host.com' } ]
44
+ # [ 'Author', 'Author_2' ]
45
+ # 'Author'
46
+ #
47
+ def authors
48
+ authors = attributes_hash["authors"]
49
+ if authors.is_a?(Hash)
50
+ authors
51
+ elsif authors.is_a?(Array)
52
+ result = {}
53
+ authors.each do |name_or_hash|
54
+ if name_or_hash.is_a?(String)
55
+ result[name_or_hash] = nil
56
+ else
57
+ result.merge!(name_or_hash)
58
+ end
59
+ end
60
+ result
61
+ elsif authors.is_a?(String)
62
+ { authors => nil }
63
+ end
64
+ end
65
+
66
+ # @return [Hash] A hash containing the license information of the Pod.
67
+ #
68
+ # @note The indentation is stripped from the license text.
69
+ #
70
+ def license
71
+ license = attributes_hash["license"]
72
+ if license.is_a?(String)
73
+ { :type => license }
74
+ elsif license.is_a?(Hash)
75
+ license = convert_keys_to_symbol(license)
76
+ license[:text] = license[:text].strip_heredoc if license[:text]
77
+ license
78
+ else
79
+ {}
80
+ end
81
+ end
82
+
83
+ # @return [String] The URL of the homepage of the Pod.
84
+ #
85
+ def homepage
86
+ attributes_hash["homepage"]
87
+ end
88
+
89
+ # @return [Hash{Symbol=>String}] The location from where the library
90
+ # should be retrieved.
91
+ #
92
+ def source
93
+ convert_keys_to_symbol(attributes_hash["source"])
94
+ end
95
+
96
+ # @return [String] A short description of the Pod.
97
+ #
98
+ def summary
99
+ attributes_hash["summary"]
100
+ end
101
+
102
+ # @return [String] A longer description of the Pod.
103
+ #
104
+ # @note The indentation is stripped from the description.
105
+ #
106
+ def description
107
+ description = attributes_hash["description"]
108
+ description.strip_heredoc if description
109
+ end
110
+
111
+ # @return [Array<String>] The list of the URL for the screenshots of the
112
+ # Pod.
113
+ #
114
+ # @note The value is coherced to an array.
115
+ #
116
+ def screenshots
117
+ value = attributes_hash["screenshots"]
118
+ [*value]
119
+ end
120
+
121
+ # @return [Hash{Symbol=>Array<String>}] The options to pass to the
122
+ # appledoc tool.
123
+ #
124
+ def documentation
125
+ convert_keys_to_symbol(attributes_hash["documentation"])
126
+ end
127
+
128
+ #-----------------------------------------------------------------------#
129
+
130
+ private
131
+
132
+ # Converts the keys of the given hash to a string.
133
+ #
134
+ # @param [Object] value
135
+ # the value that needs to be stripped from the Symbols.
136
+ #
137
+ # @return [Hash] the hash with the strings instead of the keys.
138
+ #
139
+ def convert_keys_to_symbol(value)
140
+ return unless value
141
+ result = {}
142
+ value.each do |key, subvalue|
143
+ subvalue = convert_keys_to_symbol(subvalue) if subvalue.is_a?(Hash)
144
+ result[key.to_sym] = subvalue
145
+ end
146
+ result
147
+ end
148
+
149
+ end
150
+ end
151
+ end
152
+ end