xcodeproj 0.3.5 → 0.4.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/xcodeproj +13 -0
- data/lib/xcodeproj.rb +13 -1
- data/lib/xcodeproj/command.rb +131 -0
- data/lib/xcodeproj/command/project_diff.rb +53 -0
- data/lib/xcodeproj/command/show.rb +35 -0
- data/lib/xcodeproj/command/target_diff.rb +43 -0
- data/lib/xcodeproj/config.rb +66 -38
- data/lib/xcodeproj/constants.rb +146 -0
- data/lib/xcodeproj/helper.rb +28 -0
- data/lib/xcodeproj/project.rb +462 -156
- data/lib/xcodeproj/project/object.rb +251 -138
- data/lib/xcodeproj/project/object/build_configuration.rb +27 -0
- data/lib/xcodeproj/project/object/build_file.rb +26 -0
- data/lib/xcodeproj/project/object/build_phase.rb +132 -61
- data/lib/xcodeproj/project/object/build_rule.rb +46 -0
- data/lib/xcodeproj/project/object/configuration_list.rb +47 -0
- data/lib/xcodeproj/project/object/container_item_proxy.rb +49 -0
- data/lib/xcodeproj/project/object/file_reference.rb +80 -48
- data/lib/xcodeproj/project/object/group.rb +213 -89
- data/lib/xcodeproj/project/object/native_target.rb +171 -114
- data/lib/xcodeproj/project/object/root_object.rb +66 -0
- data/lib/xcodeproj/project/object/target_dependency.rb +23 -0
- data/lib/xcodeproj/project/object_attributes.rb +382 -0
- data/lib/xcodeproj/project/object_list.rb +64 -118
- data/lib/xcodeproj/project/recursive_diff.rb +116 -0
- data/lib/xcodeproj/workspace.rb +56 -2
- metadata +38 -10
- data/lib/xcodeproj/project/association.rb +0 -54
- data/lib/xcodeproj/project/association/has_many.rb +0 -51
- data/lib/xcodeproj/project/association/has_one.rb +0 -39
- data/lib/xcodeproj/project/association/reflection.rb +0 -86
- data/lib/xcodeproj/project/object/configuration.rb +0 -97
@@ -1,207 +1,320 @@
|
|
1
|
-
require 'active_support/inflector'
|
2
|
-
|
3
1
|
module Xcodeproj
|
4
2
|
class Project
|
3
|
+
|
5
4
|
# This is the namespace in which all the classes that wrap the objects in
|
6
5
|
# a Xcode project reside.
|
7
6
|
#
|
8
|
-
# The base class from which all classes inherit is
|
7
|
+
# The base class from which all classes inherit is AbstractObject.
|
9
8
|
#
|
10
9
|
# If you need to deal with these classes directly, it's possible to include
|
11
10
|
# this namespace into yours, making it unnecessary to prefix them with
|
12
11
|
# Xcodeproj::Project::Object.
|
13
12
|
#
|
14
13
|
# @example
|
14
|
+
# class SourceFileSorter
|
15
|
+
# include Xcodeproj::Project::Object
|
16
|
+
# end
|
15
17
|
#
|
16
|
-
# class SourceFileSorter
|
17
|
-
# include Xcodeproj::Project::Object
|
18
|
-
# end
|
19
18
|
module Object
|
20
19
|
|
21
|
-
#
|
22
|
-
# be valid classes in a Xcode project. A new AbstractPBXObject subclass is
|
23
|
-
# created for the constant and returned.
|
20
|
+
# @abstract
|
24
21
|
#
|
25
|
-
# @return [Class] The generated class inhertiting from AbstractPBXObject.
|
26
|
-
def self.const_missing(name)
|
27
|
-
if name.to_s =~ /^(PBX|XC)/
|
28
|
-
klass = Class.new(AbstractPBXObject)
|
29
|
-
const_set(name, klass)
|
30
|
-
klass
|
31
|
-
else
|
32
|
-
super
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
22
|
# This is the base class of all object types that can exist in a Xcode
|
37
23
|
# project. As such it provides common behavior, but you can only use
|
38
|
-
# instances of subclasses of
|
24
|
+
# instances of subclasses of AbstractObject, because this class does
|
39
25
|
# not exist in actual Xcode projects.
|
40
|
-
|
26
|
+
#
|
27
|
+
# Almost all the methods implemented by this class are not expected to be
|
28
|
+
# used by {Xcodeproj} clients.
|
29
|
+
#
|
30
|
+
# Subclasses should clearly identify which methods reflect the xcodeproj
|
31
|
+
# document model and which methods are offered as convenience. Object
|
32
|
+
# lists always represent a relationship to many of the model while simple
|
33
|
+
# arrays represent dynamically generated values offered a convenience for
|
34
|
+
# clients.
|
35
|
+
#
|
36
|
+
class AbstractObject
|
37
|
+
|
38
|
+
# @return [String] the isa of the class.
|
39
|
+
#
|
40
|
+
def self.isa
|
41
|
+
@isa ||= name.split('::').last
|
42
|
+
end
|
41
43
|
|
42
|
-
#
|
43
|
-
# attributes hash that the object wraps.
|
44
|
+
# @return [String] the object's class name.
|
44
45
|
#
|
46
|
+
attr_reader :isa
|
47
|
+
|
48
|
+
# It is not recommended to instantiate objects through this
|
49
|
+
# constructor. To create objects manually is easier to use
|
50
|
+
# the {Project#new}. Otherwise, it is possible to use the convenience
|
51
|
+
# methods offered by {Xcodeproj} which take care of configuring the
|
52
|
+
# objects for common usage cases.
|
45
53
|
#
|
46
|
-
# @
|
54
|
+
# @param [Project] project
|
55
|
+
# the project that will host the object.
|
47
56
|
#
|
48
|
-
#
|
49
|
-
#
|
57
|
+
# @param [String] uuid
|
58
|
+
# the UUID of the new object.
|
50
59
|
#
|
51
|
-
|
52
|
-
|
53
|
-
|
60
|
+
def initialize(project, uuid)
|
61
|
+
@project, @uuid = project, uuid
|
62
|
+
@isa = self.class.isa
|
63
|
+
@referrers = []
|
64
|
+
raise "[Xcodeproj] Attempt to initialize an abstract class." unless @isa.match(/^(PBX|XC)/)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Initializes the object with the default values of simple attributes.
|
54
68
|
#
|
55
|
-
#
|
56
|
-
#
|
69
|
+
# This method is called by the {Project#new} and is not performed on
|
70
|
+
# initialization to prevet adding defaults to objects generated by a
|
71
|
+
# plist.
|
57
72
|
#
|
58
|
-
#
|
59
|
-
# build_phase.attributes # => { 'settings' => { 'COMPILER_FLAGS' => '-fobjc-no-arc' }, ... }
|
73
|
+
# @return [void]
|
60
74
|
#
|
61
|
-
|
75
|
+
def initialize_defaults
|
76
|
+
simple_attributes.each { |a| a.set_default(self) }
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [String] the object universally unique identifier.
|
62
80
|
#
|
63
|
-
|
64
|
-
|
65
|
-
#
|
81
|
+
attr_reader :uuid
|
82
|
+
|
83
|
+
# @return [Project] the project that owns the object.
|
66
84
|
#
|
67
|
-
|
68
|
-
|
85
|
+
attr_reader :project
|
86
|
+
|
87
|
+
# Removes the object from the project by asking to its referrers to
|
88
|
+
# remove the reference to it.
|
69
89
|
#
|
70
|
-
#
|
71
|
-
#
|
90
|
+
# @note The root object is owned by the project and should not be
|
91
|
+
# manipulated with this method.
|
72
92
|
#
|
93
|
+
# @return [void]
|
73
94
|
#
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
accessor_name = (options[:as] || name).to_s
|
79
|
-
attribute_name = name.to_s.camelize(:lower) # change `foo_bar' to `fooBar'
|
80
|
-
define_method(accessor_name) { @attributes[attribute_name] }
|
81
|
-
define_method("#{accessor_name}=") { |value| @attributes[attribute_name] = value }
|
95
|
+
def remove_from_project
|
96
|
+
@project.objects_by_uuid.delete(uuid)
|
97
|
+
@referrers.each { |referrer| referrer.remove_reference(self) }
|
98
|
+
raise "[Xcodeproj] BUG: #{self} should have no referrers instead the following objects are still referencing it #{referrers}" unless referrers.count == 0
|
82
99
|
end
|
83
100
|
|
84
|
-
|
85
|
-
|
101
|
+
# Returns the value of the name attribute or returns a generic name for
|
102
|
+
# the object.
|
103
|
+
#
|
104
|
+
# @note Not all concrete classes implement the name attribute and this
|
105
|
+
# method prevents from overriding it in plist.
|
106
|
+
#
|
107
|
+
# @return [String] a name for the object.
|
108
|
+
#
|
109
|
+
def display_name
|
110
|
+
result = name if respond_to?(:name)
|
111
|
+
result || isa.gsub(/^(PBX|XC)/, '')
|
86
112
|
end
|
87
113
|
|
88
|
-
|
114
|
+
# @!group Reference counting
|
89
115
|
|
90
|
-
# [
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
attribute :name
|
116
|
+
# @return [Array<ObjectList>] The list of the objects that have a
|
117
|
+
# reference to this object.
|
118
|
+
#
|
119
|
+
attr_reader :referrers
|
95
120
|
|
96
|
-
#
|
97
|
-
#
|
121
|
+
# Informs the object that another object is referencing it. If the
|
122
|
+
# object had no previous references it is added to the project UUIDs
|
123
|
+
# hash.
|
98
124
|
#
|
99
|
-
# @
|
125
|
+
# @return [void]
|
100
126
|
#
|
101
|
-
|
127
|
+
def add_referrer(referrer)
|
128
|
+
@referrers << referrer
|
129
|
+
@project.objects_by_uuid[uuid] = self
|
130
|
+
end
|
131
|
+
|
132
|
+
# Informs the object that another object stopped referencing it. If the
|
133
|
+
# object has no other references it is removed form project UUIDs hash
|
134
|
+
# because it is unreachable.
|
135
|
+
#
|
136
|
+
# @return [void]
|
102
137
|
#
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
unless uuid
|
108
|
-
# Add new objects to the main hash with a unique UUID
|
109
|
-
uuid = generate_uuid
|
110
|
-
@project.add_object_hash(uuid, @attributes)
|
138
|
+
def remove_referrer(referrer)
|
139
|
+
@referrers.delete(referrer)
|
140
|
+
if @referrers.count == 0
|
141
|
+
@project.objects_by_uuid.delete(uuid)
|
111
142
|
end
|
112
|
-
@uuid = uuid
|
113
143
|
end
|
114
144
|
|
115
|
-
|
116
|
-
|
117
|
-
|
145
|
+
# Removes all the references to a given object.
|
146
|
+
#
|
147
|
+
# @return [void]
|
148
|
+
#
|
149
|
+
def remove_reference(object)
|
150
|
+
to_one_attributes.each do |attrb|
|
151
|
+
value = attrb.get_value(self)
|
152
|
+
attrb.set_value(self, nil) if value.equal?(object)
|
153
|
+
end
|
118
154
|
|
119
|
-
|
120
|
-
|
155
|
+
to_many_attributes.each do |attrb|
|
156
|
+
list = attrb.get_value(self)
|
157
|
+
list.delete(object)
|
158
|
+
end
|
121
159
|
end
|
122
160
|
|
123
|
-
|
124
|
-
self.uuid <=> other.uuid
|
125
|
-
end
|
161
|
+
# @!group Plist related methods
|
126
162
|
|
127
|
-
|
128
|
-
|
129
|
-
|
163
|
+
# Configures the object with the objects hash from a plist.
|
164
|
+
#
|
165
|
+
# **Implementation detail**: it is important that the attributes for a
|
166
|
+
# given concrete class are unique because the value is removed from the
|
167
|
+
# array at each iteration and duplicate would result in nil values.
|
168
|
+
#
|
169
|
+
# @return [void]
|
170
|
+
#
|
171
|
+
def configure_with_plist(objects_by_uuid_plist)
|
172
|
+
object_plist = objects_by_uuid_plist[uuid].dup
|
130
173
|
|
131
|
-
|
132
|
-
|
133
|
-
return nil unless respond_to?(attribute)
|
174
|
+
raise "[Xcodeproj] Attempt to initialize `#{isa}` from plist with different isa `#{object_plist}`" unless object_plist['isa'] == isa
|
175
|
+
object_plist.delete('isa')
|
134
176
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
send(attribute) == expected_value
|
139
|
-
end
|
177
|
+
simple_attributes.each do |attrb|
|
178
|
+
attrb.set_value(self, object_plist[attrb.plist_name])
|
179
|
+
object_plist.delete(attrb.plist_name)
|
140
180
|
end
|
141
|
-
end
|
142
181
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
#
|
149
|
-
# If a block is given the list instance is yielded so that the default
|
150
|
-
# callbacks can be overridden.
|
151
|
-
#
|
152
|
-
# @param [Array] uuid_list The UUID array instance which is
|
153
|
-
# part of the internal data. If this
|
154
|
-
# would be an arbitrary array and an
|
155
|
-
# object is added, then it doesn't
|
156
|
-
# actually modify the internal data,
|
157
|
-
# meaning the change is lost.
|
158
|
-
#
|
159
|
-
# @param [AbstractPBXObject] klass The AbstractPBXObject subclass to
|
160
|
-
# which the list should be scoped.
|
161
|
-
#
|
162
|
-
# @yield [PBXObjectList] The list instance, allowing you to
|
163
|
-
# easily override the callbacks.
|
164
|
-
#
|
165
|
-
# @return [PBXObjectList<klass>] The list of matching objects.
|
166
|
-
def list_by_class(uuid_list, klass)
|
167
|
-
PBXObjectList.new(klass, @project) do |list|
|
168
|
-
list.let(:uuid_scope) do
|
169
|
-
# TODO why does this not work? should be more efficient.
|
170
|
-
#uuid_list.select do |uuid|
|
171
|
-
#@project.objects_hash[uuid]['isa'] == klass.isa
|
172
|
-
#end
|
173
|
-
uuid_list.map { |uuid| @project.objects[uuid] }.select { |o| o.is_a?(klass) }.map(&:uuid)
|
182
|
+
to_one_attributes.each do |attrb|
|
183
|
+
ref_uuid = object_plist[attrb.plist_name]
|
184
|
+
if ref_uuid
|
185
|
+
ref = project.objects_by_uuid[ref_uuid] || project.new_from_plist(ref_uuid, objects_by_uuid_plist)
|
186
|
+
attrb.set_value(self, ref)
|
174
187
|
end
|
175
|
-
|
176
|
-
|
177
|
-
|
188
|
+
object_plist.delete(attrb.plist_name)
|
189
|
+
end
|
190
|
+
|
191
|
+
to_many_attributes.each do |attrb|
|
192
|
+
ref_uuids = object_plist[attrb.plist_name] || []
|
193
|
+
list = attrb.get_value(self)
|
194
|
+
ref_uuids.each do |ref_uuid|
|
195
|
+
ref = project.objects_by_uuid[ref_uuid] || project.new_from_plist(ref_uuid, objects_by_uuid_plist)
|
196
|
+
list << ref
|
178
197
|
end
|
179
|
-
|
198
|
+
object_plist.delete(attrb.plist_name)
|
199
|
+
end
|
200
|
+
unless object_plist.empty?
|
201
|
+
raise "[!] Xcodeproj doesn't know about the following attributes " \
|
202
|
+
"#{object_plist} for the '#{isa}' isa.\n" \
|
203
|
+
"Please file and issue: https://github.com/CocoaPods/Xcodeproj/issues/new"
|
180
204
|
end
|
181
205
|
end
|
182
206
|
|
183
|
-
|
207
|
+
# @note the key for simple and to_one attributes usually appears only
|
208
|
+
# if there is a value. To many keys always appear with an empty
|
209
|
+
# array.
|
210
|
+
#
|
211
|
+
def to_plist
|
212
|
+
plist = {}
|
213
|
+
plist['isa'] = isa
|
214
|
+
|
215
|
+
simple_attributes.each do |attrb|
|
216
|
+
value = attrb.get_value(self)
|
217
|
+
plist[attrb.plist_name] = value if value
|
218
|
+
end
|
219
|
+
|
220
|
+
to_one_attributes.each do |attrb|
|
221
|
+
obj = attrb.get_value(self)
|
222
|
+
plist[attrb.plist_name] = obj.uuid if obj
|
223
|
+
end
|
184
224
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
uuid
|
225
|
+
to_many_attributes.each do |attrb|
|
226
|
+
list = attrb.get_value(self)
|
227
|
+
plist[attrb.plist_name] = list.uuids
|
228
|
+
end
|
229
|
+
|
230
|
+
plist
|
192
231
|
end
|
193
|
-
|
232
|
+
alias :to_hash :to_plist
|
233
|
+
|
234
|
+
# Returns a cascade reppresentation of the object without UUIDs.
|
235
|
+
#
|
236
|
+
# This method is designed to work in conjuction with {Hash#recursive_diff}
|
237
|
+
# to provie a complete, yet redable, diff of two projects *not* affected by
|
238
|
+
# isa differences.
|
239
|
+
#
|
240
|
+
# @todo current implementation might cause infinite loops.
|
241
|
+
#
|
242
|
+
# @return [Hash] a hash reppresentation of the project different from the
|
243
|
+
# plist one.
|
244
|
+
#
|
245
|
+
def to_tree_hash
|
246
|
+
hash = {}
|
247
|
+
hash['displayName'] = display_name if self.respond_to?(:display_name)
|
248
|
+
hash['isa'] = isa
|
249
|
+
|
250
|
+
simple_attributes.each do |attrb|
|
251
|
+
value = attrb.get_value(self)
|
252
|
+
hash[attrb.plist_name] = value if value
|
253
|
+
end
|
194
254
|
|
255
|
+
to_one_attributes.each do |attrb|
|
256
|
+
obj = attrb.get_value(self)
|
257
|
+
hash[attrb.plist_name] = obj.to_tree_hash if obj
|
258
|
+
end
|
259
|
+
|
260
|
+
to_many_attributes.each do |attrb|
|
261
|
+
list = attrb.get_value(self)
|
262
|
+
hash[attrb.plist_name] = list.map { |obj| obj.to_tree_hash }
|
263
|
+
end
|
264
|
+
|
265
|
+
hash
|
266
|
+
end
|
267
|
+
|
268
|
+
# !@group Object methods
|
269
|
+
|
270
|
+
def ==(other)
|
271
|
+
other.is_a?(AbstractObject) && self.to_plist == other.to_plist
|
272
|
+
end
|
273
|
+
|
274
|
+
def <=>(other)
|
275
|
+
self.uuid <=> other.uuid
|
276
|
+
end
|
277
|
+
|
278
|
+
def inspect
|
279
|
+
s = "#<UUID: `#{uuid}', isa: `#{isa}'>"
|
280
|
+
s << ", name: `#{name}'" if respond_to?(:name) && name
|
281
|
+
s
|
282
|
+
end
|
283
|
+
end
|
195
284
|
end
|
196
285
|
end
|
197
286
|
end
|
198
287
|
|
199
|
-
require 'xcodeproj/project/
|
288
|
+
require 'xcodeproj/project/object_attributes'
|
200
289
|
require 'xcodeproj/project/object_list'
|
201
290
|
|
202
|
-
#
|
291
|
+
# Required because some classes have cyclical references to each other.
|
292
|
+
#
|
293
|
+
# In ruby 1.8.7 the hash are not sorted so it is necessary to use an array to
|
294
|
+
# preserve the proper loading order of the various super classes.
|
295
|
+
#
|
296
|
+
# @todo I'm sure that there is a method to achieve the same result which
|
297
|
+
# doesn't present the risk of some rubist laughing at me :-)
|
298
|
+
#
|
299
|
+
Xcodeproj::Constants::ISAS_SUPER_CLASSES.each do |superclass_name|
|
300
|
+
isas = Xcodeproj::Constants::KNOWN_ISAS[superclass_name]
|
301
|
+
superklass = Xcodeproj::Project::Object.const_get(superclass_name)
|
302
|
+
isas.each do |isa|
|
303
|
+
c = Class.new(superklass)
|
304
|
+
Xcodeproj::Project::Object.const_set(isa, c)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# Now load the concrete subclasses.
|
309
|
+
require 'xcodeproj/project/object/build_configuration'
|
310
|
+
require 'xcodeproj/project/object/build_file'
|
203
311
|
require 'xcodeproj/project/object/build_phase'
|
204
|
-
require 'xcodeproj/project/object/
|
312
|
+
require 'xcodeproj/project/object/build_rule'
|
313
|
+
require 'xcodeproj/project/object/configuration_list'
|
314
|
+
require 'xcodeproj/project/object/container_item_proxy'
|
205
315
|
require 'xcodeproj/project/object/file_reference'
|
206
316
|
require 'xcodeproj/project/object/group'
|
207
317
|
require 'xcodeproj/project/object/native_target'
|
318
|
+
require 'xcodeproj/project/object/root_object'
|
319
|
+
require 'xcodeproj/project/object/target_dependency'
|
320
|
+
|