xcodeproj 0.28.2 → 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|