cocoapods 0.5.1 → 0.6.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/CHANGELOG.md +229 -2
  2. data/README.md +50 -20
  3. data/bin/pod +3 -2
  4. data/lib/cocoapods.rb +23 -9
  5. data/lib/cocoapods/command.rb +71 -30
  6. data/lib/cocoapods/command/error_report.rb +102 -0
  7. data/lib/cocoapods/command/install.rb +27 -19
  8. data/lib/cocoapods/command/list.rb +51 -8
  9. data/lib/cocoapods/command/presenter.rb +61 -0
  10. data/lib/cocoapods/command/presenter/cocoa_pod.rb +123 -0
  11. data/lib/cocoapods/command/push.rb +102 -0
  12. data/lib/cocoapods/command/repo.rb +70 -14
  13. data/lib/cocoapods/command/search.rb +7 -10
  14. data/lib/cocoapods/command/setup.rb +76 -15
  15. data/lib/cocoapods/command/spec.rb +581 -97
  16. data/lib/cocoapods/config.rb +23 -26
  17. data/lib/cocoapods/dependency.rb +86 -40
  18. data/lib/cocoapods/downloader.rb +30 -18
  19. data/lib/cocoapods/downloader/git.rb +125 -15
  20. data/lib/cocoapods/downloader/http.rb +73 -0
  21. data/lib/cocoapods/downloader/mercurial.rb +3 -9
  22. data/lib/cocoapods/downloader/subversion.rb +3 -9
  23. data/lib/cocoapods/executable.rb +26 -3
  24. data/lib/cocoapods/generator/acknowledgements.rb +37 -0
  25. data/lib/cocoapods/generator/acknowledgements/markdown.rb +38 -0
  26. data/lib/cocoapods/generator/acknowledgements/plist.rb +63 -0
  27. data/lib/cocoapods/generator/copy_resources_script.rb +8 -4
  28. data/lib/cocoapods/generator/documentation.rb +99 -0
  29. data/lib/cocoapods/generator/dummy_source.rb +14 -0
  30. data/lib/cocoapods/installer.rb +140 -109
  31. data/lib/cocoapods/installer/target_installer.rb +78 -83
  32. data/lib/cocoapods/installer/user_project_integrator.rb +162 -0
  33. data/lib/cocoapods/local_pod.rb +240 -0
  34. data/lib/cocoapods/platform.rb +41 -18
  35. data/lib/cocoapods/podfile.rb +234 -21
  36. data/lib/cocoapods/project.rb +67 -0
  37. data/lib/cocoapods/resolver.rb +62 -32
  38. data/lib/cocoapods/sandbox.rb +63 -0
  39. data/lib/cocoapods/source.rb +42 -20
  40. data/lib/cocoapods/specification.rb +294 -271
  41. data/lib/cocoapods/specification/set.rb +10 -28
  42. data/lib/cocoapods/specification/statistics.rb +112 -0
  43. metadata +124 -11
  44. data/lib/cocoapods/xcodeproj_pods.rb +0 -111
@@ -10,16 +10,23 @@ module Pod
10
10
  @instance = instance
11
11
  end
12
12
 
13
- attr_accessor :repos_dir, :project_root, :project_pods_root, :rootspec, :clean, :verbose, :silent
14
- alias_method :clean?, :clean
15
- alias_method :verbose?, :verbose
16
- alias_method :silent?, :silent
13
+ attr_accessor :repos_dir, :project_root, :project_pods_root
14
+ attr_accessor :clean, :verbose, :silent
15
+ attr_accessor :generate_docs, :doc_install
16
+ attr_accessor :integrate_targets
17
+ attr_accessor :git_cache_size
18
+
19
+ alias_method :clean?, :clean
20
+ alias_method :verbose?, :verbose
21
+ alias_method :silent?, :silent
22
+ alias_method :generate_docs?, :generate_docs
23
+ alias_method :doc_install?, :doc_install
24
+ alias_method :integrate_targets?, :integrate_targets
17
25
 
18
26
  def initialize
19
27
  @repos_dir = Pathname.new(File.expand_path("~/.cocoapods"))
20
- @clean = true
21
- @verbose = false
22
- @silent = false
28
+ @verbose = @silent = false
29
+ @clean = @generate_docs = @doc_install = @integrate_targets = true
23
30
  end
