Add custom shell script integration from Podfile

parent fcb21f44
...@@ -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 custom shell script integration from Podfile
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#6820](https://github.com/CocoaPods/CocoaPods/pull/6820)
* Show full requirement trees when a version conflict is encountered during * Show full requirement trees when a version conflict is encountered during
dependency resolution. dependency resolution.
[Samuel Giddins](https://github.com/segiddins) [Samuel Giddins](https://github.com/segiddins)
......
...@@ -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: eb21ee8e85201c20aa5d58f0573948204237fd64 revision: bdec7d4861b2b1c669748300da07bf12c51cbf27
branch: master branch: master
specs: specs:
cocoapods-core (1.3.1) cocoapods-core (1.3.1)
......
...@@ -9,10 +9,15 @@ module Pod ...@@ -9,10 +9,15 @@ module Pod
class TargetIntegrator class TargetIntegrator
autoload :XCConfigIntegrator, 'cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator' autoload :XCConfigIntegrator, 'cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator'
# @return [String] the PACKAGE emoji to use as prefix for every build phase added to the user project # @return [String] the string to use as prefix for every build phase added to the user project
# #
BUILD_PHASE_PREFIX = '[CP] '.freeze BUILD_PHASE_PREFIX = '[CP] '.freeze
# @return [String] the string to use as prefix for every build phase declared by the user within a podfile
# or podspec.
#
USER_BUILD_PHASE_PREFIX = '[CP-User] '.freeze
# @return [String] the name of the check manifest phase # @return [String] the name of the check manifest phase
# #
CHECK_MANIFEST_PHASE_NAME = 'Check Pods Manifest.lock'.freeze CHECK_MANIFEST_PHASE_NAME = 'Check Pods Manifest.lock'.freeze
...@@ -67,7 +72,7 @@ module Pod ...@@ -67,7 +72,7 @@ module Pod
# @return [void] # @return [void]
# #
def add_embed_frameworks_script_phase_to_target(native_target, script_path, input_paths = [], output_paths = []) def add_embed_frameworks_script_phase_to_target(native_target, script_path, input_paths = [], output_paths = [])
phase = TargetIntegrator.create_or_update_build_phase(native_target, EMBED_FRAMEWORK_PHASE_NAME) phase = TargetIntegrator.create_or_update_build_phase(native_target, BUILD_PHASE_PREFIX + EMBED_FRAMEWORK_PHASE_NAME)
phase.shell_script = %("#{script_path}"\n) phase.shell_script = %("#{script_path}"\n)
unless input_paths.empty? unless input_paths.empty?
phase.input_paths = input_paths phase.input_paths = input_paths
...@@ -108,7 +113,7 @@ module Pod ...@@ -108,7 +113,7 @@ module Pod
# #
def add_copy_resources_script_phase_to_target(native_target, script_path, input_paths = [], output_paths = []) def add_copy_resources_script_phase_to_target(native_target, script_path, input_paths = [], output_paths = [])
phase_name = COPY_PODS_RESOURCES_PHASE_NAME phase_name = COPY_PODS_RESOURCES_PHASE_NAME
phase = TargetIntegrator.create_or_update_build_phase(native_target, phase_name) phase = TargetIntegrator.create_or_update_build_phase(native_target, BUILD_PHASE_PREFIX + phase_name)
phase.shell_script = %("#{script_path}"\n) phase.shell_script = %("#{script_path}"\n)
unless input_paths.empty? unless input_paths.empty?
phase.input_paths = input_paths phase.input_paths = input_paths
...@@ -132,12 +137,11 @@ module Pod ...@@ -132,12 +137,11 @@ module Pod
# @return [void] # @return [void]
# #
def create_or_update_build_phase(native_target, phase_name, phase_class = Xcodeproj::Project::Object::PBXShellScriptBuildPhase) def create_or_update_build_phase(native_target, phase_name, phase_class = Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
prefixed_phase_name = BUILD_PHASE_PREFIX + phase_name
build_phases = native_target.build_phases.grep(phase_class) build_phases = native_target.build_phases.grep(phase_class)
build_phases.find { |phase| phase.name && phase.name.end_with?(phase_name) }.tap { |p| p.name = prefixed_phase_name if p } || build_phases.find { |phase| phase.name && phase.name.end_with?(phase_name) }.tap { |p| p.name = phase_name if p } ||
native_target.project.new(phase_class).tap do |phase| native_target.project.new(phase_class).tap do |phase|
UI.message("Adding Build Phase '#{prefixed_phase_name}' to project.") do UI.message("Adding Build Phase '#{phase_name}' to project.") do
phase.name = prefixed_phase_name phase.name = phase_name
phase.show_env_vars_in_log = '0' phase.show_env_vars_in_log = '0'
native_target.build_phases << phase native_target.build_phases << phase
end end
...@@ -160,6 +164,7 @@ module Pod ...@@ -160,6 +164,7 @@ module Pod
remove_embed_frameworks_script_phase_from_embedded_targets remove_embed_frameworks_script_phase_from_embedded_targets
add_copy_resources_script_phase add_copy_resources_script_phase
add_check_manifest_lock_script_phase add_check_manifest_lock_script_phase
update_target_script_phases
end end
end end
...@@ -255,6 +260,46 @@ module Pod ...@@ -255,6 +260,46 @@ module Pod
end end
end end
# Updates all target script phases for the current target, including creating or updating, deleting
# and re-ordering.
#
# @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] }
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
end
end
# Adds a shell script build phase responsible for checking if the Pods # Adds a shell script build phase responsible for checking if the Pods
# locked in the Pods/Manifest.lock file are in sync with the Pods defined # locked in the Pods/Manifest.lock file are in sync with the Pods defined
# in the Podfile.lock. # in the Podfile.lock.
...@@ -267,7 +312,7 @@ module Pod ...@@ -267,7 +312,7 @@ module Pod
def add_check_manifest_lock_script_phase def add_check_manifest_lock_script_phase
phase_name = CHECK_MANIFEST_PHASE_NAME phase_name = CHECK_MANIFEST_PHASE_NAME
native_targets.each do |native_target| native_targets.each do |native_target|
phase = TargetIntegrator.create_or_update_build_phase(native_target, phase_name) phase = TargetIntegrator.create_or_update_build_phase(native_target, BUILD_PHASE_PREFIX + phase_name)
native_target.build_phases.unshift(phase).uniq! unless native_target.build_phases.first == phase native_target.build_phases.unshift(phase).uniq! unless native_target.build_phases.first == phase
phase.shell_script = <<-SH.strip_heredoc phase.shell_script = <<-SH.strip_heredoc
diff "${PODS_PODFILE_DIR_PATH}/Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null diff "${PODS_PODFILE_DIR_PATH}/Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
......
...@@ -29,12 +29,14 @@ module Pod ...@@ -29,12 +29,14 @@ module Pod
@target_integrator.stubs(method) @target_integrator.stubs(method)
end end
@phase_prefix = Installer::UserProjectIntegrator::TargetIntegrator::BUILD_PHASE_PREFIX @phase_prefix = Installer::UserProjectIntegrator::TargetIntegrator::BUILD_PHASE_PREFIX
@user_phase_prefix = Installer::UserProjectIntegrator::TargetIntegrator::USER_BUILD_PHASE_PREFIX
@embed_framework_phase_name = @phase_prefix + @embed_framework_phase_name = @phase_prefix +
Installer::UserProjectIntegrator::TargetIntegrator::EMBED_FRAMEWORK_PHASE_NAME Installer::UserProjectIntegrator::TargetIntegrator::EMBED_FRAMEWORK_PHASE_NAME
@copy_pods_resources_phase_name = @phase_prefix + @copy_pods_resources_phase_name = @phase_prefix +
Installer::UserProjectIntegrator::TargetIntegrator::COPY_PODS_RESOURCES_PHASE_NAME Installer::UserProjectIntegrator::TargetIntegrator::COPY_PODS_RESOURCES_PHASE_NAME
@check_manifest_phase_name = @phase_prefix + @check_manifest_phase_name = @phase_prefix +
Installer::UserProjectIntegrator::TargetIntegrator::CHECK_MANIFEST_PHASE_NAME Installer::UserProjectIntegrator::TargetIntegrator::CHECK_MANIFEST_PHASE_NAME
@user_script_phase_name = @user_phase_prefix + 'Custom Script'
end end
describe '#integrate!' do describe '#integrate!' do
...@@ -366,6 +368,105 @@ module Pod ...@@ -366,6 +368,105 @@ module Pod
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReleaseVendoredFramework.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReleaseVendoredFramework.framework
) )
end end
it 'adds a custom shell script phase' do
@pod_bundle.target_definition.stubs(:script_phases).returns([:name => 'Custom Script', :script => 'echo "Hello World"'])
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
phase = target.shell_script_build_phases.find { |bp| bp.name == @user_script_phase_name }
phase.name.should == '[CP-User] Custom Script'
phase.shell_script.should == 'echo "Hello World"'
end
it 'removes outdated custom shell script phases' do
@pod_bundle.target_definition.stubs(:script_phases).returns([:name => 'Custom Script', :script => 'echo "Hello World"'])
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
target.shell_script_build_phases.find { |bp| bp.name == @user_script_phase_name }.should.not.be.nil
@pod_bundle.target_definition.stubs(:script_phases).returns([])
@target_integrator.integrate!
target.shell_script_build_phases.find { |bp| bp.name == @user_script_phase_name }.should.be.nil
end
it 'moves custom shell scripts to their correct index' do
shell_script_one = { :name => 'Custom Script', :script => 'echo "Hello World"' }
shell_script_two = { :name => 'Custom Script 2', :script => 'echo "Hello Aliens"' }
@pod_bundle.target_definition.stubs(:script_phases).returns([shell_script_one, shell_script_two])
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
target.shell_script_build_phases.map(&:name).should == [
'[CP] Check Pods Manifest.lock',
'[CP] Embed Pods Frameworks',
'[CP] Copy Pods Resources',
'[CP-User] Custom Script',
'[CP-User] Custom Script 2',
]
shell_script_one_uuid = target.shell_script_build_phases[3].uuid
shell_script_two_uuid = target.shell_script_build_phases[4].uuid
@pod_bundle.target_definition.stubs(:script_phases).returns([shell_script_two, shell_script_one])
@target_integrator.integrate!
target.shell_script_build_phases.map(&:name).should == [
'[CP] Check Pods Manifest.lock',
'[CP] Embed Pods Frameworks',
'[CP] Copy Pods Resources',
'[CP-User] Custom Script 2',
'[CP-User] Custom Script',
]
target.shell_script_build_phases[3].uuid.should == shell_script_two_uuid
target.shell_script_build_phases[4].uuid.should == shell_script_one_uuid
end
it 'adds, removes and moves custom shell script phases' do
shell_script_one = { :name => 'Custom Script', :script => 'echo "Hello World"' }
shell_script_two = { :name => 'Custom Script 2', :script => 'echo "Hello Aliens"' }
shell_script_three = { :name => 'Custom Script 3', :script => 'echo "Hello Universe"' }
shell_script_four = { :name => 'Custom Script 4', :script => 'echo "Ran out of Hellos"' }
@pod_bundle.target_definition.stubs(:script_phases).returns([shell_script_one, shell_script_two, shell_script_three])
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
target.shell_script_build_phases.map(&:name).should == [
'[CP] Check Pods Manifest.lock',
'[CP] Embed Pods Frameworks',
'[CP] Copy Pods Resources',
'[CP-User] Custom Script',
'[CP-User] Custom Script 2',
'[CP-User] Custom Script 3',
]
@pod_bundle.target_definition.stubs(:script_phases).returns([shell_script_two, shell_script_four])
@target_integrator.integrate!
target.shell_script_build_phases.map(&:name).should == [
'[CP] Check Pods Manifest.lock',
'[CP] Embed Pods Frameworks',
'[CP] Copy Pods Resources',
'[CP-User] Custom Script 2',
'[CP-User] Custom Script 4',
]
end
it 'does not touch non cocoapods shell script phases' do
@pod_bundle.target_definition.stubs(:script_phases).returns([:name => 'Custom Script', :script => 'echo "Hello World"'])
target = @target_integrator.send(:native_targets).first
target.new_shell_script_build_phase('User Script Phase 1')
target.new_shell_script_build_phase('User Script Phase 2')
@target_integrator.integrate!
target.shell_script_build_phases.map(&:name).should == [
'[CP] Check Pods Manifest.lock',
'User Script Phase 1',
'User Script Phase 2',
'[CP] Embed Pods Frameworks',
'[CP] Copy Pods Resources',
'[CP-User] Custom Script',
]
@pod_bundle.target_definition.stubs(:script_phases).returns([])
@target_integrator.integrate!
target.shell_script_build_phases.map(&:name).should == [
'[CP] Check Pods Manifest.lock',
'User Script Phase 1',
'User Script Phase 2',
'[CP] Embed Pods Frameworks',
'[CP] Copy Pods Resources',
]
end
end end
describe 'Private helpers' do describe 'Private helpers' do
......
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