Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend script phase DSL to support execution position #414

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/cocoapods-core/podfile/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,10 @@ def target(name, options = nil)
# @option options [Boolean] :show_env_vars_in_log
# whether this script phase should output the environment variables during execution.
#
# @option options [Symbol] :execution_position
# specifies the position of which this script phase should be executed. The currently supported values are:
# `:before_compile`, `:after_compile` and `:any` which is the default.
#
# @return [void]
#
def script_phase(options)
Expand Down
9 changes: 7 additions & 2 deletions lib/cocoapods-core/podfile/target_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -590,15 +590,15 @@ def store_podspec(options = nil)
# @param [Hash] options
# The options to use for this script phase. The required keys
# are: `:name`, `:script`, while the optional keys are:
# `:shell_path`, `:input_files`, `:output_files` and `:show_env_vars_in_log`.
# `:shell_path`, `:input_files`, `:output_files`, `:show_env_vars_in_log` and `:execution_position`.
#
# @return [void]
#
def store_script_phase(options)
option_keys = options.keys
unrecognized_keys = option_keys - Specification::ALL_SCRIPT_PHASE_KEYS
unless unrecognized_keys.empty?
raise StandardError, "Unrecognized options `#{unrecognized_keys}` in shell script `#{options}` within `#{name}` target. " \
raise StandardError, "Unrecognized options `#{unrecognized_keys}` in shell script `#{options[:name]}` within `#{name}` target. " \
"Available options are `#{Specification::ALL_SCRIPT_PHASE_KEYS}`."
end
missing_required_keys = Specification::SCRIPT_PHASE_REQUIRED_KEYS - option_keys
Expand All @@ -609,6 +609,11 @@ def store_script_phase(options)
if script_phases_hash.map { |script_phase_options| script_phase_options[:name] }.include?(options[:name])
raise StandardError, "Script phase with name `#{options[:name]}` name already present for target `#{name}`."
end
options[:execution_position] = :any unless options.key?(:execution_position)
unless Specification::EXECUTION_POSITION_KEYS.include?(options[:execution_position])
raise StandardError, "Invalid execution position value `#{options[:execution_position]}` in shell script `#{options[:name]}` within `#{name}` target. " \
"Available options are `#{Specification::EXECUTION_POSITION_KEYS}`."
end
script_phases_hash << options
end

Expand Down
11 changes: 9 additions & 2 deletions lib/cocoapods-core/specification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,15 @@ def prefix_header_file
# @return [Array<Hash{Symbol=>String}>] The script_phases value.
#
def script_phases
attributes_hash['script_phases'].map do |script_phase|
Specification.convert_keys_to_symbol(script_phase)
script_phases = attributes_hash['script_phases'] || []
script_phases.map do |script_phase|
phase = Specification.convert_keys_to_symbol(script_phase)
phase[:execution_position] = if phase.key?(:execution_position)
phase[:execution_position].to_sym
else
:any
end
phase
end
end

Expand Down
10 changes: 9 additions & 1 deletion lib/cocoapods-core/specification/consumer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,15 @@ def _prepare_prefix_header_contents(value)
def _prepare_script_phases(value)
if value
value.map do |script_phase|
Specification.convert_keys_to_symbol(script_phase) if script_phase.is_a?(Hash)
if script_phase.is_a?(Hash)
phase = Specification.convert_keys_to_symbol(script_phase)
phase[:execution_position] = if phase.key?(:execution_position)
phase[:execution_position].to_sym
else
:any
end
phase
end
end.compact
end
end
Expand Down
8 changes: 7 additions & 1 deletion lib/cocoapods-core/specification/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,9 @@ def dependency(*args)

SCRIPT_PHASE_REQUIRED_KEYS = [:name, :script].freeze

SCRIPT_PHASE_OPTIONAL_KEYS = [:shell_path, :input_files, :output_files, :show_env_vars_in_log].freeze
SCRIPT_PHASE_OPTIONAL_KEYS = [:shell_path, :input_files, :output_files, :show_env_vars_in_log, :execution_position].freeze

EXECUTION_POSITION_KEYS = [:before_compile, :after_compile, :any].freeze

ALL_SCRIPT_PHASE_KEYS = (SCRIPT_PHASE_REQUIRED_KEYS + SCRIPT_PHASE_OPTIONAL_KEYS).freeze

Expand All @@ -1295,6 +1297,10 @@ def dependency(*args)
#
# @example
#
# spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :before_compile }
#
# @example
#
# spec.script_phase = { :name => 'Hello World', :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby' } }
#
# @example
Expand Down
4 changes: 4 additions & 0 deletions lib/cocoapods-core/specification/linter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ def _validate_script_phases(s)
unless missing_required_keys.empty?
results.add_error('script_phases', "Missing required shell script phase options `#{missing_required_keys.join(', ')}` in script phase `#{script_phase[:name]}`.")
end
unless Specification::EXECUTION_POSITION_KEYS.include?(script_phase[:execution_position])
results.add_error('script_phases', "Invalid execution position value `#{script_phase[:execution_position]}` in shell script `#{script_phase[:name]}`. " \
"Available options are `#{Specification::EXECUTION_POSITION_KEYS}`.")
end
end
end