24
31
 
25
32
  def project_root
@@ -31,13 +38,7 @@ module Pod
31
38
  end
32
39
 
33
40
  def project_podfile
34
- unless @project_podfile
35
- @project_podfile = project_root + 'Podfile'
36
- unless @project_podfile.exist?
37
- @project_podfile = project_root.glob('*.podspec').first
38
- end
39
- end
40
- @project_podfile
41
+ @project_podfile ||= project_root + 'Podfile'
41
42
  end
42
43
 
43
44
  def headers_symlink_root
@@ -45,25 +46,21 @@ module Pod
45
46
  end
46
47
 
47
48
  # Returns the spec at the pat returned from `project_podfile`.
48
- def rootspec
49
- unless @rootspec
50
- if project_podfile
51
- if project_podfile.basename.to_s == 'Podfile'
52
- @rootspec = Podfile.from_file(project_podfile)
53
- else
54
- @rootspec = Specification.from_file(project_podfile)
55
- end
56
- end
49
+ def podfile
50
+ @podfile ||= begin
51
+ Podfile.from_file(project_podfile) if project_podfile.exist?
57
52
  end
58
- @rootspec
59
53
  end
54
+ attr_writer :podfile
60
55
 
61
56
  def ios?
62
- rootspec.platform == :ios if rootspec
57
+ # TODO: deprecate in 0.7
58
+ podfile.target_definitions[:default].platform == :ios if podfile
63
59
  end
64
60
 
65
61
  def osx?
66
- rootspec.platform == :osx if rootspec
62
+ # TODO: deprecate in 0.7
63
+ podfile.target_definitions[:default].platform == :osx if podfile
67
64
  end
68
65
 
69
66
  module Mixin
@@ -1,14 +1,12 @@
1
1
  module Gem
2
2
  end
3
3
  require 'rubygems/dependency'
4
+ require 'open-uri'
4
5
 
5
6
  module Pod
6
7
  class Dependency < Gem::Dependency
7
- attr_accessor :only_part_of_other_pod
8
- alias_method :only_part_of_other_pod?, :only_part_of_other_pod
9
-
10
- attr_accessor :external_spec_source
11
8
 
9
+ attr_reader :external_source
12
10
  attr_accessor :specification
13
11
 
14
12
  def initialize(*name_and_version_requirements, &block)
@@ -19,7 +17,7 @@ module Pod
19
17
 
20
18
  elsif !name_and_version_requirements.empty? && block.nil?
21
19
  if name_and_version_requirements.last.is_a?(Hash)
22
- @external_spec_source = name_and_version_requirements.pop
20
+ @external_source = ExternalSources.from_params(name_and_version_requirements[0].split('/').first, name_and_version_requirements.pop)
23
21
  end
24
22
  super(*name_and_version_requirements)
25
23
 
@@ -27,19 +25,24 @@ module Pod
27
25
  raise Informative, "A dependency needs either a name and version requirements, " \
28
26
  "a source hash, or a block which defines a podspec."
29
27
  end
30
- @only_part_of_other_pod = false
31
28
  end
32
29
 
33
30
  def ==(other)
34
- super &&
35
- @only_part_of_other_pod == other.only_part_of_other_pod &&
36
- (@specification ? @specification == other.specification : @external_spec_source == other.external_spec_source)
31
+ super && (@specification ? @specification == other.specification : @external_source == other.external_source)
37
32
  end
38
33
 
39
34
  def subspec_dependency?
40
35
  @name.include?('/')
41
36
  end
42
37
 
38
+ def inline?
39
+ @inline_podspec
40
+ end
41
+
42
+ def external?
43
+ !@external_source.nil?
44
+ end
45
+
43
46
  # In case this is a dependency for a subspec, e.g. 'RestKit/Networking',
44
47
  # this returns 'RestKit', which is what the Pod::Source needs to know to
45
48
  # retrieve the correct Set from disk.
@@ -58,11 +61,9 @@ module Pod
58
61
 
59
62
  def to_s
60
63
  version = ''
61
- if source = @external_spec_source
62
- version << "from `#{source[:git] || source[:podspec]}'"
63
- version << ", commit `#{source[:commit]}'" if source[:commit]
64
- version << ", tag `#{source[:tag]}'" if source[:tag]
65
- elsif @inline_podspec
64
+ if external?
65
+ version << @external_source.description
66
+ elsif inline?
66
67
  version << "defined in Podfile"
