cocoapods 0.14.0 → 0.15.0

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 (37) hide show
  1. data/CHANGELOG.md +42 -9
  2. data/bin/pod +4 -0
  3. data/lib/cocoapods.rb +2 -2
  4. data/lib/cocoapods/command.rb +2 -25
  5. data/lib/cocoapods/command/install.rb +1 -2
  6. data/lib/cocoapods/command/linter.rb +24 -4
  7. data/lib/cocoapods/command/list.rb +16 -11
  8. data/lib/cocoapods/command/outdated.rb +2 -4
  9. data/lib/cocoapods/command/push.rb +10 -10
  10. data/lib/cocoapods/command/repo.rb +22 -20
  11. data/lib/cocoapods/command/search.rb +6 -4
  12. data/lib/cocoapods/command/setup.rb +15 -14
  13. data/lib/cocoapods/command/spec.rb +17 -14
  14. data/lib/cocoapods/command/update.rb +9 -1
  15. data/lib/cocoapods/config.rb +12 -4
  16. data/lib/cocoapods/dependency.rb +18 -15
  17. data/lib/cocoapods/downloader/git.rb +41 -28
  18. data/lib/cocoapods/downloader/http.rb +10 -3
  19. data/lib/cocoapods/downloader/mercurial.rb +6 -4
  20. data/lib/cocoapods/downloader/subversion.rb +6 -2
  21. data/lib/cocoapods/executable.rb +6 -6
  22. data/lib/cocoapods/generator/acknowledgements/markdown.rb +1 -0
  23. data/lib/cocoapods/installer.rb +73 -57
  24. data/lib/cocoapods/installer/target_installer.rb +15 -11
  25. data/lib/cocoapods/installer/user_project_integrator.rb +25 -16
  26. data/lib/cocoapods/local_pod.rb +34 -12
  27. data/lib/cocoapods/podfile.rb +15 -0
  28. data/lib/cocoapods/resolver.rb +33 -39
  29. data/lib/cocoapods/source.rb +172 -48
  30. data/lib/cocoapods/specification.rb +48 -32
  31. data/lib/cocoapods/specification/set.rb +102 -34
  32. data/lib/cocoapods/specification/statistics.rb +1 -1
  33. data/lib/cocoapods/user_interface.rb +215 -0
  34. data/lib/cocoapods/user_interface/ui_pod.rb +130 -0
  35. metadata +7 -7
  36. data/lib/cocoapods/command/presenter.rb +0 -61
  37. data/lib/cocoapods/command/presenter/cocoa_pod.rb +0 -118
@@ -472,6 +472,17 @@ module Pod
472
472
  @target_definition = parent
473
473
  end
474
474
 
475
+ # This hook allows you to make any changes to the downloaded Pods and to
476
+ # their targets before they are installed.
477
+ #
478
+ # pre_install do |installer|
479
+ # # Do something fancy!
480
+ # end
481
+ #
482
+ def pre_install(&block)
483
+ @pre_install_callback = block
484
+ end
485
+
475
486
  # This hook allows you to make any last changes to the generated Xcode project
476
487
  # before it is written to disk, or any other tasks you might want to perform.
477
488
  #
@@ -532,6 +543,10 @@ module Pod
532
543
  configs_array.inject({}) { |hash, config| hash.merge(config) }
533
544
  end
534
545
 
546
+ def pre_install!(installer)
547
+ @pre_install_callback.call(installer) if @pre_install_callback
548
+ end
549
+
535
550
  def post_install!(installer)
536
551
  @post_install_callback.call(installer) if @post_install_callback
537
552
  end
@@ -66,36 +66,32 @@ module Pod
66
66
  @specs_by_target = {}
67
67
  @pods_from_external_sources = []
68
68
  @pods_to_lock = []
69
- @log_indent = 0
70
69
 
71
70
  if @lockfile
72
- puts "\nFinding added, modified or removed dependencies:".green if config.verbose?
73
71
  @pods_by_state = @lockfile.detect_changes_with_podfile(podfile)
