cocoapods 0.13.0 → 0.14.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -41,11 +41,16 @@ module Pod
41
41
  @project_podfile ||= project_root + 'Podfile'
42
42
  end
43
43
 
44
+ def project_lockfile
45
+ @project_lockfile ||= project_root + 'Podfile.lock'
46
+ end
47
+
44
48
  def headers_symlink_root
45
49
  @headers_symlink_root ||= "#{project_pods_root}/Headers"
46
50
  end
47
51
 
48
- # Returns the spec at the pat returned from `project_podfile`.
52
+ # @return [Podfile] The Podfile to use for the current execution.
53
+ #
49
54
  def podfile
50
55
  @podfile ||= begin
51
56
  Podfile.from_file(project_podfile) if project_podfile.exist?
@@ -53,6 +58,14 @@ module Pod
53
58
  end
54
59
  attr_writer :podfile
55
60
 
61
+ # @return [Lockfile] The Lockfile to use for the current execution.
62
+ #
63
+ def lockfile
64
+ @lockfile ||= begin
65
+ Lockfile.from_file(project_lockfile) if project_lockfile.exist?
66
+ end
67
+ end
68
+
56
69
  module Mixin
57
70
  def config
58
71
  Config.instance
@@ -3,9 +3,9 @@ require 'cocoapods/open_uri'
3
3
  module Pod
4
4
  class Dependency < Gem::Dependency
5
5
 
6
- attr_reader :external_source, :head
6
+ attr_reader :head
7
7
  alias :head? :head
8
- attr_accessor :specification
8
+ attr_accessor :specification, :external_source
9
9
 
10
10
  def initialize(*name_and_version_requirements, &block)
11
11
  if name_and_version_requirements.empty? && block
@@ -40,7 +40,7 @@ module Pod
40
40
  end
41
41
 
42
42
  def ==(other)
43
- super && (@specification ? @specification == other.specification : @external_source == other.external_source)
43
+ super && (head? == other.head?) && (@specification ? @specification == other.specification : @external_source == other.external_source)
44
44
  end
45
45
 
46
46
  def subspec_dependency?
@@ -76,13 +76,14 @@ module Pod
76
76
  if external?
77
77
  version << @external_source.description
78
78
  elsif inline?
79
- version << "defined in Podfile"
79
+ version << 'defined in Podfile'
80
+ elsif head?
81
+ version << 'HEAD'
80
82
  elsif @version_requirements != Gem::Requirement.default
81
83
  version << @version_requirements.to_s
82
84
  end
83
85
  result = @name.dup
84
- result += " (#{version})" unless version.empty?
85
- result += " [HEAD]" if head?
86
+ result << " (#{version})" unless version.empty?
86
87
  result
87
88
  end
88
89
 
@@ -90,6 +91,10 @@ module Pod
90
91
  @external_source.specification_from_sandbox(sandbox, platform)
91
92
  end
92
93
 
94
+ def match_version?(version)
95
+ match?(name, version) && (version.head? == head?)
96
+ end
97
+
93
98
  # Taken from RubyGems 1.3.7
94
99
  unless public_method_defined?(:match?)
95
100
  def match?(spec_name, spec_version)
@@ -128,10 +133,13 @@ module Pod
128
133
 
129
134
  module ExternalSources
130
135
  def self.from_params(name, params)
136
+ return unless name && params
131
137
  if params.key?(:git)
132
138
  GitSource.new(name, params)
133
139
  elsif params.key?(:podspec)
134
140
  PodspecSource.new(name, params)
141
+ elsif params.key?(:local)
142
+ LocalSource.new(name, params)
135
143
  else
136
144
  raise Informative, "Unknown external source parameters for #{name}: #{params}"
137
145
  end
@@ -158,18 +166,22 @@ module Pod
158
166
 
159
167
  def specification_from_external(sandbox, platform)
160
168
  copy_external_source_into_sandbox(sandbox, platform)
161
- specification_from_local(sandbox, platform)
169
+ spec = specification_from_local(sandbox, platform)
170
+ raise Informative, "No podspec found for `#{name}' in #{description}" unless spec
171
+ spec
162
172
  end
163
173
 
164
- def ==(other_source)
165
- return if other_source.nil?
166
- name == other_source.name && params == other_source.params
174
+ def ==(other)
175
+ return if other.nil?
176
+ name == other.name && params == other.params
167
177
  end
168
178
  end
169
179
 
170
180
  class GitSource < AbstractExternalSource
171
181
  def copy_external_source_into_sandbox(sandbox, platform)
