cocoapods 0.0.8 → 0.1.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.
@@ -32,6 +32,7 @@ module Pod
32
32
  "but already activated version `#{required_version}' " \
33
33
  "by #{@required_by.join(', ')}."
34
34
  end
35
+ @specification = nil
35
36
  @required_by << specification
36
37
  end
37
38
 
@@ -54,7 +55,7 @@ module Pod
54
55
  end
55
56
 
56
57
  def specification
57
- Specification.from_podspec(specification_path)
58
+ @specification ||= Specification.from_file(specification_path)
58
59
  end
59
60
 
60
61
  # Return the first version that matches the current dependency.
@@ -0,0 +1,21 @@
1
+ module Pod
2
+ module Xcode
3
+ class CopyResourcesScript
4
+ attr_reader :resources
5
+
6
+ # A list of files relative to the project pods root.
7
+ def initialize(resources)
8
+ @resources = resources
9
+ end
10
+
11
+ def create_in(root)
12
+ return if @resources.empty?
13
+ (root + 'PodsResources.sh').open('a') do |script|
14
+ @resources.each do |resource|
15
+ script.puts "install_resource '#{resource}'"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,8 +1,220 @@
1
1
  framework 'Foundation'
2
+ require 'fileutils'
2
3
 
3
4
  module Pod
4
5
  module Xcode
5
6
  class Project
