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.
Files changed (32) hide show
  1. data/bin/xcodeproj +13 -0
  2. data/lib/xcodeproj.rb +13 -1
  3. data/lib/xcodeproj/command.rb +131 -0
  4. data/lib/xcodeproj/command/project_diff.rb +53 -0
  5. data/lib/xcodeproj/command/show.rb +35 -0
  6. data/lib/xcodeproj/command/target_diff.rb +43 -0
  7. data/lib/xcodeproj/config.rb +66 -38
  8. data/lib/xcodeproj/constants.rb +146 -0
  9. data/lib/xcodeproj/helper.rb +28 -0
  10. data/lib/xcodeproj/project.rb +462 -156
  11. data/lib/xcodeproj/project/object.rb +251 -138
  12. data/lib/xcodeproj/project/object/build_configuration.rb +27 -0
  13. data/lib/xcodeproj/project/object/build_file.rb +26 -0
  14. data/lib/xcodeproj/project/object/build_phase.rb +132 -61
  15. data/lib/xcodeproj/project/object/build_rule.rb +46 -0
  16. data/lib/xcodeproj/project/object/configuration_list.rb +47 -0
  17. data/lib/xcodeproj/project/object/container_item_proxy.rb +49 -0
  18. data/lib/xcodeproj/project/object/file_reference.rb +80 -48
  19. data/lib/xcodeproj/project/object/group.rb +213 -89
  20. data/lib/xcodeproj/project/object/native_target.rb +171 -114
  21. data/lib/xcodeproj/project/object/root_object.rb +66 -0
  22. data/lib/xcodeproj/project/object/target_dependency.rb +23 -0
  23. data/lib/xcodeproj/project/object_attributes.rb +382 -0
  24. data/lib/xcodeproj/project/object_list.rb +64 -118
  25. data/lib/xcodeproj/project/recursive_diff.rb +116 -0
  26. data/lib/xcodeproj/workspace.rb +56 -2
  27. metadata +38 -10
  28. data/lib/xcodeproj/project/association.rb +0 -54
  29. data/lib/xcodeproj/project/association/has_many.rb +0 -51
  30. data/lib/xcodeproj/project/association/has_one.rb +0 -39
  31. data/lib/xcodeproj/project/association/reflection.rb +0 -86
  32. data/lib/xcodeproj/project/object/configuration.rb +0 -97
