cocoapods 1.2.1 → 1.3.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,126 @@
1
+ module Pod
2
+ class Installer
3
+ class Xcode
4
+ # The {Xcode::TargetValidator} ensures that the pod and aggregate target
5
+ # configuration is valid for installation.
6
+ #
7
+ class TargetValidator
8
+ # @return [Array<AggregateTarget>] The aggregate targets that should be
9
+ # validated.
10
+ #
11
+ attr_reader :aggregate_targets
12
+
13
+ # @return [Array<PodTarget>] The pod targets that should be validated.
14
+ #
15
+ attr_reader :pod_targets
16
+
17
+ # Create a new TargetValidator with aggregate and pod targets to
18
+ # validate.
19
+ #
20
+ # @param [Array<AggregateTarget>] aggregate_targets
21
+ # The aggregate targets to validate.
22
+ #
23
+ # @param [Array<PodTarget>] pod_targets
24
+ # The pod targets to validate.
25
+ #
26
+ def initialize(aggregate_targets, pod_targets)
27
+ @aggregate_targets = aggregate_targets
28
+ @pod_targets = pod_targets
29
+ end
30
+
31
+ # Perform the validation steps for the provided aggregate and pod
32
+ # targets.
33
+ #
34
+ def validate!
35
+ verify_no_duplicate_framework_and_library_names
36
+ verify_no_static_framework_transitive_dependencies
37
+ verify_no_pods_used_with_multiple_swift_versions
38
+ verify_framework_usage
39
+ end
40
+
41
+ private
42
+
43
+ def verify_no_duplicate_framework_and_library_names
44
+ aggregate_targets.each do |aggregate_target|
45
+ aggregate_target.user_build_configurations.keys.each do |config|
46
+ pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
47
+ file_accessors = pod_targets.flat_map(&:file_accessors)
48
+
49
+ frameworks = file_accessors.flat_map(&:vendored_frameworks).uniq.map(&:basename)
50
+ frameworks += pod_targets.select { |pt| pt.should_build? && pt.requires_frameworks? }.map(&:product_module_name)
51
+ verify_no_duplicate_names(frameworks, aggregate_target.label, 'frameworks')
52
+
53
+ libraries = file_accessors.flat_map(&:vendored_libraries).uniq.map(&:basename)
54
+ libraries += pod_targets.select { |pt| pt.should_build? && !pt.requires_frameworks? }.map(&:product_name)
55
+ verify_no_duplicate_names(libraries, aggregate_target.label, 'libraries')
56
+ end
57
+ end
58
+ end
59
+
60
+ def verify_no_duplicate_names(names, label, type)
61
+ duplicates = names.map { |n| n.to_s.downcase }.group_by { |f| f }.select { |_, v| v.size > 1 }.keys
62
+
63
+ unless duplicates.empty?
64
+ raise Informative, "The '#{label}' target has " \
65
+ "#{type} with conflicting names: #{duplicates.to_sentence}."
66
+ end
67
+ end
68
+
69
+ def verify_no_static_framework_transitive_dependencies
70
+ aggregate_targets.each do |aggregate_target|
71
+ next unless aggregate_target.requires_frameworks?
72
+
73
+ aggregate_target.user_build_configurations.keys.each do |config|
74
+ pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
75
+
76
+ dependencies = pod_targets.select(&:should_build?).flat_map(&:dependencies)
77
+ dependended_upon_targets = pod_targets.select { |t| dependencies.include?(t.pod_name) && !t.should_build? }
78
+
79
+ static_libs = dependended_upon_targets.flat_map(&:file_accessors).flat_map(&:vendored_static_artifacts)
80
+ unless static_libs.empty?
81
+ raise Informative, "The '#{aggregate_target.label}' target has " \
82
+ "transitive dependencies that include static binaries: (#{static_libs.to_sentence})"
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def verify_no_pods_used_with_multiple_swift_versions
89
+ error_message_for_target = lambda do |target|
90
+ "#{target.name} (Swift #{target.swift_version})"
91
+ end
92
+ swift_pod_targets = pod_targets.select(&:uses_swift?)
93
+ error_messages = swift_pod_targets.map do |pod_target|
94
+ swift_target_definitions = pod_target.target_definitions.reject { |target| target.swift_version.blank? }
95
+ next if swift_target_definitions.empty? || swift_target_definitions.uniq(&:swift_version).count == 1
96
+ target_errors = swift_target_definitions.map(&error_message_for_target).join(', ')
97
+ "- #{pod_target.name} required by #{target_errors}"
98
+ end.compact
99
+
100
+ unless error_messages.empty?
101
+ raise Informative, 'The following pods are integrated into targets ' \
102
+ "that do not have the same Swift version:\n\n#{error_messages.join("\n")}"
103
+ end
104
+ end
105
+
106
+ def verify_framework_usage
107
+ aggregate_targets.each do |aggregate_target|
108
+ next if aggregate_target.requires_frameworks?
109
+
110
+ aggregate_target.user_build_configurations.keys.each do |config|
111
+ pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
112
+
113
+ swift_pods = pod_targets.select(&:uses_swift?)
114
+ unless swift_pods.empty?
115
+ raise Informative, 'Pods written in Swift can only be integrated as frameworks; ' \
116
+ 'add `use_frameworks!` to your Podfile or target to opt into using it. ' \
117
+ "The Swift #{swift_pods.size == 1 ? 'Pod being used is' : 'Pods being used are'}: " +
118
+ swift_pods.map(&:name).to_sentence
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -254,6 +254,11 @@ module Pod
254
254
  end