74
- if config.verbose?
72
+ UI.section "Finding added, modified or removed dependencies:" do
73
+ marks = {:added => "A".green, :changed => "M".yellow, :removed => "R".red, :unchanged => "-" }
75
74
  @pods_by_state.each do |symbol, pod_names|
76
- case symbol
77
- when :added
78
- mark = "A".green
79
- when :changed
80
- mark = "M".yellow
81
- when :removed
82
- mark = "R".red
83
- when :unchanged
84
- mark = "-"
85
- end
86
75
  pod_names.each do |pod_name|
87
- puts " #{mark} " << pod_name
76
+ UI.message("#{marks[symbol]} #{pod_name}", '',2)
88
77
  end
89
78
  end
90
- end
79
+ end if config.verbose?
91
80
  @pods_to_lock = (lockfile.pods_names - @pods_by_state[:added] - @pods_by_state[:changed] - @pods_by_state[:removed]).uniq
92
81
  end
93
82
 
83
+ unless config.skip_repo_update?
84
+ UI.section 'Updating spec repositories' do
85
+ Command::Repo.new(Command::ARGV.new(["update"])).run
86
+ end if !@lockfile || !(@pods_by_state[:added] + @pods_by_state[:changed]).empty? || update_mode
87
+ end
88
+
94
89
  @podfile.target_definitions.values.each do |target_definition|
95
- puts "\nResolving dependencies for target `#{target_definition.name}' (#{target_definition.platform}):".green if config.verbose?
96
- @loaded_specs = []
97
- find_dependency_specs(@podfile, target_definition.dependencies, target_definition)
98
- @specs_by_target[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name)
90
+ UI.section "Resolving dependencies for target `#{target_definition.name}' (#{target_definition.platform})" do
91
+ @loaded_specs = []
92
+ find_dependency_specs(@podfile, target_definition.dependencies, target_definition)
93
+ @specs_by_target[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name)
94
+ end
99
95
  end
100
96
 
101
97
  @cached_specs.values.sort_by(&:name)
@@ -187,35 +183,33 @@ module Pod
187
183
  # @return [void]
188
184
  #
189
185
  def find_dependency_specs(dependent_specification, dependencies, target_definition)
190
- @log_indent += 1
191
186
  dependencies.each do |dependency|
192
187
  # Replace the dependency with a more specific one if the pod is already installed.
193
188
  if !update_mode && @pods_to_lock.include?(dependency.name)
194
189
  dependency = lockfile.dependency_for_installed_pod_named(dependency.name)
195
190
  end
196
-
197
- puts ' ' * @log_indent + "- #{dependency}" if config.verbose?
198
- set = find_cached_set(dependency, target_definition.platform)
199
- set.required_by(dependency, dependent_specification.to_s)
200
-
201
- # Ensure we don't resolve the same spec twice for one target
202
- unless @loaded_specs.include?(dependency.name)
203
- spec = set.specification_by_name(dependency.name)
204
- @pods_from_external_sources << spec.pod_name if dependency.external?
205
- @loaded_specs << spec.name
206
- @cached_specs[spec.name] = spec
207
- # Configure the specification
208
- spec.activate_platform(target_definition.platform)
209
- spec.version.head = dependency.head?
210
- # And recursively load the dependencies of the spec.
211
- find_dependency_specs(spec, spec.dependencies, target_definition) if spec.dependencies
191
+ UI.message("- #{dependency}", '', 2) do
192
+ set = find_cached_set(dependency, target_definition.platform)
193
+ set.required_by(dependency, dependent_specification.to_s)
194
+
195
+ # Ensure we don't resolve the same spec twice for one target
196
+ unless @loaded_specs.include?(dependency.name)
197
+ spec = set.specification_by_name(dependency.name)
198
+ @pods_from_external_sources << spec.pod_name if dependency.external?
199
+ @loaded_specs << spec.name
200
+ @cached_specs[spec.name] = spec
201
+ # Configure the specification
202
+ spec.activate_platform(target_definition.platform)
203
+ spec.version.head = dependency.head?
204
+ # And recursively load the dependencies of the spec.
205
+ find_dependency_specs(spec, spec.dependencies, target_definition) if spec.dependencies
206
+ end
207
+ validate_platform(spec || @cached_specs[dependency.name], target_definition)
212
208
  end
