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
@@ -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"))