255
255
  end
256
256
  settings['GCC_PREPROCESSOR_DEFINITIONS'] = definitions
257
+
258
+ if type == :debug
259
+ settings['SWIFT_ACTIVE_COMPILATION_CONDITIONS'] = 'DEBUG'
260
+ end
261
+
257
262
  build_configuration
258
263
  end
259
264
 
@@ -305,7 +305,7 @@ module Pod
305
305
  find_cached_set(dependency).
306
306
  all_specifications(installation_options.warn_for_multiple_pod_sources).
307
307
  select { |s| requirement.satisfied_by? s.version }.
308
- map { |s| s.subspec_by_name(dependency.name, false) }.
308
+ map { |s| s.subspec_by_name(dependency.name, false, true) }.
309
309
  compact.
310
310
  reverse
311
311
  end
@@ -18,11 +18,11 @@ module Pod
18
18
  specification.respond_to?(method, include_all)
19
19
  end
20
20
 
21
- def subspec_by_name(name = nil, raise_if_missing = true)
21
+ def subspec_by_name(name = nil, raise_if_missing = true, include_test_specifications = false)
22
22
  if !name || name == self.name
23
23
  self
24
24
  else
25
- specification.subspec_by_name(name, raise_if_missing)
25
+ specification.subspec_by_name(name, raise_if_missing, include_test_specifications)
26
26
  end
27
27
  end
28
28
 
@@ -386,5 +386,28 @@ module Pod
386
386
  end
387
387
 
388
388
  #-------------------------------------------------------------------------#
389
+
390
+ public
391
+
392
+ # @!group Pods Helpers
393
+
394
+ # Creates the file accessors for the given Pod Targets.
395
+ #
396
+ # @param [Array<PodTarget>] pod_targets
397
+ # The Pod Targets to create file accessors for.
398
+ #
399
+ def create_file_accessors(pod_targets)
400
+ pod_targets.each do |pod_target|
401
+ pod_root = pod_dir(pod_target.root_spec.name)
402
+ path_list = PathList.new(pod_root)
403
+ file_accessors = pod_target.specs.map do |spec|
404
+ FileAccessor.new(path_list, spec.consumer(pod_target.platform))
405
+ end
406
+ pod_target.file_accessors ||= []
407
+ pod_target.file_accessors.concat(file_accessors)
408
+ end
409
+ end
410
+
411
+ #-------------------------------------------------------------------------#
389
412
  end
390
413
  end
@@ -28,6 +28,16 @@ module Pod
28
28
  #
29
29
  attr_accessor :dependent_targets
30
30
 
31
+ # @return [Array<PodTarget>] the targets that this target has a test dependency
32
+ # upon.
33
+ #
34
+ attr_accessor :test_dependent_targets
35
+
36
+ # return [Array<PBXNativeTarget>] the test target generated in the Pods project for
37
+ # this library or `nil` if there is no test target created.
38
+ #
39
+ attr_accessor :test_native_targets
40
+
31
41
  # @param [Array<Specification>] specs @see #specs
32
42
  # @param [Array<TargetDefinition>] target_definitions @see #target_definitions
33
43
  # @param [Sandbox] sandbox @see #sandbox
@@ -46,7 +56,9 @@ module Pod
46
56
  @build_headers = Sandbox::HeadersStore.new(sandbox, 'Private')
47
57
  @file_accessors = []
48
58
  @resource_bundle_targets = []
