cocoapods 0.0.8 → 0.1.0

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