172
- puts " * Pre-downloading: '#{name}'" unless config.silent?
182
+ puts "-> Pre-downloading: '#{name}'" unless config.silent?
183
+ target = sandbox.root + name
184
+ target.rmtree if target.exist?
173
185
  downloader = Downloader.for_target(sandbox.root + name, @params)
174
186
  downloader.download
175
187
  if local_pod = sandbox.installed_pod_named(name, platform)
@@ -191,8 +203,10 @@ module Pod
191
203
  def copy_external_source_into_sandbox(sandbox, _)
192
204
  output_path = sandbox.root + "Local Podspecs/#{name}.podspec"
193
205
  output_path.dirname.mkpath
194
- puts " * Fetching podspec for `#{name}' from: #{@params[:podspec]}" unless config.silent?
195
- open(@params[:podspec]) do |io|
206
+ puts "-> Fetching podspec for `#{name}' from: #{@params[:podspec]}" unless config.silent?
207
+ path = @params[:podspec]
208
+ path = Pathname.new(path).expand_path if path.start_with?("~")
209
+ open(path) do |io|
196
210
  output_path.open('w') { |f| f << io.read }
197
211
  end
198
212
  end
@@ -201,6 +215,35 @@ module Pod
201
215
  "from `#{@params[:podspec]}'"
202
216
  end
203
217
  end
218
+
219
+ class LocalSource < AbstractExternalSource
220
+ def pod_spec_path
221
+ path = Pathname.new(@params[:local]).expand_path + "#{name}.podspec"
222
+ raise Informative, "No podspec found for `#{name}' in `#{@params[:local]}'" unless path.exist?
223
+ path
224
+ end
225
+
226
+ def copy_external_source_into_sandbox(sandbox, _)
227
+ output_path = sandbox.root + "Local Podspecs/#{name}.podspec"
228
+ output_path.dirname.mkpath
229
+ FileUtils.copy(pod_spec_path, output_path)
230
+ end
231
+
232
+ def specification_from_local(sandbox, platform)
233
+ specification_from_external(sandbox, platform)
234
+ end
235
+
236
+ def specification_from_external(sandbox, platform)
237
+ copy_external_source_into_sandbox(sandbox, platform)
238
+ spec = Specification.from_file(pod_spec_path)
239
+ spec.source = @params
240
+ spec
241
+ end
242
+
243
+ def description
244
+ "from `#{@params[:local]}'"
245
+ end
246
+ end
204
247
  end
205
248
  end
206
249
  end
@@ -79,6 +79,7 @@ module Pod
79
79
  git! "reset --hard HEAD"
80
80
  git! "clean -d -x -f"
81
81
  git! "pull origin master"
82
+ git! "fetch --tags"
82
83
  end
83
84
  end
84
85
 
@@ -3,12 +3,16 @@ module Pod
3
3
  class Subversion < Downloader
4
4
  executable :svn
5
5
 
6
+ def initialize(target_path, url, options)
7
+ @target_path, @url, @options = target_path, url, options
8
+ end
9
+
6
10
  def download
7
- svn! %|checkout "#{reference_url}" "#{target_path}"|
11
+ svn! %|export "#{reference_url}" "#{target_path}"|
8
12
  end
9
13
 
10
14
  def download_head
11
- svn! %|checkout "#{trunk_url}" "#{target_path}"|
15
+ svn! %|export "#{trunk_url}" "#{target_path}"|
12
16
  end
13
17
 
14
18
  def reference_url
@@ -7,19 +7,12 @@ module Pod
7
7
 
8
8
  include Config::Mixin
9
9
 
10
- attr_reader :sandbox
11
-
12
- def initialize(podfile)
13
- @podfile = podfile
14
- # FIXME: pass this into the installer as a parameter
15
- @sandbox = Sandbox.new(config.project_pods_root)
16
- @resolver = Resolver.new(@podfile, @sandbox)
17
- # TODO: remove in 0.7 (legacy support for config.ios? and config.osx?)
18
- config.podfile = podfile
19
- end
10
+ attr_reader :resolver, :sandbox, :lockfile
20
11
 
21
- def lock_file
22
- config.project_root + 'Podfile.lock'
12
+ def initialize(resolver)
13
+ @resolver = resolver
14
+ @podfile = resolver.podfile
15
+ @sandbox = resolver.sandbox
23
16
  end
24
17
 
25
18
  def project
@@ -29,7 +22,8 @@ module Pod
29
22
  pods.each do |pod|
30
23
  # Add all source files to the project grouped by pod
31
24
  pod.relative_source_files_by_spec.each do |spec, paths|
32
- group = @project.add_spec_group(spec.name)
25
+ parent_group = pod.local? ? @project.local_pods : @project.pods
26
+ group = @project.add_spec_group(pod.name, parent_group)
33
27
  paths.each do |path|
