Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #6803 from dnkoutso/inputs_outputs_improvements
Simplify logic around framework input and output paths
  • Loading branch information
dnkoutso committed Jun 16, 2017
2 parents 7bc7e36 + 951effc commit 7246c40
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 44 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,10 @@ To install release candidates run `[sudo] gem install cocoapods --pre`

##### Enhancements

* Simplify logic around framework input and output paths
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#6803](https://github.com/CocoaPods/CocoaPods/pull/6803)

* Add inputs and outputs to check manifest lock and embed framework script phases
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#6797](https://github.com/CocoaPods/CocoaPods/issues/6797)
Expand Down
4 changes: 2 additions & 2 deletions lib/cocoapods/generator/embed_frameworks_script.rb
Expand Up @@ -139,10 +139,10 @@ def script
unless frameworks_with_dsyms.empty?
script << %(if [[ "$CONFIGURATION" == "#{config}" ]]; then\n)
frameworks_with_dsyms.each do |framework_with_dsym|
script << %( install_framework "#{framework_with_dsym[:framework]}"\n)
script << %( install_framework "#{framework_with_dsym[:input_path]}"\n)
# Vendored frameworks might have a dSYM file next to them so ensure its copied. Frameworks built from
# sources will have their dSYM generated and copied by Xcode.
script << %( install_dsym "#{framework_with_dsym[:dsym]}"\n) unless framework_with_dsym[:dsym].nil?
script << %( install_dsym "#{framework_with_dsym[:dsym_input_path]}"\n) unless framework_with_dsym[:dsym_input_path].nil?
end
script << "fi\n"
end
Expand Down
Expand Up @@ -135,10 +135,10 @@ def add_embed_frameworks_script_phase_to_target(native_target)
phase = create_or_update_build_phase(native_target, EMBED_FRAMEWORK_PHASE_NAME)
script_path = target.embed_frameworks_script_relative_path
phase.shell_script = %("#{script_path}"\n)
frameworks_by_config = target.frameworks_by_config.values.flatten.uniq
unless frameworks_by_config.empty?
phase.input_paths = [target.embed_frameworks_script_relative_path, *frameworks_by_config.map { |fw| [fw[:framework], fw[:dsym]] }.flatten.compact]
phase.output_paths = frameworks_by_config.map { |fw| [fw[:install_path], fw[:dsym_install_path]] }.flatten.compact
framework_paths_by_config = target.framework_paths_by_config.values.flatten.uniq
unless framework_paths_by_config.empty?
phase.input_paths = [target.embed_frameworks_script_relative_path, *framework_paths_by_config.map { |fw| [fw[:input_path], fw[:dsym_input_path]] }.flatten.compact]
phase.output_paths = framework_paths_by_config.map { |fw| [fw[:output_path], fw[:dsym_output_path]] }.flatten.compact
end
end

Expand Down
Expand Up @@ -135,7 +135,7 @@ def create_copy_resources_script
#
def create_embed_frameworks_script
path = target.embed_frameworks_script_path
generator = Generator::EmbedFrameworksScript.new(target.frameworks_by_config)
generator = Generator::EmbedFrameworksScript.new(target.framework_paths_by_config)
generator.save_as(path)
add_file_to_support_group(path)
end
Expand Down
40 changes: 21 additions & 19 deletions lib/cocoapods/target/aggregate_target.rb
Expand Up @@ -184,40 +184,42 @@ def uses_swift?
pod_targets.any?(&:uses_swift?)
end

# @return [Hash{ Hash{ Symbol => String}}] The vendored dynamic artifacts and framework targets grouped by config
# @return [Hash{String => Array<Hash{Symbol => [String]}>] The vendored dynamic artifacts and framework target
# input and output paths grouped by config
#
def frameworks_by_config
frameworks_by_config = {}
def framework_paths_by_config
framework_paths_by_config = {}
user_build_configurations.keys.each do |config|
relevant_pod_targets = pod_targets.select do |pod_target|
pod_target.include_in_build_config?(target_definition, config)
end
frameworks_by_config[config] = relevant_pod_targets.flat_map do |pod_target|
frameworks = pod_target.file_accessors.flat_map(&:vendored_dynamic_artifacts).map do |fw|
relative_path_to_sandbox = fw.relative_path_from(sandbox.root)
relative_path_to_pods_root = "${PODS_ROOT}/#{relative_path_to_sandbox}"
install_path = "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/#{fw.basename}"
result = {
:framework => relative_path_to_pods_root,
:install_path => install_path,
}
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_path = Pathname.new("#{fw.dirname}/#{fw.basename}.dSYM")
dsym_name = "#{framework_path.basename}.dSYM"
dsym_path = Pathname.new("#{framework_path.dirname}/#{dsym_name}")
if dsym_path.exist?
result[:dsym] = "#{relative_path_to_pods_root}.dSYM"
result[:dsym_install_path] = "${DWARF_DSYM_FOLDER_PATH}/#{fw.basename}.dSYM"
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
result
frameworks << framework
end
# For non vendored frameworks Xcode will generate the dSYM and copy it instead.
if pod_target.should_build? && pod_target.requires_frameworks?
frameworks << { :framework => pod_target.build_product_path('${BUILT_PRODUCTS_DIR}'), :install_path => "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/#{pod_target.product_name}" }
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
end
frameworks_by_config
framework_paths_by_config
end