59
+ @test_native_targets = []
49
60
  @dependent_targets = []
61
+ @test_dependent_targets = []
50
62
  @build_config_cache = {}
51
63
  end
52
64
 
@@ -133,7 +145,7 @@ module Pod
133
145
  #
134
146
  attr_accessor :file_accessors
135
147
 
136
- # @return [Array<PBXTarget>] the resource bundle targets belonging
148
+ # @return [Array<PBXNativeTarget>] the resource bundle targets belonging
137
149
  # to this target.
138
150
  attr_reader :resource_bundle_targets
139
151
 
@@ -157,7 +169,7 @@ module Pod
157
169
  specs.map { |spec| spec.consumer(platform) }
158
170
  end
159
171
 
160
- # @return [Boolean] Whether the target uses Swift code
172
+ # @return [Boolean] Whether the target uses Swift code.
161
173
  #
162
174
  def uses_swift?
163
175
  return @uses_swift if defined? @uses_swift
@@ -168,6 +180,52 @@ module Pod
168
180
  end
169
181
  end
170
182
 
183
+ # @return [Boolean] Whether the target has any tests specifications.
184
+ #
185
+ def contains_test_specifications?
186
+ specs.any?(&:test_specification?)
187
+ end
188
+
189
+ # @return [Array<Symbol>] All of the test supported types within this target.
190
+ #
191
+ def supported_test_types
192
+ specs.select(&:test_specification?).map(&:test_type).uniq
193
+ end
194
+
195
+ # Returns the corresponding native target to use based on the provided specification.
196
+ # This is used to figure out whether to add a source file into the library native target or any of the
197
+ # test native targets.
198
+ #
199
+ # @param [Specification] spec
200
+ # The specifcation to base from in order to find the native target.
201
+ #
202
+ # @return [PBXNativeTarget] the native target to use or `nil` if none is found.
203
+ #
204
+ def native_target_for_spec(spec)
205
+ return native_target unless spec.test_specification?
206
+ test_native_targets.find do |native_target|
207
+ native_target.symbol_type == product_type_for_test_type(spec.test_type)
208
+ end
209
+ end
210
+
211
+ # Returns the corresponding native product type to use given the test type.
212
+ # This is primarily used when creating the native targets in order to produce the correct test bundle target
213
+ # based on the type of tests included.
214
+ #
215
+ # @param [Symbol] test_type
216
+ # The test type to map to provided by the test specification DSL.
217
+ #
218
+ # @return [Symbol] The native product type to use.
219
+ #
220
+ def product_type_for_test_type(test_type)
221
+ case test_type
222
+ when :unit
223
+ :unit_test_bundle
224
+ else
225
+ raise Informative, "Unknown test type `#{test_type}`."
226
+ end
227
+ end
228
+
171
229
  # @return [Specification] The root specification for the target.
172
230
  #
173
231
  def root_spec
@@ -189,6 +247,15 @@ module Pod
189
247
  "#{label}-#{bundle_name}"
190
248
  end
191
249
 
250
+ # @param [Symbol] test_type
251
+ # The test type to use for producing the test label.
252
+ #
253
+ # @return [String] The derived name of the test target.
254
+ #
255
+ def test_target_label(test_type)
256
+ "#{label}-#{test_type.capitalize}-Tests"
257
+ end
258
+
192
259
  # @return [Array<String>] The names of the Pods on which this target
193
260
  # depends.
194
261
  #
@@ -234,7 +301,7 @@ module Pod
234
301
 
235
302
  if whitelists.empty?
236
303
  @build_config_cache[key] = true
237
- return true
304
+ true
238
305
  elsif whitelists.count == 1
239
306
  @build_config_cache[key] = whitelists.first
240
307
  whitelists.first
@@ -258,7 +325,7 @@ module Pod
258
325
 
259
326
  if whitelists.empty?
260
327
  @inhibit_warnings = false
261
- return false
328
+ false
262
329
  elsif whitelists.count == 1
263
330
  @inhibit_warnings = whitelists.first
264
331
  whitelists.first
@@ -268,7 +335,7 @@ module Pod
268
335
  'settings to inhibit warnings. CocoaPods does not currently ' \
269
336
  'support different settings and will fall back to your preference ' \
270
337
  'set in the root target definition.'
271
- return podfile.root_target_definitions.first.inhibits_warnings_for_pod?(root_spec.name)
338
+ podfile.root_target_definitions.first.inhibits_warnings_for_pod?(root_spec.name)
272
339
  end
