cocoapods-core 0.30.0 → 1.15.2

Sign up to get free protection for your applications and to get access to all the features.
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,13 +1,14 @@
1
+ require 'cocoapods-core/specification/linter/result'
2
+ require 'cocoapods-core/specification/linter/analyzer'
3
+
1
4
  module Pod
2
5
  class Specification
3
-
4
6
  # The Linter check specifications for errors and warnings.
5
7
  #
6
8
  # It is designed not only to guarantee the formal functionality of a
7
9
  # specification, but also to support the maintenance of sources.
8
10
  #
9
11
  class Linter
10
-
11
12
  # @return [Specification] the specification to lint.
12
13
  #
13
14
  attr_reader :spec
@@ -17,6 +18,8 @@ module Pod
17
18
  #
18
19
  attr_reader :file
19
20
 
21
+ attr_reader :results
22
+
20
23
  # @param [Specification, Pathname, String] spec_or_path
21
24
  # the Specification or the path of the `podspec` file to lint.
22
25
  #
@@ -28,7 +31,7 @@ module Pod
28
31
  @file = Pathname.new(spec_or_path)
29
32
  begin
30
33
  @spec = Specification.from_file(@file)
31
- rescue Exception => e
34
+ rescue => e
32
35
  @spec = nil
33
36
  @raise_message = e.message
34
37
  end
@@ -36,20 +39,21 @@ module Pod
36
39
  end
37
40
 
38
41
  # Lints the specification adding a {Result} for any failed check to the
39
- # {#results} list.
42
+ # {#results} object.
40
43
  #
41
- # @return [Bool] whether the specification passed validation.
44
+ # @return [Boolean] whether the specification passed validation.
42
45
  #
43
46
  def lint
44
- @results = []
47
+ @results = Results.new
45
48
  if spec
46
- perform_textual_analysis
47
- check_required_root_attributes
49
+ validate_root_name
50
+ check_required_attributes
51
+ check_requires_arc_attribute
48
52
  run_root_validation_hooks
49
53
  perform_all_specs_analysis
50
54
  else
51
- error "The specification defined in `#{file}` could not be loaded." \
52
- "\n\n#{@raise_message}"
55
+ results.add_error('spec', "The specification defined in `#{file}` "\
56
+ "could not be loaded.\n\n#{@raise_message}")
53
57
  end
54
58
  results.empty?
55
59
  end
@@ -60,10 +64,6 @@ module Pod
60
64
 
61
65
  public
62
66
 
63
- # @return [Array<Result>] all the results generated by the Linter.
64
- #
65
- attr_reader :results
66
-
67
67
  # @return [Array<Result>] all the errors generated by the Linter.
68
68
  #
69
69
  def errors
@@ -82,48 +82,58 @@ module Pod
82
82
 
83
83
  # !@group Lint steps
84
84
 
85
- # It reads a podspec file and checks for strings corresponding
86
- # to features that are or will be deprecated
85
+ # Checks that the spec's root name matches the filename.
87
86
  #
88
87
  # @return [void]
89
88
  #
90
- def perform_textual_analysis
91
- return unless @file
92
- text = @file.read
93
- if text =~ /config\..?os.?/
94
- error "`config.ios?` and `config.osx?` are deprecated."
95
- end
96
- if text =~ /clean_paths/
97
- error "clean_paths are deprecated (use preserve_paths)."
89
+ def validate_root_name
90
+ if spec.root.name && file
91
+ acceptable_names = [
92
+ spec.root.name + '.podspec',
93
+ spec.root.name + '.podspec.json',
94
+ ]
95
+ names_match = acceptable_names.include?(file.basename.to_s)
96
+ unless names_match
97
+ results.add_error('name', 'The name of the spec should match the ' \
98
+ 'name of the file.')
99
+ end
98
100
  end
101
+ end
99
102
 