# @return [Hash{ Symbol => Array<Pathname> }] Uniqued Resources grouped by config
Expand Down
4 changes: 2 additions & 2 deletions spec/unit/generator/embed_frameworks_script_spec.rb
Expand Up @@ -4,8 +4,8 @@ module Pod
describe Generator::EmbedFrameworksScript do
it 'returns the embed frameworks script' do
frameworks = {
'Debug' => [{ :framework => 'Pods/Loopback.framework', :dsym => 'Pods/Loopback.framework.dSYM' }, { :framework => 'Reveal.framework', :dsym => nil }],
'Release' => [{ :framework => 'CrashlyticsFramework.framework', :dsym => nil }],
'Debug' => [{ :name => 'Loopback.framework', :input_path => 'Pods/Loopback.framework', :dsym_input_path => 'Pods/Loopback.framework.dSYM' }, { :name => 'Reveal.framework', :input_path => 'Reveal.framework' }],
'Release' => [{ :name => 'CrashlyticsFramework.framework', :input_path => 'CrashlyticsFramework.framework' }],
}
generator = Pod::Generator::EmbedFrameworksScript.new(frameworks)
generator.send(:script).should.include <<-SH.strip_heredoc
Expand Down
Expand Up @@ -285,7 +285,7 @@ module Pod
end

it 'does not add embed frameworks build phase input output paths with no frameworks' do
@pod_bundle.stubs(:frameworks_by_config => {})
@pod_bundle.stubs(:framework_paths_by_config => {})
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name }
Expand All @@ -294,24 +294,29 @@ module Pod
end

it 'adds embed frameworks build phase input and output paths for vendored and non vendored frameworks' do
debug_vendored_framework = { :framework => '${PODS_ROOT}/DebugVendoredFramework/ios/DebugVendoredFramework.framework',
:dsym => '${PODS_ROOT}/DebugVendoredFramework/ios/DebugVendoredFramework.framework.dSYM',
:install_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DebugVendoredFramework.framework',
:dsym_install_path => '${DWARF_DSYM_FOLDER_PATH}/DebugVendoredFramework.framework.dSYM' }