67
68
  elsif @version_requirements != Gem::Requirement.default
68
69
  version << @version_requirements.to_s
@@ -70,32 +71,8 @@ module Pod
70
71
  version.empty? ? @name : "#{@name} (#{version})"
71
72
  end
72
73
 
73
- # In case this dependency was defined with either a repo url, :podspec, or block,
74
- # this method will return the Specification instance.
75
- def specification
76
- @specification ||= begin
77
- if @external_spec_source
78
- config = Config.instance
79
- pod_root = config.project_pods_root + @name
80
- spec = nil
81
- if @external_spec_source[:podspec]
82
- config.project_pods_root.mkpath
83
- spec = config.project_pods_root + "#{@name}.podspec"
84
- source = @external_spec_source[:podspec]
85
- # can be http, file, etc
86
- require 'open-uri'
87
- puts " * Fetching podspec for `#{@name}' from: #{source}" unless config.silent?
88
- open(source) do |io|
89
- spec.open('w') { |f| f << io.read }
90
- end
91
- else
92
- puts " * Pre-downloading: `#{@name}'" unless config.silent?
93
- Downloader.for_source(pod_root, @external_spec_source).download
94
- spec = pod_root + "#{@name}.podspec"
95
- end
96
- Specification.from_file(spec)
97
- end
98
- end
74
+ def specification_from_sandbox(sandbox, platform)
75
+ @external_source.specification_from_sandbox(sandbox, platform)
99
76
  end
100
77
 
101
78
  # Taken from RubyGems 1.3.7
@@ -134,5 +111,74 @@ module Pod
134
111
  end
135
112
  end
136
113
 
114
+ module ExternalSources
115
+ def self.from_params(name, params)
116
+ if params.key?(:git)
117
+ GitSource.new(name, params)
118
+ elsif params.key?(:podspec)
119
+ PodspecSource.new(name, params)
120
+ else
121
+ raise Informative, "Unknown external source parameters for #{name}: #{params}"
122
+ end
123
+ end
124
+
125
+ class AbstractExternalSource
126
+ include Config::Mixin
127
+
128
+ attr_reader :name, :params
129
+
130
+ def initialize(name, params)
131
+ @name, @params = name, params
132
+ end
133
+
134
+ def specification_from_sandbox(sandbox, platform)
135
+ if local_pod = sandbox.installed_pod_named(name, platform)
136
+ local_pod.top_specification
137
+ else
138
+ copy_external_source_into_sandbox(sandbox)
139
+ local_pod = sandbox.installed_pod_named(name, platform)
140
+ local_pod.clean if config.clean? && local_pod.exists?
141
+ local_pod.top_specification
142
+ end
143
+ end
144
+
145
+ def ==(other_source)
146
+ return if other_source.nil?
147
+ name == other_source.name && params == other_source.params
148
+ end
149
+ end
150
+
151
+ class GitSource < AbstractExternalSource
152
+ def copy_external_source_into_sandbox(sandbox)
153
+ puts " * Pre-downloading: '#{name}'" unless config.silent?
154
+ Downloader.for_target(sandbox.root + name, @params).tap do |downloader|
155
+ downloader.download
156
+ end
157
+ end
158
+
159
+ def description
160
+ "from `#{@params[:git]}'".tap do |description|
161
+ description << ", commit `#{@params[:commit]}'" if @params[:commit]
162
+ description << ", tag `#{@params[:tag]}'" if @params[:tag]
163
+ end
164
+ end
165
+ end
166
+
167
+ # can be http, file, etc
168
+ class PodspecSource < AbstractExternalSource
169
+ def copy_external_source_into_sandbox(sandbox)
170
+ output_path = sandbox.root + "Local Podspecs/#{name}.podspec"
171
+ output_path.dirname.mkpath
172
+ puts " * Fetching podspec for `#{name}' from: #{@params[:podspec]}" unless config.silent?
173
+ open(@params[:podspec]) do |io|
174
+ output_path.open('w') { |f| f << io.read }
175
+ end
176
+ end
177
+
178
+ def description
179
+ "from `#{@params[:podspec]}'"
180
+ end
181
+ end
182
+ end
137
183
  end
138
184
  end
