xcodeproj 0.28.2 → 1.0.0
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/command/config_dump.rb +2 -1
- data/lib/xcodeproj/config/other_linker_flags_parser.rb +5 -2
- data/lib/xcodeproj/config.rb +14 -5
- data/lib/xcodeproj/constants.rb +62 -27
- data/lib/xcodeproj/differ.rb +7 -7
- data/lib/xcodeproj/gem_version.rb +1 -1
- data/lib/xcodeproj/plist/ffi/core_foundation.rb +441 -0
- data/lib/xcodeproj/plist/ffi/dev_tools_core.rb +188 -0
- data/lib/xcodeproj/plist/ffi.rb +119 -0
- data/lib/xcodeproj/plist/plist_gem.rb +27 -0
- data/lib/xcodeproj/plist.rb +89 -0
- data/lib/xcodeproj/project/object/build_configuration.rb +15 -0
- data/lib/xcodeproj/project/object/build_rule.rb +28 -0
- 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.rb +16 -3
- 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 +16 -4
- data/lib/xcodeproj/project/project_helper.rb +1 -1
- data/lib/xcodeproj/project/uuid_generator.rb +1 -0
- data/lib/xcodeproj/project.rb +36 -12
- data/lib/xcodeproj/scheme/build_action.rb +5 -2
- 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/scheme.rb +5 -5
- data/lib/xcodeproj/workspace/file_reference.rb +2 -3
- data/lib/xcodeproj/workspace/group_reference.rb +64 -0
- data/lib/xcodeproj/workspace.rb +127 -34
- data/lib/xcodeproj.rb +1 -1
- metadata +29 -17
- data/lib/xcodeproj/plist_helper.rb +0 -758
|
@@ -1,758 +0,0 @@
|
|
|
1
|
-
begin
|
|
2
|
-
require 'fiddle'
|
|
3
|
-
rescue LoadError
|
|
4
|
-
message = 'Xcodeproj relies on a library called `fiddle` to read and write ' \
|
|
5
|
-
'Xcode project files. Ensure your Ruby installation includes ' \
|
|
6
|
-
'`fiddle` and try again.'
|
|
7
|
-
raise Xcodeproj::Informative, message
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
module Xcodeproj
|
|
11
|
-
# TODO: Delete me (compatibility with Ruby 1.8.7 C ext bundle)
|
|
12
|
-
def self.read_plist(path)
|
|
13
|
-
PlistHelper.read(path)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# TODO: Delete me (compatibility with Ruby 1.8.7 C ext bundle)
|
|
17
|
-
def self.write_plist(hash, path)
|
|
18
|
-
PlistHelper.write(hash, path)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Provides support for loading and serializing property list files.
|
|
22
|
-
#
|
|
23
|
-
module PlistHelper
|
|
24
|
-
class << self
|
|
25
|
-
# Serializes a hash as an XML property list file.
|
|
26
|
-
#
|
|
27
|
-
# @param [#to_hash] hash
|
|
28
|
-
# The hash to store.
|
|
29
|
-
#
|
|
30
|
-
# @param [#to_s] path
|
|
31
|
-
# The path of the file.
|
|
32
|
-
#
|
|
33
|
-
def write(possible_hash, path)
|
|
34
|
-
if possible_hash.respond_to?(:to_hash)
|
|
35
|
-
hash = possible_hash.to_hash
|
|
36
|
-
else
|
|
37
|
-
raise TypeError, "The given `#{possible_hash.inspect}` must respond " \
|
|
38
|
-
"to #to_hash'."
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
unless path.is_a?(String) || path.is_a?(Pathname)
|
|
42
|
-
raise TypeError, "The given `#{path}` must be a string or 'pathname'."
|
|
43
|
-
end
|
|
44
|
-
path = path.to_s
|
|
45
|
-
raise IOError, 'Empty path.' if path == ''
|
|
46
|
-
|
|
47
|
-
if DevToolsCore.load_xcode_frameworks && path.end_with?('pbxproj')
|
|
48
|
-
ruby_hash_write_xcode(hash, path)
|
|
49
|
-
else
|
|
50
|
-
CoreFoundation.RubyHashPropertyListWrite(hash, path)
|
|
51
|
-
fix_encoding(path)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# @return [Hash] Returns the native objects loaded from a property list
|
|
56
|
-
# file.
|
|
57
|
-
#
|
|
58
|
-
# @param [#to_s] path
|
|
59
|
-
# The path of the file.
|
|
60
|
-
#
|
|
61
|
-
def read(path)
|
|
62
|
-
path = path.to_s
|
|
63
|
-
unless File.exist?(path)
|
|
64
|
-
raise Informative, "The plist file at path `#{path}` doesn't exist."
|
|
65
|
-
end
|
|
66
|
-
if file_in_conflict?(path)
|
|
67
|
-
raise Informative, "The file `#{path}` is in a merge conflict."
|
|
68
|
-
end
|
|
69
|
-
CoreFoundation.RubyHashPropertyListRead(path)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
private
|
|
73
|
-
|
|
74
|
-
# Simple workaround to escape characters which are outside of ASCII
|
|
75
|
-
# character-encoding. Relies on the fact that there are no XML characters
|
|
76
|
-
# which would need to be escaped.
|
|
77
|
-
#
|
|
78
|
-
# @note This is necessary because Xcode (4.6 currently) uses the MacRoman
|
|
79
|
-
# encoding unless the `// !$*UTF8*$!` magic comment is present. It
|
|
80
|
-
# is not possible to serialize a plist using the NeXTSTEP format
|
|
81
|
-
# without access to the private classes of Xcode and that comment
|
|
82
|
-
# is not compatible with the XML format. For the complete
|
|
83
|
-
# discussion see CocoaPods/CocoaPods#926.
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
# @note Sadly this hack is not sufficient for supporting Emoji.
|
|
87
|
-
#
|
|
88
|
-
# @param [String, Pathname] The path of the file which needs to be fixed.
|
|
89
|
-
#
|
|
90
|
-
# @return [void]
|
|
91
|
-
#
|
|
92
|
-
def fix_encoding(filename)
|
|
93
|
-
output = ''
|
|
94
|
-
input = File.open(filename, 'rb') { |file| file.read }
|
|
95
|
-
input.unpack('U*').each do |codepoint|
|
|
96
|
-
if codepoint > 127 # ASCII is 7-bit, so 0-127 are valid characters
|
|
97
|
-
output << "&##{codepoint};"
|
|
98
|
-
else
|
|
99
|
-
output << codepoint.chr
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
File.open(filename, 'wb') { |file| file.write(output) }
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# @return [Bool] Checks whether there are merge conflicts in the file.
|
|
106
|
-
#
|
|
107
|
-
# @param [#to_s] path
|
|
108
|
-
# The path of the file.
|
|
109
|
-
#
|
|
110
|
-
def file_in_conflict?(path)
|
|
111
|
-
file = File.open(path)
|
|
112
|
-
file.each_line.any? { |l| l.match(/^(<|=|>){7}/) }
|
|
113
|
-
ensure
|
|
114
|
-
file.close
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
# Serializes a hash as an ASCII plist, using Xcode.
|
|
118
|
-
#
|
|
119
|
-
# @param [Hash] hash
|
|
120
|
-
# The hash to store.
|
|
121
|
-
#
|
|
122
|
-
# @param [String] path
|
|
123
|
-
# The path of the file.
|
|
124
|
-
#
|
|
125
|
-
def ruby_hash_write_xcode(hash, path)
|
|
126
|
-
path = File.expand_path(path)
|
|
127
|
-
success = true
|
|
128
|
-
|
|
129
|
-
begin
|
|
130
|
-
plist = DevToolsCore::CFDictionary.new(CoreFoundation.RubyHashToCFDictionary(hash))
|
|
131
|
-
data = DevToolsCore::NSData.new(plist.plistDescriptionUTF8Data)
|
|
132
|
-
success &= data.writeToFileAtomically(path)
|
|
133
|
-
|
|
134
|
-
project = DevToolsCore::PBXProject.new(path)
|
|
135
|
-
success &= project.writeToFileSystemProjectFile
|
|
136
|
-
project.close
|
|
137
|
-
rescue Fiddle::DLError
|
|
138
|
-
success = false
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
CoreFoundation.RubyHashPropertyListWrite(hash, path) unless success
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
# This module provides an interface to the CoreFoundation OS X framework.
|
|
148
|
-
# Specifically it bridges the functions required to be able to read and write
|
|
149
|
-
# property lists.
|
|
150
|
-
#
|
|
151
|
-
# Everything in here should be considered an implementation detail and thus is
|
|
152
|
-
# not further documented.
|
|
153
|
-
#
|
|
154
|
-
# @todo Move this out into its own gem.
|
|
155
|
-
#
|
|
156
|
-
# @!visibility private
|
|
157
|
-
#
|
|
158
|
-
module CoreFoundation
|
|
159
|
-
PATH = '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation'
|
|
160
|
-
|
|
161
|
-
# rubocop:disable Style/MethodName
|
|
162
|
-
# rubocop:disable Style/VariableName
|
|
163
|
-
|
|
164
|
-
# @!group Ruby hash as property list (de)serialization
|
|
165
|
-
#---------------------------------------------------------------------------#
|
|
166
|
-
|
|
167
|
-
def self.RubyHashPropertyListWrite(hash, path)
|
|
168
|
-
url = CFURLCreateFromFileSystemRepresentation(NULL,
|
|
169
|
-
path,
|
|
170
|
-
path.bytesize,
|
|
171
|
-
FALSE)
|
|
172
|
-
stream = CFWriteStreamCreateWithFile(NULL, url)
|
|
173
|
-
unless CFWriteStreamOpen(stream) == TRUE
|
|
174
|
-
raise IOError, 'Unable to open stream.'
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
plist = RubyHashToCFDictionary(hash)
|
|
178
|
-
|
|
179
|
-
error_ptr = CFTypeRefPointer()
|
|
180
|
-
result = CFPropertyListWrite(plist,
|
|
181
|
-
stream,
|
|
182
|
-
KCFPropertyListXMLFormat_v1_0,
|
|
183
|
-
0,
|
|
184
|
-
error_ptr)
|
|
185
|
-
CFWriteStreamClose(stream)
|
|
186
|
-
|
|
187
|
-
if result == 0
|
|
188
|
-
description = CFCopyDescription(error_ptr.ptr)
|
|
189
|
-
raise IOError, "Unable to write plist data: #{description}"
|
|
190
|
-
end
|
|
191
|
-
result
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def self.RubyHashPropertyListRead(path)
|
|
195
|
-
url = CFURLCreateFromFileSystemRepresentation(NULL,
|
|
196
|
-
path,
|
|
197
|
-
path.bytesize,
|
|
198
|
-
FALSE)
|
|
199
|
-
stream = CFReadStreamCreateWithFile(NULL, url)
|
|
200
|
-
unless CFReadStreamOpen(stream) == TRUE
|
|
201
|
-
raise IOError, 'Unable to open stream.'
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
error_ptr = CFTypeRefPointer()
|
|
205
|
-
plist = CFPropertyListCreateWithStream(NULL,
|
|
206
|
-
stream,
|
|
207
|
-
0,
|
|
208
|
-
KCFPropertyListImmutable,
|
|
209
|
-
NULL,
|
|
210
|
-
error_ptr)
|
|
211
|
-
CFReadStreamClose(stream)
|
|
212
|
-
|
|
213
|
-
if plist.null?
|
|
214
|
-
description = CFCopyDescription(error_ptr.ptr)
|
|
215
|
-
raise IOError, "Unable to read plist data: #{description}"
|
|
216
|
-
elsif CFGetTypeID(plist) != CFDictionaryGetTypeID()
|
|
217
|
-
raise TypeError, 'Expected a plist with a dictionary root object.'
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
CFDictionaryToRubyHash(plist)
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
# @!group Types
|
|
224
|
-
#---------------------------------------------------------------------------#
|
|
225
|
-
|
|
226
|
-
# rubocop:disable Style/ConstantName
|
|
227
|
-
|
|
228
|
-
NULL = Fiddle::NULL
|
|
229
|
-
|
|
230
|
-
Void = Fiddle::TYPE_VOID
|
|
231
|
-
VoidPointer = Fiddle::TYPE_VOIDP
|
|
232
|
-
FunctionPointer = VoidPointer
|
|
233
|
-
|
|
234
|
-
UInt32 = -Fiddle::TYPE_INT
|
|
235
|
-
UInt8 = -Fiddle::TYPE_CHAR
|
|
236
|
-
|
|
237
|
-
SInt32Pointer = VoidPointer
|
|
238
|
-
UInt8Pointer = VoidPointer
|
|
239
|
-
CharPointer = VoidPointer
|
|
240
|
-
|
|
241
|
-
Boolean = Fiddle::TYPE_CHAR
|
|
242
|
-
TRUE = 1
|
|
243
|
-
FALSE = 0
|
|
244
|
-
|
|
245
|
-
SINT64_MAX = 2**63 - 1
|
|
246
|
-
SINT64_MIN = -SINT64_MAX - 1
|
|
247
|
-
|
|
248
|
-
SIZEOF_SINT64 = 8
|
|
249
|
-
SIZEOF_FLOAT64 = 8
|
|
250
|
-
|
|
251
|
-
SINT64_PACK_TEMPLATE = 'q'
|
|
252
|
-
FLOAT64_PACK_TEMPLATE = 'd'
|
|
253
|
-
|
|
254
|
-
CFTypeRef = VoidPointer
|
|
255
|
-
CFTypeRefPointer = VoidPointer
|
|
256
|
-
CFIndex = Fiddle::TYPE_LONG
|
|
257
|
-
CFTypeID = -Fiddle::TYPE_LONG
|
|
258
|
-
CFOptionFlags = UInt32
|
|
259
|
-
|
|
260
|
-
CFPropertyListMutabilityOptions = Fiddle::TYPE_INT
|
|
261
|
-
KCFPropertyListImmutable = 0
|
|
262
|
-
|
|
263
|
-
CFPropertyListFormat = Fiddle::TYPE_INT
|
|
264
|
-
KCFPropertyListXMLFormat_v1_0 = 100
|
|
265
|
-
CFPropertyListFormatPointer = VoidPointer
|
|
266
|
-
|
|
267
|
-
CFStringEncoding = UInt32
|
|
268
|
-
KCFStringEncodingUTF8 = 0x08000100
|
|
269
|
-
|
|
270
|
-
CFNumberType = Fiddle::TYPE_INT
|
|
271
|
-
KCFNumberSInt64Type = 4
|
|
272
|
-
KCFNumberFloat64Type = 6
|
|
273
|
-
|
|
274
|
-
# rubocop:enable Style/ConstantName
|
|
275
|
-
|
|
276
|
-
private
|
|
277
|
-
|
|
278
|
-
# @!group Helpers
|
|
279
|
-
#---------------------------------------------------------------------------#
|
|
280
|
-
|
|
281
|
-
def self.image
|
|
282
|
-
@image ||= Fiddle.dlopen(PATH)
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
def self.free_function
|
|
286
|
-
@free_function ||= Fiddle::Function.new(Fiddle::Handle.new['free'], [VoidPointer], Void)
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def self.CFRelease_function
|
|
290
|
-
@CFRelease ||= Fiddle::Function.new(image['CFRelease'], [CFTypeRef], Void)
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
def self.extern_image(image, symbol, parameter_types, return_type)
|
|
294
|
-
symbol = symbol.to_s
|
|
295
|
-
create_function = symbol.include?('Create')
|
|
296
|
-
function_cache_key = "@__#{symbol}__"
|
|
297
|
-
|
|
298
|
-
# Define a singleton method on the CoreFoundation module.
|
|
299
|
-
define_singleton_method(symbol) do |*args|
|
|
300
|
-
unless args.size == parameter_types.size
|
|
301
|
-
raise ArgumentError, "wrong number of arguments (#{args.size} for " \
|
|
302
|
-
"#{parameter_types.size})"
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
unless function = instance_variable_get(function_cache_key)
|
|
306
|
-
function = Fiddle::Function.new(image[symbol],
|
|
307
|
-
parameter_types,
|
|
308
|
-
return_type)
|
|
309
|
-
instance_variable_set(function_cache_key, function)
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
result = function.call(*args)
|
|
313
|
-
create_function ? CFAutoRelease(result) : result
|
|
314
|
-
end
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
def self.extern(symbol, parameter_types, return_type)
|
|
318
|
-
extern_image(image, symbol, parameter_types, return_type)
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
public
|
|
322
|
-
|
|
323
|
-
# @!group CoreFoundation function definitions
|
|
324
|
-
#---------------------------------------------------------------------------#
|
|
325
|
-
|
|
326
|
-
# CFTypeRef description
|
|
327
|
-
extern :CFShow, [CFTypeRef], Void
|
|
328
|
-
extern :CFCopyDescription, [CFTypeRef], CFTypeRef
|
|
329
|
-
|
|
330
|
-
# CFType reflection
|
|
331
|
-
extern :CFGetTypeID, [CFTypeRef], CFTypeID
|
|
332
|
-
extern :CFDictionaryGetTypeID, [], CFTypeID
|
|
333
|
-
extern :CFStringGetTypeID, [], CFTypeID
|
|
334
|
-
extern :CFArrayGetTypeID, [], CFTypeID
|
|
335
|
-
extern :CFBooleanGetTypeID, [], CFTypeID
|
|
336
|
-
extern :CFNumberGetTypeID, [], CFTypeID
|
|
337
|
-
|
|
338
|
-
# CFStream
|
|
339
|
-
extern :CFWriteStreamCreateWithFile, [CFTypeRef, CFTypeRef], CFTypeRef
|
|
340
|
-
extern :CFWriteStreamOpen, [CFTypeRef], Boolean
|
|
341
|
-
extern :CFWriteStreamClose, [CFTypeRef], Void
|
|
342
|
-
extern :CFReadStreamCreateWithFile, [CFTypeRef, CFTypeRef], CFTypeRef
|
|
343
|
-
extern :CFReadStreamOpen, [CFTypeRef], Boolean
|
|
344
|
-
extern :CFReadStreamClose, [CFTypeRef], Void
|
|
345
|
-
|
|
346
|
-
# CFURL
|
|
347
|
-
extern :CFURLCreateFromFileSystemRepresentation, [CFTypeRef, UInt8Pointer, CFIndex, Boolean], CFTypeRef
|
|
348
|
-
|
|
349
|
-
# CFPropertyList
|
|
350
|
-
extern :CFPropertyListWrite, [CFTypeRef, CFTypeRef, CFPropertyListFormat, CFOptionFlags, CFTypeRefPointer], CFIndex
|
|
351
|
-
extern :CFPropertyListCreateWithStream, [CFTypeRef, CFTypeRef, CFIndex, CFOptionFlags, CFPropertyListFormatPointer, CFTypeRefPointer], CFTypeRef
|
|
352
|
-
|
|
353
|
-
# CFString
|
|
354
|
-
extern :CFStringCreateExternalRepresentation, [CFTypeRef, CFTypeRef, CFStringEncoding, UInt8], CFTypeRef
|
|
355
|
-
extern :CFStringCreateWithCString, [CFTypeRef, CharPointer, CFStringEncoding], CFTypeRef
|
|
356
|
-
|
|
357
|
-
# CFData
|
|
358
|
-
extern :CFDataGetLength, [CFTypeRef], CFIndex
|
|
359
|
-
extern :CFDataGetBytePtr, [CFTypeRef], VoidPointer
|
|
360
|
-
|
|
361
|
-
# CFDictionary
|
|
362
|
-
extern :CFDictionaryCreateMutable, [CFTypeRef, CFIndex, VoidPointer, VoidPointer], CFTypeRef
|
|
363
|
-
extern :CFDictionaryAddValue, [CFTypeRef, CFTypeRef, CFTypeRef], VoidPointer
|
|
364
|
-
extern :CFDictionaryApplyFunction, [CFTypeRef, FunctionPointer, VoidPointer], Void
|
|
365
|
-
|
|
366
|
-
# CFArray
|
|
367
|
-
extern :CFArrayCreateMutable, [CFTypeRef, CFIndex, VoidPointer], CFTypeRef
|
|
368
|
-
extern :CFArrayAppendValue, [CFTypeRef, CFTypeRef], VoidPointer
|
|
369
|
-
extern :CFArrayGetCount, [CFTypeRef], CFIndex
|
|
370
|
-
extern :CFArrayGetValueAtIndex, [CFTypeRef, CFIndex], CFTypeRef
|
|
371
|
-
|
|
372
|
-
# CFBoolean
|
|
373
|
-
extern :CFBooleanGetValue, [CFTypeRef], Boolean
|
|
374
|
-
|
|
375
|
-
# CFNumber
|
|
376
|
-
extern :CFNumberIsFloatType, [CFTypeRef], Boolean
|
|
377
|
-
extern :CFNumberGetValue, [CFTypeRef, CFNumberType, VoidPointer], Boolean
|
|
378
|
-
extern :CFNumberCreate, [CFTypeRef, CFNumberType, VoidPointer], CFTypeRef
|
|
379
|
-
|
|
380
|
-
# @!group Custom convenience functions
|
|
381
|
-
#---------------------------------------------------------------------------#
|
|
382
|
-
|
|
383
|
-
def self.CFBooleanTrue
|
|
384
|
-
@CFBooleanTrue ||= Fiddle::Pointer.new(image['kCFBooleanTrue']).ptr
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
def self.CFBooleanFalse
|
|
388
|
-
@CFBooleanFalse ||= Fiddle::Pointer.new(image['kCFBooleanFalse']).ptr
|
|
389
|
-
end
|
|
390
|
-
|
|
391
|
-
def self.CFTypeArrayCallBacks
|
|
392
|
-
@CFTypeArrayCallBacks ||= image['kCFTypeArrayCallBacks']
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
def self.CFTypeDictionaryKeyCallBacks
|
|
396
|
-
@CFTypeDictionaryKeyCallBacks ||= image['kCFTypeDictionaryKeyCallBacks']
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
def self.CFTypeDictionaryValueCallBacks
|
|
400
|
-
@CFTypeDictionaryValueCallBacks ||= image['kCFTypeDictionaryValueCallBacks']
|
|
401
|
-
end
|
|
402
|
-
|
|
403
|
-
# This pointer will assign `CFRelease` as the free function when
|
|
404
|
-
# dereferencing the pointer.
|
|
405
|
-
#
|
|
406
|
-
# @note This means that the object will *not* be released if it's not
|
|
407
|
-
# dereferenced, but that would be a leak anyways, so be sure to do so.
|
|
408
|
-
#
|
|
409
|
-
def self.CFTypeRefPointer
|
|
410
|
-
pointer = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INTPTR_T, free_function)
|
|
411
|
-
def pointer.ptr
|
|
412
|
-
::CoreFoundation.CFAutoRelease(super)
|
|
413
|
-
end
|
|
414
|
-
pointer
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
def self.CFAutoRelease(cf_type_reference)
|
|
418
|
-
cf_type_reference.free = CFRelease_function() unless cf_type_reference.null?
|
|
419
|
-
cf_type_reference
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
def self.CFDictionaryApplyBlock(dictionary, &applier)
|
|
423
|
-
param_types = [CFTypeRef, CFTypeRef, VoidPointer]
|
|
424
|
-
closure = Fiddle::Closure::BlockCaller.new(Void, param_types, &applier)
|
|
425
|
-
closure_function = Fiddle::Function.new(closure, param_types, Void)
|
|
426
|
-
CFDictionaryApplyFunction(dictionary, closure_function, NULL)
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
def self.CFArrayApplyBlock(array)
|
|
430
|
-
CFArrayGetCount(array).times do |index|
|
|
431
|
-
yield CFArrayGetValueAtIndex(array, index)
|
|
432
|
-
end
|
|
433
|
-
end
|
|
434
|
-
|
|
435
|
-
# @!group CFTypeRef to Ruby value conversion
|
|
436
|
-
#---------------------------------------------------------------------------#
|
|
437
|
-
|
|
438
|
-
def self.CFTypeRefToRubyValue(cf_type_reference)
|
|
439
|
-
case CFGetTypeID(cf_type_reference)
|
|
440
|
-
when CFStringGetTypeID()
|
|
441
|
-
CFStringToRubyString(cf_type_reference)
|
|
442
|
-
when CFDictionaryGetTypeID()
|
|
443
|
-
CFDictionaryToRubyHash(cf_type_reference)
|
|
444
|
-
when CFArrayGetTypeID()
|
|
445
|
-
CFArrayToRubyArray(cf_type_reference)
|
|
446
|
-
when CFBooleanGetTypeID()
|
|
447
|
-
CFBooleanToRubyBoolean(cf_type_reference)
|
|
448
|
-
when CFNumberGetTypeID()
|
|
449
|
-
CFNumberToRubyNumber(cf_type_reference)
|
|
450
|
-
else
|
|
451
|
-
description = CFStringToRubyString(CFCopyDescription(cf_type_reference))
|
|
452
|
-
raise TypeError, "Unknown type: #{description}"
|
|
453
|
-
end
|
|
454
|
-
end
|
|
455
|
-
|
|
456
|
-
def self.CFStringToRubyString(string)
|
|
457
|
-
data = CFStringCreateExternalRepresentation(NULL,
|
|
458
|
-
string,
|
|
459
|
-
KCFStringEncodingUTF8,
|
|
460
|
-
0)
|
|
461
|
-
if data.null?
|
|
462
|
-
raise TypeError, 'Unable to convert CFStringRef.'
|
|
463
|
-
end
|
|
464
|
-
bytes_ptr = CFDataGetBytePtr(data)
|
|
465
|
-
result = bytes_ptr.to_str(CFDataGetLength(data))
|
|
466
|
-
result.force_encoding(Encoding::UTF_8)
|
|
467
|
-
result
|
|
468
|
-
end
|
|
469
|
-
|
|
470
|
-
def self.CFDictionaryToRubyHash(dictionary)
|
|
471
|
-
result = {}
|
|
472
|
-
CFDictionaryApplyBlock(dictionary) do |key, value|
|
|
473
|
-
result[CFStringToRubyString(key)] = CFTypeRefToRubyValue(value)
|
|
474
|
-
end
|
|
475
|
-
result
|
|
476
|
-
end
|
|
477
|
-
|
|
478
|
-
def self.CFArrayToRubyArray(array)
|
|
479
|
-
result = []
|
|
480
|
-
CFArrayApplyBlock(array) do |element|
|
|
481
|
-
result << CFTypeRefToRubyValue(element)
|
|
482
|
-
end
|
|
483
|
-
result
|
|
484
|
-
end
|
|
485
|
-
|
|
486
|
-
def self.CFBooleanToRubyBoolean(boolean)
|
|
487
|
-
CFBooleanGetValue(boolean) == TRUE
|
|
488
|
-
end
|
|
489
|
-
|
|
490
|
-
def self.CFNumberToRubyNumber(number)
|
|
491
|
-
if CFNumberIsFloatType(number) == FALSE
|
|
492
|
-
value_type = KCFNumberSInt64Type
|
|
493
|
-
pack_template = SINT64_PACK_TEMPLATE
|
|
494
|
-
size = SIZEOF_SINT64
|
|
495
|
-
else
|
|
496
|
-
value_type = KCFNumberFloat64Type
|
|
497
|
-
pack_template = FLOAT64_PACK_TEMPLATE
|
|
498
|
-
size = SIZEOF_FLOAT64
|
|
499
|
-
end
|
|
500
|
-
ptr = Fiddle::Pointer.malloc(size)
|
|
501
|
-
CFNumberGetValue(number, value_type, ptr)
|
|
502
|
-
ptr.to_str.unpack(pack_template).first
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
# @!group Ruby value to CFTypeRef conversion
|
|
506
|
-
#---------------------------------------------------------------------------#
|
|
507
|
-
|
|
508
|
-
def self.RubyValueToCFTypeRef(value)
|
|
509
|
-
result = case value
|
|
510
|
-
when String
|
|
511
|
-
RubyStringToCFString(value)
|
|
512
|
-
when Hash
|
|
513
|
-
RubyHashToCFDictionary(value)
|
|
514
|
-
when Array
|
|
515
|
-
RubyArrayToCFArray(value)
|
|
516
|
-
when true, false
|
|
517
|
-
RubyBooleanToCFBoolean(value)
|
|
518
|
-
when Numeric
|
|
519
|
-
RubyNumberToCFNumber(value)
|
|
520
|
-
else
|
|
521
|
-
RubyStringToCFString(value.to_s)
|
|
522
|
-
end
|
|
523
|
-
if result.null?
|
|
524
|
-
raise TypeError, "Unable to convert Ruby value `#{value.inspect}' " \
|
|
525
|
-
'into a CFTypeRef.'
|
|
526
|
-
end
|
|
527
|
-
result
|
|
528
|
-
end
|
|
529
|
-
|
|
530
|
-
def self.RubyStringToCFString(string)
|
|
531
|
-
CFStringCreateWithCString(NULL,
|
|
532
|
-
Fiddle::Pointer[string],
|
|
533
|
-
KCFStringEncodingUTF8)
|
|
534
|
-
end
|
|
535
|
-
|
|
536
|
-
def self.RubyHashToCFDictionary(hash)
|
|
537
|
-
result = CFDictionaryCreateMutable(NULL,
|
|
538
|
-
0,
|
|
539
|
-
CFTypeDictionaryKeyCallBacks(),
|
|
540
|
-
CFTypeDictionaryValueCallBacks())
|
|
541
|
-
hash.each do |key, value|
|
|
542
|
-
key = RubyStringToCFString(key.to_s)
|
|
543
|
-
value = RubyValueToCFTypeRef(value)
|
|
544
|
-
CFDictionaryAddValue(result, key, value)
|
|
545
|
-
end
|
|
546
|
-
result
|
|
547
|
-
end
|
|
548
|
-
|
|
549
|
-
def self.RubyArrayToCFArray(array)
|
|
550
|
-
result = CFArrayCreateMutable(NULL, 0, CFTypeArrayCallBacks())
|
|
551
|
-
array.each do |element|
|
|
552
|
-
element = RubyValueToCFTypeRef(element)
|
|
553
|
-
CFArrayAppendValue(result, element)
|
|
554
|
-
end
|
|
555
|
-
result
|
|
556
|
-
end
|
|
557
|
-
|
|
558
|
-
def self.RubyNumberToCFNumber(value)
|
|
559
|
-
case value
|
|
560
|
-
when Float
|
|
561
|
-
value_type = KCFNumberFloat64Type
|
|
562
|
-
pack_template = FLOAT64_PACK_TEMPLATE
|
|
563
|
-
when SINT64_MIN..SINT64_MAX
|
|
564
|
-
value_type = KCFNumberSInt64Type
|
|
565
|
-
pack_template = SINT64_PACK_TEMPLATE
|
|
566
|
-
else # the value is too big to be stored in a CFNumber so store it as a CFString
|
|
567
|
-
return RubyStringToCFString(value.to_s)
|
|
568
|
-
end
|
|
569
|
-
ptr = Fiddle::Pointer.to_ptr([value].pack(pack_template))
|
|
570
|
-
CFNumberCreate(NULL, value_type, ptr)
|
|
571
|
-
end
|
|
572
|
-
|
|
573
|
-
def self.RubyBooleanToCFBoolean(value)
|
|
574
|
-
value ? CFBooleanTrue() : CFBooleanFalse()
|
|
575
|
-
end
|
|
576
|
-
|
|
577
|
-
# rubocop:enable Style/MethodName
|
|
578
|
-
# rubocop:enable Style/VariableName
|
|
579
|
-
end
|
|
580
|
-
|
|
581
|
-
module DevToolsCore
|
|
582
|
-
def self.silence_stderr
|
|
583
|
-
begin
|
|
584
|
-
orig_stderr = $stderr.clone
|
|
585
|
-
$stderr.reopen File.new('/dev/null', 'w')
|
|
586
|
-
retval = yield
|
|
587
|
-
ensure
|
|
588
|
-
$stderr.reopen orig_stderr
|
|
589
|
-
end
|
|
590
|
-
retval
|
|
591
|
-
end
|
|
592
|
-
|
|
593
|
-
# rubocop:disable Style/MethodName
|
|
594
|
-
# rubocop:disable Style/VariableName
|
|
595
|
-
|
|
596
|
-
class NSObject
|
|
597
|
-
private
|
|
598
|
-
|
|
599
|
-
def self.objc_class
|
|
600
|
-
@objc_class ||= CoreFoundation.objc_getClass(name.split('::').last)
|
|
601
|
-
end
|
|
602
|
-
|
|
603
|
-
def self.image
|
|
604
|
-
@image ||= Fiddle::Handle.new
|
|
605
|
-
end
|
|
606
|
-
|
|
607
|
-
def self.extern(symbol, parameter_types, return_type)
|
|
608
|
-
CoreFoundation.extern_image(image, symbol, parameter_types, return_type)
|
|
609
|
-
end
|
|
610
|
-
|
|
611
|
-
def self.objc_msgSend(args, return_type = CoreFoundation::VoidPointer)
|
|
612
|
-
arguments = [CoreFoundation::VoidPointer, CoreFoundation::VoidPointer] + args
|
|
613
|
-
|
|
614
|
-
Fiddle::Function.new(image['objc_msgSend'], arguments, return_type)
|
|
615
|
-
end
|
|
616
|
-
|
|
617
|
-
def self.respondsToSelector(instance, sel)
|
|
618
|
-
selector = CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(sel))
|
|
619
|
-
respondsToSelector = objc_msgSend([CoreFoundation::CharPointer], CoreFoundation::Boolean)
|
|
620
|
-
result = respondsToSelector.call(
|
|
621
|
-
instance,
|
|
622
|
-
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString('respondsToSelector:')),
|
|
623
|
-
selector)
|
|
624
|
-
result == CoreFoundation::TRUE ? true : false
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
Class = CoreFoundation::VoidPointer
|
|
628
|
-
ID = CoreFoundation::VoidPointer
|
|
629
|
-
SEL = CoreFoundation::VoidPointer
|
|
630
|
-
|
|
631
|
-
extern :NSSelectorFromString, [CoreFoundation::CFTypeRef], SEL
|
|
632
|
-
|
|
633
|
-
extern :objc_getClass, [CoreFoundation::CharPointer], Class
|
|
634
|
-
extern :class_getName, [Class], CoreFoundation::CharPointer
|
|
635
|
-
end
|
|
636
|
-
|
|
637
|
-
XCODE_PATH = Pathname.new(`xcrun xcode-select --print-path`.strip).dirname
|
|
638
|
-
|
|
639
|
-
def self.load_xcode_framework(framework)
|
|
640
|
-
Fiddle.dlopen(XCODE_PATH.join(framework).to_s)
|
|
641
|
-
rescue Fiddle::DLError
|
|
642
|
-
nil
|
|
643
|
-
end
|
|
644
|
-
|
|
645
|
-
# @note The IB frameworks only seem to be necessary on Xcode 7+
|
|
646
|
-
#
|
|
647
|
-
def self.load_xcode_frameworks
|
|
648
|
-
DevToolsCore.silence_stderr do
|
|
649
|
-
load_xcode_framework('SharedFrameworks/DVTFoundation.framework/DVTFoundation')
|
|
650
|
-
load_xcode_framework('SharedFrameworks/DVTSourceControl.framework/DVTSourceControl')
|
|
651
|
-
load_xcode_framework('SharedFrameworks/CSServiceClient.framework/CSServiceClient')
|
|
652
|
-
load_xcode_framework('Frameworks/IBFoundation.framework/IBFoundation')
|
|
653
|
-
load_xcode_framework('Frameworks/IBAutolayoutFoundation.framework/IBAutolayoutFoundation')
|
|
654
|
-
load_xcode_framework('Frameworks/IDEFoundation.framework/IDEFoundation')
|
|
655
|
-
load_xcode_framework('PlugIns/Xcode3Core.ideplugin/Contents/MacOS/Xcode3Core')
|
|
656
|
-
end
|
|
657
|
-
end
|
|
658
|
-
|
|
659
|
-
class CFDictionary < NSObject
|
|
660
|
-
public
|
|
661
|
-
|
|
662
|
-
def initialize(dictionary)
|
|
663
|
-
@dictionary = dictionary
|
|
664
|
-
end
|
|
665
|
-
|
|
666
|
-
def plistDescriptionUTF8Data
|
|
667
|
-
selector = 'plistDescriptionUTF8Data'
|
|
668
|
-
return nil unless NSObject.respondsToSelector(@dictionary, selector)
|
|
669
|
-
|
|
670
|
-
plistDescriptionUTF8Data = CFDictionary.objc_msgSend([])
|
|
671
|
-
plistDescriptionUTF8Data.call(
|
|
672
|
-
@dictionary,
|
|
673
|
-
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)))
|
|
674
|
-
end
|
|
675
|
-
|
|
676
|
-
def self.image
|
|
677
|
-
@image ||= DevToolsCore.load_xcode_frameworks
|
|
678
|
-
end
|
|
679
|
-
end
|
|
680
|
-
|
|
681
|
-
class NSData < NSObject
|
|
682
|
-
public
|
|
683
|
-
|
|
684
|
-
def initialize(data)
|
|
685
|
-
@data = data
|
|
686
|
-
end
|
|
687
|
-
|
|
688
|
-
def writeToFileAtomically(path)
|
|
689
|
-
selector = 'writeToFile:atomically:'
|
|
690
|
-
return false unless NSObject.respondsToSelector(@data, selector)
|
|
691
|
-
|
|
692
|
-
writeToFileAtomically = NSData.objc_msgSend([CoreFoundation::VoidPointer, CoreFoundation::Boolean], CoreFoundation::Boolean)
|
|
693
|
-
result = writeToFileAtomically.call(
|
|
694
|
-
@data,
|
|
695
|
-
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)),
|
|
696
|
-
CoreFoundation.RubyStringToCFString(path),
|
|
697
|
-
1)
|
|
698
|
-
result == CoreFoundation::TRUE ? true : false
|
|
699
|
-
end
|
|
700
|
-
end
|
|
701
|
-
|
|
702
|
-
class PBXProject < NSObject
|
|
703
|
-
public
|
|
704
|
-
|
|
705
|
-
def initialize(path)
|
|
706
|
-
DevToolsCore.silence_stderr do
|
|
707
|
-
CoreFoundation.IDEInitialize(1, CoreFoundation::NULL)
|
|
708
|
-
|
|
709
|
-
# The parameter is whether UI must be initialized (which we don't need)
|
|
710
|
-
CoreFoundation.XCInitializeCoreIfNeeded(0)
|
|
711
|
-
end
|
|
712
|
-
|
|
713
|
-
selector = 'projectWithFile:'
|
|
714
|
-
|
|
715
|
-
if NSObject.respondsToSelector(PBXProject.objc_class, selector)
|
|
716
|
-
projectWithFile = PBXProject.objc_msgSend([CoreFoundation::VoidPointer])
|
|
717
|
-
@project = projectWithFile.call(
|
|
718
|
-
PBXProject.objc_class,
|
|
719
|
-
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)),
|
|
720
|
-
CoreFoundation.RubyStringToCFString(path))
|
|
721
|
-
end
|
|
722
|
-
end
|
|
723
|
-
|
|
724
|
-
def close
|
|
725
|
-
selector = 'close'
|
|
726
|
-
return unless NSObject.respondsToSelector(@project, selector)
|
|
727
|
-
|
|
728
|
-
close = PBXProject.objc_msgSend([], CoreFoundation::Void)
|
|
729
|
-
close.call(@project, CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)))
|
|
730
|
-
end
|
|
731
|
-
|
|
732
|
-
def writeToFileSystemProjectFile
|
|
733
|
-
selector = 'writeToFileSystemProjectFile:userFile:checkNeedsRevert:'
|
|
734
|
-
return unless NSObject.respondsToSelector(@project, selector)
|
|
735
|
-
|
|
736
|
-
writeToFile = PBXProject.objc_msgSend([CoreFoundation::Boolean, CoreFoundation::Boolean, CoreFoundation::Boolean], CoreFoundation::Boolean)
|
|
737
|
-
result = writeToFile.call(
|
|
738
|
-
@project,
|
|
739
|
-
CoreFoundation.NSSelectorFromString(CoreFoundation.RubyStringToCFString(selector)),
|
|
740
|
-
1,
|
|
741
|
-
0,
|
|
742
|
-
1)
|
|
743
|
-
result == CoreFoundation::TRUE ? true : false
|
|
744
|
-
end
|
|
745
|
-
|
|
746
|
-
private
|
|
747
|
-
|
|
748
|
-
def self.image
|
|
749
|
-
@image ||= DevToolsCore.load_xcode_frameworks
|
|
750
|
-
end
|
|
751
|
-
|
|
752
|
-
extern :IDEInitialize, [CoreFoundation::Boolean, ID], CoreFoundation::Void
|
|
753
|
-
extern :XCInitializeCoreIfNeeded, [CoreFoundation::Boolean], CoreFoundation::Void
|
|
754
|
-
end
|
|
755
|
-
|
|
756
|
-
# rubocop:enable Style/MethodName
|
|
757
|
-
# rubocop:enable Style/VariableName
|
|
758
|
-
end
|