213
- validate_platform(spec || @cached_specs[dependency.name], target_definition)
214
209
  end
215
- @log_indent -= 1
216
210
  end
217
211
 
218
- # Ensures that a spec is compatible with platform of a target.
212
+ # Ensures that a spec is compatible with the platform of a target.
219
213
  #
220
214
  # @raises If the spec is not supported by the target.
221
215
  #
@@ -1,67 +1,81 @@
1
1
  module Pod
2
- class Source
3
- class Aggregate
4
- def all
5
- @sources ||= begin
6
- repos_dir = Config.instance.repos_dir
7
- unless repos_dir.exist?
8
- raise Informative, "No spec repos found in `#{repos_dir}'. " \
9
- "To fetch the `master' repo run: $ pod setup"
10
- end
11
- repos_dir.children.select(&:directory?).map { |repo| Source.new(repo) }
12
- end
13
- end
14
2
 
15
- def all_sets
16
- all.map(&:pod_sets).flatten
17
- end
3
+ # The {Source} class is responsible to manage a collection of podspecs.
4
+ #
5
+ # @note The backing store of the podspecs collection is an implementation detail
6
+ # abstraced from the rest of CocoaPods.
7
+ #
8
+ # @note The default implementation uses a git repo as a backing store, where the
9
+ # podspecs are namespaces as:
10
+ #
11
+ # #{POD_NAME}/#{VERSION}/#{POD_NAME}.podspec
12
+ #
13
+ # @todo For better abstranction the sources should be responsible to update themselves.
14
+ #
15
+ class Source
18
16
 
19
- def search(dependency)
20
- all.map { |s| s.search(dependency) }.compact.first ||
21
- raise(Informative, "[!] Unable to find a pod named `#{dependency.name}'".red)
22
- end
17
+ # @return [Pathname] The location of the repo.
18
+ #
19
+ attr_reader :repo
23
20
 
24
- def search_by_name(query, full_text_search)
25
- result = all.map { |s| s.search_by_name(query, full_text_search) }.flatten
26
- if result.empty?
27
- extra = ", author, summary, or description" if full_text_search
28
- raise(Informative, "Unable to find a pod with name" \
29
- "#{extra} matching `#{query}'")
30
- end
31
- result
32
- end
21
+ # @param [Pathname] repo @see repo.
22
+ #
23
+ def initialize(repo)
24
+ @repo = repo
33
25
  end
34
26
 
35
- def self.all
36
- Aggregate.new.all
27
+ # @return [String] the name of the repo.
28
+ #
29
+ def name
30
+ @repo.basename.to_s
37
31
  end
38
32
 
39
- def self.all_sets
40
- Aggregate.new.all_sets
41
- end
33
+ # @!group Quering the source
42
34
 
43
- def self.search(dependency)
44
- Aggregate.new.search(dependency)
35
+ # @return [Array<String>] The name of all the Pods.
36
+ #
37
+ def pods
38
+ @repo.children.map do |child|
39
+ child.basename.to_s if child.directory? && child.basename.to_s != '.git'
40
+ end.compact
45
41
  end
46
42
 
47
- def self.search_by_name(name, full_text_search)
48
- Aggregate.new.search_by_name(name, full_text_search)
43
+ # @return [Array<Sets>] The sets of all the Pods.
44
+ #
45
+ def pod_sets
46
+ pods.map { |pod| Specification::Set.new(pod, self) }
49
47
  end
50
48
 
51
- attr_reader :repo
52
-
53
- def initialize(repo)
54
- @repo = repo
49
+ # @return [Array<Version>] All the available versions for the Pod, sorted
50
+ # from highest to lowest.
51
+ #
52
+ # @param [String] name The name of the Pod.
53
+ #
54
+ def versions(name)
55
+ pod_dir = repo + name
56
+ pod_dir.children.map do |v|
57
+ basename = v.basename.to_s
58
+ Version.new(basename) if v.directory? && basename[0,1] != '.'
59
+ end.compact.sort.reverse
55
60
  end
56
61
 
