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,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