Commit 6ca21338 authored by Fabio Pelosin's avatar Fabio Pelosin

Merge branch 'xcodeproj-refactor' into installer_clean_up

* xcodeproj-refactor: (48 commits)
  Changelog
  [Specs] Fix issue that lead to generation of files in the fixtures folder.
  Completed support for Xcodeproj refactor.
  [Gemfile] Use Xcodeproj refactor branch.
  Release 0.15.2
  [Bundle] Update.
  [Gemspec] Require Xcodeproj 0.3.5.
  Remove stray puts.
  Update the lockfiles of the examples.
  [Gemfile] Install and remove dependency on letters gem.
  [LocalPod] Always use Pathname::glob.
  [Gemspec] Use spermy indicator for xcodeproj.
  [Specs] Complete revert of 8e72cbe1
  Fixed a bug with the flags passed into `Dir.glob`
  Fixing a bug where `downcase` was being called on the list of used file paths
  Updated `clean_paths` and `expand_paths` methods to use `File::FNM_CASEFOLD`
  [Xcodeproj] Initial adaptation for refactor.
  [Linter/Push] Properly singularize words in output.
  [Bundler] Update.
  [UI] Support for running CocoaPods without a terminal.
  ...

Conflicts:
	lib/cocoapods/installer.rb
	spec/unit/installer_spec.rb
