Commit d7faa63f authored by Fabio Pelosin's avatar Fabio Pelosin

[Core Extraction] Initial support for CocoaPods-Core.

parent 482c9589
......@@ -3,6 +3,7 @@ source "http://rubygems.org"
gemspec
group :development do
gem "cocoapods-core", :git => "git://github.com/CocoaPods/Core.git"
gem "xcodeproj", :git => "git://github.com/CocoaPods/Xcodeproj.git"
gem "mocha", "~> 0.11.4"
gem "bacon"
......
GIT
remote: git://github.com/CocoaPods/Core.git
revision: 0b028885b07cd7681a889523748f12dbdcf3a6c3
specs:
cocoapods-core (0.16.0.rc2)
activesupport (~> 3.2.6)
faraday (~> 0.8.1)
octokit (~> 1.7)
GIT
remote: git://github.com/CocoaPods/Xcodeproj.git
revision: 97a5a444ecfadf52e42afdbbf054f078f7c2cc5d
......@@ -21,6 +30,7 @@ PATH
cocoapods (0.16.0.rc2)
activesupport (~> 3.2.6)
claide (~> 0.1)
cocoapods-core
colored (~> 1.2)
escape (~> 0.0.4)
faraday (~> 0.8.1)
......@@ -90,6 +100,7 @@ DEPENDENCIES
awesome_print
bacon
cocoapods!
cocoapods-core!
github-markup
kicker!
mocha (~> 0.11.4)
......
......@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
s.executables = %w{ pod }
s.require_paths = %w{ lib }
s.add_runtime_dependency 'cocoapods-core'
s.add_runtime_dependency 'claide', '~> 0.1'
s.add_runtime_dependency 'xcodeproj', '~> 0.4.0.rc1'
......
......@@ -25,26 +25,16 @@ module Pod
end
autoload :Command, 'cocoapods/command'
autoload :Config, 'cocoapods/config'
autoload :Dependency, 'cocoapods/dependency'
autoload :Downloader, 'cocoapods/downloader'
autoload :Executable, 'cocoapods/executable'
autoload :Installer, 'cocoapods/installer'
autoload :LocalPod, 'cocoapods/local_pod'
autoload :Lockfile, 'cocoapods/lockfile'
autoload :Platform, 'cocoapods/platform'
autoload :Podfile, 'cocoapods/podfile'
autoload :Project, 'cocoapods/project'
autoload :Resolver, 'cocoapods/resolver'
autoload :Sandbox, 'cocoapods/sandbox'
autoload :Source, 'cocoapods/source'
autoload :Spec, 'cocoapods/specification'
autoload :Specification, 'cocoapods/specification'
autoload :UI, 'cocoapods/user_interface'
autoload :Version, 'cocoapods/version_class'
autoload :Pathname, 'pathname'
autoload :FileList, 'cocoapods/file_list'
module Generator
autoload :BridgeSupport, 'cocoapods/generator/bridge_support'
......@@ -54,9 +44,15 @@ module Pod
autoload :Plist, 'cocoapods/generator/acknowledgements/plist'
autoload :Markdown, 'cocoapods/generator/acknowledgements/markdown'
autoload :DummySource, 'cocoapods/generator/dummy_source'
autoload :PrefixHeader, 'cocoapods/generator/prefix_header'
end
end
require 'cocoapods/file_list'
require 'cocoapods-core'
require 'cocoapods/config'
require 'cocoapods/source'
if ENV['COCOA_PODS_ENV'] == 'development'
require 'awesome_print'
end
require 'cocoapods/open_uri'
module Pod
class Dependency < Gem::Dependency
attr_reader :head
alias :head? :head
attr_accessor :specification, :external_source
def initialize(*name_and_version_requirements, &block)
if name_and_version_requirements.empty? && block
@inline_podspec = true
@specification = Specification.new(&block)
super(@specification.name, @specification.version)
elsif !name_and_version_requirements.empty? && block.nil?
version = name_and_version_requirements.last
if name_and_version_requirements.last.is_a?(Hash)
@external_source = ExternalSources.from_params(name_and_version_requirements[0].split('/').first, name_and_version_requirements.pop)
elsif version.is_a?(Symbol) && version == :head || version.is_a?(Version) && version.head?
name_and_version_requirements.pop
@head = true
end
super(*name_and_version_requirements)
if head? && !latest_version?
raise Informative, "A `:head' dependency may not specify version requirements."
end
else
raise Informative, "A dependency needs either a name and version requirements, " \
"a source hash, or a block which defines a podspec."
end
end
def latest_version?
versions = @version_requirements.requirements.map(&:last)
versions == [Gem::Version.new('0')]
end
def ==(other)
super && (head? == other.head?) && (@specification ? @specification == other.specification : @external_source == other.external_source)
end
def subspec_dependency?
@name.include?('/')
end
def inline?
@inline_podspec
end
def external?
!@external_source.nil?
end
# In case this is a dependency for a subspec, e.g. 'RestKit/Networking',
# this returns 'RestKit', which is what the Pod::Source needs to know to
# retrieve the correct Set from disk.
def top_level_spec_name
subspec_dependency? ? @name.split('/').first : @name
end
# Returns a copy of the dependency, but with the name of the top level
# spec. This is used by Pod::Specification::Set to merge dependencies on
# the complete set, irrespective of what spec in the set wil be used.
def to_top_level_spec_dependency
dep = dup
dep.name = top_level_spec_name
dep
end
def to_s
version = ''
if external?
version << @external_source.description
elsif inline?
version << 'defined in Podfile'
elsif head?
version << 'HEAD'
elsif @version_requirements != Gem::Requirement.default
version << @version_requirements.to_s
end
result = @name.dup
result << " (#{version})" unless version.empty?
result
end
def specification_from_sandbox(sandbox, platform)
@external_source.specification_from_sandbox(sandbox, platform)
end
def match_version?(version)
match?(name, version) && (version.head? == head?)
end
# Taken from RubyGems 1.3.7
unless public_method_defined?(:match?)
def match?(spec_name, spec_version)
pattern = name
if Regexp === pattern
return false unless pattern =~ spec_name
else
return false unless pattern == spec_name
end
return true if requirement.to_s == ">= 0"
requirement.satisfied_by? Gem::Version.new(spec_version)
end
end
# Taken from a newer version of RubyGems
unless public_method_defined?(:merge)
def merge other
unless name == other.name then
raise ArgumentError,
"#{self} and #{other} have different names"
end
default = Gem::Requirement.default
self_req = self.requirement
other_req = other.requirement
return self.class.new name, self_req if other_req == default
return self.class.new name, other_req if self_req == default
self.class.new name, self_req.as_list.concat(other_req.as_list)
end
end
module ExternalSources
def self.from_params(name, params)
return unless name && params
if params.key?(:git)
GitSource.new(name, params)
elsif params.key?(:podspec)
PodspecSource.new(name, params)
elsif params.key?(:local)
LocalSource.new(name, params)
else
raise Informative, "Unknown external source parameters for #{name}: #{params}"
end
end
class AbstractExternalSource
include Config::Mixin
attr_reader :name, :params
def initialize(name, params)
@name, @params = name, params
end
def specification_from_sandbox(sandbox, platform)
specification_from_local(sandbox, platform) || specification_from_external(sandbox, platform)
end
def specification_from_local(sandbox, platform)
if local_pod = sandbox.installed_pod_named(name, platform)
local_pod.top_specification
end
end
def specification_from_external(sandbox, platform)
podspec = copy_external_source_into_sandbox(sandbox, platform)
spec = specification_from_local(sandbox, platform)
raise Informative, "No podspec found for `#{name}' in #{description}" unless spec
spec
end
# Can store from a pathname or a string
#
def store_podspec(sandbox, podspec)
output_path = sandbox.root + "Local Podspecs/#{name}.podspec"
output_path.dirname.mkpath
if podspec.is_a?(String)
raise Informative, "No podspec found for `#{name}' in #{description}" unless podspec.include?('Spec.new')
output_path.open('w') { |f| f.puts(podspec) }
else
raise Informative, "No podspec found for `#{name}' in #{description}" unless podspec.exist?
FileUtils.copy(podspec, output_path)
end
end
def ==(other)
return if other.nil?
name == other.name && params == other.params
end
end
class GitSource < AbstractExternalSource
def copy_external_source_into_sandbox(sandbox, platform)
UI.info("->".green + " Pre-downloading: '#{name}'") do
target = sandbox.root + name
target.rmtree if target.exist?
downloader = Downloader.for_target(sandbox.root + name, @params)
downloader.download
store_podspec(sandbox, target + "#{name}.podspec")
if local_pod = sandbox.installed_pod_named(name, platform)
local_pod.downloaded = true
end
end
end
def description
"from `#{@params[:git]}'".tap do |description|
description << ", commit `#{@params[:commit]}'" if @params[:commit]
description << ", branch `#{@params[:branch]}'" if @params[:branch]
description << ", tag `#{@params[:tag]}'" if @params[:tag]
end
end
end
# can be http, file, etc
class PodspecSource < AbstractExternalSource
def copy_external_source_into_sandbox(sandbox, _)
UI.info("->".green + " Fetching podspec for `#{name}' from: #{@params[:podspec]}") do
path = @params[:podspec]
path = Pathname.new(path).expand_path if path.start_with?("~")
open(path) { |io| store_podspec(sandbox, io.read) }
end
end
def description
"from `#{@params[:podspec]}'"
end
end
class LocalSource < AbstractExternalSource
def pod_spec_path
path = Pathname.new(@params[:local]).expand_path
path += "#{name}.podspec"# unless path.to_s.include?("#{name}.podspec")
raise Informative, "No podspec found for `#{name}' in `#{@params[:local]}'" unless path.exist?
path
end
def copy_external_source_into_sandbox(sandbox, _)
store_podspec(sandbox, pod_spec_path)
end
def specification_from_local(sandbox, platform)
specification_from_external(sandbox, platform)
end
def specification_from_external(sandbox, platform)
copy_external_source_into_sandbox(sandbox, platform)
spec = Specification.from_file(pod_spec_path)
spec.source = @params
spec
end
def description
"from `#{@params[:local]}'"
end
end
end
end
end
require 'digest'
module Pod
class Lockfile
# @return [Lockfile] Returns the Lockfile saved in path.
# Returns {nil} If the file can't be loaded.
#
def self.from_file(path)
return nil unless path.exist?
begin
hash = YAML.load(File.open(path))
rescue Exception => e
raise Informative, "Podfile.lock syntax error: #{e.inspect}"
end
lockfile = Lockfile.new(hash)
lockfile.defined_in_file = path
lockfile
end
# @return [Lockfile] Generates a lockfile from a {Podfile} and the
# list of {Specifications} that were installed.
#
def self.generate(podfile, specs)
Lockfile.new(generate_hash_from_podfile(podfile, specs))
end
# @return [String] The file where this Lockfile is defined.
#
attr_accessor :defined_in_file
# @return [String] The hash used to initialize the Lockfile.
#
attr_reader :to_hash
# @param [Hash] hash A Hash representation of a Lockfile.
#
def initialize(hash)
@to_hash = hash
end
# @return [Array<String, Hash{String => Array[String]}>] The pods installed
# and their dependencies.
#
def pods
@pods ||= to_hash['PODS'] || []
end
# @return [Array<Dependency>] The Podfile dependencies used during the last
# install.
#
def dependencies
@dependencies ||= to_hash['DEPENDENCIES'].map { |dep| dependency_from_string(dep) } || []
end
# @return [Hash{String => Hash}] A hash where the name of the pods are
# the keys and the values are the parameters of an {AbstractExternalSource}
# of the dependency that required the pod.
#
def external_sources
@external_sources ||= to_hash["EXTERNAL SOURCES"] || {}
end
# @return [Array<String>] The names of the installed Pods.
#
def pods_names
@pods_names ||= pods.map do |pod|
pod = pod.keys.first unless pod.is_a?(String)
name_and_version_for_pod(pod)[0]
end
end
# @return [Hash{String => Version}] A Hash containing the name
# of the installed Pods as the keys and their corresponding {Version}
# as the values.
#
def pods_versions
unless @pods_versions
@pods_versions = {}
pods.each do |pod|
pod = pod.keys.first unless pod.is_a?(String)
name, version = name_and_version_for_pod(pod)
@pods_versions[name] = version
end
end
@pods_versions
end
# @return [Dependency] A dependency that describes the exact installed version
# of a Pod.
#
def dependency_for_installed_pod_named(name)
version = pods_versions[name]
raise Informative, "Attempt to lock a Pod without an known version." unless version
dependency = Dependency.new(name, version)
if external_source = external_sources[name]
dependency.external_source = Dependency::ExternalSources.from_params(dependency.name, external_source)
end
dependency
end
# @param [String] The string that describes a {Specification} generated
# from {Specification#to_s}.
#
# @example Strings examples
# "libPusher"
# "libPusher (1.0)"
# "libPusher (HEAD based on 1.0)"
# "RestKit/JSON"
#
# @return [String, Version] The name and the version of a
# pod.
#
def name_and_version_for_pod(string)
match_data = string.match(/(\S*) \((.*)\)/)
name = match_data[1]
vers = Version.from_string(match_data[2])
[name, vers]
end
# @param [String] The string that describes a {Dependency} generated
# from {Dependency#to_s}.
#
# @example Strings examples
# "libPusher"
# "libPusher (= 1.0)"
# "libPusher (~> 1.0.1)"
# "libPusher (> 1.0, < 2.0)"
# "libPusher (HEAD)"
# "libPusher (from `www.example.com')"
# "libPusher (defined in Podfile)"
# "RestKit/JSON"
#
# @return [Dependency] The dependency described by the string.
#
def dependency_from_string(string)
match_data = string.match(/(\S*)( (.*))?/)
name = match_data[1]
version = match_data[2]
version = version.gsub(/[()]/,'') if version
case version
when nil
Dependency.new(name)
when /defined in Podfile/
# @TODO: store the whole spec?, the version?
Dependency.new(name)
when /from `(.*)'/
external_source_info = external_sources[name]
Dependency.new(name, external_source_info)
when /HEAD/
# @TODO: find a way to serialize from the Downloader the information
# necessary to restore a head version.
Dependency.new(name, :head)
else
Dependency.new(name, version)
end
end
# Analyzes the {Lockfile} and detects any changes applied to the {Podfile}
# since the last installation.
#
# For each Pod, it detects one state among the following:
#
# - added: Pods that weren't present in the Podfile.
# - changed: Pods that were present in the Podfile but changed:
# - Pods whose version is not compatible anymore with Podfile,
# - Pods that changed their head or external options.
# - removed: Pods that were removed form the Podfile.
# - unchanged: Pods that are still compatible with Podfile.
#
# @TODO: detect changes for inline dependencies?
#
# @return [Hash{Symbol=>Array[Strings]}] A hash where pods are grouped
# by the state in which they are.
#
def detect_changes_with_podfile(podfile)
previous_podfile_deps = dependencies.map(&:name)
user_installed_pods = pods_names.reject { |name| !previous_podfile_deps.include?(name) }
deps_to_install = podfile.dependencies.dup
result = {}
result[:added] = []
result[:changed] = []
result[:removed] = []
result[:unchanged] = []
user_installed_pods.each do |pod_name|
dependency = deps_to_install.find { |d| d.name == pod_name }
deps_to_install.delete(dependency)
version = pods_versions[pod_name]
external_source = Dependency::ExternalSources.from_params(pod_name, external_sources[pod_name])
if dependency.nil?
result[:removed] << pod_name
elsif !dependency.match_version?(version) || dependency.external_source != external_source
result[:changed] << pod_name
else
result[:unchanged] << pod_name
end
end
deps_to_install.each do |dependency|
result[:added] << dependency.name
end
result
end
# @return [void] Writes the Lockfile to {#path}.
#
def write_to_disk(path)
path.dirname.mkpath unless path.dirname.exist?
File.open(path, 'w') {|f| f.write(to_yaml) }
defined_in_file = path
end
# @return [String] A string useful to represent the Lockfile in a message
# presented to the user.
#
def to_s
"Podfile.lock"
end
# @return [String] The YAML representation of the Lockfile, used for
# serialization.
#
def to_yaml
to_hash.to_yaml.gsub(/^--- ?\n/,"").gsub(/^([A-Z])/,"\n\\1")
end
# @return [Hash] The Hash representation of the Lockfile generated from
# a given Podfile and the list of resolved Specifications.
#
def self.generate_hash_from_podfile(podfile, specs)
hash = {}
# Get list of [name, dependencies] pairs.
pod_and_deps = specs.map do |spec|
[spec.to_s, spec.dependencies.map(&:to_s).sort]
end.uniq
# Merge dependencies of iOS and OS X version of the same pod.
tmp = {}
pod_and_deps.each do |name, deps|
if tmp[name]
tmp[name].concat(deps).uniq!
else
tmp[name] = deps
end
end
pod_and_deps = tmp.sort_by(&:first).map do |name, deps|
deps.empty? ? name : { name => deps }
end
hash["PODS"] = pod_and_deps
hash["DEPENDENCIES"] = podfile.dependencies.map{ |d| d.to_s }.sort
external_sources = {}
deps = podfile.dependencies.select(&:external?).sort{ |d, other| d.name <=> other.name}
deps.each{ |d| external_sources[d.name] = d.external_source.params }
hash["EXTERNAL SOURCES"] = external_sources unless external_sources.empty?
checksums = {}
specs.select { |spec| !spec.defined_in_file.nil? }.each do |spec|
checksum = Digest::SHA1.hexdigest(File.read(spec.defined_in_file))
checksum = checksum.encode('UTF-8') if checksum.respond_to?(:encode)
checksums[spec.name] = checksum
end
hash["SPEC CHECKSUMS"] = checksums unless checksums.empty?
hash["COCOAPODS"] = VERSION
hash
end
end
end
module Pod
# A platform describes an SDK name and deployment target.
#
class Platform
# @return [Platform] Convenience method to initialize an iOS platform.
#
def self.ios
new :ios
end
# @return [Platform] Convenience method to initialize an OS X platform.
#
def self.osx
new :osx
end
# Constructs a platform from either another platform or by
# specifying the symbolic name and optionally the deployment target.
#
# @overload initialize(name, deployment_target)
# @param [Symbol] name The name of platform.
# @param [String, Version] deployment_target The optional deployment.
# If not provided a default value according to the platform name will
# be assigned.
#
# @note that if the name is not specified a default deployment
# target will not be assigned.
#
# @example
#
# Platform.new(:ios)
# Platform.new(:ios, '4.3')
#
# @overload initialize(platform)
# @param [Platform] platform Another {Platform}.
#
# @example
#
# platform = Platform.new(:ios)
# Platform.new(platform)
#
def initialize(input, target = nil)
if input.is_a? Platform
@symbolic_name = input.name
@deployment_target = input.deployment_target
else
@symbolic_name = input
target = target[:deployment_target] if target.is_a?(Hash)
@deployment_target = Version.create(target)
end
end
# @return [Symbol] The name of the SDK represented by the platform.
#
def name
@symbolic_name
end
# @return [Version] The deployment target of the platform.
#
attr_reader :deployment_target
# @param [Platform, Symbol] other The other platform to check.
#
# @note If a symbol is passed the comparison does not take into account
# the deployment target.
#
# @return [Boolean] Whether two platforms are the equivalent.
#
def ==(other)
if other.is_a?(Symbol)
@symbolic_name == other
else
(name == other.name) && (deployment_target == other.deployment_target)
end
end
# In the context of operating system SDKs, a platform supports another
# one if they have the same name and the other platform has a minor or
# equal deployment target.
#
# @return Whether the platform supports another platform.
#
def supports?(other)
other = Platform.new(other)
if other.deployment_target && deployment_target
(other.name == name) && (other.deployment_target <= deployment_target)
else
other.name == name
end
end
# @return [String] A string representation including the deployment target.
#
def to_s
case @symbolic_name
when :ios
s = 'iOS'
when :osx
s = 'OS X'
end
s << " #{deployment_target}" if deployment_target
s
end
# @return [Symbol] A Symbol representation of the name.
#
def to_sym
name
end
# @return Whether the platform requires legacy architectures for iOS.
#
def requires_legacy_ios_archs?
(name == :ios) && deployment_target && (deployment_target < Version.new("4.3"))
end
end
end
module Pod
class Podfile
class Informative < ::Pod::Informative
def podfile_line
@podfile_line ||= self.backtrace.find {|t| t =~ /Podfile/}
end
def message
if podfile_line
super + " (#{podfile_line})\n".red
else
super
end
end
end
class UserProject
include Config::Mixin
DEFAULT_BUILD_CONFIGURATIONS = { 'Debug' => :debug, 'Release' => :release }.freeze
def initialize(path = nil, build_configurations = {})
self.path = path if path
@build_configurations = build_configurations.merge(DEFAULT_BUILD_CONFIGURATIONS)
end
def path=(path)
path = path.to_s
@path = Pathname.new(File.extname(path) == '.xcodeproj' ? path : "#{path}.xcodeproj")
@path = config.project_root + @path unless @path.absolute?
@path
end
def path
if @path
@path
else
xcodeprojs = Pathname.glob(config.project_root + '*.xcodeproj')
if xcodeprojs.size == 1
@path = xcodeprojs.first
end
end
end
def project
Xcodeproj::Project.new(path) if path && path.exist?
end
def build_configurations
if project
project.build_configurations.map(&:name).inject({}) do |hash, name|
hash[name] = :release; hash
end.merge(@build_configurations)
else
@build_configurations
end
end
end
class TargetDefinition
include Config::Mixin
attr_reader :name, :target_dependencies
attr_accessor :user_project, :link_with, :platform, :parent, :exclusive, :inhibit_all_warnings
def initialize(name, options = {})
@name, @target_dependencies = name, []
@parent, @exclusive = options.values_at(:parent, :exclusive)
end
# A target is automatically `exclusive` if the `platform` does not match
# the parent's `platform`.
def exclusive
if @exclusive.nil?
if @platform.nil?
false
else
@parent.platform != @platform
end
else
@exclusive
end
end
alias_method :exclusive?, :exclusive
def user_project
@user_project || @parent.user_project
end
def link_with=(targets)
@link_with = targets.is_a?(Array) ? targets : [targets]
end
def platform
@platform || (@parent.platform if @parent)
end
def inhibit_all_warnings
@inhibit_all_warnings.nil? ? (@parent.inhibit_all_warnings? if @parent) : @inhibit_all_warnings
end
alias_method :inhibit_all_warnings?, :inhibit_all_warnings
def label
if name == :default
"Pods"
elsif exclusive?
"Pods-#{name}"
else
"#{@parent.label}-#{name}"
end
end
def acknowledgements_path
config.project_pods_root + "#{label}-Acknowledgements"
end
# Returns a path, which is relative to the project_root, relative to the
# `$(SRCROOT)` of the user's project.
def relative_to_srcroot(path)
if user_project.path.nil?
# TODO this is not in the right place
raise Informative, "[!] Unable to find an Xcode project to integrate".red if config.integrate_targets
path
else
(config.project_root + path).relative_path_from(user_project.path.dirname)
end
end
def relative_pods_root
"${SRCROOT}/#{relative_to_srcroot "Pods"}"
end
def lib_name
"lib#{label}.a"
end
def xcconfig_name
"#{label}.xcconfig"
end
def xcconfig_relative_path
relative_to_srcroot("Pods/#{xcconfig_name}").to_s
end
attr_accessor :xcconfig
def copy_resources_script_name
"#{label}-resources.sh"
end
def copy_resources_script_relative_path
"${SRCROOT}/#{relative_to_srcroot("Pods/#{copy_resources_script_name}")}"
end
def prefix_header_name
"#{label}-prefix.pch"
end
def bridge_support_name
"#{label}.bridgesupport"
end
# Returns *all* dependencies of this target, not only the target specific
# ones in `target_dependencies`.
def dependencies
@target_dependencies + (exclusive? ? [] : @parent.dependencies)
end
def empty?
target_dependencies.empty?
end
end
def self.from_file(path)
podfile = Podfile.new do
string = File.open(path, 'r:utf-8') { |f| f.read }
# TODO: work around for Rubinius incomplete encoding in 1.9 mode
string.encode!('UTF-8') if string.respond_to?(:encoding) && string.encoding.name != "UTF-8"
begin
eval(string, nil, path.to_s)
rescue Exception => e
raise Informative, "Podfile syntax error: #{e.inspect}"
end
end
podfile.defined_in_file = path
podfile.validate!
podfile
end
include Config::Mixin
def initialize(&block)
@target_definition = TargetDefinition.new(:default, :exclusive => true)
@target_definition.user_project = UserProject.new
@target_definitions = { :default => @target_definition }
instance_eval(&block)
end
# Specifies the platform for which a static library should be build.
# This can be either `:osx` for Mac OS X applications, or `:ios` for iOS
# applications.
#
# @param [Symbol] name The name of platform.
# @param [String, Version] target The optional deployment.
# If not provided a default value according to the platform name will
# be assigned.
#
# @example
#
# platform :ios, "4.0"
# platform :ios
#
# @note If the deployment target requires it (< 4.3), armv6 will be added
# to ARCHS.
#
def platform(name, target = nil)
# Support for deprecated options parameter
target = target[:deployment_target] if target.is_a?(Hash)
unless target
case name
when :ios
target = '4.3'
when :osx
target = '10.6'
else
raise ::Pod::Podfile::Informative, "Unsupported platform: platform must be one of [:ios, :osx]"
end
end
@target_definition.platform = Platform.new(name, target)
end
# Specifies the Xcode workspace that should contain all the projects.
#
# If no explicit Xcode workspace is specified and only **one** project exists
# in the same directory as the Podfile, then the name of that project is used
# as the workspace’s name.
#
# @example
#
# workspace 'MyWorkspace'
#
def workspace(path = nil)
if path
@workspace = config.project_root + (File.extname(path) == '.xcworkspace' ? path : "#{path}.xcworkspace")
elsif @workspace
@workspace
else
projects = @target_definitions.map { |_, td| td.user_project.path }.uniq
if projects.size == 1 && (xcodeproj = @target_definitions[:default].user_project.path)
config.project_root + "#{xcodeproj.basename('.xcodeproj')}.xcworkspace"
end
end
end
# Specifies the Xcode project that contains the target that the Pods library
# should be linked with.
#
# If no explicit project is specified, it will use the Xcode project of the
# parent target. If none of the target definitions specify an explicit project
# and there is only **one** project in the same directory as the Podfile then
# that project will be used.
#
# @example
#
# # Look for target to link with in an Xcode project called ‘MyProject.xcodeproj’.
# xcodeproj 'MyProject'
#
# target :test do
# # This Pods library links with a target in another project.
# xcodeproj 'TestProject'
# end
#
def xcodeproj(path, build_configurations = {})
@target_definition.user_project = UserProject.new(path, build_configurations)
end
# Specifies the target(s) in the user’s project that this Pods library
# should be linked in.
#
# If no explicit target is specified, then the Pods target will be linked
# with the first target in your project. So if you only have one target you
# do not need to specify the target to link with.
#
# @example
#
# # Link with a target called ‘MyApp’ (in the user's project).
# link_with 'MyApp'
#
# # Link with the targets in the user’s project called ‘MyApp’ and ‘MyOtherApp’.
# link_with ['MyApp', 'MyOtherApp']
#
def link_with(targets)
@target_definition.link_with = targets
end
# Inhibits **all** warnings from the Pods library.
#
# When used, this is applied to all targets inheriting from the current one.
def inhibit_all_warnings!
@target_definition.inhibit_all_warnings = true
end
# Specifies a dependency of the project.
#
# A dependency requirement is defined by the name of the Pod and _optionally_
# a list of version requirements.
#
#
# When starting out with a project it is likely that you will want to use the
# latest version of a Pod. If this is the case, simply omit the version
# requirements.
#
# pod 'SSZipArchive'
#
#
# Later on in the project you may want to freeze to a specific version of a
# Pod, in which case you can specify that version number.
#
# pod 'Objection', '0.9'
#
#
# Besides no version, or a specific one, it is also possible to use operators:
#
# * `> 0.1` Any version higher than 0.1
# * `>= 0.1` Version 0.1 and any higher version
# * `< 0.1` Any version lower than 0.1
# * `<= 0.1` Version 0.1 and any lower version
# * `~> 0.1.2` Version 0.1.2 and the versions upto 0.2, not including 0.2
#
# A list of version requirements can be specified for even more fine
# grained control.
#
#
# For more information, regarding versioning policy, see:
#
# * http://semver.org
# * http://docs.rubygems.org/read/chapter/7
#
#
# Finally, instead of a version, you can specify the `:head` flag. This will
# use the pod’s latest version spec version, but force the download of the
# ‘bleeding edge’ version. Use this with caution, as the spec might not be
# compatible anymore.
#
#
# ## Dependency on a library, outside those available in a spec repo.
#
# ### From a podspec in the root of a library repo.
#
# Sometimes you may want to use the bleeding edge version of a Pod. Or a
# specific revision. If this is the case, you can specify that with your
# pod declaration.
#
#
# To use the `master` branch of the repo:
#
# pod 'TTTFormatterKit', :git => 'https://github.com/gowalla/AFNetworking.git'
#
#
# Or specify a commit:
#
# pod 'TTTFormatterKit', :git => 'https://github.com/gowalla/AFNetworking.git', :commit => '082f8319af'
#
#
# It is important to note, though, that this means that the version will
# have to satisfy any other dependencies on the Pod by other Pods.
#
#
# The `podspec` file is expected to be in the root of the repo, if this
# library does not have a `podspec` file in its repo yet, you will have to
# use one of the approaches outlined in the sections below.
#
#
# ### From a podspec outside a spec repo, for a library without podspec.
#
# If a podspec is available from another source outside of the library’s
# repo. Consider, for instance, a podpsec available via HTTP:
#
# pod 'JSONKit', :podspec => 'https://raw.github.com/gist/1346394/1d26570f68ca27377a27430c65841a0880395d72/JSONKit.podspec'
#
#
# ### For a library without any available podspec
#
# Finally, if no living soul has created a podspec, for the library you want
# to use, yet, you will have to specify the library yourself.
#
#
# When you omit arguments and pass a block to `pod`, an instance of
# Pod::Specification is yielded to the block. This is the same class which
# is normally used to specify a Pod.
#
# ```
# pod do |spec|
# spec.name = 'JSONKit'
# spec.version = '1.4'
# spec.source = { :git => 'https://github.com/johnezang/JSONKit.git', :tag => 'v1.4' }
# spec.source_files = 'JSONKit.*'
# end
# ```
#
#
# For more info on the definition of a Pod::Specification see:
# https://github.com/CocoaPods/CocoaPods/wiki/A-pod-specification
def pod(*name_and_version_requirements, &block)
@target_definition.target_dependencies << Dependency.new(*name_and_version_requirements, &block)
end
# Use the dependencies of a podspec file.
#
def podspec(options = nil)
if options && path = options[:path]
path = File.extname(path) == '.podspec' ? path : "#{path}.podspec"
file = Pathname.new(File.expand_path(path))
elsif options && name = options[:name]
name = File.extname(name) == '.podspec' ? name : "#{name}.podspec"
file = config.project_root + name
else
file = config.project_root.glob('*.podspec').first
end
spec = Specification.from_file(file)
spec.activate_platform(@target_definition.platform)
deps = spec.recursive_subspecs.push(spec).map {|specification| specification.external_dependencies }
deps = deps.flatten.uniq
@target_definition.target_dependencies.concat(deps)
end
def dependency(*name_and_version_requirements, &block)
warn "[DEPRECATED] `dependency' is deprecated (use `pod')"
pod(*name_and_version_requirements, &block)
end
# Specifies that a BridgeSupport metadata document should be generated from
# the headers of all installed Pods.
#
# This is for scripting languages such as MacRuby, Nu, and JSCocoa, which use
# it to bridge types, functions, etc better.
def generate_bridge_support!
@generate_bridge_support = true
end
# Defines a new static library target and scopes dependencies defined from
# the given block. The target will by default include the dependencies
# defined outside of the block, unless the `:exclusive => true` option is
# given.
#
# Consider the following Podfile:
#
# pod 'ASIHTTPRequest'
#
# target :debug do
# pod 'SSZipArchive'
# end
#
# target :test, :exclusive => true do
# pod 'JSONKit'
# end
#
# This Podfile defines three targets. The first one is the `:default` target,
# which produces the `libPods.a` file. The second and third are the `:debug`
# and `:test` ones, which produce the `libPods-debug.a` and `libPods-test.a`
# files.
#
# The `:default` target has only one dependency (ASIHTTPRequest), whereas the
# `:debug` target has two (ASIHTTPRequest, SSZipArchive). The `:test` target,
# however, is an exclusive target which means it will only have one
# dependency (JSONKit).
def target(name, options = {})
parent = @target_definition
options[:parent] = parent
@target_definitions[name] = @target_definition = TargetDefinition.new(name, options)
yield
ensure
@target_definition = parent
end
# This hook allows you to make any changes to the downloaded Pods and to
# their targets before they are installed.
#
# pre_install do |installer|
# # Do something fancy!
# end
#
def pre_install(&block)
@pre_install_callback = block
end
# This hook allows you to make any last changes to the generated Xcode project
# before it is written to disk, or any other tasks you might want to perform.
#
# For instance, say you'd want to customize the `OTHER_LDFLAGS` of all targets:
#
# post_install do |installer|
# installer.project.targets.each do |target|
# target.build_configurations.each do |config|
# config.build_settings['GCC_ENABLE_OBJC_GC'] = 'supported'
# end
# end
# end
def post_install(&block)
@post_install_callback = block
end
# Specifies that the -fobjc-arc flag should be added to the OTHER_LD_FLAGS.
#
# This is used as a workaround for a compiler bug with non-ARC projects.
# (see https://github.com/CocoaPods/CocoaPods/issues/142)
#
# This was originally done automatically but libtool as of Xcode 4.3.2 no
# longer seems to support the -fobjc-arc flag. Therefore it now has to be
# enabled explicitly using this method.
#
# This may be removed in a future release.
def set_arc_compatibility_flag!
@set_arc_compatibility_flag = true
end
# Not attributes
def podfile?
true
end
attr_accessor :defined_in_file
attr_reader :target_definitions
def dependencies
@target_definitions.values.map(&:target_dependencies).flatten.uniq
end
def dependency_by_top_level_spec_name(name)
dependencies.find { |d| d.top_level_spec_name == name }
end
def generate_bridge_support?
@generate_bridge_support
end
def set_arc_compatibility_flag?
@set_arc_compatibility_flag
end
def user_build_configurations
configs_array = @target_definitions.values.map { |td| td.user_project.build_configurations }
configs_array.inject({}) { |hash, config| hash.merge(config) }
end
def pre_install!(installer)
@pre_install_callback.call(installer) if @pre_install_callback
end
def post_install!(installer)
@post_install_callback.call(installer) if @post_install_callback
end
def validate!
end
def to_s
"Podfile"
end
end
end
require 'xcodeproj/config'
require 'active_support/core_ext/string/strip.rb'
module Pod
extend Config::Mixin
def self._eval_podspec(path)
string = File.open(path, 'r:utf-8') { |f| f.read }
# TODO: work around for Rubinius incomplete encoding in 1.9 mode
string.encode!('UTF-8') if string.respond_to?(:encoding) && string.encoding.name != "UTF-8"
eval(string, nil, path.to_s)
end
class Specification
autoload :Set, 'cocoapods/specification/set'
autoload :Statistics, 'cocoapods/specification/statistics'
### Initalization
# The file is expected to define and return a Pods::Specification.
# If name is equals to nil it returns the top level Specification,
# otherwise it returned the specification with the name that matches
def self.from_file(path, subspec_name = nil)
unless path.exist?
raise Informative, "No podspec exists at path `#{path}'."
end
spec = ::Pod._eval_podspec(path)
spec.defined_in_file = path
spec.subspec_by_name(subspec_name)
end
def initialize(parent = nil, name = nil)
@parent, @name = parent, name
@define_for_platforms = [:osx, :ios]
@clean_paths, @subspecs = [], []
@deployment_target = {}
unless parent
@source = {:git => ''}
end
# multi-platform attributes
%w[ source_files
exclude_patterns
public_header_files
resources
preserve_paths
exclude_header_search_paths
frameworks
weak_frameworks
libraries
dependencies
compiler_flags ].each do |attr|
instance_variable_set( "@#{attr}", { :ios => [], :osx => [] } )
end
@xcconfig = { :ios => Xcodeproj::Config.new, :osx => Xcodeproj::Config.new }
@header_dir = { :ios => nil, :osx => nil }
@requires_arc = { :ios => nil, :osx => nil }
@header_mappings_dir = { :ios => nil, :osx => nil }
yield self if block_given?
end
### Meta programming
# Creates a top level attribute reader. A lambda can
# 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
# Creates a top level attribute writer. A lambda can
# be passed to initalize the value
def self.top_attr_writer(attr, init_lambda = nil)
define_method("#{attr}=") do |value|
raise Informative, "#{self.inspect} Can't set `#{attr}' for subspecs." if @parent
instance_variable_set("@#{attr}", init_lambda ? init_lambda.call(value) : value);
end
end
# 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
# 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
active_plaform_check
ivar_value = instance_variable_get("@#{attr}")[active_platform]
@parent ? @parent.send(attr) + ivar_value : ( ivar_value )
end
end
# Returns the first value defined of the attribute traversing the chain
# upwards.
#
def self.pltf_first_defined_attr_reader(attr)
define_method(attr) do
active_plaform_check
ivar_value = instance_variable_get("@#{attr}")[active_platform]
ivar_value.nil? ? (@parent.send(attr) if @parent) : ivar_value
end
end
def active_plaform_check
raise Informative, "#{self.inspect} not activated for a platform before consumption." unless active_platform
end
# Attribute writer that works in conjuction with the PlatformProxy.
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 self.pltf_chained_attr_accessor(attr, block = nil)
pltf_chained_attr_reader(attr)
platform_attr_writer(attr, block)
end
# The PlatformProxy works in conjuction with Specification#_on_platform.
# It allows a syntax like `spec.ios.source_files = file`
class PlatformProxy
def initialize(specification, platform)
@specification, @platform = specification, platform
end
%w{ source_files=
exclude_patterns=
public_header_files=
resource=
resources=
preserve_paths=
preserve_path=
xcconfig=
framework=
frameworks=
weak_framework=
weak_frameworks=
library=
libraries=
compiler_flags=
deployment_target=
header_dir=
requires_arc
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
### Deprecated attributes - TODO: remove once master repo and fixtures have been updated
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
attr_accessor :preferred_dependency
def name
@parent ? "#{@parent.name}/#{@name}" : @name
end
attr_writer :name
# @return [String] The name of the pod.
#
def pod_name
top_level_parent.name
end
### Attributes that return the first value defined in the chain
def platform
@platform || ( @parent ? @parent.platform : nil )
end
def platform=(platform)
@platform = Platform.new(*platform)
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 :source
top_attr_accessor :homepage
top_attr_accessor :summary
top_attr_accessor :documentation
top_attr_accessor :version, lambda { |v| Version.new(v) }
top_attr_reader :description, lambda { |instance, ivar| ivar || instance.summary }
top_attr_writer :description, lambda { |d| d.strip_heredoc }
# @!method license
#
# @abstract
# The license of the pod.
#
# @example
# s.license = 'MIT'
# s.license = { :type => 'MIT', :file => 'license.txt', :text => 'Permission is granted to...' }
#
top_attr_accessor :license, lambda { |license|
license = ( license.kind_of? String ) ? { :type => license } : license
license[:text] = license[:text].strip_heredoc if license[:text]
license
}
# @!method authors
#
# @abstract
# The list of the authors (with email) of the pod.
#
top_attr_accessor :authors, lambda { |*names_and_email_addresses|
list = names_and_email_addresses.flatten
unless list.first.is_a?(Hash)
authors = list.last.is_a?(Hash) ? list.pop : {}
list.each { |name| authors[name] = nil }
end
authors || list.first
}
alias_method :author=, :authors=
### Attributes **with** multiple platform support
# @todo allow for subspecs?
#
top_attr_accessor :prefix_header_file, lambda { |file| Pathname.new(file) }
top_attr_accessor :prefix_header_contents
pltf_chained_attr_accessor :source_files, lambda {|value, current| pattern_list(value) }
pltf_chained_attr_accessor :exclude_patterns, lambda {|value, current| pattern_list(value) }
pltf_chained_attr_accessor :public_header_files, lambda {|value, current| pattern_list(value) }
pltf_chained_attr_accessor :resources, lambda {|value, current| pattern_list(value) }
pltf_chained_attr_accessor :preserve_paths, lambda {|value, current| pattern_list(value) } # Paths that should not be cleaned
pltf_chained_attr_accessor :exclude_header_search_paths, lambda {|value, current| pattern_list(value) } # Headers to be excluded from being added to search paths (RestKit)
pltf_chained_attr_accessor :frameworks, lambda {|value, current| (current << value).flatten }
pltf_chained_attr_accessor :weak_frameworks, lambda {|value, current| (current << value).flatten }
pltf_chained_attr_accessor :libraries, lambda {|value, current| (current << value).flatten }
alias_method :resource=, :resources=
alias_method :preserve_path=, :preserve_paths=
alias_method :framework=, :frameworks=
alias_method :weak_framework=, :weak_frameworks=
alias_method :library=, :libraries=
# @!method requires_arc=
#
# @abstract Wether the `-fobjc-arc' flag should be added to the compiler
# flags.
#
# @param [Bool] Wether the source files require ARC.
#
platform_attr_writer :requires_arc
pltf_first_defined_attr_reader :requires_arc
# @!method header_dir=
#
# @abstract The directory where to name space the headers files of
# the specification.
#
# @param [String] The headers directory.
#
platform_attr_writer :header_dir, lambda { |dir, _| Pathname.new(dir) }
pltf_first_defined_attr_reader :header_dir
# If not provided the headers files are flattened
#
platform_attr_writer :header_mappings_dir, lambda { |file, _| Pathname.new(file) }
pltf_first_defined_attr_reader :header_mappings_dir
# @!method xcconfig=
#
platform_attr_writer :xcconfig, lambda {|value, current| current.tap { |c| c.merge!(value) } }
def xcconfig
result = raw_xconfig.dup
result.libraries.merge(libraries)
result.frameworks.merge(frameworks)
result.weak_frameworks.merge(weak_frameworks)
result
end
def raw_xconfig
@parent ? @parent.raw_xconfig.merge(@xcconfig[active_platform]) : @xcconfig[active_platform]
end
def recursive_compiler_flags
@parent ? @parent.recursive_compiler_flags | @compiler_flags[active_platform] : @compiler_flags[active_platform]
end
def compiler_flags
flags = recursive_compiler_flags.dup
flags << '-fobjc-arc' if requires_arc
flags.join(' ')
end
platform_attr_writer :compiler_flags, lambda {|value, current| current << value }
def dependency(*name_and_version_requirements)
name, *version_requirements = name_and_version_requirements.flatten
raise Informative, "A specification can't require self as a subspec" if name == self.name
raise Informative, "A subspec can't require one of its parents specifications" if @parent && @parent.name.include?(name)
dep = Dependency.new(name, *version_requirements)
@define_for_platforms.each do |platform|
@dependencies[platform] << dep
end
dep
end
# External dependencies are inherited by subspecs
def external_dependencies(all_platforms = false)
active_plaform_check unless all_platforms
result = all_platforms ? @dependencies.values.flatten : @dependencies[active_platform]
result += parent.external_dependencies if parent
result
end
# A specification inherits the preferred_dependency or
# all the compatible subspecs as dependencies
def subspec_dependencies
active_plaform_check
specs = preferred_dependency ? [subspec_by_name("#{name}/#{preferred_dependency}")] : subspecs
specs.compact \
.select { |s| s.supports_platform?(active_platform) } \
.map { |s| Dependency.new(s.name, version) }
end
def dependencies
external_dependencies + subspec_dependencies
end
include Config::Mixin
def top_level_parent
@parent ? @parent.top_level_parent : self
end
def subspec?
!@parent.nil?
end
def subspec(name, &block)
subspec = Specification.new(self, name, &block)
@subspecs << subspec
subspec
end
attr_reader :subspecs
def recursive_subspecs
@recursive_subspecs ||= begin
mapper = lambda do |spec|
spec.subspecs.map do |subspec|
[subspec, *mapper.call(subspec)]
end.flatten
end
mapper.call(self)
end
end
def subspec_by_name(name)
return self if name.nil? || name == self.name
# Remove this spec's name from the beginning of the name we’re looking for
# and take the first component from the remainder, which is the spec we need
# to find now.
remainder = name[self.name.size+1..-1]
raise Informative, "Unable to find a specification named `#{name}' in `#{pod_name}'." unless remainder
subspec_name = remainder.split('/').shift
subspec = subspecs.find { |s| s.name == "#{self.name}/#{subspec_name}" }
raise Informative, "Unable to find a specification named `#{name}' in `#{pod_name}'." unless subspec
# If this was the last component in the name, then return the subspec,
# otherwise we recursively keep calling subspec_by_name until we reach the
# last one and return that
remainder.empty? ? subspec : subspec.subspec_by_name(name)
end
def local?
!source.nil? && !source[:local].nil?
end
def pod_destroot
config.project_pods_root + top_level_parent.name
end
def self.pattern_list(patterns)
if patterns.is_a?(Array) && (!defined?(Rake) || !patterns.is_a?(Rake::FileList))
patterns
else
[patterns]
end
end
# This method takes a header path and returns the location it should have
# in the pod's header dir.
#
# By default all headers are copied to the pod's header dir without any
# namespacing. However if the top level attribute accessor header_mappings_dir
# is specified the namespacing will be preserved from that directory.
def copy_header_mapping(from)
header_mappings_dir ? from.relative_path_from(header_mappings_dir) : from.basename
end
# This is a convenience method which gets called after all pods have been
# downloaded but before they have been installed, and the Xcode project and
# related files have been generated. (It receives the Pod::LocalPod
# instance generated form the specification and the #
# Pod::Podfile::TargetDefinition instance for the current target.) Override
# this to, for instance, to run any build script:
#
# Pod::Spec.new do |s|
# def s.pre_install(pod, target_definition)
# Dir.chdir(pod.root){ `sh make.sh` }
# end
# end
def pre_install(pod, target_definition)
FALSE
end
# This is a convenience method which gets called after all pods have been
# downloaded, installed, and the Xcode project and related files have been
# generated. (It receives the Pod::Installer::TargetInstaller instance for
# the current target.) Override this to, for instance, add to the prefix
# header:
#
# Pod::Spec.new do |s|
# def s.post_install(target_installer)
# prefix_header = config.project_pods_root + target_installer.prefix_header_filename
# prefix_header.open('a') do |file|
# file.puts(%{#ifdef __OBJC__\n#import "SSToolkitDefines.h"\n#endif})
# end
# end
# end
def post_install(target_installer)
FALSE
end
def podfile?
false
end
# This is used by the specification set
def dependency_by_top_level_spec_name(name)
external_dependencies(true).each do |dep|
return dep if dep.top_level_spec_name == name
end
end
def to_s
"#{name} (#{version})"
end
def inspect
"#<#{self.class.name} for #{to_s}>"
end
def ==(other)
object_id == other.object_id ||
(self.class === other &&
name && name == other.name &&
version && version == other.version)
end
# Returns whether the specification is supported in a given platform
def supports_platform?(*platform)
platform = platform[0].is_a?(Platform) ? platform[0] : Platform.new(*platform)
available_platforms.any? { |p| platform.supports?(p) }
end
# 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)
platform = platform[0].is_a?(Platform) ? platform[0] : Platform.new(*platform)
raise Informative, "#{to_s} is not compatible with #{platform}." unless supports_platform?(platform)
top_level_parent.active_platform = platform.to_sym
self
end
top_attr_accessor :active_platform
### Not attributes
# @visibility private
#
# This is used by PlatformProxy to assign attributes for the scoped platform.
def _on_platform(platform)
before, @define_for_platforms = @define_for_platforms, [platform]
yield
ensure
@define_for_platforms = before
end
# @visibility private
#
# This is multi-platform and to support
# subspecs with different platforms is is resolved as the
# first non nil value accross the chain.
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
Spec = Specification
end
require 'active_support/core_ext/array/conversions'
module Pod
class Specification
# A Specification::Set is resposible of handling all the specifications of
# a Pod. This class stores the information of the dependencies that reuired
# a Pod in the resolution process.
#
# @note The alpahbetical order of the sets is used to select a specification
# if multiple are available for a given version.
#
# @note The set class is not and should be not aware of the backing store
# of a Source.
#
class Set
# @return [String] The name of the Pod.
#
attr_reader :name
# @return [Array<Source>] The sources that contain the specifications for
# the available versions of a Pod.
#
attr_reader :sources
# @param [String] name The name of the Pod.
#
# @param [Array<Source>,Source] sources
# The sources that contain a Pod.
#
def initialize(name, sources)
@name = name
sources = sources.is_a?(Array) ? sources : [sources]
@sources = sources.sort_by(&:name)
@required_by = []
@dependencies = []
end
# @return [void] Stores a dependency on the Pod.
#
# @param [Dependency] dependency A dependency that requires the Pod.
#
# @param [String] dependent_name The name of the owner of the
# dependency. It is used only to display
# the Pod::Informative.
#
# @raises If the versions requirement of the dependency are not
# compatible with the previously stored dependencies.
#
def required_by(dependency, dependent_name)
unless @required_by.empty? || dependency.requirement.satisfied_by?(Gem::Version.new(required_version.to_s))
raise Informative, "#{dependent_name} tries to activate `#{dependency}', but already activated version `#{required_version}' by #{@required_by.to_sentence}."
end
@specification = nil
@required_by << dependent_name
@dependencies << dependency
end
# @return [Dependency] A dependency including all the versions
# requirements of the stored dependencies.
#
def dependency
@dependencies.inject(Dependency.new(name)) do |previous, dependency|
previous.merge(dependency.to_top_level_spec_dependency)
end
end
# @return [Specification] The specification for the given subspec name,
# from {specification}.
#
# @param [String] name The name of the specification. It can be the
# name of the top level parent or the name of a
# subspec.
#
# @see specification
#
def specification_by_name(name)
specification.top_level_parent.subspec_by_name(name)
end
# @return [Specification] The top level specification of the Pod for the
# {required_version}.
#
# @note If multiple sources have a specification for the
# {required_version} The alpahbetical order of their names is used
# to disambiguate.
#
def specification
unless @specification
sources = []
versions_by_source.each{ |source, versions| sources << source if versions.include?(required_version) }
source = sources.sort_by(&:name).first
@specification = source.specification(name, required_version)
end
@specification
end
# @return [Version] The highest version that satisfies {dependency}.
#
def required_version
versions.find { |v| dependency.match?(name, v) } ||
raise(Informative, "Required version (#{dependency}) not found for `#{name}'.\nAvailable versions: #{versions.join(', ')}")
end
# @return [Array<Version>] All the available versions for the Pod, sorted
# from highest to lowest.
#
def versions
versions_by_source.values.flatten.uniq.sort.reverse
end
# @return [Hash{Source => Version}] All the available versions for the
# Pod groupped by source.
#
def versions_by_source
result = {}
sources.each do |source|
result[source] = source.versions(name)
end
result
end
def ==(other)
self.class === other && @name == other.name && @sources.map(&:name) == other.sources.map(&:name)
end
def to_s
"#<#{self.class.name} for `#{name}' with required version `#{required_version}' available at `#{sources.map(&:name) * ', '}'>"
end
alias_method :inspect, :to_s
# The Set::External class handles Pods from external sources. Pods from
# external sources don't use the {Pod::Sources} and are intialized by a
# given specification.
#
# @note External sources *don't* support subspecs.
#
class External < Set
def initialize(specification)
@specification = specification
@required_by = []
@dependencies = []
end
def name
@specification.top_level_parent.name
end
def ==(other)
self.class === other && @specification == other.specification
end
def required_by(dependency, dependent_name)
before = @specification
super(dependency, dependent_name)
ensure
@specification = before
end
def specification_path
raise "specification_path"
end
def specification
@specification
end
def versions
[@specification.version]
end
end
end
end
end
require 'yaml'
# This is to make sure Faraday doesn't warn the user about the `system_timer` gem missing.
old_warn, $-w = $-w, nil
begin
require 'faraday'
ensure
$-w = old_warn
end
require 'octokit'
module Pod
class Specification
class Statistics
def self.instance
@instance ||= new
end
def self.instance=(instance)
@instance = instance
end
attr_accessor :cache_file, :cache_expiration
def initialize
@cache_file = Config.instance.repos_dir + 'statistics.yml'
@cache_expiration = 60 * 60 * 24 * 3
end
def creation_date(set)
compute_creation_date(set)
end
def creation_dates(sets)
dates = {}
sets.each { |set| dates[set.name] = compute_creation_date(set, false) }
save_cache
dates
end
def github_watchers(set)
github_stats_if_needed(set)
get_value(set, :gh_watchers)
end
def github_forks(set)
github_stats_if_needed(set)
get_value(set, :gh_forks)
end
def github_pushed_at(set)
github_stats_if_needed(set)
get_value(set, :pushed_at)
end
private
def cache
@cache ||= cache_file && cache_file.exist? ? YAML::load(cache_file.read) : {}
end
def get_value(set, key)
if cache[set.name] && cache[set.name][key]
cache[set.name][key]
end
end
def set_value(set, key, value)
cache[set.name] ||= {}
cache[set.name][key] = value
end
def save_cache
File.open(cache_file, 'w') { |f| f.write(YAML::dump(cache)) } if cache_file
end
def compute_creation_date(set, save = true)
date = get_value(set, :creation_date)
unless date
Dir.chdir(set.sources.first.repo) do
date = Time.at(`git log --first-parent --format=%ct #{set.name}`.split("\n").last.to_i)
end
set_value(set, :creation_date, date)
end
save_cache if save
date
end
def github_stats_if_needed(set)
return if get_value(set, :gh_date) && get_value(set, :gh_date) > Time.now - cache_expiration
spec = set.specification
url = spec.source[:git] || ''
repo_id = url[/github.com\/([^\/\.]*\/[^\/\.]*)\.*/, 1]
return unless repo_id
begin
repo = Octokit.repo(repo_id)
rescue
return
end
cache[set.name] ||= {}
set_value(set, :gh_watchers, repo['watchers'])
set_value(set, :gh_forks, repo['forks'])
set_value(set, :pushed_at, repo['pushed_at'])
set_value(set, :gh_date, Time.now)
save_cache
end
end
end
end
module Pod
class Version < Gem::Version
# @returns A Version described by its #to_s method.
#
# @TODO The `from' part of the regexp should be remove before 1.0.0.
#
def self.from_string(string)
if string =~ /HEAD (based on|from) (.*)/
v = Version.new($2)
v.head = true
v
else
Version.new(string)
end
end
attr_accessor :head
alias_method :head?, :head
def to_s
head? ? "HEAD based on #{super}" : super
end
end
end
require File.expand_path('../../spec_helper', __FILE__)
module Pod
describe Dependency do
it "merges dependencies (taken from newer RubyGems version)" do
dep1 = Dependency.new('bananas', '>= 1.8')
dep2 = Dependency.new('bananas', '1.9')
dep1.merge(dep2).should == Dependency.new('bananas', '>= 1.8', '1.9')
end
it "returns the name of the dependency, or the name of the pod of which this is a subspec" do
dep = Dependency.new('RestKit')
dep.top_level_spec_name.should == 'RestKit'
dep = Dependency.new('RestKit/Networking')
dep.top_level_spec_name.should == 'RestKit'
end
it "returns a copy of the dependency but for the top level spec, if it's a subspec" do
dep = Dependency.new('RestKit', '>= 1.2.3')
dep.to_top_level_spec_dependency.should == Dependency.new('RestKit', '>= 1.2.3')
dep = Dependency.new('RestKit/Networking', '>= 1.2.3')
dep.to_top_level_spec_dependency.should == Dependency.new('RestKit', '>= 1.2.3')
end
it "is equal to another dependency if `external_source' is the same" do
dep1 = Dependency.new('bananas', :git => 'GIT-URL')
dep2 = Dependency.new('bananas')
dep1.should.not == dep2
dep3 = Dependency.new('bananas', :git => 'GIT-URL')
dep1.should == dep3
end
it "is equal to another dependency if `specification' is equal" do
dep1 = Dependency.new { |s| s.name = 'bananas'; s.version = '1' }
dep2 = Dependency.new('bananas')
dep1.should.not == dep2
dep2 = Dependency.new { |s| s.name = 'bananas'; s.version = '1' }
dep1.should == dep2
end
it 'raises if created without either valid name/version/external requirements or a block' do
lambda { Dependency.new }.should.raise Informative
end
describe "defined with a block" do
before do
@dependency = Dependency.new do |spec|
spec.name = "my-custom-spec"
spec.version = "1.0.3"
end
end
it 'it identifies itself as an inline dependency' do
@dependency.should.be.inline
end
it 'attaches a custom spec to the dependency, configured by the block' do
@dependency.specification.name.should == "my-custom-spec"
end
end
describe "with a hash of external source settings" do
before do
@dependency = Dependency.new("cocoapods", :git => "git://github.com/cocoapods/cocoapods")
end
it 'identifies itself as an external dependency' do
@dependency.should.be.external
end
end
describe "with flags" do
it "identifies itself as a `bleeding edge' dependency" do
dependency = Dependency.new("cocoapods", :head)
dependency.should.be.head
dependency.to_s.should == "cocoapods (HEAD)"
end
it "only supports the `:head' option on the last version of a pod" do
should.raise Informative do
Dependency.new("cocoapods", "1.2.3", :head)
end
end
it "raises if an invalid flag is given" do
should.raise ArgumentError do
Dependency.new("cocoapods", :foot)
end
end
end
describe Dependency::ExternalSources do
before do
@sandbox = temporary_sandbox
end
it "marks a LocalPod as downloaded if it's from GitSource" do
dependency = Dependency.new("Reachability", :git => fixture('integration/Reachability'))
dependency.external_source.copy_external_source_into_sandbox(@sandbox, Platform.ios)
@sandbox.installed_pod_named('Reachability', Platform.ios).downloaded.should.be.true
end
it "creates a copy of the podspec (GitSource)" do
dependency = Dependency.new("Reachability", :git => fixture('integration/Reachability'))
dependency.external_source.copy_external_source_into_sandbox(@sandbox, Platform.ios)
path = @sandbox.root + 'Local Podspecs/Reachability.podspec'
path.should.exist?
end
it "creates a copy of the podspec (PodspecSource)" do
dependency = Dependency.new("Reachability", :podspec => fixture('integration/Reachability/Reachability.podspec').to_s)
dependency.external_source.copy_external_source_into_sandbox(@sandbox, Platform.ios)
path = @sandbox.root + 'Local Podspecs/Reachability.podspec'
path.should.exist?
end
it "creates a copy of the podspec (LocalSource)" do
dependency = Dependency.new("Reachability", :local => fixture('integration/Reachability'))
dependency.external_source.copy_external_source_into_sandbox(@sandbox, Platform.ios)
path = @sandbox.root + 'Local Podspecs/Reachability.podspec'
path.should.exist?
end
end
end
end
require File.expand_path('../../spec_helper', __FILE__)
describe "Pod::Lockfile" do
describe "In general" do
extend SpecHelper::TemporaryDirectory
def sample
text = <<-LOCKFILE.strip_heredoc
PODS:
- BananaLib (1.0):
- monkey (< 1.0.9, ~> 1.0.1)
- monkey (1.0.8)
DEPENDENCIES:
- BananaLib (~> 1.0)
SPEC CHECKSUMS:
BananaLib: !binary |-
MjI2Y2RkMTJkMzBhMWU4ZWM4OGM1ZmRkZWU2MDcwZDg0YTI1MGZjMQ==
COCOAPODS: #{Pod::VERSION}
LOCKFILE
end
def podfile
Pod::Podfile.new do
platform :ios
pod 'BananaLib', '~>1.0'
end
end
def specs
specs = [
Pod::Specification.from_file(fixture('banana-lib/BananaLib.podspec')),
Pod::Specification.new do |s|
s.name = "monkey"
s.version = "1.0.8"
end
]
specs.each { |s| s.activate_platform(:ios) }
specs
end
def tmp_path
temporary_directory + 'Podfile.lock'
end
it "loads from a hash" do
lockfile = Pod::Lockfile.new(YAML.load(sample))
lockfile.to_hash.should == YAML.load(sample)
end
it "loads from a file" do
File.open(tmp_path, 'w') {|f| f.write(sample) }
lockfile = Pod::Lockfile.from_file(tmp_path)
lockfile.defined_in_file.should == tmp_path
lockfile.to_hash.should == YAML.load(sample)
end
it "can be generated from a Podfile and a list of Specifications" do
lockfile = Pod::Lockfile.generate(podfile, specs)
lockfile.to_hash.should == YAML.load(sample)
end
before do
@lockfile = Pod::Lockfile.generate(podfile, specs)
end
it "generates a valid YAML representation" do
YAML.load(@lockfile.to_yaml).should == YAML.load(sample)
end
it "generates a valid Dictionary representation" do
@lockfile.to_hash.should == YAML.load(sample)
end
it "returns the list of the installed pods" do
@lockfile.pods_names.should == %w| BananaLib monkey |
end
it "returns the versions of the installed pods" do
@lockfile.pods_versions.should == {
"BananaLib" => Pod::Version.new("1.0"),
"monkey" => Pod::Version.new("1.0.8")
}
end
it "serializes correctly `:head' dependencies" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BananaLib', :head
end
specs = [
Pod::Specification.new do |s|
s.name = "BananaLib"
s.version = "1.0"
end,
Pod::Specification.new do |s|
s.name = "monkey"
s.version = "1.0.8"
end
]
specs.each { |s| s.activate_platform(:ios) }
lockfile = Pod::Lockfile.generate(podfile, specs)
lockfile.to_hash["DEPENDENCIES"][0].should == "BananaLib (HEAD)"
end
it "serializes correctly external dependencies" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BananaLib', { :git => "www.example.com", :tag => '1.0' }
end
specs = [
Pod::Specification.new do |s|
s.name = "BananaLib"
s.version = "1.0"
end,
Pod::Specification.new do |s|
s.name = "monkey"
s.version = "1.0.8"
end
]
specs.each { |s| s.activate_platform(:ios) }
lockfile = Pod::Lockfile.generate(podfile, specs)
lockfile.to_hash["DEPENDENCIES"][0].should == "BananaLib (from `www.example.com', tag `1.0')"
lockfile.to_hash["EXTERNAL SOURCES"]["BananaLib"].should == { :git => "www.example.com", :tag => '1.0' }
end
it "creates a dependency from a string" do
d = @lockfile.dependency_from_string("BananaLib (1.0)")
d.name.should == "BananaLib"
d.requirement.should =~ Pod::Version.new("1.0")
d.head.should.be.nil
d.external_source.should.be.nil
end
it "creates a head dependency from a string" do
d = @lockfile.dependency_from_string("BananaLib (HEAD)")
d.name.should == "BananaLib"
d.requirement.should.be.none?
d.head.should.be.true
d.external_source.should.be.nil
end
it "creates an external dependency from a string" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BananaLib', { :git => "www.example.com", :tag => '1.0' }
end
lockfile = Pod::Lockfile.generate(podfile, [])
d = lockfile.dependency_from_string("BananaLib (from `www.example.com', tag `1.0')")
d.name.should == "BananaLib"
d.requirement.should.be.none?
d.external?.should.be.true
d.external_source.description.should == "from `www.example.com', tag `1.0'"
end
end
describe "Concerning initialization from a file" do
extend SpecHelper::TemporaryDirectory
it "returns nil if it can't find the initialization file" do
lockfile = Pod::Lockfile.from_file(temporary_directory + 'Podfile.lock_not_existing')
lockfile.should == nil
end
end
describe "Concerning the identification of changes in the Podfile" do
before do
@podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit'
end
@specs = [
Pod::Specification.new do |s|
s.name = "BlocksKit"
s.version = "1.0.0"
end,
Pod::Specification.new do |s|
s.name = "JSONKit"
s.version = "1.4"
end ]
@specs.each { |s| s.activate_platform(:ios) }
@lockfile = Pod::Lockfile.generate(@podfile, @specs)
end
it "detects an added Pod" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit'
pod 'TTTAttributedLabel'
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>[],
:removed=>[],
:unchanged=>["BlocksKit", "JSONKit"],
:added=>["TTTAttributedLabel"]
}
end
it "detects an removed Pod" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>[],
:removed=>["JSONKit"],
:unchanged=>["BlocksKit"],
:added=>[]
}
end
it "detects Pods whose version changed" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', "> 1.4"
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
end
it "it doesn't mark a changed Pods whose version changed but is still compatible with the Podfile" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', "> 1.0"
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>[],
:removed=>[],
:unchanged=>["BlocksKit", "JSONKit"],
:added=>[]
}
end
it "detects Pods whose external source changed" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', :git => "example1.com"
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
@lockfile = Pod::Lockfile.generate(podfile, @specs)
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', :git => "example2.com"
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
end
it "detects Pods whose head state changed" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', :head
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
@specs = [
Pod::Specification.new do |s|
s.name = "BlocksKit"
s.version = "1.0.0"
end,
Pod::Specification.new do |s|
s.name = "JSONKit"
s.version = "1.4"
s.version.head = true
end ]
@specs.each { |s| s.activate_platform(:ios) }
@lockfile = Pod::Lockfile.generate(podfile, @specs)
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit'
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
end
end
end
require File.expand_path('../../spec_helper', __FILE__)
describe Pod::Platform do
describe "by default" do
it "returns a new Platform instance" do
Pod::Platform.ios.should == Pod::Platform.new(:ios)
Pod::Platform.osx.should == Pod::Platform.new(:osx)
end
it "can be initialized from another platform" do
platform = Pod::Platform.new(:ios)
new = Pod::Platform.new(platform)
new.should == platform
end
before do
@platform = Pod::Platform.ios
end
it "exposes it's symbolic name" do
@platform.name.should == :ios
end
it "can be compared for equality with another platform with the same symbolic name" do
@platform.should == Pod::Platform.new(:ios)
end
it "can be compared for equality with another platform with the same symbolic name and the same deployment target" do
@platform.should.not == Pod::Platform.new(:ios, '4.0')
Pod::Platform.new(:ios, '4.0').should == Pod::Platform.new(:ios, '4.0')
end
it "can be compared for equality with a matching symbolic name (backwards compatibility reasons)" do
@platform.should == :ios
end
it "presents an accurate string representation" do
@platform.to_s.should == "iOS"
Pod::Platform.new(:osx).to_s.should == 'OS X'
Pod::Platform.new(:ios, '5.0.0').to_s.should == 'iOS 5.0.0'
Pod::Platform.new(:osx, '10.7').to_s.should == 'OS X 10.7'
end
it "uses it's name as it's symbold version" do
@platform.to_sym.should == :ios
end
it "allows to specify the deployment target on initialization" do
p = Pod::Platform.new(:ios, '4.0.0')
p.deployment_target.should == Pod::Version.new('4.0.0')
end
it "allows to specify the deployment target in a hash on initialization (backwards compatibility from 0.6)" do
p = Pod::Platform.new(:ios, { :deployment_target => '4.0.0' })
p.deployment_target.should == Pod::Version.new('4.0.0')
end
end
describe "regarding supporting platforms" do
it "supports platforms with the same operating system" do
p1 = Pod::Platform.new(:ios)
p2 = Pod::Platform.new(:ios)
p1.should.supports?(p2)
p1 = Pod::Platform.new(:osx)
p2 = Pod::Platform.new(:osx)
p1.should.supports?(p2)
end
it "supports a platform with a lower or equal deployment_target" do
p1 = Pod::Platform.new(:ios, '5.0')
p2 = Pod::Platform.new(:ios, '4.0')
p1.should.supports?(p1)
p1.should.supports?(p2)
p2.should.not.supports?(p1)
end
it "doesn't supports a platform with a different operating system" do
p1 = Pod::Platform.new(:ios)
p2 = Pod::Platform.new(:osx)
p1.should.not.supports?(p2)
end
end
end
require File.expand_path('../../spec_helper', __FILE__)
describe "Pod::Podfile" do
it "loads from a file" do
podfile = Pod::Podfile.from_file(fixture('Podfile'))
podfile.defined_in_file.should == fixture('Podfile')
end
it "assigns the platform attribute to the current target" do
podfile = Pod::Podfile.new { platform :ios }
podfile.target_definitions[:default].platform.should == :ios
end
it "provides a default deployment target if not specified" do
podfile = Pod::Podfile.new { platform :ios }
podfile.target_definitions[:default].platform.deployment_target.should == Pod::Version.new('4.3')
end
it "raise error if unsupported platform is supplied" do
lambda {
Pod::Podfile.new { platform :iOS }
}.should.raise Pod::Podfile::Informative
begin
Pod::Podfile.new { platform :iOS }
rescue Pod::Podfile::Informative => e
e.stubs(:podfile_line).returns("./podfile_spec.rb:1")
e.message.should.be =~ /podfile_spec\.rb:1/
end
end
it "adds dependencies" do
podfile = Pod::Podfile.new { pod 'ASIHTTPRequest'; pod 'SSZipArchive', '>= 0.1' }
podfile.dependencies.size.should == 2
podfile.dependency_by_top_level_spec_name('ASIHTTPRequest').should == Pod::Dependency.new('ASIHTTPRequest')
podfile.dependency_by_top_level_spec_name('SSZipArchive').should == Pod::Dependency.new('SSZipArchive', '>= 0.1')
end
it "adds a dependency on a Pod repo outside of a spec repo (the repo is expected to contain a podspec)" do
podfile = Pod::Podfile.new do
pod 'SomeExternalPod', :git => 'GIT-URL', :commit => '1234'
end
dep = podfile.dependency_by_top_level_spec_name('SomeExternalPod')
dep.external_source.params.should == { :git => 'GIT-URL', :commit => '1234' }
end
it "adds a subspec dependency on a Pod repo outside of a spec repo (the repo is expected to contain a podspec)" do
podfile = Pod::Podfile.new do
pod 'MainSpec/FirstSubSpec', :git => 'GIT-URL', :commit => '1234'
end
dep = podfile.dependency_by_top_level_spec_name('MainSpec')
dep.external_source.name.should == 'MainSpec'
end
it "adds a dependency on a library outside of a spec repo (the repo does not need to contain a podspec)" do
podfile = Pod::Podfile.new do
pod 'SomeExternalPod', :podspec => 'http://gist/SomeExternalPod.podspec'
end
dep = podfile.dependency_by_top_level_spec_name('SomeExternalPod')
dep.external_source.params.should == { :podspec => 'http://gist/SomeExternalPod.podspec' }
end
it "adds a dependency on a library by specifying the podspec inline" do
podfile = Pod::Podfile.new do
pod do |s|
s.name = 'SomeExternalPod'
end
end
dep = podfile.dependency_by_top_level_spec_name('SomeExternalPod')
dep.specification.name.should == 'SomeExternalPod'
end
it "specifies that BridgeSupport metadata should be generated" do
Pod::Podfile.new {}.should.not.generate_bridge_support
Pod::Podfile.new { generate_bridge_support! }.should.generate_bridge_support
end
it 'specifies that ARC compatibility flag should be generated' do
Pod::Podfile.new { set_arc_compatibility_flag! }.should.set_arc_compatibility_flag
end
it "stores a block that will be called with the Installer before the target integration" do
yielded = nil
Pod::Podfile.new do
pre_install do |installer|
yielded = installer
end
end.pre_install!(:an_installer)
yielded.should == :an_installer
end
it "stores a block that will be called with the Installer instance once installation is finished (but the project is not written to disk yet)" do
yielded = nil
Pod::Podfile.new do
post_install do |installer|
yielded = installer
end
end.post_install!(:an_installer)
yielded.should == :an_installer
end
it "assumes the xcode project is the only existing project in the root" do
podfile = Pod::Podfile.new do
target(:another_target) {}
end
path = config.project_root + 'MyProject.xcodeproj'
Pathname.expects(:glob).with(config.project_root + '*.xcodeproj').returns([path])
podfile.target_definitions[:default].user_project.path.should == path
podfile.target_definitions[:another_target].user_project.path.should == path
end
it "assumes the basename of the workspace is the same as the default target's project basename" do
path = config.project_root + 'MyProject.xcodeproj'
Pathname.expects(:glob).with(config.project_root + '*.xcodeproj').returns([path])
Pod::Podfile.new {}.workspace.should == config.project_root + 'MyProject.xcworkspace'
Pod::Podfile.new do
xcodeproj 'AnotherProject.xcodeproj'
end.workspace.should == config.project_root + 'AnotherProject.xcworkspace'
end
it "does not base the workspace name on the default target's project if there are multiple projects specified" do
Pod::Podfile.new do
xcodeproj 'MyProject'
target :another_target do
xcodeproj 'AnotherProject'
end
end.workspace.should == nil
end
it "specifies the Xcode workspace to use" do
Pod::Podfile.new do
xcodeproj 'AnotherProject'
workspace 'MyWorkspace'
end.workspace.should == config.project_root + 'MyWorkspace.xcworkspace'
Pod::Podfile.new do
xcodeproj 'AnotherProject'
workspace 'MyWorkspace.xcworkspace'
end.workspace.should == config.project_root + 'MyWorkspace.xcworkspace'
end
describe "concerning targets (dependency groups)" do
it "returns wether or not a target has any dependencies" do
Pod::Podfile.new do
end.target_definitions[:default].should.be.empty
Pod::Podfile.new do
pod 'JSONKit'
end.target_definitions[:default].should.not.be.empty
end
before do
@podfile = Pod::Podfile.new do
platform :ios
xcodeproj 'iOS Project', 'iOS App Store' => :release, 'Test' => :debug
target :debug do
pod 'SSZipArchive'
end
target :test, :exclusive => true do
link_with 'TestRunner'
inhibit_all_warnings!
pod 'JSONKit'
target :subtarget do
pod 'Reachability'
end
end
target :osx_target do
platform :osx
xcodeproj 'OSX Project.xcodeproj', 'Mac App Store' => :release, 'Test' => :debug
link_with 'OSXTarget'
pod 'ASIHTTPRequest'
target :nested_osx_target do
end
end
pod 'ASIHTTPRequest'
end
end
it "returns all dependencies of all targets combined, which is used during resolving to ensure compatible dependencies" do
@podfile.dependencies.map(&:name).sort.should == %w{ ASIHTTPRequest JSONKit Reachability SSZipArchive }
end
it "adds dependencies outside of any explicit target block to the default target" do
target = @podfile.target_definitions[:default]
target.label.should == 'Pods'
target.dependencies.should == [Pod::Dependency.new('ASIHTTPRequest')]
end
it "adds dependencies of the outer target to non-exclusive targets" do
target = @podfile.target_definitions[:debug]
target.label.should == 'Pods-debug'
target.dependencies.sort_by(&:name).should == [
Pod::Dependency.new('ASIHTTPRequest'),
Pod::Dependency.new('SSZipArchive')
]
end
it "does not add dependencies of the outer target to exclusive targets" do
target = @podfile.target_definitions[:test]
target.label.should == 'Pods-test'
target.dependencies.should == [Pod::Dependency.new('JSONKit')]
end
it "adds dependencies of the outer target to nested targets" do
target = @podfile.target_definitions[:subtarget]
target.label.should == 'Pods-test-subtarget'
target.dependencies.should == [Pod::Dependency.new('Reachability'), Pod::Dependency.new('JSONKit')]
end
it "returns the Xcode project that contains the target to link with" do
[:default, :debug, :test, :subtarget].each do |target_name|
target = @podfile.target_definitions[target_name]
target.user_project.path.should == config.project_root + 'iOS Project.xcodeproj'
end
[:osx_target, :nested_osx_target].each do |target_name|
target = @podfile.target_definitions[target_name]
target.user_project.path.should == config.project_root + 'OSX Project.xcodeproj'
end
end
it "returns a Xcode project found in the working dir when no explicit project is specified" do
xcodeproj1 = config.project_root + '1.xcodeproj'
Pathname.expects(:glob).with(config.project_root + '*.xcodeproj').returns([xcodeproj1])
Pod::Podfile::UserProject.new.path.should == xcodeproj1
end
it "returns `nil' if more than one Xcode project was found in the working when no explicit project is specified" do
xcodeproj1, xcodeproj2 = config.project_root + '1.xcodeproj', config.project_root + '2.xcodeproj'
Pathname.expects(:glob).with(config.project_root + '*.xcodeproj').returns([xcodeproj1, xcodeproj2])
Pod::Podfile::UserProject.new.path.should == nil
end
it "leaves the name of the target, to link with, to be automatically resolved" do
target = @podfile.target_definitions[:default]
target.link_with.should == nil
end
it "returns the names of the explicit targets to link with" do
target = @podfile.target_definitions[:test]
target.link_with.should == ['TestRunner']
end
it "returns the name of the Pods static library" do
@podfile.target_definitions[:default].lib_name.should == 'libPods.a'
@podfile.target_definitions[:test].lib_name.should == 'libPods-test.a'
end
it "returns the name of the xcconfig file for the target" do
@podfile.target_definitions[:default].xcconfig_name.should == 'Pods.xcconfig'
@podfile.target_definitions[:default].xcconfig_relative_path.should == 'Pods/Pods.xcconfig'
@podfile.target_definitions[:test].xcconfig_name.should == 'Pods-test.xcconfig'
@podfile.target_definitions[:test].xcconfig_relative_path.should == 'Pods/Pods-test.xcconfig'
end
it "returns the name of the 'copy resources script' file for the target" do
@podfile.target_definitions[:default].copy_resources_script_name.should == 'Pods-resources.sh'
@podfile.target_definitions[:default].copy_resources_script_relative_path.should == '${SRCROOT}/Pods/Pods-resources.sh'
@podfile.target_definitions[:test].copy_resources_script_name.should == 'Pods-test-resources.sh'
@podfile.target_definitions[:test].copy_resources_script_relative_path.should == '${SRCROOT}/Pods/Pods-test-resources.sh'
end
it "returns the name of the 'prefix header' file for the target" do
@podfile.target_definitions[:default].prefix_header_name.should == 'Pods-prefix.pch'
@podfile.target_definitions[:test].prefix_header_name.should == 'Pods-test-prefix.pch'
end
it "returns the name of the BridgeSupport file for the target" do
@podfile.target_definitions[:default].bridge_support_name.should == 'Pods.bridgesupport'
@podfile.target_definitions[:test].bridge_support_name.should == 'Pods-test.bridgesupport'
end
it "returns the platform of the target" do
@podfile.target_definitions[:default].platform.should == :ios
@podfile.target_definitions[:test].platform.should == :ios
@podfile.target_definitions[:osx_target].platform.should == :osx
end
it "assigs a deployment target to the platforms if not specified" do
@podfile.target_definitions[:default].platform.deployment_target.to_s.should == '4.3'
@podfile.target_definitions[:test].platform.deployment_target.to_s.should == '4.3'
@podfile.target_definitions[:osx_target].platform.deployment_target.to_s.should == '10.6'
end
it "autmatically marks a target as exclusive if the parent platform doesn't match" do
@podfile.target_definitions[:osx_target].should.be.exclusive
@podfile.target_definitions[:nested_osx_target].should.not.be.exclusive
end
it "returns the specified configurations and wether it should be based on a debug or a release build" do
Pod::Podfile::UserProject.any_instance.stubs(:project)
all = { 'Release' => :release, 'Debug' => :debug, 'Test' => :debug }
@podfile.target_definitions[:default].user_project.build_configurations.should == all.merge('iOS App Store' => :release)
@podfile.target_definitions[:test].user_project.build_configurations.should == all.merge('iOS App Store' => :release)
@podfile.target_definitions[:osx_target].user_project.build_configurations.should == all.merge('Mac App Store' => :release)
@podfile.target_definitions[:nested_osx_target].user_project.build_configurations.should == all.merge('Mac App Store' => :release)
@podfile.user_build_configurations.should == all.merge('iOS App Store' => :release, 'Mac App Store' => :release)
end
it "defaults, for unspecified configurations, to a release build" do
project = Pod::Podfile::UserProject.new(fixture('SampleProject/SampleProject.xcodeproj'), 'Test' => :debug)
project.build_configurations.should == { 'Release' => :release, 'Debug' => :debug, 'Test' => :debug, 'App Store' => :release }
end
it "specifies that the inhibit all warnings flag should be added to the target's build settings" do
@podfile.target_definitions[:default].should.not.inhibit_all_warnings
@podfile.target_definitions[:test].should.inhibit_all_warnings
@podfile.target_definitions[:subtarget].should.inhibit_all_warnings
end
describe "with an Xcode project that's not in the project_root" do
before do
@target_definition = @podfile.target_definitions[:default]
@target_definition.user_project.stubs(:path).returns(config.project_root + 'subdir/iOS Project.xcodeproj')
end
it "returns the $(PODS_ROOT) relative to the project's $(SRCROOT)" do
@target_definition.relative_pods_root.should == '${SRCROOT}/../Pods'
end
it "simply returns the $(PODS_ROOT) path if no xcodeproj file is available and doesn't needs to integrate" do
config.integrate_targets.should.equal true
config.integrate_targets = false
@target_definition.relative_pods_root.should == '${SRCROOT}/../Pods'
@target_definition.user_project.stubs(:path).returns(nil)
@target_definition.relative_pods_root.should == '${SRCROOT}/Pods'
config.integrate_targets = true
end
it "returns the xcconfig file path relative to the project's $(SRCROOT)" do
@target_definition.xcconfig_relative_path.should == '../Pods/Pods.xcconfig'
end
it "returns the 'copy resources script' path relative to the project's $(SRCROOT)" do
@target_definition.copy_resources_script_relative_path.should == '${SRCROOT}/../Pods/Pods-resources.sh'
end
end
end
describe "concerning the podspec method" do
xit "it can use use the dependencies of a podspec" do
end
xit "it allows to specify the name of a podspec" do
end
xit "it allows to specify the path of a podspec" do
end
end
describe "concerning validations" do
it "raises if it should integrate and can't find an xcodeproj" do
config.integrate_targets = true
target_definition = Pod::Podfile.new {}.target_definitions[:default]
target_definition.user_project.stubs(:path).returns(nil)
exception = lambda {
target_definition.relative_pods_root
}.should.raise Pod::Informative
exception.message.should.include "Xcode project"
end
end
end
require File.expand_path('../../spec_helper', __FILE__)
describe "Pod::Source" do
before do
@source = Pod::Source.new(fixture('spec-repos/master'))
end
it "return its name" do
@source.name.should == 'master'
end
it "returns the sets of all the available Pods" do
set_names = @source.pod_sets.map(&:name)
set_names.should.include('JSONKit')
set_names.should.include('Reachability')
end
it "returns the available versions of a Pod" do
@source.versions('Reachability').map(&:to_s).should == %w| 3.0.0 2.0.5 2.0.4 |
end
it "returns the specification of a given version of a Pod" do
spec = @source.specification('Reachability', Pod::Version.new('3.0.0'))
spec.name.should == 'Reachability'
spec.version.should.to_s == '3.0.0'
end
it "properly configures the sources of a set in seach by name" do
source = Pod::Source.new(fixture('spec-repos/test_repo'))
sets = source.search_by_name('monkey', true)
sets.count.should == 1
set = sets.first
set.name.should == 'BananaLib'
set.sources.map(&:name).should == %w| test_repo |
end
describe "Pod::Source::Aggregate" do
# BananaLib is available only in test_repo.
# JSONKit is in test repo has version 1.4 (duplicated) and the 999.999.999.
it "returns all the sources" do
Pod::Source.all.map(&:name).should == %w| master test_repo |
end
it "returns the name of all the available pods" do
pod_names = Pod::Source::Aggregate.new.all_pods
pod_names.should.include('JSONKit')
pod_names.should.include('BananaLib')
end
it "returns all the available sets with the sources configured" do
sets = Pod::Source.all_sets
banana_sets = sets.select{ |set| set.name == 'BananaLib' }
banana_sets.count.should == 1
banana_sets.first.sources.map(&:name).should == %w| test_repo |
json_set = sets.select{ |set| set.name == 'JSONKit' }
json_set.count.should == 1
json_set.first.sources.map(&:name).should == %w| master test_repo |
end
it "searches the sets by dependency" do
dep = Pod::Dependency.new('JSONKit')
set = Pod::Source.search(dep)
set.name.should == 'JSONKit'
set.sources.map(&:name).should == %w| master test_repo |
end
it "searches the sets specifing a dependency on a subspec" do
dep = Pod::Dependency.new('RestKit/JSON')
set = Pod::Source.search(dep)
set.name.should == 'RestKit'
set.sources.map(&:name).should == %w| master |
end
it "raises if a specification set can't be found" do
lambda {
dep = Pod::Dependency.new('DoesNotExist')
set = Pod::Source.search(dep)
}.should.raise Pod::Informative
end
it "raises if a subspec can't be found" do
lambda {
dep = Pod::Dependency.new('RestKit/DoesNotExist')
set = Pod::Source.search(dep)
}.should.raise Pod::Informative
end
it "searches the sets by name" do
sets = Pod::Source.search_by_name('JSONKit')
sets.count.should == 1
set = sets.first
set.name.should == 'JSONKit'
set.sources.map(&:name).should == %w| master test_repo |
end
it "properly configures the sources of a set in search by name" do
sets = Pod::Source.search_by_name('BananaLib')
sets.count.should == 1
set = sets.first
set.name.should == 'BananaLib'
set.sources.map(&:name).should == %w| test_repo |
end
end
end
require File.expand_path('../../../spec_helper', __FILE__)
describe "Pod::Specification::Set" do
describe "In general" do
before do
@source = Pod::Source.new(fixture('spec-repos/master'))
@set = Pod::Spec::Set.new('CocoaLumberjack', @source)
end
it "returns the name of the pod" do
@set.name.should == 'CocoaLumberjack'
end
it "returns the versions available for this pod ordered from highest to lowest" do
@set.versions.should == %w[1.6 1.3.3 1.3.2 1.3.1 1.3 1.2.3 1.2.2 1.2.1 1.2 1.1 1.0].map { |v| Pod::Version.new(v) }
end
it "checks if the dependency of the specification is compatible with existing requirements" do
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '1.2'), 'Spec')
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '< 1.2.1'), 'Spec')
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '> 1.1'), 'Spec')
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '~> 1.2.0'), 'Spec')
@set.required_by(Pod::Dependency.new('CocoaLumberjack'), 'Spec')
lambda {
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '< 1.0' ), 'Spec')
}.should.raise Pod::Informative
end
it "raises if the required version doesn't exist" do
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '< 1.0'), 'Spec')
lambda { @set.required_version }.should.raise Pod::Informative
end
it "can test if it is equal to another set" do
@set.should == Pod::Spec::Set.new('CocoaLumberjack', @source)
@set.should.not == Pod::Spec::Set.new('RestKit', @source)
end
before do
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '< 1.2.1'), 'Spec')
end
it "returns the version required for the dependency" do
@set.required_version.should == Pod::Version.new('1.2')
end
it "returns the specification for the required version" do
@set.specification.should == Pod::Spec.new { |s| s.name = 'CocoaLumberjack'; s.version = '1.2' }
end
it "ignores dotfiles when getting the version directories" do
`touch #{fixture('spec-repos/master/CocoaLumberjack/.DS_Store')}`
lambda { @set.versions }.should.not.raise
end
it "raises if a version is incompatible with the activated version" do
spec = Pod::Dependency.new('CocoaLumberjack', '1.2.1')
lambda { @set.required_by(spec, 'Spec') }.should.raise Pod::Informative
end
end
describe "Concerning multiple sources" do
before do
# JSONKit is in test repo has version 1.4 (duplicated) and the 999.999.999.
@set = Pod::Source.search_by_name('JSONKit').first
end
it "returns all the available versions sorted from biggest to lowest" do
@set.versions.map(&:to_s).should == %w| 999.999.999 1.5pre 1.4 |
end
it "returns all the available versions by source sorted from bigest to lowest" do
hash = {}
@set.versions_by_source.each { |source, versions| hash[source.name] = versions.map(&:to_s) }
hash['master'].should == %w| 1.5pre 1.4 |
hash['test_repo'].should == %w| 999.999.999 1.4 |
hash.keys.sort.should == %w| master test_repo |
end
it "returns the specification from the `master` source for the required version" do
dep = Pod::Dependency.new('JSONKit', '1.5pre')
@set.required_by(dep, 'Spec')
spec = @set.specification
spec.name.should == 'JSONKit'
spec.version.to_s.should == '1.5pre'
spec.defined_in_file.should == fixture('spec-repos/master/JSONKit/1.5pre/JSONKit.podspec')
end
it "returns the specification from `test_repo` source for the required version" do
dep = Pod::Dependency.new('JSONKit', '999.999.999')
@set.required_by(dep, 'Spec')
spec = @set.specification
spec.name.should == 'JSONKit'
spec.version.to_s.should == '999.999.999'
spec.defined_in_file.should == fixture('spec-repos/test_repo/JSONKit/999.999.999/JSONKit.podspec')
end
it "prefers sources by alphabetical order" do
dep = Pod::Dependency.new('JSONKit', '1.4')
@set.required_by(dep, 'Spec')
spec = @set.specification
spec.name.should == 'JSONKit'
spec.version.to_s.should == '1.4'
spec.defined_in_file.should == fixture('spec-repos/master/JSONKit/1.4/JSONKit.podspec')
end
end
end
require File.expand_path('../../spec_helper', __FILE__)
describe "A Pod::Specification loaded from a podspec" do
before do
fixture('banana-lib') # ensure the archive is unpacked
@spec = Pod::Specification.from_file(fixture('banana-lib/BananaLib.podspec'))
end
it "has no parent if it is the top level spec" do
@spec.parent.nil?.should == true
end
it "returns that it's not loaded from a podfile" do
@spec.should.not.be.podfile
end
it "returns the path to the podspec" do
@spec.defined_in_file.should == fixture('banana-lib/BananaLib.podspec')
end
it "returns the directory where the pod should be checked out to" do
@spec.pod_destroot.should == config.project_pods_root + 'BananaLib'
end
it "returns the pod's name" do
@spec.name.should == 'BananaLib'
end
it "returns the pod's version" do
@spec.version.should == Pod::Version.new('1.0')
end
it "returns a list of authors and their email addresses" do
@spec.authors.should == {
'Banana Corp' => nil,
'Monkey Boy' => 'monkey@banana-corp.local'
}
end
it "returns the pod's homepage" do
@spec.homepage.should == 'http://banana-corp.local/banana-lib.html'
end
it "returns the pod's summary" do
@spec.summary.should == 'Chunky bananas!'
end
it "returns the pod's description" do
@spec.description.should == 'Full of chunky bananas.'
end
it "returns the pod's source" do
@spec.source.should == {
:git => 'http://banana-corp.local/banana-lib.git',
:tag => 'v1.0'
}
end
it "returns the pod's source files" do
@spec.activate_platform(:ios).source_files.should == ['Classes/*.{h,m}', 'Vendor']
@spec.activate_platform(:osx).source_files.should == ['Classes/*.{h,m}', 'Vendor']
end
it "returns the pod's dependencies" do
expected = Pod::Dependency.new('monkey', '~> 1.0.1', '< 1.0.9')
@spec.activate_platform(:ios).dependencies.should == [expected]
@spec.activate_platform(:osx).dependencies.should == [expected]
end
it "returns the pod's xcconfig settings" do
@spec.activate_platform(:ios).xcconfig.should == { 'OTHER_LDFLAGS' => '-framework SystemConfiguration' }
end
it "has a shortcut to add frameworks to the xcconfig" do
@spec.frameworks = 'CFNetwork', 'CoreText'
@spec.activate_platform(:ios).xcconfig.should == {
'OTHER_LDFLAGS' => '-framework CFNetwork ' \
'-framework CoreText ' \
'-framework SystemConfiguration' }
end
it "has a shortcut to add weak frameworks to the xcconfig" do
@spec.weak_frameworks = 'Twitter'
@spec.activate_platform(:ios).xcconfig.should == {
"OTHER_LDFLAGS"=>"-framework SystemConfiguration -weak_framework Twitter"
}
end
it "has a shortcut to add libraries to the xcconfig" do
@spec.libraries = 'z', 'xml2'
@spec.activate_platform(:ios).xcconfig.should == {
'OTHER_LDFLAGS' => '-lxml2 -lz -framework SystemConfiguration'
}
end
it "returns that it's equal to another specification if the name and version are equal" do
@spec.should == Pod::Spec.new { |s| s.name = 'BananaLib'; s.version = '1.0' }
@spec.should.not == Pod::Spec.new { |s| s.name = 'OrangeLib'; s.version = '1.0' }
@spec.should.not == Pod::Spec.new { |s| s.name = 'BananaLib'; s.version = '1.1' }
@spec.should.not == Pod::Spec.new
end
it "never equals when it's from a Podfile" do
Pod::Spec.new.should.not == Pod::Spec.new
end
it "adds compiler flags if ARC is required" do
@spec.parent.should == nil
@spec.requires_arc = true
@spec.activate_platform(:ios).compiler_flags.should == "-fobjc-arc"
@spec.activate_platform(:osx).compiler_flags.should == "-fobjc-arc"
@spec.compiler_flags = "-Wunused-value"
@spec.activate_platform(:ios).compiler_flags.should == "-Wunused-value -fobjc-arc"
@spec.activate_platform(:osx).compiler_flags.should == "-Wunused-value -fobjc-arc"
end
end
describe "A Pod::Specification, in general," do
before do
@spec = Pod::Spec.new
end
it "returns the platform that the static library should be build for" do
@spec.platform = :ios
@spec.platform.should == :ios
end
it "returns the platform and the deployment target" do
@spec.platform = :ios, '4.0'
@spec.platform.should == :ios
@spec.platform.deployment_target.should == Pod::Version.new('4.0')
end
it "returns the available platforms for which the pod is supported" do
@spec.platform = :ios, '4.0'
@spec.available_platforms.count.should == 1
@spec.available_platforms.first.should == :ios
@spec.available_platforms.first.deployment_target.should == Pod::Version.new('4.0')
end
it "returns the license of the Pod" do
@spec.license = {
:type => 'MIT',
:file => 'LICENSE',
:text => 'Permission is hereby granted ...'
}
@spec.license.should == {
:type => 'MIT',
:file => 'LICENSE',
:text => 'Permission is hereby granted ...'
}
end
it "returns the license of the Pod specified in the old format" do
@spec.license = 'MIT'
@spec.license.should == {
:type => 'MIT',
}
end
it "returns the documentation of the Pod" do
@spec.documentation = {
:html => 'http://EXAMPLE/#{@name}/documentation',
:appledoc => ['--project-name', '#{@name}',
'--project-company', '"Company Name"',
'--company-id', 'com.company',
'--ignore', 'Common',
'--ignore', '.m']
}
@spec.documentation[:html].should == 'http://EXAMPLE/#{@name}/documentation'
@spec.documentation[:appledoc].should == ['--project-name', '#{@name}',
'--project-company', '"Company Name"',
'--company-id', 'com.company',
'--ignore', 'Common',
'--ignore', '.m']
end
it "takes a list of paths to clean" do
@spec.clean_paths = 'Demo', 'Doc'
@spec.clean_paths.should == %w{ Demo Doc }
end
it "takes any object for clean_paths as long as it responds to #glob (we provide this for Rake::FileList)" do
@spec.clean_paths = Pod::FileList['*'].exclude('Rakefile')
list = ROOT + @spec.clean_paths.first
list.glob.should == Pod::FileList[(ROOT + '*').to_s].exclude('Rakefile').map { |path| Pathname.new(path) }
end
it "takes a list of paths to preserve" do
@spec.preserve_paths = 'script.sh'
@spec.activate_platform(:ios).preserve_paths.should == %w{ script.sh }
end
it "takes any object for source_files as long as it responds to #glob (we provide this for Rake::FileList)" do
@spec.source_files = Pod::FileList['*'].exclude('Rakefile')
@spec.activate_platform(:ios)
list = ROOT + @spec.source_files.first
list.glob.should == Pod::FileList[(ROOT + '*').to_s].exclude('Rakefile').map { |path| Pathname.new(path) }
end
it "takes a prefix header path which will be appended to the Pods pch file" do
@spec.prefix_header_file.should == nil
@spec.prefix_header_file = 'Classes/Demo.pch'
@spec.prefix_header_file.should == Pathname.new('Classes/Demo.pch')
end
it "takes code that's to be appended to the Pods pch file" do
@spec.prefix_header_contents.should == nil
@spec.prefix_header_contents = '#import "BlocksKit.h"'
@spec.prefix_header_contents.should == '#import "BlocksKit.h"'
end
it "can be activated for a supported platorm" do
@spec.platform = :ios
lambda {@spec.activate_platform(:ios)}.should.not.raise Pod::Informative
end
it "raised if attempted to be activated for an unsupported platform" do
@spec.platform = :osx, '10.7'
lambda {@spec.activate_platform(:ios)}.should.raise Pod::Informative
lambda {@spec.activate_platform(:ios, '10.6')}.should.raise Pod::Informative
end
it "raises if not activated for a platform before accessing a multiplatform value" do
@spec.platform = :ios
lambda {@spec.source_files}.should.raise Pod::Informative
end
it "returns self on activation for method chainablity" do
@spec.platform = :ios
@spec.activate_platform(:ios).should == @spec
end
it "it handles local sources" do
@spec.activate_platform(:ios)
@spec.source = {:local => '/tmp/local/path'}
@spec.local?.should.be.true
end
end
describe "A Pod::Specification, hierarchy" do
before do
@spec = Pod::Spec.new do |s|
s.name = 'MainSpec'
s.version = '0.999'
s.dependency 'awesome_lib'
s.subspec 'SubSpec.0' do |fss|
fss.platform = :ios
fss.subspec 'SubSpec.0.0' do |sss|
end
end
s.subspec 'SubSpec.1'
end
@subspec = @spec.subspecs.first
@spec.activate_platform(:ios)
end
it "automatically includes all the compatible subspecs as a dependencis if not preference is given" do
@spec.dependencies.map { |s| s.name }.should == %w[ awesome_lib MainSpec/SubSpec.0 MainSpec/SubSpec.1 ]
@spec.activate_platform(:osx).dependencies.map { |s| s.name }.should == %w[ awesome_lib MainSpec/SubSpec.1 ]
end
it "uses the spec version for the dependencies" do
@spec.dependencies.
select { |d| d.name =~ /MainSpec/ }.
all? { |d| d.requirement.to_s == '= 0.999' }.
should.be.true
end
it "respecs the preferred dependency for subspecs, if specified" do
@spec.preferred_dependency = 'SubSpec.0'
@spec.dependencies.map { |s| s.name }.should == %w[ awesome_lib MainSpec/SubSpec.0 ]
end
it "raises if it has dependecy on a self or on an upstream subspec" do
lambda { @subspec.dependency('MainSpec/SubSpec.0') }.should.raise Pod::Informative
lambda { @subspec.dependency('MainSpec') }.should.raise Pod::Informative
end
it "inherits external dependecies from the parent" do
@subspec.dependencies.map { |s| s.name }.should == %w[ awesome_lib MainSpec/SubSpec.0/SubSpec.0.0 ]
end
it "it accepts a dependency on a subspec that is in the same level of the hierarchy" do
@subspec.dependency('MainSpec/SubSpec.1')
@subspec.dependencies.map { |s| s.name }.should == %w[ MainSpec/SubSpec.1 awesome_lib MainSpec/SubSpec.0/SubSpec.0.0 ]
end
end
describe "A Pod::Specification subspec" do
before do
@spec = Pod::Spec.new do |s|
s.name = 'MainSpec'
s.version = '1.2.3'
s.license = 'MIT'
s.author = 'Joe the Plumber'
s.source = { :git => '/some/url' }
s.requires_arc = true
s.source_files = 'spec.m'
s.resource = 'resource'
s.platform = :ios
s.library = 'xml'
s.framework = 'CoreData'
s.subspec 'FirstSubSpec' do |fss|
fss.ios.source_files = 'subspec_ios.m'
fss.osx.source_files = 'subspec_osx.m'
fss.framework = 'CoreGraphics'
fss.weak_framework = 'Twitter'
fss.library = 'z'
fss.subspec 'SecondSubSpec' do |sss|
sss.source_files = 'subsubspec.m'
sss.requires_arc = false
end
end
end
@subspec = @spec.subspecs.first
@subsubspec = @subspec.subspecs.first
end
it "returns the top level parent spec" do
@spec.subspecs.first.top_level_parent.should == @spec
@spec.subspecs.first.subspecs.first.top_level_parent.should == @spec
end
it "is named after the parent spec" do
@spec.subspecs.first.name.should == 'MainSpec/FirstSubSpec'
@spec.subspecs.first.subspecs.first.name.should == 'MainSpec/FirstSubSpec/SecondSubSpec'
end
it "correctly resolves the inheritance chain" do
@spec.subspecs.first.subspecs.first.parent.should == @spec.subspecs.first
@spec.subspecs.first.parent.should == @spec
end
it "automatically forwards top level attributes to the subspecs" do
@spec.activate_platform(:ios)
[:version, :license, :authors].each do |attr|
@spec.subspecs.first.send(attr).should == @spec.send(attr)
@spec.subspecs.first.subspecs.first.send(attr).should == @spec.send(attr)
end
end
it "resolves correctly chained attributes" do
@spec.activate_platform(:ios)
@spec.source_files.map { |f| f.to_s }.should == %w[ spec.m ]
@subspec.source_files.map { |f| f.to_s }.should == %w[ spec.m subspec_ios.m ]
@subsubspec.source_files.map { |f| f.to_s }.should == %w[ spec.m subspec_ios.m subsubspec.m ]
@subsubspec.resources.should == %w[ resource ]
@subsubspec.compiler_flags = '-Wdeprecated-implementations'
@subsubspec.compiler_flags.should == '-Wdeprecated-implementations'
end
it "allows to specify arc settings for subspecs" do
@spec.activate_platform(:ios)
@spec.requires_arc.should == true
@subspec.requires_arc.should == true
@subsubspec.requires_arc.should == false
end
it "returns empty arrays for chained attributes with no value in the chain" do
@spec = Pod::Spec.new do |s|
s.name = 'MainSpec'
s.platform = :ios
s.subspec 'FirstSubSpec' do |fss|
fss.subspec 'SecondSubSpec' do |sss|
sss.source_files = 'subsubspec.m'
end
end
end
@spec.activate_platform(:ios).source_files.should == []
@spec.subspecs.first.source_files.should == []
@spec.subspecs.first.subspecs.first.source_files.should == %w[ subsubspec.m ]
end
it "does not cache platform attributes and can activate another platform" do
@spec.stubs(:platform).returns nil
@spec.activate_platform(:ios)
@subsubspec.source_files.map { |f| f.to_s }.should == %w[ spec.m subspec_ios.m subsubspec.m ]
@spec.activate_platform(:osx)
@subsubspec.source_files.map { |f| f.to_s }.should == %w[ spec.m subspec_osx.m subsubspec.m ]
end
it "resolves correctly the available platforms" do
@spec.stubs(:platform).returns nil
@subspec.platform = :ios, '4.0'
@spec.available_platforms.map{ |p| p.to_sym }.should == [ :osx, :ios ]
@subspec.available_platforms.first.to_sym.should == :ios
@subsubspec.available_platforms.first.to_sym.should == :ios
@subsubspec.platform = :ios, '5.0'
@subspec.available_platforms.first.deployment_target.to_s.should == '4.0'
@subsubspec.available_platforms.first.deployment_target.to_s.should == '5.0'
end
it "resolves reports correctly the supported platforms" do
@spec.stubs(:platform).returns nil
@subspec.platform = :ios, '4.0'
@subsubspec.platform = :ios, '5.0'
@spec.supports_platform?(:ios).should.be.true
@spec.supports_platform?(:osx).should.be.true
@subspec.supports_platform?(:ios).should.be.true
@subspec.supports_platform?(:osx).should.be.false
@subspec.supports_platform?(:ios, '4.0').should.be.true
@subspec.supports_platform?(:ios, '5.0').should.be.true
@subsubspec.supports_platform?(:ios).should.be.true
@subsubspec.supports_platform?(:osx).should.be.false
@subsubspec.supports_platform?(:ios, '4.0').should.be.false
@subsubspec.supports_platform?(:ios, '5.0').should.be.true
@subsubspec.supports_platform?(Pod::Platform.new(:ios, '4.0')).should.be.false
@subsubspec.supports_platform?(Pod::Platform.new(:ios, '5.0')).should.be.true
end
it "raises a top level attribute is assigned to a spec with a parent" do
lambda { @subspec.version = '0.0.1' }.should.raise Pod::Informative
end
it "returns subspecs by name" do
@spec.subspec_by_name(nil).should == @spec
@spec.subspec_by_name('MainSpec').should == @spec
@spec.subspec_by_name('MainSpec/FirstSubSpec').should == @subspec
@spec.subspec_by_name('MainSpec/FirstSubSpec/SecondSubSpec').should == @subsubspec
end
it "has the same active platform accross the chain attributes" do
@spec.activate_platform(:ios)
@subspec.active_platform.should == :ios
@subsubspec.active_platform.should == :ios
@spec.stubs(:platform).returns nil
@subsubspec.activate_platform(:osx)
@subspec.active_platform.should == :osx
@spec.active_platform.should == :osx
end
it "resolves the libraries correctly" do
@spec.activate_platform(:ios)
@spec.libraries.should == %w[ xml ]
@subspec.libraries.should == %w[ xml z ]
@subsubspec.libraries.should == %w[ xml z ]
end
it "resolves the frameworks correctly" do
@spec.activate_platform(:ios)
@spec.frameworks.should == %w[ CoreData ]
@subspec.frameworks.should == %w[ CoreData CoreGraphics ]
@subsubspec.frameworks.should == %w[ CoreData CoreGraphics ]
end
it "resolves the weak frameworks correctly" do
@spec.activate_platform(:ios)
@spec.weak_frameworks.should == %w[ ]
@subspec.weak_frameworks.should == %w[ Twitter ]
end
it "resolves the xcconfig" do
@spec.activate_platform(:ios)
@spec.xcconfig = { 'OTHER_LDFLAGS' => "-Wl,-no_compact_unwind" }
@spec.xcconfig.should == {"OTHER_LDFLAGS"=>"-Wl,-no_compact_unwind -lxml -framework CoreData"}
@subspec.xcconfig.should == {"OTHER_LDFLAGS"=>"-Wl,-no_compact_unwind -lxml -lz -framework CoreData -framework CoreGraphics -weak_framework Twitter"}
@subsubspec.xcconfig.should == {"OTHER_LDFLAGS"=>"-Wl,-no_compact_unwind -lxml -lz -framework CoreData -framework CoreGraphics -weak_framework Twitter"}
@subsubspec.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
@spec.xcconfig.should == {"OTHER_LDFLAGS"=>"-Wl,-no_compact_unwind -lxml -framework CoreData"}
@subsubspec.xcconfig.should == {"OTHER_LDFLAGS"=>"-Wl,-no_compact_unwind -lxml -lz -framework CoreData -framework CoreGraphics -weak_framework Twitter", "HEADER_SEARCH_PATHS"=>"$(SDKROOT)/usr/include/libxml2"}
end
end
describe "A Pod::Specification with :local source" do
before do
@spec = Pod::Spec.new do |s|
s.name = 'MainSpec'
s.source = { :local => fixture("integration/JSONKit") }
s.source_files = "."
end
end
it "is marked as local" do
@spec.should.be.local
end
it "it returns the expanded local path" do
@spec.source.should == {:local => fixture("integration/JSONKit")}
end
end
describe "A Pod::Specification, concerning its attributes that support different values per platform," do
describe "when **no** platform specific values are given" do
before do
@spec = Pod::Spec.new do |s|
s.source_files = 'file1', 'file2'
s.resources = 'file1', 'file2'
s.xcconfig = { 'OTHER_LDFLAGS' => '-lObjC' }
s.framework = 'QuartzCore'
s.library = 'z'
s.compiler_flags = '-Wdeprecated-implementations'
s.requires_arc = true
s.dependency 'JSONKit'
s.dependency 'SSZipArchive'
end
end
it "returns the same list of source files for each platform" do
@spec.activate_platform(:ios).source_files.should == %w{ file1 file2 }
@spec.activate_platform(:osx).source_files.should == %w{ file1 file2 }
end
it "returns the same list of resources for each platform" do
@spec.activate_platform(:ios).resources.should == %w{ file1 file2 }
@spec.activate_platform(:osx).resources.should == %w{ file1 file2 }
end
it "returns the same list of xcconfig build settings for each platform" do
build_settings = { 'OTHER_LDFLAGS' => '-lObjC -lz -framework QuartzCore' }
@spec.activate_platform(:ios).xcconfig.should == build_settings
@spec.activate_platform(:osx).xcconfig.should == build_settings
end
it "returns the same list of compiler flags for each platform" do
compiler_flags = '-Wdeprecated-implementations -fobjc-arc'
@spec.activate_platform(:ios).compiler_flags.should == compiler_flags
@spec.activate_platform(:osx).compiler_flags.should == compiler_flags
end
it "returns the same list of dependencies for each platform" do
dependencies = %w{ JSONKit SSZipArchive }.map { |name| Pod::Dependency.new(name) }
@spec.activate_platform(:ios).dependencies.should == dependencies
@spec.activate_platform(:osx).dependencies.should == dependencies
end
end
describe "when platform specific values are given" do
before do
@spec = Pod::Spec.new do |s|
s.ios.source_files = 'file1'
s.osx.source_files = 'file1', 'file2'
s.ios.resource = 'file1'
s.osx.resources = 'file1', 'file2'
s.ios.xcconfig = { 'OTHER_LDFLAGS' => '-lObjC' }
s.osx.xcconfig = { 'OTHER_LDFLAGS' => '-lObjC -all_load' }
s.ios.framework = 'QuartzCore'
s.osx.frameworks = 'QuartzCore', 'CoreData'
s.ios.library = 'z'
s.osx.libraries = 'z', 'xml'
s.ios.compiler_flags = '-Wdeprecated-implementations'
s.osx.compiler_flags = '-Wfloat-equal'
s.requires_arc = true # does not take platform options, just here to check it's added to compiler_flags
s.ios.dependency 'JSONKit'
s.osx.dependency 'SSZipArchive'
s.ios.deployment_target = '4.0'
end
end
it "returns a different list of source files for each platform" do
@spec.activate_platform(:ios).source_files.should == %w{ file1 }
@spec.activate_platform(:osx).source_files.should == %w{ file1 file2 }
end
it "returns a different list of resources for each platform" do
@spec.activate_platform(:ios).resources.should == %w{ file1 }
@spec.activate_platform(:osx).resources.should == %w{ file1 file2 }
end
it "returns a different list of xcconfig build settings for each platform" do
@spec.activate_platform(:ios).xcconfig.should == { 'OTHER_LDFLAGS' => '-lObjC -lz -framework QuartzCore' }
@spec.activate_platform(:osx).xcconfig.should == { 'OTHER_LDFLAGS' => '-all_load -lObjC -lxml -lz -framework CoreData -framework QuartzCore' }
end
it "returns the list of the supported platfroms and deployment targets" do
@spec.available_platforms.count.should == 2
@spec.available_platforms.should.include? Pod::Platform.new(:osx)
@spec.available_platforms.should.include? Pod::Platform.new(:ios, '4.0')
end
it "returns the same list of compiler flags for each platform" do
@spec.activate_platform(:ios).compiler_flags.should == '-Wdeprecated-implementations -fobjc-arc'
@spec.activate_platform(:osx).compiler_flags.should == '-Wfloat-equal -fobjc-arc'
end
it "returns the same list of dependencies for each platform" do
@spec.activate_platform(:ios).dependencies.should == [Pod::Dependency.new('JSONKit')]
@spec.activate_platform(:osx).dependencies.should == [Pod::Dependency.new('SSZipArchive')]
end
end
end
require File.expand_path('../../spec_helper', __FILE__)
module Pod
describe Version do
it "returns wether or not it's a `bleeding edge' version" do
version = Version.new('1.2.3')
version.should.not.be.head
version.head = true
version.should.be.head
end
it "serializes to and from a string" do
version = Version.from_string('1.2.3')
version.to_s.should == '1.2.3'
version.should.not.be.head
version = Version.from_string('HEAD based on 1.2.3')
version.should.be.head
version.to_s.should == 'HEAD based on 1.2.3'
end
it "supports the previous way that a HEAD version was described" do
version = Version.from_string('HEAD from 1.2.3')
version.should.be.head
version.to_s.should == 'HEAD based on 1.2.3'
end
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