Skip to content

Commit

Permalink
Add podspec script_phases DSL
Browse files Browse the repository at this point in the history
  • Loading branch information
dnkoutso committed Sep 29, 2017
1 parent 283ac03 commit 30e9ac4
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 46 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

##### Enhancements

* Add podspec `script_phases` DSL
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[#413](https://github.com/CocoaPods/Core/pull/413)

* Update commments/docs to indicate prefix_header=false will skip pch generation
[Paul Beusterien](https://github.com/paulb777)
[#412](https://github.com/CocoaPods/Core/pull/412)
Expand Down
12 changes: 3 additions & 9 deletions lib/cocoapods-core/podfile/target_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -585,12 +585,6 @@ def store_podspec(options = nil)

#--------------------------------------#

SCRIPT_PHASE_REQUIRED_KEYS = [:name, :script].freeze

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

ALL_SCRIPT_PHASE_KEYS = (SCRIPT_PHASE_REQUIRED_KEYS + SCRIPT_PHASE_OPTIONAL_KEYS).freeze

# Stores the script phase to add for this target definition.
#
# @param [Hash] options
Expand All @@ -602,12 +596,12 @@ def store_podspec(options = nil)
#
def store_script_phase(options)
option_keys = options.keys
unrecognized_keys = option_keys - ALL_SCRIPT_PHASE_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. " \
"Available options are `#{ALL_SCRIPT_PHASE_KEYS}`."
"Available options are `#{Specification::ALL_SCRIPT_PHASE_KEYS}`."
end
missing_required_keys = SCRIPT_PHASE_REQUIRED_KEYS - option_keys
missing_required_keys = Specification::SCRIPT_PHASE_REQUIRED_KEYS - option_keys
unless missing_required_keys.empty?
raise StandardError, "Missing required shell script phase options `#{missing_required_keys.join(', ')}`"
end
Expand Down
38 changes: 27 additions & 11 deletions lib/cocoapods-core/specification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ def prefix_header_file
attributes_hash['prefix_header_file']
end

# @return [Array<Hash{Symbol=>String}>] The script_phases value.
#
def script_phases
Specification.convert_keys_to_symbol(attributes_hash['script_phases'])
end

#-------------------------------------------------------------------------#

public
Expand All @@ -376,11 +382,6 @@ def local?
#
# @overload supported_on_platform?(symbolic_name, deployment_target)
#
# @param [Symbol] symbolic_name
# the name of the platform which is checked for support.
#
# @param [String] deployment_target
# the deployment target which is checked for support.
#
def supported_on_platform?(*platform)
platform = Platform.new(*platform)
Expand Down Expand Up @@ -462,7 +463,7 @@ def platform_hash
# @param [Object] value
# the value to store.
#
# @param [Symbol] platform.
# @param [Symbol] platform_name
# If provided the attribute is stored only for the given platform.
#
# @note If the provides value is Hash the keys are converted to a string.
Expand All @@ -471,7 +472,7 @@ def platform_hash
#
def store_attribute(name, value, platform_name = nil)
name = name.to_s
value = convert_keys_to_string(value) if value.is_a?(Hash)
value = Specification.convert_keys_to_string(value) if value.is_a?(Hash)
value = value.strip_heredoc.strip if value.respond_to?(:strip_heredoc)
if platform_name
platform_name = platform_name.to_s
Expand All @@ -495,25 +496,40 @@ def store_attribute(name, value, platform_name = nil)
end
end

private

# Converts the keys of the given hash to a string.
#
# @param [Object] value
# the value that needs to be stripped from the Symbols.
#
# @return [Hash] the hash with the strings instead of the keys.
#
def convert_keys_to_string(value)
def self.convert_keys_to_string(value)
return unless value
result = {}
value.each do |key, subvalue|
subvalue = convert_keys_to_string(subvalue) if subvalue.is_a?(Hash)
subvalue = Specification.convert_keys_to_string(subvalue) if subvalue.is_a?(Hash)
result[key.to_s] = subvalue
end
result
end

# Converts the keys of the given hash to a string.
#
# @param [Object] value
# the value that needs to be stripped from the Symbols.
#
# @return [Hash] the hash with the strings instead of the keys.
#
def self.convert_keys_to_symbol(value)
return unless value
result = {}
value.each do |key, subvalue|
subvalue = Specification.convert_keys_to_symbol(subvalue) if subvalue.is_a?(Hash)
result[key.to_sym] = subvalue
end
result
end

#-------------------------------------------------------------------------#

public
Expand Down
26 changes: 25 additions & 1 deletion lib/cocoapods-core/specification/consumer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ def user_target_xcconfig
#
spec_attr_accessor :resource_bundles

# @return [Array<Hash{Symbol=>String}>] An array of hashes where each hash
# represents a script phase.
#
spec_attr_accessor :script_phases

# @return [Array<String>] A hash where the key represents the
# paths of the resources to copy and the values the paths of
# the resources that should be copied.
Expand Down Expand Up @@ -324,7 +329,11 @@ def merge_values(attr, existing_value, new_value)
#
def prepare_value(attr, value)
if attr.container == Array
value = [*value].compact
value = if value.is_a?(Hash)
[value]
else
[*value].compact
end
end

hook_name = prepare_hook_name(attr)
Expand Down Expand Up @@ -362,6 +371,21 @@ def _prepare_prefix_header_contents(value)
end
end

# Converts the array of hashes (script phases) where keys are strings into symbols.
#
# @param [Array<Hash{String=>String}>] value.
# The value of the attribute as specified by the user.
#
# @return [Array<Hash{Symbol=>String}>] the script phases array with symbols for each hash instead of strings.
#
def _prepare_script_phases(value)
if value
value.map do |script_phase|
Specification.convert_keys_to_symbol(script_phase)
end
end
end

# Ensures that the file patterns of the resource bundles are contained in
# an array.
#
Expand Down
42 changes: 41 additions & 1 deletion lib/cocoapods-core/specification/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1134,7 +1134,7 @@ def dependency(*args)

#------------------#

# @!method resource_bundles=(*frameworks)
# @!method resource_bundles=(*resource_bundles)
#
# This attribute allows to define the name and the file of the resource
# bundles which should be built for the Pod. They are specified as a
Expand Down Expand Up @@ -1272,6 +1272,46 @@ def dependency(*args)
attribute :module_map,
:root_only => true

#------------------#

SCRIPT_PHASE_REQUIRED_KEYS = [:name, :script].freeze

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

ALL_SCRIPT_PHASE_KEYS = (SCRIPT_PHASE_REQUIRED_KEYS + SCRIPT_PHASE_OPTIONAL_KEYS).freeze

# @!method script_phases=(*script_phases)
#
# This attribute allows to define a script phase to execute as part of compilation of the Pod.
# Unlike a prepare command, script phases execute as part of `xcodebuild` they can also utilize all environment
# variables that are set during compilation.
#
# A Pod can provide multiple script phases to execute and they will be added in the order they were
# declared.
#
# @example
#
# spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"' }
#
# @example
#
# spec.script_phase = { :name => 'Hello World', :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby' } }
#
# @example
#
# spec.script_phases = [
# { :name => 'Hello World', :script => 'echo "Hello World"' },
# { :name => 'Hello Ruby World', :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby' } },
# ]
#
# @param [Array<Hash{Symbol=>String}>] script_phases
# An array of hashes where each hash represents a single script phase.
#
attribute :script_phases,
:types => [Hash],
:container => Array,
:singularize => true

#-----------------------------------------------------------------------#

# @!group Subspecs
Expand Down
17 changes: 17 additions & 0 deletions lib/cocoapods-core/specification/linter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,23 @@ def _validate_test_type(t)
end
end

# Performs validations related to the `script_phases` attribute.
#
def _validate_script_phases(s)
s.each do |script_phase|
keys = script_phase.keys
unrecognized_keys = keys - Specification::ALL_SCRIPT_PHASE_KEYS
unless unrecognized_keys.empty?
results.add_error('script_phases', "Unrecognized options `#{unrecognized_keys}` in script phase `#{script_phase[:name]}`. " \
"Available options are `#{Specification::ALL_SCRIPT_PHASE_KEYS}`.")
end
missing_required_keys = Specification::SCRIPT_PHASE_REQUIRED_KEYS - keys
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
end
end

# Performs validations related to github sources.
#
def perform_github_source_checks(s)
Expand Down
25 changes: 2 additions & 23 deletions lib/cocoapods-core/specification/root_attribute_accessors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def license
if license.is_a?(String)
{ :type => license }
elsif license.is_a?(Hash)
license = convert_keys_to_symbol(license)
license = Specification.convert_keys_to_symbol(license)
license[:text] = license[:text].strip_heredoc if license[:text]
license
else
Expand All @@ -110,7 +110,7 @@ def homepage
# should be retrieved.
#
def source
convert_keys_to_symbol(attributes_hash['source'])
Specification.convert_keys_to_symbol(attributes_hash['source'])
end

# @return [String] A short description of the Pod.
Expand Down Expand Up @@ -187,27 +187,6 @@ def module_map
end

#---------------------------------------------------------------------#

private

# Converts the keys of the given hash to a string.
#
# @param [Object] value
# the value that needs to be stripped from the Symbols.
#
# @return [Hash] the hash with the strings instead of the keys.
#
def convert_keys_to_symbol(value)
return unless value
result = {}
value.each do |key, subvalue|
subvalue = convert_keys_to_symbol(subvalue) if subvalue.is_a?(Hash)
result[key.to_sym] = subvalue
end
result
end

#---------------------------------------------------------------------#
end
end
end
Expand Down
41 changes: 41 additions & 0 deletions spec/specification/consumer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,47 @@ module Pod
test_consumer = Specification::Consumer.new(test_spec, :ios)
test_consumer.requires_app_host?.should.be.true
end

#------------------#

it 'returns the script phases in correct format when using a string for value' do
@spec.script_phases = { :name => 'Hello World', :script => 'echo "Hello World"' }
@consumer.script_phases.should == [{ :name => 'Hello World', :script => 'echo "Hello World"' }]
end

it 'returns the script phases in correct format when using a hash for value' 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' }]
end

it 'returns the script phases in correct format when using both string and hash for values' do
@spec.script_phases = [
{ :name => 'Hello World', :script => 'echo "Hello World"' },
{ :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' },
]
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"' }]
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"' }
@consumer.script_phases.should == [
{ :name => 'Hello World', :script => 'echo "Hello World"' },
{ :name => 'Hello World iOS', :script => 'echo "Hello World iOS"' },
]
end

it 'returns the empty hash if no script phases have been specified' do
@consumer.script_phases.should == []
end
end

#-------------------------------------------------------------------------#
Expand Down
12 changes: 11 additions & 1 deletion spec/specification/dsl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,16 @@ module Pod
@spec.module_map = 'module.modulemap'
@spec.attributes_hash['module_map'].should == 'module.modulemap'
end

it 'allows to specify the script phases shipped with the Pod' do
@spec.script_phases = { 'HelloWorld' => 'echo "Hello World"' }
@spec.attributes_hash['script_phases'].should == { 'HelloWorld' => 'echo "Hello World"' }
end

it 'allows to specify the script phases shipped with the Pod as a hash' do
@spec.script_phases = { 'HelloWorld' => { :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby' } }
@spec.attributes_hash['script_phases'].should == { 'HelloWorld' => { 'script' => 'puts "Hello World"', 'shell_path' => '/usr/bin/ruby' } }
end
end

#-----------------------------------------------------------------------------#
Expand Down Expand Up @@ -464,7 +474,7 @@ module Pod
end
singularized.map { |attr| attr.name.to_s }.sort.should == %w(
authors compiler_flags default_subspecs frameworks libraries
preserve_paths resource_bundles resources screenshots
preserve_paths resource_bundles resources screenshots script_phases
vendored_frameworks vendored_libraries weak_frameworks
)
end
Expand Down
Loading

0 comments on commit 30e9ac4

Please sign in to comment.