Commit 77c1f0b1 authored by Fabio Pelosin's avatar Fabio Pelosin

[Specification] Clean up.

parent 1d7b4e76
require 'xcodeproj/config' require 'xcodeproj/config'
require 'colored'
module Pod module Pod
extend Config::Mixin extend Config::Mixin
...@@ -12,6 +11,8 @@ module Pod ...@@ -12,6 +11,8 @@ module Pod
autoload :Set, 'cocoapods/specification/set' autoload :Set, 'cocoapods/specification/set'
autoload :Statistics, 'cocoapods/specification/statistics' autoload :Statistics, 'cocoapods/specification/statistics'
### Initalization
# The file is expected to define and return a Pods::Specification. # The file is expected to define and return a Pods::Specification.
# If name is equals to nil it returns the top level Specification, # If name is equals to nil it returns the top level Specification,
# otherwise it returned the specification with the name that matches # otherwise it returned the specification with the name that matches
...@@ -24,97 +25,148 @@ module Pod ...@@ -24,97 +25,148 @@ module Pod
spec.subspec_by_name(subspec_name) spec.subspec_by_name(subspec_name)
end end
attr_accessor :parent
def initialize(parent = nil, name = nil) def initialize(parent = nil, name = nil)
@parent, @name = parent, name if parent @parent, @name = parent, name
post_initialize
yield self if block_given?
end
# TODO This is just to work around a MacRuby bug
def post_initialize
@define_for_platforms = [:osx, :ios] @define_for_platforms = [:osx, :ios]
@clean_paths, @subspecs = [], [] @clean_paths, @subspecs = [], []
@deployment_target = {} @deployment_target = {}
unless parent
@source = {:git => ''}
end
initialized_multiplatform_attributes initialized_multiplatform_attributes
# deprecated attributes yield self if block_given?
@source = {:git => ''}
end end
# Deprecated attributes ### Meta programming
# TODO: remove once master repo and fixtures are updated
def part_of_dependency=(value) # Creates a top level attribute reader. A lambda can
puts "[!] `part_of_dependency' is deprecated in #{name}".cyan # be passed to process the ivar before returning it
def self.top_attr_reader(attr, read_lambda = nil)
define_method(attr) do
ivar = instance_variable_get("@#{attr}")
@parent ? top_level_parent.send(attr) : ( read_lambda ? read_lambda.call(self, ivar) : ivar )
end
end end
def part_of=(value) # Creates a top level attribute writer. A lambda can
puts "[!] `part_of' is deprecated in #{name}".cyan # be passed to initalize the value
def self.top_attr_writer(attr, init_lambda = nil)
raise Informative "Can't set #{attr} for subspecs." if @parent
define_method("#{attr}=") do |value|
instance_variable_set("@#{attr}", init_lambda ? init_lambda.call(value) : value);
end
end end
# Normal attributes # Creates a top level attribute accessor. A lambda can
# be passed to initialize the value in the attribute writer.
def self.top_attr_accessor(attr, writer_labmda = nil)
top_attr_reader attr
top_attr_writer attr, writer_labmda
end
def name # Returns the value of the attribute for the active platform.
@parent ? "#{@parent.name}/#{@name}" : @name # In this way clients do not need to be aware of wich attributes
# are multiplatform.
def self.platform_attr_reader(attr)
define_method(attr) do
raise Informative, "#{self.inspect}##{attr} not activated for a platform before consumption." unless active_platform
instance_variable_get("@#{attr}")[active_platform]
end
end end
attr_writer :name # Returns the value of the attribute for the active platform.
# chained with the upstream specifications. The ivar must store
# the platform specific values as an array.
def self.pltf_chained_attr_reader(attr)
define_method(attr) do
raise Informative, "#{self.inspect}##{attr} not activated for a platform before consumption." unless active_platform
ivar_value = instance_variable_get("@#{attr}")[active_platform]
@parent ? @parent.send(attr) + ivar_value : ivar_value
end
end
def summary # Attribute writer that works in conjuction with the PlatformProxy.
@summary || ( @parent.summary if @parent ) def self.platform_attr_writer(attr, block = nil)
define_method("#{attr}=") do |value|
current = instance_variable_get("@#{attr}")
@define_for_platforms.each do |platform|
block ? current[platform] = block.call(value, current[platform]) : current[platform] = value
end
end
end end
attr_writer :summary # The PlatformProxy works in conjuction with Specification#_on_platform.
# It allows a syntax like `source_files[:ios] = file`
class PlatformProxy
def initialize(specification, platform)
@specification, @platform = specification, platform
end
#TODO: relocate %w{ source_files= resource= resources= xcconfig= framework= frameworks= library= libraries= compiler_flags= deployment_target= dependency }.each do |method|
define_method(method) do |args|
@specification._on_platform(@platform) do
@specification.send(method, args)
end
end
end
end
def platform def ios
@platform || ( @parent ? @parent.platform : Platform.new(nil) ) PlatformProxy.new(self, :ios)
end end
def platform=(platform) def osx
@platform = Platform.new(*platform) PlatformProxy.new(self, :osx)
end end
def available_platforms ### Deprecated attributes - TODO: remove once master repo and fixtures have been updated
platform.nil? ? @define_for_platforms.map { |platform| Platform.new(platform, @deployment_target[platform]) } : [ platform ]
attr_writer :part_of_dependency
attr_writer :part_of
top_attr_accessor :clean_paths, lambda { |patterns| pattern_list(patterns) }
alias_method :clean_path=, :clean_paths=
### Regular attributes
attr_accessor :parent
def name
@parent ? "#{@parent.name}/#{@name}" : @name
end end
attr_writer :main_subspec attr_writer :name
def main_subspec def main_subspec
return self unless @main_subspec return self unless @main_subspec
subspecs.find { |s| s.name == "#{self.name}/#{@main_subspec}" } subspecs.find { |s| s.name == "#{self.name}/#{@main_subspec}" }
end end
### Top level attributes. These attributes represent the unique features of pod and can't be specified by subspecs. attr_writer :main_subspec
# Attributes **without** multiple platform support ### Attributes that return the first value defined in the chain
# Creates a top level attribute reader. def summary
def self.top_attr_reader(attr, read_lambda = nil) @summary || ( @parent.summary if @parent )
define_method(attr) do
ivar = instance_variable_get("@#{attr}")
@parent ? top_level_parent.send(attr) : ( read_lambda ? read_lambda.call(self, ivar) : ivar )
end
end end
# Creates a top level attribute writer. A lambda can be passed to initialize the value. attr_writer :summary
def self.top_attr_writer(attr, init_lambda = nil)
raise Informative "Can't set #{attr} for subspecs" if @parent def platform
define_method("#{attr}=") do |value| @platform || ( @parent ? @parent.platform : Platform.new(nil) )
instance_variable_set("@#{attr}", init_lambda ? init_lambda.call(value) : value);
end
end end
# Creates a top level attribute accessor. A lambda can be passed to initialize the value in the attribute writer. def platform=(platform)
def self.top_attr_accessor(attr, writer_labmda = nil) @platform = Platform.new(*platform)
top_attr_reader attr
top_attr_writer attr, writer_labmda
end end
# If not platform is specified all the platforms are returned.
def available_platforms
platform.nil? ? @define_for_platforms.map { |platform| Platform.new(platform, deployment_target(platform)) } : [ platform ]
end
### Top level attributes. These attributes represent the unique features of pod and can't be specified by subspecs.
top_attr_accessor :defined_in_file top_attr_accessor :defined_in_file
top_attr_accessor :homepage top_attr_accessor :homepage
...@@ -123,22 +175,16 @@ module Pod ...@@ -123,22 +175,16 @@ module Pod
top_attr_accessor :requires_arc top_attr_accessor :requires_arc
top_attr_accessor :license, lambda { |l| ( l.kind_of? String ) ? { :type => l } : l } top_attr_accessor :license, lambda { |l| ( l.kind_of? String ) ? { :type => l } : l }
top_attr_accessor :version, lambda { |v| Version.new(v) } top_attr_accessor :version, lambda { |v| Version.new(v) }
top_attr_accessor :readme_file, lambda { |file| Pathname.new(file) }
top_attr_accessor :authors, lambda { |a| parse_authors(a) } top_attr_accessor :authors, lambda { |a| parse_authors(a) }
alias_method :author=, :authors= top_attr_accessor :prefix_header_contents #TODO: is this top level?
top_attr_accessor :prefix_header_file, lambda { |file| Pathname.new(file) } #TODO: is this top level?
top_attr_reader :description, lambda { |instance, ivar| ivar || instance.summary } top_attr_reader :description, lambda { |instance, ivar| ivar || instance.summary }
top_attr_writer :description top_attr_writer :description
top_attr_reader :header_dir, lambda {|instance, ivar| ivar || instance.pod_destroot_name } #TODO: is this top level?
top_attr_writer :header_dir, lambda {|dir| Pathname.new(dir) } #TODO: is this top level?
#TODO: are those top level? alias_method :author=, :authors=
top_attr_accessor :prefix_header_contents
top_attr_accessor :prefix_header_file, lambda { |file| Pathname.new(file) }
top_attr_reader :header_dir, lambda {|instance, ivar| ivar || instance.pod_destroot_name }
top_attr_writer :header_dir, lambda {|dir| Pathname.new(dir) }
#TODO: deprecate and clean all unused files
top_attr_accessor :clean_paths, lambda { |patterns| pattern_list(patterns) }
alias_method :clean_path=, :clean_paths=
def self.parse_authors(*names_and_email_addresses) def self.parse_authors(*names_and_email_addresses)
list = names_and_email_addresses.flatten list = names_and_email_addresses.flatten
...@@ -151,60 +197,10 @@ module Pod ...@@ -151,60 +197,10 @@ module Pod
### Attributes **with** multiple platform support ### Attributes **with** multiple platform support
class PlatformProxy
def initialize(specification, platform)
@specification, @platform = specification, platform
end
%w{ source_files= resource= resources= xcconfig= framework= frameworks= library= libraries= compiler_flags= deployment_target= dependency }.each do |method|
define_method(method) do |args|
@specification._on_platform(@platform) do
@specification.send(method, args)
end
end
end
end
def ios
PlatformProxy.new(self, :ios)
end
def osx
PlatformProxy.new(self, :osx)
end
# It returns the value of the attribute for the current platform. In this way clients do not need to be aware of wich
# attributes are multiplatform
def self.platform_attr_reader(attr)
define_method(attr) do
raise Informative, "#{self.inspect}##{attr} not activated for a platform before consumption." unless active_platform
instance_variable_get("@#{attr}")[active_platform]
end
end
# It returns the value of a pod merged with upstream. The optional lambda can specify how to merge the values
def self.pltf_chained_attr_reader(attr, merge_lambda = nil)
# TODO: chain the value with the upstream
define_method(attr) do
raise Informative, "#{self.inspect}##{attr} not activated for a platform before consumption." unless active_platform
instance_variable_get("@#{attr}")[active_platform]
end
end
def self.platform_attr_writer(attr, block = nil)
define_method("#{attr}=") do |value|
current = instance_variable_get("@#{attr}")
@define_for_platforms.each do |platform|
block ? current[platform] = block.call(value, current[platform]) : current[platform] = value
end
end
end
def initialized_multiplatform_attributes def initialized_multiplatform_attributes
%w[ source_files resources frameworks libraries dependencies compiler_flags].each do |attr| %w[ source_files resources frameworks libraries dependencies compiler_flags].each do |attr|
instance_variable_set( "@#{attr}", { :ios => [], :osx => [] } ) instance_variable_set( "@#{attr}", { :ios => [], :osx => [] } )
end end
@dependencies = { :ios => [], :osx => [] }
@xcconfig = { :ios => Xcodeproj::Config.new, :osx => Xcodeproj::Config.new } @xcconfig = { :ios => Xcodeproj::Config.new, :osx => Xcodeproj::Config.new }
end end
...@@ -264,12 +260,14 @@ module Pod ...@@ -264,12 +260,14 @@ module Pod
dep dep
end end
# External dependencies are inherited by subspecs
def external_dependencies def external_dependencies
result = @dependencies[active_platform] || [] result = @dependencies[active_platform] || []
result += parent.external_dependencies if parent result += parent.external_dependencies if parent
result result
end end
# A specification inherits all of its subspecs as dependencies
def dependencies def dependencies
raise Informative, "#{self.inspect}#dependencies not activated for a platform before consumption." unless active_platform raise Informative, "#{self.inspect}#dependencies not activated for a platform before consumption." unless active_platform
result = @dependencies[active_platform] + subspecs.map { |s| Dependency.new(s.name, version) } result = @dependencies[active_platform] + subspecs.map { |s| Dependency.new(s.name, version) }
...@@ -277,12 +275,6 @@ module Pod ...@@ -277,12 +275,6 @@ module Pod
result result
end end
# TODO: make top level?
def deployment_target=(version)
raise Informative, "The deployment target must be defined per platform like s.ios.deployment_target = '5.0'" unless @define_for_platforms.count == 1
@deployment_target[@define_for_platforms.first] = version
end
### Not attributes ### Not attributes
# @visibility private # @visibility private
...@@ -334,20 +326,22 @@ module Pod ...@@ -334,20 +326,22 @@ module Pod
remainder.empty? ? subspec : subspec.subspec_by_name(name) remainder.empty? ? subspec : subspec.subspec_by_name(name)
end end
# Returns if the specification is supported in a given platform # Returns whether the specification is supported in a given platform
def supports_platform?(plaform) def supports_platform?(plaform)
available_platforms.any? { |p| platform.supports? p } available_platforms.any? { |p| platform.supports? p }
end end
# Defines the active platform for comsumption of the specification and returns self # Defines the active platform for comsumption of the specification and
# returns self for method chainability.
# The active platform must the the same accross the chain so attributes
# that are inherited can be correctly resolved.
def activate_platform(platform) def activate_platform(platform)
platform = Platform.new(platform) if platform.is_a? Hash platform = Platform.new(platform) if platform.is_a? Hash
raise "[!] #{name} does not support platform".red unless supports_platform?(platform) raise "#{to_s} is not compatible with #{platform}." unless supports_platform?(platform)
top_level_parent.active_platform = platform.to_sym top_level_parent.active_platform = platform.to_sym
self self
end end
# The active platform needs to be the same accross the chain
top_attr_accessor :active_platform top_attr_accessor :active_platform
def local? def local?
...@@ -411,11 +405,9 @@ module Pod ...@@ -411,11 +405,9 @@ module Pod
end end
def dependency_by_top_level_spec_name(name) def dependency_by_top_level_spec_name(name)
# dependencies.each do |_, platform_deps|
dependencies.each do |dep| dependencies.each do |dep|
return dep if dep.top_level_spec_name == name return dep if dep.top_level_spec_name == name
end end
# end
end end
def to_s def to_s
...@@ -432,6 +424,17 @@ module Pod ...@@ -432,6 +424,17 @@ module Pod
name && name == other.name && name && name == other.name &&
version && version == other.version) version && version == other.version)
end end
private
def deployment_target=(version)
raise Informative, "The deployment target must be defined per platform like `s.ios.deployment_target = '5.0'`." unless @define_for_platforms.count == 1
deployment_target[@define_for_platforms.first] = version
end
def deployment_target(platform)
@deployment_target[platform] || ( @parent ? @parent.deployment_target(platform) : nil )
end
end end
Spec = Specification Spec = Specification
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