34
28
  group.files.new('path' => path.to_s)
35
29
  end
@@ -46,8 +40,17 @@ module Pod
46
40
  end.compact
47
41
  end
48
42
 
43
+ # Install the Pods. If the resolver indicated that a Pod should be installed
44
+ # and it exits, it is removed an then reinstalled. In any case if the Pod
45
+ # doesn't exits it is installed.
46
+ #
47
+ # @return [void]
48
+ #
49
49
  def install_dependencies!
50
- pods.each do |pod|
50
+ pods.sort_by { |pod| pod.top_specification.name.downcase }.each do |pod|
51
+ name = pod.top_specification.name
52
+ should_install = @resolver.should_install?(name) || !pod.exists?
53
+
51
54
  unless config.silent?
52
55
  marker = config.verbose ? "\n-> ".green : ''
53
56
  if subspec_name = pod.top_specification.preferred_dependency
@@ -55,15 +58,14 @@ module Pod
55
58
  else
56
59
  name = pod.to_s
57
60
  end
58
- name << " [HEAD]" if pod.top_specification.version.head?
59
- puts marker << ( pod.exists? ? "Using #{name}" : "Installing #{name}".green )
61
+ puts marker << ( should_install ? "Installing #{name}".green : "Using #{name}" )
60
62
  end
61
63
 
62
- download_pod(pod) unless pod.exists?
63
-
64
- # This will not happen if the pod existed before we started the install
65
- # process.
66
- if pod.downloaded?
64
+ if should_install
65
+ unless pod.downloaded?
66
+ pod.implode
67
+ download_pod(pod)
68
+ end
67
69
  # The docs need to be generated before cleaning because the
68
70
  # documentation is created for all the subspecs.
69
71
  generate_docs(pod)
@@ -100,12 +102,26 @@ module Pod
100
102
  end
101
103
  end
102
104
 
105
+ # @TODO: use the local pod implode
106
+ #
107
+ def remove_deleted_dependencies!
108
+ resolver.removed_pods.each do |pod_name|
109
+ marker = config.verbose ? "\n-> ".red : ''
110
+ path = sandbox.root + pod_name
111
+ puts marker << "Removing #{pod_name}".red
112
+ path.rmtree if path.exist?
113
+ end
114
+ end
115
+
103
116
  def install!
104
117
  @sandbox.prepare_for_install
105
118
 
106
119
  print_title "Resolving dependencies of: #{@podfile.defined_in_file}"
107
120
  specs_by_target
108
121
 
122
+ print_title "Removing deleted dependencies" unless resolver.removed_pods.empty?
123
+ remove_deleted_dependencies!
124
+
109
125
  print_title "Installing dependencies"
110
126
  install_dependencies!
111
127
 
@@ -119,15 +135,17 @@ module Pod
119
135
  generate_dummy_source(target_installer)
120
136
  end
121
137
 
122
- generate_lock_file!(specifications)
123
-
124
138
  puts "- Running post install hooks" if config.verbose?
125
139
  # Post install hooks run _before_ saving of project, so that they can alter it before saving.
126
140
  run_post_install_hooks
127
141
 
128
- puts "- Writing Xcode project file to `#{@sandbox.project_path}'\n\n" if config.verbose?
142
+ puts "- Writing Xcode project file to `#{@sandbox.project_path}'" if config.verbose?
129
143
  project.save_as(@sandbox.project_path)
130
144
 
145
+ puts "- Writing lockfile in `#{config.project_lockfile}'\n\n" if config.verbose?
146
+ @lockfile = Lockfile.generate(@podfile, specs_by_target.values.flatten)
147
+ @lockfile.write_to_disk(config.project_lockfile)
148
+
131
149
  UserProjectIntegrator.new(@podfile).integrate! if config.integrate_targets?
132
150
  end
133
151
 
@@ -143,44 +161,6 @@ module Pod
143
161
  @podfile.post_install!(self)
144
162
  end
145
163
 
146
- def generate_lock_file!(specs)
147
- lock_file.open('w') do |file|
148
- file.puts "PODS:"
149
-
150
- # Get list of [name, dependencies] pairs.
151
- pod_and_deps = specs.map do |spec|
152
- [spec.to_s, spec.dependencies.map(&:to_s).sort]
153
- end.uniq
154
-
155
- # Merge dependencies of ios and osx version of the same pod.
156
- tmp = {}
157
- pod_and_deps.each do |name, deps|
158
- if tmp[name]
159
- tmp[name].concat(deps).uniq!
160
- else
161
- tmp[name] = deps
162
- end
163
- end
164
- pod_and_deps = tmp
165
-
166
- # Sort by name and print
167
- pod_and_deps.sort_by(&:first).each do |name, deps|
168
- if deps.empty?
169
- file.puts " - #{name}"
170
- else
171
- file.puts " - #{name}:"
172
- deps.each { |dep| file.puts " - #{dep}" }
173
- end
174
- end
175
-
176
- file.puts
177
- file.puts "DEPENDENCIES:"
178
- @podfile.dependencies.map(&:to_s).sort.each do |dep|
179
- file.puts " - #{dep}"
180
- end
181
- end
182
- end
183
-
184
164
  def generate_dummy_source(target_installer)