100
- all_lines_count = text.lines.count
101
- comments_lines_count = text.scan(/^\s*#\s+/).length
102
- comments_ratio = comments_lines_count.fdiv(all_lines_count)
103
- if comments_lines_count > 20 && comments_ratio > 0.2
104
- warning "Comments must be deleted."
105
- end
106
- if text.lines.first =~ /^\s*#\s+/
107
- warning "Comments placed at the top of the specification must be " \
108
- "deleted."
103
+ # Generates a warning if the requires_arc attribute has true or false string values.
104
+ #
105
+ # @return [void]
106
+ #
107
+ def check_requires_arc_attribute
108
+ attribute = DSL.attributes.values.find { |attr| attr.name == :requires_arc }
109
+ if attribute
110
+ value = spec.send(attribute.name)
111
+ if value == 'true' || value == 'false'
112
+ results.add_warning('requires_arc', value + ' is considered to be the name of a file.')
113
+ end
109
114
  end
110
115
  end
111
116
 
112
- # Checks that every root only attribute which is required has a value.
117
+ # Checks that every required attribute has a value.
113
118
  #
114
119
  # @return [void]
115
120
  #
116
- def check_required_root_attributes
117
- attributes = DSL.attributes.values.select(&:root_only?)
121
+ def check_required_attributes
122
+ attributes = DSL.attributes.values.select(&:required?)
118
123
  attributes.each do |attr|
119
- value = spec.send(attr.name)
120
- next unless attr.required?
121
- unless value && (!value.respond_to?(:empty?) || !value.empty?)
122
- if attr.name == :license
123
- warning("Missing required attribute `#{attr.name}`.")
124
- else
125
- error("Missing required attribute `#{attr.name}`.")
124
+ begin
125
+ value = spec.send(attr.name)
126
+ unless value && (!value.respond_to?(:empty?) || !value.empty?)
127
+ if attr.name == :license
128
+ results.add_warning('attributes', 'Missing required attribute ' \
129
+ "`#{attr.name}`.")
130
+ else
131
+ results.add_error('attributes', 'Missing required attribute ' \
132
+ "`#{attr.name}`.")
133
+ end
126
134
  end
135
+ rescue => exception
136
+ results.add_error('attributes', "Unable to parse attribute `#{attr.name}` due to error: #{exception}")
127
137
  end
128
138
  end
129
139
  end
@@ -134,16 +144,10 @@ module Pod
134
144
  #
135
145
  def run_root_validation_hooks
136
146
  attributes = DSL.attributes.values.select(&:root_only?)
137
- attributes.each do |attr|
138
- validation_hook = "_validate_#{attr.name}"
139
- next unless respond_to?(validation_hook, true)
140
- value = spec.send(attr.name)
141
- next unless value
142
- send(validation_hook, value)
143
- end
147
+ run_validation_hooks(attributes, spec)
144
148
  end
145
149
 
146
- # Run validations for multi-platform attributes activating .
150
+ # Run validations for multi-platform attributes activating.
147
151
  #
148
152
  # @return [void]
149
153
  #
@@ -152,12 +156,12 @@ module Pod
152
156
  all_specs.each do |current_spec|
153
157
  current_spec.available_platforms.each do |platform|
154
158
  @consumer = Specification::Consumer.new(current_spec, platform)
159
+ results.consumer = @consumer
155
160
  run_all_specs_validation_hooks
156
- validate_file_patterns
157
- check_tmp_arc_not_nil
158
- check_if_spec_is_empty
159
- check_install_hooks
161
+ analyzer = Analyzer.new(@consumer, results)
162
+ results = analyzer.analyze
160
163
  @consumer = nil
164
+ results.consumer = nil
161
165
  end
162
166
  end
163
167
  end
@@ -172,57 +176,81 @@ module Pod
172
176
  #
173
177
  def run_all_specs_validation_hooks
174
178
  attributes = DSL.attributes.values.reject(&:root_only?)
175
- attributes.each do |attr|
176
- validation_hook = "_validate_#{attr.name}"
177
- next unless respond_to?(validation_hook, true)
178
- value = consumer.send(attr.name)
179
- next unless value
180
- send(validation_hook, value)
181
- end
179
+ run_validation_hooks(attributes, consumer)
182
180
  end
183
181
 
184
182
  # Runs the validation hook for each attribute.
185
183
  #
186
184
  # @note Hooks are called only if there is a value for the attribute as
187
185
  # required attributes are already checked by the
188
- # {#check_required_root_attributes} step.
186
+ # {#check_required_attributes} step.
189
187
  #
190
188
  # @return [void]
191
189
  #
192
- # def run_validation_hooks(attributes)
193
- #
194
- # end
190
+ def run_validation_hooks(attributes, target)
191
+ attributes.each do |attr|
192
+ validation_hook = "_validate_#{attr.name}"
193
+ next unless respond_to?(validation_hook, true)
194
+ begin
195
+ value = target.send(attr.name)
196
+ next unless value
197
+ send(validation_hook, value)
198
+ rescue => e
199
+ results.add_error(attr.name, "Unable to validate due to exception: #{e}")
200
+ end
201
+ end
202
+ end
195
203
 
196
204
  #-----------------------------------------------------------------------#
197
205
 
198
206
  private
199
207
 
200
- # @!group Root spec validation helpers
201
-
202
208
  # Performs validations related to the `name` attribute.
203
209
  #
204
- def _validate_name(n)
205
- if spec.name && file
206
- acceptable_names = [
207
- spec.root.name + '.podspec',
208
- spec.root.name + '.podspec.json',
209
- ]
210
- names_match = acceptable_names.include?(file.basename.to_s)
211
- unless names_match
212
- error "The name of the spec should match the name of the file."
213
- end
210
+ def _validate_name(name)
211
+ if name =~ %r{/}
212
+ results.add_error('name', 'The name of a spec should not contain ' \
213
+ 'a slash.')
214
+ end
215
+
216
+ if name =~ /\s/
217
+ results.add_error('name', 'The name of a spec should not contain ' \
218
+ 'whitespace.')
219
+ end
220
+
221
+ if name[0, 1] == '.'
222
+ results.add_error('name', 'The name of a spec should not begin' \
223
+ ' with a period.')
224
+ end
225
+ end
226
+
227
+ # @!group Root spec validation helpers
214
228
 
215
- if spec.root.name =~ /\s/
216
- error "The name of a spec should not contain whitespace."
229
+ # Performs validations related to the `authors` attribute.
230
+ #
231
+ def _validate_authors(a)
232
+ if a.is_a? Hash
233
+ if a == { 'YOUR NAME HERE' => 'YOUR EMAIL HERE' }
234
+ results.add_error('authors', 'The authors have not been updated ' \
235
+ 'from default')
217
236
  end
218
237
  end
219
238
  end
220
239
 
240
+ # Performs validations related to the `version` attribute.
241
+ #
221
242
  def _validate_version(v)
222
243
  if v.to_s.empty?
223
- error "A version is required."
224
- elsif v <= Version::ZERO
225
- error "The version of the spec should be higher than 0."
244
+ results.add_error('version', 'A version is required.')
245
+ end
246
+ end
247
+
248
+ # Performs validations related to the `module_name` attribute.
249
+ #
250
+ def _validate_module_name(m)
251
+ unless m.nil? || m =~ /^[a-z_][0-9a-z_]*$/i
252
+ results.add_error('module_name', 'The module name of a spec' \
253
+ ' should be a valid C99 identifier.')
226
254
  end
227
255
  end
228
256
 
@@ -230,33 +258,37 @@ module Pod
230
258
  #
231
259
  def _validate_summary(s)
232
260
  if s.length > 140
233
- warning "The summary should be a short version of `description` " \
234
- "(max 140 characters)."
261
+ results.add_warning('summary', 'The summary should be a short ' \
262
+ 'version of `description` (max 140 characters).')
235
263
  end
236
264
  if s =~ /A short description of/
237
- warning "The summary is not meaningful."
265
+ results.add_warning('summary', 'The summary is not meaningful.')
238
266
  end
239
267
  end
240
268
 
241
269
  # Performs validations related to the `description` attribute.
242
270
  #
243
271
  def _validate_description(d)
244
- if d =~ /An optional longer description of/
245
- warning "The description is not meaningful."
246
- end
247
272
  if d == spec.summary
248
- warning "The description is equal to the summary."
273
+ results.add_warning('description', 'The description is equal to' \
274
+ ' the summary.')
249
275
  end
250
- if d.length < spec.summary.length
251
- warning "The description is shorter than the summary."
276
+
277
+ if d.strip.empty?
278
+ results.add_error('description', 'The description is empty.')
279
+ elsif spec.summary && d.length < spec.summary.length
280
+ results.add_warning('description', 'The description is shorter ' \
281
+ 'than the summary.')
252
282
  end
253
283
  end
254
284
 
255
285
  # Performs validations related to the `homepage` attribute.
256
286
  #
257
287
  def _validate_homepage(h)
288
+ return unless h.is_a?(String)
258
289
  if h =~ %r{http://EXAMPLE}
259
- warning "The homepage has not been updated from default"
290
+ results.add_warning('homepage', 'The homepage has not been updated' \
291
+ ' from default')
260
292
  end
261
293
  end
262
294
 
@@ -264,7 +296,8 @@ module Pod
264
296
  #
265
297
  def _validate_frameworks(frameworks)
266
298
  if frameworks_invalid?(frameworks)
267
- error "A framework should only be specified by its name"
299
+ results.add_error('frameworks', 'A framework should only be' \
300
+ ' specified by its name')
268
301
  end
269
302
  end
270
303
 
@@ -272,15 +305,32 @@ module Pod
272
305
  #
273
306
  def _validate_weak_frameworks(frameworks)
274
307
  if frameworks_invalid?(frameworks)
275
- error "A weak framework should only be specified by its name"
308
+ results.add_error('weak_frameworks', 'A weak framework should only be' \
309
+ ' specified by its name')
276
310
  end
277
311
  end
278
312
 
279
313
  # Performs validations related to the `libraries` attribute.
280
314
  #
281
315
  def _validate_libraries(libs)
282
- if libraries_invalid?(libs)
283
- error "A library should only be specified by its name"
316
+ libs.each do |lib|
317
+ lib = lib.downcase
318
+ if lib.end_with?('.a') || lib.end_with?('.dylib')
319
+ results.add_error('libraries', 'Libraries should not include the' \
320
+ ' extension ' \
321
+ "(`#{lib}`)")
322
+ end
323
+
324
+ if lib.start_with?('lib')
325
+ results.add_error('libraries', 'Libraries should omit the `lib`' \
326
+ ' prefix ' \
327
+ " (`#{lib}`)")
328
+ end
329
+
330
+ if lib.include?(',')
331
+ results.add_error('libraries', 'Libraries should not include comas ' \
332
+ "(`#{lib}`)")
333
+ end
284
334
  end
285
335
  end
286
336
 
@@ -288,294 +338,245 @@ module Pod
288
338
  #
289
339
  def _validate_license(l)
290
340
  type = l[:type]
341
+ file = l[:file]
291
342
  if type.nil?
292
- warning "Missing license type."
343
+ results.add_warning('license', 'Missing license type.')
293
344
  end
294
- if type && type.gsub(' ', '').gsub("\n", '').empty?
295
- warning "Invalid license type."
345
+ if type && type.delete(' ').delete("\n").empty?
346
+ results.add_warning('license', 'Invalid license type.')
296
347
  end
297
348
  if type && type =~ /\(example\)/
298
- error "Sample license type."
349
+ results.add_error('license', 'Sample license type.')
350
+ end
351
+ if file && Pathname.new(file).extname !~ /^(\.(txt|md|markdown|))?$/i
352
+ results.add_error('license', 'Invalid file type')
299
353
  end
300
354
  end
301
355
 
302
356
  # Performs validations related to the `source` attribute.
303
357
  #
304
358
  def _validate_source(s)
359
+ return unless s.is_a?(Hash)
305
360
  if git = s[:git]
306
361
  tag, commit = s.values_at(:tag, :commit)
307
362
  version = spec.version.to_s
308
363
 
309
364
  if git =~ %r{http://EXAMPLE}
310
- error "The Git source still contains the example URL."
365
+ results.add_error('source', 'The Git source still contains the ' \
366
+ 'example URL.')
311
367
  end
312
368
  if commit && commit.downcase =~ /head/
313
- error 'The commit of a Git source cannot be `HEAD`.'
369
+ results.add_error('source', 'The commit of a Git source cannot be' \
370
+ ' `HEAD`.')
314
371
  end
315
- if tag && !tag.include?(version)
316
- warning 'The version should be included in the Git tag.'
372
+ if tag && !tag.to_s.include?(version)
373
+ results.add_warning('source', 'The version should be included in' \
374
+ ' the Git tag.')
317
375
  end
318
-
319
- if version == '0.0.1'
320
- if commit.nil? && tag.nil?
321
- error 'Git sources should specify either a commit or a tag.'
322
- end
323
- else
324
- warning 'Git sources should specify a tag.' if tag.nil?
376
+ if tag.nil?
377
+ results.add_warning('source', 'Git sources should specify a tag.', true)
325
378
  end
326
379
  end
327
380
 
328
381
  perform_github_source_checks(s)
382
+ check_git_ssh_source(s)
329
383
  end
330
384
 
331
- # Performs validations related to github sources.
385
+ # Performs validations related to the `deprecated_in_favor_of` attribute.
332
386
  #
333
- def perform_github_source_checks(s)
334
- supported_domains = [
335
- 'https://github.com',
336
- 'https://gist.github.com',
337
- ]
338
-
339
- if git = s[:git]
340
- is_github = git.include?('github.com')
341
- if is_github
342
- unless git.end_with?('.git')
343
- warning "Github repositories should end in `.git`."
344
- end
345
- unless supported_domains.find { |domain| git.start_with?(domain) }
346
- warning "Github repositories should use `https` link."
347
- end
348
- end
387
+ def _validate_deprecated_in_favor_of(d)
388
+ if spec.root.name == Specification.root_name(d)
389
+ results.add_error('deprecated_in_favor_of', 'a spec cannot be ' \
390
+ 'deprecated in favor of itself')
349
391
  end
350
392
  end
351
393
 
352
- #-----------------------------------------------------------------------#
353
-
354
- # @!group All specs validation helpers
394
+ # Performs validations related to the `test_type` attribute.
395
+ #
396
+ def _validate_test_type(t)
397
+ supported_test_types = Specification::DSL::SUPPORTED_TEST_TYPES.map(&:to_s)
398
+ unless supported_test_types.include?(t.to_s)
399
+ results.add_error('test_type', "The test type `#{t}` is not supported. " \
400
+ "Supported test type values are #{supported_test_types}.")
401
+ end
402
+ end
355
403
 
356
- private
404
+ def _validate_app_host_name(n)
405
+ unless consumer.requires_app_host?
406
+ results.add_error('app_host_name', '`requires_app_host` must be set to ' \
407
+ '`true` when `app_host_name` is specified.')
408
+ end
357
409
 
358
- # Performs validations related to the `compiler_flags` attribute.
359
- #
360
- def _validate_compiler_flags(flags)
361
- if flags.join(' ').split(' ').any? { |flag| flag.start_with?('-Wno') }
362
- warning "Warnings must not be disabled (`-Wno' compiler flags)."
410
+ unless consumer.dependencies.map(&:name).include?(n)
411
+ results.add_error('app_host_name', "The app host name (#{n}) specified by `#{consumer.spec.name}` could " \
412
+ 'not be found. You must explicitly declare a dependency on that app spec.')
363
413
  end
364
414
  end
365
415
 
366
- # Checks the attributes that represent file patterns.
367
- #
368
- # @todo Check the attributes hash directly.
416
+ # Performs validations related to the `script_phases` attribute.
369
417
  #
370
- def validate_file_patterns
371
- attributes = DSL.attributes.values.select(&:file_patterns?)
372
- attributes.each do |attrb|
373
- patterns = consumer.send(attrb.name)
374
- if patterns.is_a?(Hash)
375
- patterns = patterns.values.flatten(1)
418
+ def _validate_script_phases(s)
419
+ s.each do |script_phase|
420
+ keys = script_phase.keys
421
+ unrecognized_keys = keys - Specification::ALL_SCRIPT_PHASE_KEYS
422
+ unless unrecognized_keys.empty?
423
+ results.add_error('script_phases', "Unrecognized option(s) `#{unrecognized_keys.join(', ')}` in script phase `#{script_phase[:name]}`. " \
424
+ "Available options are `#{Specification::ALL_SCRIPT_PHASE_KEYS.join(', ')}`.")
376
425
  end
377
- patterns.each do |pattern|
378
- if pattern.start_with?('/')
379
- error "File patterns must be relative and cannot start with a " \
380
- "slash (#{attrb.name})."
381
- end
426
+ missing_required_keys = Specification::SCRIPT_PHASE_REQUIRED_KEYS - keys
427
+ unless missing_required_keys.empty?
428
+ results.add_error('script_phases', "Missing required shell script phase options `#{missing_required_keys.join(', ')}` in script phase `#{script_phase[:name]}`.")
429
+ end
430
+ unless Specification::EXECUTION_POSITION_KEYS.include?(script_phase[:execution_position])
431
+ results.add_error('script_phases', "Invalid execution position value `#{script_phase[:execution_position]}` in shell script `#{script_phase[:name]}`. " \
432
+ "Available options are `#{Specification::EXECUTION_POSITION_KEYS.join(', ')}`.")
382
433
  end
383
434
  end
384
435
  end
385
436
 
386
- # @todo remove in 0.18 and switch the default to true.
437
+ # Performs validations related to the `on_demand_resources` attribute.
387
438
  #
388
- def check_tmp_arc_not_nil
389
- if consumer.requires_arc.nil?
390
- warning "A value for `requires_arc` should be specified until the " \
391
- "migration to a `true` default."
439
+ def _validate_on_demand_resources(h)
440
+ h.values.each do |value|
441
+ unless Specification::ON_DEMAND_RESOURCES_CATEGORY_KEYS.include?(value[:category])
442
+ results.add_error('on_demand_resources', "Invalid on demand resources category value `#{value[:category]}`. " \
443
+ "Available options are `#{Specification::ON_DEMAND_RESOURCES_CATEGORY_KEYS.join(', ')}`.")
444
+ end
392
445
  end
393
446
  end
394
447
 
395
- # Check empty subspec attributes
448
+ # Performs validation related to the `scheme` attribute.
396
449
  #
397
- def check_if_spec_is_empty
398
- methods = %w(source_files resources preserve_paths dependencies vendored_libraries vendored_frameworks)
399
- empty_patterns = methods.all? { |m| consumer.send(m).empty? }
400
- empty = empty_patterns && consumer.spec.subspecs.empty?
401
- if empty
402
- error "The #{consumer.spec} spec is empty (no source files, " \
403
- "resources, preserve paths, vendored libraries, " \
404
- "vendored frameworks, dependencies or subspecs)."
450
+ def _validate_scheme(s)
451
+ unless s.empty?
452
+ if consumer.spec.subspec? && consumer.spec.library_specification?
453
+ results.add_error('scheme', 'Scheme configuration is not currently supported for subspecs.')
454
+ return
455
+ end
456
+ if s.key?(:launch_arguments) && !s[:launch_arguments].is_a?(Array)
457
+ results.add_error('scheme', 'Expected an array for key `launch_arguments`.')
458
+ end
459
+ if s.key?(:environment_variables) && !s[:environment_variables].is_a?(Hash)
460
+ results.add_error('scheme', 'Expected a hash for key `environment_variables`.')
461
+ end
462
+ if s.key?(:code_coverage) && ![true, false].include?(s[:code_coverage])
463
+ results.add_error('scheme', 'Expected a boolean for key `code_coverage`.')
464
+ end
465
+ if s.key?(:parallelizable) && ![true, false].include?(s[:parallelizable])
466
+ results.add_error('scheme', 'Expected a boolean for key `parallelizable`.')
467
+ end
468
+ if s.key?(:build_configurations) && !s[:build_configurations].is_a?(Hash)
469
+ results.add_error('scheme', 'Expected a hash for key `build_configurations`.')
470
+ end
405
471
  end
406
472
  end
407
473
 
408
- # Check the hooks
474
+ # Performs validations related to github sources.
409
475
  #
410
- def check_install_hooks
411
- unless consumer.spec.pre_install_callback.nil?
412
- warning "The pre install hook of the specification DSL has been " \
413
- "deprecated, use the `resource_bundles` or the " \
414
- "`prepare_command` attributes."
476
+ def perform_github_source_checks(s)
477
+ require 'uri'
478
+
479
+ if git = s[:git]
480
+ return unless git =~ /^#{URI.regexp}$/
481
+ git_uri = URI.parse(git)
482
+ if git_uri.host
483
+ perform_github_uri_checks(git, git_uri) if git_uri.host.end_with?('github.com')
484
+ end
415
485
  end
486
+ end
416
487
 
417
- unless consumer.spec.post_install_callback.nil?
418
- warning "The post install hook of the specification DSL has been " \
419
- "deprecated, use the `resource_bundles` or the " \
420
- " `prepare_command` attributes."
488
+ def perform_github_uri_checks(git, git_uri)
489
+ if git_uri.host.start_with?('www.')
490
+ results.add_warning('github_sources', 'Github repositories should ' \
491
+ 'not use `www` in their URL.')
492
+ end
493
+ unless git.end_with?('.git')
494
+ results.add_warning('github_sources', 'Github repositories ' \
495
+ 'should end in `.git`.')
496
+ end
497
+ unless git_uri.scheme == 'https'
498
+ results.add_warning('github_sources', 'Github repositories ' \
499
+ 'should use an `https` link.', true)
421
500
  end
422
501
  end
423
502
 
424
- # Returns whether the frameworks are valid
503
+ # Performs validations related to SSH sources
425
504
  #
426
- # @params frameworks [Array<String>]
427
- # The frameworks to be validated
505
+ def check_git_ssh_source(s)
506
+ if git = s[:git]
507
+ if git =~ %r{\w+\@(\w|\.)+\:(/\w+)*}
508
+ results.add_warning('source', 'Git SSH URLs will NOT work for ' \
509
+ 'people behind firewalls configured to only allow HTTP, ' \
510
+ 'therefore HTTPS is preferred.', true)
511
+ end
512
+ end
513
+ end
514
+
515
+ # Performs validations related to the `social_media_url` attribute.
428
516
  #
429
- # @return [Boolean] true if a framework ends in `.framework`
517
+ def _validate_social_media_url(s)
518
+ if s =~ %r{https://twitter.com/EXAMPLE}
519
+ results.add_warning('social_media_url', 'The social media URL has ' \
520
+ 'not been updated from the default.')
521
+ end
522
+ end
523
+
524
+ # Performs validations related to the `readme` attribute.
430
525
  #
431
- def frameworks_invalid?(frameworks)
432
- frameworks.any? { |framework| framework.end_with?('.framework') }
526
+ def _validate_readme(s)
527
+ if s =~ %r{https://www.example.com/README}
528
+ results.add_warning('readme', 'The readme has ' \
529
+ 'not been updated from the default.')
530
+ end
433
531
  end
434
532
 
435
- # Returns whether the libraries are valid
533
+ # Performs validations related to the `changelog` attribute.
436
534
  #
437
- # @params libs [Array<String>]
438
- # The libraries to be validated
535
+ def _validate_changelog(s)
536
+ if s =~ %r{https://www.example.com/CHANGELOG}
537
+ results.add_warning('changelog', 'The changelog has ' \
538
+ 'not been updated from the default.')
539
+ end
540
+ end
541
+
542
+ # @param [Hash,Object] value
439
543
  #
440
- # @return [Boolean] true if a library ends with `.a`, `.dylib`, or
441
- # starts with `lib`.
442
- def libraries_invalid?(libs)
443
- libs.any? { |lib| lib.end_with?('.a', '.dylib') || lib.start_with?('lib') }
544
+ def _validate_info_plist(value)
545
+ return if value.empty?
546
+ if consumer.spec.subspec? && consumer.spec.library_specification?
547
+ results.add_error('info_plist', 'Info.plist configuration is not currently supported for subspecs.')
548
+ end
444
549
  end
445
550
 
446
551
  #-----------------------------------------------------------------------#
447
552
 
448
- # !@group Result Helpers
553
+ # @!group All specs validation helpers
449
554
 
450
555
  private
451
556
 
452
- # Adds an error result with the given message.
453
- #
454
- # @param [String] message
455
- # The message of the result.
456
- #
457
- # @return [void]
458
- #
459
- def error(message)
460
- add_result(:error, message)
461
- end
462
-
463
- # Adds an warning result with the given message.
464
- #
465
- # @param [String] message
466
- # The message of the result.
467
- #
468
- # @return [void]
557
+ # Performs validations related to the `compiler_flags` attribute.
469
558
  #
470
- def warning(message)
471
- add_result(:warning, message)
559
+ def _validate_compiler_flags(flags)
560
+ if flags.join(' ').split(' ').any? { |flag| flag.start_with?('-Wno') }
561
+ results.add_warning('compiler_flags', 'Warnings must not be disabled' \
562
+ '(`-Wno compiler` flags).')
563
+ end
472
564
  end
473
565
 
474
- # Adds a result of the given type with the given message. If there is a
475
- # current platform it is added to the result. If a result with the same
476
- # type and the same message is already available the current platform is
477
- # added to the existing result.
478
- #
479
- # @param [Symbol] type
480
- # The type of the result (`:error`, `:warning`).
566
+ # Returns whether the frameworks are valid
481
567
  #
482
- # @param [String] message
483
- # The message of the result.
568
+ # @param frameworks [Array<String>]
569
+ # The frameworks to be validated
484
570
  #
485
- # @return [void]
571
+ # @return [Boolean] true if a framework contains any
572
+ # non-alphanumeric character or includes an extension.
486
573
  #
487
- def add_result(type, message)
488
- result = results.find { |r| r.type == type && r.message == message }
489
- unless result
490
- result = Result.new(type, message)
491
- results << result
492
- end
493
- result.platforms << consumer.platform_name if consumer
494
- end
495
-
496
- #-----------------------------------------------------------------------#
497
-
498
- class Result
499
-
500
- # @return [Symbol] the type of result.
501
- #
502
- attr_reader :type
503
-
504
- # @return [String] the message associated with result.
505
- #
506
- attr_reader :message
507
-
508
- # @param [Symbol] type @see type
509
- # @param [String] message @see message
510
- #
511
- def initialize(type, message)
512
- @type = type
513
- @message = message
514
- @platforms = []
515
- end
516
-
517
- # @return [Array<Platform>] the platforms where this result was
518
- # generated.
519
- #
520
- attr_reader :platforms
521
-
522
- # @return [String] a string representation suitable for UI output.
523
- #
524
- def to_s
525
- r = "[#{type.to_s.upcase}] #{message}"
526
- if platforms != Specification::PLATFORMS
527
- platforms_names = platforms.uniq.map do |p|
528
- Platform.string_name(p)
529
- end
530
- r << " [#{platforms_names * ' - '}]" unless platforms.empty?
531
- end
532
- r
574
+ def frameworks_invalid?(frameworks)
575
+ frameworks.any? do |framework|
576
+ framework_regex = /[^\w\-\+]/
577
+ framework =~ framework_regex
533
578
  end
534
579
  end
535
-
536
- #-----------------------------------------------------------------------#
537
-
538
580
  end
539
581
  end
540
582
  end
541
-
542
- # # TODO
543
- # # Converts the resources file patterns to a hash defaulting to the
544
- # # resource key if they are defined as an Array or a String.
545
- # #
546
- # # @param [String, Array, Hash] value.
547
- # # The value of the attribute as specified by the user.
548
- # #
549
- # # @return [Hash] the resources.
550
- # #
551
- # def _prepare_deployment_target(deployment_target)
552
- # unless @define_for_platforms.count == 1
553
- # raise StandardError, "The deployment target must be defined per platform like `s.ios.deployment_target = '5.0'`."
554
- # end
555
- # Version.new(deployment_target)
556
- # end
557
-
558
- # # TODO
559
- # # Converts the resources file patterns to a hash defaulting to the
560
- # # resource key if they are defined as an Array or a String.
561
- # #
562
- # # @param [String, Array, Hash] value.
563
- # # The value of the attribute as specified by the user.
564
- # #
565
- # # @return [Hash] the resources.
566
- # #
567
- # def _prepare_platform(name_and_deployment_target)
568
- # return nil if name_and_deployment_target.nil?
569
- # if name_and_deployment_target.is_a?(Array)
570
- # name = name_and_deployment_target.first
571
- # deployment_target = name_and_deployment_target.last
572
- # else
573
- # name = name_and_deployment_target
574
- # deployment_target = nil
575
- # end
576
- # unless PLATFORMS.include?(name)
577
- # raise StandardError, "Unsupported platform `#{name}`. The available " \
578
- # "names are `#{PLATFORMS.inspect}`"
579
- # end
580
- # Platform.new(name, deployment_target)
581
- # end