57
- def pod_sets
58
- @repo.children.map do |child|
59
- if child.directory? && child.basename.to_s != '.git'
60
- Specification::Set.new(child)
61
- end
62
- end.compact
62
+ # @return [Specification] The specification for a given version of Pod.
63
+ #
64
+ # @param [String] name The name of the Pod.
65
+ #
66
+ # @param [Version,String] version
67
+ # The version for the specification.
68
+ #
69
+ def specification(name, version)
70
+ specification_path = repo + name + version.to_s + "#{name}.podspec"
71
+ Specification.from_file(specification_path)
63
72
  end
64
73
 
74
+ # @!group Searching the source
75
+
76
+ # @return [Set] A set for a given dependency. The set is identified by the
77
+ # name of the dependency and takes into account subspecs.
78
+ #
65
79
  def search(dependency)
66
80
  pod_sets.find do |set|
67
81
  # First match the (top level) name, which does not yet load the spec from disk
@@ -72,7 +86,19 @@ module Pod
72
86
  end
73
87
  end
74
88
 
75
- def search_by_name(query, full_text_search)
89
+ # @return [Array<Set>] The sets that contain the search term.
90
+ #
91
+ # @param [String] query The search term.
92
+ #
93
+ # @param [Bool] full_text_search Whether the search should be limited to
94
+ # the name of the Pod or should include
95
+ # also the author, the summary, and the
96
+ # description.
97
+ #
98
+ # @note Full text search requires to load the specification for each pod,
99
+ # hence is considerably slower.
100
+ #
101
+ def search_by_name(query, full_text_search = false)
76
102
  pod_sets.map do |set|
77
103
  text = if full_text_search
78
104
  s = set.specification
@@ -83,5 +109,103 @@ module Pod
83
109
  set if text.downcase.include?(query.downcase)
84
110
  end.compact
85
111
  end
112
+
113
+ # The {Source::Aggregate} manages all the sources available to CocoaPods.
114
+ #
115
+ class Aggregate
116
+
117
+ # @return [Array<Source>] All the sources.
118
+ #
119
+ def all
120
+ @sources ||= dirs.map { |repo| Source.new(repo) }.sort_by(&:name)
121
+ end
122
+
123
+ # @return [Array<String>] The names of all the pods available.
124
+ #
125
+ def all_pods
126
+ all.map(&:pods).flatten.uniq
127
+ end
128
+
129
+ # @return [Array<Set>] The sets for all the pods available.
130
+ #
131
+ def all_sets
132
+ all_pods.map do |pod|
133
+ sources = all.select{ |s| s.pods.include?(pod) }.compact
134
+ Specification::Set.new(pod, sources)
135
+ end
136
+ end
137
+
138
+ # @return [Set] A set for a given dependency including all the Sources
139
+ # that countain the Pod.
140
+ #
141
+ # @raises If no source including the set can be foud.
142
+ #
143
+ # @see Source#search
144
+ #
145
+ def search(dependency)
146
+ sources = all.select { |s| !s.search(dependency).nil? }
147
+ raise(Informative, "[!] Unable to find a pod named `#{dependency.name}'".red) if sources.empty?
148
+ Specification::Set.new(dependency.top_level_spec_name, sources)
149
+ end
150
+
151
+ # @return [Array<Set>] The sets that contain the search term.
152
+ #
153
+ # @raises If no source including the set can be foud.
154
+ #
155
+ # @see Source#search_by_name
156
+ #
157
+ def search_by_name(query, full_text_search = false)
158
+ pods_by_source = {}
159
+ result = []
160
+ all.each { |s| pods_by_source[s] = s.search_by_name(query, full_text_search).map(&:name) }
161
+ pod_names = pods_by_source.values.flatten.uniq
162
+ pod_names.each do |pod|
163
+ sources = []
164
+ pods_by_source.each{ |source, pods| sources << source if pods.include?(pod) }
165
+ result << Specification::Set.new(pod, sources)
166
+ end
167
+ if result.empty?
168
+ extra = ", author, summary, or description" if full_text_search
169
+ raise(Informative, "Unable to find a pod with name" \
170
+ "#{extra} matching `#{query}'")
171
+ end
172
+ result
173
+ end
174
+
175
+ # @return [Array<Pathname>] The directories where the sources are stored.
176
+ #
177
+ # @raises If the repos dir doesn't exits.
178
+ #
179
+ def dirs
180
+ if ENV['CP_MASTER_REPO_DIR']
181
+ [Pathname.new(ENV['CP_MASTER_REPO_DIR'])]
182
+ else
183
+ repos_dir = Config.instance.repos_dir
184
+ unless repos_dir.exist?
185
+ raise Informative, "No spec repos found in `#{repos_dir}'. " \
186
+ "To fetch the `master' repo run: $ pod setup"
187
+ end
188
+ repos_dir.children.select(&:directory?)
189
+ end
190
+ end
191
+ end
192
+
193
+ # @!group Shortcuts
194
+
195
+ def self.all
196
+ Aggregate.new.all
197
+ end
198
+
199
+ def self.all_sets
200
+ Aggregate.new.all_sets
201
+ end
202
+
203
+ def self.search(dependency)
204
+ Aggregate.new.search(dependency)
205
+ end
206
+
207
+ def self.search_by_name(name, full_text_search = false)
208
+ Aggregate.new.search_by_name(name, full_text_search)
209
+ end
86
210
  end