Expand Down
23 changes: 18 additions & 5 deletions spec/podfile/target_definition_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,14 @@ module Pod

it 'raises if script phase includes an unrecognized key' do
e = lambda { @parent.store_script_phase(:name => 'PhaseName', :unknown => 'Unknown') }.should.raise Podfile::StandardError
e.message.should == 'Unrecognized options `[:unknown]` in shell script `{:name=>"PhaseName", :unknown=>"Unknown"}` within `MyApp` target. ' \
'Available options are `[:name, :script, :shell_path, :input_files, :output_files, :show_env_vars_in_log]`.'
e.message.should == 'Unrecognized options `[:unknown]` in shell script `PhaseName` within `MyApp` target. ' \
'Available options are `[:name, :script, :shell_path, :input_files, :output_files, :show_env_vars_in_log, :execution_position]`.'
end

it 'raises if script phase includes an invalid execution position key' do
e = lambda { @parent.store_script_phase(:name => 'PhaseName', :script => 'echo "Hello World"', :execution_position => :unknown) }.should.raise Podfile::StandardError
e.message.should == 'Invalid execution position value `unknown` in shell script `PhaseName` within `MyApp` target. ' \
'Available options are `[:before_compile, :after_compile, :any]`.'
end

it 'raises if the same script phase name already exists' do
Expand All @@ -347,14 +353,21 @@ module Pod
it 'stores a script phase if requirements are provided' do
@parent.store_script_phase(:name => 'PhaseName', :script => 'echo "Hello World"')
@parent.script_phases.should == [
{ :name => 'PhaseName', :script => 'echo "Hello World"' },
{ :name => 'PhaseName', :script => 'echo "Hello World"', :execution_position => :any },
]
end

it 'stores a script phase with requirements and optional keys' do
@parent.store_script_phase(:name => 'PhaseName', :script => 'echo "Hello World"', :shell_path => :'/usr/bin/ruby')
@parent.store_script_phase(:name => 'PhaseName', :script => 'echo "Hello World"', :shell_path => '/usr/bin/ruby')
@parent.script_phases.should == [
{ :name => 'PhaseName', :script => 'echo "Hello World"', :shell_path => '/usr/bin/ruby', :execution_position => :any },
]
end

it 'stores a script phase with a specified execution position value' do
@parent.store_script_phase(:name => 'PhaseName', :script => 'echo "Hello World"', :shell_path => '/usr/bin/ruby', :execution_position => :before_compile)
@parent.script_phases.should == [
{ :name => 'PhaseName', :script => 'echo "Hello World"', :shell_path => :'/usr/bin/ruby' },
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was wrong :'/usr/bin/ruby' with the : added. Fixed.

{ :name => 'PhaseName', :script => 'echo "Hello World"', :shell_path => '/usr/bin/ruby', :execution_position => :before_compile },
]
end

Expand Down
25 changes: 17 additions & 8 deletions spec/specification/consumer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,12 @@ module Pod

it 'returns the script phases in correct format' do
@spec.script_phases = { :name => 'Hello World', :script => 'echo "Hello World"' }
@consumer.script_phases.should == [{ :name => 'Hello World', :script => 'echo "Hello World"' }]
@consumer.script_phases.should == [{ :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :any }]
end

it 'returns the script phases in correct format with optional options' do
@spec.script_phases = { :name => 'Hello Ruby World', :script => 'puts "Hello Ruby World"', :shell_path => 'usr/bin/ruby' }
@consumer.script_phases.should == [{ :name => 'Hello Ruby World', :script => 'puts "Hello Ruby World"', :shell_path => 'usr/bin/ruby' }]
@consumer.script_phases.should == [{ :name => 'Hello Ruby World', :script => 'puts "Hello Ruby World"', :shell_path => 'usr/bin/ruby', :execution_position => :any }]
end

it 'returns the script phases in correct format for multiple script phases' do
Expand All @@ -298,22 +298,31 @@ module Pod
{ :name => 'Hello Ruby World', :script => 'puts "Hello Ruby World"', :shell_path => 'usr/bin/ruby' },
]
@consumer.script_phases.should == [
{ :name => 'Hello World', :script => 'echo "Hello World"' },
{ :name => 'Hello Ruby World', :script => 'puts "Hello Ruby World"', :shell_path => 'usr/bin/ruby' },
{ :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :any },
{ :name => 'Hello Ruby World', :script => 'puts "Hello Ruby World"', :shell_path => 'usr/bin/ruby', :execution_position => :any },
]
end

it 'handles multi-platform script phases' do
@spec.ios.script_phases = { :name => 'Hello World iOS', :script => 'echo "Hello World iOS"' }
@consumer.script_phases.should == [{ :name => 'Hello World iOS', :script => 'echo "Hello World iOS"' }]
@consumer.script_phases.should == [{ :name => 'Hello World iOS', :script => 'echo "Hello World iOS"', :execution_position => :any }]
end