@@ -1,34 +1,46 @@
1
+ require 'uri'
2
+
1
3
  module Pod
2
4
  class Downloader
3
5
  autoload :Git, 'cocoapods/downloader/git'
6
+ autoload :GitHub, 'cocoapods/downloader/git'
4
7
  autoload :Mercurial, 'cocoapods/downloader/mercurial'
5
8
  autoload :Subversion, 'cocoapods/downloader/subversion'
9
+ autoload :Http, 'cocoapods/downloader/http'
6
10
 
7
11
  extend Executable
8
12
 
9
- def self.for_source(pod_root, source)
10
- options = source.dup
11
- if url = options.delete(:git)
12
- Git.new(pod_root, url, options)
13
- elsif url = options.delete(:hg)
14
- Mercurial.new(pod_root, url, options)
15
- elsif url = options.delete(:svn)
16
- Subversion.new(pod_root, url, options)
17
- else
18
- raise "Unsupported download strategy `#{source.inspect}'."
19
- end
13
+ def self.for_pod(pod)
14
+ spec = pod.top_specification
15
+ for_target(pod.root, spec.source.dup)
20
16
  end
21
17
 
22
- attr_reader :pod_root, :url, :options
18
+ attr_reader :target_path, :url, :options
23
19
 
24
- def initialize(pod_root, url, options)
25
- @pod_root, @url, @options = pod_root, url, options
20
+ def initialize(target_path, url, options)
21
+ @target_path, @url, @options = target_path, url, options
22
+ @target_path.mkpath
26
23
  end
27
24
 
28
- def clean(clean_paths = [])
29
- clean_paths.each do |path|
30
- path.rmtree
31
- end if clean_paths
25
+ private
26
+
27
+ def self.for_target(target_path, options)
28
+ options = options.dup
29
+ if url = options.delete(:git)
30
+ if url.to_s =~ /github.com/
31
+ GitHub.new(target_path, url, options)
32
+ else
33
+ Git.new(target_path, url, options)
34
+ end
35
+ elsif url = options.delete(:hg)
36
+ Mercurial.new(target_path, url, options)
37
+ elsif url = options.delete(:svn)
38
+ Subversion.new(target_path, url, options)
39
+ elsif url = options.delete(:http)
40
+ Http.new(target_path, url, options)
41
+ else
42
+ raise "Unsupported download strategy `#{options.inspect}'."
43
+ end
32
44
  end
33
45
  end
34
46
  end
@@ -1,46 +1,156 @@
1
+ require 'open-uri'
2
+ require 'tempfile'
3
+ require 'zlib'
4
+ require 'digest/sha1'
5
+
1
6
  module Pod
2
7
  class Downloader
3
8
  class Git < Downloader
9
+ include Config::Mixin
4
10
  executable :git
5
11
 
12
+ MAX_CACHE_SIZE = 500
13
+
6
14
  def download
7
- @pod_root.dirname.mkpath
8
- if @options[:tag]
15
+ create_cache unless cache_exist?
16
+ puts '-> Cloning git repo' if config.verbose?
17
+ if options[:tag]
9
18
  download_tag
10
- elsif @options[:commit]
19
+ elsif options[:commit]
11
20
  download_commit
12
21
  else
13
22
  download_head
14
23
  end