87
211
  end
@@ -54,6 +54,7 @@ module Pod
54
54
  @xcconfig = { :ios => Xcodeproj::Config.new, :osx => Xcodeproj::Config.new }
55
55
  @header_dir = { :ios => nil, :osx => nil }
56
56
  @requires_arc = { :ios => nil, :osx => nil }
57
+ @header_mappings_dir = { :ios => nil, :osx => nil }
57
58
 
58
59
  yield self if block_given?
59
60
  end
@@ -88,6 +89,7 @@ module Pod
88
89
  # Returns the value of the attribute for the active platform
89
90
  # chained with the upstream specifications. The ivar must store
90
91
  # the platform specific values as an array.
92
+ #
91
93
  def self.pltf_chained_attr_reader(attr)
92
94
  define_method(attr) do
93
95
  active_plaform_check
@@ -96,6 +98,17 @@ module Pod
96
98
  end
97
99
  end
98
100
 
101
+ # Returns the first value defined of the attribute traversing the chain
102
+ # upwards.
103
+ #
104
+ def self.pltf_first_defined_attr_reader(attr)
105
+ define_method(attr) do
106
+ active_plaform_check
107
+ ivar_value = instance_variable_get("@#{attr}")[active_platform]
108
+ ivar_value.nil? ? (@parent.send(attr) if @parent) : ivar_value
109
+ end
110
+ end
111
+
99
112
  def active_plaform_check
100
113
  raise Informative, "#{self.inspect} not activated for a platform before consumption." unless active_platform
101
114
  end
@@ -240,13 +253,11 @@ module Pod
240
253
 
241
254
  ### Attributes **with** multiple platform support
242
255
 
243
- # @todo allow for subspecs
256
+ # @todo allow for subspecs?
244
257
  #
245
- top_attr_accessor :header_mappings_dir, lambda { |file| Pathname.new(file) } # If not provided the headers files are flattened
246
258
  top_attr_accessor :prefix_header_file, lambda { |file| Pathname.new(file) }
247
259
  top_attr_accessor :prefix_header_contents
248
260
 
249
-
250
261
  pltf_chained_attr_accessor :source_files, lambda {|value, current| pattern_list(value) }
251
262
  pltf_chained_attr_accessor :public_header_files, lambda {|value, current| pattern_list(value) }
252
263
  pltf_chained_attr_accessor :resources, lambda {|value, current| pattern_list(value) }
@@ -262,7 +273,6 @@ module Pod
262
273
  alias_method :weak_framework=, :weak_frameworks=
263
274
  alias_method :library=, :libraries=
264
275
 
265
-
266
276
  # @!method requires_arc=
267
277
  #
268
278
  # @abstract Wether the `-fobjc-arc' flag should be added to the compiler
@@ -271,14 +281,7 @@ module Pod
271
281
  # @param [Bool] Wether the source files require ARC.
272
282
  #
273
283
  platform_attr_writer :requires_arc
