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
@@ -0,0 +1,162 @@
1
+ require 'xcodeproj/workspace'
2
+ require 'xcodeproj/project'
3
+
4
+ module Pod
5
+ class Installer
6
+
7
+ class UserProjectIntegrator
8
+ include Pod::Config::Mixin
9
+
10
+ def initialize(podfile)
11
+ @podfile = podfile
12
+ end
13
+
14
+ def integrate!
15
+ create_workspace!
16
+
17
+ # Only need to write out the user's project if any of the target
18
+ # integrators actually did some work.
19
+ target_integrators.map(&:integrate!)
20
+ end
21
+
22
+ def workspace_path
23
+ @podfile.workspace || raise(Informative, "Could not automatically select an Xcode workspace. " \
24
+ "Specify one in your Podfile.")
25
+ end
26
+
27
+ def pods_project_path
28
+ config.project_root + "Pods/Pods.xcodeproj"
29
+ end
30
+
31
+ def target_integrators
32
+ @target_integrators ||= @podfile.target_definitions.values.map do |definition|
33
+ TargetIntegrator.new(definition) unless definition.empty?
34
+ end.compact
35
+ end
36
+
37
+ def user_project_paths
38
+ @podfile.target_definitions.values.map do |td|
39
+ next if td.empty?
40
+ td.user_project.path #|| raise(Informative, "Could not resolve the Xcode project in which the " \
41
+ # "`#{td.name}' target should be integrated.")
42
+ end.compact
43
+ end
44
+
45
+ def create_workspace!
46
+ workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
47
+ [pods_project_path, *user_project_paths].each do |project_path|
48
+ project_path = project_path.relative_path_from(config.project_root).to_s
49
+ workspace << project_path unless workspace.include?(project_path)
50
+ end
51
+ unless workspace_path.exist? || config.silent?
52
+ puts "[!] From now on use `#{workspace_path.basename}'."
53
+ end
54
+ workspace.save_as(workspace_path)
55
+ end
56
+
57
+ class TargetIntegrator
58
+ attr_reader :target_definition
59
+
60
+ def initialize(target_definition)
61
+ @target_definition = target_definition
62
+ end
63
+
64
+ def inspect
65
+ "#<#{self.class} for target `#{@target_definition.label}'>"
66
+ end
67
+
68
+ def integrate!
69
+ return if targets.empty?
70
+
71
+ unless Config.instance.silent?
72
+ # TODO let's just use ActiveSupport.
73
+ plural = targets.size > 1
74
+ puts "-> Integrating `#{@target_definition.lib_name}' into target#{'s' if plural} " \
75
+ "`#{targets.map(&:name).join(', ')}' of Xcode project `#{user_project_path.basename}'.".green
76
+ end
77
+
78
+ add_xcconfig_base_configuration
79
+ add_pods_library
80
+ add_copy_resources_script_phase
81
+ user_project.save_as(@target_definition.user_project.path)
82
+ end
83
+
84
+ def user_project_path
85
+ if path = @target_definition.user_project.path
86
+ unless path.exist?
87
+ raise Informative, "The Xcode project `#{path}' does not exist."
88
+ end
89
+ path
90
+ else
91
+ raise Informative, "Could not automatically select an Xcode project.\n" \
92
+ "Specify one in your Podfile like so:\n\n" \
93
+ " xcodeproj 'path/to/XcodeProject'"
94
+ end
95
+ end
96
+
97
+ def user_project
98
+ @user_project ||= Xcodeproj::Project.new(user_project_path)
99
+ end
100
+
101
+ # This returns a list of the targets from the user’s project to which
102
+ # this Pods static library should be linked. If no explicit target was
103
+ # specified, then the first encountered target is assumed.
104
+ #
105
+ # In addition this will only return targets that do **not** already
106
+ # have the Pods library in their frameworks build phase.
107
+ #
108
+ # @return [Array<PBXNativeTarget>] Returns the list of targets that
109
+ # the Pods lib should be linked with.
110
+ def targets
111
+ @targets ||= begin
112
+ if link_with = @target_definition.link_with
113
+ # Find explicitly linked targets.
114
+ user_project.targets.select do |target|
115
+ link_with.include? target.name
116
+ end
117
+ elsif @target_definition.name != :default
118
+ # Find the target with the matching name.
119
+ target = user_project.targets.find { |target| target.name == @target_definition.name.to_s }
120
+ raise Informative, "Unable to find a target named `#{@target_definition.name.to_s}'" unless target
121
+ [target]
122
+ else
123
+ # Default to the first, which in a simple project is probably an app target.
124
+ [user_project.targets.first]
125
+ end.reject do |target|
126
+ # Reject any target that already has this Pods library in one of its frameworks build phases
127
+ target.frameworks_build_phases.any? do |phase|
128
+ phase.files.any? { |file| file.name == @target_definition.lib_name }
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ def add_xcconfig_base_configuration
135
+ xcconfig = user_project.files.new('path' => @target_definition.xcconfig_relative_path)
136
+ targets.each do |target|
137
+ target.build_configurations.each do |config|
138
+ config.base_configuration = xcconfig
139
+ end
140
+ end
141
+ end
142
+
143
+ def add_pods_library
144
+ pods_library = user_project.group("Frameworks").files.new_static_library(@target_definition.label)
145
+ targets.each do |target|
146
+ target.frameworks_build_phases.each { |build_phase| build_phase << pods_library }
147
+ end
148
+ end
149
+
150
+ def add_copy_resources_script_phase
151
+ targets.each do |target|
152
+ phase = target.shell_script_build_phases.new
153
+ phase.name = 'Copy Pods Resources'
154
+ phase.shell_script = %{"#{@target_definition.copy_resources_script_relative_path}"\n}
155
+ end
156
+ end
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,240 @@
1
+ module Pod
2
+ class LocalPod
3
+ attr_reader :top_specification, :specifications
4
+ attr_reader :sandbox
5
+
6
+ def initialize(specification, sandbox, platform)
7
+ @top_specification, @sandbox = specification.top_level_parent, sandbox
8
+ @top_specification.activate_platform(platform)
9
+ @specifications = [] << specification
10
+ end
11
+
12
+ def self.from_podspec(podspec, sandbox, platform)
13
+ new(Specification.from_file(podspec), sandbox, platform)
14
+ end
15
+
16
+ # Method to add the specifications sharing the same top level
17
+ # parent. With this information the local pod can determine the
18
+ # paths to clean and avoid duplication in file processing.
19
+ # Adding specifications is idempotent.
20
+ def add_specification(spec)
21
+ raise Informative, "[Local Pod] Attempt to add a specification from another pod" unless spec.top_level_parent == top_specification
22
+ spec.activate_platform(platform)
23
+ @specifications << spec unless @specifications.include?(spec)
24
+ end
25
+
26
+ def root
27
+ @sandbox.root + top_specification.name
28
+ end
29
+
30
+ def subspecs
31
+ specifications.reject{|s| s.parent.nil? }
32
+ end
33
+
34
+ def to_s
35
+ result = top_specification.to_s
36
+ result << " [LOCAL]" if top_specification.local?
37
+ result
38
+ end
39
+
40
+ def name
41
+ top_specification.name
42
+ end
43
+
44
+ def platform
45
+ top_specification.active_platform
46
+ end
47
+
48
+ # Installation methods
49
+
50
+ def create
51
+ root.mkpath unless exists?
52
+ end
53
+
54
+ def exists?
55
+ root.exist?
56
+ end
57
+
58
+ def chdir(&block)
59
+ create
60
+ Dir.chdir(root, &block)
61
+ end
62
+
63
+ def implode
64
+ root.rmtree if exists?
65
+ end
66
+
67
+ # It deletes all the files identified by clean_files, then it removes
68
+ # all the empty folders or symlinks.
69
+ def clean
70
+ clean_files.each { |path| FileUtils.rm_rf(path) }
71
+
72
+ # Get all the directories. Then sort them from the longest
73
+ # to the shortest, so a directory will be empty if its
74
+ # subdirs where empty. We need to delete the symlinks because
75
+ # it might prevent a bundle from being deleted
76
+ dirs = Dir.glob(root + "**/*", File::FNM_DOTMATCH)
77
+ dirs = dirs.reject { |d| d.end_with?('.', '..') || !File.directory?(d) }.sort_by(&:length).reverse
78
+ dirs.each { |d| FileUtils.rm_rf(d) if File.symlink?(d) || (Dir.entries(d) == %w[ . .. ]) }
79
+ end
80
+
81
+ # File attributes
82
+
83
+ def prefix_header_file
84
+ root + top_specification.prefix_header_file if top_specification.prefix_header_file
85
+ end
86
+
87
+ def source_files(relative = true)
88
+ chained_expanded_paths(:source_files, :glob => '*.{h,m,mm,c,cpp}', :relative_to_sandbox => relative)
89
+ end
90
+
91
+ def header_files
92
+ source_files.select { |f| f.extname == '.h' }
93
+ end
94
+
95
+ def resources(relative = true)
96
+ chained_expanded_paths(:resources, :relative_to_sandbox => relative)
97
+ end
98
+
99
+ def clean_files
100
+ all_files = Dir.glob(root + "**/*", File::FNM_DOTMATCH).map { |f| root + f }.reject { |p| p.directory? }
101
+ all_files - used_files
102
+ end
103
+
104
+ def used_files
105
+ source_files(false) + resources(false) + preserve_paths + [ readme_file, license_file, prefix_header_file ]
106
+ end
107
+
108
+ # TODO: implement case insensitive search
109
+ def preserve_paths
110
+ chained_expanded_paths(:preserve_paths) + expanded_paths(%w[ *.podspec notice* NOTICE* CREDITS* ])
111
+ end
112
+
113
+ def readme_file
114
+ expanded_paths(%w[ README{*,.*} readme{*,.*} ]).first
115
+ end
116
+
117
+ def license_file
118
+ if top_specification.license && top_specification.license[:file]
119
+ root + top_specification.license[:file]
120
+ else
121
+ expanded_paths(%w[ LICENSE{*,.*} licence{*,.*} ]).first
122
+ end
123
+ end
124
+
125
+ def license_text
126
+ if (license_hash = top_specification.license)
127
+ if (result = license_hash[:text])
128
+ result
129
+ elsif license_file
130
+ result = IO.read(license_file)
131
+ end
132
+ end
133
+ end
134
+
135
+ def xcconfig
136
+ specifications.map { |s| s.xcconfig }.reduce(:merge)
137
+ end
138
+
139
+ # Method used by documentation generator. It return the source files
140
+ # of all the specs.
141
+ def all_specs_public_header_files
142
+ #TODO: merge with #221
143
+ specs = top_specification.recursive_subspecs << top_specification
144
+ specs.map { |s| expanded_paths(s.source_files, :glob => '*.{h}') }.compact.flatten.select { |f| f.extname == '.h' }.uniq
145
+ end
146
+
147
+ # Integration methods
148
+
149
+ def link_headers
150
+ copy_header_mappings.each do |namespaced_path, files|
151
+ @sandbox.add_header_files(namespaced_path, files)
152
+ end
153
+ end
154
+
155
+ def add_to_target(target)
156
+ sources_files_by_specification.each do | spec, files |
157
+ files.each do |file|
158
+ # TODO: Xcodeproj::Project::Object::PBXNativeTarget#add_source_file is quite slow
159
+ # The issus appears to be related to the find call in line 107.
160
+ target.add_source_file(file, nil, spec.compiler_flags.strip)
161
+ end
162
+ end
163
+ end
164
+
165
+ def requires_arc?
166
+ top_specification.requires_arc
167
+ end
168
+
169
+ private
170
+
171
+ def implementation_files
172
+ source_files.select { |f| f.extname != '.h' }
173
+ end
174
+
175
+ def relative_root
176
+ root.relative_path_from(@sandbox.root)
177
+ end
178
+
179
+ # TODO this is being overriden in the RestKit 0.9.4 spec, need to do
180
+ # something with that, and this method also still exists in Specification.
181
+ #
182
+ # This is not overriden anymore in specification refactor and the code
183
+ # Pod::Specification#copy_header_mapping can be moved here.
184
+ def copy_header_mappings
185
+ search_path_headers = header_files - headers_excluded_from_search_paths
186
+ search_path_headers.inject({}) do |mappings, from|
187
+ from_without_prefix = from.relative_path_from(relative_root)
188
+ to = top_specification.header_dir + top_specification.copy_header_mapping(from_without_prefix)
189
+ (mappings[to.dirname] ||= []) << from
190
+ mappings
191
+ end
192
+ end
193
+
194
+ # returns an hash where the source_files are groupped by specification.
195
+ # If the same file is required by two specifications the one at the
196
+ # higher level in the inheritance chain wins.
197
+ def sources_files_by_specification
198
+ files_by_spec = {}
199
+ processed_files = []
200
+ specifications.sort_by { |s| s.name.length }.each do |spec|
201
+ files = []
202
+ expanded_paths(spec.source_files, :glob => '*.{h,m,mm,c,cpp}', :relative_to_sandbox => true).each do | file |
203
+ files << file unless processed_files.include?(file)
204
+ end
205
+ files_by_spec[spec] = files
206
+ processed_files += files
207
+ end
208
+ files_by_spec
209
+ end
210
+
211
+ def headers_excluded_from_search_paths
212
+ chained_expanded_paths(:exclude_header_search_paths, :glob => '*.h', :relative_to_sandbox => true)
213
+ end
214
+
215
+ def chained_expanded_paths(accessor, options = {})
216
+ specifications.map { |s| expanded_paths(s.send(accessor), options) }.compact.flatten.uniq
217
+ end
218
+
219
+ def expanded_paths(patterns, options = {})
220
+ raise Informative, "[Local Pod] Attempt to resolve paths for non existent pod." unless exists?
221
+
222
+ patterns = [ patterns ] if patterns.is_a? String
223
+ patterns.map do |pattern|
224
+ pattern = root + pattern
225
+
226
+ if pattern.directory? && options[:glob]
227
+ pattern += options[:glob]
228
+ end
229
+
230
+ pattern.glob.map do |file|
231
+ if options[:relative_to_sandbox]
232
+ file.relative_path_from(@sandbox.root)
233
+ else
234
+ file
235
+ end
236
+ end
237
+ end.flatten
238
+ end
239
+ end
240
+ end
@@ -1,42 +1,65 @@
1
1
  module Pod
