Commit 8b7778f6 authored by Dimitris Koutsogiorgas's avatar Dimitris Koutsogiorgas Committed by GitHub

Merge pull request #6828 from dnkoutso/copy_frameworks_and_resources_to_tests

Integrate test targets to embed frameworks and resources
parents 7e0a52c8 beba752e
......@@ -18,6 +18,10 @@ To install release candidates run `[sudo] gem install cocoapods --pre`
##### Bug Fixes
* Integrate test targets to embed frameworks and resources
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#6828](https://github.com/CocoaPods/CocoaPods/pull/6828)
* Ensure resource bundle and test dependencies are set for test native targets
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#6829](https://github.com/CocoaPods/CocoaPods/pull/6829)
......
......@@ -70,10 +70,10 @@ module Pod
end
XCConfigHelper.add_target_specific_settings(target, @xcconfig)
recursive_dependent_targets = target.recursive_dependent_targets
@xcconfig.merge! XCConfigHelper.settings_for_dependent_targets(target, recursive_dependent_targets)
@xcconfig.merge! XCConfigHelper.settings_for_dependent_targets(target, recursive_dependent_targets, @test_xcconfig)
if @test_xcconfig
test_dependent_targets = [target, *target.test_dependent_targets]
@xcconfig.merge! XCConfigHelper.settings_for_dependent_targets(target, test_dependent_targets - recursive_dependent_targets)
@xcconfig.merge! XCConfigHelper.settings_for_dependent_targets(target, test_dependent_targets - recursive_dependent_targets, @test_xcconfig)
XCConfigHelper.generate_vendored_build_settings(nil, test_dependent_targets, @xcconfig)
XCConfigHelper.generate_other_ld_flags(nil, test_dependent_targets, @xcconfig)
XCConfigHelper.generate_ld_runpath_search_paths(target, false, true, @xcconfig)
......
......@@ -265,9 +265,12 @@ module Pod
# @param [Array<PodTarget>] dependent_targets
# The pod targets the given target depends on.
#
# @param [Boolean] test_xcconfig
# Whether the settings for dependent targets are being generated for a test xcconfig or not.
#
# @return [Hash<String, String>] the settings
#
def self.settings_for_dependent_targets(target, dependent_targets)
def self.settings_for_dependent_targets(target, dependent_targets, test_xcconfig = false)
dependent_targets = dependent_targets.select(&:should_build?)
# Alias build dirs to avoid recursive definitions for pod targets and depending
......@@ -278,7 +281,7 @@ module Pod
}
# Scope pod targets
if target.respond_to?(:configuration_build_dir)
if !test_xcconfig && target.respond_to?(:configuration_build_dir)
build_settings['CONFIGURATION_BUILD_DIR'] = target.configuration_build_dir(CONFIGURATION_BUILD_DIR_VARIABLE)
end
......
module Pod
class Installer
# This class is responsible for integrating a pod target. This includes integrating
# the test targets included by each pod target.
#
class PodTargetIntegrator
# @return [PodTarget] the target that should be integrated.
#
attr_reader :target
# Init a new PodTargetIntegrator.
#
# @param [PodTarget] target @see #target
#
def initialize(target)
@target = target
end
# Integrates the pod target.
#
# @return [void]
#
def integrate!
UI.section(integration_message) do
target.test_native_targets.each do |native_target|
add_embed_frameworks_script_phase(native_target)
add_copy_resources_script_phase(native_target)
end
end
end
# @return [String] a string representation suitable for debugging.
#
def inspect
"#<#{self.class} for target `#{target.label}'>"
end
private
# @!group Integration steps
#---------------------------------------------------------------------#
# Find or create a 'Copy Pods Resources' build phase
#
# @return [void]
#
def add_copy_resources_script_phase(native_target)
test_type = target.test_type_for_product_type(native_target.symbol_type)
script_path = "${PODS_ROOT}/#{target.copy_resources_script_path_for_test_type(test_type).relative_path_from(target.sandbox.root)}"
resource_paths = [target, *target.test_dependent_targets].flat_map(&:resource_paths)
input_paths = []
output_paths = []
unless resource_paths.empty?
input_paths = [script_path, *resource_paths.flatten.uniq]
output_paths = ['${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}']
end
Pod::Installer::UserProjectIntegrator::TargetIntegrator.add_copy_resources_script_phase_to_target(native_target, script_path, input_paths, output_paths)
end
# Find or create a 'Embed Pods Frameworks' Copy Files Build Phase
#
# @return [void]
#
def add_embed_frameworks_script_phase(native_target)
test_type = target.test_type_for_product_type(native_target.symbol_type)
script_path = "${PODS_ROOT}/#{target.embed_frameworks_script_path_for_test_type(test_type).relative_path_from(target.sandbox.root)}"
framework_paths = [target, *target.test_dependent_targets].flat_map(&:framework_paths)
input_paths = []
output_paths = []
unless framework_paths.empty?
input_paths = [script_path, *framework_paths.map { |fw| [fw[:input_path], fw[:dsym_input_path]] }.flatten.compact]
output_paths = framework_paths.map { |fw| [fw[:output_path], fw[:dsym_output_path]] }.flatten.compact
end
Pod::Installer::UserProjectIntegrator::TargetIntegrator.add_embed_frameworks_script_phase_to_target(native_target, script_path, input_paths, output_paths)
end
# @return [String] the message that should be displayed for the target
# integration.
#
def integration_message
"Integrating target `#{target.name}`"
end
end
end
end
......@@ -4,6 +4,7 @@ module Pod
# The {PodsProjectGenerator} handles generation of the 'Pods/Pods.xcodeproj'
#
class PodsProjectGenerator
require 'cocoapods/installer/pods_project_integrator/pod_target_integrator'
require 'cocoapods/installer/xcode/pods_project_generator/target_installer'
require 'cocoapods/installer/xcode/pods_project_generator/pod_target_installer'
require 'cocoapods/installer/xcode/pods_project_generator/file_references_installer'
......@@ -62,6 +63,7 @@ module Pod
prepare
install_file_references
install_libraries
integrate_test_targets
set_target_dependencies
end
......@@ -175,6 +177,17 @@ module Pod
end
end
def integrate_test_targets
pod_targets_with_test_targets = pod_targets.reject { |pt| pt.test_native_targets.empty? }
unless pod_targets_with_test_targets.empty?
UI.message '- Integrating test targets' do
pod_targets_with_test_targets.each do |pod_target|
Pod::Installer::PodTargetIntegrator.new(pod_target).integrate!
end
end
end
end
def add_system_framework_dependencies
# @TODO: Add Specs
pod_targets.sort_by(&:name).each do |pod_target|
......
......@@ -18,8 +18,8 @@ module Pod
UI.message "- Installing target `#{target.name}` #{target.platform}" do
add_target
add_test_targets if target.contains_test_specifications?
create_support_files_dir
add_test_targets if target.contains_test_specifications?
add_resources_bundle_targets
add_files_to_build_phases
create_xcconfig_file
......@@ -176,9 +176,9 @@ module Pod
target.supported_test_types.each do |test_type|
product_type = target.product_type_for_test_type(test_type)
name = target.test_target_label(test_type)
platform = target.platform.name
platform_name = target.platform.name
language = target.uses_swift? ? :swift : :objc
native_test_target = project.new_target(product_type, name, platform, deployment_target, nil, language)
native_test_target = project.new_target(product_type, name, platform_name, deployment_target, nil, language)
native_test_target.product_reference.name = name
target.user_build_configurations.each do |bc_name, type|
......@@ -196,6 +196,10 @@ module Pod
configuration.build_settings['PRODUCT_NAME'] = name
end
# Test native targets also need frameworks and resources to be copied over to their xctest bundle.
create_test_target_embed_frameworks_script(test_type)
create_test_target_copy_resources_script(test_type)
target.test_native_targets << native_test_target
end
end
......@@ -310,6 +314,38 @@ module Pod
end
end
# Creates a script that copies the resources to the bundle of the test target.
#
# @param [Symbol] test_type
# The test type to create the script for.
#
# @return [void]
#
def create_test_target_copy_resources_script(test_type)
path = target.copy_resources_script_path_for_test_type(test_type)
pod_targets = [target, *target.test_dependent_targets]
resource_paths_by_config = { 'Debug' => pod_targets.flat_map(&:resource_paths) }
generator = Generator::CopyResourcesScript.new(resource_paths_by_config, target.platform)
generator.save_as(path)
add_file_to_support_group(path)
end
# Creates a script that embeds the frameworks to the bundle of the test target.
#
# @param [Symbol] test_type
# The test type to create the script for.
#
# @return [void]
#
def create_test_target_embed_frameworks_script(test_type)
path = target.embed_frameworks_script_path_for_test_type(test_type)
pod_targets = [target, *target.test_dependent_targets]
framework_paths_by_config = { 'Debug' => pod_targets.flat_map(&:framework_paths) }
generator = Generator::EmbedFrameworksScript.new(framework_paths_by_config)
generator.save_as(path)
add_file_to_support_group(path)
end
# Manually add `libswiftSwiftOnoneSupport.dylib` as it seems there is an issue with tests that do not include it for Debug configurations.
# Possibly related to Swift module optimization.
#
......
......@@ -184,7 +184,7 @@ module Pod
pod_targets.any?(&:uses_swift?)
end
# @return [Hash{String => Array<Hash{Symbol => [String]}>] The vendored dynamic artifacts and framework target
# @return [Hash{String => Array<Hash{Symbol => [String]}>}] The vendored dynamic artifacts and framework target
# input and output paths grouped by config
#
def framework_paths_by_config
......@@ -193,51 +193,21 @@ module Pod
relevant_pod_targets = pod_targets.select do |pod_target|
pod_target.include_in_build_config?(target_definition, config)
end
framework_paths_by_config[config] = relevant_pod_targets.flat_map do |pod_target|
frameworks = []
pod_target.file_accessors.flat_map(&:vendored_dynamic_artifacts).map do |framework_path|
relative_path_to_sandbox = framework_path.relative_path_from(sandbox.root)
framework = { :name => framework_path.basename.to_s,
:input_path => "${PODS_ROOT}/#{relative_path_to_sandbox}",
:output_path => "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/#{framework_path.basename}" }
# Until this can be configured, assume the dSYM file uses the file name as the framework.
# See https://github.com/CocoaPods/CocoaPods/issues/1698
dsym_name = "#{framework_path.basename}.dSYM"
dsym_path = Pathname.new("#{framework_path.dirname}/#{dsym_name}")
if dsym_path.exist?
framework[:dsym_name] = dsym_name
framework[:dsym_input_path] = "${PODS_ROOT}/#{relative_path_to_sandbox}.dSYM"
framework[:dsym_output_path] = "${DWARF_DSYM_FOLDER_PATH}/#{dsym_name}"
end
frameworks << framework
end
if pod_target.should_build? && pod_target.requires_frameworks?
frameworks << { :name => pod_target.product_name,
:input_path => pod_target.build_product_path('${BUILT_PRODUCTS_DIR}'),
:output_path => "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/#{pod_target.product_name}" }
end
frameworks
end
framework_paths_by_config[config] = relevant_pod_targets.flat_map(&:framework_paths)
end
framework_paths_by_config
end
# @return [Hash{ String => Array<String> }] Uniqued Resources grouped by config
# @return [Hash{String => Array<String>}] Uniqued Resources grouped by config
#
def resource_paths_by_config
library_targets = pod_targets.reject do |pod_target|
relevant_pod_targets = pod_targets.reject do |pod_target|
pod_target.should_build? && pod_target.requires_frameworks?
end
user_build_configurations.keys.each_with_object({}) do |config, resources_by_config|
resources_by_config[config] = library_targets.flat_map do |library_target|
next [] unless library_target.include_in_build_config?(target_definition, config)
resource_paths = library_target.file_accessors.flat_map do |accessor|
accessor.resources.flat_map { |res| "${PODS_ROOT}/#{res.relative_path_from(sandbox.project.path.dirname)}" }
end
resource_bundles = library_target.file_accessors.flat_map do |accessor|
accessor.resource_bundles.keys.map { |name| "#{library_target.configuration_build_dir}/#{name.shellescape}.bundle" }
end
(resource_paths + resource_bundles + [bridge_support_file].compact).uniq
resources_by_config[config] = relevant_pod_targets.flat_map do |pod_target|
next [] unless pod_target.include_in_build_config?(target_definition, config)
(pod_target.resource_paths + [bridge_support_file].compact).uniq
end
end
end
......
......@@ -192,6 +192,51 @@ module Pod
specs.select(&:test_specification?).map(&:test_type).uniq
end
# @return [Array<Hash{Symbol => [String]}>] The vendored and non vendored framework paths
# this target depends upon.
#
def framework_paths
@framework_paths ||= begin
frameworks = []
file_accessors.flat_map(&:vendored_dynamic_artifacts).map do |framework_path|
relative_path_to_sandbox = framework_path.relative_path_from(sandbox.root)
framework = { :name => framework_path.basename.to_s,
:input_path => "${PODS_ROOT}/#{relative_path_to_sandbox}",
:output_path => "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/#{framework_path.basename}" }
# Until this can be configured, assume the dSYM file uses the file name as the framework.
# See https://github.com/CocoaPods/CocoaPods/issues/1698
dsym_name = "#{framework_path.basename}.dSYM"
dsym_path = Pathname.new("#{framework_path.dirname}/#{dsym_name}")
if dsym_path.exist?
framework[:dsym_name] = dsym_name
framework[:dsym_input_path] = "${PODS_ROOT}/#{relative_path_to_sandbox}.dSYM"
framework[:dsym_output_path] = "${DWARF_DSYM_FOLDER_PATH}/#{dsym_name}"
end
frameworks << framework
end
if should_build? && requires_frameworks?
frameworks << { :name => product_name,
:input_path => build_product_path('${BUILT_PRODUCTS_DIR}'),
:output_path => "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/#{product_name}" }
end
frameworks
end
end
# @return [Array<String>] The resource and resource bundle paths this target depends upon.
#
def resource_paths
@resource_paths ||= begin
resource_paths = file_accessors.flat_map do |accessor|
accessor.resources.flat_map { |res| "${PODS_ROOT}/#{res.relative_path_from(sandbox.project.path.dirname)}" }
end
resource_bundles = file_accessors.flat_map do |accessor|
accessor.resource_bundles.keys.map { |name| "#{configuration_build_dir}/#{name.shellescape}.bundle" }
end
resource_paths + resource_bundles
end
end
# Returns the corresponding native target to use based on the provided specification.
# This is used to figure out whether to add a source file into the library native target or any of the
# test native targets.
......@@ -226,6 +271,22 @@ module Pod
end
end
# Returns the corresponding test type given the product type.
#
# @param [Symbol] product_type
# The product type to map to a test type.
#
# @return [Symbol] The native product type to use.
#
def test_type_for_product_type(product_type)
case product_type
when :unit_test_bundle
:unit
else
raise Informative, "Unknown product type `#{product_type}`."
end
end
# @return [Specification] The root specification for the target.
#
def root_spec
......@@ -256,6 +317,24 @@ module Pod
"#{label}-#{test_type.capitalize}-Tests"
end
# @param [Symbol] test_type
# The test type this embed frameworks script path is for.
#
# @return [Pathname] The absolute path of the copy resources script for the given test type.
#
def copy_resources_script_path_for_test_type(test_type)
support_files_dir + "#{test_target_label(test_type)}-resources.sh"
end
# @param [Symbol] test_type
# The test type this embed frameworks script path is for.
#
# @return [Pathname] The absolute path of the embed frameworks script for the given test type.
#
def embed_frameworks_script_path_for_test_type(test_type)
support_files_dir + "#{test_target_label(test_type)}-frameworks.sh"
end
# @return [Array<String>] The names of the Pods on which this target
# depends.
#
......
......@@ -113,6 +113,10 @@ module Pod
@xcconfig.to_hash['PODS_CONFIGURATION_BUILD_DIR'].should == '$PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)'
end
it 'sets the CONFIGURATION_BUILD_DIR build variable' do
@xcconfig.to_hash['CONFIGURATION_BUILD_DIR'].should.be == '$PODS_CONFIGURATION_BUILD_DIR/BananaLib'
end
it 'will be skipped when installing' do
@xcconfig.to_hash['SKIP_INSTALL'].should == 'YES'
end
......@@ -205,6 +209,12 @@ module Pod
xcconfig = generator.generate
xcconfig.to_hash['LD_RUNPATH_SEARCH_PATHS'].should == "$(inherited) '@executable_path/../Frameworks' '@loader_path/../Frameworks'"
end
it 'does not set configuration build dir for test xcconfigs' do
generator = PodXCConfig.new(@coconut_pod_target, true)
xcconfig = generator.generate
xcconfig.to_hash['CONFIGURATION_BUILD_DIR'].should.be.nil
end
end
end
end
......
......@@ -349,6 +349,23 @@ module Pod
exception = lambda { @test_pod_target.product_type_for_test_type(:weird_test_type) }.should.raise Informative
exception.message.should.include 'Unknown test type `weird_test_type`.'
end
it 'returns the correct test type for product type' do
@test_pod_target.test_type_for_product_type(:unit_test_bundle).should == :unit
end
it 'raises for unknown product type' do
exception = lambda { @test_pod_target.test_type_for_product_type(:weird_product_type) }.should.raise Informative
exception.message.should.include 'Unknown product type `weird_product_type`'
end
it 'returns correct copy resources script path for test unit test type' do
@test_pod_target.copy_resources_script_path_for_test_type(:unit).to_s.should.include 'Pods/Target Support Files/CoconutLib/CoconutLib-Unit-Tests-resources.sh'
end
it 'returns correct embed frameworks script path for test unit test type' do
@test_pod_target.embed_frameworks_script_path_for_test_type(:unit).to_s.should.include 'Pods/Target Support Files/CoconutLib/CoconutLib-Unit-Tests-frameworks.sh'
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