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