cocoapods 0.14.0 → 0.15.0

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