xcodeproj 0.28.2 → 1.0.0.beta.1
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.
- checksums.yaml +4 -4
- data/lib/xcodeproj.rb +1 -1
- data/lib/xcodeproj/command/config_dump.rb +1 -1
- data/lib/xcodeproj/config.rb +14 -5
- data/lib/xcodeproj/config/other_linker_flags_parser.rb +5 -2
- data/lib/xcodeproj/constants.rb +57 -24
- data/lib/xcodeproj/differ.rb +2 -2
- data/lib/xcodeproj/gem_version.rb +1 -1
- data/lib/xcodeproj/plist.rb +89 -0
- data/lib/xcodeproj/plist/ffi.rb +119 -0
- data/lib/xcodeproj/plist/ffi/core_foundation.rb +441 -0
- data/lib/xcodeproj/plist/ffi/dev_tools_core.rb +186 -0
- data/lib/xcodeproj/plist/plist_gem.rb +27 -0
- data/lib/xcodeproj/project.rb +36 -12
- data/lib/xcodeproj/project/object.rb +15 -2
- data/lib/xcodeproj/project/object/group.rb +28 -0
- data/lib/xcodeproj/project/object/helpers/file_references_factory.rb +1 -1
- data/lib/xcodeproj/project/object/native_target.rb +26 -7
- data/lib/xcodeproj/project/object_attributes.rb +10 -0
- data/lib/xcodeproj/project/object_dictionary.rb +2 -0
- data/lib/xcodeproj/project/object_list.rb +12 -0
- data/lib/xcodeproj/project/uuid_generator.rb +1 -0
- data/lib/xcodeproj/scheme/build_action.rb +2 -1
- data/lib/xcodeproj/scheme/environment_variables.rb +170 -0
- data/lib/xcodeproj/scheme/launch_action.rb +16 -0
- data/lib/xcodeproj/scheme/test_action.rb +18 -0
- data/lib/xcodeproj/workspace.rb +125 -34
- data/lib/xcodeproj/workspace/group_reference.rb +64 -0
- metadata +19 -13
- data/lib/xcodeproj/plist_helper.rb +0 -758
@@ -0,0 +1,441 @@
|
|
1
|
+
require 'fiddle'
|
2
|
+
|
3
|
+
module Xcodeproj
|
4
|
+
module Plist
|
5
|
+
module FFI
|
6
|
+
# This module provides an interface to the CoreFoundation OS X framework.
|
7
|
+
# Specifically it bridges the functions required to be able to read and write
|
8
|
+
# property lists.
|
9
|
+
#
|
10
|
+
# Everything in here should be considered an implementation detail and thus is
|
11
|
+
# not further documented.
|
12
|
+
#
|
13
|
+
# @todo Move this out into its own gem.
|
14
|
+
#
|
15
|
+
# @!visibility private
|
16
|
+
#
|
17
|
+
module CoreFoundation
|
18
|
+
PATH = '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation'
|
19
|
+
|
20
|
+
# rubocop:disable Style/MethodName
|
21
|
+
# rubocop:disable Style/VariableName
|
22
|
+
|
23
|
+
# @!group Ruby hash as property list (de)serialization
|
24
|
+
#---------------------------------------------------------------------------#
|
25
|
+
|
26
|
+
def self.RubyHashPropertyListWrite(hash, path)
|
27
|
+
url = CFURLCreateFromFileSystemRepresentation(NULL,
|
28
|
+
path,
|
29
|
+
path.bytesize,
|
30
|
+
FALSE)
|
31
|
+
stream = CFWriteStreamCreateWithFile(NULL, url)
|
32
|
+
unless CFWriteStreamOpen(stream) == TRUE
|
33
|
+
raise IOError, 'Unable to open stream.'
|
34
|
+
end
|
35
|
+
|
36
|
+
plist = RubyHashToCFDictionary(hash)
|
37
|
+
|
38
|
+
error_ptr = CFTypeRefPointer()
|
39
|
+
result = CFPropertyListWrite(plist,
|
40
|
+
stream,
|
41
|
+
KCFPropertyListXMLFormat_v1_0,
|
42
|
+
0,
|
43
|
+
error_ptr)
|
44
|
+
CFWriteStreamClose(stream)
|
45
|
+
|
46
|
+
if result == 0
|
47
|
+
description = CFCopyDescription(error_ptr.ptr)
|
48
|
+
raise IOError, "Unable to write plist data: #{description}"
|
49
|
+
end
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.RubyHashPropertyListRead(path)
|
54
|
+
url = CFURLCreateFromFileSystemRepresentation(NULL,
|
55
|
+
path,
|
56
|
+
path.bytesize,
|
57
|
+
FALSE)
|
58
|
+
stream = CFReadStreamCreateWithFile(NULL, url)
|
59
|
+
unless CFReadStreamOpen(stream) == TRUE
|
60
|
+
raise IOError, 'Unable to open stream.'
|
61
|
+
end
|
62
|
+
|
63
|
+
error_ptr = CFTypeRefPointer()
|
64
|
+
plist = CFPropertyListCreateWithStream(NULL,
|
65
|
+
stream,
|
66
|
+
0,
|
67
|
+
KCFPropertyListImmutable,
|
68
|
+
NULL,
|
69
|
+
error_ptr)
|
70
|
+
CFReadStreamClose(stream)
|
71
|
+
|
72
|
+
if plist.null?
|
73
|
+
description = CFCopyDescription(error_ptr.ptr)
|
74
|
+
raise IOError, "Unable to read plist data: #{description}"
|
75
|
+
elsif CFGetTypeID(plist) != CFDictionaryGetTypeID()
|
76
|
+
raise TypeError, 'Expected a plist with a dictionary root object.'
|
77
|
+
end
|
78
|
+
|
79
|
+
CFDictionaryToRubyHash(plist)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @!group Types
|
83
|
+
#---------------------------------------------------------------------------#
|
84
|
+
|
85
|
+
# rubocop:disable Style/ConstantName
|
86
|
+
|
87
|
+
NULL = Fiddle::NULL
|
88
|
+
|
89
|
+
Void = Fiddle::TYPE_VOID
|
90
|
+
VoidPointer = Fiddle::TYPE_VOIDP
|
91
|
+
FunctionPointer = VoidPointer
|
92
|
+
|
93
|
+
UInt32 = -Fiddle::TYPE_INT
|
94
|
+
UInt8 = -Fiddle::TYPE_CHAR
|
95
|
+
|
96
|
+
SInt32Pointer = VoidPointer
|
97
|
+
UInt8Pointer = VoidPointer
|
98
|
+
CharPointer = VoidPointer
|
99
|
+
|
100
|
+
Boolean = Fiddle::TYPE_CHAR
|
101
|
+
TRUE = 1
|
102
|
+
FALSE = 0
|
103
|
+
|
104
|
+
SINT64_MAX = 2**63 - 1
|
105
|
+
SINT64_MIN = -SINT64_MAX - 1
|
106
|
+
|
107
|
+
SIZEOF_SINT64 = 8
|
108
|
+
SIZEOF_FLOAT64 = 8
|
109
|
+
|
110
|
+
SINT64_PACK_TEMPLATE = 'q'
|
111
|
+
FLOAT64_PACK_TEMPLATE = 'd'
|
112
|
+
|
113
|
+
CFTypeRef = VoidPointer
|
114
|
+
CFTypeRefPointer = VoidPointer
|
115
|
+
CFIndex = Fiddle::TYPE_LONG
|
116
|
+
CFTypeID = -Fiddle::TYPE_LONG
|
117
|
+
CFOptionFlags = UInt32
|
118
|
+
|
119
|
+
CFPropertyListMutabilityOptions = Fiddle::TYPE_INT
|
120
|
+
KCFPropertyListImmutable = 0
|
121
|
+
|
122
|
+
CFPropertyListFormat = Fiddle::TYPE_INT
|
123
|
+
KCFPropertyListXMLFormat_v1_0 = 100
|
124
|
+
CFPropertyListFormatPointer = VoidPointer
|
125
|
+
|
126
|
+
CFStringEncoding = UInt32
|
127
|
+
KCFStringEncodingUTF8 = 0x08000100
|
128
|
+
|
129
|
+
CFNumberType = Fiddle::TYPE_INT
|
130
|
+
KCFNumberSInt64Type = 4
|
131
|
+
KCFNumberFloat64Type = 6
|
132
|
+
|
133
|
+
# rubocop:enable Style/ConstantName
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
# @!group Helpers
|
138
|
+
#---------------------------------------------------------------------------#
|
139
|
+
|
140
|
+
def self.image
|
141
|
+
@image ||= Fiddle.dlopen(PATH)
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.free_function
|
145
|
+
@free_function ||= Fiddle::Function.new(Fiddle::Handle.new['free'], [VoidPointer], Void)
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.CFRelease_function
|
149
|
+
@CFRelease ||= Fiddle::Function.new(image['CFRelease'], [CFTypeRef], Void)
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.extern_image(image, symbol, parameter_types, return_type)
|
153
|
+
symbol = symbol.to_s
|
154
|
+
create_function = symbol.include?('Create')
|
155
|
+
function_cache_key = "@__#{symbol}__"
|
156
|
+
|
157
|
+
# Define a singleton method on the CoreFoundation module.
|
158
|
+
define_singleton_method(symbol) do |*args|
|
159
|
+
unless args.size == parameter_types.size
|
160
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for " \
|
161
|
+
"#{parameter_types.size})"
|
162
|
+
end
|
163
|
+
|
164
|
+
unless function = instance_variable_get(function_cache_key)
|
165
|
+
function = Fiddle::Function.new(image[symbol],
|
166
|
+
parameter_types,
|
167
|
+
return_type)
|
168
|
+
instance_variable_set(function_cache_key, function)
|
169
|
+
end
|
170
|
+
|
171
|
+
result = function.call(*args)
|
172
|
+
create_function ? CFAutoRelease(result) : result
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.extern(symbol, parameter_types, return_type)
|
177
|
+
extern_image(image, symbol, parameter_types, return_type)
|
178
|
+
end
|
179
|
+
|
180
|
+
public
|
181
|
+
|
182
|
+
# @!group CoreFoundation function definitions
|
183
|
+
#---------------------------------------------------------------------------#
|
184
|
+
|
185
|
+
# CFTypeRef description
|
186
|
+
extern :CFShow, [CFTypeRef], Void
|
187
|
+
extern :CFCopyDescription, [CFTypeRef], CFTypeRef
|
188
|
+
|
189
|
+
# CFType reflection
|
190
|
+
extern :CFGetTypeID, [CFTypeRef], CFTypeID
|
191
|
+
extern :CFDictionaryGetTypeID, [], CFTypeID
|
192
|
+
extern :CFStringGetTypeID, [], CFTypeID
|
193
|
+
extern :CFArrayGetTypeID, [], CFTypeID
|
194
|
+
extern :CFBooleanGetTypeID, [], CFTypeID
|
195
|
+
extern :CFNumberGetTypeID, [], CFTypeID
|
196
|
+
|
197
|
+
# CFStream
|
198
|
+
extern :CFWriteStreamCreateWithFile, [CFTypeRef, CFTypeRef], CFTypeRef
|
199
|
+
extern :CFWriteStreamOpen, [CFTypeRef], Boolean
|
200
|
+
extern :CFWriteStreamClose, [CFTypeRef], Void
|
201
|
+
extern :CFReadStreamCreateWithFile, [CFTypeRef, CFTypeRef], CFTypeRef
|
202
|
+
extern :CFReadStreamOpen, [CFTypeRef], Boolean
|
203
|
+
extern :CFReadStreamClose, [CFTypeRef], Void
|
204
|
+
|
205
|
+
# CFURL
|
206
|
+
extern :CFURLCreateFromFileSystemRepresentation, [CFTypeRef, UInt8Pointer, CFIndex, Boolean], CFTypeRef
|
207
|
+
|
208
|
+
# CFPropertyList
|
209
|
+
extern :CFPropertyListWrite, [CFTypeRef, CFTypeRef, CFPropertyListFormat, CFOptionFlags, CFTypeRefPointer], CFIndex
|
210
|
+
extern :CFPropertyListCreateWithStream, [CFTypeRef, CFTypeRef, CFIndex, CFOptionFlags, CFPropertyListFormatPointer, CFTypeRefPointer], CFTypeRef
|
211
|
+
|
212
|
+
# CFString
|
213
|
+
extern :CFStringCreateExternalRepresentation, [CFTypeRef, CFTypeRef, CFStringEncoding, UInt8], CFTypeRef
|
214
|
+
extern :CFStringCreateWithCString, [CFTypeRef, CharPointer, CFStringEncoding], CFTypeRef
|
215
|
+
|
216
|
+
# CFData
|
217
|
+
extern :CFDataGetLength, [CFTypeRef], CFIndex
|
218
|
+
extern :CFDataGetBytePtr, [CFTypeRef], VoidPointer
|
219
|
+
|
220
|
+
# CFDictionary
|
221
|
+
extern :CFDictionaryCreateMutable, [CFTypeRef, CFIndex, VoidPointer, VoidPointer], CFTypeRef
|
222
|
+
extern :CFDictionaryAddValue, [CFTypeRef, CFTypeRef, CFTypeRef], VoidPointer
|
223
|
+
extern :CFDictionaryApplyFunction, [CFTypeRef, FunctionPointer, VoidPointer], Void
|
224
|
+
|
225
|
+
# CFArray
|
226
|
+
extern :CFArrayCreateMutable, [CFTypeRef, CFIndex, VoidPointer], CFTypeRef
|
227
|
+
extern :CFArrayAppendValue, [CFTypeRef, CFTypeRef], VoidPointer
|
228
|
+
extern :CFArrayGetCount, [CFTypeRef], CFIndex
|
229
|
+
extern :CFArrayGetValueAtIndex, [CFTypeRef, CFIndex], CFTypeRef
|
230
|
+
|
231
|
+
# CFBoolean
|
232
|
+
extern :CFBooleanGetValue, [CFTypeRef], Boolean
|
233
|
+
|
234
|
+
# CFNumber
|
235
|
+
extern :CFNumberIsFloatType, [CFTypeRef], Boolean
|
236
|
+
extern :CFNumberGetValue, [CFTypeRef, CFNumberType, VoidPointer], Boolean
|
237
|
+
extern :CFNumberCreate, [CFTypeRef, CFNumberType, VoidPointer], CFTypeRef
|
238
|
+
|
239
|
+
# @!group Custom convenience functions
|
240
|
+
#---------------------------------------------------------------------------#
|
241
|
+
|
242
|
+
def self.CFBooleanTrue
|
243
|
+
@CFBooleanTrue ||= Fiddle::Pointer.new(image['kCFBooleanTrue']).ptr
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.CFBooleanFalse
|
247
|
+
@CFBooleanFalse ||= Fiddle::Pointer.new(image['kCFBooleanFalse']).ptr
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.CFTypeArrayCallBacks
|
251
|
+
@CFTypeArrayCallBacks ||= image['kCFTypeArrayCallBacks']
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.CFTypeDictionaryKeyCallBacks
|
255
|
+
@CFTypeDictionaryKeyCallBacks ||= image['kCFTypeDictionaryKeyCallBacks']
|
256
|
+
end
|
257
|
+
|
258
|
+
def self.CFTypeDictionaryValueCallBacks
|
259
|
+
@CFTypeDictionaryValueCallBacks ||= image['kCFTypeDictionaryValueCallBacks']
|
260
|
+
end
|
261
|
+
|
262
|
+
# This pointer will assign `CFRelease` as the free function when
|
263
|
+
# dereferencing the pointer.
|
264
|
+
#
|
265
|
+
# @note This means that the object will *not* be released if it's not
|
266
|
+
# dereferenced, but that would be a leak anyways, so be sure to do so.
|
267
|
+
#
|
268
|
+
def self.CFTypeRefPointer
|
269
|
+
pointer = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INTPTR_T, free_function)
|
270
|
+
def pointer.ptr
|
271
|
+
::CoreFoundation.CFAutoRelease(super)
|
272
|
+
end
|
273
|
+
pointer
|
274
|
+
end
|
275
|
+
|
276
|
+
def self.CFAutoRelease(cf_type_reference)
|
277
|
+
cf_type_reference.free = CFRelease_function() unless cf_type_reference.null?
|
278
|
+
cf_type_reference
|
279
|
+
end
|
280
|
+
|
281
|
+
def self.CFDictionaryApplyBlock(dictionary, &applier)
|
282
|
+
param_types = [CFTypeRef, CFTypeRef, VoidPointer]
|
283
|
+
closure = Fiddle::Closure::BlockCaller.new(Void, param_types, &applier)
|
284
|
+
closure_function = Fiddle::Function.new(closure, param_types, Void)
|
285
|
+
CFDictionaryApplyFunction(dictionary, closure_function, NULL)
|
286
|
+
end
|
287
|
+
|
288
|
+
def self.CFArrayApplyBlock(array)
|
289
|
+
CFArrayGetCount(array).times do |index|
|
290
|
+
yield CFArrayGetValueAtIndex(array, index)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# @!group CFTypeRef to Ruby value conversion
|
295
|
+
#---------------------------------------------------------------------------#
|
296
|
+
|
297
|
+
def self.CFTypeRefToRubyValue(cf_type_reference)
|
298
|
+
case CFGetTypeID(cf_type_reference)
|
299
|
+
when CFStringGetTypeID()
|
300
|
+
CFStringToRubyString(cf_type_reference)
|
301
|
+
when CFDictionaryGetTypeID()
|
302
|
+
CFDictionaryToRubyHash(cf_type_reference)
|
303
|
+
when CFArrayGetTypeID()
|
304
|
+
CFArrayToRubyArray(cf_type_reference)
|
305
|
+
when CFBooleanGetTypeID()
|
306
|
+
CFBooleanToRubyBoolean(cf_type_reference)
|
307
|
+
when CFNumberGetTypeID()
|
308
|
+
CFNumberToRubyNumber(cf_type_reference)
|
309
|
+
else
|
310
|
+
description = CFStringToRubyString(CFCopyDescription(cf_type_reference))
|
311
|
+
raise TypeError, "Unknown type: #{description}"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def self.CFStringToRubyString(string)
|
316
|
+
data = CFStringCreateExternalRepresentation(NULL,
|
317
|
+
string,
|
318
|
+
KCFStringEncodingUTF8,
|
319
|
+
0)
|
320
|
+
if data.null?
|
321
|
+
raise TypeError, 'Unable to convert CFStringRef.'
|
322
|
+
end
|
323
|
+
bytes_ptr = CFDataGetBytePtr(data)
|
324
|
+
result = bytes_ptr.to_str(CFDataGetLength(data))
|
325
|
+
result.force_encoding(Encoding::UTF_8)
|
326
|
+
result
|
327
|
+
end
|
328
|
+
|
329
|
+
def self.CFDictionaryToRubyHash(dictionary)
|
330
|
+
result = {}
|
331
|
+
CFDictionaryApplyBlock(dictionary) do |key, value|
|
332
|
+
result[CFStringToRubyString(key)] = CFTypeRefToRubyValue(value)
|
333
|
+
end
|
334
|
+
result
|
335
|
+
end
|
336
|
+
|
337
|
+
def self.CFArrayToRubyArray(array)
|
338
|
+
result = []
|
339
|
+
CFArrayApplyBlock(array) do |element|
|
340
|
+
result << CFTypeRefToRubyValue(element)
|
341
|
+
end
|
342
|
+
result
|
343
|
+
end
|
344
|
+
|
345
|
+
def self.CFBooleanToRubyBoolean(boolean)
|
346
|
+
CFBooleanGetValue(boolean) == TRUE
|
347
|
+
end
|
348
|
+
|
349
|
+
def self.CFNumberToRubyNumber(number)
|
350
|
+
if CFNumberIsFloatType(number) == FALSE
|
351
|
+
value_type = KCFNumberSInt64Type
|
352
|
+
pack_template = SINT64_PACK_TEMPLATE
|
353
|
+
size = SIZEOF_SINT64
|
354
|
+
else
|
355
|
+
value_type = KCFNumberFloat64Type
|
356
|
+
pack_template = FLOAT64_PACK_TEMPLATE
|
357
|
+
size = SIZEOF_FLOAT64
|
358
|
+
end
|
359
|
+
ptr = Fiddle::Pointer.malloc(size)
|
360
|
+
CFNumberGetValue(number, value_type, ptr)
|
361
|
+
ptr.to_str.unpack(pack_template).first
|
362
|
+
end
|
363
|
+
|
364
|
+
# @!group Ruby value to CFTypeRef conversion
|
365
|
+
#---------------------------------------------------------------------------#
|
366
|
+
|
367
|
+
def self.RubyValueToCFTypeRef(value)
|
368
|
+
result = case value
|
369
|
+
when String
|
370
|
+
RubyStringToCFString(value)
|
371
|
+
when Hash
|
372
|
+
RubyHashToCFDictionary(value)
|
373
|
+
when Array
|
374
|
+
RubyArrayToCFArray(value)
|
375
|
+
when true, false
|
376
|
+
RubyBooleanToCFBoolean(value)
|
377
|
+
when Numeric
|
378
|
+
RubyNumberToCFNumber(value)
|
379
|
+
else
|
380
|
+
RubyStringToCFString(value.to_s)
|
381
|
+
end
|
382
|
+
if result.null?
|
383
|
+
raise TypeError, "Unable to convert Ruby value `#{value.inspect}' " \
|
384
|
+
'into a CFTypeRef.'
|
385
|
+
end
|
386
|
+
result
|
387
|
+
end
|
388
|
+
|
389
|
+
def self.RubyStringToCFString(string)
|
390
|
+
CFStringCreateWithCString(NULL,
|
391
|
+
Fiddle::Pointer[string.encode('UTF-8')],
|
392
|
+
KCFStringEncodingUTF8)
|
393
|
+
end
|
394
|
+
|
395
|
+
def self.RubyHashToCFDictionary(hash)
|
396
|
+
result = CFDictionaryCreateMutable(NULL,
|
397
|
+
0,
|
398
|
+
CFTypeDictionaryKeyCallBacks(),
|
399
|
+
CFTypeDictionaryValueCallBacks())
|
400
|
+
hash.each do |key, value|
|
401
|
+
key = RubyStringToCFString(key.to_s)
|
402
|
+
value = RubyValueToCFTypeRef(value)
|
403
|
+
CFDictionaryAddValue(result, key, value)
|
404
|
+
end
|
405
|
+
result
|
406
|
+
end
|
407
|
+
|
408
|
+
def self.RubyArrayToCFArray(array)
|
409
|
+
result = CFArrayCreateMutable(NULL, 0, CFTypeArrayCallBacks())
|
410
|
+
array.each do |element|
|
411
|
+
element = RubyValueToCFTypeRef(element)
|
412
|
+
CFArrayAppendValue(result, element)
|
413
|
+
end
|
414
|
+
result
|
415
|
+
end
|
416
|
+
|
417
|
+
def self.RubyNumberToCFNumber(value)
|
418
|
+
case value
|
419
|
+
when Float
|
420
|
+
value_type = KCFNumberFloat64Type
|
421
|
+
pack_template = FLOAT64_PACK_TEMPLATE
|
422
|
+
when SINT64_MIN..SINT64_MAX
|
423
|
+
value_type = KCFNumberSInt64Type
|
424
|
+
pack_template = SINT64_PACK_TEMPLATE
|
425
|
+
else # the value is too big to be stored in a CFNumber so store it as a CFString
|
426
|
+
return RubyStringToCFString(value.to_s)
|
427
|
+
end
|
428
|
+
ptr = Fiddle::Pointer.to_ptr([value].pack(pack_template))
|
429
|
+
CFNumberCreate(NULL, value_type, ptr)
|
430
|
+
end
|
431
|
+
|
432
|
+
def self.RubyBooleanToCFBoolean(value)
|
433
|
+
value ? CFBooleanTrue() : CFBooleanFalse()
|
434
|
+
end
|
435
|
+
|
436
|
+
# rubocop:enable Style/MethodName
|
437
|
+
# rubocop:enable Style/VariableName
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'fiddle'
|
2
|
+
|
3
|
+
module Xcodeproj
|
4
|
+
module Plist
|
5
|
+
module FFI
|
6
|
+
module DevToolsCore
|
7
|
+
def self.silence_stderr
|
8
|
+
begin
|
9
|
+
orig_stderr = $stderr.clone
|
10
|
+
$stderr.reopen File.new('/dev/null', 'w')
|
11
|
+
retval = yield
|
12
|
+
ensure
|
13
|
+
$stderr.reopen orig_stderr
|
14
|
+
end
|
15
|
+
retval
|
16
|
+
end
|
17
|
+
|
18
|
+
# rubocop:disable Style/MethodName
|
19
|
+
# rubocop:disable Style/VariableName
|
20
|
+
|
21
|
+
class NSObject
|
22
|
+
private
|
23
|
+
|
24
|
+
def self.objc_class
|
25
|
+
@objc_class ||= CoreFoundation.objc_getClass(name.split('::').last)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.image
|
29
|
+
@image ||= Fiddle::Handle.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.extern(symbol, parameter_types, return_type)
|
33
|
+
CoreFoundation.extern_image(image, symbol, parameter_types, return_type)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.objc_msgSend(args, return_type = CoreFoundation::VoidPointer)
|
37
|
+
arguments = [CoreFoundation::VoidPointer, CoreFoundation::VoidPointer] + args
|
38
|
+
|
39
|
+
Fiddle::Function.new(image['objc_msgSend'], arguments, return_type)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.respondsToSelector(instance, sel)
|
43
|
+
selector = CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(sel))
|
44
|
+
respondsToSelector = objc_msgSend([CoreFoundation::CharPointer], CoreFoundation::Boolean)
|
45
|
+
result = respondsToSelector.call(
|
46
|
+
instance,
|
47
|
+
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString('respondsToSelector:')),
|
48
|
+
selector)
|
49
|
+
result == CoreFoundation::TRUE ? true : false
|
50
|
+
end
|
51
|
+
|
52
|
+
Class = CoreFoundation::VoidPointer
|
53
|
+
ID = CoreFoundation::VoidPointer
|
54
|
+
SEL = CoreFoundation::VoidPointer
|
55
|
+
|
56
|
+
extern :NSSelectorFromString, [CoreFoundation::CFTypeRef], SEL
|
57
|
+
|
58
|
+
extern :objc_getClass, [CoreFoundation::CharPointer], Class
|
59
|
+
extern :class_getName, [Class], CoreFoundation::CharPointer
|
60
|
+
end
|
61
|
+
|
62
|
+
XCODE_PATH = Pathname.new(`xcrun xcode-select --print-path`.strip).dirname
|
63
|
+
|
64
|
+
def self.load_xcode_framework(framework)
|
65
|
+
Fiddle.dlopen(XCODE_PATH.join(framework).to_s)
|
66
|
+
rescue Fiddle::DLError
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
# @note The IB frameworks only seem to be necessary on Xcode 7+
|
71
|
+
#
|
72
|
+
def self.load_xcode_frameworks
|
73
|
+
DevToolsCore.silence_stderr do
|
74
|
+
load_xcode_framework('SharedFrameworks/DVTFoundation.framework/DVTFoundation')
|
75
|
+
load_xcode_framework('SharedFrameworks/DVTSourceControl.framework/DVTSourceControl')
|
76
|
+
load_xcode_framework('SharedFrameworks/CSServiceClient.framework/CSServiceClient')
|
77
|
+
load_xcode_framework('Frameworks/IBFoundation.framework/IBFoundation')
|
78
|
+
load_xcode_framework('Frameworks/IBAutolayoutFoundation.framework/IBAutolayoutFoundation')
|
79
|
+
load_xcode_framework('Frameworks/IDEFoundation.framework/IDEFoundation')
|
80
|
+
load_xcode_framework('PlugIns/Xcode3Core.ideplugin/Contents/MacOS/Xcode3Core')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class CFDictionary < NSObject
|
85
|
+
public
|
86
|
+
|
87
|
+
def initialize(dictionary)
|
88
|
+
@dictionary = dictionary
|
89
|
+
end
|
90
|
+
|
91
|
+
def plistDescriptionUTF8Data
|
92
|
+
selector = 'plistDescriptionUTF8Data'
|
93
|
+
return nil unless NSObject.respondsToSelector(@dictionary, selector)
|
94
|
+
|
95
|
+
plistDescriptionUTF8Data = CFDictionary.objc_msgSend([])
|
96
|
+
plistDescriptionUTF8Data.call(
|
97
|
+
@dictionary,
|
98
|
+
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)))
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.image
|
102
|
+
@image ||= DevToolsCore.load_xcode_frameworks
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class NSData < NSObject
|
107
|
+
public
|
108
|
+
|
109
|
+
def initialize(data)
|
110
|
+
@data = data
|
111
|
+
end
|
112
|
+
|
113
|
+
def writeToFileAtomically(path)
|
114
|
+
selector = 'writeToFile:atomically:'
|
115
|
+
return false unless NSObject.respondsToSelector(@data, selector)
|
116
|
+
|
117
|
+
writeToFileAtomically = NSData.objc_msgSend([CoreFoundation::VoidPointer, CoreFoundation::Boolean], CoreFoundation::Boolean)
|
118
|
+
result = writeToFileAtomically.call(
|
119
|
+
@data,
|
120
|
+
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)),
|
121
|
+
CoreFoundation.RubyStringToCFString(path),
|
122
|
+
1)
|
123
|
+
result == CoreFoundation::TRUE ? true : false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class PBXProject < NSObject
|
128
|
+
public
|
129
|
+
|
130
|
+
def initialize(path)
|
131
|
+
DevToolsCore.silence_stderr do
|
132
|
+
CoreFoundation.IDEInitialize(1, CoreFoundation::NULL)
|
133
|
+
|
134
|
+
# The parameter is whether UI must be initialized (which we don't need)
|
135
|
+
CoreFoundation.XCInitializeCoreIfNeeded(0)
|
136
|
+
end
|
137
|
+
|
138
|
+
selector = 'projectWithFile:'
|
139
|
+
|
140
|
+
if NSObject.respondsToSelector(PBXProject.objc_class, selector)
|
141
|
+
projectWithFile = PBXProject.objc_msgSend([CoreFoundation::VoidPointer])
|
142
|
+
@project = projectWithFile.call(
|
143
|
+
PBXProject.objc_class,
|
144
|
+
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)),
|
145
|
+
CoreFoundation.RubyStringToCFString(path))
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def close
|
150
|
+
selector = 'close'
|
151
|
+
return unless NSObject.respondsToSelector(@project, selector)
|
152
|
+
|
153
|
+
close = PBXProject.objc_msgSend([], CoreFoundation::Void)
|
154
|
+
close.call(@project, CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)))
|
155
|
+
end
|
156
|
+
|
157
|
+
def writeToFileSystemProjectFile
|
158
|
+
selector = 'writeToFileSystemProjectFile:userFile:checkNeedsRevert:'
|
159
|
+
return unless NSObject.respondsToSelector(@project, selector)
|
160
|
+
|
161
|
+
writeToFile = PBXProject.objc_msgSend([CoreFoundation::Boolean, CoreFoundation::Boolean, CoreFoundation::Boolean], CoreFoundation::Boolean)
|
162
|
+
result = writeToFile.call(
|
163
|
+
@project,
|
164
|
+
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)),
|
165
|
+
1,
|
166
|
+
0,
|
167
|
+
1)
|
168
|
+
result == CoreFoundation::TRUE ? true : false
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def self.image
|
174
|
+
@image ||= DevToolsCore.load_xcode_frameworks
|
175
|
+
end
|
176
|
+
|
177
|
+
extern :IDEInitialize, [CoreFoundation::Boolean, ID], CoreFoundation::Void
|
178
|
+
extern :XCInitializeCoreIfNeeded, [CoreFoundation::Boolean], CoreFoundation::Void
|
179
|
+
end
|
180
|
+
|
181
|
+
# rubocop:enable Style/MethodName
|
182
|
+
# rubocop:enable Style/VariableName
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|