274
-
275
- def requires_arc
276
- requires_arc = @requires_arc[active_platform]
277
- if requires_arc.nil?
278
- requires_arc = @parent ? @parent.requires_arc : false
279
- end
280
- requires_arc
281
- end
284
+ pltf_first_defined_attr_reader :requires_arc
282
285
 
283
286
  # @!method header_dir=
284
287
  #
@@ -287,17 +290,13 @@ module Pod
287
290
  #
288
291
  # @param [String] The headers directory.
289
292
  #
290
- platform_attr_writer :header_dir, lambda { |dir, _| Pathname.new(dir) }
293
+ platform_attr_writer :header_dir, lambda { |dir, _| Pathname.new(dir) }
294
+ pltf_first_defined_attr_reader :header_dir
291
295
 
292
- # @abstract (see #header_dir=)
293
- #
294
- # @return [Pathname] The headers directory.
296
+ # If not provided the headers files are flattened
295
297
  #
296
- # @note If no value is provided it returns an empty {Pathname}.
297
- #
298
- def header_dir
299
- @header_dir[active_platform] || (@parent.header_dir if @parent) || Pathname.new('')
300
- end
298
+ platform_attr_writer :header_mappings_dir, lambda { |file, _| Pathname.new(file) }
299
+ pltf_first_defined_attr_reader :header_mappings_dir
301
300
 
302
301
  # @!method xcconfig=
303
302
  #
@@ -316,13 +315,14 @@ module Pod
316
315
  end
317
316
 
318
317
 
318
+ def recursive_compiler_flags
319
+ @parent ? @parent.recursive_compiler_flags | @compiler_flags[active_platform] : @compiler_flags[active_platform]
320
+ end
321
+
319
322
  def compiler_flags
320
- if @parent
321
- flags = [@parent.compiler_flags]
322
- else
323
- flags = [requires_arc ? ' -fobjc-arc' : '']
324
- end
325
- (flags + @compiler_flags[active_platform].clone).join(' ')
323
+ flags = recursive_compiler_flags.dup
324
+ flags << '-fobjc-arc' if requires_arc
325
+ flags.join(' ')
326
326
  end
327
327
 
328
328
  platform_attr_writer :compiler_flags, lambda {|value, current| current << value }
@@ -430,20 +430,36 @@ module Pod
430
430
  header_mappings_dir ? from.relative_path_from(header_mappings_dir) : from.basename
431
431
  end
432
432
 
433
+ # This is a convenience method which gets called after all pods have been
434
+ # downloaded but before they have been installed, and the Xcode project and
435
+ # related files have been generated. (It receives the Pod::LocalPod
436
+ # instance generated form the specification and the #
437
+ # Pod::Podfile::TargetDefinition instance for the current target.) Override
438
+ # this to, for instance, to run any build script:
439
+ #
440
+ # Pod::Spec.new do |s|
441
+ # def pre_install(pod, target_definition)
442
+ # Dir.chdir(pod.root){ `sh make.sh` }
443
+ # end
444
+ # end
445
+ def pre_install(pod, target_definition)
446
+ end
447
+
433
448
  # This is a convenience method which gets called after all pods have been
434
449
  # downloaded, installed, and the Xcode project and related files have been
435
- # generated. (It receives the Pod::Installer::Target instance for the current
436
- # target.) Override this to, for instance, add to the prefix header:
450
+ # generated. (It receives the Pod::Installer::TargetInstaller instance for
451
+ # the current target.) Override this to, for instance, add to the prefix
452
+ # header:
437
453
  #
438
454
  # Pod::Spec.new do |s|
439
- # def s.post_install(target)
440
- # prefix_header = config.project_pods_root + target.prefix_header_filename
455
+ # def s.post_install(target_installer)
456
+ # prefix_header = config.project_pods_root + target_installer.prefix_header_filename
441
457
  # prefix_header.open('a') do |file|
442
458
  # file.puts(%{#ifdef __OBJC__\n#import "SSToolkitDefines.h"\n#endif})
443
459
  # end
444
460
  # end
445
461
  # end
446
- def post_install(target)
462
+ def post_install(target_installer)
447
463
  end
448
464
 
449
465
  def podfile?