parents dec60494 ccdc81e6
## Master
[CocoaPods](https://github.com/CocoaPods/CocoaPods/compare/0.14.0...master)
[CocoaPods](https://github.com/CocoaPods/CocoaPods/compare/0.15.2...master)[Xcodeproj](https://github.com/CocoaPods/Xcodeproj/compare/0.3.5...master)
- Xcodeproj partial rewrite.
[#576](https://github.com/CocoaPods/CocoaPods/pull/576)
- Performance improvements in the `Generating supporting files` phase.
- Better support for editing existing projects and sorting groups.
## 0.15.2
[CocoaPods](https://github.com/CocoaPods/CocoaPods/compare/0.15.1...0.15.2)
###### Enhancements
- Added support for `.hh` headers.
[#576](https://github.com/CocoaPods/CocoaPods/pull/576)
###### Bug fixes
- Restored support for running CocoaPods without a terminal.
[#575](https://github.com/CocoaPods/CocoaPods/issues/575)
[#577](https://github.com/CocoaPods/CocoaPods/issues/577)
- The git cache now always uses a barebones repo preventing a number of related issues.
[#581](https://github.com/CocoaPods/CocoaPods/issues/581)
[#569](https://github.com/CocoaPods/CocoaPods/issues/569)
- Improved fix for the issue that lead to empty directories for Pods.
[#572](https://github.com/CocoaPods/CocoaPods/issues/572)
[#602](https://github.com/CocoaPods/CocoaPods/issues/602)
- Xcodeproj robustness against invalid values, such as malformed UTF8.
[#592](https://github.com/CocoaPods/CocoaPods/issues/592)
## 0.15.1
[CocoaPods](https://github.com/CocoaPods/CocoaPods/compare/0.15.0...0.15.1)
###### Enhancements
- Show error if syntax error in Podfile or Podfile.lock.
###### Bug fixes
- Fixed an issue that lead to empty directories for Pods.
[#519](https://github.com/CocoaPods/CocoaPods/issues/519)
[#568](https://github.com/CocoaPods/CocoaPods/issues/568)
- Fixed a crash related to the RubyGems version informative.
[#570](https://github.com/CocoaPods/CocoaPods/issues/570)
- Fixed a crash for `pod outdated`.
[#567](https://github.com/CocoaPods/CocoaPods/issues/567)
- Fixed an issue that lead to excessively slow sets computation.
## 0.15.0
[CocoaPods](https://github.com/CocoaPods/CocoaPods/compare/0.14.0...0.15.0)[Xcodeproj](https://github.com/CocoaPods/Xcodeproj/compare/0.3.3...0.3.4)
###### Enhancements
- Pod `install` will update the specs repo only if needed.
[#533](https://github.com/CocoaPods/CocoaPods/issues/533)
- CocoaPods now searches for the highest version of a Pod on
all the repos.
- CocoaPods now searches for the highest version of a Pod on all the repos.
[#85](https://github.com/CocoaPods/CocoaPods/issues/85)
- 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.
- Added support for linting a Podspec using the files from its folder `pod spec
lint --local`
- Refactored UI.
- Added support for Podfiles named `Podfile.cocoapods` which allows to
- Added support for Podfiles named `CocoaPods.podfile` which allows to
associate an editor application in Mac OS X.
[#528](https://github.com/CocoaPods/CocoaPods/issues/528)
- Added config option to disable the new version available message.
......@@ -24,6 +77,7 @@
###### Bug fixes
- Subspecs namespacing has been restored.
[#541](https://github.com/CocoaPods/CocoaPods/issues/541)
- Improvements to the git cache that should be more robust.
......@@ -33,6 +87,8 @@
[#514](https://github.com/CocoaPods/CocoaPods/issues/514)
- Forcing UTF-8 encoding on licenses generation in Ruby 1.9.
[#530](https://github.com/CocoaPods/CocoaPods/issues/530)
- Added support for `.hpp` headers.
[#244](https://github.com/CocoaPods/CocoaPods/issues/244)
## 0.14.0
......@@ -77,32 +133,26 @@
[#191](https://github.com/CocoaPods/CocoaPods/issues/191)
- the external source changes.
- the head status changes (from disabled to enabled or vice-versa).
- Introduce `pod update` command that installs the dependencies of the Podfile
**ignoring** the lockfile `Podfile.lock`.
[#131](https://github.com/CocoaPods/CocoaPods/issues/131)
- Introduce `pod outdated` command that shows the pods with known updates.
- Add `:local` option for dependencies which will use the source files directly
from a local directory. This is usually used for libraries that are being
developed in parallel to the end product (application/library).
[#458](https://github.com/CocoaPods/CocoaPods/issues/458),
[#415](https://github.com/CocoaPods/CocoaPods/issues/415),
[#156](https://github.com/CocoaPods/CocoaPods/issues/156).
- Folders of Pods which are no longer required are removed during installation.
[#298](https://github.com/CocoaPods/CocoaPods/issues/298)
- Add meaningful error messages for when:
- a podspec can’t be found in the root of an external source.
- Add meaningful error messages
- ia podspec can’t be found in the root of an external source.
[#385](https://github.com/CocoaPods/CocoaPods/issues/385),
[#338](https://github.com/CocoaPods/CocoaPods/issues/338),
[#337](https://github.com/CocoaPods/CocoaPods/issues/337).
- a subspec name is misspelled.
[#327](https://github.com/CocoaPods/CocoaPods/issues/327)
- an unrecognized command and/or argument is provided.
- The subversion downloader now does an export instead of a checkout, which
makes it play nicer with SCMs that store metadata in each directory.
[#245](https://github.com/CocoaPods/CocoaPods/issues/245)
......
......@@ -3,20 +3,25 @@ source "http://rubygems.org"
gemspec
group :development do
gem "xcodeproj", :git => "git://github.com/CocoaPods/Xcodeproj.git"
gem "xcodeproj", :git => "git://github.com/CocoaPods/Xcodeproj.git", :branch => "refactor"
gem "mocha", "~> 0.11.4"
gem "bacon"
gem "kicker", :git => "https://github.com/alloy/kicker.git", :branch => "3.0.0"
gem "mocha-on-bacon"
gem "rake"
gem "rb-fsevent"
gem "vcr"
gem "webmock"
end
group :debugging do
gem "rb-fsevent"
gem "kicker", :git => "https://github.com/alloy/kicker.git", :branch => "3.0.0"
gem "awesome_print"
gem "pry"
end
group :documentation do
gem 'yard'
gem 'redcarpet'
gem 'github-markup'
end
GIT
remote: git://github.com/CocoaPods/Xcodeproj.git
revision: e3c513a66dee94b681588326bf1ce8ca4a30aef3
revision: 280a500ed5ee5a5f6bb4e3c5edcfef0240fa427b
branch: refactor
specs:
xcodeproj (0.3.3)
xcodeproj (0.3.5)
activesupport (~> 3.2.6)
GIT
......@@ -17,7 +18,7 @@ GIT
PATH
remote: .
specs:
cocoapods (0.14.0)
cocoapods (0.15.2)
activesupport (~> 3.2.6)
colored (~> 1.2)
escape (~> 0.0.4)
......@@ -26,7 +27,7 @@ PATH
octokit (~> 1.7)
open4 (~> 1.3.0)
rake (~> 0.9.0)
xcodeproj (>= 0.3.3)
xcodeproj (~> 0.3.5)
GEM
remote: http://rubygems.org/
......@@ -35,9 +36,9 @@ GEM
i18n (~> 0.6)
multi_json (~> 1.0)
addressable (2.3.2)
awesome_print (1.0.2)
awesome_print (1.1.0)
bacon (1.1.0)
coderay (1.0.7)
coderay (1.0.8)
colored (1.2)
crack (0.3.1)
escape (0.0.4)
......@@ -49,16 +50,16 @@ GEM
hashie (1.2.0)
i18n (0.6.1)
json (1.7.5)
listen (0.5.0)
listen (0.5.3)
metaclass (0.0.1)
method_source (0.8)
method_source (0.8.1)
mocha (0.11.4)
metaclass (~> 0.0.1)
mocha-on-bacon (0.2.1)
mocha (>= 0.9.8)
multi_json (1.3.6)
multipart-post (1.1.5)
octokit (1.13.0)
octokit (1.18.0)
addressable (~> 2.2)
faraday (~> 0.8)
faraday_middleware (~> 0.8)
......@@ -70,15 +71,15 @@ GEM
method_source (~> 0.8)
slop (~> 3.3.1)
rake (0.9.2.2)
rb-fsevent (0.9.1)
redcarpet (2.1.1)
rb-fsevent (0.9.2)
redcarpet (2.2.2)
slop (3.3.3)
terminal-notifier (1.4.2)
vcr (2.2.5)
webmock (1.8.10)
webmock (1.8.11)
addressable (>= 2.2.7)
crack (>= 0.1.7)
yard (0.8.2.1)
yard (0.8.3)
PLATFORMS
ruby
......
Copyright (c) 2012 Eloy Durán <eloy.de.enige@gmail.com>
Copyright (c) 2011 - 2012 Eloy Durán <eloy.de.enige@gmail.com>
Copyright (c) 2012 Fabio Pelosin <fabiopelosin@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......
......@@ -33,7 +33,7 @@ namespace :travis do
task :install do
sh "git submodule update --init"
sh "sudo apt-get install subversion"
sh "env CFLAGS='-I#{rvm_ruby_dir}/include' bundle install"
sh "env CFLAGS='-I#{rvm_ruby_dir}/include' bundle install --without debugging documentation"
end
task :setup => [:install_opencflite_debs, :install]
......
......@@ -7,9 +7,9 @@ Gem::Specification.new do |s|
s.version = Pod::VERSION
s.date = Date.today
s.license = "MIT"
s.email = "eloy.de.enige@gmail.com"
s.email = ["eloy.de.enige@gmail.com", "fabiopelosin@gmail.com"]
s.homepage = "https://github.com/CocoaPods/CocoaPods"
s.authors = ["Eloy Duran"]
s.authors = ["Eloy Duran", "Fabio Pelosin"]
s.summary = "An Objective-C library package manager."
s.description = "CocoaPods manages library dependencies for your Xcode project.\n\n" \
......@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
s.executables = %w{ pod }
s.require_paths = %w{ lib }
s.add_runtime_dependency 'xcodeproj', '>= 0.3.3'
s.add_runtime_dependency 'xcodeproj', '~> 0.3.5'
s.add_runtime_dependency 'faraday', '~> 0.8.1'
s.add_runtime_dependency 'octokit', '~> 1.7'
......
PODS:
- AFNetworking (0.7.0):
COCOAPODS: 0.15.1
PODS:
- AFNetworking (0.7.0):
- JSONKit
- FormatterKit (1.0.0):
- FormatterKit (1.0.0):
- FormatterKit/ArrayFormatter (= 1.0.0)
- FormatterKit/LocationFormatter (= 1.0.0)
- FormatterKit/OrdinalNumberFormatter (= 1.0.0)
......@@ -17,19 +19,17 @@ PODS:
- FormatterKit/UnitOfInformationFormatter (1.0.0)
- JSONKit (1.5pre)
DEPENDENCIES:
- AFNetworking (~> 0.7.0)
- FormatterKit
SPEC CHECKSUMS:
AFNetworking: 1dd63e737768662b7180659e91846408df4ae7df
FormatterKit: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/ArrayFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/LocationFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/OrdinalNumberFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
SPEC CHECKSUMS:
FormatterKit/UnitOfInformationFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/TimeIntervalFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
AFNetworking: 7bf22b0ed1d9068909cd67206db78204eb63dd2c
JSONKit: a01a22c75f27eae76b4badd55a91c20fe6e86477
FormatterKit/OrdinalNumberFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/LocationFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/URLRequestFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/UnitOfInformationFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
JSONKit: 3d4708953ea7ae399a49777372d8b060a43ddd27
FormatterKit/ArrayFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit: 12dea999a2df19e389f7b821962fc4088de8b821
COCOAPODS: 0.13.0
DEPENDENCIES:
- AFNetworking (~> 0.7.0)
- FormatterKit
PODS:
- AFNetworking (0.7.0):
SPEC CHECKSUMS:
FormatterKit/UnitOfInformationFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
AFNetworking: 7bf22b0ed1d9068909cd67206db78204eb63dd2c
JSONKit: a01a22c75f27eae76b4badd55a91c20fe6e86477
FormatterKit/URLRequestFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/OrdinalNumberFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/TimeIntervalFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/ArrayFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/LocationFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
PODS:
- AFNetworking (0.7.0):
- JSONKit
- FormatterKit (1.0.0):
- FormatterKit (1.0.0):
- FormatterKit/ArrayFormatter (= 1.0.0)
- FormatterKit/LocationFormatter (= 1.0.0)
- FormatterKit/OrdinalNumberFormatter (= 1.0.0)
......@@ -17,19 +28,8 @@ PODS:
- FormatterKit/UnitOfInformationFormatter (1.0.0)
- JSONKit (1.5pre)
DEPENDENCIES:
COCOAPODS: 0.15.1
DEPENDENCIES:
- AFNetworking (~> 0.7.0)
- FormatterKit
SPEC CHECKSUMS:
AFNetworking: 1dd63e737768662b7180659e91846408df4ae7df
FormatterKit: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/ArrayFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/LocationFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/OrdinalNumberFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/TimeIntervalFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/URLRequestFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
FormatterKit/UnitOfInformationFormatter: 12dea999a2df19e389f7b821962fc4088de8b821
JSONKit: 3d4708953ea7ae399a49777372d8b060a43ddd27
COCOAPODS: 0.13.0
PODS:
- MGSplitViewController (1.0.0)
DEPENDENCIES:
DEPENDENCIES:
- MGSplitViewController (= 1.0.0)
SPEC CHECKSUMS:
SPEC CHECKSUMS:
MGSplitViewController: e0b0bc01aa81e1559765c39e7a764c890dac373a
COCOAPODS: 0.13.0
PODS:
- MGSplitViewController (1.0.0)
COCOAPODS: 0.15.1
PODS:
- AFNetworking (1.0RC1)
COCOAPODS: 0.15.1
DEPENDENCIES:
DEPENDENCIES:
- AFNetworking
SPEC CHECKSUMS:
SPEC CHECKSUMS:
AFNetworking: b21c1252d437fd322e7db1caa93b163d76a362cb
COCOAPODS: 0.13.0
PODS:
- AFNetworking (1.0RC1)
......@@ -7,12 +7,12 @@ 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))
STDERR.puts "Your RubyGems version (#{Gem::VERSION}) is too old, please update with: `gem update --system`".red
STDERR.puts "\e[1;31m" + "Your RubyGems version (1.8.24) is too old, please update with: `gem update --system`" + "\e[0m"
exit 1
end
module Pod
VERSION = '0.14.0'
VERSION = '0.15.2'
class PlainInformative < StandardError
end
......@@ -57,13 +57,6 @@ module Pod
end
end
class Pathname
def glob(pattern = '')
Dir.glob((self + pattern).to_s).map { |f| Pathname.new(f) }
end
end
if ENV['COCOA_PODS_ENV'] == 'development'
require 'pry'
require 'awesome_print'
end
......@@ -6,6 +6,14 @@ module Pod
# TODO: Add check to ensure that attributes inherited by subspecs are not duplicated ?
attr_accessor :quick, :no_clean, :repo_path
# @return [Bool] Wether the lint should be performed against the root of
# the podspec instead to its original source. Uses the `:local` option
# of the Podfile.
#
attr_accessor :local
alias :local? :local
attr_reader :spec, :file
attr_reader :errors, :warnings, :notes
......@@ -114,11 +122,16 @@ module Pod
def podfile_from_spec
name = spec.name
podspec = file.realpath.to_s
podspec = file.realpath
platform = @platform
local = local?
podfile = Pod::Podfile.new do
platform(platform.to_sym, platform.deployment_target)
pod name, :podspec => podspec
if (local)
pod name, :local => podspec.dirname.to_s
else
pod name, :podspec => podspec.to_s
end
end
podfile
end
......
......@@ -22,7 +22,6 @@ module Pod
def run
verify_podfile_exists!
verify_lockfile_exists!
update_spec_repos_if_necessary!
sandbox = Sandbox.new(config.project_pods_root)
resolver = Resolver.new(config.podfile, config.lockfile, sandbox)
......@@ -30,6 +29,9 @@ module Pod
resolver.update_external_specs = false
resolver.resolve
#TODO: the command report new dependencies (added to by updated ones)
# as updates.
names = resolver.pods_to_install - resolver.pods_from_external_sources
specs = resolver.specs.select do |spec|
names.include?(spec.name) && !spec.version.head?
......
require 'fileutils'
require 'active_support/core_ext/string/inflections'
module Pod
class Command
......@@ -66,12 +67,18 @@ module Pod
def podspec_files
files = Pathname.glob(@podspec || "*.podspec")
raise Informative, "[!] Couldn't find .podspec file in current directory".red if files.empty?
raise Informative, "[!] Couldn't find any .podspec file in current directory".red if files.empty?
files
end
# @return [Integer] The number of the podspec files to push.
#
def count
podspec_files.count
end
def validate_podspec_files
UI.puts "\nValidating specs".yellow unless config.silent
UI.puts "\nValidating #{'spec'.pluralize(count)}".yellow unless config.silent
lint_argv = ["lint"]
lint_argv << "--only-errors" if @allow_warnings
lint_argv << "--silent" if config.silent
......@@ -82,7 +89,7 @@ module Pod
end
def add_specs_to_repo
UI.puts "\nAdding the specs to the #{@repo} repo\n".yellow unless config.silent
UI.puts "\nAdding the #{'spec'.pluralize(count)} 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)
......
......@@ -90,7 +90,7 @@ module Pod
dirs.each do |dir|
check_versions(dir)
UI.puts "\nLinting spec repo `#{dir.realpath.basename}'\n".yellow
podspecs = dir.glob('**/*.podspec')
podspecs = Pathname.glob( dir + '**/*.podspec')
invalid_count = 0
podspecs.each do |podspec|
......
# encoding: utf-8
require 'active_support/core_ext/string/inflections'
module Pod
class Command
class Spec < Command
......@@ -20,6 +22,7 @@ module Pod
def self.options
[ ["--quick", "Lint skips checks that would require to download and build the spec"],
["--local", "Lint a podspec against the local files contained in its directory"],
["--only-errors", "Lint validates even if warnings are present"],
["--no-clean", "Lint leaves the build directory intact for inspection"] ].concat(super)
end
......@@ -32,9 +35,10 @@ module Pod
super if @name_or_url.nil?
super unless argv.empty?
elsif @action == 'lint'
@quick = argv.option('--quick')
@only_errors = argv.option('--only-errors')
@no_clean = argv.option('--no-clean')
@quick = argv.option('--quick')
@local = argv.option('--local')
@only_errors = argv.option('--only-errors')
@no_clean = argv.option('--no-clean')
@podspecs_paths = argv
else
super
......@@ -72,8 +76,9 @@ module Pod
UI.puts
invalid_count = 0
podspecs_to_lint.each do |podspec|
linter = Linter.new(podspec)
linter.quick = @quick
linter = Linter.new(podspec)
linter.quick = @quick
linter.local = @local
linter.no_clean = @no_clean
# Show immediatly which pod is being processed.
......@@ -101,8 +106,8 @@ module Pod
UI.puts unless config.silent?
end
UI.puts "Analyzed #{podspecs_to_lint.count} podspecs files.\n\n" unless config.silent?
count = podspecs_to_lint.count
UI.puts "Analyzed #{count} #{'podspec'.pluralize(count)}.\n\n" unless config.silent?
if invalid_count == 0
lint_passed_message = count == 1 ? "#{podspecs_to_lint.first.basename} passed validation." : "All the specs passed validation."
UI.puts lint_passed_message.green << "\n\n" unless config.silent?
......@@ -133,7 +138,7 @@ module Pod
end
files << output_path
else if (pathname = Pathname.new(path)).directory?
files += pathname.glob('**/*.podspec')
files += Pathname.glob(pathname + '**/*.podspec')
raise Informative, "No specs found in the current directory." if files.empty?
else
files << (pathname = Pathname.new(path))
......@@ -171,7 +176,7 @@ module Pod
data = {}
data[:name] = repo['name']
data[:summary] = repo['description'].gsub(/["]/, '\"')
data[:summary] = (repo['description'] || '').gsub(/["]/, '\"')
data[:homepage] = (repo['homepage'] && !repo['homepage'].empty? ) ? repo['homepage'] : repo['html_url']
data[:author_name] = user['name'] || user['login']
data[:author_email] = user['email'] || 'email@address.com'
......
......@@ -41,7 +41,7 @@ module Pod
def project_podfile
unless @project_podfile
@project_podfile = project_root + 'Podfile.cocoapods'
@project_podfile = project_root + 'CocoaPods.podfile'
unless @project_podfile.exist?
@project_podfile = project_root + 'Podfile'
end
......
......@@ -231,7 +231,8 @@ module Pod
class LocalSource < AbstractExternalSource
def pod_spec_path
path = Pathname.new(@params[:local]).expand_path + "#{name}.podspec"
path = Pathname.new(@params[:local]).expand_path
path += "#{name}.podspec"# unless path.to_s.include?("#{name}.podspec")
raise Informative, "No podspec found for `#{name}' in `#{@params[:local]}'" unless path.exist?
path
end
......
require 'open-uri'
require 'tempfile'
# require 'tempfile'
require 'zlib'
require 'digest/sha1'
module Pod
class Downloader
# Concreted Downloader class that provides support for specifications with
# git sources.
#
class Git < Downloader
include Config::Mixin
executable :git
MAX_CACHE_SIZE = 500
def download
create_cache unless cache_exist?
UI.section(' > Cloning git repo', '', 1) do
......@@ -29,88 +31,26 @@ module Pod
prune_cache
end
def create_cache
UI.section(" > Creating cache git repo (#{cache_path})",'',1)
cache_path.rmtree if cache_path.exist?
cache_path.mkpath
git! %Q|clone --mirror "#{url}" "#{cache_path}"|
end
def prune_cache
return unless caches_dir.exist?
Dir.chdir(caches_dir) do
repos = Pathname.new(caches_dir).children.select { |c| c.directory? }.sort_by(&:ctime)
while caches_size >= MAX_CACHE_SIZE && !repos.empty?
dir = repos.shift
UI.message "#{'->'.yellow} Removing git cache for `#{origin_url(dir)}'"
dir.rmtree
end
end
end
def cache_path
@cache_path ||= caches_dir + "#{Digest::SHA1.hexdigest(url.to_s)}"
end
def cache_exist?
cache_path.exist? && origin_url(cache_path).to_s == url.to_s
end
def origin_url(dir)
Dir.chdir(dir) { `git config remote.origin.url`.chomp }
end
def caches_dir
Pathname.new(File.expand_path("~/Library/Caches/CocoaPods/Git"))
end
# @!group Download implementations
# @return [Pathname] The clone URL, which resolves to the cache path.
#
def clone_url
cache_path
end
def caches_size
# expressed in Mb
`du -cm`.split("\n").last.to_i
end
def update_cache
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
# @return [void] Convenience method to perform clones operations.
#
def clone(from, to)
UI.section(" > Cloning to Pods folder",'',1) do
git! %Q|clone "#{from}" "#{to}"|
end
end
def ref_exists?(ref)
Dir.chdir(cache_path) { git "rev-list --max-count=1 #{ref}" }
$? == 0
end
def ensure_ref_exists(ref)
return if ref_exists?(ref)
# Skip pull if not needed
update_cache
raise Informative, "[!] Cache unable to find git reference `#{ref}' for `#{url}'.".red unless ref_exists?(ref)
end
def branch_exists?(branch)
Dir.chdir(cache_path) { git "branch --all | grep #{branch}$" } # check for remote branch and do suffix matching ($ anchor)
$? == 0
end
def ensure_remote_branch_exists(branch)
return if branch_exists?(branch)
update_cache
raise Informative, "[!] Cache unable to find git reference `#{branch}' for `#{url}' (#{$?}).".red unless branch_exists?(branch)
end
# @return [void] Checkouts the HEAD of the git source in the destination
# path.
#
def download_head
if cache_exist?
update_cache
......@@ -122,6 +62,9 @@ module Pod
Dir.chdir(target_path) { git! "submodule update --init" } if options[:submodules]
end
# @return [void] Checkouts a specific tag of the git source in the
# destination path.
#
def download_tag
ensure_ref_exists(options[:tag])
Dir.chdir(target_path) do
......@@ -133,6 +76,9 @@ module Pod
end
end
# @return [void] Checkouts a specific commit of the git source in the
# destination path.
#
def download_commit
ensure_ref_exists(options[:commit])
clone(clone_url, target_path)
......@@ -141,6 +87,9 @@ module Pod
end
end
# @return [void] Checkouts the HEAD of a specific branch of the git
# source in the destination path.
#
def download_branch
ensure_remote_branch_exists(options[:branch])
clone(clone_url, target_path)
......@@ -152,13 +101,135 @@ module Pod
end
end
def clone(from, to)
UI.section(" > Cloning to Pods folder",'',1) do
git! %Q|clone "#{from}" "#{to}"|
# @!group Checking references
# @return [Bool] Wether a reference (commit SHA or tag)
#
def ref_exists?(ref)
Dir.chdir(cache_path) { git "rev-list --max-count=1 #{ref}" }
$? == 0
end
# @return [void] Checks if a reference exists in the cache and updates
# only if necessary.
#
# @raises if after the update the reference can't be found.
#
def ensure_ref_exists(ref)
return if ref_exists?(ref)
update_cache
raise Informative, "[!] Cache unable to find git reference `#{ref}' for `#{url}'.".red unless ref_exists?(ref)
end
# @return [Bool] Wether a branch exists in the cache.
#
def branch_exists?(branch)
Dir.chdir(cache_path) { git "branch --all | grep #{branch}$" } # check for remote branch and do suffix matching ($ anchor)
$? == 0
end
# @return [void] Checks if a branch exists in the cache and updates
# only if necessary.
#
# @raises if after the update the branch can't be found.
#
def ensure_remote_branch_exists(branch)
return if branch_exists?(branch)
update_cache
raise Informative, "[!] Cache unable to find git reference `#{branch}' for `#{url}' (#{$?}).".red unless branch_exists?(branch)
end
# @!group Cache
# The maximum allowed size for the cache expressed in Mb.
#
MAX_CACHE_SIZE = 500
# @return [Pathname] The directory where the cache for the current git
# repo is stored.
#
# @note The name of the directory is the SHA1 hash value of the URL of
# the git repo.
#
def cache_path
@cache_path ||= caches_root + "#{Digest::SHA1.hexdigest(url.to_s)}"
end
# @return [Pathname] The directory where the git caches are stored.
#
def caches_root
Pathname.new(File.expand_path("~/Library/Caches/CocoaPods/Git"))
end
# @return [Integer] The global size of the git cache expressed in Mb.
#
def caches_size
`du -cm`.split("\n").last.to_i
end
# @return [Bool] Wether the cache exits.
#
# @note The previous implementation of the cache didn't use a barebone
# git repo. This method takes into account this fact and checks
# that the cache is actually a barebone repo. If the cache was not
# barebone it will be deleted and recreated.
#
def cache_exist?
cache_path.exist? &&
cache_origin_url(cache_path).to_s == url.to_s &&
Dir.chdir(cache_path) { git("config core.bare").chomp == "true" }
end
# @return [String] The origin URL of the cache with the given directory.
#
# @param [String] dir The directory of the cache.
#
def cache_origin_url(dir)
Dir.chdir(dir) { `git config remote.origin.url`.chomp }
end
# @return [void] Creates the barebone repo that will serve as the cache
# for the current repo.
#
def create_cache
UI.section(" > Creating cache git repo (#{cache_path})",'',1) do
cache_path.rmtree if cache_path.exist?
cache_path.mkpath
git! %Q|clone --mirror "#{url}" "#{cache_path}"|
end
end
# @return [void] Updates the barebone repo used as a cache against its
# remote.
#
def update_cache
UI.section(" > Updating cache git repo (#{cache_path})",'',1) do
Dir.chdir(cache_path) { git! "remote update" }
end
end
# @return [void] Deletes the oldest caches until they the global size is
# below the maximum allowed.
#
def prune_cache
return unless caches_root.exist?
Dir.chdir(caches_root) do
repos = Pathname.new(caches_root).children.select { |c| c.directory? }.sort_by(&:ctime)
while caches_size >= MAX_CACHE_SIZE && !repos.empty?
dir = repos.shift
UI.message "#{'->'.yellow} Removing git cache for `#{cache_origin_url(dir)}'"
dir.rmtree
end
end
end
end
# This class allows to download tarballs from GitHub and is not currently
# being used by CocoaPods as the git cache is preferable.
#
class GitHub < Git
def download_head
download_only? ? download_and_extract_tarball('master') : super
......
......@@ -274,7 +274,6 @@ module Pod
end
# @!group Resolution steps
# @return [Hash{Podfile::TargetDefinition => Array<Spec>}]
......@@ -487,7 +486,6 @@ module Pod
UI.message "- Creating Pods project" do
@pods_project = Pod::Project.new
pods_project.user_build_configurations = podfile.user_build_configurations
pods_project.main_group.groups.new('name' => 'Targets Support Files')
end
end
......@@ -502,19 +500,8 @@ module Pod
#
def add_source_files_to_pods_project
UI.message "- Adding source files to Pods project" do
pods.each do |pod|
pod.relative_source_files_by_spec.each do |spec, paths|
if pod.local?
parent_group = pods_project.local_pods
else
parent_group = pods_project.pods
end
group = pods_project.add_spec_group(spec.name, parent_group)
paths.each do |path|
group.files.new('path' => path.to_s)
end
end
end
pods.each { |p| p.add_file_references_to_project(@project) }
pods.each { |p| p.link_headers }
end
end
......@@ -567,11 +554,8 @@ module Pod
filename = "#{dummy_source.class_name}.m"
pathname = Pathname.new(sandbox.root + filename)
dummy_source.save_as(pathname)
project_file = pods_project.files.new('path' => filename)
pods_project.group("Targets Support Files") << project_file
target_installer.target.source_build_phases.first << project_file
file = project.new_file(filename, "Targets Support Files")
target_installer.target.source_build_phase.add_file_reference(file)
end
def write_pod_project
......
module Pod
class Installer
# This class is reponsible of creating and configuring the static library
# target in Pods project. Every target is generated from a target
# definition of the Podfile.
#
class TargetInstaller
include Config::Mixin
attr_reader :podfile, :project, :target_definition, :target
attr_accessor :requires_arc
# @return [Podfile]
#
# TODO: is really needed to pass the podfile?
#
attr_reader :podfile
# @return [Project] The Pods project.
#
attr_reader :project
# @return [TargetDefinition] The target definition whoose target needs to
# be generated.
#
attr_reader :target_definition
def initialize(podfile, project, target_definition)
@podfile, @project, @target_definition = podfile, project, target_definition
@podfile = podfile
@project = project
@target_definition = target_definition
end
# @return [void] Creates the target in the Pods project and its support
# files.
#
# @param [Array<LocalPod>] pods The pods are required by the target
# definition of this installer.
#
# @param [Sandbox] sandbox The sanbox where the support files
# should be generated.
#
def install!(pods, sandbox)
self.requires_arc = pods.any? { |pod| pod.requires_arc? }
@target = @project.add_pod_target(@target_definition.label, @target_definition.platform)
source_file_descriptions = []
pods.each { |p| p.add_build_files_to_target(@target) }
support_files_group = @project.support_files_group.new_group(@target_definition.label)
target_support_files.each { |path| support_files_group.new_file(path) }
xcconfig_file = support_files_group.files.find { |f| f.path == @target_definition.xcconfig_name }
configure_build_configurations(xcconfig_file, sandbox)
create_files(pods, sandbox)
end
def xcconfig
@xcconfig ||= Xcodeproj::Config.new({
# In a workspace this is where the static library headers should be found.
'PODS_ROOT' => @target_definition.relative_pods_root,
'PODS_HEADERS_SEARCH_PATHS' => '${PODS_PUBLIC_HEADERS_SEARCH_PATHS}',
'ALWAYS_SEARCH_USER_PATHS' => 'YES', # needed to make EmbedReader build
'OTHER_LDFLAGS' => default_ld_flags
# @return [PBXNativeTarget] The target generated by the installation
# process.
#
attr_reader :target
# @return [Boold] Wether the any of the pods requires arc.
#
# TODO: This should not be an attribute reader.
#
attr_accessor :requires_arc
attr_reader :xcconfig
# In a workspace this is where the static library headers should be found.
#
def generate_xcconfig(pods, sandbox)
xcconfig = Xcodeproj::Config.new({
'ALWAYS_SEARCH_USER_PATHS' => 'YES', # needed to make EmbedReader build
'OTHER_LDFLAGS' => default_ld_flags,
'HEADER_SEARCH_PATHS' => '${PODS_HEADERS_SEARCH_PATHS}',
# CocoaPods global keys
'PODS_ROOT' => @target_definition.relative_pods_root,
'PODS_BUILD_HEADERS_SEARCH_PATHS' => quoted(sandbox.build_headers.search_paths).join(" "),
'PODS_PUBLIC_HEADERS_SEARCH_PATHS' => quoted(sandbox.public_headers.search_paths).join(" "),
# Pods project specific keys
'PODS_HEADERS_SEARCH_PATHS' => '${PODS_PUBLIC_HEADERS_SEARCH_PATHS}'
})
pods.each { |pod| xcconfig.merge!(pod.xcconfig) }
@xcconfig = xcconfig
end
#
#
def copy_resources_script_for(pods)
@copy_resources_script ||= Generator::CopyResourcesScript.new(pods.map { |p| p.relative_resource_files }.flatten)
end
......@@ -57,37 +125,9 @@ module Pod
[:copy_resources_script_name, :prefix_header_name, :xcconfig_name].map { |file| @target_definition.send(file) }
end
# TODO move xcconfig related code into the xcconfig method, like copy_resources_script and generate_bridge_support.
def install!(pods, sandbox)
self.requires_arc = pods.any? { |pod| pod.requires_arc? }
@target = @project.add_pod_target(@target_definition.label, @target_definition.platform)
source_file_descriptions = []
pods.each do |pod|
xcconfig.merge!(pod.xcconfig)
source_file_descriptions += pod.source_file_descriptions
# TODO: this doesn't need to be done here, it has nothing to do with the target
pod.link_headers
end
@target.add_source_files(source_file_descriptions)
xcconfig.merge!('HEADER_SEARCH_PATHS' => '${PODS_HEADERS_SEARCH_PATHS}')
xcconfig.merge!('PODS_BUILD_HEADERS_SEARCH_PATHS' => quoted(sandbox.build_headers.search_paths).join(" "))
xcconfig.merge!('PODS_PUBLIC_HEADERS_SEARCH_PATHS' => quoted(sandbox.public_headers.search_paths).join(" "))
support_files_group = @project.group("Targets Support Files").create_group(@target_definition.label)
support_files_group.create_files(target_support_files)
xcconfig_file = support_files_group.files.where(:path => @target_definition.xcconfig_name)
configure_build_configurations(xcconfig_file, sandbox)
create_files(pods, sandbox)
end
def configure_build_configurations(xcconfig_file, sandbox)
@target.build_configurations.each do |config|
config.base_configuration = xcconfig_file
config.base_configuration_reference = xcconfig_file
config.build_settings['OTHER_LDFLAGS'] = ''
config.build_settings['GCC_PREFIX_HEADER'] = @target_definition.prefix_header_name
config.build_settings['PODS_ROOT'] = '${SRCROOT}'
......@@ -96,6 +136,8 @@ module Pod
end
end
#
#
def create_files(pods, sandbox)
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
......@@ -104,6 +146,7 @@ module Pod
end if @podfile.generate_bridge_support?
UI.message "- Generating xcconfig file at #{UI.path(sandbox.root + @target_definition.xcconfig_name)}" do
generate_xcconfig(pods, sandbox)
xcconfig.save_as(sandbox.root + @target_definition.xcconfig_name)
@target_definition.xcconfig = xcconfig
end
......
......@@ -137,20 +137,18 @@ module Pod
# Default to the first, which in a simple project is probably an app target.
[user_project.targets.first]
end.reject do |target|
# Reject any target that already has this Pods library in one of its frameworks build phases
target.frameworks_build_phases.any? do |phase|
phase.files.any? { |file| file.name == @target_definition.lib_name }
end
end
# Reject any target that already has this Pods library in one of its frameworks build phases
target.frameworks_build_phase.files.any? { |build_file| build_file.file_ref.name == @target_definition.lib_name }
end
end
end
def add_xcconfig_base_configuration
xcconfig = user_project.files.new('path' => @target_definition.xcconfig_relative_path)
xcconfig = user_project.new_file(@target_definition.xcconfig_relative_path)
targets.each do |target|
config_build_names_by_overriden_key = {}
target.build_configurations.each do |config|
config_name = config.attributes["name"]
config_name = config.name
if @target_definition.xcconfig
@target_definition.xcconfig.attributes.each do |key, value|
target_value = config.build_settings[key]
......@@ -161,11 +159,11 @@ module Pod
end
end
config.base_configuration = xcconfig
config.base_configuration_reference = xcconfig
end
config_build_names_by_overriden_key.each do |key, config_build_names|
name = "#{target.attributes["name"]} [#{config_build_names.join(' - ')}]"
name = "#{target.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
......@@ -173,23 +171,20 @@ module Pod
end
def add_pods_library
group = user_project.group("Frameworks") || user_project.main_group
pods_library = group.files.new_static_library(@target_definition.label)
frameworks = user_project.frameworks_group
pods_library = frameworks.new_static_library(@target_definition.label)
targets.each do |target|
target.frameworks_build_phases.each { |build_phase| build_phase << pods_library }
target.frameworks_build_phase.add_file_reference(pods_library)
end
end
def add_copy_resources_script_phase
targets.each do |target|
phase = target.shell_script_build_phases.new
phase.name = 'Copy Pods Resources'
phase = target.new_shell_script_build_phase('Copy Pods Resources')
phase.shell_script = %{"#{@target_definition.copy_resources_script_relative_path}"\n}
end
end
end
end
end
end
......@@ -163,12 +163,15 @@ module Pod
#
# @return [Array<Strings>] The paths that can be deleted.
#
# @note Implementation detail: Don't use Dir#glob as there is an
# unexplained issue (#568, #572 and #602).
#
def clean_paths
cached_used_paths = used_files
files = Dir.glob(root + "**/*", File::FNM_DOTMATCH)
cached_used = used_files
files = Pathname.glob(root + "**/*", File::FNM_DOTMATCH | File::FNM_CASEFOLD).map(&:to_s)
files.reject! do |candidate|
candidate.end_with?('.', '..') || cached_used_paths.any? do |path|
candidate.end_with?('.', '..') || cached_used.any? do |path|
path.include?(candidate) || candidate.include?(path)
end
end
......@@ -187,6 +190,27 @@ module Pod
# @!group Files
# @return [Pathname] Returns the relative path from the sandbox.
#
# @note If the two abosule paths don't share the same root directory an
# extra `../` is added to the result of {Pathname#relative_path_from}
#
# path = Pathname.new('/Users/dir')
# @sandbox
# #=> Pathname('/tmp/CocoaPods/Lint/Pods')
#
# p.relative_path_from(@sandbox
# #=> '../../../../Users/dir'
#
# relativize_from_sandbox(path)
# #=> '../../../../../Users/dir'
#
def relativize_from_sandbox(path)
result = path.relative_path_from(@sandbox.root)
result = Pathname.new('../') + result unless @sandbox.root.to_s.split('/')[1] == path.to_s.split('/')[1]
result
end
# @return [Array<Pathname>] The paths of the source files.
#
def source_files
......@@ -196,13 +220,13 @@ module Pod
# @return [Array<Pathname>] The *relative* paths of the source files.
#
def relative_source_files
source_files.map{ |p| p.relative_path_from(@sandbox.root) }
source_files.map{ |p| relativize_from_sandbox(p) }
end
def relative_source_files_by_spec
result = {}
source_files_by_spec.each do |spec, paths|
result[spec] = paths.map{ |p| p.relative_path_from(@sandbox.root) }
result[spec] = paths.map{ |p| relativize_from_sandbox(p) }
end
result
end
......@@ -217,7 +241,7 @@ module Pod
# {Specification}.
#
def source_files_by_spec
options = {:glob => '*.{h,m,mm,c,cpp}'}
options = {:glob => '*.{h,hpp,hh,m,mm,c,cpp}'}
paths_by_spec(:source_files, options)
end
......@@ -230,7 +254,7 @@ module Pod
# @return [Array<Pathname>] The *relative* paths of the source files.
#
def relative_header_files
header_files.map{ |p| p.relative_path_from(@sandbox.root) }
header_files.map{ |p| relativize_from_sandbox(p) }
end
# @return [Hash{Specification => Array<Pathname>}] The paths of the header
......@@ -239,7 +263,7 @@ module Pod
def header_files_by_spec
result = {}
source_files_by_spec.each do |spec, paths|
headers = paths.select { |f| f.extname == '.h' }
headers = paths.select { |f| f.extname == '.h' || f.extname == '.hpp' || f.extname == '.hh' }
result[spec] = headers unless headers.empty?
end
result
......@@ -253,7 +277,7 @@ module Pod
# header files (i.e. the build ones) are intended to be public.
#
def public_header_files_by_spec
public_headers = paths_by_spec(:public_header_files, :glob => '*.h')
public_headers = paths_by_spec(:public_header_files, :glob => '*.{h,hpp,hh}')
build_headers = header_files_by_spec
result = {}
......@@ -276,7 +300,7 @@ module Pod
# @return [Array<Pathname>] The *relative* paths of the resources.
#
def relative_resource_files
resource_files.map{ |p| p.relative_path_from(@sandbox.root) }
resource_files.map{ |p| relativize_from_sandbox(p) }
end
# @return [Pathname] The absolute path of the prefix header file
......@@ -356,14 +380,60 @@ module Pod
if (public_h = public_headers[spec]) && !public_h.empty?
result += public_h
elsif (source_f = source_files[spec]) && !source_f.empty?
build_h = source_f.select { |f| f.extname == '.h' }
build_h = source_f.select { |f| f.extname == '.h' || f.extname == '.hpp' || f.extname == '.hh' }
result += build_h unless build_h.empty?
end
end
result
end
# @!group Target integration
# @!group Xcodeproj integration
# Adds the file references, to the given `Pods.xcodeproj` project, for the
# source files of the pod. The file references are grouped by specification
# and stored in {#file_references_by_spec}.
#
# @note If the pod is locally sourced the file references are stored in the
# `Local Pods` group otherwise they are stored in the `Pods` group.
#
# @return [void]
#
def add_file_references_to_project(project)
@file_references_by_spec = {}
parent_group = local? ? project.local_pods : project.pods
relative_source_files_by_spec.each do |spec, paths|
group = project.add_spec_group(spec.name, parent_group)
file_references = []
paths.each do |path|
file_references << group.new_file(path)
end
@file_references_by_spec[spec] = file_references
end
end
# @return [Hash{Specification => Array<PBXFileReference>}] The file
# references of the pod in the `Pods.xcodeproj` project.
#
attr_reader :file_references_by_spec
# Adds a build file for each file reference to a given target taking into
# account the compiler flags of the corresponding specification.
#
# @raise If the {#add_file_references_to_project} was not called before of
# calling this method.
#
# @return [void]
#
def add_build_files_to_target(target)
unless file_references_by_spec
raise Informative, "Local Pod needs to add the file references to the " \
"project before adding the build files to the target."
end
file_references_by_spec.each do |spec, file_reference|
target.add_file_references(file_reference, spec.compiler_flags.strip)
end
end
# @return [void] Copies the pods headers to the sandbox.
#
......@@ -380,26 +450,10 @@ module Pod
end
end
# @param [Xcodeproj::Project::Object::PBXNativeTarget] target
# The target to integrate.
#
# @return [void] Adds the pods source files to a given target.
#
def source_file_descriptions
result = []
source_files_by_spec.each do | spec, files |
compiler_flags = spec.compiler_flags.strip
files.each do |file|
file = file.relative_path_from(@sandbox.root)
desc = Xcodeproj::Project::PBXNativeTarget::SourceFileDescription.new(file, compiler_flags, nil)
result << desc
end
end
result
end
# @return Whether the pod requires ARC.
#
# TODO: this should be not used anymore.
#
def requires_arc?
top_specification.requires_arc
end
......@@ -410,7 +464,7 @@ module Pod
# (the files the need to compiled) of the pod.
#
def implementation_files
relative_source_files.select { |f| f.extname != '.h' }
relative_source_files.reject { |f| f.extname == '.h' || f.extname == '.hpp' || f.extname == '.hh' }
end
# @return [Pathname] The path of the pod relative from the sandbox.
......@@ -453,7 +507,7 @@ module Pod
# included in the linker search paths.
#
def headers_excluded_from_search_paths
options = { :glob => '*.h' }
options = { :glob => '*.{h,hpp,hh}' }
paths = paths_by_spec(:exclude_header_search_paths, options)
paths.values.compact.uniq
end
......@@ -506,15 +560,27 @@ module Pod
"\t Options: #{options.inspect}"
end
patterns = [ patterns ] if patterns.is_a? String
patterns.map do |pattern|
pattern = root + pattern
return [] if patterns.empty?
patterns = [ patterns ] if patterns.is_a?(String)
file_lists = patterns.select { |p| p.is_a?(FileList) }
glob_patterns = patterns - file_lists
result = []
result << file_lists.map do |file_list|
file_list.prepend_patterns(root)
file_list.glob
end
result << glob_patterns.map do |pattern|
pattern = root + pattern
if pattern.directory? && options[:glob]
pattern += options[:glob]
end
Pathname.glob(pattern, File::FNM_CASEFOLD)
end.flatten
result.flatten.compact.uniq
end
# A {LocalSourcedPod} is a {LocalPod} that interacts with the files of
......
......@@ -8,7 +8,11 @@ module Pod
#
def self.from_file(path)
return nil unless path.exist?
hash = YAML.load(File.open(path))
begin
hash = YAML.load(File.open(path))
rescue Exception => e
raise Informative, "Podfile.lock syntax error: #{e.inspect}"
end
lockfile = Lockfile.new(hash)
lockfile.defined_in_file = path
lockfile
......
......@@ -35,7 +35,7 @@ module Pod
if @path
@path
else
xcodeprojs = config.project_root.glob('*.xcodeproj')
xcodeprojs = Pathname.glob(config.project_root + '*.xcodeproj')
if xcodeprojs.size == 1
@path = xcodeprojs.first
end
......@@ -177,7 +177,11 @@ module Pod
string = File.open(path, 'r:utf-8') { |f| f.read }
# TODO: work around for Rubinius incomplete encoding in 1.9 mode
string.encode!('UTF-8') if string.respond_to?(:encoding) && string.encoding.name != "UTF-8"
eval(string, nil, path.to_s)
begin
eval(string, nil, path.to_s)
rescue Exception => e
raise Informative, "Podfile syntax error: #{e.inspect}"
end
end
podfile.defined_in_file = path
podfile.validate!
......
require 'xcodeproj/project'
require 'xcodeproj/project/object/build_phase'
Xcodeproj::Project::Object::PBXCopyFilesBuildPhase.instance_eval do
def self.new_pod_dir(project, pod_name, path)
new(project, nil, {
"dstPath" => "Pods/#{path}",
"name" => "Copy #{pod_name} Public Headers",
})
end
end
# Xcodeproj::Project::Object::PBXCopyFilesBuildPhase.instance_eval do
# def self.new_pod_dir(project, pod_name, path)
# new(project, nil, {
# "dstPath" => "Pods/#{path}",
# "name" => "Copy #{pod_name} Public Headers",
# })
# end
# end
module Pod
class Project < Xcodeproj::Project
attr_reader :support_files_group
def initialize(*)
super
main_group << groups.new('name' => 'Pods')
new_group('Pods')
@support_files_group = new_group('Targets Support Files')
@user_build_configurations = []
end
......@@ -24,40 +27,44 @@ module Pod
# any build settings themselves, that's left to `add_pod_target`.
user_build_configurations.each do |name, _|
unless build_configurations.map(&:name).include?(name)
build_configurations.new('name' => name)
bc = new(XCBuildConfiguration)
bc.name = name
build_configurations << bc
end
end
end
# Shortcut access to the `Pods' PBXGroup.
def pods
@pods ||= groups.where(:name => 'Pods') || groups.new('name' => 'Pods')
@pods ||= self['Pods'] || new_group('Pods')
end
# Shortcut access to the `Local Pods' PBXGroup.
def local_pods
@local_pods ||= groups.where(:name => 'Local Pods') || groups.new('name' => 'Local Pods')
@local_pods ||= self['Local Pods'] || new_group('Local Pods')
end
# Adds a group as child to the `Pods' group namespacing subspecs.
def add_spec_group(name, parent_group)
groups = parent_group.groups
current_group = parent_group
group = nil
name.split('/').each do |name|
group = groups.where(:name => name) || groups.new('name' => name)
groups = group.groups
group = current_group[name] || current_group.new_group(name)
current_group = group
end
group
end
def add_pod_target(name, platform)
target = targets.new_static_library(platform.name, name)
target = new_target(:static_library, name, platform.name)
settings = {}
if platform.requires_legacy_ios_archs?
settings['ARCHS'] = "armv6 armv7"
end
if platform == :ios && platform.deployment_target
# TODO: add for osx as well
settings['IPHONEOS_DEPLOYMENT_TARGET'] = platform.deployment_target.to_s
end
......@@ -66,9 +73,11 @@ module Pod
@user_build_configurations.each do |name, type|
unless target.build_configurations.map(&:name).include?(name)
config = target.build_configurations.new('name' => name)
bc = new(XCBuildConfiguration)
bc.name = name
target.build_configurations << bc
# Copy the settings from either the Debug or the Release configuration.
config.build_settings = target.build_settings(type.to_s.capitalize).merge(settings)
bc.build_settings = target.build_settings(type.to_s.capitalize).merge(settings)
end
end
......
......@@ -128,10 +128,22 @@ module Pod
# @return [Array<Set>] The sets for all the pods available.
#
# @note Implementation detail: The sources don't cache their values
# because they might change in response to an update. Therefore
# this method to prevent slowness caches the values before
# processing them.
#
def all_sets
all_pods.map do |pod|
sources = all.select{ |s| s.pods.include?(pod) }.compact
Specification::Set.new(pod, sources)
pods_by_source = {}
all.each do |source|
pods_by_source[source] = source.pods
end
sources = pods_by_source.keys
pods = pods_by_source.values.flatten.uniq
pods.map do |pod|
pod_sources = sources.select{ |s| pods_by_source[s].include?(pod) }.compact
Specification::Set.new(pod, pod_sources)
end
end
......
......@@ -438,11 +438,12 @@ module Pod
# this to, for instance, to run any build script:
#
# Pod::Spec.new do |s|
# def pre_install(pod, target_definition)
# def s.pre_install(pod, target_definition)
# Dir.chdir(pod.root){ `sh make.sh` }
# end
# end
def pre_install(pod, target_definition)
FALSE
end
# This is a convenience method which gets called after all pods have been
......@@ -460,6 +461,7 @@ module Pod
# end
# end
def post_install(target_installer)
FALSE
end
def podfile?
......
......@@ -90,7 +90,7 @@ module Pod
def github_stats_if_needed(set)
return if get_value(set, :gh_date) && get_value(set, :gh_date) > Time.now - cache_expiration
spec = set.specification
url = spec.source.reject {|k,_| k == :commit || k == :tag }.values.first
url = spec.source[:git] || ''
repo_id = url[/github.com\/([^\/\.]*\/[^\/\.]*)\.*/, 1]
return unless repo_id
......
......@@ -196,8 +196,9 @@ module Pod
# @!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/
# @return [String] 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
#
......@@ -205,8 +206,12 @@ module Pod
#
# @return [String] The formatted string.
#
# @note If CocoaPods is not being run in a terminal or the width of the
# terminal is too small a width of 80 is assumed.
#
def wrap_string(txt, indent = '')
width = `stty size`.split(' ')[1].to_i - indent.length
width = 80 unless width >= 10
txt.strip.gsub(/(.{1,#{width}})( +|$)\n?|(.{#{width}})/, indent + "\\1\\3\n")
end
end
......
......@@ -42,7 +42,7 @@ module Pod
end
def authors
spec.authors.keys.to_sentence
spec.authors ? spec.authors.keys.to_sentence : ''
end
def homepage
......
......@@ -32,21 +32,13 @@ describe Pod::Installer::UserProjectIntegrator do
@sample_project = Xcodeproj::Project.new(@sample_project_path)
end
it 'adds references to the Pods static libraries to the root of the project if the Frameworks group does not exist' do
@sample_project.group('Frameworks').destroy
@sample_project.save_as(@sample_project_path)
integrate!
@sample_project.main_group.files.where(:name => "libPods.a").should.not == nil
@sample_project.main_group.files.where(:name => "libPods-test_runner.a").should.not == nil
end
before do
integrate!
end
it 'adds references to the Pods static libraries to the Frameworks group' do
@sample_project.group('Frameworks').files.where(:name => "libPods.a").should.not == nil
@sample_project.group('Frameworks').files.where(:name => "libPods-test_runner.a").should.not == nil
@sample_project["Frameworks/libPods.a"].should.not == nil
@sample_project["Frameworks/libPods-test_runner.a"].should.not == nil
end
it 'creates a workspace with a name matching the project' do
......@@ -66,26 +58,25 @@ describe Pod::Installer::UserProjectIntegrator do
it 'sets the Pods xcconfig as the base config for each build configuration' do
@podfile.target_definitions.each do |_, definition|
target = @sample_project.targets.where(:name => definition.link_with.first)
xcconfig_file = @sample_project.files.where(:path => "Pods/#{definition.xcconfig_name}")
target = @sample_project.targets.find { |t| t.name == definition.link_with.first }
xcconfig_file = @sample_project.files.find { |f| f.path == "Pods/#{definition.xcconfig_name}" }
target.build_configurations.each do |config|
config.base_configuration.should == xcconfig_file
config.base_configuration_reference.should == xcconfig_file
end
end
end
it 'adds the libPods static library to the "Link binary with libraries" build phase of each target' do
@podfile.target_definitions.each do |_, definition|
target = @sample_project.targets.where(:name => definition.link_with.first)
framework_build_phase = target.frameworks_build_phases.first
framework_build_phase.files.where(:name => definition.lib_name).should.not == nil
target = @sample_project.targets.find { |t| t.name == definition.link_with.first }
target.frameworks_build_phase.files.find { |f| f.file_ref.name == definition.lib_name}.should.not == nil
end
end
it 'adds a Copy Pods Resources build phase to each target' do
@podfile.target_definitions.each do |_, definition|
target = @sample_project.targets.where(:name => definition.link_with.first)
phase = target.shell_script_build_phases.where(:name => "Copy Pods Resources")
target = @sample_project.targets.find { |t| t.name == definition.link_with.first }
phase = target.shell_script_build_phases.find { |bp| bp.name == "Copy Pods Resources" }
phase.shell_script.strip.should == "\"${SRCROOT}/Pods/#{definition.copy_resources_script_name}\""
end
end
......@@ -101,8 +92,8 @@ describe Pod::Installer::UserProjectIntegrator do
# Remove libPods.a from the app target. But don't do it through TargetIntegrator#targets,
# as it will return only those that still need integration.
app_target = app_integrator.user_project.targets.where(:name => 'SampleProject')
app_target.frameworks_build_phases.first.build_files.last.destroy
app_target = app_integrator.user_project.targets.find { |t| t.name == 'SampleProject' }
app_target.frameworks_build_phase.files.last.remove_from_project
app_integrator.expects(:add_pods_library)
test_runner_integrator.expects(:add_pods_library).never
......
......@@ -389,7 +389,9 @@ else
installer.install!
project = Pod::Project.new(config.project_pods_root + 'Pods.xcodeproj')
project.source_files.should == installer.project.source_files
disk_source_files = project.files.sort.reject { |f| f.build_files.empty? }
installer_source_files = installer.project.files.sort.reject { |f| f.build_files.empty? }
disk_source_files.should == installer_source_files
end
it "creates a project with multiple targets" do
......@@ -409,7 +411,7 @@ else
project = Xcodeproj::Project.new(config.project_pods_root + 'Pods.xcodeproj')
project.targets.each do |target|
phase = target.build_phases.find { |phase| phase.is_a?(Xcodeproj::Project::Object::PBXSourcesBuildPhase) }
files = phase.files.map(&:name)
files = phase.files.map { |bf| bf.file_ref.name }
case target.product_name
when 'Pods'
files.should.include "ASIHTTPRequest.m"
......@@ -466,9 +468,9 @@ else
target = project.targets.first
target.build_configurations.each do |config|
config.base_configuration.path.should == 'Pods/Pods.xcconfig'
config.base_configuration_reference.path.should == 'Pods/Pods.xcconfig'
end
target.frameworks_build_phases.first.files.should.include libPods
target.frameworks_build_phase.files.should.include libPods.build_files.first
# should be the last phase
target.build_phases.last.shell_script.should == %{"${SRCROOT}/Pods/Pods-resources.sh"\n}
end
......
......@@ -30,9 +30,9 @@ describe "Pod::Config" do
end
it "returns the path to the project Podfile if specified with the extension" do
(temporary_directory + 'Podfile.cocoapods').open('w') { |f| f << '# Yo' }
(temporary_directory + 'CocoaPods.podfile').open('w') { |f| f << '# Yo' }
Dir.chdir(temporary_directory) do
config.project_podfile.should == Pathname.pwd + 'Podfile.cocoapods'
config.project_podfile.should == Pathname.pwd + 'CocoaPods.podfile'
end
end
......
......@@ -13,7 +13,7 @@ describe Pod::Installer::TargetInstaller do
@target_definition = @podfile.target_definitions[:default]
@project = Pod::Project.new
@project.main_group.groups.new('name' => 'Targets Support Files')
@project.new_group('Targets Support Files')
@installer = Pod::Installer::TargetInstaller.new(@podfile, @project, @target_definition)
......@@ -24,6 +24,7 @@ describe Pod::Installer::TargetInstaller do
end
def do_install!
@pods.each { |pod| pod.add_file_references_to_project(@project) }
@installer.install!(@pods, @sandbox)
end
......@@ -40,14 +41,15 @@ describe Pod::Installer::TargetInstaller do
end
it 'adds each pod to the static library target' do
@pods[0].expects(:source_file_descriptions).returns([])
@pods[0].expects(:add_build_files_to_target)
do_install!
end
it 'tells each pod to link its headers' do
@pods[0].expects(:link_headers)
do_install!
end
# TODO: move to project
# it 'tells each pod to link its headers' do
# @pods[0].expects(:link_headers)
# do_install!
# end
it 'adds the sandbox header search paths to the xcconfig, with quotes' do
do_install!
......@@ -62,7 +64,7 @@ describe Pod::Installer::TargetInstaller do
it 'adds the -fobjc-arc to OTHER_LDFLAGS if any pods require arc (to support non-ARC projects on iOS 4.0)' do
@podfile.stubs(:set_arc_compatibility_flag? => true)
@specification.stubs(:requires_arc).returns(true)
@installer.install!(@pods, @sandbox)
do_install!
@installer.xcconfig.to_hash['OTHER_LDFLAGS'].split(" ").should.include("-fobjc-arc")
end
......
......@@ -4,18 +4,47 @@ module Pod
describe Installer do
before do
config.repos_dir = fixture('spec-repos')
config.project_pods_root = fixture('integration')
end
describe "by default" do
before do
podfile = Podfile.new do
platform :ios
xcodeproj 'MyProject'
pod 'JSONKit'
end
@sandbox = temporary_sandbox
config.project_pods_root = temporary_sandbox.root
FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit')
@installer = Installer.new(sandbox, podfile)
target_installer = @installer.target_installers.first
target_installer.generate_xcconfig([], @sandbox)
@xcconfig = target_installer.xcconfig.to_hash
end
it "sets the header search paths where installed Pod headers can be found" do
@xcconfig['ALWAYS_SEARCH_USER_PATHS'].should == 'YES'
end
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'
end
it "sets the PODS_ROOT build variable" do
@xcconfig['PODS_ROOT'].should.not == nil
end
it "generates a BridgeSupport metadata file from all the pod headers" do
podfile = Podfile.new do
platform :osx
pod 'ASIHTTPRequest'
end
sandbox = Sandbox.new(fixture('integration'))
installer = Installer.new(sandbox, podfile)
FileUtils.cp_r(fixture('integration/ASIHTTPRequest'), @sandbox.root + 'ASIHTTPRequest')
resolver = Resolver.new(podfile, nil, @sandbox)
installer = Installer.new(resolver)
pods = installer.specifications.map do |spec|
LocalPod.new(spec, installer.sandbox, podfile.target_definitions[:default].platform)
end
......@@ -31,7 +60,7 @@ module Pod
pod 'JSONKit'
end
end
resolver = Resolver.new(podfile, nil, Sandbox.new(fixture('integration')))
resolver = Resolver.new(podfile, nil, @sandbox)
installer = Installer.new(resolver)
installer.target_installers.map(&:target_definition).map(&:name).should == [:not_empty]
end
......@@ -130,7 +159,7 @@ module Pod
it "adds the files of the pod to the Pods project only once" do
@installer.install!
group = @installer.project.pods.groups.where(:name => 'Reachability')
group = @installer.project.pods.groups.find { |g| g.name == 'Reachability' }
group.files.map(&:name).should == ["Reachability.h", "Reachability.m"]
end
......@@ -158,13 +187,13 @@ module Pod
it "namespaces local pods" do
@installer.install!
group = @installer.project.groups.where(:name => 'Local Pods')
group = @installer.project['Local Pods']
group.groups.map(&:name).sort.should == %w| Chameleon |
end
it "namespaces subspecs" do
@installer.install!
group = @installer.project.groups.where(:name => 'Chameleon')
group = @installer.project['Local Pods/Chameleon']
group.groups.map(&:name).sort.should == %w| AVFoundation AssetsLibrary MediaPlayer MessageUI StoreKit UIKit |
end
end
......
......@@ -38,7 +38,7 @@ describe Pod::LocalPod do
].sort
end
it "returns the source files groupped by specification" do
it "returns the source files grouped by specification" do
files = @pod.source_files_by_spec[@pod.specifications.first].sort
files.should == [
@pod.root + "Classes/Banana.m",
......@@ -83,18 +83,26 @@ describe Pod::LocalPod do
end
it "can add it's source files to an Xcode project target" do
@pod.source_file_descriptions.should == [
Xcodeproj::Project::PBXNativeTarget::SourceFileDescription.new(Pathname.new("BananaLib/Classes/Banana.h"), "", nil),
Xcodeproj::Project::PBXNativeTarget::SourceFileDescription.new(Pathname.new("BananaLib/Classes/Banana.m"), "", nil)
]
project = Pod::Project.new
@pod.add_file_references_to_project(project)
project['Pods/BananaLib/Banana.h'].path.should == "BananaLib/Classes/Banana.h"
project['Pods/BananaLib/Banana.m'].path.should == "BananaLib/Classes/Banana.m"
end
it "can add it's source files to a target with any specially configured compiler flags" do
project = Pod::Project.new
target = project.new_target(:static, 'Pods', :ios)
@pod.top_specification.compiler_flags = '-d some_flag'
@pod.source_file_descriptions.should == [
Xcodeproj::Project::PBXNativeTarget::SourceFileDescription.new(Pathname.new("BananaLib/Classes/Banana.h"), '-d some_flag', nil),
Xcodeproj::Project::PBXNativeTarget::SourceFileDescription.new(Pathname.new("BananaLib/Classes/Banana.m"), '-d some_flag', nil)
]
@pod.add_file_references_to_project(project)
@pod.add_build_files_to_target(target)
h_build_file = target.headers_build_phase.files.first
h_build_file.file_ref.path.should == "BananaLib/Classes/Banana.h"
h_build_file.settings.should == {"ATTRIBUTES"=>["Public"]}
m_build_file = target.source_build_phase.files.first
m_build_file.file_ref.path.should == "BananaLib/Classes/Banana.m"
m_build_file.settings.should == {"COMPILER_FLAGS"=>"-d some_flag"}
end
it "returns the platform" do
......@@ -208,7 +216,8 @@ describe Pod::LocalPod do
/StoreKit/StoreKit_Prefix.pch
/UIKit/UIKit_Prefix.pch
]
computed = @pod.clean_paths.each{ |p| p.gsub!(@pod.root.to_s, '') }
root = @pod.root.to_s
computed = @pod.clean_paths.each{ |p| p.gsub!(root, '') }
assert_array_equals(expected, computed)
end
......
......@@ -105,7 +105,7 @@ describe "Pod::Podfile" do
end
path = config.project_root + 'MyProject.xcodeproj'
config.project_root.expects(:glob).with('*.xcodeproj').returns([path])
Pathname.expects(:glob).with(config.project_root + '*.xcodeproj').returns([path])
podfile.target_definitions[:default].user_project.path.should == path
podfile.target_definitions[:another_target].user_project.path.should == path
......@@ -113,7 +113,7 @@ describe "Pod::Podfile" do
it "assumes the basename of the workspace is the same as the default target's project basename" do
path = config.project_root + 'MyProject.xcodeproj'
config.project_root.expects(:glob).with('*.xcodeproj').returns([path])
Pathname.expects(:glob).with(config.project_root + '*.xcodeproj').returns([path])
Pod::Podfile.new {}.workspace.should == config.project_root + 'MyProject.xcworkspace'
Pod::Podfile.new do
......@@ -225,13 +225,13 @@ describe "Pod::Podfile" do
it "returns a Xcode project found in the working dir when no explicit project is specified" do
xcodeproj1 = config.project_root + '1.xcodeproj'
config.project_root.expects(:glob).with('*.xcodeproj').returns([xcodeproj1])
Pathname.expects(:glob).with(config.project_root + '*.xcodeproj').returns([xcodeproj1])
Pod::Podfile::UserProject.new.path.should == xcodeproj1
end
it "returns `nil' if more than one Xcode project was found in the working when no explicit project is specified" do
xcodeproj1, xcodeproj2 = config.project_root + '1.xcodeproj', config.project_root + '2.xcodeproj'
config.project_root.expects(:glob).with('*.xcodeproj').returns([xcodeproj1, xcodeproj2])
Pathname.expects(:glob).with(config.project_root + '*.xcodeproj').returns([xcodeproj1, xcodeproj2])
Pod::Podfile::UserProject.new.path.should == nil
end
......
......@@ -5,44 +5,32 @@ describe 'Pod::Project' do
@project = Pod::Project.new
end
def find_object(conditions)
@project.objects_hash.select do |_, object|
(conditions.keys - object.keys).empty? && object.values_at(*conditions.keys) == conditions.values
end.first
end
it "adds a group to the `Pods' group" do
group = @project.add_spec_group('JSONKit', @project.pods)
@project.pods.child_references.should.include group.uuid
find_object({
'isa' => 'PBXGroup',
'name' => 'JSONKit',
'sourceTree' => '<group>',
'children' => []
}).should.not == nil
@project.pods.children.should.include?(group)
g = @project['Pods/JSONKit']
g.name.should == 'JSONKit'
g.children.should.be.empty?
end
it "namespaces subspecs in groups" do
group = @project.add_spec_group('JSONKit/Subspec', @project.pods)
@project.pods.groups.find { |g| g.name == 'JSONKit' }.child_references.should.include group.uuid
find_object({
'isa' => 'PBXGroup',
'name' => 'Subspec',
'sourceTree' => '<group>',
'children' => []
}).should.not == nil
@project.pods.groups.find { |g| g.name == 'JSONKit' }.children.should.include?(group)
g = @project['Pods/JSONKit/Subspec']
g.name.should == 'Subspec'
g.children.should.be.empty?
end
it "creates a copy build header phase which will copy headers to a specified path" do
@project.targets.new
phase = @project.targets.first.copy_files_build_phases.new_pod_dir("SomePod", "Path/To/Source")
find_object({
'isa' => 'PBXCopyFilesBuildPhase',
'dstPath' => 'Pods/Path/To/Source',
'name' => 'Copy SomePod Public Headers'
}).should.not == nil
@project.targets.first.build_phases.should.include phase
end
# it "creates a copy build header phase which will copy headers to a specified path" do
# @project.targets.new
# phase = @project.targets.first.copy_files_build_phases.new_pod_dir("SomePod", "Path/To/Source")
# find_object({
# 'isa' => 'PBXCopyFilesBuildPhase',
# 'dstPath' => 'Pods/Path/To/Source',
# 'name' => 'Copy SomePod Public Headers'
# }).should.not == nil
# @project.targets.first.build_phases.should.include phase
# end
it "adds build configurations named after every configuration across all of the user's projects" do
@project.user_build_configurations = { 'Debug' => :debug, 'Release' => :release, 'Test' => :debug, 'AppStore' => :release }
......
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