7
+ class PBXObject
8
+ def self.attributes_accessor(*names)
9
+ names.each do |name|
10
+ name = name.to_s
11
+ define_method(name) { @attributes[name] }
12
+ define_method("#{name}=") { |value| @attributes[name] = value }
13
+ end
14
+ end
15
+
16
+ def self.isa
17
+ @isa ||= name.split('::').last
18
+ end
19
+
20
+ attr_reader :uuid, :attributes
21
+ attributes_accessor :isa, :name
22
+
23
+ def initialize(project, uuid, attributes)
24
+ @project, @uuid, @attributes = project, uuid || generate_uuid, attributes
25
+ self.isa ||= self.class.isa
26
+ end
27
+
28
+ def inspect
29
+ "#<#{isa} UUID: `#{uuid}', name: `#{name}'>"
30
+ end
31
+
32
+ private
33
+
34
+ def generate_uuid
35
+ _uuid = CFUUIDCreate(nil)
36
+ uuid = CFUUIDCreateString(nil, _uuid)
37
+ CFRelease(_uuid)
38
+ CFMakeCollectable(uuid)
39
+ # Xcode's version is actually shorter, not worrying about collisions too much right now.
40
+ uuid.gsub('-', '')[0..23]
41
+ end
42
+
43
+ def list_by_class(uuids, klass, scoped = nil)
44
+ unless scoped
45
+ scoped = uuids.map { |uuid| @project.objects[uuid] }.select { |o| o.is_a?(klass) }
46
+ end
47
+ PBXObjectList.new(klass, @project, scoped) do |object|
48
+ # Add the uuid of a newly created object to the uuids list
49
+ uuids << object.uuid
50
+ end
51
+ end
52
+ end
53
+
54
+ class PBXGroup < PBXObject
55
+ attributes_accessor :sourceTree, :children
56
+
57
+ def initialize(project, uuid, attributes)
58
+ super
59
+ self.sourceTree ||= '<group>'
60
+ self.children ||= []
61
+ end
62
+
63
+ def files
64
+ list_by_class(children, PBXFileReference)
65
+ end
66
+
67
+ def source_files
68
+ list_by_class(children, PBXFileReference, files.select { |file| !file.build_file.nil? })
69
+ end
70
+
71
+ def groups
72
+ list_by_class(children, PBXGroup)
73
+ end
74
+
75
+ def add_source_file(path, copy_header_phase = nil, compiler_flags = nil)
76
+ file = files.new('path' => path.to_s)
77
+ build_file = file.build_file
78
+ if path.extname == '.h'
79
+ build_file.settings = { 'ATTRIBUTES' => ["Public"] }
80
+ # Working around a bug in Xcode 4.2 betas, remove this once the Xcode bug is fixed:
81
+ # https://github.com/alloy/cocoapods/issues/13
82
+ #phase = copy_header_phase || @project.headers_build_phases.first
83
+ phase = copy_header_phase || @project.copy_files_build_phases.first # TODO is this really needed?
84
+ phase.files << build_file
85
+ else
86
+ build_file.settings = { 'COMPILER_FLAGS' => compiler_flags } if compiler_flags
87
+ @project.source_build_phase.files << build_file
88
+ end
89
+ file
90
+ end
91
+ end
92
+
93
+ class PBXFileReference < PBXObject
94
+ attributes_accessor :path, :sourceTree
95
+
96
+ def initialize(project, uuid, attributes)
97
+ is_new = uuid.nil?
98
+ super
99
+ self.name ||= pathname.basename.to_s
100
+ self.sourceTree ||= 'SOURCE_ROOT'
101
+ if is_new
102
+ @project.build_files.new.file = self
103
+ end
104
+ end
105
+
106
+ def pathname
107
+ Pathname.new(path)
108
+ end
109
+
110
+ def build_file
111
+ @project.build_files.find { |o| o.fileRef == uuid }
112
+ end
113
+ end
114
+
115
+ class PBXBuildFile < PBXObject
116
+ attributes_accessor :fileRef, :settings
117
+
118
+ # Takes a PBXFileReference instance and assigns its uuid to the fileRef attribute.
119
+ def file=(file)
120
+ self.fileRef = file.uuid
121
+ end
122
+
123
+ # Returns a PBXFileReference instance corresponding to the uuid in the fileRef attribute.
124
+ def file
125
+ project.objects[fileRef]
126
+ end
127
+ end
128
+
129
+ class PBXBuildPhase < PBXObject
130
+ attributes_accessor :files
131
+ alias_method :file_uuids, :files
132
+ alias_method :file_uuids=, :files=
133
+
134
+ def initialize(project, uuid, attributes)
135
+ super
136
+ self.file_uuids ||= []
137
+ end
138
+
139
+ def files
140
+ list_by_class(file_uuids, PBXBuildFile)
141
+ end
142
+ end
143
+ class PBXSourcesBuildPhase < PBXBuildPhase; end
144
+ class PBXCopyFilesBuildPhase < PBXBuildPhase; end
145
+
146
+ class PBXNativeTarget < PBXObject
147
+ attributes_accessor :buildPhases
148
+ alias_method :build_phase_uuids, :buildPhases
149
+ alias_method :build_phase_uuids=, :buildPhases=
150
+
151
+ def buildPhases
152
+ list_by_class(build_phase_uuids, PBXBuildPhase)
153
+ end
154
+ end
155
+
156
+ # Missing constants that begin with either `PBX' or `XC' are assumed to be
157
+ # valid classes in a Xcode project. A new PBXObject subclass is created
158
+ # for the constant and returned.
159
+ def self.const_missing(name)
160
+ if name =~ /^(PBX|XC)/
161
+ klass = Class.new(PBXObject)
162
+ const_set(name, klass)
163
+ klass
164
+ else
165
+ super
166
+ end
167
+ end
168
+
169
+ class PBXObjectList
170
+ include Enumerable
171
+
172
+ def initialize(represented_class, project, scoped, &new_object_callback)
173
+ @represented_class = represented_class
174
+ @project = project
175
+ @scoped_hash = scoped.is_a?(Array) ? scoped.inject({}) { |h, o| h[o.uuid] = o.attributes; h } : scoped
176
+ @callback = new_object_callback
177
+ end
178
+
179
+ def [](uuid)
180
+ if hash = @scoped_hash[uuid]
181
+ Project.const_get(hash['isa']).new(@project, uuid, hash)
182
+ end
183
+ end
184
+
185
+ def add(klass, hash = {})
186
+ object = klass.new(@project, nil, hash)
187
+ @project.objects_hash[object.uuid] = object.attributes
188
+ object
189
+ end
190
+
191
+ def new(hash = {})
192
+ object = add(@represented_class, hash)
193
+ @callback.call(object) if @callback
194
+ object
195
+ end
196
+
197
+ def <<(object)
198
+ @callback.call(object) if @callback
199
+ end
200
+
201
+ def each
202
+ @scoped_hash.keys.each do |uuid|
203
+ yield self[uuid]
204
+ end
205
+ end
206
+
207
+ def inspect
208
+ "<PBXObjectList: #{map(&:inspect)}>"
209
+ end
210
+
211
+ # Only makes sense on the list that has the full objects_hash as its scoped hash.
212
+ def select_by_class(klass)
213
+ scoped = @project.objects_hash.select { |_, attr| attr['isa'] == klass.isa }
214
+ PBXObjectList.new(klass, @project, scoped)
215
+ end
216
+ end
217
+
6
218
  include Pod::Config::Mixin
