Commit 9b013470 authored by Dimitris Koutsogiorgas's avatar Dimitris Koutsogiorgas Committed by GitHub

Merge pull request #7092 from dnkoutso/script_phase_podspec

Add support to integrate script phases from podspecs
parents 10daf0bf c23607a6
......@@ -8,6 +8,10 @@ To install release candidates run `[sudo] gem install cocoapods --pre`
##### Enhancements
* Add support to integrate script phases from podspecs
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#7092](https://github.com/CocoaPods/CocoaPods/pull/7092)
* Add support for preventing pch file generation with the skip_pch podspec attribute
[Paul Beusterien](https://github.com/paulb777)
[#7044](https://github.com/CocoaPods/CocoaPods/pull/7044)
......
......@@ -7,7 +7,7 @@ GIT
GIT
remote: https://github.com/CocoaPods/Core.git
revision: 283ac03a69984faab02c03ad65871966f7581481
revision: 3f98bce41fd8eb6d385441eef4e5611665d13e64
branch: master
specs:
cocoapods-core (1.4.0.beta.1)
......
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.all_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.all_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
......@@ -147,6 +147,41 @@ module Pod
end
end
end
# Updates all target script phases for the current target, including creating or updating, deleting
# and re-ordering.
#
# @return [void]
#
def create_or_update_user_script_phases(script_phases, native_target)
script_phase_names = script_phases.map { |k| k[:name] }
# Delete script phases no longer present in the target definition.
native_target_script_phases = native_target.shell_script_build_phases.select { |bp| !bp.name.nil? && bp.name.start_with?(USER_BUILD_PHASE_PREFIX) }
native_target_script_phases.each do |script_phase|
script_phase_name_without_prefix = script_phase.name.sub(USER_BUILD_PHASE_PREFIX, '')
unless script_phase_names.include?(script_phase_name_without_prefix)
native_target.build_phases.delete(script_phase)
end
end
# Create or update the ones that are expected to be.
script_phases.each do |td_script_phase|
phase = TargetIntegrator.create_or_update_build_phase(native_target, USER_BUILD_PHASE_PREFIX + td_script_phase[:name])
phase.shell_script = td_script_phase[:script]
phase.shell_path = td_script_phase[:shell_path] if td_script_phase.key?(:shell_path)
phase.input_paths = td_script_phase[:input_files] if td_script_phase.key?(:input_files)
phase.output_paths = td_script_phase[:output_files] if td_script_phase.key?(:output_files)
phase.show_env_vars_in_log = td_script_phase[:show_env_vars_in_log] ? '1' : '0' if td_script_phase.key?(:show_env_vars_in_log)
end
# Move script phases to their correct index if the order has changed.
offset = native_target.build_phases.count - script_phases.count
script_phases.each_with_index do |td_script_phase, index|
current_index = native_target.build_phases.index do |bp|
bp.is_a?(Xcodeproj::Project::Object::PBXShellScriptBuildPhase) && bp.name.sub(USER_BUILD_PHASE_PREFIX, '') == td_script_phase[:name]
end
expected_index = offset + index
native_target.build_phases.insert(expected_index, native_target.build_phases.delete_at(current_index)) if current_index != expected_index
end
end
end
# Integrates the user project targets. Only the targets that do **not**
......@@ -164,7 +199,7 @@ module Pod
remove_embed_frameworks_script_phase_from_embedded_targets
add_copy_resources_script_phase
add_check_manifest_lock_script_phase
update_target_script_phases
add_user_script_phases
end
end
......@@ -265,38 +300,9 @@ module Pod
#
# @return [void]
#
def update_target_script_phases
target_definition_script_phases = target.target_definition.script_phases
target_definition_script_phase_names = target_definition_script_phases.map { |k| k[:name] }
def add_user_script_phases
native_targets.each do |native_target|
# Delete script phases no longer present in the target definition.
native_target_script_phases = native_target.shell_script_build_phases.select { |bp| !bp.name.nil? && bp.name.start_with?(USER_BUILD_PHASE_PREFIX) }
native_target_script_phases.each do |script_phase|
script_phase_name_without_prefix = script_phase.name.sub(USER_BUILD_PHASE_PREFIX, '')
unless target_definition_script_phase_names.include?(script_phase_name_without_prefix)
native_target.build_phases.delete(script_phase)
end
end
# Create or update the ones that are expected to be.
target_definition_script_phases.each do |td_script_phase|
phase = TargetIntegrator.create_or_update_build_phase(native_target, USER_BUILD_PHASE_PREFIX + td_script_phase[:name])
phase.shell_script = td_script_phase[:script]
phase.shell_path = td_script_phase[:shell_path] if td_script_phase.key?(:shell_path)
phase.input_paths = td_script_phase[:input_files] if td_script_phase.key?(:input_files)
phase.output_paths = td_script_phase[:output_files] if td_script_phase.key?(:output_files)
phase.show_env_vars_in_log = td_script_phase[:show_env_vars_in_log] ? '1' : '0' if td_script_phase.key?(:show_env_vars_in_log)
end
# Move script phases to their correct index if the order has changed.
offset = native_target.build_phases.count - target_definition_script_phases.count
target_definition_script_phases.each_with_index do |td_script_phase, index|
current_index = native_target.build_phases.index do |bp|
bp.is_a?(Xcodeproj::Project::Object::PBXShellScriptBuildPhase) && bp.name.sub(USER_BUILD_PHASE_PREFIX, '') == td_script_phase[:name]
end
expected_index = offset + index
native_target.build_phases.insert(expected_index, native_target.build_phases.delete_at(current_index)) if current_index != expected_index
end
TargetIntegrator.create_or_update_user_script_phases(target.target_definition.script_phases, native_target)
end
end
......
......@@ -4,7 +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/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'
......@@ -63,7 +63,7 @@ module Pod
prepare
install_file_references
install_libraries
integrate_test_targets
integrate_targets
set_target_dependencies
end
......@@ -179,12 +179,12 @@ module Pod
end
end
def integrate_test_targets
def integrate_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
UI.message '- Integrating targets' do
pod_targets_with_test_targets.each do |pod_target|
Pod::Installer::PodTargetIntegrator.new(pod_target).integrate!
PodTargetIntegrator.new(pod_target).integrate!
end
end
end
......
module Pod
class Installer
class Xcode
class PodsProjectGenerator
# 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_specs_by_native_target.each do |native_target, test_specs|
add_embed_frameworks_script_phase(native_target)
add_copy_resources_script_phase(native_target)
UserProjectIntegrator::TargetIntegrator.create_or_update_user_script_phases(script_phases_for_specs(test_specs), native_target)
end
specs = target.specs.reject(&:test_specification?)
UserProjectIntegrator::TargetIntegrator.create_or_update_user_script_phases(script_phases_for_specs(specs), target.native_target)
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.all_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
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.all_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
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
# @param [Array<Specification] specs
# the specs to return script phrases from.
#
# @return [Array<Hash<Symbol=>String>] an array of all combined script phases from the specs.
#
def script_phases_for_specs(specs)
specs.map { |spec| spec.consumer(target.platform) }.map(&:script_phases).flatten
end
end
end
end
end
end
require File.expand_path('../../../../../spec_helper', __FILE__)
module Pod
class Installer
class Xcode
class PodsProjectGenerator
class PodTargetIntegrator
describe 'In general' do
before do
@project = Pod::Project.new(config.sandbox.project_path)
@project.save
@target_definition = fixture_target_definition
@coconut_spec = fixture_spec('coconut-lib/CoconutLib.podspec')
@coconut_pod_target = PodTarget.new([@coconut_spec, *@coconut_spec.recursive_subspecs], [@target_definition], config.sandbox)
@native_target = stub('NativeTarget', :shell_script_build_phases => [], :build_phases => [], :project => @project)
@test_native_target = stub('TestNativeTarget', :symbol_type => :unit_test_bundle, :build_phases => [], :shell_script_build_phases => [], :project => @project)
@coconut_pod_target.stubs(:native_target).returns(@native_target)
@coconut_pod_target.stubs(:test_native_targets).returns([@test_native_target])
end
describe '#integrate!' do
it 'integrates test native targets with frameworks and resources script phases' do
PodTargetIntegrator.new(@coconut_pod_target).integrate!
@test_native_target.build_phases.count.should == 2
@test_native_target.build_phases.map(&:display_name).should == [
'[CP] Embed Pods Frameworks',
'[CP] Copy Pods Resources',
]
@test_native_target.build_phases[0].shell_script.should == "\"${PODS_ROOT}/Target Support Files/CoconutLib/CoconutLib-Unit-Tests-frameworks.sh\"\n"
@test_native_target.build_phases[1].shell_script.should == "\"${PODS_ROOT}/Target Support Files/CoconutLib/CoconutLib-Unit-Tests-resources.sh\"\n"
end
it 'integrates test native targets with frameworks and resources script phase input and output paths' do
framework_paths = { :name => 'Vendored.framework', :input_path => '${PODS_ROOT}/Vendored/Vendored.framework', :output_path => '${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Vendored.framework' }
resource_paths = ['${PODS_CONFIGURATION_BUILD_DIR}/TestResourceBundle.bundle']
@coconut_pod_target.stubs(:framework_paths).returns(framework_paths)
@coconut_pod_target.stubs(:resource_paths).returns(resource_paths)
PodTargetIntegrator.new(@coconut_pod_target).integrate!
@test_native_target.build_phases.count.should == 2
@test_native_target.build_phases.map(&:display_name).should == [
'[CP] Embed Pods Frameworks',
'[CP] Copy Pods Resources',
]
@test_native_target.build_phases[0].input_paths.should == [
'${PODS_ROOT}/Target Support Files/CoconutLib/CoconutLib-Unit-Tests-frameworks.sh',
'${PODS_ROOT}/Vendored/Vendored.framework',
]
@test_native_target.build_phases[0].output_paths.should == [
'${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Vendored.framework',
]
@test_native_target.build_phases[1].input_paths.should == [
'${PODS_ROOT}/Target Support Files/CoconutLib/CoconutLib-Unit-Tests-resources.sh',
'${PODS_CONFIGURATION_BUILD_DIR}/TestResourceBundle.bundle',
]
@test_native_target.build_phases[1].output_paths.should == [
'${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}',
]
end
it 'integrates test native target with shell script phases' do
@coconut_spec.test_specs.first.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"' }
PodTargetIntegrator.new(@coconut_pod_target).integrate!
@test_native_target.build_phases.count.should == 3
@test_native_target.build_phases[2].display_name.should == '[CP-User] Hello World'
@test_native_target.build_phases[2].shell_script.should == 'echo "Hello World"'
end
it 'integrates native target with shell script phases' do
@coconut_spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"' }
PodTargetIntegrator.new(@coconut_pod_target).integrate!
@native_target.build_phases.count.should == 1
@native_target.build_phases[0].display_name.should == '[CP-User] Hello World'
@native_target.build_phases[0].shell_script.should == 'echo "Hello World"'
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