273
340
  end
274
341
 
@@ -28,8 +28,12 @@ module Pod
28
28
  # the Source URLs to use in creating a {Podfile}.
29
29
  #
30
30
  def initialize(spec_or_path, source_urls)
31
- @source_urls = source_urls.map { |url| config.sources_manager.source_with_name_or_url(url) }.map(&:url)
32
31
  @linter = Specification::Linter.new(spec_or_path)
32
+ @source_urls = if @linter.spec && @linter.spec.dependencies.empty?
33
+ []
34
+ else
35
+ source_urls.map { |url| config.sources_manager.source_with_name_or_url(url) }.map(&:url)
36
+ end
33
37
  end
34
38
 
35
39
  #-------------------------------------------------------------------------#
@@ -68,7 +72,7 @@ module Pod
68
72
  a_spec = spec
69
73
  if spec && @only_subspec
70
74
  subspec_name = @only_subspec.start_with?(spec.root.name) ? @only_subspec : "#{spec.root.name}/#{@only_subspec}"
71
- a_spec = spec.subspec_by_name(subspec_name)
75
+ a_spec = spec.subspec_by_name(subspec_name, true, true)
72
76
  @subspec_name = a_spec.name
73
77
  end
74
78
 
@@ -142,15 +146,8 @@ module Pod
142
146
  reasons << 'all results apply only to public specs, but you can use ' \
143
147
  '`--private` to ignore them if linting the specification for a private pod'
144
148
  end
145
- if dot_swift_version.nil?
146
- reasons.to_sentence + ".\n[!] The validator for Swift projects uses " \
147
- 'Swift 3.0 by default, if you are using a different version of ' \
148
- 'swift you can use a `.swift-version` file to set the version for ' \
149
- "your Pod. For example to use Swift 2.3, run: \n" \
150
- ' `echo "2.3" > .swift-version`'
151
- else
152
- reasons.to_sentence
153
- end
149
+
150
+ reasons.to_sentence
154
151
  end
155
152
 
156
153
  #-------------------------------------------------------------------------#
@@ -189,10 +186,14 @@ module Pod
189
186
  #
190
187
  attr_accessor :only_subspec
191
188
 
192
- # @return [Bool] Whether the validator should validate all subspecs
189
+ # @return [Bool] Whether the validator should validate all subspecs.
193
190
  #
194
191
  attr_accessor :no_subspecs
195
192
 
193
+ # @return [Bool] Whether the validator should skip building and running tests.
194
+ #
195
+ attr_accessor :skip_tests
196
+
196
197
  # @return [Bool] Whether frameworks should be used for the installation.
197
198
  #
198
199
  attr_accessor :use_frameworks
@@ -245,7 +246,7 @@ module Pod
245
246
  # @return [Pathname] the temporary directory used by the linter.
246
247
  #
247
248
  def validation_dir
248
- Pathname(Dir.tmpdir) + 'CocoaPods/Lint'
249
+ @validation_dir ||= Pathname(Dir.mktmpdir(['CocoaPods-Lint-', "-#{spec.name}"]))
249
250
  end
250
251
 
251
252
  # @return [String] the SWIFT_VERSION to use for validation.
@@ -290,6 +291,10 @@ module Pod
290
291
  # Perform analysis for a given spec (or subspec)
291
292
  #
292
293
  def perform_extensive_analysis(spec)
294
+ if spec.test_specification?
295
+ error('spec', "Validating a test spec (`#{spec.name}`) is not supported.")
296
+ return false
297
+ end
293
298
  validate_homepage(spec)
294
299
  validate_screenshots(spec)
295
300
  validate_social_media_url(spec)
@@ -304,9 +309,11 @@ module Pod
304
309
  download_pod
305
310
  check_file_patterns
306
311
  install_pod
312
+ validate_dot_swift_version
307
313
  add_app_project_import
308
314
  validate_vendored_dynamic_frameworks
309
315
  build_pod
316
+ test_pod unless skip_tests
310
317
  ensure
311
318
  tear_down_validation_environment
312
319
  end
@@ -324,7 +331,7 @@ module Pod
324
331
  # Recursively perform the extensive analysis on all subspecs
325
332
  #
326
333
  def perform_extensive_subspec_analysis(spec)
327
- spec.subspecs.send(fail_fast ? :all? : :each) do |subspec|
334
+ spec.subspecs.reject(&:test_specification?).send(fail_fast ? :all? : :each) do |subspec|
328
335
  @subspec_name = subspec.name