7
219
 
8
220
  # TODO this is a workaround for an issue with MacRuby with compiled files
@@ -12,9 +224,15 @@ module Pod
12
224
  file = $LOADED_FEATURES.find { |file| file =~ %r{cocoapods/xcode/project\.rbo?$} }
13
225
  TEMPLATES_DIR = Pathname.new(File.expand_path('../../../../xcode-project-templates', file))
14
226
 
15
- # TODO see if we really need different templates for iOS and OS X
16
- def self.ios_static_library
17
- new TEMPLATES_DIR + 'cocoa-touch-static-library'
227
+ def self.static_library(platform)
228
+ case platform
229
+ when :osx
230
+ new TEMPLATES_DIR + 'cocoa-static-library'
231
+ when :ios
232
+ new TEMPLATES_DIR + 'cocoa-touch-static-library'
233
+ else
234
+ raise "No Xcode project template exists for the platform `#{platform.inspect}'"
235
+ end
18
236
  end
19
237
 
20
238
  def initialize(template_dir)
@@ -31,56 +249,58 @@ module Pod
31
249
  @template
32
250
  end
33
251
 
34
- def find_objects(conditions)
35
- objects.select do |_, object|
36
- object.objectsForKeys(conditions.keys, notFoundMarker:Object.new) == conditions.values
37
- end
252
+ def objects_hash
253
+ @template['objects']
254
+ end
255
+
256
+ def objects
257
+ @objects ||= PBXObjectList.new(PBXObject, self, objects_hash)
258
+ end
259
+
260
+ def groups
261
+ objects.select_by_class(PBXGroup)
262
+ end
263
+
264
+ # Shortcut access to the `Pods' PBXGroup.
265
+ def pods
266
+ groups.find { |g| g.name == 'Pods' }
267
+ end
268
+
269
+ # Adds a group as child to the `Pods' group.
270
+ def add_pod_group(name)
271
+ pods.groups.new('name' => name)
272
+ end
273
+
274
+ def files
275
+ objects.select_by_class(PBXFileReference)
38
276
  end
39
277
 
40
- def find_object(conditions)
41
- find_objects(conditions).first
278
+ def build_files
279
+ objects.select_by_class(PBXBuildFile)
280
+ end
281
+
282
+ def source_build_phase
283
+ objects.find { |o| o.is_a?(PBXSourcesBuildPhase) }
284
+ end
285
+
286
+ def copy_files_build_phases
287
+ objects.select_by_class(PBXCopyFilesBuildPhase)
288
+ end
289
+
290
+ def targets
291
+ objects.select_by_class(PBXNativeTarget)
42
292
  end
43
293
 
44
294
  IGNORE_GROUPS = ['Pods', 'Frameworks', 'Products', 'Supporting Files']
45
295
  def source_files
46
296
  source_files = {}
47
- find_objects('isa' => 'PBXGroup').each do |_, object|
48
- next if object['name'].nil? || IGNORE_GROUPS.include?(object['name'])
49
- source_files[object['name']] = object['children'].map do |uuid|
50
- Pathname.new(objects[uuid]['path'])
51
- end
297
+ groups.each do |group|
298
+ next if group.name.nil? || IGNORE_GROUPS.include?(group.name)
299
+ source_files[group.name] = group.source_files.map(&:pathname)
52
300
  end
53
301
  source_files
54
302
  end
55
303
 
