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

Add podspec script_phases DSL #413

Merged
merged 1 commit into from
Oct 4, 2017
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 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
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thosed moved to specification.rb, no logical changes here.


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
40 changes: 29 additions & 11 deletions lib/cocoapods-core/specification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@ def prefix_header_file
attributes_hash['prefix_header_file']
end

# @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)
end
end

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

public
Expand All @@ -376,11 +384,6 @@ def local?
#
# @overload supported_on_platform?(symbolic_name, deployment_target)
#
# @param [Symbol] symbolic_name
Copy link
Contributor Author

Choose a reason for hiding this comment

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

unused params, deleted the doc

# 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 +465,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 +474,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 +498,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)
Copy link
Contributor Author

@dnkoutso dnkoutso Sep 29, 2017

Choose a reason for hiding this comment

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

for symmetry, decided to make this a class method as well as it maintains no state.

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)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@segiddins this is the primary fix that helps me accomplish this. Basically I want to the container to be an "Array" for this attribute since I want to preserve the order in which the script phases are added.

However, without this change the Hash becomes an array and then I lose the ability to recreate the hashes from the arrays.

This is the first attribute that requires an array of hashes so this is probably why I hit this.

[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) if script_phase.is_a?(Hash)
end.compact
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
Copy link
Member

Choose a reason for hiding this comment

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

spec linter should validate this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeap working on that as we speak.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tests added


# @!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' 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 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' }]
end

it 'returns the script phases in correct format for multiple script phases' 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 = { :name => 'Hello World', :script => 'echo "Hello World"' }
@spec.attributes_hash['script_phases'].should == { 'name' => 'Hello World', 'script' => 'echo "Hello World"' }
end

it 'allows to specify the script phases shipped with the Pod as a hash' do
@spec.script_phases = { :name => 'Hello Ruby World', :script => 'puts "Hello Ruby World"', :shell_path => 'usr/bin/ruby' }
@spec.attributes_hash['script_phases'].should == { 'name' => 'Hello Ruby World', 'script' => 'puts "Hello Ruby 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