329
336
  perform_extensive_analysis(subspec)
330
337
  end
@@ -378,6 +385,17 @@ module Pod
378
385
  validate_url(spec.documentation_url) if spec.documentation_url
379
386
  end
380
387
 
388
+ def validate_dot_swift_version
389
+ if !used_swift_version.nil? && dot_swift_version.nil?
390
+ warning(:swift_version,
391
+ 'The validator for Swift projects uses ' \
392
+ 'Swift 3.0 by default, if you are using a different version of ' \
393
+ 'swift you can use a `.swift-version` file to set the version for ' \
394
+ "your Pod. For example to use Swift 2.3, run: \n" \
395
+ ' `echo "2.3" > .swift-version`')
396
+ end
397
+ end
398
+
381
399
  def setup_validation_environment
382
400
  validation_dir.rmtree if validation_dir.exist?
383
401
  validation_dir.mkpath
@@ -401,10 +419,11 @@ module Pod
401
419
  end
402
420
 
403
421
  def download_pod
404
- podfile = podfile_from_spec(consumer.platform_name, deployment_target, use_frameworks)
422
+ podfile = podfile_from_spec(consumer.platform_name, deployment_target, use_frameworks, consumer.spec.test_specs.map(&:name))
405
423
  sandbox = Sandbox.new(config.sandbox_root)
406
424
  @installer = Installer.new(sandbox, podfile)
407
425
  @installer.use_default_plugins = false
426
+ @installer.has_dependencies = !spec.dependencies.empty?
408
427
  %i(prepare resolve_dependencies download_dependencies).each { |m| @installer.send(m) }
409
428
  @file_accessor = @installer.pod_targets.flat_map(&:file_accessors).find { |fa| fa.spec.name == consumer.spec.name }
410
429
  end
@@ -480,15 +499,13 @@ module Pod
480
499
  # for all available platforms with xcodebuild.
481
500
  #
482
501
  def install_pod
