Commit 3ec34b5a authored by Fabio Pelosin's avatar Fabio Pelosin

Merge pull request #512 from CocoaPods/b0.15.0

0.15.0
parents 0cc4fd51 efba29b1
## 0.15.0 (unreleased)
[CocoaPods](https://github.com/CocoaPods/CocoaPods/compare/master...b0.15.0)
###### Enhancements
- Added a pre install hook to the Podfile and to root specifications.
[#486](https://github.com/CocoaPods/CocoaPods/issues/486)
- Support for `header_mappings_dir` attribute in subspecs.
- Refactored UI.
###### Bug fixes
- Improvements to the git cache that should be more robust.
[#517](https://github.com/CocoaPods/CocoaPods/issues/517)
## Master
[CocoaPods](https://github.com/CocoaPods/CocoaPods/compare/0.14.0...master)
......
......@@ -7,7 +7,6 @@ require 'rubygems'
#
# E.g. https://github.com/CocoaPods/CocoaPods/issues/398
unless Gem::Version::Requirement.new('>= 1.4.0').satisfied_by?(Gem::Version.new(Gem::VERSION))
require 'colored'
STDERR.puts "Your RubyGems version (#{Gem::VERSION}) is too old, please update with: `gem update --system`".red
exit 1
end
......@@ -41,6 +40,7 @@ module Pod
autoload :Source, 'cocoapods/source'
autoload :Spec, 'cocoapods/specification'
autoload :Specification, 'cocoapods/specification'
autoload :UI, 'cocoapods/user_interface'
autoload :Version, 'cocoapods/version'
autoload :Pathname, 'pathname'
......
......@@ -7,7 +7,6 @@ module Pod
autoload :List, 'cocoapods/command/list'
autoload :Linter, 'cocoapods/command/linter'
autoload :Outdated, 'cocoapods/command/outdated'
autoload :Presenter, 'cocoapods/command/presenter'
autoload :Push, 'cocoapods/command/push'
autoload :Repo, 'cocoapods/command/repo'
autoload :Search, 'cocoapods/command/search'
......@@ -74,6 +73,7 @@ module Pod
Setup.new(ARGV.new).run_if_needed
end
sub_command.run
UI.puts
rescue Interrupt
puts "[!] Cancelled".red
......@@ -145,24 +145,9 @@ module Pod
def update_spec_repos_if_necessary!
if @update_repo
print_title 'Updating Spec Repositories', true
Repo.new(ARGV.new(["update"])).run
end
end
def print_title(title, only_verbose = true)
if config.verbose?
puts "\n" + title.yellow
elsif !config.silent? && !only_verbose
puts title
end
end
def print_subtitle(title, only_verbose = false)
if config.verbose?
puts "\n" + title.green
elsif !config.silent? && !only_verbose
puts title
UI.section 'Updating Spec Repositories' do
Repo.new(ARGV.new(["update"])).run
end
end
end
end
......
......@@ -14,23 +14,26 @@ module Pod
end
def self.options
[["--update", "Run `pod repo update` before listing"]].concat(Presenter.options).concat(super)
[[
"--update", "Run `pod repo update` before listing",
"--stats", "Show additional stats (like GitHub watchers and forks)"
]].concat(super)
end
extend Executable
executable :git
def initialize(argv)
@update = argv.option('--update')
@new = argv.option('new')
@presenter = Presenter.new(argv)
@update = argv.option('--update')
@stats = argv.option('--stats')
@new = argv.option('new')
super unless argv.empty?
end
def list_all
sets = Source.all_sets
sets.each {|s| puts @presenter.describe(s)}
puts "\n#{sets.count} pods were found"
sets.each { |set| UI.pod(set, :name) }
UI.puts "\n#{sets.count} pods were found"
end
def list_new
......@@ -53,16 +56,18 @@ module Pod
days.reverse.each do |d|
sets = groups[d]
next unless sets
puts "\nPods added in the last #{d == 1 ? 'day' : "#{d} days"}".yellow
sets.sort_by {|s| creation_dates[s.name]}.each {|s| puts @presenter.describe(s)}
UI.section("\nPods added in the last #{"day".pluralize(d)}".yellow) do
sorted = sets.sort_by {|s| creation_dates[s.name]}
sorted.each { |set| UI.pod(set, (@stats ? :stats : :name)) }
end
end
end
def run
puts "\nUpdating Spec Repositories\n".yellow if @update && config.verbose?
Repo.new(ARGV.new(["update"])).run if @update
UI.section("\nUpdating Spec Repositories\n".yellow) do
Repo.new(ARGV.new(["update"])).run
end if @update && config.verbose?
@new ? list_new : list_all
puts
end
end
end
......
module Pod
class Command
class Presenter
def self.options
[["--stats", "Show additional stats (like GitHub watchers and forks)"]]
end
autoload :CocoaPod, 'cocoapods/command/presenter/cocoa_pod'
def initialize(argv)
@stats = argv.option('--stats')
end
def render(array)
result = "\n"
seats.each {|s| puts describe(s)}
result
end
def describe(set)
pod = CocoaPod.new(set)
result = "\n--> #{pod.name} (#{pod.versions})\n".green
result << wrap_string(pod.summary)
result << detail('Homepage', pod.homepage)
result << detail('Source', pod.source_url)
if @stats
result << detail('Pushed', pod.github_last_activity)
result << detail('Authors', pod.authors) if pod.authors =~ /,/
result << detail('Author', pod.authors) if pod.authors !~ /,/
result << detail('License', pod.license)
result << detail('Platform', pod.platform)
result << detail('Watchers', pod.github_watchers)
result << detail('Forks', pod.github_forks)
end
result << detail('Sub specs', pod.subspecs)
result
end
private
# adapted from http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/
def wrap_string(txt, col = 80, indentation = 4)
indent = ' ' * indentation
txt.strip.gsub(/(.{1,#{col}})( +|$)\n?|(.{#{col}})/, indent + "\\1\\3\n")
end
def detail(title, value)
return '' if !value
''.tap do |t|
t << " - #{title}:".ljust(16)
if value.class == Array
separator = "\n - "
t << separator << value.join(separator)
else
t << value.to_s << "\n"
end
end
end
end
end
end
require 'active_support/core_ext/array/conversions'
module Pod
class Command
class Presenter
class CocoaPod
attr_accessor :set
def initialize(set)
@set = set
end
# set information
def name
@set.name
end
def version
@set.versions.last
end
def versions
@set.versions.reverse.join(", ")
end
# specification information
def spec
@set.specification
end
def authors
spec.authors.keys.to_sentence
end
def homepage
spec.homepage
end
def description
spec.description
end
def summary
spec.summary
end
def source_url
spec.source.reject {|k,_| k == :commit || k == :tag }.values.first
end
def platform
spec.available_platforms.sort { |a,b| a.to_s.downcase <=> b.to_s.downcase }.join(' - ')
end
def license
spec.license[:type] if spec.license
end
# will return array of all subspecs (recursevly) or nil
def subspecs
(spec.recursive_subspecs.any? && spec.recursive_subspecs) || nil
end
# Statistics information
def creation_date
Pod::Specification::Statistics.instance.creation_date(@set)
end
def github_watchers
Pod::Specification::Statistics.instance.github_watchers(@set)
end
def github_forks
Pod::Specification::Statistics.instance.github_forks(@set)
end
def github_last_activity
distance_from_now_in_words(Pod::Specification::Statistics.instance.github_pushed_at(@set))
end
def ==(other)
self.class === other && @set == other.set
end
def eql?(other)
self.class === other && name.eql?(other.name)
end
def hash
name.hash
end
private
def distance_from_now_in_words(from_time)
return nil unless from_time
from_time = Time.parse(from_time)
to_time = Time.now
distance_in_days = (((to_time - from_time).abs)/60/60/24).round
case distance_in_days
when 0..7
"less than a week ago"
when 8..29
"#{distance_in_days} days ago"
when 30..45
"1 month ago"
when 46..365
"#{(distance_in_days.to_f / 30).round} months ago"
else
"more than a year ago"
end
end
end
end
end
end
......@@ -36,21 +36,20 @@ module Pod
update_repo
add_specs_to_repo
push_repo unless @local_only
puts
end
private
def update_repo
puts "Updating the `#{@repo}' repo\n".yellow unless config.silent
UI.puts "Updating the `#{@repo}' repo\n".yellow unless config.silent
# show the output of git even if not verbose
# TODO: use the `git!' and find a way to show the output in realtime.
Dir.chdir(repo_dir) { puts `git pull 2>&1` }
Dir.chdir(repo_dir) { UI.puts `git pull 2>&1` }
end
def push_repo
puts "\nPushing the `#{@repo}' repo\n".yellow unless config.silent
Dir.chdir(repo_dir) { puts `git push 2>&1` }
UI.puts "\nPushing the `#{@repo}' repo\n".yellow unless config.silent
Dir.chdir(repo_dir) { UI.puts `git push 2>&1` }
end
def repo_dir
......@@ -72,7 +71,7 @@ module Pod
end
def validate_podspec_files
puts "\nValidating specs".yellow unless config.silent
UI.puts "\nValidating specs".yellow unless config.silent
lint_argv = ["lint"]
lint_argv << "--only-errors" if @allow_warnings
lint_argv << "--silent" if config.silent
......@@ -83,7 +82,7 @@ module Pod
end
def add_specs_to_repo
puts "\nAdding the specs to the #{@repo} repo\n".yellow unless config.silent
UI.puts "\nAdding the specs to the #{@repo} repo\n".yellow unless config.silent
podspec_files.each do |spec_file|
spec = Pod::Specification.from_file(spec_file)
output_path = File.join(repo_dir, spec.name, spec.version.to_s)
......@@ -94,7 +93,7 @@ module Pod
else
message = "[Add] #{spec}"
end
puts " - #{message}" unless config.silent
UI.puts " - #{message}" unless config.silent
FileUtils.mkdir_p(output_path)
FileUtils.cp(Pathname.new(spec.name+'.podspec'), output_path)
......
......@@ -56,23 +56,25 @@ module Pod
end
def add
print_subtitle "Cloning spec repo `#{@name}' from `#{@url}'#{" (branch `#{@branch}')" if @branch}"
config.repos_dir.mkpath
Dir.chdir(config.repos_dir) { git!("clone '#{@url}' #{@name}") }
Dir.chdir(dir) { git!("checkout #{@branch}") } if @branch
check_versions(dir)
UI.section ("Cloning spec repo `#{@name}' from `#{@url}'#{" (branch `#{@branch}')" if @branch}") do
config.repos_dir.mkpath
Dir.chdir(config.repos_dir) { git!("clone '#{@url}' #{@name}") }
Dir.chdir(dir) { git!("checkout #{@branch}") } if @branch
check_versions(dir)
end
end
def update
dirs = @name ? [dir] : config.repos_dir.children.select {|c| c.directory?}
dirs.each do |dir|
print_subtitle "Updating spec repo `#{dir.basename}'"
Dir.chdir(dir) do
`git rev-parse >/dev/null 2>&1`
if $?.exitstatus.zero?
git!("pull")
else
puts(" Not a git repository") if config.verbose?
UI.section "Updating spec repo `#{dir.basename}'" do
Dir.chdir(dir) do
`git rev-parse >/dev/null 2>&1`
if $?.exitstatus.zero?
git!("pull")
else
UI.message "Not a git repository"
end
end
end
check_versions(dir)
......@@ -87,7 +89,7 @@ module Pod
end
dirs.each do |dir|
check_versions(dir)
puts "\nLinting spec repo `#{dir.realpath.basename}'\n".yellow
UI.puts "\nLinting spec repo `#{dir.realpath.basename}'\n".yellow
podspecs = dir.glob('**/*.podspec')
invalid_count = 0
......@@ -109,19 +111,19 @@ module Pod
end
if should_display
puts " -> ".send(color) << linter.spec_name
UI.puts " -> ".send(color) << linter.spec_name
print_messages('ERROR', linter.errors)
unless @only_errors
print_messages('WARN', linter.warnings)
print_messages('NOTE', linter.notes)
end
puts unless config.silent?
UI.puts unless config.silent?
end
end
puts "Analyzed #{podspecs.count} podspecs files.\n\n" unless config.silent?
UI.puts "Analyzed #{podspecs.count} podspecs files.\n\n" unless config.silent?
if invalid_count == 0
puts "All the specs passed validation.".green << "\n\n" unless config.silent?
UI.puts "All the specs passed validation.".green << "\n\n" unless config.silent?
else
raise Informative, "#{invalid_count} podspecs failed validation."
end
......@@ -130,7 +132,7 @@ module Pod
def print_messages(type, messages)
return if config.silent?
messages.each {|msg| puts " - #{type.ljust(5)} | #{msg}"}
messages.each {|msg| UI.puts " - #{type.ljust(5)} | #{msg}"}
end
def check_versions(dir)
......@@ -142,7 +144,7 @@ module Pod
"\n[!] The `#{dir.basename.to_s}' repo requires CocoaPods #{version_msg}\n".red +
"Update Cocoapods, or checkout the appropriate tag in the repo.\n\n"
end
puts "\nCocoapods #{versions['last']} is available.\n".green if has_update(versions) && config.new_version_message?
UI.puts "\nCocoapods #{versions['last']} is available.\n".green if has_update(versions) && config.new_version_message?
end
def self.compatible?(name)
......
......@@ -12,20 +12,22 @@ module Pod
end
def self.options
[["--full", "Search by name, summary, and description"]].concat(Presenter.options).concat(super)
[[
"--full", "Search by name, summary, and description",
"--stats", "Show additional stats (like GitHub watchers and forks)"
]].concat(super)
end
def initialize(argv)
@full_text_search = argv.option('--full')
@presenter = Presenter.new(argv)
@stats = argv.option('--stats')
@query = argv.shift_argument
super unless argv.empty? && @query
end
def run
sets = Source.search_by_name(@query.strip, @full_text_search)
sets.each {|s| puts @presenter.describe(s)}
puts
sets.each { |set| UI.pod(set, (@stats ? :stats : :normal)) }
end
end
end
......
......@@ -89,21 +89,22 @@ module Pod
end
def run
print_title "Setting up CocoaPods master repo"
if dir.exist?
set_master_repo_url
set_master_repo_branch
update_master_repo
else
add_master_repo
end
# Mainly so the specs run with submodule repos
if (dir + '.git/hooks').exist?
hook = dir + '.git/hooks/pre-commit'
hook.open('w') { |f| f << "#!/bin/sh\nrake lint" }
`chmod +x '#{hook}'`
UI.section "Setting up CocoaPods master repo" do
if dir.exist?
set_master_repo_url
set_master_repo_branch
update_master_repo
else
add_master_repo
end
# Mainly so the specs run with submodule repos
if (dir + '.git/hooks').exist?
hook = dir + '.git/hooks/pre-commit'
hook.open('w') { |f| f << "#!/bin/sh\nrake lint" }
`chmod +x '#{hook}'`
end
end
print_subtitle "Setup completed (#{push? ? "push" : "read-only"} access)"
UI.puts "Setup completed (#{push? ? "push" : "read-only"} access)".green
end
end
end
......
......@@ -59,17 +59,17 @@ module Pod
repo_id = repo_id_match[1]
data = github_data_for_template(repo_id)
data[:name] = @name_or_url if @url
puts semantic_versioning_notice(repo_id, data[:name]) if data[:version] == '0.0.1'
UI.puts semantic_versioning_notice(repo_id, data[:name]) if data[:version] == '0.0.1'
else
data = default_data_for_template(@name_or_url)
end
spec = spec_template(data)
(Pathname.pwd + "#{data[:name]}.podspec").open('w') { |f| f << spec }
puts "\nSpecification created at #{data[:name]}.podspec".green
UI.puts "\nSpecification created at #{data[:name]}.podspec".green
end
def lint
puts
UI.puts
invalid_count = 0
podspecs_to_lint.each do |podspec|
linter = Linter.new(podspec)
......@@ -93,19 +93,19 @@ module Pod
end
# This overwrites the previously printed text
puts " -> ".send(color) << linter.spec_name unless config.silent?
UI.puts " -> ".send(color) << linter.spec_name unless config.silent?
print_messages('ERROR', linter.errors)
print_messages('WARN', linter.warnings)
print_messages('NOTE', linter.notes)
puts unless config.silent?
UI.puts unless config.silent?
end
puts "Analyzed #{podspecs_to_lint.count} podspecs files.\n\n" unless config.silent?
UI.puts "Analyzed #{podspecs_to_lint.count} podspecs files.\n\n" unless config.silent?
count = podspecs_to_lint.count
if invalid_count == 0
lint_passed_message = count == 1 ? "#{podspecs_to_lint.first.basename} passed validation." : "All the specs passed validation."
puts lint_passed_message.green << "\n\n" unless config.silent?
UI.puts lint_passed_message.green << "\n\n" unless config.silent?
else
raise Informative, count == 1 ? "The spec did not pass validation." : "#{invalid_count} out of #{count} specs failed validation."
end
......@@ -116,7 +116,7 @@ module Pod
def print_messages(type, messages)
return if config.silent?
messages.each {|msg| puts " - #{type.ljust(5)} | #{msg}"}
messages.each {|msg| UI.puts " - #{type.ljust(5)} | #{msg}"}
end
def podspecs_to_lint
......
......@@ -193,14 +193,15 @@ module Pod
class GitSource < AbstractExternalSource
def copy_external_source_into_sandbox(sandbox, platform)
puts "-> Pre-downloading: '#{name}'" unless config.silent?
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
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
......@@ -216,10 +217,11 @@ module Pod
# can be http, file, etc
class PodspecSource < AbstractExternalSource
def copy_external_source_into_sandbox(sandbox, _)
puts "-> Fetching podspec for `#{name}' from: #{@params[:podspec]}" unless config.silent?
path = @params[:podspec]
path = Pathname.new(path).expand_path if path.start_with?("~")
open(path) { |io| store_podspec(sandbox, io.read) }
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
......
......@@ -7,33 +7,33 @@ module Pod
class Downloader
class Git < Downloader
include Config::Mixin
executable :git
MAX_CACHE_SIZE = 500
def download
create_cache unless cache_exist?
puts '-> Cloning git repo' if config.verbose?
if options[:tag]
download_tag
elsif options[:branch]
download_branch
elsif options[:commit]
download_commit
else
download_head
UI.section(' > Cloning git repo', '', 1) do
if options[:tag]
download_tag
elsif options[:branch]
download_branch
elsif options[:commit]
download_commit
else
download_head
end
Dir.chdir(target_path) { git! "submodule update --init" } if options[:submodules]
end
Dir.chdir(target_path) { git! "submodule update --init" } if options[:submodules]
prune_cache
end
def create_cache
puts "-> Creating cache git repo (#{cache_path})" if config.verbose?
UI.section(" > Creating cache git repo (#{cache_path})",'',1)
cache_path.rmtree if cache_path.exist?
cache_path.mkpath
clone(url, cache_path)
git! %Q|clone --mirror "#{url}" "#{cache_path}"|
end
def prune_cache
......@@ -42,7 +42,7 @@ module Pod
repos = Pathname.new(caches_dir).children.select { |c| c.directory? }.sort_by(&:ctime)
while caches_size >= MAX_CACHE_SIZE && !repos.empty?
dir = repos.shift
puts '->'.yellow << " Removing git cache for `#{origin_url(dir)}'" if config.verbose?
UI.message "#{'->'.yellow} Removing git cache for `#{origin_url(dir)}'"
dir.rmtree
end
end
......@@ -74,13 +74,17 @@ module Pod
end
def update_cache
puts "-> Updating cache git repo (#{cache_path})" if config.verbose?
Dir.chdir(cache_path) do
git! "reset --hard HEAD"
git! "clean -d -x -f"
git! "pull origin master"
git! "fetch"
git! "fetch --tags"
UI.section(" > Updating cache git repo (#{cache_path})",'',1) do
Dir.chdir(cache_path) do
if git("config core.bare").chomp == "true"
git! "remote update"
else
git! "reset --hard HEAD"
git! "clean -d -x -f"
git! "pull origin master"
git! "fetch --tags"
end
end
end
end
......@@ -97,7 +101,7 @@ module Pod
end
def branch_exists?(branch)
Dir.chdir(cache_path) { git "branch -r | grep #{branch}$" } # check for remote branch and do suffix matching ($ anchor)
Dir.chdir(cache_path) { git "branch --all | grep #{branch}$" } # check for remote branch and do suffix matching ($ anchor)
$? == 0
end
......@@ -144,12 +148,14 @@ module Pod
git! "remote add upstream '#{@url}'" # we need to add the original url, not the cache url
git! "fetch -q upstream" # refresh the branches
git! "checkout --track -b activated-pod-commit upstream/#{options[:branch]}" # create a new tracking branch
puts "Just downloaded and checked out branch: #{options[:branch]} from upstream #{clone_url}" if config.verbose?
UI.message("Just downloaded and checked out branch: #{options[:branch]} from upstream #{clone_url}")
end
end
def clone(from, to)
git! %Q|clone "#{from}" "#{to}"|
UI.section(" > Cloning to Pods folder",'',1) do
git! %Q|clone "#{from}" "#{to}"|
end
end
end
......
......@@ -16,9 +16,10 @@ module Pod
def download
@filename = filename_with_type type
@download_path = target_path + @filename
download_file @download_path
extract_with_type @download_path, type
UI.section(' > Downloading from HTTP', '', 3) do
download_file @download_path
extract_with_type @download_path, type
end
end
def type
......
......@@ -4,10 +4,12 @@ module Pod
executable :hg
def download
if options[:revision]
download_revision
else
download_head
UI.section(' > Cloning mercurial repo', '', 3) do
if options[:revision]
download_revision
else
download_head
end
end
end
......
......@@ -8,11 +8,15 @@ module Pod
end
def download
svn! %|export "#{reference_url}" "#{target_path}"|
UI.section(' > Exporting subversion repo', '', 3) do
svn! %|export "#{reference_url}" "#{target_path}"|
end
end
def download_head
svn! %|export "#{trunk_url}" "#{target_path}"|
UI.section(' > Exporting subversion repo', '', 3) do
svn! %|export "#{trunk_url}" "#{target_path}"|
end
end
def reference_url
......
......@@ -3,12 +3,14 @@ require 'open4'
module Pod
module Executable
class Indenter < ::Array
include Config::Mixin
attr_accessor :indent
attr_accessor :io
def initialize(io = nil, indent = ' ')
def initialize(io = nil)
@io = io
@indent = indent
@indent = ' ' * UI.indentation_level
end
def <<(value)
......@@ -27,20 +29,19 @@ module Pod
end
full_command = "#{bin} #{command}"
if Config.instance.verbose?
puts " $ #{full_command}"
UI.message("$ #{full_command}")
stdout, stderr = Indenter.new(STDOUT), Indenter.new(STDERR)
else
stdout, stderr = Indenter.new, Indenter.new
end
status = Open4.spawn(full_command, :stdout => stdout, :stderr => stderr, :status => true)
# TODO not sure that we should be silent in case of a failure.
output = stdout.join("\n") + stderr.join("\n") # TODO will this suffice?
unless status.success?
if should_raise
raise Informative, "#{name} #{command}\n\n#{output}"
else
puts((Config.instance.verbose? ? ' ' : '') << "[!] Failed: #{full_command}".red) unless Config.instance.silent?
UI.message("[!] Failed: #{full_command}".red)
end
end
output
......@@ -54,7 +55,6 @@ module Pod
send(base_method, command, true)
end
private name
end
end
......
......@@ -49,23 +49,21 @@ module Pod
def install_dependencies!
pods.sort_by { |pod| pod.top_specification.name.downcase }.each do |pod|
should_install = @resolver.should_install?(pod.top_specification.name) || !pod.exists?
unless config.silent?
marker = config.verbose ? "\n-> ".green : ''
puts marker << ( should_install ? "Installing #{pod}".green : "Using #{pod}" )
end
if should_install
unless pod.downloaded?
pod.implode
download_pod(pod)
UI.section("Installing #{pod}".green, "-> ".green) do
unless pod.downloaded?
pod.implode
download_pod(pod)
end
# The docs need to be generated before cleaning because the
# documentation is created for all the subspecs.
generate_docs(pod)
# Here we clean pod's that just have been downloaded or have been
# pre-downloaded in AbstractExternalSource#specification_from_sandbox.
pod.clean! if config.clean?
end
# The docs need to be generated before cleaning because the
# documentation is created for all the subspecs.
generate_docs(pod)
# Here we clean pod's that just have been downloaded or have been
# pre-downloaded in AbstractExternalSource#specification_from_sandbox.
pod.clean! if config.clean?
else
UI.section("Using #{pod}", "-> ".green)
end
end
end
......@@ -89,10 +87,10 @@ module Pod
def generate_docs(pod)
doc_generator = Generator::Documentation.new(pod)
if ( config.generate_docs? && !doc_generator.already_installed? )
puts "-> Installing documentation" if config.verbose?
UI.section " > Installing documentation"
doc_generator.generate(config.doc_install?)
else
puts "-> Using existing documentation" if config.verbose?
UI.section " > Using existing documentation"
end
end
......@@ -100,47 +98,61 @@ module Pod
#
def remove_deleted_dependencies!
resolver.removed_pods.each do |pod_name|
marker = config.verbose ? "\n-> ".red : ''
path = sandbox.root + pod_name
puts marker << "Removing #{pod_name}".red
path.rmtree if path.exist?
UI.section("Removing #{pod_name}", "-> ".red) do
path = sandbox.root + pod_name
path.rmtree if path.exist?
end
end
end
def install!
@sandbox.prepare_for_install
UI.section "Resolving dependencies of #{UI.path @podfile.defined_in_file}" do
specs_by_target
end
print_title "Resolving dependencies of: #{@podfile.defined_in_file}"
specs_by_target
UI.section "Removing deleted dependencies" do
remove_deleted_dependencies!
end unless resolver.removed_pods.empty?
print_title "Removing deleted dependencies" unless resolver.removed_pods.empty?
remove_deleted_dependencies!
UI.section "Downloading dependencies" do
install_dependencies!
end
print_title "Installing dependencies"
install_dependencies!
UI.section "Generating support files" do
UI.message "- Running pre install hooks" do
run_pre_install_hooks
end
print_title("Generating support files\n", false)
target_installers.each do |target_installer|
pods_for_target = pods_by_target[target_installer.target_definition]
target_installer.install!(pods_for_target, @sandbox)
acknowledgements_path = target_installer.target_definition.acknowledgements_path
Generator::Acknowledgements.new(target_installer.target_definition,
pods_for_target).save_as(acknowledgements_path)
generate_dummy_source(target_installer)
end
UI.message"- Installing targets" do
generate_target_support_files
end
puts "- Running post install hooks" if config.verbose?
# Post install hooks run _before_ saving of project, so that they can alter it before saving.
run_post_install_hooks
UI.message "- Running post install hooks" do
# Post install hooks run _before_ saving of project, so that they can alter it before saving.
run_post_install_hooks
end
UI.message "- Writing Xcode project file to #{UI.path @sandbox.project_path}" do
project.save_as(@sandbox.project_path)
end
puts "- Writing Xcode project file to `#{@sandbox.project_path}'" if config.verbose?
project.save_as(@sandbox.project_path)
UI.message "- Writing lockfile in #{UI.path config.project_lockfile}" do
@lockfile = Lockfile.generate(@podfile, specs_by_target.values.flatten)
@lockfile.write_to_disk(config.project_lockfile)
end
end
puts "- Writing lockfile in `#{config.project_lockfile}'\n\n" if config.verbose?
@lockfile = Lockfile.generate(@podfile, specs_by_target.values.flatten)
@lockfile.write_to_disk(config.project_lockfile)
UserProjectIntegrator.new(@podfile).integrate! if config.integrate_targets?
end
UserProjectIntegrator.new(@podfile).integrate! if config.integrate_targets?
def run_pre_install_hooks
pods_by_target.each do |target_definition, pods|
pods.each do |pod|
pod.top_specification.pre_install(pod, target_definition)
end
end
@podfile.pre_install!(self)
end
def run_post_install_hooks
......@@ -151,10 +163,20 @@ module Pod
spec.post_install(target_installer)
end
end
@podfile.post_install!(self)
end
def generate_target_support_files
target_installers.each do |target_installer|
pods_for_target = pods_by_target[target_installer.target_definition]
target_installer.install!(pods_for_target, @sandbox)
acknowledgements_path = target_installer.target_definition.acknowledgements_path
Generator::Acknowledgements.new(target_installer.target_definition,
pods_for_target).save_as(acknowledgements_path)
generate_dummy_source(target_installer)
end
end
def generate_dummy_source(target_installer)
class_name_identifier = target_installer.target_definition.label
dummy_source = Generator::DummySource.new(class_name_identifier)
......@@ -198,15 +220,5 @@ module Pod
end
result
end
private
def print_title(title, only_verbose = true)
if config.verbose?
puts "\n" + title.yellow
elsif !config.silent? && !only_verbose
puts title
end
end
end
end
......@@ -97,20 +97,24 @@ module Pod
end
def create_files(pods, sandbox)
if @podfile.generate_bridge_support?
bridge_support_metadata_path = sandbox.root + @target_definition.bridge_support_name
puts "- Generating BridgeSupport metadata file at `#{bridge_support_metadata_path}'" if config.verbose?
bridge_support_metadata_path = sandbox.root + @target_definition.bridge_support_name
UI.message "- Generating BridgeSupport metadata file at #{UI.path bridge_support_metadata_path}" do
bridge_support_generator_for(pods, sandbox).save_as(bridge_support_metadata_path)
copy_resources_script_for(pods).resources << @target_definition.bridge_support_name
end if @podfile.generate_bridge_support?
UI.message "- Generating xcconfig file at #{UI.path(sandbox.root + @target_definition.xcconfig_name)}" do
xcconfig.save_as(sandbox.root + @target_definition.xcconfig_name)
@target_definition.xcconfig = xcconfig
end
UI.message "- Generating prefix header at #{UI.path(sandbox.root + @target_definition.prefix_header_name)}" do
save_prefix_header_as(sandbox.root + @target_definition.prefix_header_name, pods)
end
UI.message "- Generating copy resources script at #{UI.path(sandbox.root + @target_definition.copy_resources_script_name)}" do
copy_resources_script_for(pods).save_as(sandbox.root + @target_definition.copy_resources_script_name)
end
puts "- Generating xcconfig file at `#{sandbox.root + @target_definition.xcconfig_name}'" if config.verbose?
xcconfig.save_as(sandbox.root + @target_definition.xcconfig_name)
@target_definition.xcconfig = xcconfig
puts "- Generating prefix header at `#{sandbox.root + @target_definition.prefix_header_name}'" if config.verbose?
save_prefix_header_as(sandbox.root + @target_definition.prefix_header_name, pods)
puts "- Generating copy resources script at `#{sandbox.root + @target_definition.copy_resources_script_name}'" if config.verbose?
copy_resources_script_for(pods).save_as(sandbox.root + @target_definition.copy_resources_script_name)
end
private
......
......@@ -51,7 +51,7 @@ module Pod
workspace << project_path unless workspace.include?(project_path)
end
unless workspace_path.exist? || config.silent?
puts "[!] From now on use `#{workspace_path.basename}'."
UI.notice "From now on use `#{workspace_path.basename}'."
end
workspace.save_as(workspace_path)
end
......@@ -69,20 +69,30 @@ module Pod
"#<#{self.class} for target `#{@target_definition.label}'>"
end
# Integrates the user project targets. Only the targets that do **not**
# already have the Pods library in their frameworks build phase are
# processed.
#
# @return [void]
#
def integrate!
return if targets.empty?
unless Config.instance.silent?
puts "-> Integrating `#{@target_definition.lib_name}' into #{'target'.pluralize(targets.size)} " \
"`#{targets.map(&:name).to_sentence}' of Xcode project `#{user_project_path.basename}'.".green
UI.section ("Integrating `#{@target_definition.lib_name}' into #{'target'.pluralize(targets.size)} " \
"`#{targets.map(&:name).to_sentence}' of Xcode project #{UI.path user_project_path}.") do
add_xcconfig_base_configuration
add_pods_library
add_copy_resources_script_phase
user_project.save_as(@target_definition.user_project.path)
end
add_xcconfig_base_configuration
add_pods_library
add_copy_resources_script_phase
user_project.save_as(@target_definition.user_project.path)
end
# @return [Pathname] the path of the user project.
#
# @raises If the path doesn't exits.
#
# @raises If the project is implicit and there are multiple projects.
#
def user_project_path
if path = @target_definition.user_project.path
unless path.exist?
......@@ -96,6 +106,8 @@ module Pod
end
end
# @return [Xcodeproj::Project] Returns the project of the user.
#
def user_project
@user_project ||= Xcodeproj::Project.new(user_project_path)
end
......@@ -152,13 +164,10 @@ module Pod
config.base_configuration = xcconfig
end
unless config.silent?
config_build_names_by_overriden_key.each do |key, config_build_names|
name = "#{target.attributes["name"]} [#{config_build_names.join(' - ')}]"
puts "\n[!] The target `#{name}' overrides the `#{key}' build setting defined in `#{@target_definition.xcconfig_relative_path}'.".yellow
puts " - Use the `$(inherited)' flag, or"
puts " - Remove the build settings from the target."
end
config_build_names_by_overriden_key.each do |key, config_build_names|
name = "#{target.attributes["name"]} [#{config_build_names.join(' - ')}]"
actions = [ "Use the `$(inherited)' flag, or", "Remove the build settings from the target." ]
UI.warn("The target `#{name}' overrides the `#{key}' build setting defined in `#{@target_definition.xcconfig_relative_path}'.", actions)
end
end
end
......
......@@ -432,9 +432,10 @@ module Pod
mappings = {}
files_by_spec.each do |spec, paths|
paths = paths - headers_excluded_from_search_paths
dir = spec.header_dir ? (headers_sandbox + spec.header_dir) : headers_sandbox
paths.each do |from|
from_relative = from.relative_path_from(root)
to = headers_sandbox + (spec.header_dir) + spec.copy_header_mapping(from_relative)
to = dir + spec.copy_header_mapping(from_relative)
(mappings[to.dirname] ||= []) << from
end
end
......
......@@ -472,6 +472,17 @@ module Pod
@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.
#
......@@ -532,6 +543,10 @@ module Pod
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
......
......@@ -66,36 +66,26 @@ module Pod
@specs_by_target = {}
@pods_from_external_sources = []
@pods_to_lock = []
@log_indent = 0
if @lockfile
puts "\nFinding added, modified or removed dependencies:".green if config.verbose?
@pods_by_state = @lockfile.detect_changes_with_podfile(podfile)
if config.verbose?
UI.section "Finding added, modified or removed dependencies:" do
marks = {:added => "A".green, :changed => "M".yellow, :removed => "R".red, :unchanged => "-" }
@pods_by_state.each do |symbol, pod_names|
case symbol
when :added
mark = "A".green
when :changed
mark = "M".yellow
when :removed
mark = "R".red
when :unchanged
mark = "-"
end
pod_names.each do |pod_name|
puts " #{mark} " << pod_name
UI.message("#{marks[symbol]} #{pod_name}", '',2)
end
end
end
end if config.verbose?
@pods_to_lock = (lockfile.pods_names - @pods_by_state[:added] - @pods_by_state[:changed] - @pods_by_state[:removed]).uniq
end
@podfile.target_definitions.values.each do |target_definition|
puts "\nResolving dependencies for target `#{target_definition.name}' (#{target_definition.platform}):".green if config.verbose?
@loaded_specs = []
find_dependency_specs(@podfile, target_definition.dependencies, target_definition)
@specs_by_target[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name)
UI.section "Resolving dependencies for target `#{target_definition.name}' (#{target_definition.platform}):" do
@loaded_specs = []
find_dependency_specs(@podfile, target_definition.dependencies, target_definition)
@specs_by_target[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name)
end
end
@cached_specs.values.sort_by(&:name)
......@@ -187,32 +177,30 @@ module Pod
# @return [void]
#
def find_dependency_specs(dependent_specification, dependencies, target_definition)
@log_indent += 1
dependencies.each do |dependency|
# Replace the dependency with a more specific one if the pod is already installed.
if !update_mode && @pods_to_lock.include?(dependency.name)
dependency = lockfile.dependency_for_installed_pod_named(dependency.name)
end
puts ' ' * @log_indent + "- #{dependency}" if config.verbose?
set = find_cached_set(dependency, target_definition.platform)
set.required_by(dependency, dependent_specification.to_s)
# Ensure we don't resolve the same spec twice for one target
unless @loaded_specs.include?(dependency.name)
spec = set.specification_by_name(dependency.name)
@pods_from_external_sources << spec.pod_name if dependency.external?
@loaded_specs << spec.name
@cached_specs[spec.name] = spec
# Configure the specification
spec.activate_platform(target_definition.platform)
spec.version.head = dependency.head?
# And recursively load the dependencies of the spec.
find_dependency_specs(spec, spec.dependencies, target_definition) if spec.dependencies
UI.message("- #{dependency}", '', 2) do
set = find_cached_set(dependency, target_definition.platform)
set.required_by(dependency, dependent_specification.to_s)
# Ensure we don't resolve the same spec twice for one target
unless @loaded_specs.include?(dependency.name)
spec = set.specification_by_name(dependency.name)
@pods_from_external_sources << spec.pod_name if dependency.external?
@loaded_specs << spec.name
@cached_specs[spec.name] = spec
# Configure the specification
spec.activate_platform(target_definition.platform)
spec.version.head = dependency.head?
# And recursively load the dependencies of the spec.
find_dependency_specs(spec, spec.dependencies, target_definition) if spec.dependencies
end
validate_platform(spec || @cached_specs[dependency.name], target_definition)
end
validate_platform(spec || @cached_specs[dependency.name], target_definition)
end
@log_indent -= 1
end
# Ensures that a spec is compatible with platform of a target.
......
......@@ -54,6 +54,7 @@ module Pod
@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
......@@ -88,6 +89,7 @@ module Pod
# 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
......@@ -96,6 +98,17 @@ module Pod
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
......@@ -240,13 +253,11 @@ module Pod
### Attributes **with** multiple platform support
# @todo allow for subspecs
# @todo allow for subspecs?
#
top_attr_accessor :header_mappings_dir, lambda { |file| Pathname.new(file) } # If not provided the headers files are flattened
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 :public_header_files, lambda {|value, current| pattern_list(value) }
pltf_chained_attr_accessor :resources, lambda {|value, current| pattern_list(value) }
......@@ -262,7 +273,6 @@ module Pod
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
......@@ -271,14 +281,7 @@ module Pod
# @param [Bool] Wether the source files require ARC.
#
platform_attr_writer :requires_arc
def requires_arc
requires_arc = @requires_arc[active_platform]
if requires_arc.nil?
requires_arc = @parent ? @parent.requires_arc : false
end
requires_arc
end
pltf_first_defined_attr_reader :requires_arc
# @!method header_dir=
#
......@@ -287,17 +290,13 @@ module Pod
#
# @param [String] The headers directory.
#
platform_attr_writer :header_dir, lambda { |dir, _| Pathname.new(dir) }
platform_attr_writer :header_dir, lambda { |dir, _| Pathname.new(dir) }
pltf_first_defined_attr_reader :header_dir
# @abstract (see #header_dir=)
#
# @return [Pathname] The headers directory.
# If not provided the headers files are flattened
#
# @note If no value is provided it returns an empty {Pathname}.
#
def header_dir
@header_dir[active_platform] || (@parent.header_dir if @parent) || Pathname.new('')
end
platform_attr_writer :header_mappings_dir, lambda { |file, _| Pathname.new(file) }
pltf_first_defined_attr_reader :header_mappings_dir
# @!method xcconfig=
#
......@@ -316,13 +315,14 @@ module Pod
end
def recursive_compiler_flags
@parent ? @parent.recursive_compiler_flags | @compiler_flags[active_platform] : @compiler_flags[active_platform]
end
def compiler_flags
if @parent
flags = [@parent.compiler_flags]
else
flags = [requires_arc ? ' -fobjc-arc' : '']
end
(flags + @compiler_flags[active_platform].clone).join(' ')
flags = recursive_compiler_flags.dup
flags << '-fobjc-arc' if requires_arc
flags.join(' ')
end
platform_attr_writer :compiler_flags, lambda {|value, current| current << value }
......@@ -430,20 +430,36 @@ module Pod
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 pre_install(pod, target_definition)
# Dir.chdir(pod.root){ `sh make.sh` }
# end
# end
def pre_install(pod, target_definition)
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::Target instance for the current
# target.) Override this to, for instance, add to the prefix header:
# 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)
# prefix_header = config.project_pods_root + target.prefix_header_filename
# 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)
def post_install(target_installer)
end
def podfile?
......
module Pod
require 'colored'
class UserInterface
autoload :UIPod, 'cocoapods/user_interface/ui_pod'
@title_colors = %w|yellow green|
@title_level = 0
@indentation_level = 2
@treat_titles_as_messages = false
class << self
include Config::Mixin
attr_accessor :indentation_level, :title_level
# Prints a title taking an optional verbose prefix and
# a relative indentation valid for the UI action in the passed
# block.
#
# In verbose mode titles are printed with a color according
# to their level. In normal mode titles are printed only if
# they have nesting level smaller than 2.
#
# TODO: refactor to title (for always visible titles like search)
# and sections (titles that reppresent collapsible sections).
#
def section(title, verbose_prefix = '', relative_indentation = 0)
if config.verbose?
title(title, verbose_prefix, relative_indentation)
elsif title_level < 2
puts title
end
self.indentation_level += relative_indentation
self.title_level += 1
yield if block_given?
self.indentation_level -= relative_indentation
self.title_level -= 1
end
# A title oposed to a section is always visible
#
def title(title, verbose_prefix = '', relative_indentation = 2)
if(@treat_titles_as_messages)
message(title, verbose_prefix)
else
title = verbose_prefix + title if config.verbose?
title = "\n#{title}" if @title_level < 2
if (color = @title_colors[@title_level])
title = title.send(color)
end
puts "#{title}"
end
self.indentation_level += relative_indentation
self.title_level += 1
yield if block_given?
self.indentation_level -= relative_indentation
self.title_level -= 1
end
# def title(title, verbose_prefix = '', relative_indentation = 2)
# end
# Prints a verbose message taking an optional verbose prefix and
# a relative indentation valid for the UI action in the passed
# block.
#
# TODO: clean interface.
#
def message(message, verbose_prefix = '', relative_indentation = 2)
message = verbose_prefix + message if config.verbose?
puts_indented message if config.verbose?
self.indentation_level += relative_indentation
yield if block_given?
self.indentation_level -= relative_indentation
end
# Prints an info to the user. The info is always displayed.
# It respects the current indentation level only in verbose
# mode.
#
# Any title printed in the optional block is treated as a message.
#
def info(message)
indentation = config.verbose? ? self.indentation_level : 0
indented = wrap_string(message, " " * indentation)
puts(indented)
self.indentation_level += 2
@treat_titles_as_messages = true
yield if block_given?
@treat_titles_as_messages = false
self.indentation_level -= 2
end
# Prints an important message to the user.
#
# @param [String] message The message to print.
#
# return [void]
#
def notice(message)
puts("\n[!] #{message}".green)
end
# Prints an important warning to the user optionally followed by actions
# that the user should take.
#
# @param [String] message The message to print.
# @param [Actions] actions The actions that the user should take.
#
# return [void]
#
def warn(message, actions)
puts("\n[!] #{message}".yellow)
actions.each do |action|
indented = wrap_string(action, " - ")
puts(indented)
end
end
# Returns a string containing relative location of a path from the Podfile.
# The returned path is quoted. If the argument is nit it returns the
# empty string.
#
def path(pathname)
if pathname
"`./#{pathname.relative_path_from(config.project_podfile.dirname || Pathname.pwd)}'"
else
''
end
end
# Prints the textual repprensentation of a given set.
#
def pod(set, mode = :normal)
if mode == :name
puts_indented set.name
else
pod = UIPod.new(set)
title("\n-> #{pod.name} (#{pod.version})".green, '', 3) do
puts_indented pod.summary
labeled('Homepage', pod.homepage)
labeled('Source', pod.source_url)
labeled('Versions', pod.versions) unless set.versions.count == 1
if mode == :stats
labeled('Pushed', pod.github_last_activity)
labeled('Authors', pod.authors) if pod.authors =~ /,/
labeled('Author', pod.authors) if pod.authors !~ /,/
labeled('License', pod.license)
labeled('Platform', pod.platform)
labeled('Watchers', pod.github_watchers)
labeled('Forks', pod.github_forks)
end
labeled('Sub specs', pod.subspecs)
end
end
end
# Prints a message with a label.
#
def labeled(label, value)
if value
''.tap do |t|
t << " - #{label}:".ljust(16)
if value.is_a?(Array)
separator = "\n - "
puts_indented t << separator << value.join(separator)
else
puts_indented t << value.to_s << "\n"
end
end
end
end
# @!group Basic printing
# Prints a message unless config is silent.
#
def puts(message = '')
super(message) unless config.silent?
end
# Prints a message respecting the current indentation level and
# wrapping it to the terminal width if necessary.
#
def puts_indented(message = '')
indented = wrap_string(message, " " * self.indentation_level)
puts(indented)
end
private
# @!group Helpers
# Wraps a string taking into account the width of the terminal and an
# option indent. Adapted from http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/
#
# @param [String] txt The string to wrap
#
# @param [String] indent The string to use to indent the result.
#
# @return [String] The formatted string.
#
def wrap_string(txt, indent = '')
width = `stty size`.split(' ')[1].to_i - indent.length
txt.strip.gsub(/(.{1,#{width}})( +|$)\n?|(.{#{width}})/, indent + "\\1\\3\n")
end
end
end
UI = UserInterface
end
require 'active_support/core_ext/array/conversions'
module Pod
class UserInterface
class UIPod
attr_accessor :set
def initialize(set)
@set = set
end
# set information
def name
@set.name
end
def version
@set.versions.last
end
def versions
@set.versions.reverse.join(", ")
end
# specification information
def spec
@set.specification
end
def authors
spec.authors.keys.to_sentence
end
def homepage
spec.homepage
end
def description
spec.description
end
def summary
spec.summary
end
def source_url
spec.source.reject {|k,_| k == :commit || k == :tag }.values.first
end
def platform
spec.available_platforms.sort { |a,b| a.to_s.downcase <=> b.to_s.downcase }.join(' - ')
end
def license
spec.license[:type] if spec.license
end
# will return array of all subspecs (recursevly) or nil
def subspecs
(spec.recursive_subspecs.any? && spec.recursive_subspecs) || nil
end
# Statistics information
def creation_date
Pod::Specification::Statistics.instance.creation_date(@set)
end
def github_watchers
Pod::Specification::Statistics.instance.github_watchers(@set)
end
def github_forks
Pod::Specification::Statistics.instance.github_forks(@set)
end
def github_last_activity
distance_from_now_in_words(Pod::Specification::Statistics.instance.github_pushed_at(@set))
end
def ==(other)
self.class === other && @set == other.set
end
def eql?(other)
self.class === other && name.eql?(other.name)
end
def hash
name.hash
end
private
def distance_from_now_in_words(from_time)
return nil unless from_time
from_time = Time.parse(from_time)
to_time = Time.now
distance_in_days = (((to_time - from_time).abs)/60/60/24).round
case distance_in_days
when 0..7
"less than a week ago"
when 8..29
"#{distance_in_days} days ago"
when 30..45
"1 month ago"
when 46..365
"#{(distance_in_days.to_f / 30).round} months ago"
else
"more than a year ago"
end
end
end
end
end
......@@ -31,7 +31,7 @@ describe "Pod::Command::List" do
/MagicalRecord/,
/A2DynamicDelegate/,
/\d+ pods were found/
].each { |regex| list.output.should =~ regex }
].each { |regex| Pod::UI.output.should =~ regex }
end
it "returns the new pods" do
......@@ -46,7 +46,7 @@ describe "Pod::Command::List" do
'FileMD5Hash',
'cocoa-oauth',
'iRate'
].each {|s| list.output.should.include s }
].each {|s| Pod::UI.output.should.include s }
end
end
......
......@@ -67,8 +67,8 @@ describe Pod::Command::Push do
cmd.expects(:validate_podspec_files).returns(true)
Dir.chdir(temporary_directory) { cmd.run }
cmd.output.should.include('[Add] PushTest (1.4)')
cmd.output.should.include('[Fix] JSONKit (1.4)')
Pod::UI.output.should.include('[Add] PushTest (1.4)')
Pod::UI.output.should.include('[Fix] JSONKit (1.4)')
git('upstream', 'checkout test') # checkout because test because is it the branch used in the specs.
(@upstream.dir + 'PushTest/1.4/PushTest.podspec').read.should.include('PushTest')
......
......@@ -57,7 +57,7 @@ describe "Pod::Command::Repo" do
it "lints a repo" do
cmd = command('repo', 'lint', 'master')
lambda { cmd.run }.should.raise Pod::Informative
cmd.output.should.include "Missing license type"
Pod::UI.output.should.include "Missing license type"
end
end
......@@ -104,7 +104,7 @@ describe "Pod::Command::Repo" do
write_version_file({'last' => "999.0.0"})
cmd = command('repo', 'update')
cmd.check_versions(versions_file.dirname)
cmd.output.should.include "Cocoapods 999.0.0 is available"
Pod::UI.output.should.include "Cocoapods 999.0.0 is available"
end
it "has a class method that returns if a repo is supported" do
......
......@@ -32,9 +32,9 @@ describe "Pod::Command::Setup" do
it "creates the local spec-repos directory and creates a clone of the `master' repo" do
output = run_command('setup')
git_config('master', 'remote.origin.url').should == fixture('spec-repos/master').to_s
output.should.include "Setup completed"
output.should.not.include "push"
git_config('master', 'remote.origin.url').should == fixture('spec-repos/master').to_s
end
it "preserves push access for the `master' repo" do
......@@ -47,9 +47,8 @@ describe "Pod::Command::Setup" do
it "can run if needed" do
output = run_command('setup')
output.should.include "Setup completed"
command = command('setup')
command.run_if_needed
command.output.should == nil
Pod::UI.output = ''
command('setup').run_if_needed
Pod::UI.output.should == ''
end
end
......@@ -107,7 +107,7 @@ describe "Pod::Command::Spec#lint" do
Dir.chdir(fixture('spec-repos') + 'master/JSONKit/1.4/') do
cmd = command('spec', 'lint', '--quick', '--only-errors')
cmd.run
cmd.output.should.include "passed validation"
Pod::UI.output.should.include "passed validation"
end
end
......@@ -129,12 +129,12 @@ describe "Pod::Command::Spec#lint" do
it "lints a givent podspec" do
cmd = command('spec', 'lint', '--quick', @spec_path)
lambda { cmd.run }.should.raise Pod::Informative
cmd.output.should.include "Missing license type"
Pod::UI.output.should.include "Missing license type"
end
it "respects the -only--errors option" do
cmd = command('spec', 'lint', '--quick', '--only-errors', @spec_path)
lambda { cmd.run }.should.not.raise
cmd.output.should.include "Missing license type"
Pod::UI.output.should.include "Missing license type"
end
end
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../../spec_helper', __FILE__)
require 'net/http'
describe Pod::Command::Presenter do
Presenter = Pod::Command::Presenter
describe Pod::UI do
extend SpecHelper::Command
before do
@set = Pod::Spec::Set.new(fixture('spec-repos/master/CocoaLumberjack'))
......@@ -11,8 +10,8 @@ describe Pod::Command::Presenter do
end
it "presents the name, version, description, homepage and source of a specification set" do
presenter = Presenter.new(argv())
output = presenter.describe(@set)
Pod::UI.pod(@set)
puts output = Pod::UI.output.gsub(/\n */,'')
output.should.include? 'CocoaLumberjack'
output.should.include? '1.0'
output.should.include? '1.1'
......@@ -24,8 +23,8 @@ describe Pod::Command::Presenter do
it "presents the stats of a specification set" do
repo = { "forks"=>42, "watchers"=>318, "pushed_at"=>"2011-01-26T19:06:43Z" }
Octokit.expects(:repo).with("robbiehanson/CocoaLumberjack").returns(repo)
presenter = Presenter.new(argv('--stats', '--no-color'))
output = presenter.describe(@set)
Pod::UI.pod(@set, :stats)
output = Pod::UI.output
output.should.include? 'Author: Robbie Hanson'
output.should.include? 'License: BSD'
output.should.include? 'Platform: iOS - OS X'
......@@ -35,8 +34,9 @@ describe Pod::Command::Presenter do
end
it "should print at least one subspec" do
presenter = Presenter.new(argv())
output = presenter.describe(Pod::Spec::Set.new(fixture('spec-repos/master/RestKit')))
@set = Pod::Spec::Set.new(fixture('spec-repos/master/RestKit'))
Pod::UI.pod(@set)
output = Pod::UI.output
output.should.include? "RestKit/Network"
end
end
......
......@@ -249,6 +249,22 @@ else
FileUtils.cp_r(fixture('integration/.'), config.project_pods_root)
end
it "runs the optional pre_install callback defined in the Podfile _before_ the targets are integrated but _after_ the pods have been downloaded" do
podfile = Pod::Podfile.new do
self.platform platform
xcodeproj 'dummy'
pod 'SSZipArchive', '0.1.0'
pre_install do |installer|
memo = "PODS:#{installer.pods * ', '} TARGETS:#{installer.project.targets.to_a * ', '}"
File.open(config.project_pods_root + 'memo.txt', 'w') {|f| f.puts memo}
end
end
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
SpecHelper::Installer.new(resolver).install!
File.open(config.project_pods_root + 'memo.txt','rb').read.should == "PODS:SSZipArchive (0.1.0) TARGETS:\n"
end
it "runs the optional post_install callback defined in the Podfile _before_ the project is saved to disk" do
podfile = Pod::Podfile.new do
self.platform platform
......
......@@ -19,6 +19,7 @@ require 'spec_helper/github'
require 'spec_helper/temporary_directory'
require 'spec_helper/temporary_repos'
require 'spec_helper/config'
require 'spec_helper/user_interface'
module Bacon
class Context
......
require 'spec_helper/temporary_directory'
class Pod::Command
attr_accessor :output
def puts(msg = '') (@output ||= '') << "#{msg}\n" end
end
module SpecHelper
module Command
def command(*argv)
......@@ -15,9 +9,21 @@ module SpecHelper
def run_command(*args)
Dir.chdir(SpecHelper.temporary_directory) do
command = command(*args)
command.run
command.output
Pod::UI.output = ''
# TODO: remove this once all cocoapods has
# been converted to use the UI.puts
config_silent = config.silent?
config.silent = false
# Very nasty behaviour where the relative increments are
# not reverted and lead to sections being collapsed and
# not being printed to the output.
Pod::UI.indentation_level = 0
Pod::UI.title_level = 0
command(*args).run
config.silent = config_silent
Pod::UI.output
end
end
end
......
module Pod
class UI
@output = ''
class << self
attr_accessor :output
def puts(message = '')
@output << "#{message}"
end
end
end
end
......@@ -79,6 +79,16 @@ describe "Pod::Podfile" 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
......
......@@ -107,11 +107,11 @@ describe "A Pod::Specification loaded from a podspec" do
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.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 == " -fobjc-arc -Wunused-value"
@spec.activate_platform(:osx).compiler_flags.should == " -fobjc-arc -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
......@@ -334,9 +334,9 @@ describe "A Pod::Specification subspec" do
@spec.subspecs.first.parent.should == @spec
end
it "automatically forwards top level attributes to the top level parent" do
it "automatically forwards top level attributes to the subspecs" do
@spec.activate_platform(:ios)
[:version, :license, :authors, :compiler_flags].each do |attr|
[: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
......@@ -350,7 +350,7 @@ describe "A Pod::Specification subspec" do
@subsubspec.resources.should == %w[ resource ]
@subsubspec.compiler_flags = '-Wdeprecated-implementations'
@subsubspec.compiler_flags.should == ' -fobjc-arc -Wdeprecated-implementations'
@subsubspec.compiler_flags.should == '-Wdeprecated-implementations'
end
it "allows to specify arc settings for subspecs" do
......@@ -523,7 +523,7 @@ describe "A Pod::Specification, concerning its attributes that support different
end
it "returns the same list of compiler flags for each platform" do
compiler_flags = ' -fobjc-arc -Wdeprecated-implementations'
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
......@@ -587,8 +587,8 @@ describe "A Pod::Specification, concerning its attributes that support different
end
it "returns the same list of compiler flags for each platform" do
@spec.activate_platform(:ios).compiler_flags.should == ' -fobjc-arc -Wdeprecated-implementations'
@spec.activate_platform(:osx).compiler_flags.should == ' -fobjc-arc -Wfloat-equal'
@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
......
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