24
+ removed_cached_repos_if_needed
25
+ end
26
+
27
+ def create_cache
28
+ puts "-> Creating cache git repo (#{cache_path})" if config.verbose?
29
+ cache_path.rmtree if cache_path.exist?
30
+ cache_path.mkpath
31
+ git "clone '#{url}' #{cache_path}"
32
+ end
33
+
34
+ def removed_cached_repos_if_needed
35
+ return unless caches_dir.exist?
36
+ Dir.chdir(caches_dir) do
37
+ repos = Pathname.new(caches_dir).children.select { |c| c.directory? }.sort_by(&:ctime)
38
+ while caches_size >= MAX_CACHE_SIZE && !repos.empty?
39
+ dir = repos.shift
40
+ puts '->'.yellow << " Removing git cache for `#{origin_url(dir)}'" if config.verbose?
41
+ dir.rmtree
42
+ end
43
+ end
44
+ end
45
+
46
+ def cache_path
47
+ @cache_path ||= caches_dir + "#{Digest::SHA1.hexdigest(url.to_s)}"
48
+ end
49
+
50
+ def cache_exist?
51
+ cache_path.exist? && origin_url(cache_path).to_s == url.to_s
52
+ end
53
+
54
+ def origin_url(dir)
55
+ Dir.chdir(dir) { `git config remote.origin.url`.chomp }
56
+ end
57
+
58
+ def caches_dir
59
+ Pathname.new(File.expand_path("~/Library/Caches/CocoaPods/Git"))
60
+ end
61
+
62
+ def clone_url
63
+ cache_path
64
+ end
65
+
66
+ def caches_size
67
+ # expressed in Mb
68
+ `du -cm`.split("\n").last.to_i
69
+ end
70
+
71
+ def update_cache
72
+ puts "-> Updating cache git repo (#{cache_path})" if config.verbose?
73
+ Dir.chdir(cache_path) do
74
+ git "reset --hard HEAD"
75
+ git "clean -d -x -f"
76
+ git "pull"
77
+ end
78
+ end
79
+
80
+ def ref_exists?(ref)
81
+ Dir.chdir(cache_path) { git "rev-list --max-count=1 #{ref}" }
82
+ $? == 0
83
+ end
84
+
85
+ def ensure_ref_exists(ref)
86
+ return if ref_exists?(ref)
87
+ # Skip pull if not needed
88
+ update_cache
89
+ raise Informative, "[!] Cache unable to find git reference `#{ref}' for `#{url}'.".red unless ref_exists?(ref)
15
90
  end
16
91
 
17
92
  def download_head
18
- git "clone '#{@url}' '#{@pod_root}'"
93
+ update_cache
94
+ git "clone '#{clone_url}' '#{target_path}'"
19
95
  end
20
96
 
21
97
  def download_tag
22
- @pod_root.mkpath
23
- Dir.chdir(@pod_root) do
98
+ ensure_ref_exists(options[:tag])
99
+ Dir.chdir(target_path) do
24
100
  git "init"
25
- git "remote add origin '#{@url}'"
26
- git "fetch origin tags/#{@options[:tag]}"
101
+ git "remote add origin '#{clone_url}'"
102
+ git "fetch origin tags/#{options[:tag]}"
27
103
  git "reset --hard FETCH_HEAD"
28
104
  git "checkout -b activated-pod-commit"
29
105
  end
30
106
  end
31
107
 
32
108
  def download_commit
33
- git "clone '#{@url}' '#{@pod_root}'"
34
- Dir.chdir(@pod_root) do
35
- git "checkout -b activated-pod-commit #{@options[:commit]}"
109
+ ensure_ref_exists(options[:commit])
110
+ git "clone '#{clone_url}' '#{target_path}'"
111
+ Dir.chdir(target_path) do
112
+ git "checkout -b activated-pod-commit #{options[:commit]}"
36
113
  end
37
114
  end
115
+ end
116
+
117
+ class GitHub < Git
118
+ def download_head
119
+ download_only? ? download_and_extract_tarball('master') : super
120
+ end
121
+
122
+ def download_tag
123
+ download_only? ? download_and_extract_tarball(options[:tag]) : super
124
+ end
38
125
 
39
- def clean(clean_paths = [])
40
- super
41
- (@pod_root + '.git').rmtree
126
+ def download_commit
127
+ download_only? ? download_and_extract_tarball(options[:commit]) : super
128
+ end
129
+
130
+ def tarball_url_for(id)
131
+ original_url, username, reponame = *(url.match(/[:\/]([\w\-]+)\/([\w\-]+)\.git/).to_a)
132
+ "https://github.com/#{username}/#{reponame}/tarball/#{id}"
133
+ end
134
+
135
+ def tmp_path
136
+ target_path + "tarball.tar.gz"
137
+ end
138
+
139
+ private
140
+
141
+ def download_only?
142
+ @options[:download_only]
143
+ end
144
+
145
+ def download_and_extract_tarball(id)
146
+ File.open(tmp_path, "w+") do |tmpfile|
147
+ open tarball_url_for(id) do |archive|
148
+ tmpfile.write Zlib::GzipReader.new(archive).read
149
+ end
150
+
151
+ system "tar xf #{tmpfile.path} -C #{target_path} --strip-components 1"
152
+ end
42
153
  end
43
154
  end
44
155
  end
45
156
  end
46
-