xcodeproj 0.3.5 → 0.4.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.
- 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
|
+
|