@@ -0,0 +1,66 @@
1
+ module Xcodeproj
2
+ class Project
3
+ module Object
4
+
5
+ # This class represents the root object of a project document.
6
+ #
7
+ class PBXProject < AbstractObject
8
+
9
+ # @return [ObjectList<PBXNativeTarget>] a list of all the targets in
10
+ # the project.
11
+ #
12
+ has_many :targets, PBXNativeTarget
13
+
14
+ # @return [Hash{String => String}] attributes the attributes of the
15
+ # target.
16
+ #
17
+ # The hash might contain the following keys:
18
+ #
19
+ # - `CLASSPREFIX`
20
+ # - `LastUpgradeCheck`
21
+ # - `ORGANIZATIONNAME`
22
+ #
23
+ attribute :attributes, Hash, {'LastUpgradeCheck' => '0450'}
24
+
25
+ # @return [XCConfigurationList] the configuration list of the project.
26
+ #
27
+ has_one :build_configuration_list, XCConfigurationList
28
+
29
+ # @return [String] the compatibility version of the project.
30
+ #
31
+ attribute :compatibility_version, String, 'Xcode 3.2'
32
+
33
+ # @return [String] the development region of the project.
34
+ #
35
+ attribute :development_region, String, 'English'
36
+
37
+ # @return [String] whether the project has scanned for encodings.
38
+ #
39
+ attribute :has_scanned_for_encodings, String, '0'
40
+
41
+ # @return [Array<String>] the list of known regions.
42
+ #
43
+ attribute :known_regions, Array, ['en']
44
+
45
+ # @return [PBXGroup] the main group of the project. The one displayed
46
+ # by Xcode in the Project Navigator.
47
+ #
48
+ has_one :main_group, PBXGroup
49
+
50
+ # @return [PBXGroup] the group containing the references to products of
51
+ # the project.
52
+ #
53
+ has_one :product_ref_group, PBXGroup
54
+
55
+ # @return [String] the directory of the project.
56
+ #
57
+ attribute :project_dir_path, String
58
+
59
+ # @return [String] the root of the project.
60
+ #
61
+ attribute :project_root, String
62
+
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,23 @@
1
+ module Xcodeproj
2
+ class Project
3
+ module Object
4
+
5
+ # Represents a dependency of a target on another one.
6
+ #
7
+ class PBXTargetDependency < AbstractObject
8
+
9
+ # @return [PBXNativeTarget] the target that needs to be built to
10
+ # satisfy the dependency.
11
+ #
12
+ has_one :target, PBXNativeTarget
13
+
14
+ # @return [PBXContainerItemProxy] a proxy for the target that needs to
15
+ # be built. Apparently to support targets in other projects of the
16
+ # same workspace.
17
+ #
18
+ has_one :targetProxy, PBXContainerItemProxy
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,382 @@
1
+ module Xcodeproj
2
+ class Project
3
+ module Object
4
+
5
+ # This class represents an attribute of {AbstractObject} subclasses.
6
+ # Attributes are created by the {AbstractObject} DSL methods and allow to
7
+ # mirror the underlying attributes of the xcodeproj document model.
8
+ #
9
+ # Attributes provide support for runtime type checking. They also allow
10
+ # {AbstractObject} initialization and serialization to plist.
11
+ #
12
+ # @todo Add support for a list of required values so objects can be
13
+ # validated before serialization ?
14
+ #
15
+ class AbstractObjectAttribute
16
+
17
+ require 'active_support/inflector'
18
+
19
+ # @return [Symbol] the type of the attribute. It can be `:simple`,
20
+ # `:to_one`, `:to_many`.
21
+ #
22
+ attr_reader :type
23
+
24
+ # @return [Symbol] the name of the attribute.
25
+ #
26
+ attr_reader :name
27
+
28
+ # @return [Class] the class that owns the attribute.
29
+ #
30
+ attr_accessor :owner
31
+
32
+ # Creates a new attribute with the given type and name.
33
+ #
34
+ # Attributes are expected to be instantiated only by the
35
+ # {AbstractObject} DSL methods.
36
+ #
37
+ # @param [Symbol] type
38
+ # the type of the attribute.
39
+ #
40
+ # @param [Symbol] name
41
+ # the name of the attribute.
42
+ #
43
+ # @param [Class] owner
44
+ # the class that owns the attribute.
45
+ #
46
+ def initialize(type, name, owner)
47
+ @type = type
48
+ @name = name
49
+ @owner = owner
50
+ end
51
+
52
+ # @return[String] The name of the attribute in camel case.
53
+ #
54
+ # @example
55
+ # attribute.new(:simple, project_root)
56
+ # attribute.plist_name #=> projectRoot
57
+ #
58
+ def plist_name
59
+ case name
60
+ when :remote_global_id_string
61
+ # `_id_` would become `Id`
62
+ 'remoteGlobalIDString'
63
+ else
64
+ name.to_s.camelize(:lower)
65
+ end
66
+ end
67
+
68
+ # @return [Array<Class>] the list of the classes accepted by the
69
+ # attribute.
70
+ #
71
+ attr_accessor :classes
72
+
73
+ # @return [String, Array, Hash] the default value, if any, for simple
74
+ # attributes.
75
+ #
76
+ attr_accessor :default_value
77
+
78
+ # Convenience method that returns the value of this attribute for a
79
+ # given object.
80
+ #
81
+ # @param [AbstractObject] object
82
+ # the object for which the value of this attribute is requested.
83
+ #
84
+ # @return [String, Array, Hash, AbstractObject, ObjectList]
85
+ # the value.
86
+ #
87
+ def get_value(object)
88
+ object.send("#{name}")
89
+ end
90
+
91
+ # Convenience method that sets the value of this attribute for a
92
+ # given object. It makes sense only for `:simple` or `:to_one`
93
+ # attributes.
94
+ #
95
+ # @raise It the type of this attribute is `:to_many`.
96
+ #
97
+ # @param [AbstractObject] object
98
+ # the object for which to set the value.
99
+ #
100
+ # @param [String, Hash, Array, AbstractObject] new_value
101
+ # the value to set for the attribute.
102
+ #
103
+ # @return [void]
104
+ #
105
+ def set_value(object, new_value)
106
+ raise "[Xcodeproj] Set value called for to many attribute" if type == :to_many
107
+ object.send("#{name}=", new_value)
108
+ end
109
+
110
+ # Convenience method that sets the value of this attribute for a
111
+ # given object to the default (if any). It makes sense only for
112
+ # `:simple` attributes.
113
+ #
114
+ # @param [AbstractObject] object
115
+ # the object for which to set the default value.
116
+ #
117
+ # @return [void]
118
+ #
119
+ def set_default(object)
120
+ raise "[Xcodeproj] Set value called for to many attribute" unless type == :simple
121
+ set_value(object, default_value) if default_value
122
+ end
123
+
124
+ # Checks that a given value is compatible with the attribute.
125
+ #
126
+ # This method powers the runtime type checking of the {AbstractObject}
127
+ # and is used its by synthesised methods.
128
+ #
129
+ # @raise If the class of the value is not compatible with the attribute.
130
+ #
131
+ # @return [void]
132
+ #
133
+ def validate_value(object)
134
+ return unless object
135
+ acceptable = classes.find { |klass| object.class == klass || object.class < klass }
136
+ if type == :simple
137
+ raise "[Xcodeproj] Type checking error: '#{owner.isa}' expected '#{classes.inspect}' got '#{object.class}' for attribute: #{inspect}" unless acceptable
138
+ else
139
+ raise "[Xcodeproj] Type checking error: '#{owner.isa}' expected #{classes.map(&:isa)} got #{object.isa} for attribute: #{inspect}" unless acceptable
140
+ end
141
+ end
142
+
143
+ def inspect
144
+ "#<name: '#{name}', type: '#{type}', classes: '#{classes}', owner class: '#{owner.isa}'>"
145
+ end
146
+ end
147
+
148
+ class AbstractObject
149
+
150
+ # The {AbstractObject} DSL methods allow to specify with fidelity the
151
+ # underlying model of the xcodeproj document format. {AbstractObject}
152
+ # subclasses should specify their attributes through the following
153
+ # methods:
154
+ #
155
+ # - `{AbstractObject.attribute}`
156
+ # - `{AbstractObject.has_one}`
157
+ # - `{AbstractObject.has_many}`
158
+ #
159
+ # The subclasses should not interfere with the methods synthesised by
160
+ # the DSL and should only implement convenience methods in top of them.
161
+ #
162
+ # Attributes are typed and are validated at runtime.
163
+ #
164
+ class << self
165
+
166
+ # @return [Array<AbstractObjectAttribute>] the attributes associated
167
+ # with the class.
168
+ #
169
+ # @note It includes the attributes defined in the superclass and the
170
+ # list is cleaned for duplicates. Subclasses should not duplicate
171
+ # an attribute of the superclass but for the method implementation
172
+ # they will duplicate them.
173
+ #
174
+ def attributes
175
+ unless @full_attributes
176
+ attributes = @attributes || []
177
+ super_attributes = superclass.respond_to?(:attributes) ? superclass.attributes : []
178
+ # The uniqueness of the attributes is very important because the
179
+ # initialization from plist deletes the values from the dictionary.
180
+ @full_attributes = attributes.concat(super_attributes).uniq
181
+ end
182
+ @full_attributes
183
+ end
184
+
185
+ # @return [Array<AbstractObjectAttribute>] the simple attributes
186
+ # associated with with the class.
187
+ #
188
+ def simple_attributes
189
+ @simple_attributes ||= attributes.select { |a| a.type == :simple }
190
+ end
191
+
192
+ # @return [Array<AbstractObjectAttribute>] the attributes
193
+ # representing a to one relationship associated with with the
194
+ # class.
195
+ #
196
+ def to_one_attributes
197
+ @to_one_attributes ||= attributes.select { |a| a.type == :to_one }
198
+ end
199
+
200
+ # @return [Array<AbstractObjectAttribute>] the attributes
201
+ # representing a to many relationship associated with with the
202
+ # class.
203
+ #
204
+ def to_many_attributes
205
+ @to_many_attributes ||= attributes.select { |a| a.type == :to_many }
206
+ end
207
+
208
+ private
209
+
210
+ # Defines a new simple attribute and synthesises the corresponding
211
+ # methods.
212
+ #
213
+ # @note Simple attributes are directly stored in a hash. They can
214
+ # contain only a string, array of strings or a hash containing
215
+ # strings and thus they are not affected by reference counting.
216
+ # Clients can access the hash directly through the
217
+ # {AbstractObject#simple_attributes_hash} method.
218
+ #
219
+ # @param [Symbol] name
220
+ # the name of the attribute.
221
+ #
222
+ # @param [Class] klass
223
+ # the accepted {Class} for the values of the attribute.
224
+ #
225
+ # @param [String, Array<String>, Hash{String=>String}] default_value
226
+ # the default value for new objects.
227
+ #
228
+ # @example
229
+ # attribute project_root
230
+ # #=> leads to the creation of the following methods
231
+ #
232
+ # def project_root
233
+ # @simple_attributes_hash[projectRoot]
234
+ # end
235
+ #
236
+ # def project_root=(value)
237
+ # attribute.validate_value(value)
238
+ # @simple_attributes_hash[projectRoot] = value
239
+ # end
240
+ #
241
+ # @macro [attach] attribute
242
+ # @!attribute [rw] $1
243
+ #
244
+ def attribute(name, klass, default_value = nil)
245
+ attrb = AbstractObjectAttribute.new(:simple, name, self)
246
+ attrb.classes = [klass]
247
+ attrb.default_value = default_value
248
+ add_attribute(attrb)
249
+
250
+ define_method(attrb.name) do
251
+ @simple_attributes_hash ||= {}
252
+ @simple_attributes_hash[attrb.plist_name]
253
+ end
254
+
255
+ define_method("#{attrb.name}=") do |value|
256
+ @simple_attributes_hash ||= {}
257
+ attrb.validate_value(value)
258
+ @simple_attributes_hash[attrb.plist_name] = value
259
+ end
260
+ end
261
+
262
+ # Defines a new relationship to a single and synthesises the
263
+ # corresponding methods.
264
+ #
265
+ # @note The synthesised setter takes care of handling reference
266
+ # counting directly.
267
+ #
268
+ # @param [String] singular_name
269
+ # the name of the relationship.
270
+ #
271
+ # @param [Class, Array<Class>] isas
272
+ # the list of the classes corresponding to the accepted isas for
273
+ # this relationship.
274
+ #
275
+ # @macro [attach] has_one
276
+ # @!attribute [rw] $1
277
+ #
278
+ def has_one(singular_name, isas)
279
+ isas = [isas] unless isas.is_a?(Array)
280
+ attrb = AbstractObjectAttribute.new(:to_one, singular_name, self)
281
+ attrb.classes = isas
282
+ add_attribute(attrb)
283
+
284
+ attr_reader(attrb.name)
285
+
286
+ define_method("#{attrb.name}=") do |value|
287
+ attrb.validate_value(value)
288
+
289
+ previous_value = send(attrb.name)
290
+ previous_value.remove_referrer(self) if previous_value
291
+ instance_variable_set("@#{attrb.name}", value)
292
+ value.add_referrer(self) if value
293
+ end
294
+ end
295
+
296
+ # Defines a new ordered relationship to many.
297
+ #
298
+ # @note This attribute only generates the reader method. Clients are
299
+ # not supposed to create {ObjectList} objects which are created
300
+ # by the methods synthesised by this attribute on demand.
301
+ # Clients, however can mutate the list according to its
302
+ # interface. The list is responsible to manage the reference
303
+ # counting for its values.
304
+ #
305
+ # @param [String] plural_name
306
+ # the name of the relationship.
307
+ #
308
+ # @param [Class, Array<Class>] isas
309
+ # the list of the classes corresponding to the accepted isas for
310
+ # this relationship.
311
+ #
312
+ # @macro [attach] has_many
313
+ # @!attribute [r] $1
314
+ #
315
+ def has_many(plural_name, isas)
316
+ isas = [isas] unless isas.is_a?(Array)
317
+
318
+ attrb = AbstractObjectAttribute.new(:to_many, plural_name, self)
319
+ attrb.classes = isas
320
+ add_attribute(attrb)
321
+
322
+ define_method(attrb.name) do
323
+ # Here we are in the context of the instance
324
+ list = instance_variable_get("@#{attrb.name}")
325
+ unless list
326
+ list = ObjectList.new(attrb, self)
327
+ instance_variable_set("@#{attrb.name}", list)
328
+ end
329
+ list
330
+ end
331
+ end
332
+
333
+ protected
334
+
335
+ # Adds an attribute to the list of attributes of the class.
336
+ #
337
+ # @note This method is intended to be invoked only by the
338
+ # {AbstractObject} meta programming methods
339
+ #
340
+ # @return [void]
341
+ #
342
+ def add_attribute(attribute)
343
+ raise "[Xcodeproj] BUG - missing classes for #{attribute.inspect}" unless attribute.classes
344
+ raise "[Xcodeproj] BUG - classes:#{attribute.classes} for #{attribute.inspect}" unless attribute.classes.all? { |klass| klass.is_a?(Class) }
345
+ @attributes ||= []
346
+ @attributes << attribute
347
+ end
348
+ end # AbstractObject << self
349
+
350
+ # @return [Hash] the simple attributes hash.
351
+ #
352
+ attr_reader :simple_attributes_hash
353
+
354
+ # @!group xcodeproj format attributes
355
+
356
+ # @return (see AbstractObject.attributes)
357
+ #
358
+ def attributes
359
+ self.class.attributes
360
+ end
361
+
362
+ # @return (see AbstractObject.simple_attributes)
363
+ #
364
+ def simple_attributes
365
+ self.class.simple_attributes
366
+ end
367
+
368
+ # @return (see AbstractObject.to_one_attributes)
369
+ #
370
+ def to_one_attributes
371
+ self.class.to_one_attributes
372
+ end
373
+
374
+ # @return (see AbstractObject.to_many_attributes)
375
+ #
376
+ def to_many_attributes
377
+ self.class.to_many_attributes
378
+ end
379
+ end # AbstractObject
380
+ end
381
+ end
382
+ end