Commit edff6c05 authored by Will Pragnell's avatar Will Pragnell

Merge branch master into acknowledgements

parents 5c62253a 42cc83a1
source "http://rubygems.org" source "http://rubygems.org"
gem "open4"
gem "colored" gem "colored"
gem "escape" gem "escape"
gem "json" gem "json"
......
...@@ -38,7 +38,6 @@ GEM ...@@ -38,7 +38,6 @@ GEM
faraday_middleware (~> 0.8) faraday_middleware (~> 0.8)
hashie (~> 1.2) hashie (~> 1.2)
multi_json (~> 1.0) multi_json (~> 1.0)
open4 (1.3.0)
pry (0.9.8.4) pry (0.9.8.4)
coderay (~> 1.0.5) coderay (~> 1.0.5)
method_source (~> 0.7.1) method_source (~> 0.7.1)
...@@ -64,7 +63,6 @@ DEPENDENCIES ...@@ -64,7 +63,6 @@ DEPENDENCIES
kicker kicker
mocha-on-bacon mocha-on-bacon
octokit octokit
open4
pry pry
rake rake
rb-fsevent rb-fsevent
......
...@@ -29,7 +29,6 @@ Gem::Specification.new do |s| ...@@ -29,7 +29,6 @@ Gem::Specification.new do |s|
"you are upgrading, first run: $ pod setup" "you are upgrading, first run: $ pod setup"
s.add_runtime_dependency 'xcodeproj', '~> 0.1.0' s.add_runtime_dependency 'xcodeproj', '~> 0.1.0'
s.add_runtime_dependency 'popen4', '~> 0.1.2'
s.add_runtime_dependency 'colored', '~> 1.2' s.add_runtime_dependency 'colored', '~> 1.2'
s.add_runtime_dependency 'escape', '~> 0.0.4' s.add_runtime_dependency 'escape', '~> 0.0.4'
s.add_development_dependency 'bacon', '~> 1.1' s.add_development_dependency 'bacon', '~> 1.1'
......
...@@ -33,6 +33,7 @@ module Pod ...@@ -33,6 +33,7 @@ module Pod
autoload :Acknowledgements, 'cocoapods/generator/acknowledgements' autoload :Acknowledgements, 'cocoapods/generator/acknowledgements'
autoload :Plist, 'cocoapods/generator/acknowledgements/plist' autoload :Plist, 'cocoapods/generator/acknowledgements/plist'
autoload :Markdown, 'cocoapods/generator/acknowledgements/markdown' autoload :Markdown, 'cocoapods/generator/acknowledgements/markdown'
autoload :DummySource, 'cocoapods/generator/dummy_source'
end end
end end
...@@ -42,3 +43,7 @@ class Pathname ...@@ -42,3 +43,7 @@ class Pathname
end end
end end
if ENV['COCOA_PODS_ENV'] == 'development'
require 'pry'
require 'awesome_print'
end
...@@ -6,6 +6,7 @@ module Pod ...@@ -6,6 +6,7 @@ module Pod
autoload :Install, 'cocoapods/command/install' autoload :Install, 'cocoapods/command/install'
autoload :List, 'cocoapods/command/list' autoload :List, 'cocoapods/command/list'
autoload :Presenter, 'cocoapods/command/presenter' autoload :Presenter, 'cocoapods/command/presenter'
autoload :Push, 'cocoapods/command/push'
autoload :Repo, 'cocoapods/command/repo' autoload :Repo, 'cocoapods/command/repo'
autoload :Search, 'cocoapods/command/search' autoload :Search, 'cocoapods/command/search'
autoload :Setup, 'cocoapods/command/setup' autoload :Setup, 'cocoapods/command/setup'
...@@ -18,12 +19,13 @@ module Pod ...@@ -18,12 +19,13 @@ module Pod
def message def message
[ [
@command_class.banner,
'', '',
'Options', @command_class.banner.gsub(/\$ pod (.*)/, '$ pod \1'.green),
'-------',
'', '',
options 'Options:',
'',
options,
"\n",
].join("\n") ].join("\n")
end end
...@@ -45,22 +47,19 @@ module Pod ...@@ -45,22 +47,19 @@ module Pod
end end
def self.banner def self.banner
"To see help for the available commands run:\n" \ commands = ['install', 'list', 'push', 'repo', 'search', 'setup', 'spec'].sort
"\n" \ banner = "\nTo see help for the available commands run:\n\n"
" * $ pod setup --help\n" \ commands.each {|cmd| banner << " * $ pod #{cmd.green} --help\n"}
" * $ pod search --help\n" \ banner
" * $ pod list --help\n" \
" * $ pod install --help\n" \
" * $ pod repo --help\n" \
" * $ pod spec --help"
end end
def self.options def self.options
[ [
['--help', 'Show help information'], ['--help', 'Show help information'],
['--silent', 'Print nothing'], ['--silent', 'Print nothing'],
['--verbose', 'Print more information while working'], ['--no-color', 'Print output without color'],
['--version', 'Prints the version of CocoaPods'], ['--verbose', 'Print more information while working'],
['--version', 'Prints the version of CocoaPods'],
] ]
end end
...@@ -90,6 +89,8 @@ module Pod ...@@ -90,6 +89,8 @@ module Pod
Config.instance.silent = argv.option('--silent') Config.instance.silent = argv.option('--silent')
Config.instance.verbose = argv.option('--verbose') Config.instance.verbose = argv.option('--verbose')
String.send(:define_method, :colorize) { |string , _| string } if argv.option( '--no-color' )
command_class = case argv.shift_argument command_class = case argv.shift_argument
when 'install' then Install when 'install' then Install
when 'repo' then Repo when 'repo' then Repo
...@@ -97,6 +98,7 @@ module Pod ...@@ -97,6 +98,7 @@ module Pod
when 'list' then List when 'list' then List
when 'setup' then Setup when 'setup' then Setup
when 'spec' then Spec when 'spec' then Spec
when 'push' then Push
end end
if show_help || command_class.nil? if show_help || command_class.nil?
......
...@@ -35,7 +35,7 @@ module Pod ...@@ -35,7 +35,7 @@ module Pod
def initialize(argv) def initialize(argv)
config.clean = !argv.option('--no-clean') config.clean = !argv.option('--no-clean')
config.doc = !argv.option('--no-doc') config.generate_docs = !argv.option('--no-doc')
config.force_doc = argv.option('--force-doc') config.force_doc = argv.option('--force-doc')
config.integrate_targets = !argv.option('--no-integrate') config.integrate_targets = !argv.option('--no-integrate')
@update_repo = !argv.option('--no-update') @update_repo = !argv.option('--no-update')
......
...@@ -2,7 +2,7 @@ module Pod ...@@ -2,7 +2,7 @@ module Pod
class Command class Command
class Presenter class Presenter
def self.options def self.options
" --stats Show additional stats (like GitHub watchers and forks)\n" [["--stats", "Show additional stats (like GitHub watchers and forks)"]]
end end
autoload :CocoaPod, 'cocoapods/command/presenter/cocoa_pod' autoload :CocoaPod, 'cocoapods/command/presenter/cocoa_pod'
...@@ -24,10 +24,11 @@ module Pod ...@@ -24,10 +24,11 @@ module Pod
result << detail('Homepage', pod.homepage) result << detail('Homepage', pod.homepage)
result << detail('Source', pod.source_url) result << detail('Source', pod.source_url)
if @stats if @stats
result << detail('Pushed', pod.github_last_activity)
result << detail('Authors', pod.authors) if pod.authors =~ /,/ result << detail('Authors', pod.authors) if pod.authors =~ /,/
result << detail('Author', pod.authors) if pod.authors !~ /,/ result << detail('Author', pod.authors) if pod.authors !~ /,/
result << detail('Platform', pod.platform)
result << detail('License', pod.license) result << detail('License', pod.license)
result << detail('Platform', pod.platform)
result << detail('Watchers', pod.github_watchers) result << detail('Watchers', pod.github_watchers)
result << detail('Forks', pod.github_forks) result << detail('Forks', pod.github_forks)
end end
...@@ -43,18 +44,15 @@ module Pod ...@@ -43,18 +44,15 @@ module Pod
txt.strip.gsub(/(.{1,#{col}})( +|$)\n?|(.{#{col}})/, indent + "\\1\\3\n") txt.strip.gsub(/(.{1,#{col}})( +|$)\n?|(.{#{col}})/, indent + "\\1\\3\n")
end end
def detail(title, value, preferred_indentation = 8) def detail(title, value)
# 8 is the length of Homepage
return '' if !value return '' if !value
number_of_spaces = ((preferred_indentation - title.length) > 0) ? (preferred_indentation - title.length) : 0
spaces = ' ' * number_of_spaces
''.tap do |t| ''.tap do |t|
t << " - #{title}:" t << " - #{title}:".ljust(16)
if value.class == Array if value.class == Array
separator = "\n - " separator = "\n - "
t << separator + value.join(separator) t << separator << value.join(separator)
else else
t << " #{spaces + value.to_s}\n" t << value.to_s << "\n"
end end
end end
end end
......
...@@ -72,6 +72,10 @@ module Pod ...@@ -72,6 +72,10 @@ module Pod
Pod::Specification::Statistics.instance.github_forks(@set) Pod::Specification::Statistics.instance.github_forks(@set)
end end
def github_last_activity
distance_from_now_in_words(Pod::Specification::Statistics.instance.github_pushed_at(@set))
end
def ==(other) def ==(other)
self.class === other && @set == other.set self.class === other && @set == other.set
end end
...@@ -92,6 +96,25 @@ module Pod ...@@ -92,6 +96,25 @@ module Pod
"#{words[0..-2].join(', ')}, and #{words.last}" "#{words[0..-2].join(', ')}, and #{words.last}"
end end
end end
def distance_from_now_in_words(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
end end
......
require 'fileutils'
module Pod
class Command
class Push < Command
def self.banner
%{Pushing new specifications to a spec-repo:
$ pod push [REPO]
Validates `*.podspec' in the current working dir, updates
the local copy of the repository named REPO, adds specifications
to REPO, and finally it pushes REPO to its remote.}
end
def self.options
[["--allow-warnings", "Allows to push if warnings are not evitable"]].concat(super)
end
extend Executable
executable :git
def initialize(argv)
@allow_warnings = argv.option('--allow-warnings')
@repo = argv.shift_argument
super unless argv.empty? && @repo
end
def run
validate_podspec_files
check_repo_status
update_repo
add_specs_to_repo
push_repo
puts
end
private
def update_repo
puts "Updating the `#{@repo}' repo\n".yellow unless config.silent
# show the output of git even if not verbose
Dir.chdir(repo_dir) { 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` }
end
def repo_dir
dir = config.repos_dir + @repo
raise Informative, "[!] `#{@repo}' repo not found".red unless dir.exist?
dir
end
def check_repo_status
# TODO: add specs for staged and unstaged files (tested manually)
clean = Dir.chdir(repo_dir) { `git status --porcelain 2>&1` } == ''
raise Informative, "[!] `#{@repo}' repo not clean".red unless clean
end
def podspec_files
files = Pathname.glob("*.podspec")
raise Informative, "[!] Couldn't find .podspec file in current directory".red if files.empty?
files
end
def validate_podspec_files
puts "\nValidating specs".yellow unless config.silent
lint_argv = ["lint"]
lint_argv << "--only-errors" if @allow_warnings
lint_argv << "--silent" if config.silent
all_valid = Spec.new(ARGV.new(lint_argv)).run
end
def add_specs_to_repo
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)
if Pathname.new(output_path).exist?
message = "[Fix] #{spec}"
elsif Pathname.new(File.join(repo_dir, spec.name)).exist?
message = "[Update] #{spec}"
else
message = "[Add] #{spec}"
end
puts " - #{message}" unless config.silent
FileUtils.mkdir_p(output_path)
FileUtils.cp(Pathname.new(spec.name+'.podspec'), output_path)
Dir.chdir(repo_dir) do
git("add #{spec.name}")
git("commit -m '#{message}'")
end
end
end
end
end
end
# encoding: utf-8 # encoding: utf-8
require 'net/https'
require 'uri'
require 'octokit'
require 'json'
module Pod module Pod
class Command class Command
class Spec < Command class Spec < Command
def self.banner def self.banner
%{Managing PodSpec files: %{Managing PodSpec files:
$ pod spec create [NAME] $ pod spec create [ NAME | https://github.com/USER/REPO ]
$ pod spec create [https://github.com/USER/REPO]
Creates a PodSpec, in the current working dir, called `NAME.podspec'. Creates a PodSpec, in the current working dir, called `NAME.podspec'.
If a GitHub url is passed the spec is prepopulated. If a GitHub url is passed the spec is prepopulated.
$ pod spec lint [NAME.podspec] $ pod spec lint [ NAME.podspec | REPO ]
Validates `NAME.podspec'. In case `NAME.podspec' is omitted, it defaults Validates `NAME.podspec'. In case `NAME.podspec' is omitted, it defaults
to `*.podspec' in the current working dir.} to `*.podspec' in the current working dir. If the name of a repo is
provided it validates all its specs.}
end
def self.options
[ ["--quick", "Lint skips checks that would require to donwload and build the spec"],
["--only-errors", "Lint validates even if warnings are present"] ].concat(super)
end end
def initialize(argv) def initialize(argv)
args = argv.arguments @action = argv.shift_argument
unless (args[0] == 'create' && args.size == 2) || if @action == 'create'
(args[0] == 'lint' && args.size <= 2) @name_or_url = argv.shift_argument
@url = argv.shift_argument
super if @name_or_url.nil?
elsif @action == 'lint'
@quick = argv.option('--quick')
@only_errors = argv.option('--only-errors')
@repo_or_podspec = argv.shift_argument unless argv.empty?
super unless argv.size <= 1
else
super super
end end
@action, @name_or_url = args.first(2) super unless argv.empty?
end end
def run def run
...@@ -37,64 +45,400 @@ module Pod ...@@ -37,64 +45,400 @@ module Pod
end end
def create def create
if repo_id = @name_or_url[/github.com\/([^\/\.]*\/[^\/\.]*)\.*/, 1] if repo_id_match = (@url || @name_or_url).match(/github.com\/([^\/\.]*\/[^\/\.]*)\.*/)
require 'octokit'
repo_id = repo_id_match[1]
data = github_data_for_template(repo_id) 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' puts semantic_versioning_notice(repo_id, data[:name]) if data[:version] == '0.0.1'
else else
data = default_data_for_template(@name_or_url) data = default_data_for_template(@name_or_url)
end end
spec = spec_template(data) spec = spec_template(data)
(Pathname.pwd + "#{data[:name]}.podspec").open('w') { |f| f << spec } (Pathname.pwd + "#{data[:name]}.podspec").open('w') { |f| f << spec }
puts "\nSpecification created at #{data[:name]}.podspec\n".green puts "\nSpecification created at #{data[:name]}.podspec".green
end end
def lint def lint
name = @name_or_url
file = name ? Pathname.new(name) : Pathname.pwd.glob('*.podspec').first
spec = Specification.from_file(file)
puts "\nThe #{spec.name} specification contains all the required attributes.".green if spec.validate!
warnings = []
warnings << 'The name of the specification should match the name of the podspec file' unless path_matches_name?(file, spec)
warnings << 'Missing license[:type]' unless spec.license && spec.license[:type]
warnings << 'Missing license[:file] or [:text]' unless spec.license && (spec.license[:file] || spec.license[:text])
warnings << "Github repositories should end in `.git'" if spec.source[:git] =~ /github.com/ && spec.source[:git] !~ /.*\.git/
warnings << "Github repositories should end in `.git'" if spec.source[:git] =~ /github.com/ && spec.source[:git] !~ /.*\.git/
warnings << "The description should end with a dot" if spec.description && spec.description !~ /.*\./
warnings << "The summary should end with a dot" if spec.summary !~ /.*\./
unless warnings.empty?
puts "\n[!] The #{spec.name} specification raised the following warnings".yellow
warnings.each { |warn| puts ' - '+ warn }
end
puts puts
invalid_count = lint_podspecs
if invalid_count == 0
puts lint_passed_message unless config.silent?
else
raise Informative, lint_failed_message(invalid_count)
end
end end
private private
def path_matches_name?(path, spec) def lint_podspecs
(path.dirname + "#{spec.name}.podspec").to_s == @name_or_url specs_count, invalid_count = 0, 0
podspecs_to_lint.each do |podspec_file|
root_spec = Specification.from_file(podspec_file)
specs = root_spec.recursive_subspecs.any? ? root_spec.recursive_subspecs : [root_spec]
specs.each do |spec|
# Show immediatly which pod is being processed.
print " -> #{spec}\r" unless config.silent? || is_repo?
$stdout.flush
linter = Linter.new(spec, podspec_file)
linter.lenient = @only_errors
linter.quick = @quick || is_repo?
invalid_count += 1 unless linter.lint
# This overwrites the previously printed text
puts " -> ".send(lint_result_color(linter)) << spec.to_s unless config.silent? || should_skip?(linter)
print_messages(spec, 'ERROR', linter.errors)
print_messages(spec, 'WARN', linter.warnings)
print_messages(spec, 'NOTE', linter.notes)
puts unless config.silent? || should_skip?(linter)
end
specs_count += specs.count
end
puts "Analyzed #{specs_count} specs in #{podspecs_to_lint.count} podspecs files.\n\n" if is_repo? && !config.silent?
invalid_count
end end
def suggested_ref_and_version(repo) def lint_result_color(linter)
tags = Octokit.tags(:username => repo['owner']['login'], :repo => repo['name']).map {|tag| tag["name"]} if linter.errors.empty? && linter.warnings.empty?
versions_tags = {} :green
tags.each do |tag| elsif linter.errors.empty?
clean_tag = tag.gsub(/^v(er)? ?/,'') :yellow
versions_tags[Gem::Version.new(clean_tag)] = tag if Gem::Version.correct?(clean_tag) else
:red
end end
version = versions_tags.keys.sort.last || '0.0.1' end
data = {:version => version}
if version == '0.0.1' def should_skip?(linter)
branches = Octokit.branches(:username => repo['owner']['login'], :repo => repo['name']) is_repo? && linter.errors.empty? && linter.warnings.empty? && linter.notes.empty?
master_name = repo['master_branch'] || 'master' end
master = branches.select {|branch| branch['name'] == master_name }.first
data[:ref_type] = ':commit' def print_messages(spec, type, messages)
data[:ref] = master['commit']['sha'] return if config.silent?
if spec.platform.name
messages = clean_platfrom_messages(messages)
else else
data[:ref_type] = ':tag' messages = clean_duplicate_platfrom_messages(messages)
data[:ref] = versions_tags[version] end
messages.each {|msg| puts " - #{type.ljust(5)} | #{msg}"}
end
def clean_platfrom_messages(messages)
messages.map { |l| l.gsub(/ios: /,'').gsub(/osx: /,'') }
end
def clean_duplicate_platfrom_messages(messages)
duplicate_candiates = messages.select {|l| l.include?("ios: ")}
duplicated = duplicate_candiates.select {|l| messages.include?(l.gsub(/ios: /,'osx: ')) }
duplicated.uniq.each do |l|
clean = l.gsub(/ios: /,'')
messages.insert(messages.index(l), clean)
messages.delete(l)
messages.delete('osx: ' + clean)
end
messages
end
def podspecs_to_lint
@podspecs_to_lint ||= begin
if (is_repo?)
files = (config.repos_dir + @repo_or_podspec).glob('**/*.podspec')
elsif @repo_or_podspec
files = [Pathname.new(@repo_or_podspec)]
raise Informative, "[!] Unable to find a spec named #{@repo_or_podspec}".red << "\n\n" unless files[0].exist? && @repo_or_podspec.include?('.podspec')
else
files = Pathname.pwd.glob('*.podspec')
raise Informative, "[!] No specs found in the current directory".red << "\n\n" if files.empty?
end
files
end
end
def is_repo?
@is_repo ||= @repo_or_podspec && (config.repos_dir + @repo_or_podspec).exist? && !@repo_or_podspec.include?('/')
end
def lint_passed_message
( podspecs_to_lint.count == 1 ? "#{podspecs_to_lint.first.basename} passed validation" : "All the specs passed validation" ).green << "\n\n"
end
def lint_failed_message(count)
( podspecs_to_lint.count == 1 ? "[!] The spec did not pass validation" : "[!] #{count} specs failed validation" ).red << "\n\n"
end
# Linter class
#
class Linter
include Config::Mixin
attr_accessor :quick, :lenient
attr_reader :spec, :file
def initialize(spec, podspec_file)
@spec = spec
@file = podspec_file.realpath
end
# Takes an array of podspec files and lints them all
#
# It returns true if the spec passed validation
#
def lint
# If the spec doesn't validate it raises and informative
# TODO: consider raising the informative in the clients of Pod::Specification#validate!
# and just report the errors here
peform_multiplatform_analysis unless quick
# Skip validation if there are errors in the podspec as it would result in a crash
unless podspec_errors.empty?
@errors, @warnings, @notes = podspec_errors, [], ['[!] Fatal errors found skipping the rest of the validation']
return false
end
valid?
end
def valid?
lenient ? errors.empty? : errors.empty? && warnings.empty?
end
def errors
@errors ||= file_patterns_errors + build_errors
end
def warnings
@warnings ||= podspec_warnings + deprecation_warnings
end
def notes
@notes ||= build_warnings
end
# Performs platform specific analysis.
# It requires to download the source at each iteration
#
def peform_multiplatform_analysis
platform_names.each do |platform_name|
set_up_lint_environment
puts "\n\n#{spec} - Analyzing on #{Platform.new platform_name} platform.".green.reversed if config.verbose?
install_pod(platform_name)
puts "Building with xcodebuild.\n".yellow if config.verbose?
xcodebuild_output.concat(xcodebuild_output_for_platfrom(platform_name))
file_patterns_errors.concat(file_patterns_errors_for_platfrom(platform_name))
tear_down_lint_environment
end
end
def platform_names
spec.platform.name ? [spec.platform.name] : [:ios, :osx]
end
def install_pod(platform_name)
podfile = podfile_from_spec(platform_name)
config.verbose
Installer.new(podfile).install!
config.silent
end
def podfile_from_spec(platform_name)
name = spec.name
podspec = file.realpath.to_s
podfile = Pod::Podfile.new do
platform platform_name
dependency name, :podspec => podspec
end
end
def set_up_lint_environment
tmp_dir.rmtree if tmp_dir.exist?
tmp_dir.mkpath
@original_config = Config.instance.clone
config.project_root = tmp_dir
config.project_pods_root = tmp_dir + 'Pods'
config.silent = !config.verbose
config.integrate_targets = false
config.generate_docs = false
end
def tear_down_lint_environment
tmp_dir.rmtree
Config.instance = @original_config
end
def tmp_dir
Pathname.new('/tmp/CocoaPods/Lint')
end
def pod_dir
tmp_dir + 'Pods' + spec.name
end
# @return [Array<String>] List of the fatal defects detected in a podspec
def podspec_errors
messages = []
messages << "Missing name" unless spec.name
messages << "Missing version" unless spec.version
messages << "Missing summary" unless spec.summary
messages << "Missing homepage" unless spec.homepage
messages << "Missing author(s)" unless spec.authors
messages << "Missing source or part_of" unless spec.source || spec.part_of
messages << "Missing source_files" if spec.source_files.empty? && spec.subspecs.empty?
messages << "The name of the spec should match the name of the file" unless names_match?
messages << "Unrecognized platfrom (no value, :ios, :osx)" unless [nil, :ios, :osx].include?(spec.platform.name)
messages += paths_starting_with_a_slash_errors
messages
end
def names_match?
return true unless spec.name
root_name = spec.name.match(/[^\/]*/)[0]
file.basename.to_s == root_name + '.podspec'
end
def paths_starting_with_a_slash_errors
messages = []
%w[source_files resources clean_paths].each do |accessor|
patterns = spec.send(accessor.to_sym)
# Some values are multiplaform
patterns = patterns.is_a?(Hash) ? patterns.values.flatten(1) : patterns
patterns.each do |pattern|
# Skip Filelist that would otherwise be resolved from the working directory resulting
# in a potentially very expensi operation
next if pattern.is_a?(FileList)
invalid = pattern.is_a?(Array) ? pattern.any? { |path| path.start_with?('/') } : pattern.start_with?('/')
if invalid
messages << "Paths cannot start with a slash (#{accessor})"
break
end
end
end
messages
end
# @return [Array<String>] List of the **non** fatal defects detected in a podspec
def podspec_warnings
license = @spec.license || {}
source = @spec.source || {}
text = @file.read
messages = []
messages << "Missing license[:type]" unless license[:type]
messages << "Missing license[:file] or [:text]" unless license[:file] || license[:text]
messages << "The summary is not meaningful" if spec.summary =~ /A short description of/
messages << "The description is not meaningful" if spec.description && spec.description =~ /An optional longer description of/
messages << "The summary should end with a dot" if @spec.summary !~ /.*\./
messages << "The description should end with a dot" if @spec.description !~ /.*\./ && @spec.description != @spec.summary
messages << "Git sources should specify either a tag or a commit" if source[:git] && !source[:commit] && !source[:tag]
messages << "Github repositories should end in `.git'" if github_source? && source[:git] !~ /.*\.git/
messages << "Github repositories should use `https' link" if github_source? && source[:git] !~ /https:\/\/github.com/
messages << "Comments must be deleted" if text =~ /^\w*#\n\w*#/ # allow a single line comment as it is generally used in subspecs
messages
end
def github_source?
@spec.source && @spec.source[:git] =~ /github.com/
end
# It reads a podspec file and checks for strings corresponding
# to features that are or will be deprecated
#
# @return [Array<String>]
def deprecation_warnings
text = @file.read
deprecations = []
deprecations << "`config.ios?' and `config.osx?' are deprecated and will be removed in version 0.7" if text. =~ /config\..?os.?/
deprecations << "The `post_install' hook is reserved for edge cases" if text. =~ /post_install/
deprecations
end
def build_errors
@build_errors ||= xcodebuild_output.select {|msg| msg.include?('error')}
end
def build_warnings
@build_warnings ||= xcodebuild_output - build_errors
end
def xcodebuild_output
@xcodebuild_output ||= []
end
# It creates a podfile in memory and builds a library containing
# the pod for all available platfroms with xcodebuild.
#
# It returns a array of strings
#
def xcodebuild_output_for_platfrom(platform_name)
return [] if `which xcodebuild`.strip.empty?
messages = []
output = Dir.chdir(config.project_pods_root) { `xcodebuild 2>&1` }
clean_output = process_xcode_build_output(output).map {|l| "#{platform_name}: #{l}"}
messages += clean_output
puts(output) if config.verbose?
messages
end end
def process_xcode_build_output(output)
output_by_line = output.split("\n")
selected_lines = output_by_line.select do |l|
l.include?('error') && (l !~ /errors? generated\./) \
|| l.include?('warning') && (l !~ /warnings? generated\./)\
|| l.include?('note')
end
selected_lines.map do |l|
new = l.gsub(/\/tmp\/CocoaPods\/Lint\/Pods\//,'') # Remove the unnecessary tmp path
new.gsub!(/^ */,' ') # Remove indentation
"XCODEBUILD > " << new # Mark
end
end
def file_patterns_errors
@file_patterns_errors ||= []
end
# It checks that every file pattern specified in a spec yields
# at least one file. It requires the pods to be alredy present
# in the current working directory under Pods/spec.name
#
# It returns a array of messages
#
def file_patterns_errors_for_platfrom(platform_name)
Dir.chdir(config.project_pods_root + spec.name ) do
messages = []
messages += check_spec_files_exists(:source_files, platform_name, '*.{h,m,mm,c,cpp}')
messages += check_spec_files_exists(:resources, platform_name)
messages << "#{platform_name}: license[:file] = '#{spec.license[:file]}' -> did not match any file" if spec.license[:file] && pod_dir.glob(spec.license[:file]).empty?
messages.compact
end
end
def check_spec_files_exists(accessor, platform_name, options = {})
result = []
patterns = spec.send(accessor)[platform_name]
patterns.each do |original_pattern|
pattern = pod_dir + original_pattern
if pattern.directory? && options[:glob]
pattern += options[:glob]
end
result << "#{platform_name}: [#{accessor} = '#{original_pattern}'] -> did not match any file" if pattern.glob.empty?
end
result
end
end
# Templates and github information retrival for spec create
def default_data_for_template(name)
data = {}
data[:name] = name
data[:version] = '0.0.1'
data[:summary] = "A short description of #{name}."
data[:homepage] = "http://EXAMPLE/#{name}"
data[:author_name] = `git config --get user.name`.strip
data[:author_email] = `git config --get user.email`.strip
data[:source_url] = "http://EXAMPLE/#{name}.git"
data[:ref_type] = ':tag'
data[:ref] = '0.0.1'
data data
end end
...@@ -113,17 +457,25 @@ module Pod ...@@ -113,17 +457,25 @@ module Pod
data.merge suggested_ref_and_version(repo) data.merge suggested_ref_and_version(repo)
end end
def default_data_for_template(name) def suggested_ref_and_version(repo)
data = {} tags = Octokit.tags(:username => repo['owner']['login'], :repo => repo['name']).map {|tag| tag["name"]}
data[:name] = name versions_tags = {}
data[:version] = '0.0.1' tags.each do |tag|
data[:summary] = "A short description of #{name}." clean_tag = tag.gsub(/^v(er)? ?/,'')
data[:homepage] = "http://EXAMPLE/#{name}" versions_tags[Gem::Version.new(clean_tag)] = tag if Gem::Version.correct?(clean_tag)
data[:author_name] = `git config --get user.name`.strip end
data[:author_email] = `git config --get user.email`.strip version = versions_tags.keys.sort.last || '0.0.1'
data[:source_url] = "http://EXAMPLE/#{name}.git" data = {:version => version}
data[:ref_type] = ':tag' if version == '0.0.1'
data[:ref] = '0.0.1' branches = Octokit.branches(:username => repo['owner']['login'], :repo => repo['name'])
master_name = repo['master_branch'] || 'master'
master = branches.select {|branch| branch['name'] == master_name }.first
data[:ref_type] = ':commit'
data[:ref] = master['commit']['sha']
else
data[:ref_type] = ':tag'
data[:ref] = versions_tags[version]
end
data data
end end
...@@ -183,16 +535,17 @@ Pod::Spec.new do |s| ...@@ -183,16 +535,17 @@ Pod::Spec.new do |s|
s.description = 'An optional longer description of #{data[:name]}.' s.description = 'An optional longer description of #{data[:name]}.'
# If this Pod runs only on iOS or OS X, then specify that with one of # If this Pod runs only on iOS or OS X, then specify the platform and
# these, or none if it runs on both platforms. # the deployment target.
# If the pod runs on both plafroms but presents different deployment
# targets, source files, etc. create two different pods: `#{data[:name]}-iOS'
# and `#{data[:name]}-OSX'.
# #
# s.platform = :ios, '5.0'
# s.platform = :ios # s.platform = :ios
# s.platform = :ios, { :deployment_target => "5.0" }
# s.platform = :osx # If this Pod runs on boths platforms, then specify the deployment
# s.platform = :osx, { :deployment_target => "10.7" } # targets.
#
# s.ios.deployment_target = '5.0'
# s.osx.deployment_target = '10.7'
# A list of resources included with the Pod. These are copied into the # A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script. # target bundle with a build phase script.
...@@ -240,9 +593,10 @@ Pod::Spec.new do |s| ...@@ -240,9 +593,10 @@ Pod::Spec.new do |s|
# #
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' } # s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
end # ――― INFO ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
SPEC
end end
SPEC
end
def semantic_versioning_notice(repo_id, repo) def semantic_versioning_notice(repo_id, repo)
return <<-EOS return <<-EOS
...@@ -271,7 +625,7 @@ $ git push --tags ...@@ -271,7 +625,7 @@ $ git push --tags
After commiting the specification, consider opening a ticket with the template displayed above: After commiting the specification, consider opening a ticket with the template displayed above:
- link: https://github.com/#{repo_id}/issues/new - link: https://github.com/#{repo_id}/issues/new
- title: Please add semantic version tags - title: Please add semantic version tags
EOS EOS
end end
end end
end end
......
...@@ -12,13 +12,13 @@ module Pod ...@@ -12,13 +12,13 @@ module Pod
attr_accessor :repos_dir, :project_root, :project_pods_root attr_accessor :repos_dir, :project_root, :project_pods_root
attr_accessor :clean, :verbose, :silent attr_accessor :clean, :verbose, :silent
attr_accessor :doc, :doc_install, :force_doc attr_accessor :generate_docs, :doc_install, :force_doc
attr_accessor :integrate_targets attr_accessor :integrate_targets
alias_method :clean?, :clean alias_method :clean?, :clean
alias_method :verbose?, :verbose alias_method :verbose?, :verbose
alias_method :silent?, :silent alias_method :silent?, :silent
alias_method :doc?, :doc # TODO rename to generate_docs? alias_method :generate_docs?, :generate_docs
alias_method :doc_install?, :doc_install alias_method :doc_install?, :doc_install
alias_method :force_doc?, :force_doc alias_method :force_doc?, :force_doc
alias_method :integrate_targets?, :integrate_targets alias_method :integrate_targets?, :integrate_targets
...@@ -26,7 +26,7 @@ module Pod ...@@ -26,7 +26,7 @@ module Pod
def initialize def initialize
@repos_dir = Pathname.new(File.expand_path("~/.cocoapods")) @repos_dir = Pathname.new(File.expand_path("~/.cocoapods"))
@verbose = @silent = @force_doc = false @verbose = @silent = @force_doc = false
@clean = @doc = @doc_install = @integrate_targets = true @clean = @generate_docs = @doc_install = @integrate_targets = true
end end
def project_root def project_root
...@@ -51,18 +51,15 @@ module Pod ...@@ -51,18 +51,15 @@ module Pod
Podfile.from_file(project_podfile) if project_podfile.exist? Podfile.from_file(project_podfile) if project_podfile.exist?
end end
end end
attr_writer :podfile
def ios? def ios?
require 'colored' # TODO: deprecate in 0.7
caller.find { |line| line =~ /^(.+.podspec):\d*/ }
puts "[!] The use of `config.ios?` is deprecated and will be removed in version 0.7.#{" Called from: #{$1}" if $1}".red
podfile.target_definitions[:default].platform == :ios if podfile podfile.target_definitions[:default].platform == :ios if podfile
end end
def osx? def osx?
require 'colored' # TODO: deprecate in 0.7
caller.find { |line| line =~ /^(.+.podspec):\d*/ }
puts "[!] The use of `config.osx?` is deprecated and will be removed in version 0.7.#{" Called from: #{$1}" if $1}".red
podfile.target_definitions[:default].platform == :osx if podfile podfile.target_definitions[:default].platform == :osx if podfile
end end
......
...@@ -60,13 +60,21 @@ module Pod ...@@ -60,13 +60,21 @@ module Pod
end end
def clean def clean
# no-op if download_only?
FileUtils.rm_f(tmp_path)
else
super
end
end end
def tarball_url_for(id) def tarball_url_for(id)
original_url, username, reponame = *(url.match(/[:\/]([\w\-]+)\/([\w\-]+)\.git/).to_a) original_url, username, reponame = *(url.match(/[:\/]([\w\-]+)\/([\w\-]+)\.git/).to_a)
"https://github.com/#{username}/#{reponame}/tarball/#{id}" "https://github.com/#{username}/#{reponame}/tarball/#{id}"
end end
def tmp_path
target_path + "tarball.tar.gz"
end
private private
...@@ -75,8 +83,6 @@ module Pod ...@@ -75,8 +83,6 @@ module Pod
end end
def download_and_extract_tarball(id) def download_and_extract_tarball(id)
tmp_path = target_path + "tarball.tar.gz"
File.open(tmp_path, "w+") do |tmpfile| File.open(tmp_path, "w+") do |tmpfile|
open tarball_url_for(id) do |archive| open tarball_url_for(id) do |archive|
tmpfile.write Zlib::GzipReader.new(archive).read tmpfile.write Zlib::GzipReader.new(archive).read
...@@ -84,8 +90,6 @@ module Pod ...@@ -84,8 +90,6 @@ module Pod
system "tar xf #{tmpfile.path} -C #{target_path} --strip-components 1" system "tar xf #{tmpfile.path} -C #{target_path} --strip-components 1"
end end
FileUtils.rm_f(tmp_path)
end end
end end
end end
......
...@@ -7,7 +7,7 @@ module Pod ...@@ -7,7 +7,7 @@ module Pod
raise Informative, "Unable to locate the executable `#{name}'" raise Informative, "Unable to locate the executable `#{name}'"
end end
if Config.instance.verbose? if Config.instance.verbose?
puts "#{bin} #{command}" puts "-> #{bin} #{command}"
`#{bin} #{command} 1>&2` `#{bin} #{command} 1>&2`
else else
`#{bin} #{command} 2> /dev/null` `#{bin} #{command} 2> /dev/null`
......
...@@ -65,13 +65,19 @@ module Pod ...@@ -65,13 +65,19 @@ module Pod
'--ignore', '.m', '--ignore', '.m',
'--keep-undocumented-objects', '--keep-undocumented-objects',
'--keep-undocumented-members', '--keep-undocumented-members',
'--keep-intermediate-files' '--keep-intermediate-files',
'--exit-threshold', '2'
# appledoc exits with 1 if a warning was logged
] ]
index = index_file index = index_file
options += ['--index-desc', index] if index options += ['--index-desc', index] if index
options += spec_appledoc_options options += spec_appledoc_options
end end
def already_installed?
Pathname.new(File.expand_path("~/Library/Developer/Shared/Documentation/DocSets/org.cocoapods.#{name.gsub(/ /,'-')}.docset")).exist?
end
def generate(install = false) def generate(install = false)
options = appledoc_options options = appledoc_options
options += ['--output', @target_path.to_s] options += ['--output', @target_path.to_s]
...@@ -82,9 +88,9 @@ module Pod ...@@ -82,9 +88,9 @@ module Pod
@pod.chdir do @pod.chdir do
appledoc Escape.shell_command(options) appledoc Escape.shell_command(options)
end end
# appledoc exits with 1 if a warning was logged
if $?.exitstatus >= 2 if $?.exitstatus != 0
raise "Appledoc encountered an error (exitstatus: #{$?.exitstatus})." puts "[!] Appledoc encountered an error (exitstatus: #{$?.exitstatus}), an update might be available to solve the issue." unless config.silent?
end end
rescue Informative rescue Informative
......
module Pod
module Generator
class DummySource
def save_as(pathname)
pathname.open('w') do |source|
source.puts "@interface PodsDummy : NSObject"
source.puts "@end"
source.puts "@implementation PodsDummy"
source.puts "@end"
end
end
end
end
end
...@@ -14,6 +14,8 @@ module Pod ...@@ -14,6 +14,8 @@ module Pod
# FIXME: pass this into the installer as a parameter # FIXME: pass this into the installer as a parameter
@sandbox = Sandbox.new(config.project_pods_root) @sandbox = Sandbox.new(config.project_pods_root)
@resolver = Resolver.new(@podfile, @sandbox) @resolver = Resolver.new(@podfile, @sandbox)
# TODO: remove in 0.7 (legacy support for config.ios? and config.osx?)
config.podfile = podfile
end end
def lock_file def lock_file
...@@ -59,9 +61,14 @@ module Pod ...@@ -59,9 +61,14 @@ module Pod
end end
end end
if (should_install && config.doc?) || config.force_doc? if (should_install && config.generate_docs?) || config.force_doc?
puts "Installing Documentation for #{pod.specification}".green if config.verbose? doc_generator = Generator::Documentation.new(pod)
Generator::Documentation.new(pod).generate(config.doc_install?) if doc_generator.already_installed?
puts "Using Existing Documentation for #{pod.specification}".green if config.verbose?
else
puts "Installing Documentation for #{pod.specification}".green if config.verbose?
doc_generator.generate(config.doc_install?)
end
end end
end end
end end
...@@ -87,6 +94,7 @@ module Pod ...@@ -87,6 +94,7 @@ module Pod
end end
generate_lock_file!(pods) generate_lock_file!(pods)
generate_dummy_source
puts "* Running post install hooks" if config.verbose? puts "* Running post install hooks" if config.verbose?
# Post install hooks run _before_ saving of project, so that they can alter it before saving. # Post install hooks run _before_ saving of project, so that they can alter it before saving.
...@@ -156,6 +164,19 @@ module Pod ...@@ -156,6 +164,19 @@ module Pod
end end
end end
def generate_dummy_source
filename = "PodsDummy.m"
pathname = Pathname.new(sandbox.root + filename)
Generator::DummySource.new.save_as(pathname)
project_file = project.files.new('path' => filename)
project.group("Targets Support Files") << project_file
target_installers.each do |target_installer|
target_installer.target.source_build_phases.first << project_file
end
end
def specs_by_target def specs_by_target
@specs_by_target ||= @resolver.resolve @specs_by_target ||= @resolver.resolve
end end
......
...@@ -75,9 +75,8 @@ module Pod ...@@ -75,9 +75,8 @@ module Pod
support_files_group = @project.group("Targets Support Files").create_group(@target_definition.label) support_files_group = @project.group("Targets Support Files").create_group(@target_definition.label)
support_files_group.create_files(target_support_files) support_files_group.create_files(target_support_files)
xcconfig_file = support_files_group.files.where(:path => @target_definition.xcconfig_name) xcconfig_file = support_files_group.files.where(:path => @target_definition.xcconfig_name)
configure_build_configurations(xcconfig_file) configure_build_configurations(xcconfig_file)
create_files(pods, sandbox) create_files(pods, sandbox)
end end
...@@ -113,7 +112,7 @@ module Pod ...@@ -113,7 +112,7 @@ module Pod
end end
def default_ld_flags def default_ld_flags
flags = %w{-ObjC -all_load} flags = %w{-ObjC}
flags << '-fobjc-arc' if @podfile.set_arc_compatibility_flag? && self.requires_arc flags << '-fobjc-arc' if @podfile.set_arc_compatibility_flag? && self.requires_arc
flags.join(" ") flags.join(" ")
end end
......
...@@ -3,19 +3,19 @@ module Pod ...@@ -3,19 +3,19 @@ module Pod
attr_reader :specification attr_reader :specification
attr_reader :sandbox attr_reader :sandbox
attr_reader :platform attr_reader :platform
def initialize(specification, sandbox, platform) def initialize(specification, sandbox, platform)
@specification, @sandbox, @platform = specification, sandbox, platform @specification, @sandbox, @platform = specification, sandbox, platform
end end
def self.from_podspec(podspec, sandbox, platform) def self.from_podspec(podspec, sandbox, platform)
new(Specification.from_file(podspec), sandbox, platform) new(Specification.from_file(podspec), sandbox, platform)
end end
def root def root
@sandbox.root + specification.name @sandbox.root + specification.name
end end
def to_s def to_s
if specification.local? if specification.local?
"#{specification} [LOCAL]" "#{specification} [LOCAL]"
...@@ -23,28 +23,28 @@ module Pod ...@@ -23,28 +23,28 @@ module Pod
specification.to_s specification.to_s
end end
end end
def name def name
specification.name specification.name
end end
def create def create
root.mkpath unless exists? root.mkpath unless exists?
end end
def exists? def exists?
root.exist? root.exist?
end end
def chdir(&block) def chdir(&block)
create create
Dir.chdir(root, &block) Dir.chdir(root, &block)
end end
def implode def implode
root.rmtree if exists? root.rmtree if exists?
end end
def clean def clean
clean_paths.each { |path| FileUtils.rm_rf(path) } clean_paths.each { |path| FileUtils.rm_rf(path) }
end end
...@@ -54,19 +54,19 @@ module Pod ...@@ -54,19 +54,19 @@ module Pod
@sandbox.root + specification.name + prefix_header @sandbox.root + specification.name + prefix_header
end end
end end
def source_files def source_files
expanded_paths(specification.source_files, :glob => '*.{h,m,mm,c,cpp}', :relative_to_sandbox => true) expanded_paths(specification.source_files, :glob => '*.{h,m,mm,c,cpp}', :relative_to_sandbox => true)
end end
def absolute_source_files def absolute_source_files
expanded_paths(specification.source_files, :glob => '*.{h,m,mm,c,cpp}') expanded_paths(specification.source_files, :glob => '*.{h,m,mm,c,cpp}')
end end
def clean_paths def clean_paths
expanded_paths(specification.clean_paths) expanded_paths(specification.clean_paths)
end end
def resources def resources
expanded_paths(specification.resources, :relative_to_sandbox => true) expanded_paths(specification.resources, :relative_to_sandbox => true)
end end
...@@ -90,13 +90,13 @@ module Pod ...@@ -90,13 +90,13 @@ module Pod
@sandbox.add_header_files(namespaced_path, files) @sandbox.add_header_files(namespaced_path, files)
end end
end end
def add_to_target(target) def add_to_target(target)
implementation_files.each do |file| implementation_files.each do |file|
target.add_source_file(file, nil, specification.compiler_flags[@platform.name].strip) target.add_source_file(file, nil, specification.compiler_flags[@platform.name].strip)
end end
end end
def requires_arc? def requires_arc?
specification.requires_arc specification.requires_arc
end end
...@@ -104,13 +104,13 @@ module Pod ...@@ -104,13 +104,13 @@ module Pod
def dependencies def dependencies
specification.dependencies[@platform.name] specification.dependencies[@platform.name]
end end
private private
def implementation_files def implementation_files
source_files.select { |f| f.extname != '.h' } source_files.select { |f| f.extname != '.h' }
end end
def relative_root def relative_root
root.relative_path_from(@sandbox.root) root.relative_path_from(@sandbox.root)
end end
...@@ -125,7 +125,7 @@ module Pod ...@@ -125,7 +125,7 @@ module Pod
mappings mappings
end end
end end
def expanded_paths(platforms_with_patterns, options = {}) def expanded_paths(platforms_with_patterns, options = {})
patterns = platforms_with_patterns.is_a?(Hash) ? platforms_with_patterns[@platform.name] : platforms_with_patterns patterns = platforms_with_patterns.is_a?(Hash) ? platforms_with_patterns[@platform.name] : platforms_with_patterns
patterns.map do |pattern| patterns.map do |pattern|
......
...@@ -8,22 +8,29 @@ module Pod ...@@ -8,22 +8,29 @@ module Pod
new :osx new :osx
end end
attr_reader :options attr_reader :deployment_target
def initialize(symbolic_name, options = {}) def initialize(symbolic_name, deployment_target = nil)
@symbolic_name = symbolic_name @symbolic_name = symbolic_name
@options = options if deployment_target
version = deployment_target.is_a?(Hash) ? deployment_target[:deployment_target] : deployment_target # backwards compatibility from 0.6
@deployment_target = Pod::Version.create(version)
end
end end
def name def name
@symbolic_name @symbolic_name
end end
def deployment_target= (version)
@deployment_target = Pod::Version.create(version)
end
def ==(other_platform_or_symbolic_name) def ==(other_platform_or_symbolic_name)
if other_platform_or_symbolic_name.is_a?(Symbol) if other_platform_or_symbolic_name.is_a?(Symbol)
@symbolic_name == other_platform_or_symbolic_name @symbolic_name == other_platform_or_symbolic_name
else else
self == (other_platform_or_symbolic_name.name) self.name == (other_platform_or_symbolic_name.name) && self.deployment_target == other_platform_or_symbolic_name.deployment_target
end end
end end
...@@ -51,12 +58,6 @@ module Pod ...@@ -51,12 +58,6 @@ module Pod
name.nil? name.nil?
end end
def deployment_target
if (options && opt = options[:deployment_target])
Pod::Version.new(opt)
end
end
def requires_legacy_ios_archs? def requires_legacy_ios_archs?
return unless deployment_target return unless deployment_target
(name == :ios) && (deployment_target < Pod::Version.new("4.3")) (name == :ios) && (deployment_target < Pod::Version.new("4.3"))
......
...@@ -4,7 +4,7 @@ module Pod ...@@ -4,7 +4,7 @@ module Pod
include Config::Mixin include Config::Mixin
attr_reader :name, :target_dependencies attr_reader :name, :target_dependencies
attr_accessor :xcodeproj, :link_with, :platform, :parent, :exclusive attr_accessor :xcodeproj, :link_with, :platform, :parent, :exclusive
def initialize(name, options = {}) def initialize(name, options = {})
...@@ -64,7 +64,8 @@ module Pod ...@@ -64,7 +64,8 @@ module Pod
# Returns a path, which is relative to the project_root, relative to the # Returns a path, which is relative to the project_root, relative to the
# `$(SRCROOT)` of the user's project. # `$(SRCROOT)` of the user's project.
def relative_to_srcroot(path) def relative_to_srcroot(path)
(config.project_root + path).relative_path_from(xcodeproj.dirname) raise Informative, "[!] Unable to find an Xcode project to integrate".red unless xcodeproj || !config.integrate_targets
xcodeproj ? (config.project_root + path).relative_path_from(xcodeproj.dirname) : path
end end
def relative_pods_root def relative_pods_root
...@@ -368,7 +369,7 @@ module Pod ...@@ -368,7 +369,7 @@ module Pod
# This is used as a workaround for a compiler bug with non-ARC projects. # This is used as a workaround for a compiler bug with non-ARC projects.
# (see https://github.com/CocoaPods/CocoaPods/issues/142) # (see https://github.com/CocoaPods/CocoaPods/issues/142)
# #
# This was originally done automatically but libtool as of Xcode 4.3.2 no # 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 # longer seems to support the -fobjc-arc flag. Therefore it now has to be
# enabled explicitly using this method. # enabled explicitly using this method.
# #
...@@ -397,7 +398,7 @@ module Pod ...@@ -397,7 +398,7 @@ module Pod
def generate_bridge_support? def generate_bridge_support?
@generate_bridge_support @generate_bridge_support
end end
def set_arc_compatibility_flag? def set_arc_compatibility_flag?
@set_arc_compatibility_flag @set_arc_compatibility_flag
end end
......
...@@ -20,10 +20,10 @@ module Pod ...@@ -20,10 +20,10 @@ module Pod
targets_and_specs = {} targets_and_specs = {}
@podfile.target_definitions.values.each do |target_definition| @podfile.target_definitions.values.each do |target_definition|
puts "\nResolving dependencies for target `#{target_definition.name}'".green if config.verbose? puts "\nResolving dependencies for target `#{target_definition.name}' (#{target_definition.platform})".green if config.verbose?
@loaded_specs = [] @loaded_specs = []
# TODO @podfile.platform will change to target_definition.platform # TODO @podfile.platform will change to target_definition.platform
find_dependency_sets(@podfile, target_definition.dependencies, target_definition.platform) find_dependency_sets(@podfile, target_definition.dependencies, target_definition)
targets_and_specs[target_definition] = @specs.values_at(*@loaded_specs).sort_by(&:name) targets_and_specs[target_definition] = @specs.values_at(*@loaded_specs).sort_by(&:name)
end end
...@@ -56,11 +56,11 @@ module Pod ...@@ -56,11 +56,11 @@ module Pod
end end
end end
def find_dependency_sets(dependent_specification, dependencies, platform) def find_dependency_sets(dependent_specification, dependencies, target_definition)
@log_indent += 1 @log_indent += 1
dependencies.each do |dependency| dependencies.each do |dependency|
puts ' ' * @log_indent + "- #{dependency}" if config.verbose? puts ' ' * @log_indent + "- #{dependency}" if config.verbose?
set = find_cached_set(dependency, platform) set = find_cached_set(dependency, target_definition.platform)
set.required_by(dependent_specification) set.required_by(dependent_specification)
# Ensure we don't resolve the same spec twice for one target # Ensure we don't resolve the same spec twice for one target
unless @loaded_specs.include?(dependency.name) unless @loaded_specs.include?(dependency.name)
...@@ -71,24 +71,21 @@ module Pod ...@@ -71,24 +71,21 @@ module Pod
if dependency.subspec_dependency? if dependency.subspec_dependency?
spec = spec.subspec_by_name(dependency.name) spec = spec.subspec_by_name(dependency.name)
end end
validate_platform!(spec)
@loaded_specs << spec.name @loaded_specs << spec.name
@specs[spec.name] = spec @specs[spec.name] = spec
# And recursively load the dependencies of the spec. # And recursively load the dependencies of the spec.
# TODO fix the need to return an empty arrayf if there are no deps for the given platform # TODO fix the need to return an empty arrayf if there are no deps for the given platform
find_dependency_sets(spec, (spec.dependencies[platform.to_sym] || []), platform) find_dependency_sets(spec, (spec.dependencies[target_definition.platform.to_sym] || []), target_definition)
end end
validate_platform!(spec || @specs[dependency.name], target_definition)
end end
@log_indent -= 1 @log_indent -= 1
end end
def validate_platform!(spec) def validate_platform!(spec, target)
plaform = @podfile.target_definitions[:default].platform unless spec.platforms.any? { |platform| target.platform.support?(platform) }
unless plaform.support?(spec.platform) raise Informative, "[!] The platform of the target `#{target.name}' (#{target.platform}) is not compatible with `#{spec}' which has a minimun requirement of #{spec.platforms.join(' - ')}.".red
raise Informative, "The platform required by the Podfile `#{plaform}' does not match that of #{spec} `#{spec.platform}'"
end end
end end
end end
......
...@@ -34,6 +34,7 @@ module Pod ...@@ -34,6 +34,7 @@ module Pod
@define_for_platforms = [:osx, :ios] @define_for_platforms = [:osx, :ios]
@clean_paths, @subspecs = [], [] @clean_paths, @subspecs = [], []
@dependencies, @source_files, @resources = { :ios => [], :osx => [] }, { :ios => [], :osx => [] }, { :ios => [], :osx => [] } @dependencies, @source_files, @resources = { :ios => [], :osx => [] }, { :ios => [], :osx => [] }, { :ios => [], :osx => [] }
@deployment_target = {}
@platform = Platform.new(nil) @platform = Platform.new(nil)
@xcconfig = { :ios => Xcodeproj::Config.new, :osx => Xcodeproj::Config.new } @xcconfig = { :ios => Xcodeproj::Config.new, :osx => Xcodeproj::Config.new }
@compiler_flags = { :ios => '', :osx => '' } @compiler_flags = { :ios => '', :osx => '' }
...@@ -114,17 +115,14 @@ module Pod ...@@ -114,17 +115,14 @@ module Pod
end end
def platform=(platform) def platform=(platform)
if platform.class == Array @platform = Platform.new(*platform)
name = platform[0]
options = platform[1]
else
name = platform
options = nil
end
@platform = Platform.new(name, options)
end end
attr_reader :platform attr_reader :platform
def platforms
@platform.nil? ? @define_for_platforms.map { |platfrom| Platform.new(platfrom, @deployment_target[platfrom]) } : [platform]
end
def requires_arc=(requires_arc) def requires_arc=(requires_arc)
self.compiler_flags = '-fobjc-arc' if requires_arc self.compiler_flags = '-fobjc-arc' if requires_arc
@requires_arc = requires_arc @requires_arc = requires_arc
...@@ -157,7 +155,7 @@ module Pod ...@@ -157,7 +155,7 @@ module Pod
@specification, @platform = specification, platform @specification, @platform = specification, platform
end end
%w{ source_files= resource= resources= xcconfig= framework= frameworks= library= libraries= compiler_flags= dependency }.each do |method| %w{ source_files= resource= resources= xcconfig= framework= frameworks= library= libraries= compiler_flags= deployment_target= dependency }.each do |method|
define_method(method) do |args| define_method(method) do |args|
@specification._on_platform(@platform) do @specification._on_platform(@platform) do
@specification.send(method, args) @specification.send(method, args)
...@@ -181,6 +179,11 @@ module Pod ...@@ -181,6 +179,11 @@ module Pod
end end
attr_reader :source_files attr_reader :source_files
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 resources=(patterns) def resources=(patterns)
@define_for_platforms.each do |platform| @define_for_platforms.each do |platform|
@resources[platform] = pattern_list(patterns) @resources[platform] = pattern_list(patterns)
...@@ -340,49 +343,6 @@ module Pod ...@@ -340,49 +343,6 @@ module Pod
"#<#{self.class.name} for #{to_s}>" "#<#{self.class.name} for #{to_s}>"
end end
def validate!
missing = []
missing << "name" unless name
missing << "version" unless version
missing << "summary" unless summary
missing << "homepage" unless homepage
missing << "author(s)" unless authors
missing << "source or part_of" unless source || part_of
missing << "source_files" if source_files.empty? && subspecs.empty?
# TODO
# * validate subspecs
incorrect = []
allowed = [nil, :ios, :osx]
incorrect << "platform - accepted values are (no value, :ios, :osx)" unless allowed.include?(platform.name)
{
:source_files => source_files.values,
:resources => resources.values,
:clean_paths => clean_paths
}.each do |name, paths|
if paths.flatten.any? { |path| path.start_with?("/") }
incorrect << "#{name} - paths cannot start with a slash"
end
end
if source && source[:local] && source[:local].start_with?("/")
incorrect << "source[:local] - paths cannot start with a slash"
end
no_errors_found = missing.empty? && incorrect.empty?
unless no_errors_found
message = "\n[!] The #{name || 'nameless'} specification is incorrect\n".red
missing.each {|s| message << " - Missing #{s}\n"}
incorrect.each {|s| message << " - Incorrect #{s}\n"}
message << "\n"
raise Informative, message
end
no_errors_found
end
# This is a convenience method which gets called after all pods have been # This is a convenience method which gets called after all pods have been
# downloaded, installed, and the Xcode project and related files have been # downloaded, installed, and the Xcode project and related files have been
# generated. (It receives the Pod::Installer::Target instance for the current # generated. (It receives the Pod::Installer::Target instance for the current
...@@ -436,7 +396,7 @@ module Pod ...@@ -436,7 +396,7 @@ module Pod
end end
# Override the getters to always return the value of the top level parent spec. # Override the getters to always return the value of the top level parent spec.
[:version, :summary, :platform, :license, :authors, :requires_arc, :compiler_flags, :documentation].each do |attr| [:version, :summary, :platform, :license, :authors, :requires_arc, :compiler_flags, :documentation, :homepage].each do |attr|
define_method(attr) { top_level_parent.send(attr) } define_method(attr) { top_level_parent.send(attr) }
end end
......
...@@ -41,6 +41,11 @@ module Pod ...@@ -41,6 +41,11 @@ module Pod
get_value(set, :gh_forks) get_value(set, :gh_forks)
end end
def github_pushed_at(set)
github_stats_if_needed(set)
get_value(set, :pushed_at)
end
private private
def cache def cache
...@@ -88,9 +93,10 @@ module Pod ...@@ -88,9 +93,10 @@ module Pod
end end
cache[set.name] ||= {} cache[set.name] ||= {}
set_value(set, :gh_watchers, repo['watchers']) set_value(set, :gh_watchers, repo['watchers'])
set_value(set, :gh_forks, repo['forks']) set_value(set, :gh_forks, repo['forks'])
set_value(set, :gh_date, Time.now) set_value(set, :pushed_at, repo['pushed_at'])
set_value(set, :gh_date, Time.now)
save_cache save_cache
end end
end end
......
...@@ -22,15 +22,16 @@ describe Pod::Command::Presenter do ...@@ -22,15 +22,16 @@ describe Pod::Command::Presenter do
end end
it "presents the stats of a specification set" do it "presents the stats of a specification set" do
repo = {"forks"=>42, "watchers"=>318} repo = { "forks"=>42, "watchers"=>318, "pushed_at"=>"2011-01-26T19:06:43Z" }
Octokit.expects(:repo).with("robbiehanson/CocoaLumberjack").returns(repo) Octokit.expects(:repo).with("robbiehanson/CocoaLumberjack").returns(repo)
presenter = Presenter.new(argv('--stats')) presenter = Presenter.new(argv('--stats', '--no-color'))
output = presenter.describe(@set) output = presenter.describe(@set)
output.should.include? 'Author: Robbie Hanson' output.should.include? 'Author: Robbie Hanson'
output.should.include? 'License: BSD' output.should.include? 'License: BSD'
output.should.include? 'Platform: iOS - OS X' output.should.include? 'Platform: iOS - OS X'
output.should.include? 'Watchers: 318' output.should.include? 'Watchers: 318'
output.should.include? 'Forks: 42' output.should.include? 'Forks: 42'
output.should.include? 'Pushed: more than a year ago'
end end
it "should print at least one subspec" do it "should print at least one subspec" do
......
require File.expand_path('../../../spec_helper', __FILE__)
describe Pod::Command::Push do
extend SpecHelper::Command
extend SpecHelper::Git
extend SpecHelper::TemporaryDirectory
it "complains for wrong parameters" do
lambda { run_command('push') }.should.raise Pod::Command::Help
lambda { run_command('push', '--allow-warnings') }.should.raise Pod::Command::Help
lambda { run_command('push', '--wrong-option') }.should.raise Pod::Command::Help
end
it "complains if it can't find the repo" do
repo1 = add_repo('repo1', fixture('spec-repos/master'))
Dir.chdir(fixture('banana-lib')) do
lambda { run_command('push', 'repo2') }.should.raise Pod::Informative
end
end
it "complains if it can't find a spec" do
repo1 = add_repo('repo1', fixture('spec-repos/master'))
lambda { run_command('push', 'repo1') }.should.raise Pod::Informative
end
it "it raises if the pod is not validated" do
repo1 = add_repo('repo1', fixture('spec-repos/master'))
git('repo1', 'checkout master') # checkout master, because the fixture is a submodule
repo2 = add_repo('repo2', repo1.dir)
git_config('repo2', 'remote.origin.url').should == (tmp_repos_path + 'repo1').to_s
Dir.chdir(fixture('banana-lib')) do
lambda { command('push', 'repo2', '--silent').run }.should.raise Pod::Informative
end
# (repo1.dir + 'BananaLib/1.0/BananaLib.podspec').read.should.include 'Added!'
end
before do
# prepare the repos
@upstream = add_repo('upstream', fixture('spec-repos/master'))
git('upstream', 'checkout -b master') # checkout master, because the fixture is a submodule
@local_repo = add_repo('local_repo', @upstream.dir)
git_config('local_repo', 'remote.origin.url').should == (tmp_repos_path + 'upstream').to_s
git('upstream', 'checkout -b no-master') # checkout no-master, to allow push in a non-bare repository
# prepare the spec
spec_fix = (fixture('spec-repos') + 'master/JSONKit/1.4/JSONKit.podspec').read
spec_add = spec_fix.gsub(/https:\/\/github\.com\/johnezang\/JSONKit\.git/, fixture('integration/JSONKit').to_s)
spec_add.gsub!(/'JSONKit'/, "'PushTest'")
File.open(temporary_directory + 'JSONKit.podspec', 'w') {|f| f.write(spec_fix) }
File.open(temporary_directory + 'PushTest.podspec', 'w') {|f| f.write(spec_add) }
end
it "refuses to push if the repo is not clean" do
File.open(@local_repo.dir + 'README', 'w') {|f| f.write('Added!') }
(@local_repo.dir + 'README').read.should.include 'Added!'
cmd = command('push', 'local_repo')
cmd.expects(:validate_podspec_files).returns(true)
Dir.chdir(temporary_directory) { lambda { cmd.run }.should.raise Pod::Informative }
git('upstream', 'checkout master') # checkout master, because the fixture is a submodule
(@upstream.dir + 'PushTest/1.4/PushTest.podspec').should.not.exist?
end
it "sucessfully pushes a spec" do
cmd = command('push', 'local_repo')
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)')
git('upstream', 'checkout master') # checkout master, because the fixture is a submodule
(@upstream.dir + 'PushTest/1.4/PushTest.podspec').read.should.include('PushTest')
end
end
...@@ -3,6 +3,7 @@ require File.expand_path('../../../spec_helper', __FILE__) ...@@ -3,6 +3,7 @@ require File.expand_path('../../../spec_helper', __FILE__)
describe "Pod::Command::Search" do describe "Pod::Command::Search" do
extend SpecHelper::Command extend SpecHelper::Command
extend SpecHelper::Git extend SpecHelper::Git
extend SpecHelper::TemporaryDirectory
before do before do
config.repos_dir = fixture('spec-repos') config.repos_dir = fixture('spec-repos')
...@@ -19,6 +20,7 @@ describe "Pod::Command::Search" do ...@@ -19,6 +20,7 @@ describe "Pod::Command::Search" do
end end
it "complains for wrong parameters" do it "complains for wrong parameters" do
lambda { run_command('search') }.should.raise Pod::Command::Help
lambda { run_command('search', 'too', 'many') }.should.raise Pod::Command::Help lambda { run_command('search', 'too', 'many') }.should.raise Pod::Command::Help
lambda { run_command('search', 'too', '--wrong') }.should.raise Pod::Command::Help lambda { run_command('search', 'too', '--wrong') }.should.raise Pod::Command::Help
lambda { run_command('search', '--wrong') }.should.raise Pod::Command::Help lambda { run_command('search', '--wrong') }.should.raise Pod::Command::Help
......
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
describe "Pod::Command::Spec" do describe Pod::Command::Spec do
extend SpecHelper::Command extend SpecHelper::Command
extend SpecHelper::Github
extend SpecHelper::TemporaryDirectory extend SpecHelper::TemporaryDirectory
it "runs with correct parameters" do
lambda{ run_command('spec', 'create', 'Bananas') }.should.not.raise
expect_github_repo_request
expect_github_user_request
expect_github_tags_request
lambda{ run_command('spec', 'create', 'https://github.com/lukeredpath/libPusher.git') }.should.not.raise
end
it "complains for wrong parameters" do it "complains for wrong parameters" do
lambda { run_command('spec') }.should.raise Pod::Command::Help
lambda { run_command('spec', 'create') }.should.raise Pod::Command::Help
lambda { run_command('spec', '--create') }.should.raise Pod::Command::Help lambda { run_command('spec', '--create') }.should.raise Pod::Command::Help
lambda { run_command('spec', 'NAME') }.should.raise Pod::Command::Help
lambda { run_command('spec', 'createa') }.should.raise Pod::Command::Help lambda { run_command('spec', 'createa') }.should.raise Pod::Command::Help
lambda { run_command('spec', 'create') }.should.raise Pod::Command::Help lambda { run_command('lint', 'agument1', '2') }.should.raise Pod::Command::Help
end end
end
describe "Pod::Command::Spec#create" do
extend SpecHelper::Command
extend SpecHelper::Github
extend SpecHelper::TemporaryDirectory
extend SpecHelper::Git
it "creates a new podspec stub file" do it "creates a new podspec stub file" do
run_command('spec', 'create', 'Bananas') run_command('spec', 'create', 'Bananas')
...@@ -50,6 +51,22 @@ describe "Pod::Command::Spec" do ...@@ -50,6 +51,22 @@ describe "Pod::Command::Spec" do
spec.source.should == { :git => 'https://github.com/lukeredpath/libPusher.git', :tag => 'v1.3' } spec.source.should == { :git => 'https://github.com/lukeredpath/libPusher.git', :tag => 'v1.3' }
end end
it "accepts a name when creating a podspec form github" do
expect_github_repo_request
expect_github_user_request
expect_github_tags_request
run_command('spec', 'create', 'other_name', 'https://github.com/lukeredpath/libPusher.git')
path = temporary_directory + 'other_name.podspec'
spec = Pod::Specification.from_file(path)
spec.name.should == 'other_name'
spec.license.should == { :type => "MIT", :file => "LICENSE" }
spec.version.should == Pod::Version.new('1.3')
spec.summary.should == 'An Objective-C interface to Pusher (pusherapp.com)'
spec.homepage.should == 'https://github.com/lukeredpath/libPusher'
spec.authors.should == {"Luke Redpath"=>"luke@lukeredpath.co.uk"}
spec.source.should == { :git => 'https://github.com/lukeredpath/libPusher.git', :tag => 'v1.3' }
end
it "correctly suggests the head commit if a suitable tag is not available on github" do it "correctly suggests the head commit if a suitable tag is not available on github" do
expect_github_repo_request expect_github_repo_request
expect_github_user_request expect_github_user_request
...@@ -71,8 +88,53 @@ describe "Pod::Command::Spec" do ...@@ -71,8 +88,53 @@ describe "Pod::Command::Spec" do
output.should.include 'MARKDOWN TEMPLATE' output.should.include 'MARKDOWN TEMPLATE'
output.should.include 'Please add semantic version tags' output.should.include 'Please add semantic version tags'
end end
end end
describe "Pod::Command::Spec#lint" do
extend SpecHelper::Command
extend SpecHelper::TemporaryDirectory
extend SpecHelper::Git
before do
config.repos_dir = fixture('spec-repos')
end
after do
config.repos_dir = tmp_repos_path
end
it "lints a repo" do
# The fixture has an error due to a name mismatch
cmd = command('spec', 'lint', 'master')
lambda { cmd.run }.should.raise Pod::Informative
cmd.output.should.include "InAppSettingKit (0.0.1)\n - ERROR | The name of the spec should match the name of the file"
cmd.output.should.include "WARN"
end
it "complains if no repo name or url are provided and there a no specs in the current working directory" do
Dir.chdir(fixture('spec-repos') + 'master/JSONKit/') do
lambda { command('spec', 'lint').run }.should.raise Pod::Informative
end
end
it "lints the current working directory" do
Dir.chdir(fixture('spec-repos') + 'master/JSONKit/1.4/') do
output = command('spec', 'lint', '--quick', '--only-errors').run
output.should.include "passed validation"
end
end
it "lints a givent podspec" do
spec_file = fixture('spec-repos') + 'master/JSONKit/1.4/JSONKit.podspec'
cmd = command('spec', 'lint', '--quick', spec_file.to_s)
lambda { cmd.run }.should.raise Pod::Informative
cmd.output.should.include "Missing license[:file] or [:text]"
end
it "respects the -only--errors option" do
spec_file = fixture('spec-repos') + 'master/JSONKit/1.4/JSONKit.podspec'
cmd = command('spec', 'lint', '--quick', '--only-errors', spec_file.to_s)
lambda { cmd.run }.should.not.raise
cmd.output.should.include "Missing license[:file] or [:text]"
end
end
...@@ -84,9 +84,21 @@ describe "Pod::Downloader" do ...@@ -84,9 +84,21 @@ describe "Pod::Downloader" do
downloader = Pod::Downloader.for_pod(@pod) downloader = Pod::Downloader.for_pod(@pod)
VCR.use_cassette('tarballs', :record => :new_episodes) { downloader.download } VCR.use_cassette('tarballs', :record => :new_episodes) { downloader.download }
downloader.clean
(@pod.root + 'tarball.tar.gz').should.not.exist (@pod.root + 'tarball.tar.gz').should.not.exist
end end
it "removes the .git directory when cleaning" do
@pod.specification.stubs(:source).returns(
:git => "git://github.com/lukeredpath/libPusher.git", :download_only => false
)
downloader = Pod::Downloader.for_pod(@pod)
downloader.download
downloader.clean
(@pod.root + '.git').should.not.exist
end
end end
describe "for Mercurial" do describe "for Mercurial" do
......
...@@ -45,7 +45,7 @@ else ...@@ -45,7 +45,7 @@ else
@config_before = config @config_before = config
create_config! create_config!
config.doc = false config.generate_docs = false
end end
after do after do
...@@ -118,6 +118,26 @@ else ...@@ -118,6 +118,26 @@ else
'DEPENDENCIES' => ["Reachability (from `#{url}')"] 'DEPENDENCIES' => ["Reachability (from `#{url}')"]
} }
end end
it "install a dummy source file" do
create_config!
podfile = Pod::Podfile.new do
self.platform :ios
xcodeproj 'dummy'
dependency do |s|
s.name = 'JSONKit'
s.version = '1.2'
s.source = { :git => SpecHelper.fixture('integration/JSONKit').to_s, :tag => 'v1.2' }
s.source_files = 'JSONKit.*'
end
end
installer = SpecHelper::Installer.new(podfile)
installer.install!
dummy = (config.project_pods_root + 'PodsDummy.m').read
dummy.should.include?('@implementation PodsDummy')
end
it "installs a library with a podspec defined inline" do it "installs a library with a podspec defined inline" do
podfile = Pod::Podfile.new do podfile = Pod::Podfile.new do
...@@ -188,6 +208,8 @@ else ...@@ -188,6 +208,8 @@ else
dependency 'SSToolkit' dependency 'SSToolkit'
end end
Pod::Generator::Documentation.any_instance.stubs(:already_installed?).returns(false)
installer = SpecHelper::Installer.new(podfile) installer = SpecHelper::Installer.new(podfile)
installer.install! installer.install!
......
...@@ -9,6 +9,7 @@ end ...@@ -9,6 +9,7 @@ end
module SpecHelper module SpecHelper
module Command module Command
def command(*argv) def command(*argv)
argv << '--no-color'
Pod::Command.parse(*argv) Pod::Command.parse(*argv)
end end
......
require File.expand_path('../../../spec_helper', __FILE__)
describe "Pod::Command::Spec::Linter" do
extend SpecHelper::TemporaryDirectory
def write_podspec(text, name = 'JSONKit.podspec')
file = temporary_directory + 'JSONKit.podspec'
File.open(file, 'w') {|f| f.write(text) }
spec = Pod::Specification.from_file(file)
[spec, file]
end
def stub_podspec(pattern = nil, replacement = nil)
spec = (fixture('spec-repos') + 'master/JSONKit/1.4/JSONKit.podspec').read
spec.gsub!(/https:\/\/github\.com\/johnezang\/JSONKit\.git/, fixture('integration/JSONKit').to_s)
spec.gsub!(pattern, replacement) if pattern && replacement
spec
end
it "fails a specifications that does not contain the minimum required attributes" do
spec, file = write_podspec('Pod::Spec.new do |s| end')
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.lenient, linter.quick = true, true
linter.lint.should == false
linter.errors.join(' | ') =~ /name.*version.*summary.*homepage.*authors.*(source.*part_of).*source_files/
end
it "fails specifications if the name does not match the name of the file" do
spec, file = write_podspec(stub_podspec(/s.name *= 'JSONKit'/, "s.name = 'JSONKitAAA'"))
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.lenient, linter.quick = true, true
linter.lint.should == false
linter.errors.count.should == 1
linter.errors[0].should =~ /The name of the spec should match the name of the file/
end
it "fails a specification if a path starts with a slash" do
spec, file = write_podspec(stub_podspec(/s.source_files = 'JSONKit\.\*'/, "s.source_files = '/JSONKit.*'"))
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.lenient, linter.quick = true, true
linter.lint.should == false
linter.errors.count.should == 1
linter.errors[0].should =~ /Paths cannot start with a slash/
end
it "fails a specification if the plafrom is unrecognized" do
spec, file = write_podspec(stub_podspec(/s.name *= 'JSONKit'/, "s.name = 'JSONKit'\ns.platform = :iososx\n"))
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.lenient, linter.quick = true, true
linter.lint.should == false
linter.errors.count.should == 1
linter.errors[0].should =~ /Unrecognized platfrom/
end
it "fails validation if the specification contains warnings" do
spec, file = write_podspec(stub_podspec)
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.lenient, linter.quick = false, true
linter.lint.should == false
linter.errors.should.be.empty
linter.warnings.should.not.be.empty
end
it "validates in lenient mode if there are no erros but there are warnings" do
spec, file = write_podspec(stub_podspec)
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.lenient, linter.quick = true, true
linter.lint.should == true
linter.errors.should.be.empty
linter.warnings.should.not.be.empty
end
it "respects quick mode" do
spec, file = write_podspec(stub_podspec)
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.expects(:peform_multiplatform_analysis).never
linter.expects(:install_pod).never
linter.expects(:xcodebuild_output_for_platfrom).never
linter.expects(:file_patterns_errors_for_platfrom).never
linter.lenient, linter.quick = false, true
linter.lint
end
it "produces deprecation notices" do
spec, file = write_podspec(stub_podspec(/s\.source_files = 'JSONKit\.\*'/, "s.source_files = 'JSONKit.*'\n if config.ios?\nend"))
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.lenient, linter.quick = false, true
linter.lint.should == false
linter.errors.should.be.empty
linter.warnings.join(' | ').should =~ /`config.ios\?' and `config.osx\?' are deprecated and will be removed in version 0.7/
end
it "uses xcodebuild to generate notes and warnings" do
spec, file = write_podspec(stub_podspec)
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.lenient, linter.quick = false, false
linter.lint.should == false
linter.notes.join(' | ').should.include "JSONKit/JSONKit.m:1640:27: warning: equality comparison with extraneous parentheses" unless `which xcodebuild`.strip.empty?
end
it "checks for file patterns" do
spec, file = write_podspec(stub_podspec(/s\.source_files = 'JSONKit\.\*'/, "s.source_files = 'JSONKit.*'\ns.resources = 'WRONG_FOLDER'"))
linter = Pod::Command::Spec::Linter.new(spec, file)
linter.stubs(:xcodebuild_output_for_platfrom).returns([])
linter.lenient, linter.quick = false, false
linter.lint.should == false
linter.errors.join(' | ').should.include "[resources = 'WRONG_FOLDER'] -> did not match any file"
end
end
...@@ -36,6 +36,7 @@ describe Pod::Generator::Documentation do ...@@ -36,6 +36,7 @@ describe Pod::Generator::Documentation do
'--keep-undocumented-objects', '--keep-undocumented-objects',
'--keep-undocumented-members', '--keep-undocumented-members',
'--keep-intermediate-files', '--keep-intermediate-files',
'--exit-threshold', '2',
'--index-desc', 'README', '--index-desc', 'README',
# TODO We need to either make this a hash so that options can be merged # TODO We need to either make this a hash so that options can be merged
# or not use any defaults in case an options are specified. # or not use any defaults in case an options are specified.
......
require File.expand_path('../../../spec_helper', __FILE__)
describe Pod::Generator::DummySource do
extend SpecHelper::TemporaryDirectory
before do
setup_temporary_directory
end
after do
teardown_temporary_directory
end
it "generates a dummy sourcefile with the appropriate class" do
generator = Pod::Generator::DummySource.new
file = temporary_directory + 'PodsDummy.m'
generator.save_as(file)
file.read.should == <<-EOS
@interface PodsDummy : NSObject
@end
@implementation PodsDummy
@end
EOS
end
end
...@@ -26,8 +26,8 @@ describe "Pod::Installer" do ...@@ -26,8 +26,8 @@ describe "Pod::Installer" do
@xcconfig['ALWAYS_SEARCH_USER_PATHS'].should == 'YES' @xcconfig['ALWAYS_SEARCH_USER_PATHS'].should == 'YES'
end end
it "configures the project to load categories from the static library" do it "configures the project to load all members that implement Objective-c classes or categories from the static library" do
@xcconfig['OTHER_LDFLAGS'].should == '-ObjC -all_load' @xcconfig['OTHER_LDFLAGS'].should == '-ObjC'
end end
it "sets the PODS_ROOT build variable" do it "sets the PODS_ROOT build variable" do
......
...@@ -18,36 +18,44 @@ describe Pod::Platform do ...@@ -18,36 +18,44 @@ describe Pod::Platform do
@platform.should == Pod::Platform.new(:ios) @platform.should == Pod::Platform.new(:ios)
end 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 it "can be compared for equality with a matching symbolic name (backwards compatibility reasons)" do
@platform.should == :ios @platform.should == :ios
end end
it "presents an accurate string representation" do it "presents an accurate string representation" do
@platform.to_s.should == "iOS" @platform.to_s.should == "iOS"
Pod::Platform.new(:osx).to_s.should == 'OS X' Pod::Platform.new(:osx).to_s.should == 'OS X'
Pod::Platform.new(nil).to_s.should == "iOS - OS X" Pod::Platform.new(nil).to_s.should == "iOS - OS X"
Pod::Platform.new(:ios, { :deployment_target => '5.0.0' }).to_s.should == 'iOS 5.0.0' Pod::Platform.new(:ios, '5.0.0').to_s.should == 'iOS 5.0.0'
Pod::Platform.new(:osx, { :deployment_target => '10.7' }).to_s.should == 'OS X 10.7' Pod::Platform.new(:osx, '10.7').to_s.should == 'OS X 10.7'
end
it "correctly indicates if it supports another platfrom" do
ios4 = Pod::Platform.new(:ios, { :deployment_target => '4.0.0' })
ios5 = Pod::Platform.new(:ios, { :deployment_target => '5.0.0' })
ios5.should.support?(ios4)
ios4.should.not.support?(ios5)
osx6 = Pod::Platform.new(:osx, { :deployment_target => '10.6' })
osx7 = Pod::Platform.new(:osx, { :deployment_target => '10.7' })
osx7.should.support?(osx6)
osx6.should.not.support?(osx7)
both = Pod::Platform.new(nil)
both.should.support?(ios4)
both.should.support?(osx6)
both.should.support?(nil)
end end
it "uses it's name as it's symbold version" do it "uses it's name as it's symbold version" do
@platform.to_sym.should == :ios @platform.to_sym.should == :ios
end 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
it "allows to specify the deployment target after initialization" do
p = Pod::Platform.new(:ios, '4.0.0')
p.deployment_target = '4.0.0'
p.deployment_target.should == Pod::Version.new('4.0.0')
p.deployment_target = Pod::Version.new('4.0.0')
p.deployment_target.should == Pod::Version.new('4.0.0')
end
end end
describe "Pod::Platform with a nil value" do describe "Pod::Platform with a nil value" do
...@@ -59,3 +67,35 @@ describe "Pod::Platform with a nil value" do ...@@ -59,3 +67,35 @@ describe "Pod::Platform with a nil value" do
@platform.should.be.nil @platform.should.be.nil
end end
end end
describe "Pod::Platform#support?" do
it "supports another platform is with the same operating system" do
p1 = Pod::Platform.new(:ios)
p2 = Pod::Platform.new(:ios)
p1.should.support?(p2)
p1 = Pod::Platform.new(:osx)
p2 = Pod::Platform.new(:osx)
p1.should.support?(p2)
end
it "supports a nil platform" do
p1 = Pod::Platform.new(:ios)
p1.should.support?(nil)
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.support?(p1)
p1.should.support?(p2)
p2.should.not.support?(p1)
end
it "supports a platform regardless of the deployment_target if one of the two does not specify it" do
p1 = Pod::Platform.new(:ios)
p2 = Pod::Platform.new(:ios, '4.0')
p1.should.support?(p2)
p2.should.support?(p1)
end
end
...@@ -48,7 +48,7 @@ describe "Pod::Podfile" do ...@@ -48,7 +48,7 @@ describe "Pod::Podfile" do
Pod::Podfile.new {}.should.not.generate_bridge_support Pod::Podfile.new {}.should.not.generate_bridge_support
Pod::Podfile.new { generate_bridge_support! }.should.generate_bridge_support Pod::Podfile.new { generate_bridge_support! }.should.generate_bridge_support
end end
it 'specifies that ARC compatibility flag should be generated' do it 'specifies that ARC compatibility flag should be generated' do
Pod::Podfile.new { set_arc_compatibility_flag! }.should.set_arc_compatibility_flag Pod::Podfile.new { set_arc_compatibility_flag! }.should.set_arc_compatibility_flag
end end
...@@ -260,6 +260,15 @@ describe "Pod::Podfile" do ...@@ -260,6 +260,15 @@ describe "Pod::Podfile" do
@target_definition.relative_pods_root.should == '${SRCROOT}/../Pods' @target_definition.relative_pods_root.should == '${SRCROOT}/../Pods'
end 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.stubs(:xcodeproj).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 it "returns the xcconfig file path relative to the project's $(SRCROOT)" do
@target_definition.xcconfig_relative_path.should == '../Pods/Pods.xcconfig' @target_definition.xcconfig_relative_path.should == '../Pods/Pods.xcconfig'
end end
...@@ -271,6 +280,17 @@ describe "Pod::Podfile" do ...@@ -271,6 +280,17 @@ describe "Pod::Podfile" do
end end
describe "concerning validations" do describe "concerning validations" do
it "raises if it should integrate and can't find an xcodeproj" do
config.integrate_targets.should.equal true
target_definition = Pod::Podfile.new {}.target_definitions[:default]
target_definition.stubs(:xcodeproj).returns(nil)
exception = lambda {
target_definition.relative_pods_root
}.should.raise Pod::Informative
exception.message.should.include "Xcode project"
end
xit "raises if no platform is specified" do xit "raises if no platform is specified" do
exception = lambda { exception = lambda {
Pod::Podfile.new {}.validate! Pod::Podfile.new {}.validate!
......
...@@ -39,7 +39,7 @@ describe "Pod::Resolver" do ...@@ -39,7 +39,7 @@ describe "Pod::Resolver" do
lambda { @resolver.resolve }.should.not.raise lambda { @resolver.resolve }.should.not.raise
end end
it "raises once any of the dependencies does not match the platform of the root spec (Podfile)" do it "raises once any of the dependencies does not match the platform of its podfile target" do
set = Pod::Spec::Set.new(config.repos_dir + 'master/ASIHTTPRequest') set = Pod::Spec::Set.new(config.repos_dir + 'master/ASIHTTPRequest')
@resolver.cached_sets['ASIHTTPRequest'] = set @resolver.cached_sets['ASIHTTPRequest'] = set
...@@ -59,27 +59,15 @@ describe "Pod::Resolver" do ...@@ -59,27 +59,15 @@ describe "Pod::Resolver" do
lambda { @resolver.resolve }.should.raise Pod::Informative lambda { @resolver.resolve }.should.raise Pod::Informative
end end
it "does not raise if all of the dependencies have a deployment target equal or lower of the root spec (Podfile)" do it "raises once any of the dependencies does not have a deployment_target compatible with its podfile target" do
set = Pod::Spec::Set.new(config.repos_dir + 'master/ASIHTTPRequest') set = Pod::Spec::Set.new(config.repos_dir + 'master/ASIHTTPRequest')
@resolver.cached_sets['ASIHTTPRequest'] = set @resolver.cached_sets['ASIHTTPRequest'] = set
@podfile.platform :ios, "4.0"
def set.stub_platform=(platform); @stubbed_platform = platform; end Pod::Specification.any_instance.stubs(:platforms).returns([ Pod::Platform.new(:ios, '4.0'), Pod::Platform.new(:osx, '10.7') ])
def set.specification; spec = super; spec.platform = @stubbed_platform; spec; end
@podfile.platform :ios, { :deployment_target => "4.0.0" }
set.stub_platform = :ios, { :deployment_target => "4.0.0" }
lambda { @resolver.resolve }.should.not.raise lambda { @resolver.resolve }.should.not.raise
end
it "raises once any of the dependencies requires a higher deployment target of the root spec (Podfile)" do
set = Pod::Spec::Set.new(config.repos_dir + 'master/ASIHTTPRequest')
@resolver.cached_sets['ASIHTTPRequest'] = set
def set.stub_platform=(platform); @stubbed_platform = platform; end
def set.specification; spec = super; spec.platform = @stubbed_platform; spec; end
@podfile.platform :ios, { :deployment_target => "4.0.0" } Pod::Specification.any_instance.stubs(:platforms).returns([ Pod::Platform.new(:ios, '5.0'), Pod::Platform.new(:osx, '10.7') ])
set.stub_platform = :ios, { :deployment_target => "5.0.0" }
lambda { @resolver.resolve }.should.raise Pod::Informative lambda { @resolver.resolve }.should.raise Pod::Informative
end end
......
...@@ -149,28 +149,22 @@ describe "A Pod::Specification, in general," do ...@@ -149,28 +149,22 @@ describe "A Pod::Specification, in general," do
@spec = Pod::Spec.new @spec = Pod::Spec.new
end end
def validate(&block) it "returns the platform that the static library should be build for" do
Proc.new(&block).should.raise(Pod::Informative) @spec.platform = :ios
@spec.platform.should == :ios
end end
it "raises if the specification does not contain the minimum required attributes" do it "returns the platform and the deployment target" do
exception = validate { @spec.validate! } @spec.platform = :ios, '4.0'
exception.message =~ /name.*version.*summary.*homepage.*authors.*(source.*part_of).*source_files/ @spec.platform.should == :ios
@spec.platform.deployment_target.should == Pod::Version.new('4.0')
end end
it "raises if the platform is unrecognized" do it "returns the platfroms for which the pod is supported" do
validate { @spec.validate! }.message.should.not.include 'platform' @spec.platform = :ios, '4.0'
@spec.platform = :ios @spec.platforms.count.should == 1
validate { @spec.validate! }.message.should.not.include 'platform' @spec.platforms.first.should == :ios
@spec.platform = :osx @spec.platforms.first.deployment_target.should == Pod::Version.new('4.0')
validate { @spec.validate! }.message.should.not.include 'platform'
@spec.platform = :windows
validate { @spec.validate! }.message.should.include 'platform'
end
it "returns the platform that the static library should be build for" do
@spec.platform = :ios
@spec.platform.should == :ios
end end
it "returns the license of the Pod" do it "returns the license of the Pod" do
...@@ -185,7 +179,7 @@ describe "A Pod::Specification, in general," do ...@@ -185,7 +179,7 @@ describe "A Pod::Specification, in general," do
:text => 'Permission is hereby granted ...' :text => 'Permission is hereby granted ...'
} }
end end
it "returns the license of the Pod specified in the old format" do it "returns the license of the Pod specified in the old format" do
@spec.license = 'MIT' @spec.license = 'MIT'
@spec.license.should == { @spec.license.should == {
...@@ -200,7 +194,7 @@ describe "A Pod::Specification, in general," do ...@@ -200,7 +194,7 @@ describe "A Pod::Specification, in general," do
'--project-company', '"Company Name"', '--project-company', '"Company Name"',
'--company-id', 'com.company', '--company-id', 'com.company',
'--ignore', 'Common', '--ignore', 'Common',
'--ignore', '.m'] '--ignore', '.m']
} }
@spec.documentation[:html].should == 'http://EXAMPLE/#{@name}/documentation' @spec.documentation[:html].should == 'http://EXAMPLE/#{@name}/documentation'
@spec.documentation[:appledoc].should == ['--project-name', '#{@name}', @spec.documentation[:appledoc].should == ['--project-name', '#{@name}',
...@@ -305,11 +299,11 @@ describe "A Pod::Specification with :local source" do ...@@ -305,11 +299,11 @@ describe "A Pod::Specification with :local source" do
s.source_files = "." s.source_files = "."
end end
end end
it "is marked as local" do it "is marked as local" do
@spec.should.be.local @spec.should.be.local
end end
it "it returns the expanded local path" do it "it returns the expanded local path" do
@spec.local_path.should == fixture("integration/JSONKit") @spec.local_path.should == fixture("integration/JSONKit")
end end
...@@ -381,6 +375,8 @@ describe "A Pod::Specification, concerning its attributes that support different ...@@ -381,6 +375,8 @@ describe "A Pod::Specification, concerning its attributes that support different
s.ios.dependency 'JSONKit' s.ios.dependency 'JSONKit'
s.osx.dependency 'SSZipArchive' s.osx.dependency 'SSZipArchive'
s.ios.deployment_target = '4.0'
end end
end end
...@@ -399,6 +395,12 @@ describe "A Pod::Specification, concerning its attributes that support different ...@@ -399,6 +395,12 @@ describe "A Pod::Specification, concerning its attributes that support different
} }
end end
it "returns the list of the supported platfroms and deployment targets" do
@spec.platforms.count.should == 2
@spec.platforms.should.include? Pod::Platform.new(:osx)
@spec.platforms.should.include? Pod::Platform.new(:ios, '4.0')
end
it "returns the same list of compiler flags for each platform" do it "returns the same list of compiler flags for each platform" do
@spec.compiler_flags.should == { @spec.compiler_flags.should == {
:ios => ' -Wdeprecated-implementations -fobjc-arc', :ios => ' -Wdeprecated-implementations -fobjc-arc',
......
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