Add support for test target creation in the pods project generator

parent ad59b03d
...@@ -8,6 +8,10 @@ To install release candidates run `[sudo] gem install cocoapods --pre` ...@@ -8,6 +8,10 @@ To install release candidates run `[sudo] gem install cocoapods --pre`
##### Enhancements ##### Enhancements
* Add support for test target creation in the pods project generator
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#6703](https://github.com/CocoaPods/CocoaPods/pull/6703)
* Copy dSYM for vendored frameworks. * Copy dSYM for vendored frameworks.
[Dimitris Koutsogiorgas](https://github.com/dnkoutso) [Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#1698](https://github.com/CocoaPods/CocoaPods/issues/1698) [#1698](https://github.com/CocoaPods/CocoaPods/issues/1698)
......
...@@ -7,7 +7,7 @@ GIT ...@@ -7,7 +7,7 @@ GIT
GIT GIT
remote: https://github.com/CocoaPods/Core.git remote: https://github.com/CocoaPods/Core.git
revision: 77639caff51a44aac7696adc23ac9ca04dfe5e82 revision: 5574572de5dd607f7dbc29025f7cf9f7c5d12cec
branch: master branch: master
specs: specs:
cocoapods-core (1.2.1) cocoapods-core (1.2.1)
...@@ -31,7 +31,7 @@ GIT ...@@ -31,7 +31,7 @@ GIT
GIT GIT
remote: https://github.com/CocoaPods/Xcodeproj.git remote: https://github.com/CocoaPods/Xcodeproj.git
revision: 526f5128a23ca0f8702b9eab4fc1ff67873fab84 revision: 7355a4eac5d3634725e0925cfbfa46fbcd88b933
branch: master branch: master
specs: specs:
xcodeproj (1.4.4) xcodeproj (1.4.4)
......
...@@ -71,8 +71,9 @@ module Pod ...@@ -71,8 +71,9 @@ module Pod
XCConfigHelper.add_target_specific_settings(target, @xcconfig) XCConfigHelper.add_target_specific_settings(target, @xcconfig)
generate_vendored_build_settings targets = pod_targets + target.search_paths_aggregate_targets.flat_map(&:pod_targets)
generate_other_ld_flags XCConfigHelper.generate_vendored_build_settings(target, targets, @xcconfig)
XCConfigHelper.generate_other_ld_flags(target, pod_targets, @xcconfig)
# TODO: Need to decide how we are going to ensure settings like these # TODO: Need to decide how we are going to ensure settings like these
# are always excluded from the user's project. # are always excluded from the user's project.
...@@ -176,40 +177,6 @@ module Pod ...@@ -176,40 +177,6 @@ module Pod
end end
end end
# Add custom build settings and required build settings to link to
# vendored libraries and frameworks.
#
# @note
# In case of generated pod targets, which require frameworks, the
# vendored frameworks and libraries are already linked statically
# into the framework binary and must not be linked again to the
# user target.
#
def generate_vendored_build_settings
targets = pod_targets + target.search_paths_aggregate_targets.flat_map(&:pod_targets)
targets.each do |pod_target|
unless pod_target.should_build? && pod_target.requires_frameworks?
XCConfigHelper.add_settings_for_file_accessors_of_target(target, pod_target, @xcconfig)
end
end
end
# Add pod target to list of frameworks / libraries that are linked
# with the user’s project.
#
def generate_other_ld_flags
other_ld_flags = pod_targets.select(&:should_build?).map do |pod_target|
if pod_target.requires_frameworks?
%(-framework "#{pod_target.product_basename}")
else
%(-l "#{pod_target.product_basename}") if XCConfigHelper.links_dependency?(target, pod_target)
end
end
@xcconfig.merge!('OTHER_LDFLAGS' => other_ld_flags.compact.join(' '))
end
# Ensure to add the default linker run path search paths as they could # Ensure to add the default linker run path search paths as they could
# be not present due to being historically absent in the project or # be not present due to being historically absent in the project or
# target template or just being removed by being superficial when # target template or just being removed by being superficial when
......
...@@ -16,8 +16,12 @@ module Pod ...@@ -16,8 +16,12 @@ module Pod
# #
# @param [Target] target @see target # @param [Target] target @see target
# #
def initialize(target) # @param [Boolean] test_xcconfig
# whether this is an xcconfig for a test native target.
#
def initialize(target, test_xcconfig = false)
@target = target @target = target
@test_xcconfig = test_xcconfig
end end
# @return [Xcodeproj::Config] The generated xcconfig. # @return [Xcodeproj::Config] The generated xcconfig.
...@@ -49,7 +53,7 @@ module Pod ...@@ -49,7 +53,7 @@ module Pod
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1', 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1',
'HEADER_SEARCH_PATHS' => XCConfigHelper.quote(search_paths), 'HEADER_SEARCH_PATHS' => XCConfigHelper.quote(search_paths),
'LIBRARY_SEARCH_PATHS' => '$(inherited) ', 'LIBRARY_SEARCH_PATHS' => '$(inherited) ',
'OTHER_LDFLAGS' => XCConfigHelper.default_ld_flags(target), 'OTHER_LDFLAGS' => XCConfigHelper.default_ld_flags(target, @test_xcconfig),
'PODS_ROOT' => '${SRCROOT}', 'PODS_ROOT' => '${SRCROOT}',
'PODS_TARGET_SRCROOT' => target.pod_target_srcroot, 'PODS_TARGET_SRCROOT' => target.pod_target_srcroot,
'PRODUCT_BUNDLE_IDENTIFIER' => 'org.cocoapods.${PRODUCT_NAME:rfc1034identifier}', 'PRODUCT_BUNDLE_IDENTIFIER' => 'org.cocoapods.${PRODUCT_NAME:rfc1034identifier}',
...@@ -66,6 +70,11 @@ module Pod ...@@ -66,6 +70,11 @@ module Pod
end end
XCConfigHelper.add_target_specific_settings(target, @xcconfig) XCConfigHelper.add_target_specific_settings(target, @xcconfig)
@xcconfig.merge! XCConfigHelper.settings_for_dependent_targets(target, target.recursive_dependent_targets) @xcconfig.merge! XCConfigHelper.settings_for_dependent_targets(target, target.recursive_dependent_targets)
if @test_xcconfig
@xcconfig.merge! XCConfigHelper.settings_for_dependent_targets(target, target.test_dependent_targets)
XCConfigHelper.generate_vendored_build_settings(nil, target.test_dependent_targets, @xcconfig)
XCConfigHelper.generate_other_ld_flags(nil, target.test_dependent_targets, @xcconfig)
end
@xcconfig @xcconfig
end end
......
...@@ -45,13 +45,15 @@ module Pod ...@@ -45,13 +45,15 @@ module Pod
# the target, which is used to check if the ARC compatibility # the target, which is used to check if the ARC compatibility
# flag is required. # flag is required.
# #
# @return [String] the default linker flags. `-ObjC` is always included # @param [Boolean] include_objc_flag
# while `-fobjc-arc` is included only if requested in the # whether to include `-ObjC` in the other linker flags
# Podfile.
# #
def self.default_ld_flags(target, includes_static_libraries = false) # @return [String] the default linker flags. `-ObjC` is optionally included depending
# on the target while `-fobjc-arc` is included only if requested in the Podfile.
#
def self.default_ld_flags(target, include_objc_flag = false)
ld_flags = '' ld_flags = ''
ld_flags << '-ObjC' if includes_static_libraries ld_flags << '-ObjC' if include_objc_flag
if target.podfile.set_arc_compatibility_flag? && if target.podfile.set_arc_compatibility_flag? &&
target.spec_consumers.any?(&:requires_arc?) target.spec_consumers.any?(&:requires_arc?)
ld_flags << ' -fobjc-arc' ld_flags << ' -fobjc-arc'
...@@ -296,6 +298,57 @@ module Pod ...@@ -296,6 +298,57 @@ module Pod
build_settings build_settings
end end
# Add custom build settings and required build settings to link to
# vendored libraries and frameworks.
#
# @param [AggregateTarget] aggregate_target
# The aggregate target, may be nil.
#
# @param [Array<PodTarget] pod_targets
# The pod targets to add the vendored build settings for.
#
# @param [Xcodeproj::Config] xcconfig
# The xcconfig to edit.
#
# @note
# In case of generated pod targets, which require frameworks, the
# vendored frameworks and libraries are already linked statically
# into the framework binary and must not be linked again to the
# user target.
#
def self.generate_vendored_build_settings(aggregate_target, pod_targets, xcconfig)
pod_targets.each do |pod_target|
unless pod_target.should_build? && pod_target.requires_frameworks?
XCConfigHelper.add_settings_for_file_accessors_of_target(aggregate_target, pod_target, xcconfig)
end
end
end
# Add pod target to list of frameworks / libraries that are linked
# with the user’s project.
#
# @param [AggregateTarget] aggregate_target
# The aggregate target, may be nil.
#
# @param [Array<PodTarget] pod_targets
# The pod targets to add the vendored build settings for.
#
# @param [Xcodeproj::Config] xcconfig
# The xcconfig to edit.
#
# @return [void]
#
def self.generate_other_ld_flags(aggregate_target, pod_targets, xcconfig)
other_ld_flags = pod_targets.select(&:should_build?).map do |pod_target|
if pod_target.requires_frameworks?
%(-framework "#{pod_target.product_basename}")
elsif XCConfigHelper.links_dependency?(aggregate_target, pod_target)
%(-l "#{pod_target.product_basename}")
end
end
xcconfig.merge!('OTHER_LDFLAGS' => other_ld_flags.compact.join(' '))
end
# Checks if the given target requires language specific settings and # Checks if the given target requires language specific settings and
# configures the given Xcconfig. # configures the given Xcconfig.
# #
......
...@@ -492,15 +492,12 @@ module Pod ...@@ -492,15 +492,12 @@ module Pod
hash[name] = values.sort_by { |pt| pt.specs.count } hash[name] = values.sort_by { |pt| pt.specs.count }
end end
pod_targets.each do |target| pod_targets.each do |target|
dependencies = transitive_dependencies_for_specs(target.specs, target.platform, all_specs).group_by(&:root) dependencies = transitive_dependencies_for_specs(target.specs.reject(&:test_specification?), target.platform, all_specs).group_by(&:root)
target.dependent_targets = dependencies.map do |root_spec, deps| test_dependencies = transitive_dependencies_for_specs(target.specs.select(&:test_specification?), target.platform, all_specs).group_by(&:root)
pod_targets_by_name[root_spec.name].find do |t| target.dependent_targets = filter_dependencies(dependencies, pod_targets_by_name, target)
next false if t.platform.symbolic_name != target.platform.symbolic_name || target.test_dependent_targets = filter_dependencies(test_dependencies, pod_targets_by_name, target)
t.requires_frameworks? != target.requires_frameworks? # Test dependendent targets include our own target as a test dependency.
spec_names = t.specs.map(&:name) target.test_dependent_targets << target
deps.all? { |dep| spec_names.include?(dep.name) }
end
end
end end
else else
dedupe_cache = {} dedupe_cache = {}
...@@ -512,12 +509,27 @@ module Pod ...@@ -512,12 +509,27 @@ module Pod
pod_targets.each do |target| pod_targets.each do |target|
dependencies = transitive_dependencies_for_specs(target.specs, target.platform, specs).group_by(&:root) dependencies = transitive_dependencies_for_specs(target.specs, target.platform, specs).group_by(&:root)
test_dependencies = transitive_dependencies_for_specs(target.specs.select(&:test_specification?), target.platform, all_specs).group_by(&:root)
target.dependent_targets = pod_targets.reject { |t| dependencies[t.root_spec].nil? } target.dependent_targets = pod_targets.reject { |t| dependencies[t.root_spec].nil? }
target.test_dependent_targets = pod_targets.reject { |t| test_dependencies[t.root_spec].nil? }
# Test dependendent targets include our own target as a test dependency.
target.test_dependent_targets << target
end end
end end
end end
end end
def filter_dependencies(dependencies, pod_targets_by_name, target)
dependencies.map do |root_spec, deps|
pod_targets_by_name[root_spec.name].find do |t|
next false if t.platform.symbolic_name != target.platform.symbolic_name ||
t.requires_frameworks? != target.requires_frameworks?
spec_names = t.specs.map(&:name)
deps.all? { |dep| spec_names.include?(dep.name) }
end
end
end
# Returns the specs upon which the given specs _transitively_ depend. # Returns the specs upon which the given specs _transitively_ depend.
# #
# @note: This is implemented in the analyzer, because we don't have to # @note: This is implemented in the analyzer, because we don't have to
......
...@@ -86,6 +86,11 @@ module Pod ...@@ -86,6 +86,11 @@ module Pod
development_pod_targets.select(&:should_build?).each do |pod_target| development_pod_targets.select(&:should_build?).each do |pod_target|
next unless share_scheme_for_development_pod?(pod_target.pod_name) next unless share_scheme_for_development_pod?(pod_target.pod_name)
Xcodeproj::XCScheme.share_scheme(project.path, pod_target.label) Xcodeproj::XCScheme.share_scheme(project.path, pod_target.label)
if pod_target.contains_test_specifications?
pod_target.supported_test_types.each do |test_type|
Xcodeproj::XCScheme.share_scheme(project.path, pod_target.test_target_label(test_type))
end
end
end end
end end
...@@ -203,16 +208,10 @@ module Pod ...@@ -203,16 +208,10 @@ module Pod
aggregate_target.native_target.add_dependency(pod_target.native_target) aggregate_target.native_target.add_dependency(pod_target.native_target)
configure_app_extension_api_only_for_target(pod_target) if is_app_extension configure_app_extension_api_only_for_target(pod_target) if is_app_extension
pod_target.dependent_targets.each do |pod_dependency_target| add_dependent_targets_to_native_target(pod_target.dependent_targets, pod_target.native_target, is_app_extension, pod_target.requires_frameworks?, frameworks_group)
next unless pod_dependency_target.should_build?
pod_target.native_target.add_dependency(pod_dependency_target.native_target)
configure_app_extension_api_only_for_target(pod_dependency_target) if is_app_extension
if pod_target.requires_frameworks? pod_target.test_native_targets.each do |test_native_target|
product_ref = frameworks_group.files.find { |f| f.path == pod_dependency_target.product_name } || add_dependent_targets_to_native_target(pod_target.test_dependent_targets, test_native_target, false, pod_target.requires_frameworks?, frameworks_group)
frameworks_group.new_product_ref_for_target(pod_dependency_target.product_basename, pod_dependency_target.product_type)
pod_target.native_target.frameworks_build_phase.add_file_reference(product_ref, true)
end
end end
end end
end end
...@@ -251,6 +250,20 @@ module Pod ...@@ -251,6 +250,20 @@ module Pod
private private
def add_dependent_targets_to_native_target(dependent_targets, native_target, is_app_extension, requires_frameworks, frameworks_group)
dependent_targets.each do |pod_dependency_target|
next unless pod_dependency_target.should_build?
native_target.add_dependency(pod_dependency_target.native_target)
configure_app_extension_api_only_for_target(pod_dependency_target) if is_app_extension
if requires_frameworks
product_ref = frameworks_group.files.find { |f| f.path == pod_dependency_target.product_name } ||
frameworks_group.new_product_ref_for_target(pod_dependency_target.product_basename, pod_dependency_target.product_type)
native_target.frameworks_build_phase.add_file_reference(product_ref, true)
end
end
end
# Sets the APPLICATION_EXTENSION_API_ONLY build setting to YES for all # Sets the APPLICATION_EXTENSION_API_ONLY build setting to YES for all
# configurations of the given target # configurations of the given target
# #
......
...@@ -18,20 +18,24 @@ module Pod ...@@ -18,20 +18,24 @@ module Pod
UI.message "- Installing target `#{target.name}` #{target.platform}" do UI.message "- Installing target `#{target.name}` #{target.platform}" do
add_target add_target
add_test_targets if target.contains_test_specifications?
create_support_files_dir create_support_files_dir
add_resources_bundle_targets add_resources_bundle_targets
add_files_to_build_phases add_files_to_build_phases
create_xcconfig_file create_xcconfig_file
create_test_xcconfig_files if target.contains_test_specifications?
if target.requires_frameworks? if target.requires_frameworks?
create_info_plist_file create_info_plist_file
create_module_map create_module_map
create_umbrella_header do |generator| create_umbrella_header do |generator|
file_accessors = target.file_accessors
file_accessors = file_accessors.reject { |f| f.spec.test_specification? } if target.contains_test_specifications?
generator.imports += if header_mappings_dir generator.imports += if header_mappings_dir
target.file_accessors.flat_map(&:public_headers).map do |pathname| file_accessors.flat_map(&:public_headers).map do |pathname|
pathname.relative_path_from(header_mappings_dir) pathname.relative_path_from(header_mappings_dir)
end end
else else
target.file_accessors.flat_map(&:public_headers).map(&:basename) file_accessors.flat_map(&:public_headers).map(&:basename)
end end
end end
create_build_phase_to_symlink_header_folders create_build_phase_to_symlink_header_folders
...@@ -130,6 +134,7 @@ module Pod ...@@ -130,6 +134,7 @@ module Pod
target.file_accessors.each do |file_accessor| target.file_accessors.each do |file_accessor|
consumer = file_accessor.spec_consumer consumer = file_accessor.spec_consumer
native_target = native_target_for_consumer(consumer)
headers = file_accessor.headers headers = file_accessor.headers
public_headers = file_accessor.public_headers public_headers = file_accessor.public_headers
private_headers = file_accessor.private_headers private_headers = file_accessor.private_headers
...@@ -147,7 +152,7 @@ module Pod ...@@ -147,7 +152,7 @@ module Pod
header_file_refs = project_file_references_array(headers, 'header') header_file_refs = project_file_references_array(headers, 'header')
native_target.add_file_references(header_file_refs) do |build_file| native_target.add_file_references(header_file_refs) do |build_file|
add_header(build_file, public_headers, private_headers) add_header(build_file, public_headers, private_headers, native_target)
end end
other_file_refs = other_source_files.map { |sf| project.reference_for_path(sf) } other_file_refs = other_source_files.map { |sf| project.reference_for_path(sf) }
...@@ -162,6 +167,72 @@ module Pod ...@@ -162,6 +167,72 @@ module Pod
end end
end end
# Returns the corresponding native target to use based on the provided consumer.
# This is used to figure out whether to add a source file into the library native target or any of the
# test native targets.
#
# @param [Consumer] consumer
# The consumer to base from in order to find the native target.
#
# @return [PBXNativeTarget] the native target to use or `nil` if none is found.
#
def native_target_for_consumer(consumer)
return native_target unless consumer.spec.test_specification?
target.test_native_targets.find do |native_target|
native_target.symbol_type == product_type_for_test_type(consumer.spec.test_type)
end
end
# Returns the corresponding native product type to use given the test type.
# This is primarily used when creating the native targets in order to produce the correct test bundle target
# based on the type of tests included.
#
# @param [Symbol] test_type
# The test type to map to provided by the test specification DSL.
#
# @return [Symbol] The native product type to use.
#
def product_type_for_test_type(test_type)
case test_type
when :unit
:unit_test_bundle
else
raise Informative, "Unknown test type passed `#{test_type}`."
end
end
# Adds the test targets for the library to the Pods project with the
# appropriate build configurations.
#
# @return [void]
#
def add_test_targets
target.supported_test_types.each do |test_type|
product_type = product_type_for_test_type(test_type)
name = target.test_target_label(test_type)
platform = target.platform.name
language = target.uses_swift? ? :swift : :objc
native_test_target = project.new_target(product_type, name, platform, deployment_target, nil, language)
product_name = target.product_name
product = native_test_target.product_reference
product.name = product_name
target.user_build_configurations.each do |bc_name, type|
native_test_target.add_build_configuration(bc_name, type)
end
native_test_target.build_configurations.each do |configuration|
configuration.build_settings.merge!(custom_build_settings)
# target_installer will automatically add an empth `OTHER_LDFLAGS`. For test
# targets those are set via a test xcconfig file instead.
configuration.build_settings.delete('OTHER_LDFLAGS')
end
target.test_native_targets << native_test_target
end
end
# Adds the resources of the Pods to the Pods project. # Adds the resources of the Pods to the Pods project.
# #
# @note The source files are grouped by Pod and in turn by subspec # @note The source files are grouped by Pod and in turn by subspec
...@@ -186,6 +257,7 @@ module Pod ...@@ -186,6 +257,7 @@ module Pod
bundle_target.add_resources(resource_phase_refs + compile_phase_refs) bundle_target.add_resources(resource_phase_refs + compile_phase_refs)
end end
native_target = native_target_for_consumer(file_accessor.spec_consumer)
target.user_build_configurations.each do |bc_name, type| target.user_build_configurations.each do |bc_name, type|
bundle_target.add_build_configuration(bc_name, type) bundle_target.add_build_configuration(bc_name, type)
end end
...@@ -221,7 +293,7 @@ module Pod ...@@ -221,7 +293,7 @@ module Pod
:watchos => '1,2' # The device family for watchOS is 4, but Xcode creates watchkit-compatible bundles as 1,2 :watchos => '1,2' # The device family for watchOS is 4, but Xcode creates watchkit-compatible bundles as 1,2
} }
if family = device_family_by_platform[target.platform.name] if (family = device_family_by_platform[target.platform.name])
c.build_settings['TARGETED_DEVICE_FAMILY'] = family c.build_settings['TARGETED_DEVICE_FAMILY'] = family
end end
end end
...@@ -251,6 +323,25 @@ module Pod ...@@ -251,6 +323,25 @@ module Pod
end end
end end
# Generates the contents of the xcconfig file used for each test target type and saves it to disk.
#
# @return [void]
#
def create_test_xcconfig_files
target.supported_test_types.each do |test_type|
path = target.xcconfig_path(test_type.to_s)
xcconfig_gen = Generator::XCConfig::PodXCConfig.new(target, true)
xcconfig_gen.save_as(path)
xcconfig_file_ref = add_file_to_support_group(path)
target.test_native_targets.each do |test_target|
test_target.build_configurations.each do |test_target_bc|
test_target_bc.base_configuration_reference = xcconfig_file_ref
end
end
end
end
# Creates a build phase which links the versioned header folders # Creates a build phase which links the versioned header folders
# of the OS X into the framework bundle's root root directory. # of the OS X into the framework bundle's root root directory.
# This is only necessary because the way how headers are copied # This is only necessary because the way how headers are copied
...@@ -400,7 +491,7 @@ module Pod ...@@ -400,7 +491,7 @@ module Pod
end end
end end
def add_header(build_file, public_headers, private_headers) def add_header(build_file, public_headers, private_headers, native_target)
file_ref = build_file.file_ref file_ref = build_file.file_ref
acl = if public_headers.include?(file_ref.real_path) acl = if public_headers.include?(file_ref.real_path)
'Public' 'Public'
......
...@@ -305,7 +305,7 @@ module Pod ...@@ -305,7 +305,7 @@ module Pod
find_cached_set(dependency). find_cached_set(dependency).
all_specifications(installation_options.warn_for_multiple_pod_sources). all_specifications(installation_options.warn_for_multiple_pod_sources).
select { |s| requirement.satisfied_by? s.version }. select { |s| requirement.satisfied_by? s.version }.
map { |s| s.subspec_by_name(dependency.name, false) }. map { |s| s.subspec_by_name(dependency.name, false, true) }.
compact. compact.
reverse reverse
end end
......
...@@ -18,11 +18,11 @@ module Pod ...@@ -18,11 +18,11 @@ module Pod
specification.respond_to?(method, include_all) specification.respond_to?(method, include_all)
end end
def subspec_by_name(name = nil, raise_if_missing = true) def subspec_by_name(name = nil, raise_if_missing = true, include_test_specifications = false)
if !name || name == self.name if !name || name == self.name
self self
else else
specification.subspec_by_name(name, raise_if_missing) specification.subspec_by_name(name, raise_if_missing, include_test_specifications)
end end
end end
......
...@@ -28,6 +28,16 @@ module Pod ...@@ -28,6 +28,16 @@ module Pod
# #
attr_accessor :dependent_targets attr_accessor :dependent_targets
# @return [Array<PodTarget>] the targets that this target has a test dependency
# upon.
#
attr_accessor :test_dependent_targets
# return [Array<PBXNativeTarget>] the test target generated in the Pods project for
# this library or `nil` if there is no test target created.
#
attr_accessor :test_native_targets
# @param [Array<Specification>] specs @see #specs # @param [Array<Specification>] specs @see #specs
# @param [Array<TargetDefinition>] target_definitions @see #target_definitions # @param [Array<TargetDefinition>] target_definitions @see #target_definitions
# @param [Sandbox] sandbox @see #sandbox # @param [Sandbox] sandbox @see #sandbox
...@@ -46,7 +56,9 @@ module Pod ...@@ -46,7 +56,9 @@ module Pod
@build_headers = Sandbox::HeadersStore.new(sandbox, 'Private') @build_headers = Sandbox::HeadersStore.new(sandbox, 'Private')
@file_accessors = [] @file_accessors = []
@resource_bundle_targets = [] @resource_bundle_targets = []
@test_native_targets = []
@dependent_targets = [] @dependent_targets = []
@test_dependent_targets = []
@build_config_cache = {} @build_config_cache = {}
end end
...@@ -133,7 +145,7 @@ module Pod ...@@ -133,7 +145,7 @@ module Pod
# #
attr_accessor :file_accessors attr_accessor :file_accessors
# @return [Array<PBXTarget>] the resource bundle targets belonging # @return [Array<PBXNativeTarget>] the resource bundle targets belonging
# to this target. # to this target.
attr_reader :resource_bundle_targets attr_reader :resource_bundle_targets
...@@ -157,7 +169,7 @@ module Pod ...@@ -157,7 +169,7 @@ module Pod
specs.map { |spec| spec.consumer(platform) } specs.map { |spec| spec.consumer(platform) }
end end
# @return [Boolean] Whether the target uses Swift code # @return [Boolean] Whether the target uses Swift code.
# #
def uses_swift? def uses_swift?
return @uses_swift if defined? @uses_swift return @uses_swift if defined? @uses_swift
...@@ -168,6 +180,18 @@ module Pod ...@@ -168,6 +180,18 @@ module Pod
end end
end end
# @return [Boolean] Whether the target has any tests specifications.
#
def contains_test_specifications?
specs.any?(&:test_specification?)
end
# @return [Array<Symbol>] All of the test supported types within this target.
#
def supported_test_types
specs.select(&:test_specification?).map(&:test_type).uniq
end
# @return [Specification] The root specification for the target. # @return [Specification] The root specification for the target.
# #
def root_spec def root_spec
...@@ -189,6 +213,15 @@ module Pod ...@@ -189,6 +213,15 @@ module Pod
"#{label}-#{bundle_name}" "#{label}-#{bundle_name}"
end end
# @param [Symbol] test_type
# The test type to use for producing the test label.
#
# @return [String] The derived name of the test target.
#
def test_target_label(test_type)
"#{label}-#{test_type.capitalize}-Tests"
end
# @return [Array<String>] The names of the Pods on which this target # @return [Array<String>] The names of the Pods on which this target
# depends. # depends.
# #
...@@ -234,7 +267,7 @@ module Pod ...@@ -234,7 +267,7 @@ module Pod
if whitelists.empty? if whitelists.empty?
@build_config_cache[key] = true @build_config_cache[key] = true
return true true
elsif whitelists.count == 1 elsif whitelists.count == 1
@build_config_cache[key] = whitelists.first @build_config_cache[key] = whitelists.first
whitelists.first whitelists.first
...@@ -258,7 +291,7 @@ module Pod ...@@ -258,7 +291,7 @@ module Pod
if whitelists.empty? if whitelists.empty?
@inhibit_warnings = false @inhibit_warnings = false
return false false
elsif whitelists.count == 1 elsif whitelists.count == 1
@inhibit_warnings = whitelists.first @inhibit_warnings = whitelists.first
whitelists.first whitelists.first
...@@ -268,7 +301,7 @@ module Pod ...@@ -268,7 +301,7 @@ module Pod
'settings to inhibit warnings. CocoaPods does not currently ' \ 'settings to inhibit warnings. CocoaPods does not currently ' \
'support different settings and will fall back to your preference ' \ 'support different settings and will fall back to your preference ' \
'set in the root target definition.' 'set in the root target definition.'
return podfile.root_target_definitions.first.inhibits_warnings_for_pod?(root_spec.name) podfile.root_target_definitions.first.inhibits_warnings_for_pod?(root_spec.name)
end end
end end
......
#import <Foundation/Foundation.h>
/** Coconuts are cool */
@interface CoconutObj : NSObject
@end
Pod::Spec.new do |s|
s.name = 'CoconutLib'
s.version = '1.0'
s.authors = 'Coconut Corp', { 'Monkey Boy' => 'monkey@coconut-corp.local' }
s.homepage = 'http://coconut-corp.local/coconut-lib.html'
s.summary = 'Coconuts For the Win.'
s.description = 'All the Coconuts'
s.source = { :git => 'http://coconut-corp.local/coconut-lib.git', :tag => 'v1.0' }
s.license = {
:type => 'MIT',
:file => 'LICENSE',
:text => 'Permission is hereby granted ...'
}
s.source_files = 'Classes/*.{h,m}'
s.test_spec do |test_spec|
test_spec.source_files = 'Tests/*.{h,m}'
test_spec.dependency 'OCMock'
end
end
#import <XCTest/XCTest.h>
@interface CoconutTests : XCTestCase
@end
@implementation CoconutTests
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
@end
...@@ -128,6 +128,48 @@ module Pod ...@@ -128,6 +128,48 @@ module Pod
generated.class.should == Xcodeproj::Config generated.class.should == Xcodeproj::Config
end end
end end
describe 'test xcconfig generation' do
before do
@monkey_spec = fixture_spec('monkey/monkey.podspec')
@monkey_pod_target = fixture_pod_target(@monkey_spec)
@banana_spec = fixture_spec('banana-lib/BananaLib.podspec')
@banana_pod_target = fixture_pod_target(@banana_spec)
@coconut_spec = fixture_spec('coconut-lib/CoconutLib.podspec')
@coconut_pod_target = fixture_pod_target(@coconut_spec)
@consumer = @coconut_pod_target.spec_consumers.first
@podfile = @coconut_pod_target.podfile
file_accessors = [Sandbox::FileAccessor.new(fixture('coconut-lib'), @consumer)]
@coconut_pod_target.stubs(:file_accessors).returns(file_accessors)
end
it 'includes other ld flags for test dependent targets' do
@coconut_pod_target.test_dependent_targets = [@monkey_pod_target]
generator = PodXCConfig.new(@coconut_pod_target, true)
xcconfig = generator.generate
xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-l"monkey" -framework "dynamic-monkey"'
end
it 'adds settings for test dependent targets' do
@coconut_pod_target.test_dependent_targets = [@banana_pod_target]
generator = PodXCConfig.new(@coconut_pod_target, true)
xcconfig = generator.generate
xcconfig.to_hash['LIBRARY_SEARCH_PATHS'].should == '$(inherited) "$PODS_CONFIGURATION_BUILD_DIR/BananaLib" $(inherited) "${PODS_ROOT}/../../spec/fixtures/banana-lib"'
end
it 'does not include other ld flags for test dependent targets if its not a test xcconfig' do
@coconut_pod_target.test_dependent_targets = [@monkey_pod_target]
generator = PodXCConfig.new(@coconut_pod_target)
xcconfig = generator.generate
xcconfig.to_hash['LIBRARY_SEARCH_PATHS'].should.be.nil
xcconfig.to_hash['OTHER_LDFLAGS'].should.be.nil
end
end
end end
end end
end end
......
...@@ -139,6 +139,106 @@ module Pod ...@@ -139,6 +139,106 @@ module Pod
end end
end end
describe 'test target generation' do
before do
config.sandbox.prepare
@podfile = Podfile.new do
platform :ios, '6.0'
project 'SampleProject/SampleProject'
target 'SampleProject'
end
@target_definition = @podfile.target_definitions['SampleProject']
@project = Project.new(config.sandbox.project_path)
config.sandbox.project = @project
@coconut_spec = fixture_spec('coconut-lib/CoconutLib.podspec')
# Add sources to the project.
file_accessor = Sandbox::FileAccessor.new(Sandbox::PathList.new(fixture('coconut-lib')), @coconut_spec.consumer(:ios))
@project.add_pod_group('CoconutLib', fixture('coconut-lib'))
group = @project.group_for_spec('CoconutLib')
file_accessor.source_files.each do |file|
@project.add_file_reference(file, group)
end
file_accessor.resources.each do |resource|
@project.add_file_reference(resource, group)
end
# Add test sources to the project.
test_file_accessor = Sandbox::FileAccessor.new(Sandbox::PathList.new(fixture('coconut-lib')), @coconut_spec.test_specs.first.consumer(:ios))
@project.add_pod_group('CoconutLibTests', fixture('coconut-lib'))
group = @project.group_for_spec('CoconutLibTests')
test_file_accessor.source_files.each do |file|
@project.add_file_reference(file, group)
end
test_file_accessor.resources.each do |resource|
@project.add_file_reference(resource, group)
end
@coconut_pod_target = PodTarget.new([@coconut_spec, *@coconut_spec.recursive_subspecs], [@target_definition], config.sandbox)
@coconut_pod_target.file_accessors = [file_accessor, test_file_accessor]
@coconut_pod_target.user_build_configurations = { 'Debug' => :debug, 'Release' => :release }
@installer = PodTargetInstaller.new(config.sandbox, @coconut_pod_target)
end
it 'adds the native test target to the project' do
@installer.install!
@project.targets.count.should == 2
@project.targets.first.name.should == 'CoconutLib'
native_test_target = @project.targets[1]
native_test_target.name.should == 'CoconutLib-Unit-Tests'
native_test_target.symbol_type.should == :unit_test_bundle
@coconut_pod_target.test_native_targets.count.should == 1
end
it 'adds files to build phases correctly depending on the native target' do
@installer.install!
@project.targets.count.should == 2
native_target = @project.targets[0]
native_target.source_build_phase.files.count.should == 2
native_target.source_build_phase.files.map(&:display_name).sort.should == [
'Coconut.m',
'CoconutLib-dummy.m',
]
native_test_target = @project.targets[1]
native_test_target.source_build_phase.files.count.should == 1
native_test_target.source_build_phase.files.map(&:display_name).sort.should == [
'CoconutTests.m',
]
end
it 'adds xcconfig file reference for test native targets' do
@installer.install!
@project.support_files_group
group = @project['Pods/CoconutLib/Support Files']
group.children.map(&:display_name).sort.should.include 'CoconutLib.unit.xcconfig'
end
it 'does not add test header imports to umbrella header' do
@coconut_pod_target.stubs(:requires_frameworks?).returns(true)
@installer.install!
content = @coconut_pod_target.umbrella_header_path.read
content.should.not =~ /"CoconutTestHeader.h"/
end
it 'returns the correct native target based on the consumer provided' do
@installer.install!
native_target = @installer.send(:native_target_for_consumer, @coconut_spec.consumer(:ios))
native_target.name.should == 'CoconutLib'
test_native_target = @installer.send(:native_target_for_consumer, @coconut_spec.test_specs.first.consumer(:ios))
test_native_target.name.should == 'CoconutLib-Unit-Tests'
end
it 'returns the correct product type for test type' do
@installer.send(:product_type_for_test_type, :unit).should == :unit_test_bundle
end
it 'raises for unknown test type' do
exception = lambda { @installer.send(:product_type_for_test_type, :weird_test_type) }.should.raise Informative
exception.message.should.include 'Unknown test type passed `weird_test_type`.'
end
end
#--------------------------------------# #--------------------------------------#
it 'adds the source files of each pod to the target of the Pod library' do it 'adds the source files of each pod to the target of the Pod library' do
......
...@@ -242,6 +242,50 @@ module Pod ...@@ -242,6 +242,50 @@ module Pod
#--------------------------------------# #--------------------------------------#
describe '#set_test_target_dependencies' do
before do
spec = fixture_spec('coconut-lib/CoconutLib.podspec')
target_definition = Podfile::TargetDefinition.new(:default, @installer.podfile.root_target_definitions.first)
@pod_target = PodTarget.new([spec, *spec.recursive_subspecs], [target_definition], config.sandbox)
@target = AggregateTarget.new(target_definition, config.sandbox)
@mock_target = mock('PodNativeTarget')
mock_project = mock('PodsProject', :frameworks_group => mock('FrameworksGroup'))
@generator.stubs(:project).returns(mock_project)
@target.stubs(:native_target).returns(@mock_target)
@target.stubs(:pod_targets).returns([@pod_target])
@generator.stubs(:aggregate_targets).returns([@target])
end
it 'adds test dependent targets to test native targets' do
mock_native_target = mock('CoconutLib')
mock_test_native_target = mock('CoconutLib-Unit-Tests')
dependent_target = mock('dependent-target', :should_build? => true, :native_target => 'DependentNativeTarget')
test_dependent_target = mock('dependent-test-target', :should_build? => true, :native_target => 'TestDependentNativeTarget')
@pod_target.stubs(:native_target).returns(mock_native_target)
@pod_target.stubs(:test_native_targets).returns([mock_test_native_target])
@pod_target.stubs(:dependent_targets).returns([dependent_target])
@pod_target.stubs(:test_dependent_targets).returns([test_dependent_target])
@pod_target.stubs(:should_build? => true)
@mock_target.expects(:add_dependency).with(mock_native_target)
mock_native_target.expects(:add_dependency).with('DependentNativeTarget')
mock_native_target.expects(:add_dependency).with('TestDependentNativeTarget').never
mock_test_native_target.expects(:add_dependency).with('DependentNativeTarget').never
mock_test_native_target.expects(:add_dependency).with('TestDependentNativeTarget')
@generator.send(:set_target_dependencies)
end
end
#--------------------------------------#
describe '#write' do describe '#write' do
before do before do
@generator.stubs(:aggregate_targets).returns([]) @generator.stubs(:aggregate_targets).returns([])
...@@ -303,6 +347,30 @@ module Pod ...@@ -303,6 +347,30 @@ module Pod
@generator.send(:share_development_pod_schemes) @generator.send(:share_development_pod_schemes)
end end
it 'shares test schemes' do
spec = fixture_spec('coconut-lib/CoconutLib.podspec')
target_definition = Podfile::TargetDefinition.new(:default, @installer.podfile.root_target_definitions.first)
pod_target = Pod::PodTarget.new([spec, *spec.recursive_subspecs], [target_definition], config.sandbox)
pod_target.stubs(:should_build?).returns(true)
@generator.installation_options.
stubs(:share_schemes_for_development_pods).
returns(true)
@generator.stubs(:pod_targets).returns([pod_target])
@generator.sandbox.stubs(:development_pods).returns('CoconutLib' => nil)
Xcodeproj::XCScheme.expects(:share_scheme).with(
@generator.project.path,
'CoconutLib')
Xcodeproj::XCScheme.expects(:share_scheme).with(
@generator.project.path,
'CoconutLib-Unit-Tests')
@generator.send(:share_development_pod_schemes)
end
it 'allows opting out' do it 'allows opting out' do
@generator.installation_options. @generator.installation_options.
stubs(:share_schemes_for_development_pods). stubs(:share_schemes_for_development_pods).
......
...@@ -219,6 +219,10 @@ module Pod ...@@ -219,6 +219,10 @@ module Pod
it 'returns that it requires being built as framework' do it 'returns that it requires being built as framework' do
@pod_target.requires_frameworks?.should == true @pod_target.requires_frameworks?.should == true
end end
it 'returns that it has no test specifications' do
@pod_target.contains_test_specifications?.should == false
end
end end
describe 'Host does not requires frameworks' do describe 'Host does not requires frameworks' do
...@@ -304,6 +308,28 @@ module Pod ...@@ -304,6 +308,28 @@ module Pod
end end
end end
end end
describe 'test spec support' do
before do
spec = fixture_spec('coconut-lib/CoconutLib.podspec')
@test_spec_target_definition = Podfile::TargetDefinition.new('Pods', nil)
@test_spec_target_definition.abstract = false
@test_pod_target = PodTarget.new([spec, *spec.recursive_subspecs], [@test_spec_target_definition], config.sandbox)
@test_pod_target.stubs(:platform).returns(:ios)
end
it 'returns that it has test specifications' do
@test_pod_target.contains_test_specifications?.should == true
end
it 'returns supported test types' do
@test_pod_target.supported_test_types.should == [:unit]
end
it 'returns test label based on test type' do
@test_pod_target.test_target_label(:unit).should == 'CoconutLib-Unit-Tests'
end
end
end end
end end
end end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment