cocoapods 0.5.1 → 0.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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
-