483
- %i(verify_no_duplicate_framework_and_library_names
484
- verify_no_static_framework_transitive_dependencies
485
- verify_framework_usage generate_pods_project integrate_user_project
502
+ %i(validate_targets generate_pods_project integrate_user_project
486
503
  perform_post_install_actions).each { |m| @installer.send(m) }
487
504
 
488
505
  deployment_target = spec.subspec_by_name(subspec_name).deployment_target(consumer.platform_name)
489
506
  @installer.aggregate_targets.each do |target|
490
507
  target.pod_targets.each do |pod_target|
491
- next unless native_target = pod_target.native_target
508
+ next unless (native_target = pod_target.native_target)
492
509
  native_target.build_configuration_list.build_configurations.each do |build_configuration|
493
510
  (build_configuration.build_settings['OTHER_CFLAGS'] ||= '$(inherited)') << ' -Wincomplete-umbrella'
494
511
  build_configuration.build_settings['SWIFT_VERSION'] = swift_version if pod_target.uses_swift?
@@ -529,28 +546,38 @@ module Pod
529
546
  UI.warn "Skipping compilation with `xcodebuild' because it can't be found.\n".yellow
530
547
  else
531
548
  UI.message "\nBuilding with xcodebuild.\n".yellow do
532
- output = xcodebuild
549
+ scheme = if skip_import_validation?
550
+ @installer.pod_targets.find { |pt| pt.pod_name == spec.root.name }.label
551
+ else
552
+ 'App'
553
+ end
554
+ output = xcodebuild('build', scheme, 'Release')
533
555
  UI.puts output
534
556
  parsed_output = parse_xcodebuild_output(output)
535
- parsed_output.each do |message|
536
- # Checking the error for `InputFile` is to work around an Xcode
537
- # issue where linting would fail even though `xcodebuild` actually
538
- # succeeds. Xcode.app also doesn't fail when this issue occurs, so
539
- # it's safe for us to do the same.
540
- #
541
- # For more details see https://github.com/CocoaPods/CocoaPods/issues/2394#issuecomment-56658587
542
- #
543
- if message.include?("'InputFile' should have")
544
- next
545
- end
557
+ translate_output_to_linter_messages(parsed_output)
558
+ end
559
+ end
560
+ end
546
561
 
547
- if message =~ /\S+:\d+:\d+: error:/
548
- error('xcodebuild', message)
549
- elsif message =~ /\S+:\d+:\d+: warning:/
550
- warning('xcodebuild', message)
551
- else
552
- note('xcodebuild', message)
553
- end
562
+ # Builds and runs all test sources associated with the current specification being validated.
563
+ #
564
+ # @note Xcode warnings are treaded as notes because the spec maintainer
565
+ # might not be the author of the library
566
+ #
567
+ # @return [void]
568
+ #
569
+ def test_pod
570
+ if !xcodebuild_available?
571
+ UI.warn "Skipping test validation with `xcodebuild' because it can't be found.\n".yellow
572
+ else
573
+ UI.message "\nTesting with xcodebuild.\n".yellow do
574
+ pod_target = @installer.pod_targets.find { |pt| pt.pod_name == spec.root.name }
575
+ consumer.spec.test_specs.each do |test_spec|
576
+ scheme = pod_target.native_target_for_spec(test_spec)
577
+ output = xcodebuild('test', scheme, 'Debug')
578
+ UI.puts output
579
+ parsed_output = parse_xcodebuild_output(output)
580
+ translate_output_to_linter_messages(parsed_output)
554
581
  end
555
582
  end
556
583
  end
@@ -670,6 +697,29 @@ module Pod
670
697
  add_result(:note, *args)
671
698
  end
672
699
 
700
+ def translate_output_to_linter_messages(parsed_output)
701
+ parsed_output.each do |message|
702
+ # Checking the error for `InputFile` is to work around an Xcode
703
+ # issue where linting would fail even though `xcodebuild` actually
704
+ # succeeds. Xcode.app also doesn't fail when this issue occurs, so
705
+ # it's safe for us to do the same.
706
+ #
707
+ # For more details see https://github.com/CocoaPods/CocoaPods/issues/2394#issuecomment-56658587
708
+ #
709
+ if message.include?("'InputFile' should have")
710
+ next
711
+ end
712
+
713
+ if message =~ /\S+:\d+:\d+: error:/
714
+ error('xcodebuild', message)
715
+ elsif message =~ /\S+:\d+:\d+: warning:/
716
+ warning('xcodebuild', message)
717
+ else
718
+ note('xcodebuild', message)
719
+ end
720
+ end
721
+ end
722
+
673
723
  def shares_pod_target_xcscheme?(pod_target)
674
724
  Pathname.new(@installer.pods_project.path + pod_target.label).exist?
675
725
  end
@@ -719,13 +769,16 @@ module Pod
719
769
  # @param [Bool] use_frameworks
720
770
  # whether frameworks should be used for the installation
721
771
  #
772
+ # @param [Array<String>] test_spec_names
773
+ # the test spec names to include in the podfile.
774
+ #
722
775
  # @return [Podfile] a podfile that requires the specification on the
723
776
  # current platform.
724
777
  #
725
778
  # @note The generated podfile takes into account whether the linter is
726
779
  # in local mode.
727
780
  #
728
- def podfile_from_spec(platform_name, deployment_target, use_frameworks = true)
781
+ def podfile_from_spec(platform_name, deployment_target, use_frameworks = true, test_spec_names = [])
729
782
  name = subspec_name || spec.name
730
783
  podspec = file.realpath
731
784
  local = local?
@@ -741,6 +794,13 @@ module Pod
741
794
  else
742
795
  pod name, :podspec => podspec.to_s
743
796
  end
797
+ test_spec_names.each do |test_spec_name|
798
+ if local
799
+ pod test_spec_name, :path => podspec.dirname.to_s
800
+ else
801
+ pod test_spec_name, :podspec => podspec.to_s
802
+ end
803
+ end
744
804
  end
745
805
  end
746
806
  end
@@ -771,14 +831,9 @@ module Pod
771
831
  # @return [String] Executes xcodebuild in the current working directory and
772
832
  # returns its output (both STDOUT and STDERR).
773
833
  #
774
- def xcodebuild
834
+ def xcodebuild(action, scheme, configuration)
775
835
  require 'fourflusher'
776
- scheme = if skip_import_validation?
777
- @installer.pod_targets.find { |pt| pt.pod_name == spec.root.name }.label
778
- else
779
- 'App'
780
- end
781
- command = %W(clean build -workspace #{File.join(validation_dir, 'App.xcworkspace')} -scheme #{scheme} -configuration Release)
836
+ command = %W(clean #{action} -workspace #{File.join(validation_dir, 'App.xcworkspace')} -scheme #{scheme} -configuration #{configuration})
782
837
  case consumer.platform_name
783
838
  when :osx, :macos
784
839
  command += %w(CODE_SIGN_IDENTITY=)