it 'returns both global and multi platform script phases' do
@spec.script_phases = { :name => 'Hello World', :script => 'echo "Hello World"' }
@spec.ios.script_phases = { :name => 'Hello World iOS', :script => 'echo "Hello World iOS"' }
@spec.ios.script_phases = { :name => 'Hello World iOS', :script => 'echo "Hello World iOS"', :execution_position => :any }
@consumer.script_phases.should == [
{ :name => 'Hello World', :script => 'echo "Hello World"' },
{ :name => 'Hello World iOS', :script => 'echo "Hello World iOS"' },
{ :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :any },
{ :name => 'Hello World iOS', :script => 'echo "Hello World iOS"', :execution_position => :any },
]
end

it 'retains the value set for execution position' do
@spec.script_phases = { :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :before_compile }
@spec.ios.script_phases = { :name => 'Hello World iOS', :script => 'echo "Hello World iOS"', :execution_position => :after_compile }
@consumer.script_phases.should == [
{ :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :before_compile },
{ :name => 'Hello World iOS', :script => 'echo "Hello World iOS"', :execution_position => :after_compile },
]
end

Expand Down
17 changes: 14 additions & 3 deletions spec/specification/json_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,13 @@ module Pod
it 'writes script phases' do
@spec.script_phases = [
{ :name => 'Hello World', :script => 'echo "Hello World"' },
{ :name => 'Hello World 2', :script => 'echo "Hello World 2"', :execution_position => :after_compile },
{ :name => 'Hello Ruby World', :script => 'puts "Hello Ruby World"', :shell_path => 'usr/bin/ruby' },
]
hash = @spec.to_hash
hash['script_phases'].should == [
{ :name => 'Hello World', :script => 'echo "Hello World"' },
{ :name => 'Hello World 2', :script => 'echo "Hello World 2"', :execution_position => :after_compile },
{ :name => 'Hello Ruby World', :script => 'puts "Hello Ruby World"', :shell_path => 'usr/bin/ruby' },
]
end
Expand Down Expand Up @@ -182,8 +184,8 @@ module Pod
result = Specification.from_hash(hash)
result.script_phases.count.should.equal 2
result.script_phases.should == [
{ :name => 'Hello World', :script => 'echo "Hello World"' },
{ :name => 'Hello Ruby World', :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby' },
{ :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :any },
{ :name => 'Hello Ruby World', :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby', :execution_position => :any },
]
end

Expand Down Expand Up @@ -221,7 +223,16 @@ module Pod
result = Specification.from_json(json)
result.script_phases.count.should.equal 1
result.script_phases.should == [
{ :name => 'Hello World', :script => 'echo "Hello World"' },
{ :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :any },
]
end

it 'loads script phase execution position from json' do
json = '{"script_phases": [{"name": "Hello World", "script": "echo \"Hello World\"", "execution_position": "before_compile"}]}'
result = Specification.from_json(json)
result.script_phases.count.should.equal 1
result.script_phases.should == [
{ :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :before_compile },
]
end

Expand Down
8 changes: 7 additions & 1 deletion spec/specification/linter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,13 @@ def result_should_include(*values)
it 'checks script phases include the required keys' do
@spec.script_phases = { :name => 'Hello World', :script => 'echo "Hello World"', :unknown => 'unknown' }
result_should_include('script_phases', 'Unrecognized options `[:unknown]` in script phase `Hello World`. ' \
'Available options are `[:name, :script, :shell_path, :input_files, :output_files, :show_env_vars_in_log]`.')
'Available options are `[:name, :script, :shell_path, :input_files, :output_files, :show_env_vars_in_log, :execution_position]`.')
end

it 'checks script phases include a valid execution position value' do
@spec.script_phases = { :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :unknown }
result_should_include('script_phases', 'Invalid execution position value `unknown` in shell script `Hello World`. ' \
'Available options are `[:before_compile, :after_compile, :any]`.')
end

#------------------#
Expand Down
17 changes: 17 additions & 0 deletions spec/specification_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,23 @@ module Pod
spec_1.to_s.should == 'No-name'
end

it 'returns any empty array without any script phases' do
spec = @spec.dup
spec.script_phases.should == []
end

it 'returns the script phases with keys converted to symbols' do
spec = @spec.dup
spec.script_phase = [{ 'name' => 'Hello World', 'script' => 'echo "Hello World"', 'execution_position' => :before_compile }]
spec.script_phases.should == [{ :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :before_compile }]
end

it 'returns the script phases with default execution position' do
spec = @spec.dup
spec.script_phase = [{ 'name' => 'Hello World', 'script' => 'echo "Hello World"' }]
spec.script_phases.should == [{ :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :any }]
end

describe '#validate_cocoapods_version' do
it 'passes when none is specified' do
spec_1 = Specification.new
Expand Down