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.
@@ -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