2
2
  class Platform
3
- attr_reader :options
4
-
5
- def initialize(symbolic_name, options = {})
3
+ def self.ios
4
+ new :ios
5
+ end
6
+
7
+ def self.osx
8
+ new :osx
9
+ end
10
+
11
+ attr_reader :deployment_target
12
+
13
+ def initialize(symbolic_name = nil, deployment_target = nil)
6
14
  @symbolic_name = symbolic_name
7
- @options = options
15
+ if deployment_target
16
+ version = deployment_target.is_a?(Hash) ? deployment_target[:deployment_target] : deployment_target # backwards compatibility from 0.6
17
+ @deployment_target = Pod::Version.create(version)
18
+ end
8
19
  end
9
-
20
+
10
21
  def name
11
22
  @symbolic_name
12
23
  end
13
-
24
+
25
+ def deployment_target= (version)
26
+ @deployment_target = Pod::Version.create(version)
27
+ end
28
+
14
29
  def ==(other_platform_or_symbolic_name)
15
30
  if other_platform_or_symbolic_name.is_a?(Symbol)
16
31
  @symbolic_name == other_platform_or_symbolic_name
17
32
  else
18
- self == (other_platform_or_symbolic_name.name)
33
+ self.name == (other_platform_or_symbolic_name.name) && self.deployment_target == other_platform_or_symbolic_name.deployment_target
19
34
  end
20
35
  end
21
-
36
+
37
+ def supports?(other)
38
+ return true if @symbolic_name.nil? || other.nil?
39
+ os_check = @symbolic_name == other.name
40
+ version_check = (deployment_target.nil? || other.deployment_target.nil? || deployment_target >= other.deployment_target)
41
+ os_check && version_check
42
+ end
43
+
22
44
  def to_s
23
- name.to_s
45
+ case @symbolic_name
46
+ when :ios
47
+ 'iOS' + (deployment_target ? " #{deployment_target}" : '')
48
+ when :osx
49
+ 'OS X' + (deployment_target ? " #{deployment_target}" : '')
50
+ else
51
+ 'iOS - OS X'
52
+ end
24
53
  end
25
-
54
+
26
55
  def to_sym
27
56
  name
28
57
  end
29
-
58
+
30
59
  def nil?
31
60
  name.nil?
32
61
  end
33
-
34
- def deployment_target
35
- if (opt = options[:deployment_target])
36
- Pod::Version.new(opt)
37
- end
38
- end
39
-
62
+
40
63
  def requires_legacy_ios_archs?
41
64
  return unless deployment_target
42
65
  (name == :ios) && (deployment_target < Pod::Version.new("4.3"))