debug_non_vendored_framework = { :framework => '${BUILT_PRODUCTS_DIR}/DebugCompiledFramework/DebugCompiledFramework.framework',
:install_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DebugCompiledFramework.framework' }

release_vendored_framework = { :framework => '${PODS_ROOT}/ReleaseVendoredFramework/ios/ReleaseVendoredFramework.framework',
:dsym => '${PODS_ROOT}/ReleaseVendoredFramework/ios/ReleaseVendoredFramework.framework.dSYM',
:install_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReleaseVendoredFramework.framework',
:dsym_install_path => '${DWARF_DSYM_FOLDER_PATH}/ReleaseVendoredFramework.framework.dSYM' }

frameworks_by_config = {
debug_vendored_framework = { :name => 'DebugVendoredFramework.framework',
:input_path => '${PODS_ROOT}/DebugVendoredFramework/ios/DebugVendoredFramework.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DebugVendoredFramework.framework',
:dsym_name => 'DebugVendoredFramework.framework.dSYM',
:dsym_input_path => '${PODS_ROOT}/DebugVendoredFramework/ios/DebugVendoredFramework.framework.dSYM',
:dsym_output_path => '${DWARF_DSYM_FOLDER_PATH}/DebugVendoredFramework.framework.dSYM' }

debug_non_vendored_framework = { :name => 'DebugCompiledFramework.framework',
:input_path => '${BUILT_PRODUCTS_DIR}/DebugCompiledFramework/DebugCompiledFramework.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DebugCompiledFramework.framework' }

release_vendored_framework = { :name => 'ReleaseVendoredFramework.framework',
:input_path => '${PODS_ROOT}/ReleaseVendoredFramework/ios/ReleaseVendoredFramework.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReleaseVendoredFramework.framework',
:dsym_name => 'ReleaseVendoredFramework.framework.dSYM',
:dsym_input_path => '${PODS_ROOT}/ReleaseVendoredFramework/ios/ReleaseVendoredFramework.framework.dSYM',
:dsym_output_path => '${DWARF_DSYM_FOLDER_PATH}/ReleaseVendoredFramework.framework.dSYM' }

framework_paths_by_config = {
'Debug' => [debug_vendored_framework, debug_non_vendored_framework],
'Release' => [release_vendored_framework],
}
@pod_bundle.stubs(:frameworks_by_config => frameworks_by_config)
@pod_bundle.stubs(:framework_paths_by_config => framework_paths_by_config)
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name }
Expand Down
94 changes: 94 additions & 0 deletions spec/unit/target/aggregate_target_spec.rb
Expand Up @@ -124,6 +124,100 @@ module Pod
end
end

describe 'frameworks by config and input output paths' do
before do
@coconut_spec = fixture_spec('coconut-lib/CoconutLib.podspec')
@pod_target_release = PodTarget.new([@coconut_spec], [@target_definition], config.sandbox)
@target.pod_targets = [@pod_target]
@target.user_build_configurations = {
'Debug' => :debug,
'Release' => :release,
}
end

it 'returns non vendored framework input and output paths by config' do
@pod_target.stubs(:should_build?).returns(true)
@pod_target.stubs(:requires_frameworks?).returns(true)
@target.framework_paths_by_config['Debug'].should == [
{ :name => 'BananaLib.framework',
:input_path => '${BUILT_PRODUCTS_DIR}/BananaLib/BananaLib.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BananaLib.framework' },
]
@target.framework_paths_by_config['Release'].should == [
{ :name => 'BananaLib.framework',
:input_path => '${BUILT_PRODUCTS_DIR}/BananaLib/BananaLib.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BananaLib.framework' },
]
end

it 'returns non vendored frameworks by config with different release and debug targets' do
@pod_target_release.stubs(:should_build?).returns(true)
@pod_target_release.stubs(:requires_frameworks?).returns(true)
@pod_target_release.expects(:include_in_build_config?).with(@target_definition, 'Debug').returns(false)
@pod_target_release.expects(:include_in_build_config?).with(@target_definition, 'Release').returns(true)
@pod_target.stubs(:should_build?).returns(true)
@pod_target.stubs(:requires_frameworks?).returns(true)
@target.pod_targets = [@pod_target, @pod_target_release]
framework_paths_by_config = @target.framework_paths_by_config
framework_paths_by_config['Debug'].should == [
{ :name => 'BananaLib.framework',
:input_path => '${BUILT_PRODUCTS_DIR}/BananaLib/BananaLib.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BananaLib.framework' },
]
framework_paths_by_config['Release'].should == [
{ :name => 'BananaLib.framework',
:input_path => '${BUILT_PRODUCTS_DIR}/BananaLib/BananaLib.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BananaLib.framework' },
{ :name => 'CoconutLib.framework',
:input_path => '${BUILT_PRODUCTS_DIR}/CoconutLib/CoconutLib.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CoconutLib.framework' },
]
end

it 'returns vendored frameworks by config' do
path_list = Sandbox::PathList.new(fixture('banana-lib'))
file_accessor = Sandbox::FileAccessor.new(path_list, @spec.consumer(:ios))
@pod_target.file_accessors = [file_accessor]
@pod_target.file_accessors.first.stubs(:vendored_dynamic_artifacts).returns(
[Pathname('/some/absolute/path/to/FrameworkA.framework')],
)
@target.framework_paths_by_config['Debug'].should == [
{ :name => 'FrameworkA.framework',
:input_path => '${PODS_ROOT}/../../../../../../../some/absolute/path/to/FrameworkA.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FrameworkA.framework' },
]
end

it 'returns correct input and output paths for non vendored frameworks' do
@pod_target.stubs(:should_build?).returns(true)
@pod_target.stubs(:requires_frameworks?).returns(true)
@target.framework_paths_by_config['Debug'].should == [
{ :name => 'BananaLib.framework',
:input_path => '${BUILT_PRODUCTS_DIR}/BananaLib/BananaLib.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BananaLib.framework' },
]
@target.framework_paths_by_config['Release'].should == [
{ :name => 'BananaLib.framework',
:input_path => '${BUILT_PRODUCTS_DIR}/BananaLib/BananaLib.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BananaLib.framework' },
]
end

it 'returns correct input and output paths for vendored frameworks' do
path_list = Sandbox::PathList.new(fixture('banana-lib'))
file_accessor = Sandbox::FileAccessor.new(path_list, @spec.consumer(:ios))
@pod_target.file_accessors = [file_accessor]
@pod_target.file_accessors.first.stubs(:vendored_dynamic_artifacts).returns(
[Pathname('/absolute/path/to/FrameworkA.framework')],
)
@target.framework_paths_by_config['Debug'].should == [
{ :name => 'FrameworkA.framework',
:input_path => '${PODS_ROOT}/../../../../../../../absolute/path/to/FrameworkA.framework',
:output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FrameworkA.framework' },
]
end
end

it 'returns the specs of the Pods used by this aggregate target' do
@target.specs.map(&:name).should == ['BananaLib']
end
Expand Down

0 comments on commit 7246c40

Please sign in to comment.