56
- def add_source_file(file, group, phase_uuid = nil, compiler_flags = nil)
57
- file_ref_uuid = add_file_reference(file, 'SOURCE_ROOT')
58
- add_object_to_group(file_ref_uuid, group)
59
- if file.extname == '.h'
60
- build_file_uuid = add_build_file(file_ref_uuid, "settings" => { "ATTRIBUTES" => ["Public"] })
61
- # Working around a bug in Xcode 4.2 betas, remove this once the Xcode bug is fixed:
62
- # https://github.com/alloy/cocoapods/issues/13
63
- #add_file_to_list('PBXHeadersBuildPhase', build_file_uuid)
64
- add_file_to_list('PBXCopyFilesBuildPhase', build_file_uuid, phase_uuid)
65
- else
66
- extra = compiler_flags ? {"settings" => { "COMPILER_FLAGS" => compiler_flags }} : {}
67
- build_file_uuid = add_build_file(file_ref_uuid, extra)
68
- add_file_to_list('PBXSourcesBuildPhase', build_file_uuid)
69
- end
70
- file_ref_uuid
71
- end
72
-
73
- def add_group(name)
74
- group_uuid = add_object({
75
- "name" => name,
76
- "isa" => "PBXGroup",
77
- "sourceTree" => "<group>",
78
- "children" => []
79
- })
80
- add_object_to_group(group_uuid, 'Pods')
81
- group_uuid
82
- end
83
-
84
304
  def create_in(pods_root)
85
305
  puts " * Copying contents of template directory `#{@template_dir}' to `#{pods_root}'" if config.verbose?
86
306
  FileUtils.cp_r("#{@template_dir}/.", pods_root)
@@ -91,83 +311,18 @@ module Pod
91
311
 
92
312
  # TODO add comments, or even constants, describing what these magic numbers are.
93
313
  def add_copy_header_build_phase(name, path)
94
- phase_uuid = add_object({
95
- "isa" => "PBXCopyFilesBuildPhase",
314
+ phase = copy_files_build_phases.new({
96
315
  "buildActionMask" => "2147483647",
97
316
  "dstPath" => "$(PUBLIC_HEADERS_FOLDER_PATH)/#{path}",
98
317
  "dstSubfolderSpec" => "16",
99
- "files" => [],
100
318
  "name" => "Copy #{name} Public Headers",
101
319
  "runOnlyForDeploymentPostprocessing" => "0",
102
320
  })
103
-
104
- object_uuid, object = objects_by_isa('PBXNativeTarget').first
105
- object['buildPhases'] << phase_uuid
106
- phase_uuid
107
- end
108
-
109
- def objects_by_isa(isa)
110
- objects.select { |_, object| object['isa'] == isa }
321
+ targets.first.buildPhases << phase
322
+ phase
111
323
  end
112
324
 
113
- private
114
-
115
- def add_object(object)
116
- uuid = generate_uuid
117
- objects[uuid] = object
118
- uuid
119
- end
120
-
121
- def add_file_reference(path, source_tree)
122
- add_object({
123
- "name" => path.basename.to_s,
124
- "isa" => "PBXFileReference",
125
- "sourceTree" => source_tree,
126
- "path" => path.to_s,
127
- })
128
- end
129
-
130
- def add_build_file(file_ref_uuid, extra = {})
131
- add_object(extra.merge({
132
- "isa" => "PBXBuildFile",
133
- "fileRef" => file_ref_uuid
134
- }))
135
- end
136
-
137
- # TODO refactor to create PBX object classes and make this take aither a uuid or a class instead of both.
138
- def add_file_to_list(isa, build_file_uuid, phase_uuid = nil)
139
- objects = objects_by_isa(isa)
140
- _ = object = nil
141
- if phase_uuid.nil?
142
- _, object = objects.first
143
- else
144
- object = objects[phase_uuid]
145
- end
146
- object['files'] << build_file_uuid
147
- end
148
-
149
- def add_object_to_group(object_ref_uuid, name)
150
- object_uuid, object = objects.find do |_, object|
151
- object['isa'] == 'PBXGroup' && object['name'] == name
152
- end
153
- object['children'] << object_ref_uuid
154
- end
155
-
156
- def objects
157
- @template['objects']
158
- end
159
-
160
- def generate_uuid
161
- _uuid = CFUUIDCreate(nil)
162
- uuid = CFUUIDCreateString(nil, _uuid)
163
- CFRelease(_uuid)
164
- CFMakeCollectable(uuid)
165
- # Xcode's version is actually shorter, not worrying about collisions too much right now.
166
- uuid.gsub('-', '')[0..23]
167
- end
168
-
169
- public
170
-
325
+ # A silly hack to pretty print the objects hash from MacRuby.
171
326
  def pretty_print
172
327
  puts `ruby -r pp -e 'pp(#{@template.inspect})'`
173
328
  end