185
165
  class_name_identifier = target_installer.target_definition.label
186
166
  dummy_source = Generator::DummySource.new(class_name_identifier)
@@ -206,7 +186,7 @@ module Pod
206
186
  # @return [Array<LocalPod>] A list of LocalPod instances for each
207
187
  # dependency that is not a download-only one.
208
188
  def pods
209
- pods_by_target.values.flatten
189
+ pods_by_target.values.flatten.uniq
210
190
  end
211
191
 
212
192
  def pods_by_target
@@ -215,7 +195,11 @@ module Pod
215
195
  specs_by_target.each do |target_definition, specs|
216
196
  @pods_by_spec[target_definition.platform] = {}
217
197
  result[target_definition] = specs.map do |spec|
218
- @sandbox.local_pod_for_spec(spec, target_definition.platform)
198
+ if spec.local?
199
+ LocalPod::LocalSourcedPod.new(spec, sandbox, target_definition.platform)
200
+ else
201
+ @sandbox.local_pod_for_spec(spec, target_definition.platform)
202
+ end
219
203
  end.uniq.compact
220
204
  end
221
205
  result
@@ -45,16 +45,18 @@ module Pod
45
45
  attr_accessor :downloaded
46
46
  alias_method :downloaded?, :downloaded
47
47
 
48
- # @param [Specification] specification
49
- # The first activated specification of the pod.
50
- # @param [Sandbox] sandbox
51
- # The sandbox where the files of the pod will be located.
52
- # @param [Platform] platform
53
- # The platform that will be used to build the pod.
48
+ # @param [Specification] specification The first activated specification
49
+ # of the pod.
50
+ #
51
+ # @param [Sandbox] sandbox The sandbox where the files of the
52
+ # pod will be located.
53
+ #
54
+ # @param [Platform] platform The platform that will be used to
55
+ # build the pod.
54
56
  #
55
57
  # @todo The local pod should be initialized with all the activated
56
- # specifications passed as an array, in order to be able to cache the
57
- # computed values. In other words, it should be immutable.
58
+ # specifications passed as an array, in order to be able to cache the
59
+ # computed values. In other words, it should be immutable.
58
60
  #
59
61
  def initialize(specification, sandbox, platform)
60
62
  @top_specification, @sandbox, @platform = specification.top_level_parent, sandbox, platform
@@ -93,12 +95,10 @@ module Pod
93
95
  end
94
96
 
95
97
  # @return [String] A string representation of the pod which indicates if
96
- # the pods comes from a local source.
98
+ # the pods comes from a local source.
97
99
  #
98
100
  def to_s
99
- result = top_specification.to_s
100
- result << " [LOCAL]" if top_specification.local?
101
- result
101
+ top_specification.to_s
102
102
  end
103
103
 
104
104
  # @return [String] The name of the Pod.
@@ -140,6 +140,10 @@ module Pod
140
140
  root.rmtree if exists?
141
141
  end
142
142
 
143
+ def local?
144
+ false
145
+ end
146
+
143
147
  # @!group Cleaning
144
148
 
145
149
  # Deletes any path that is not used by the pod.
@@ -508,5 +512,39 @@ module Pod
508
512
  Pathname.glob(pattern, File::FNM_CASEFOLD)
509
513
  end.flatten
510
514
  end
515
+
516
+ # A {LocalSourcedPod} is a {LocalPod} that interacts with the files of
517
+ # a folder controlled by the users. As such this class does not alter
518
+ # in any way the contents of the folder.
519
+ #
520
+ class LocalSourcedPod < LocalPod
521
+ def downloaded?
522
+ true
523
+ end
524
+
525
+ def create
526
+ # No ops
527
+ end
528
+
529
+ def root
530
+ @root ||= Pathname.new(@top_specification.source[:local]).expand_path
531
+ end
532
+
533
+ def implode
534
+ # No ops
535
+ end
536
+
537
+ def clean!
538
+ # No ops
539
+ end
540
+
541
+ def to_s
542
+ super + " [LOCAL]"
543
+ end
544
+
545
+ def local?
546
+ true
547
+ end
548
+ end
511
549
  end
512
550
  end