1
|
1 #!/usr/bin/env ruby
|
|
2
|
|
3 # Copyright (c) 2008-2009, Jacob Carlborg. All rights reserved.
|
|
4 # Copyright (c) 2006-2007, Apple Inc. All rights reserved.
|
|
5 # Copyright (c) 2005-2006 FUJIMOTO Hisakuni
|
|
6 #
|
|
7 # Redistribution and use in source and binary forms, with or without
|
|
8 # modification, are permitted provided that the following conditions
|
|
9 # are met:
|
|
10 # 1. Redistributions of source code must retain the above copyright
|
|
11 # notice, this list of conditions and the following disclaimer.
|
|
12 # 2. Redistributions in binary form must reproduce the above copyright
|
|
13 # notice, this list of conditions and the following disclaimer in the
|
|
14 # documentation and/or other materials provided with the distribution.
|
|
15 # 3. Neither the name of Apple Inc. ("Apple") nor the names of
|
|
16 # its contributors may be used to endorse or promote products derived
|
|
17 # from this software without specific prior written permission.
|
|
18 #
|
|
19 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
|
|
20 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
22 # ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
|
|
23 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
25 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
26 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
27 # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
28 # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
29 # POSSIBILITY OF SUCH DAMAGE.
|
|
30
|
|
31 require "rubygems"
|
|
32 gem "builder", "~> 2.0"
|
|
33 require "builder"
|
|
34 require "tmpdir"
|
|
35 require "optparse"
|
|
36 include Builder
|
|
37
|
|
38 $KCODE = "UTF8"
|
|
39
|
|
40 # Extensions that adds support for member access syntax
|
|
41 class Hash
|
|
42 def type
|
|
43 result = self["type"]
|
|
44 return result unless result.nil?
|
|
45 self[:type]
|
|
46 end
|
|
47
|
|
48 def type= (type)
|
|
49 self[:type] = type
|
|
50 end
|
|
51
|
|
52 def id
|
|
53 result = self["id"]
|
|
54 return result unless result.nil?
|
|
55 self[:id]
|
|
56 end
|
|
57
|
|
58 def id= (id)
|
|
59 self[:id] = id
|
|
60 end
|
|
61
|
|
62 def methods
|
|
63 result = self["methods"]
|
|
64 return result unless result.nil?
|
|
65 self[:methods]
|
|
66 end
|
|
67
|
|
68 def methods= (methods)
|
|
69 self[:methods] = methods
|
|
70 end
|
|
71
|
|
72 def method_missing (method, *args)
|
|
73 self.class.instance_eval do
|
|
74 define_method(method) do |*args|
|
|
75 if args.length > 0
|
|
76 self[method[0 ... -1]] = args[0]
|
|
77 self[eval(":#{method}"[0 ... -1])] = args[0]
|
|
78 else
|
|
79 result = self[method]
|
|
80 return result unless result.nil?
|
|
81 self[eval(":#{method}")]
|
|
82 end
|
|
83 end
|
|
84 end
|
|
85
|
|
86 if (method = method.id2name) =~ /=/
|
|
87 eval("self.#{method} (args.length < 2 ? args[0] : args)")
|
|
88 else
|
|
89 eval("self.#{method}")
|
|
90 end
|
|
91 end
|
|
92 end
|
|
93
|
|
94 # This Struct represents an Objective-C Framework
|
|
95 Framework = Struct.new(:name, :headers, :path) do
|
|
96 def initialize
|
|
97 self.headers = []
|
|
98 self.name = ""
|
|
99 self.path = ""
|
|
100 end
|
|
101 end
|
|
102
|
|
103 # This Struct represents a C/Objective-C header
|
|
104 HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :defines, :enums, :externs, :functions,
|
|
105 :imports, :inline_functions, :opaques, :path, :structs, :typedefs) do
|
|
106 def initialize
|
|
107 self.name = ""
|
|
108 self.cftypes = []
|
|
109 self.constants = []
|
|
110 self.defines = []
|
|
111 self.enums = []
|
|
112 self.externs = []
|
|
113 self.framework = ""
|
|
114 self.functions = []
|
|
115 self.imports = []
|
|
116 self.inline_functions = []
|
|
117 self.opaques = []
|
|
118 self.path = ""
|
|
119 self.structs = []
|
|
120 self.typedefs = []
|
|
121 end
|
|
122 end
|
|
123
|
|
124 # This class scans the headers
|
|
125 class HeaderScaner
|
|
126 CPP = ['/usr/bin/cpp-4.0', '/usr/bin/cpp-3.3', '/usr/bin/cpp3'].find { |x| File.exist?(x) }
|
|
127 raise "cpp not found" if CPP.nil?
|
|
128 CPPFLAGS = "-D__APPLE_CPP__ -include /usr/include/AvailabilityMacros.h"
|
|
129 CPPFLAGS << "-D__GNUC__" unless /\Acpp-4/.match(File.basename(CPP))
|
|
130
|
|
131 attr_accessor :frameworks, :headers, :do_64bit
|
|
132
|
|
133 def initialize
|
|
134 @extern_name = 'extern'
|
|
135 @frameworks = []
|
|
136 @file_content = nil
|
|
137 @headers = []
|
|
138 @classes = {}
|
|
139 @informal_protocols = {}
|
|
140 @function_pointer_types = {}
|
|
141 @do_64bit = false
|
|
142 end
|
|
143
|
|
144 def classes
|
|
145 @classes
|
|
146 end
|
|
147
|
|
148 def protocols
|
|
149 @informal_protocols
|
|
150 end
|
|
151
|
|
152 def cftypes (header)
|
|
153 re = /typedef\s+(const\s+)?(struct\s*\w+\s*\*\s*)([^\s]+Ref)\s*;/
|
|
154 @cpp_result.scan(re).each do |m|
|
|
155 header.cftypes << { :name => m[2], :type => m[1], :const => m[0] =~ /const/ ? true : false}
|
|
156 end
|
|
157 end
|
|
158
|
|
159 def constants (header)
|
|
160 tmp = header.externs.map do |i|
|
|
161 constant?(i, true)
|
|
162 end
|
|
163
|
|
164 header.constants = tmp.flatten.compact
|
|
165 end
|
|
166
|
|
167 def defines (header)
|
|
168 re = /#define\s+([^\s]+)\s+(\([^)]+\)|[^\s]+)\s*$/
|
|
169 @file_content.scan(re).each do |m|
|
|
170 next unless !m[0].include?('(') && m[1] != '\\'
|
|
171 header.defines << { :name => m[0], :value => m[1] }
|
|
172 end
|
|
173 end
|
|
174
|
|
175 def enums (header)
|
|
176 re = /\benum\b\s*(\w+\s+)?\{([^}]*)\}/
|
|
177 @cpp_result.scan(re).each do |m|
|
|
178 enum = { :name => m[0], :members => [] }
|
|
179
|
|
180 m[1].split(",").map do |i|
|
|
181 name, value = i.split("=", 2).map do |x|
|
|
182 x.strip
|
|
183 end
|
|
184
|
|
185 enum[:members] << { :name => name, :value => value } unless name.empty? || name[0] == ?#
|
|
186 end
|
|
187
|
|
188 header.enums << enum
|
|
189 end
|
|
190 end
|
|
191
|
|
192 def function_pointer_types (header)
|
|
193 re = /typedef\s+([\w\s]+)\s*\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*;/
|
|
194 data = @cpp_result.scan(re)
|
|
195 re = /typedef\s+([\w\s]+)\s*\(([^)]+)\)\s*;/
|
|
196 data |= @cpp_result.scan(re).map do |m|
|
|
197 ary = m[0].split(/(\w+)$/)
|
|
198 ary[1] << " *"
|
|
199 ary << m[1]
|
|
200 ary
|
|
201 end
|
|
202
|
|
203 data.each do |m|
|
|
204 name = m[1]
|
|
205 args = m[2].split(",").map do |x|
|
|
206 if x.include?(" ")
|
|
207 ptr = x.sub!(/\[\]\s*$/, "")
|
|
208 x = x.sub(/\w+\s*$/, "").strip
|
|
209 ptr ? x + "*" : x
|
|
210 else
|
|
211 x.strip
|
|
212 end
|
|
213 end
|
|
214
|
|
215 type = "#{m[0]}(*)(#{args.join(', ')})"
|
|
216 @function_pointer_types[name] = type
|
|
217 end
|
|
218 end
|
|
219
|
|
220 def typedefs (header)
|
|
221 re = /^\s*typedef\s+(.+)\s+([\w\*]+)\s*;$/
|
|
222 data = @cpp_result
|
|
223 data.scan(re).each do |m|
|
|
224 var = get_var_info(m[0] + " " + m[1])
|
|
225 header.typedefs << get_var_info(m[0] + " " + m[1]) if var
|
|
226 end
|
|
227 end
|
|
228
|
|
229 def externs (header)
|
|
230 re = /^\s*#{@extern_name}\s+\b(.*)\s*;.*$/
|
|
231 header.externs = @cpp_result.scan(re).map do |m|
|
|
232 m[0].strip
|
|
233 end
|
|
234 end
|
|
235
|
|
236 def imports (header)
|
|
237 tmp = []
|
|
238
|
|
239 @file_content.each do |line|
|
|
240 if line =~ /#(include|import) <(.+)\.h>/
|
|
241 next if $2 == header.name
|
|
242 tmp << $2
|
|
243 elsif line =~ /@class(\s+)(\w+)(,|;)/
|
|
244 next if $2 == header.name
|
|
245
|
|
246 if $3 == ";"
|
|
247 tmp << header.framework + "/" + $2 unless header.framework == ""
|
|
248 tmp << $2 if header.framework == ""
|
|
249 elsif
|
|
250 str = line[6 + $1.length ... -2]
|
|
251 str.gsub!(" ", "")
|
|
252 arr = str.split(",")
|
|
253
|
|
254 arr.each do |s|
|
|
255 tmp << header.framework + "/" + s unless header.framework == ""
|
|
256 tmp << s if header.framework == ""
|
|
257 end
|
|
258 end
|
|
259 end
|
|
260 end
|
|
261
|
|
262 header.imports = tmp.compact.uniq
|
|
263 end
|
|
264
|
|
265 def informal_protocols (header)
|
|
266 self.methods(header)
|
|
267 end
|
|
268
|
|
269 def methods (header)
|
|
270 interface_re = /^@(interface|protocol)\s+(\w+)\s*:?\s*(\w*)\s*(\([^)]+\))?/
|
|
271
|
|
272 end_re = /^@end/
|
|
273 body_re = /^[-+]\s*(\([^)]+\))?\s*([^:\s;]+)/
|
|
274 args_re = /\w+\s*:/
|
|
275 prop_re = /^@property\s*(\([^)]+\))?\s*([^;]+);$/
|
|
276 current_interface = current_category = nil
|
|
277 i = 0
|
|
278 parent = nil
|
|
279
|
|
280 @cpp_result.each_line do |line|
|
|
281 size = line.size
|
|
282 line.strip!
|
|
283
|
|
284 if md = interface_re.match(line)
|
|
285 parent = nil
|
|
286 current_interface = md[1] == "protocol" ? "NSObject" : md[2]
|
|
287 current_category = md[4].delete("()").strip if md[4]
|
|
288 parent = md[3] unless md[3] == ""
|
|
289
|
|
290 elsif end_re.match(line)
|
|
291 current_interface = current_category = nil
|
|
292
|
|
293 elsif current_interface && md = prop_re.match(line)
|
|
294 # Parsing Objective-C 2.0 properties
|
|
295 if (a = md[2].split(/\s/)).length >= 2 && /^\w+$/.match(name = a[-1]) && (type = a[0 .. -2].join(" ")).index(",").nil?
|
|
296 getter, setter = name, "set#{name[0].chr.upcase + name[1 .. -1]}"
|
|
297 readonly = false
|
|
298
|
|
299 if attributes = md[1]
|
|
300 if md = /getter\s*=\s*(\w+)/.match(attributes)
|
|
301 getter = md[1]
|
|
302 end
|
|
303
|
|
304 if md = /setter\s*=\s*(\w+)/.match(attributes)
|
|
305 setter = md[1]
|
|
306 end
|
|
307
|
|
308 readonly = true if attributes.index("readonly")
|
|
309 end
|
|
310
|
|
311 typeinfo = VarInfo.new(type, "", "")
|
|
312
|
|
313 @classes[current_interface] ||= {}
|
|
314 methods = (@classes[current_interface].methods ||= [])
|
|
315 methods << MethodInfo.new(typeinfo, getter, false, [], line)
|
|
316
|
|
317 unless readonly
|
|
318 methods << MethodInfo.new(VarInfo.new("void", "", ""), setter + ":", false, [typeinfo], line)
|
|
319 end
|
|
320 end
|
|
321
|
|
322 elsif current_interface && (line[0] == ?+ || line[0] == ?-)
|
|
323 mtype = line[0]
|
|
324 data = @cpp_result[i .. -1]
|
|
325 body_md = body_re.match(data)
|
|
326
|
|
327 next if body_md.nil?
|
|
328
|
|
329 rettype = body_md[1] ? body_md[1].delete("()") : "id"
|
|
330 retval = VarInfo.new(rettype, "", "")
|
|
331 args = []
|
|
332 selector = ""
|
|
333 data = data[0 .. data.index(";")]
|
|
334 args_data = []
|
|
335
|
|
336 data.scan(args_re) do |x|
|
|
337 args_data << [$`, x, $']
|
|
338 end
|
|
339
|
|
340 variadic = false
|
|
341 args_data.each_with_index do |ary, n|
|
|
342 before, argname, argtype = ary
|
|
343 arg_nameless = (n > 0 && /\)\s*$/.match(before))
|
|
344 argname = ":" if arg_nameless
|
|
345 realargname = nil
|
|
346
|
|
347 if n < args_data.length - 1
|
|
348 argtype.sub!(args_data[n + 1][2], "")
|
|
349
|
|
350 if arg_nameless
|
|
351 argtype.sub!(/(\w+\s*)?\w+\s*:\s*$/, "")
|
|
352 else
|
|
353 unless argtype.sub!(/(\w+)\s+\w+:\s*$/) { |s| realargname = $1; "" }
|
|
354 # maybe the next argument is nameless
|
|
355 argtype.sub!(/\w+\s*:\s*$/, "")
|
|
356 end
|
|
357 end
|
|
358 else
|
|
359 argtype.sub!(/\s+__attribute__\(\(.+\)\)/, "")
|
|
360
|
|
361 if arg_nameless
|
|
362 argtype.sub!(/\w+\s*;$/, "")
|
|
363 else
|
|
364 unless argtype.sub!(/(\w+)\s*;$/) { |s| realargname = $1; "" }
|
|
365 variadic = argtype.sub!(/,\s*\.\.\.\s*;/, "") != nil
|
|
366 argtype.sub!(/\w+\s*$/, "") if variadic
|
|
367 end
|
|
368 end
|
|
369 end
|
|
370
|
|
371 selector << argname
|
|
372 realargname ||= argname.sub(/:/, "")
|
|
373 args << VarInfo.new(argtype, realargname, "") unless argtype.empty?
|
|
374 end
|
|
375
|
|
376 selector = body_md[2] if selector.empty?
|
|
377 args << VarInfo.new("...", "vararg", "") if variadic
|
|
378 method = MethodInfo.new(retval, selector, line[0] == ?+, args, data)
|
|
379
|
|
380 if current_category && current_interface == "NSObject"
|
|
381 (@informal_protocols[current_category] ||= []) << method
|
|
382 end
|
|
383
|
|
384 @classes[current_interface] ||= {}
|
|
385
|
|
386 if header.name == current_interface
|
|
387 @classes[current_interface].file = header.name
|
|
388 else
|
|
389 @classes[current_interface].file ||= header.name
|
|
390 end
|
|
391
|
|
392 unless parent == current_interface || parent =~ /\s+/ || parent.nil?
|
|
393 @classes[current_interface].parent = parent
|
|
394 end
|
|
395
|
|
396 (@classes[current_interface].methods ||= []) << method
|
|
397 end
|
|
398 i += size
|
|
399 end
|
|
400 end
|
|
401
|
|
402 def structs (header)
|
|
403 re = /typedef\s+struct\s*\w*\s*((\w+)|\{([^{}]*(\{[^}]+\})?)*\}\s*([^\s]+))\s*(__attribute__\(.+\))?\s*;/
|
|
404 i = 0
|
|
405 body = nil
|
|
406 @cpp_result.scan(re).each do |m|
|
|
407 struct = { :name => m[4], :members => [] }
|
|
408
|
|
409 unless struct[:name].nil?
|
|
410 if struct[:name][0, 1] == "*"
|
|
411 struct[:name].sub!("*", "")
|
|
412 end
|
|
413 end
|
|
414
|
|
415 return_type = nil
|
|
416 stripped_return_type = nil
|
|
417 body = m[2]
|
|
418
|
|
419 if m[2]
|
|
420 m[2].split(/,|;/).map do |i|
|
|
421 str, bytes = i.split(":", 2).map do |x|
|
|
422 x.strip
|
|
423 end
|
|
424
|
|
425 var = get_var_info(str, true)
|
|
426
|
|
427 if var
|
|
428 if var.return_type == "***dummy***"
|
|
429 var.return_type = return_type
|
|
430 var.stripped_return_type = stripped_return_type
|
|
431 else
|
|
432 return_type = var.return_type
|
|
433 stripped_return_type = var.stripped_return_type
|
|
434 end
|
|
435
|
|
436 struct[:members] << { :name => var.name, :bytes => bytes, :declaredType => var.return_type, :type => "", :type64 => "" } unless str.empty? || str[0] == ?#
|
|
437
|
|
438 names = []
|
|
439
|
|
440 tmp = struct[:members].collect do |member|
|
|
441 unless names.include?(member[:name])
|
|
442 names << member[:name]
|
|
443 member
|
|
444 end
|
|
445 end
|
|
446
|
|
447 struct[:members] = tmp.compact
|
|
448 end
|
|
449 end
|
|
450 end
|
|
451
|
|
452 header.structs << struct if body
|
|
453 end
|
|
454 end
|
|
455
|
|
456 def functions (header, inline = false)
|
|
457 if inline
|
|
458 inline_func_re = /(inline|__inline__)\s+((__attribute__\(\([^)]*\)\)\s+)?([\w\s\*<>]+)\s*\(([^)]*)\)\s*)\{/
|
|
459 res = @cpp_result.scan(inline_func_re)
|
|
460 res.each do |x|
|
|
461 x.delete_at(0)
|
|
462 x.delete_at(1)
|
|
463 end
|
|
464 else
|
|
465 skip_inline_re = /(static)?\s(inline|__inline__)[^{;]+(;|\{([^{}]*(\{[^}]+\})?)*\})\s*/
|
|
466 func_re = /(^([\w\s\*<>]+)\s*\(([^)]*)\)\s*)(__attribute__[^;]+)?;/
|
|
467 res = @cpp_result.gsub(skip_inline_re, '').scan(func_re)
|
|
468 end
|
|
469
|
|
470 funcs = res.map do |m|
|
|
471 orig, base, args = m
|
|
472 base.sub!(/^.*extern\s/, "")
|
|
473 func = constant?(base)
|
|
474
|
|
475 if func
|
|
476 args = args.strip.split(",").map do |i|
|
|
477 constant?(i)
|
|
478 end
|
|
479
|
|
480 next if args.any? do |x|
|
|
481 x.nil?
|
|
482 end
|
|
483
|
|
484 args = [] if args.size == 1 && args[0].return_type == "void"
|
|
485 FunctionInfo.new(func, args, orig, inline)
|
|
486 end
|
|
487 end.compact
|
|
488
|
|
489 if inline
|
|
490 header.inline_functions = funcs
|
|
491 else
|
|
492 header.functions = funcs
|
|
493 end
|
|
494 end
|
|
495
|
|
496 def prepare (path)
|
|
497 @file_content = File.read(path)
|
|
498 @file_content.gsub!(%r{(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)}, "");
|
|
499 @complete_cpp_result, @cpp_result = do_cpp(path, false, true, "")
|
|
500 end
|
|
501
|
|
502 def scan (frameworks, headers)
|
|
503 @frameworks = frameworks
|
|
504 @headers = headers
|
|
505
|
|
506 @frameworks.each do |framework|
|
|
507 framework.headers.each do |header|
|
|
508 prepare(header.path)
|
|
509
|
|
510 imports(header)
|
|
511 cftypes(header)
|
|
512 externs(header)
|
|
513 constants(header)
|
|
514 enums(header)
|
|
515 structs(header)
|
|
516 typedefs(header)
|
|
517 functions(header)
|
|
518 functions(header, true)
|
|
519 defines(header)
|
|
520 methods(header)
|
|
521 function_pointer_types(header)
|
|
522 end
|
|
523 end
|
|
524
|
|
525 @headers.each do |header|
|
|
526 prepare(header.path)
|
|
527
|
|
528 imports(header)
|
|
529 cftypes(header)
|
|
530 externs(header)
|
|
531 constants(header)
|
|
532 enums(header)
|
|
533 structs(header)
|
|
534 typedefs(header)
|
|
535 functions(header)
|
|
536 functions(header, true)
|
|
537 defines(header)
|
|
538 methods(header)
|
|
539 function_pointer_types(header)
|
|
540 end
|
|
541 end
|
|
542
|
|
543 def get_var_info (str, multi = false)
|
|
544 str.strip!
|
|
545
|
|
546 return nil if str.empty?
|
|
547
|
|
548 if str == "..."
|
|
549 VarInfo.new("...", "...", str)
|
|
550 else
|
|
551 str = "***dummy*** " + str if str[-1].chr == '*' || str.index(/\s/).nil?
|
|
552 tokens = multi ? str.split(',') : [str]
|
|
553 part = tokens.first
|
|
554 re = /^([^()]*)\b(\w+)\b\s*(\[[^\]]*\])*$/
|
|
555 m = re.match(part)
|
|
556
|
|
557 if m
|
|
558 return nil if m[1].split(/\s+/).any? do |x|
|
|
559 ['end', 'typedef'].include?(x)
|
|
560 end
|
|
561
|
|
562 m = m.to_a[1..-1].compact.map do |i|
|
|
563 i.strip
|
|
564 end
|
|
565
|
|
566 m[0] += m[2] if m.size == 3
|
|
567 m[0] = 'void' if m[1] == 'void'
|
|
568
|
|
569 var = begin
|
|
570 VarInfo.new(m[0], m[1], part)
|
|
571 rescue
|
|
572 return nil
|
|
573 end
|
|
574
|
|
575 if tokens.size > 1
|
|
576 [var, *tokens[1..-1].map { |x| constant?(m[0] + x.strip.sub(/^\*+/, '')) }]
|
|
577 else
|
|
578 var
|
|
579 end
|
|
580 end
|
|
581 end
|
|
582 end
|
|
583
|
|
584 def constant? (str, multi = false)
|
|
585 str.strip!
|
|
586 return nil if str.empty?
|
|
587
|
|
588 if str == '...'
|
|
589 VarInfo.new('...', '...', str)
|
|
590 else
|
|
591 str << " dummy" if str[-1].chr == '*' or str.index(/\s/).nil?
|
|
592 tokens = multi ? str.split(',') : [str]
|
|
593 part = tokens.first
|
|
594 re = /^([^()]*)\b(\w+)\b\s*(\[[^\]]*\])*$/
|
|
595 m = re.match(part)
|
|
596
|
|
597 if m
|
|
598 return nil if m[1].split(/\s+/).any? do |x|
|
|
599 ['end', 'typedef'].include?(x)
|
|
600 end
|
|
601
|
|
602 m = m.to_a[1..-1].compact.map do |i|
|
|
603 i.strip
|
|
604 end
|
|
605
|
|
606 m[0] += m[2] if m.size == 3
|
|
607 m[0] = 'void' if m[1] == 'void'
|
|
608
|
|
609 var = begin
|
|
610 VarInfo.new(m[0], m[1], part)
|
|
611 rescue
|
|
612 return nil
|
|
613 end
|
|
614
|
|
615 if tokens.size > 1
|
|
616 [var, *tokens[1..-1].map { |x| constant?(m[0] + x.strip.sub(/^\*+/, '')) }]
|
|
617 else
|
|
618 var
|
|
619 end
|
|
620 end
|
|
621 end
|
|
622 end
|
|
623
|
|
624 class VarInfo
|
|
625
|
|
626 attr_reader :name, :orig, :const, :type_modifier
|
|
627 attr_accessor :octype, :resolved_type, :resolved_type64, :return_type, :stripped_return_type
|
|
628
|
|
629 def initialize (type, name, orig)
|
|
630 @return_type = type.clone
|
|
631 @name = name
|
|
632 @orig = orig
|
|
633 @const = false
|
|
634 @type_modifier = ""
|
|
635
|
|
636 @return_type.gsub!(/\[[^\]]*\]/, "*")
|
|
637
|
|
638 if @return_type =~ /\bconst\b/
|
|
639 @const = true
|
|
640 @return_type.gsub!("const", "")
|
|
641 end
|
|
642
|
|
643 if @return_type =~ /\b(in|out|inout|oneway|bycopy|byref)\b/
|
|
644
|
|
645 case $1
|
|
646 when "in"
|
|
647 @type_modifier << "n"
|
|
648 when "out"
|
|
649 @type_modifier << "o"
|
|
650 when "inout"
|
|
651 @type_modifier << "N"
|
|
652 when "oneway"
|
|
653 @type_modifier << "w"
|
|
654 when "bycopy"
|
|
655 @type_modifier << "c"
|
|
656 when "byref"
|
|
657 @type_modifier << "r"
|
|
658 end
|
|
659
|
|
660 @return_type.gsub!("#{$1}", "")
|
|
661 end
|
|
662
|
|
663 @return_type.gsub!(/\s?\*\s?/, "*")
|
|
664 @return_type.gsub!(/^\(|\)$/, "")
|
|
665 @return_type.strip!
|
|
666
|
|
667 t = type.gsub(/\b(__)?const\b/, "")
|
|
668 t.gsub!(/<[^>]*>/, '')
|
|
669 t.gsub!(/\b(in|out|inout|oneway|const)\b/, "")
|
|
670 t.gsub!(/\b__private_extern__\b/, "")
|
|
671 t.gsub!(/^\s*\(?\s*/, "")
|
|
672 t.gsub!(/\s*\)?\s*$/, "")
|
|
673
|
|
674 raise "Empty type (was '#{type}')" if t.empty?
|
|
675
|
|
676 @stripped_return_type = t
|
|
677 end
|
|
678
|
|
679 def function_pointer? (function_pointer_types)
|
|
680 type = @function_pointer_types[@stripped_return_type] || @stripped_return_type
|
|
681 @function_pointer_type ||= FunctionPointerInfo.new_from_type(type)
|
|
682 end
|
|
683
|
|
684 def <=>(x)
|
|
685 self.name <=> x.name
|
|
686 end
|
|
687
|
|
688 def hash
|
|
689 @name.hash
|
|
690 end
|
|
691
|
|
692 def eql? (o)
|
|
693 @name == o.name
|
|
694 end
|
|
695 end
|
|
696
|
|
697 class FunctionInfo < VarInfo
|
|
698
|
|
699 attr_reader :args, :argc
|
|
700
|
|
701 def initialize (function, args, orig, inline = false)
|
|
702 super(function.return_type, function.name, orig)
|
|
703
|
|
704 @args = args
|
|
705 @argc = @args.size
|
|
706 @variadic = false
|
|
707
|
|
708 if @args[-1] && @args[-1].return_type == "..."
|
|
709 @argc -= 1
|
|
710 @variadic = true
|
|
711 @args.pop
|
|
712 end
|
|
713
|
|
714 @inline = inline
|
|
715 self
|
|
716 end
|
|
717
|
|
718 def variadic?
|
|
719 @variadic
|
|
720 end
|
|
721
|
|
722 def inline?
|
|
723 @inline
|
|
724 end
|
|
725 end
|
|
726
|
|
727 class FunctionPointerInfo < FunctionInfo
|
|
728 def initialize (return_type, arg_types, orig)
|
|
729 args = arg_types.map do |x|
|
|
730 VarInfo.new(x, "", "")
|
|
731 end
|
|
732
|
|
733 super(VarInfo.new(return_type, "", ""), args, orig)
|
|
734 end
|
|
735
|
|
736 def self.new_from_type (type)
|
|
737 @cache ||= {}
|
|
738 info = @cache[type]
|
|
739
|
|
740 return info if info
|
|
741
|
|
742 tokens = type.split(/\(\*\)/)
|
|
743 return nil if tokens.size != 2
|
|
744
|
|
745 return_type = tokens.first.strip
|
|
746 rest = tokens.last.sub(/^\s*\(\s*/, "").sub(/\s*\)\s*$/, "")
|
|
747
|
|
748 arg_types = rest.split(/,/).map do |x|
|
|
749 x.strip
|
|
750 end
|
|
751
|
|
752 @cache[type] = self.new(return_type, arg_types, type)
|
|
753 end
|
|
754 end
|
|
755
|
|
756 class MethodInfo < FunctionInfo
|
|
757
|
|
758 attr_reader :selector
|
|
759
|
|
760 def initialize (method, selector, is_class, args, orig)
|
|
761 super(method, args, orig)
|
|
762
|
|
763 @selector = selector
|
|
764 @is_class = is_class
|
|
765 self
|
|
766 end
|
|
767
|
|
768 def class_method?
|
|
769 @is_class
|
|
770 end
|
|
771
|
|
772 def <=>(o)
|
|
773 @selector <=> o.selector
|
|
774 end
|
|
775
|
|
776 def hash
|
|
777 @selector.hash
|
|
778 end
|
|
779
|
|
780 def eql? (o)
|
|
781 @selector == o.selector
|
|
782 end
|
|
783 end
|
|
784
|
|
785 def do_cpp (path, fails_on_error = true, do_64 = true, flags = "")
|
|
786 f_on = false
|
|
787 err_file = '/tmp/.cpp.err'
|
|
788 cpp_line = "#{CPP} #{CPPFLAGS} #{flags} #{do_64 ? '-D__LP64__' : ''} \"#{path}\" 2>#{err_file}"
|
|
789 complete_result = `#{cpp_line}`
|
|
790
|
|
791 if $?.to_i != 0 && fails_on_error
|
|
792 $stderr.puts File.read(err_file)
|
|
793 File.unlink(err_file)
|
|
794 raise "#{CPP} returned #{$?.to_int/256} exit status\nline was: #{cpp_line}"
|
|
795 end
|
|
796
|
|
797 result = complete_result.select do |s|
|
|
798 # First pass to only grab non-empty lines and the pre-processed lines
|
|
799 # only from the target header (and not the entire pre-processing result).
|
|
800 next if s.strip.empty?
|
|
801 m = %r{^#\s*\d+\s+"([^"]+)"}.match(s)
|
|
802 f_on = (File.basename(m[1]) == File.basename(path)) if m
|
|
803 f_on
|
|
804 end.select do |s|
|
|
805 # Second pass to ignore all pro-processor comments that were left.
|
|
806 /^#/.match(s) == nil
|
|
807 end.join
|
|
808
|
|
809 File.unlink(err_file)
|
|
810 return [complete_result, result]
|
|
811 end
|
|
812 end
|
|
813
|
|
814 class DStepGenerator
|
|
815
|
|
816 VERSION = 1.0
|
|
817
|
|
818 attr_accessor :out_file, :scaner, :code_to_inject
|
|
819
|
|
820 def initialize
|
|
821 @do_64bit = false
|
|
822 @frameworks = []
|
|
823 @framework_paths = []
|
|
824 @headers = []
|
|
825 @import_directives = "#import <Foundation/Foundation.h>\n"
|
|
826 @informal_protocols = []
|
|
827 @classes = []
|
|
828 @scaner = HeaderScaner.new
|
|
829 @scaner.do_64bit = @do_64bit
|
|
830 @umbrella_framework = nil
|
|
831 @handled_dependencies = []
|
|
832
|
|
833 # link to foundation framework by default
|
|
834 @compiler_flags = "-framework Foundation"
|
|
835 end
|
|
836
|
|
837 def do_64bit
|
|
838 return @do_64bit
|
|
839 end
|
|
840
|
|
841 def do_64bit= (do_64bit)
|
|
842 @do_64bit = do_64bit
|
|
843 @scaner.do_64bit = do_64bit
|
|
844 end
|
|
845
|
|
846 def collect
|
|
847 scaner.scan(@frameworks, @headers)
|
|
848 @classes = scaner.classes
|
|
849 @informal_protocols = scaner.protocols
|
|
850 end
|
|
851
|
|
852 def handle_framework (framework, sub_framework = false, parent_framework = nil)
|
|
853 val = framework.name
|
|
854 path = framework_path(val)
|
|
855
|
|
856 raise "Can't locate framework '#{val}'" if path.nil?
|
|
857 @framework_paths << File.dirname(path)
|
|
858 raise "Can't find framework '#{val}'" if path.nil?
|
|
859
|
|
860 parent_path, name = path.scan(/^(.+)\/(\w+)\.framework\/?$/)[0]
|
|
861
|
|
862 if @private
|
|
863 headers_path = File.join(path, "PrivateHeaders")
|
|
864 raise "Can't locate private framework headers at '#{headers_path}'" unless File.exist?(headers_path)
|
|
865
|
|
866 headers = Dir.glob(File.join(headers_path, "**", "*.h"))
|
|
867 public_headers_path = File.join(path, "Headers")
|
|
868 public_headers = if File.exist?(public_headers_path)
|
|
869 HeaderScaner::CPPFLAGS << " -I#{public_headers_path} "
|
|
870 Dir.glob(File.join8(headers_path, "**", "*.h"))
|
|
871 else
|
|
872 []
|
|
873 end
|
|
874 else
|
|
875 headers_path = File.join(path, "Headers")
|
|
876 raise "Can't locate public framework headers at '#{headers_path}'" unless File.exist?(headers_path)
|
|
877 public_headers = headers = Dir.glob(File.join(headers_path, "**", "*.h"))
|
|
878 end
|
|
879
|
|
880 # We can't just "#import <x/x.h>" as the main Framework header might not include _all_ headers.
|
|
881 # So we are tricking this by importing the main header first, then all headers.
|
|
882 header_basenames = (headers | public_headers).map do |x|
|
|
883 x.sub(/#{headers_path}\/*/, "")
|
|
884 end
|
|
885
|
|
886 if idx = header_basenames.index("#{name}.h")
|
|
887 header_basenames.delete_at(idx)
|
|
888 header_basenames.unshift("#{name}.h")
|
|
889 end
|
|
890
|
|
891 if sub_framework
|
|
892 pp = framework_path(parent_framework.name)
|
|
893 tmp, pname = pp.scan(/^(.+)\/(\w+)\.framework\/?$/)[0]
|
|
894 @import_directives << "#import <#{pname}/#{pname}.h>"
|
|
895 else
|
|
896 @import_directives << header_basenames.map do |x|
|
|
897 "#import <#{name}/#{x}>"
|
|
898 end.join("\n")
|
|
899 end
|
|
900
|
|
901 @import_directives << "\n"
|
|
902
|
|
903 @compiler_flags ||= "-F\"#{parent_path}\" -framework #{name}"
|
|
904 @cpp_flags ||= ""
|
|
905 @cpp_flags << "-F\"#{parent_path}\" "
|
|
906
|
|
907 headers.each do |header|
|
|
908 header_file = HeaderFile.new
|
|
909 header_file.path = header
|
|
910 header_file.name = File.basename(header, File.extname(header))
|
|
911 header_file.framework = framework.name
|
|
912
|
|
913 framework.headers << header_file
|
|
914 end
|
|
915
|
|
916 # Memorize the dependencies.
|
|
917 @dependencies = DStepGenerator.dependencies_of_framework(path)
|
|
918
|
|
919 handle_sub_frameworks(framework)
|
|
920 end
|
|
921
|
|
922 def handle_sub_frameworks (framework)
|
|
923 path = framework_path(framework.name)
|
|
924 frameworks_path = File.join(path, "Frameworks")
|
|
925 frameworks = Dir.glob(File.join(frameworks_path, "*.framework"))
|
|
926
|
|
927 frameworks.each do |f|
|
|
928 add_framework(f, true, framework)
|
|
929 end
|
|
930 end
|
|
931
|
|
932 def framework_path (val)
|
|
933 return val if File.exist?(val)
|
|
934
|
|
935 val += ".framework" unless /\.framework$/.match(val)
|
|
936 paths = ["/System/Library/Frameworks", "/Library/Frameworks", "#{ENV['HOME']}/Library/Frameworks"]
|
|
937 paths << "/System/Library/PrivateFrameworks" if @private
|
|
938
|
|
939 paths.each do |dir|
|
|
940 path = File.join(dir, val)
|
|
941 return path if File.exist?(path)
|
|
942 end
|
|
943
|
|
944 return nil
|
|
945 end
|
|
946
|
|
947 def umbrella_framework (val)
|
|
948
|
|
949 unless val == "" || val.nil?
|
|
950 @umbrella_framework = val
|
|
951
|
|
952 unless @compiler_flags.nil? || @compiler_flags == ""
|
|
953 i = @compiler_flags.index("-framework")
|
|
954 @compiler_flags = @compiler_flags[0 .. i - 1] + "-framework #{val}"
|
|
955 else
|
|
956 @compiler_flags = "-framework #{val}"
|
|
957 end
|
|
958 end
|
|
959 end
|
|
960
|
|
961 def self.dependencies_of_framework (path)
|
|
962 @dependencies ||= {}
|
|
963 name = File.basename(path, ".framework")
|
|
964 path = File.join(path, name)
|
|
965 deps = @dependencies[path]
|
|
966
|
|
967 if deps.nil?
|
|
968 deps = `otool -L "#{path}"`.scan(/\t([^\s]+)/).map do |m|
|
|
969 dpath = m[0]
|
|
970 next if File.basename(dpath) == name
|
|
971 next if dpath.include?("PrivateFrameworks")
|
|
972 next unless dpath.sub!(/\.framework\/Versions\/\w+\/\w+$/, "")
|
|
973 dpath + ".framework"
|
|
974 end.compact
|
|
975
|
|
976 @dependencies[path] = deps
|
|
977 end
|
|
978
|
|
979 return deps
|
|
980 end
|
|
981
|
|
982 def compile_and_execute (code, enable_64bit = false, clean_when_fail = false)
|
|
983 compiler_line = "gcc "
|
|
984 src = File.new(unique_tmp_path("src", ".m", "/Users/doob/development/eclipse_workspace/dstep/bindings"), "w")
|
|
985 src << code
|
|
986 src.close
|
|
987
|
|
988 arch_flag = if enable_64bit
|
|
989 "-arch x86_64 -arch ppc64"
|
|
990 else
|
|
991 "-arch i386 -arch ppc"
|
|
992 end
|
|
993
|
|
994 compiler_line << arch_flag
|
|
995
|
|
996 bin = unique_tmp_path "bin", "", "/Users/doob/development/eclipse_workspace/dstep/bindings"
|
|
997 log = unique_tmp_path "log", ".txt", "/Users/doob/development/eclipse_workspace/dstep/bindings"
|
|
998
|
|
999 line = "#{compiler_line} -o #{bin} #{src.path} #{@compiler_flags} 2>#{log}"
|
|
1000
|
|
1001 unless system(line)
|
|
1002 msg = "Cannot compile Objective-C code ...aborting\nCommand was: #{line}\n\nLog:\n#{File.read(log)}\n\n"
|
|
1003 $stderr << msg
|
|
1004 File.delete src.path if clean_when_fail
|
|
1005 raise msg
|
|
1006 end
|
|
1007
|
|
1008 result = `#{bin}`
|
|
1009
|
|
1010 unless $?.success?
|
|
1011 raise "Cannot execute compiled Objective-C code ... aborting\nCommand was: #{line}\nBinary is: #{bin}"
|
|
1012 end
|
|
1013
|
|
1014 File.delete bin
|
|
1015 File.delete log
|
|
1016 File.delete src.path
|
|
1017
|
|
1018 return result
|
|
1019 end
|
|
1020
|
|
1021 def unique_tmp_path (base, extension = "", dir = Dir.tmpdir)
|
|
1022 i = 0
|
|
1023 loop do
|
|
1024 path = File.join(dir, "#{base}-#{Process.pid}-#{i}#{extension}")
|
|
1025 return path unless File.exists?(path)
|
|
1026 i += 1
|
|
1027 end
|
|
1028 end
|
|
1029
|
|
1030 def add_header (path)
|
|
1031 header = HeaderFile.new
|
|
1032 header.path = path
|
|
1033 header.name = File.basename(path, File.extname(path))
|
|
1034 @import_directives << "#include <#{path}>\n"
|
|
1035 @headers << header
|
|
1036 end
|
|
1037
|
|
1038 def add_framework (name, sub_framework = false, parent_framework = nil)
|
|
1039 framework = Framework.new
|
|
1040 framework.name = name
|
|
1041 handle_framework(framework, sub_framework, parent_framework)
|
|
1042 #@import_directives << "#import <#{framework.name}/#{framework.name}.h>\n"
|
|
1043 @frameworks << framework
|
|
1044 end
|
|
1045
|
|
1046 def collect_header_types (header, enable_64bit)
|
|
1047 types = []
|
|
1048
|
|
1049 header.cftypes.each do |cftype|
|
|
1050 types << cftype[:type]
|
|
1051 end
|
|
1052
|
|
1053 header.constants.each do |constant|
|
|
1054 types << constant.stripped_return_type
|
|
1055 end
|
|
1056
|
|
1057 header.structs.each do |struct|
|
|
1058 types << struct[:name]
|
|
1059
|
|
1060 struct[:members].each do |member|
|
|
1061 types << member[:declaredType]
|
|
1062 end
|
|
1063 end
|
|
1064
|
|
1065 header.typedefs.each do |typedef|
|
|
1066 types << typedef.stripped_return_type
|
|
1067 end
|
|
1068
|
|
1069 header.functions.each do |function|
|
|
1070 types << function.stripped_return_type
|
|
1071
|
|
1072 function.args.each do |arg|
|
|
1073 types << arg.stripped_return_type
|
|
1074 end
|
|
1075 end
|
|
1076
|
|
1077 types
|
|
1078 end
|
|
1079
|
|
1080 def collect_classes_types (enable_64bit)
|
|
1081 types = []
|
|
1082
|
|
1083 @classes.each do |clazz, value|
|
|
1084 value.methods.each do |method|
|
|
1085 types << method.stripped_return_type
|
|
1086
|
|
1087 method.args.each do |arg|
|
|
1088 types << arg.stripped_return_type
|
|
1089 end
|
|
1090 end
|
|
1091 end
|
|
1092
|
|
1093 types
|
|
1094 end
|
|
1095
|
|
1096 def collect_informal_protocols_types (enable_64bit)
|
|
1097 types = []
|
|
1098
|
|
1099 @informal_protocols.each do |name, methods|
|
|
1100 methods.each do |method|
|
|
1101 types << method.stripped_return_type
|
|
1102
|
|
1103 method.args.each do |arg|
|
|
1104 types << arg.stripped_return_type
|
|
1105 end
|
|
1106 end
|
|
1107 end
|
|
1108
|
|
1109 types
|
|
1110 end
|
|
1111
|
|
1112 def resolve_header_types (header, enable_64bit, resolved_types, x)
|
|
1113 i = x
|
|
1114
|
|
1115 header.cftypes.each do |cftype|
|
|
1116 cftype[enable_64bit ? :type64 : :type] = resolved_types[i]
|
|
1117 i += 1
|
|
1118 end
|
|
1119
|
|
1120 header.constants.each do |constant|
|
|
1121 constant.resolved_type = resolved_types[i] unless enable_64bit
|
|
1122 constant.resolved_type64 = resolved_types[i] if enable_64bit
|
|
1123 i += 1
|
|
1124 end
|
|
1125
|
|
1126 header.structs.each do |struct|
|
|
1127 struct[enable_64bit ? :type64 : :type] = resolved_types[i]
|
|
1128 i += 1
|
|
1129
|
|
1130 struct[:members].each do |member|
|
|
1131 member[enable_64bit ? :type64 : :type] = resolved_types[i]
|
|
1132 i += 1
|
|
1133 end
|
|
1134 end
|
|
1135
|
|
1136 header.typedefs.each do |typedef|
|
|
1137 typedef.resolved_type = resolved_types[i] unless enable_64bit
|
|
1138 typedef.resolved_type64 = resolved_types[i] if enable_64bit
|
|
1139 i += 1
|
|
1140 end
|
|
1141
|
|
1142 header.functions.each do |function|
|
|
1143 function.resolved_type = resolved_types[i] unless enable_64bit
|
|
1144 function.resolved_type64 = resolved_types[i] if enable_64bit
|
|
1145 i += 1
|
|
1146
|
|
1147 function.args.each do |arg|
|
|
1148 arg.resolved_type = resolved_types[i] unless enable_64bit
|
|
1149 arg.resolved_type64 = resolved_types[i] if enable_64bit
|
|
1150 i += 1
|
|
1151 end
|
|
1152 end
|
|
1153
|
|
1154 i
|
|
1155 end
|
|
1156
|
|
1157 def resolve_methods_types (enable_64bit, resolved_types, x)
|
|
1158 i = x
|
|
1159
|
|
1160 @classes.each do |clazz, value|
|
|
1161 value.methods.each do |method|
|
|
1162 method.resolved_type = resolved_types[i] unless enable_64bit
|
|
1163 method.resolved_type64 = resolved_types[i] if enable_64bit
|
|
1164 i += 1
|
|
1165
|
|
1166 method.args.each do |arg|
|
|
1167 arg.resolved_type = resolved_types[i] unless enable_64bit
|
|
1168 arg.resolved_type64 = resolved_types[i] if enable_64bit
|
|
1169 i += 1
|
|
1170 end
|
|
1171 end
|
|
1172 end
|
|
1173
|
|
1174 i
|
|
1175 end
|
|
1176
|
|
1177 def resolve_informal_protocols_types (enable_64bit, resolved_types, x)
|
|
1178 i = x
|
|
1179
|
|
1180 @informal_protocols.each do |name, methods|
|
|
1181 methods.each do |method|
|
|
1182 method.resolved_type = resolved_types[i] unless enable_64bit
|
|
1183 method.resolved_type64 = resolved_types[i] if enable_64bit
|
|
1184 i += 1
|
|
1185
|
|
1186 method.args.each do |arg|
|
|
1187 arg.resolved_type = resolved_types[i] unless enable_64bit
|
|
1188 arg.resolved_type64 = resolved_types[i] if enable_64bit
|
|
1189 i += 1
|
|
1190 end
|
|
1191 end
|
|
1192 end
|
|
1193
|
|
1194 i
|
|
1195 end
|
|
1196
|
|
1197 def resolve_types (enable_64bit = false)
|
|
1198 code = "#include <stdio.h>\n"
|
|
1199 code << "#import <objc/objc-class.h>\n"
|
|
1200
|
|
1201 # @dependencies.each do |f|
|
|
1202 # # get the framework name
|
|
1203 # i = f.index('.')
|
|
1204 # str = f[0 ... i]
|
|
1205 # i = str.rindex('/')
|
|
1206 # str = str[i + 1 .. -1]
|
|
1207 #
|
|
1208 # code << "#import <#{str}/#{str}.h>\n"
|
|
1209 # end
|
|
1210
|
|
1211 code << @import_directives
|
|
1212 types = []
|
|
1213
|
|
1214 code << @code_to_inject + ";\n" unless @code_to_inject.nil?
|
|
1215
|
|
1216 @frameworks.each do |framework|
|
|
1217 framework.headers.each do |header|
|
|
1218
|
|
1219 header.imports do |import|
|
|
1220 code << "#import <#{import}.h>\n"
|
|
1221 end
|
|
1222
|
|
1223 types << collect_header_types(header, enable_64bit)
|
|
1224 end
|
|
1225 end
|
|
1226
|
|
1227 @headers.each do |header|
|
|
1228 types << collect_header_types(header, enable_64bit)
|
|
1229 end
|
|
1230
|
|
1231 types << collect_classes_types(enable_64bit)
|
|
1232 types << collect_informal_protocols_types(enable_64bit)
|
|
1233
|
|
1234 code << "int main ()\n{\n"
|
|
1235 types.flatten!
|
|
1236
|
|
1237 types.each do |type|
|
|
1238 code << ' printf("%s\n", ' + "@encode(#{type}));\n"
|
|
1239 end
|
|
1240
|
|
1241 code << " return 0;\n}"
|
|
1242
|
|
1243 resolved_types = []
|
|
1244
|
|
1245 compile_and_execute(code, enable_64bit).split("\n").each do |line|
|
|
1246 resolved_types << line
|
|
1247 end
|
|
1248
|
|
1249 i = 0
|
|
1250
|
|
1251 @frameworks.each do |framework|
|
|
1252 framework.headers.each do |header|
|
|
1253 i = resolve_header_types(header, enable_64bit, resolved_types, i)
|
|
1254 end
|
|
1255 end
|
|
1256
|
|
1257 @headers.each do |header|
|
|
1258 i = resolve_header_types(header, enable_64bit, resolved_types, i)
|
|
1259 end
|
|
1260
|
|
1261 i = resolve_methods_types(enable_64bit, resolved_types, i)
|
|
1262 i = resolve_informal_protocols_types(enable_64bit, resolved_types, i)
|
|
1263 end
|
|
1264
|
|
1265 def generate_header (xml, header)
|
|
1266 xml.file :name => header.name do
|
|
1267 header.imports.each do |import|
|
|
1268 xml.import import
|
|
1269 end
|
|
1270
|
|
1271 header.defines.each do |define|
|
|
1272 xml.define define
|
|
1273 end
|
|
1274
|
|
1275 header.cftypes.each do |cftype|
|
|
1276 xml.cftype cftype
|
|
1277 end
|
|
1278
|
|
1279 header.constants.each do |constant|
|
|
1280 xml.constant :name => constant.name, :declaredType => constant.return_type, :type => constant.resolved_type, :type64 => constant.resolved_type64, :const => constant.const
|
|
1281 end
|
|
1282
|
|
1283 header.enums.each do |enum|
|
|
1284 xml.enum :name => enum[:name] do
|
|
1285 enum[:members].each do |member|
|
|
1286 xml.member member
|
|
1287 end
|
|
1288 end
|
|
1289 end
|
|
1290
|
|
1291 header.structs.each do |struct|
|
|
1292 xml.struct :name => struct[:name], :type => struct[:type], :type64 => struct[:type64] do
|
|
1293 struct[:members].each do |member|
|
|
1294 xml.member member
|
|
1295 end
|
|
1296 end
|
|
1297 end
|
|
1298
|
|
1299 header.typedefs.each do |typedef|
|
|
1300 xml.typedef :name => typedef.name, :declaredType => typedef.return_type, :type => typedef.resolved_type, :type64 => typedef.resolved_type64, :const => typedef.const
|
|
1301 end
|
|
1302
|
|
1303 header.functions.each do |function|
|
|
1304 xml.function :name => function.name, :inline => function.inline?, :variadic => function.variadic? do
|
|
1305 function.args.each do |arg|
|
|
1306 xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier
|
|
1307 end
|
|
1308
|
|
1309 xml.returnValue :declaredType => function.return_type, :type => function.resolved_type, :type64 => function.resolved_type64, :const => function.const, :typeModifier => function.type_modifier
|
|
1310 end
|
|
1311 end
|
|
1312 end
|
|
1313 end
|
|
1314
|
|
1315 def generate_classes (xml)
|
|
1316 @classes.each do |clazz, value|
|
|
1317 xml.class :name => clazz, :parent => value.parent, :file => value.file do
|
|
1318 value.methods.each do |method|
|
|
1319 xml.method :selector => method.selector, :classMethod => method.class_method?, :variadic => method.variadic? do
|
|
1320 method.args.each do |arg|
|
|
1321 xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier
|
|
1322 end
|
|
1323
|
|
1324 xml.returnValue :declaredType => method.return_type, :type => method.resolved_type, :type64 => method.resolved_type64, :const => method.const, :typeModifier => method.type_modifier
|
|
1325 end
|
|
1326 end
|
|
1327 end
|
|
1328 end
|
|
1329 end
|
|
1330
|
|
1331 def generate_informal_protocols (xml)
|
|
1332 @informal_protocols.each do |name, methods|
|
|
1333 xml.informalProtocol :name => name do
|
|
1334 methods.each do |method|
|
|
1335 xml.method :selector => method.selector, :classMethod => method.class_method?, :variadic => method.variadic? do
|
|
1336 method.args.each do |arg|
|
|
1337 xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier
|
|
1338 end
|
|
1339
|
|
1340 xml.returnValue :declaredType => method.return_type, :type => method.resolved_type, :type64 => method.resolved_type64, :const => method.const, :typeModifier => method.type_modifier
|
|
1341 end
|
|
1342 end
|
|
1343 end
|
|
1344 end
|
|
1345 end
|
|
1346
|
|
1347 def generate
|
|
1348 resolve_types
|
|
1349 resolve_types(true) if @do_64bit
|
|
1350
|
|
1351 file = STDOUT if @out_file == nil
|
|
1352 file = File.open @out_file, "w" unless @out_file == nil
|
|
1353
|
|
1354 xml = XmlMarkup.new(:target => file, :indent => 4)
|
|
1355 xml.instruct!
|
|
1356
|
|
1357 xml.dstep :xmlns => "http://www.dsource.org/projects/dstep", "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation" => "http://www.dsource.org/projects/dstep/trunk/scripts/dstep.xsd" do
|
|
1358 @frameworks.each do |framework|
|
|
1359 xml.framework :name => framework.name do
|
|
1360 framework.headers.each do |header|
|
|
1361 generate_header(xml, header)
|
|
1362 end
|
|
1363 end
|
|
1364 end
|
|
1365
|
|
1366 @headers.each do |header|
|
|
1367 generate_header(xml, header)
|
|
1368 end
|
|
1369
|
|
1370 generate_classes(xml)
|
|
1371 generate_informal_protocols(xml)
|
|
1372 end
|
|
1373
|
|
1374 file.close unless file == STDOUT
|
|
1375 end
|
|
1376 end
|
|
1377
|
|
1378 def die (*msg)
|
|
1379 $stderr.puts msg
|
|
1380 exit 1
|
|
1381 end
|
|
1382
|
|
1383 if __FILE__ == $0
|
|
1384 dstep_gen = DStepGenerator.new
|
|
1385
|
|
1386 OptionParser.new do |opts|
|
|
1387 opts.banner = "Usage: #{File.basename(__FILE__)} [options] <headers...>"
|
|
1388 opts.separator ""
|
|
1389 opts.separator "Options:"
|
|
1390
|
|
1391 opts.on("-f", "--framework FRAMEWORK", "Generate metadata for the given framework.") do |opt|
|
|
1392 dstep_gen.add_framework(opt)
|
|
1393 end
|
|
1394
|
|
1395 opts.on("-u", "--umbrella FRAMEWORK", "Link againts the given umbrella framework.") do |opt|
|
|
1396 dstep_gen.umbrella_framework(opt)
|
|
1397 end
|
|
1398
|
|
1399 opts.on(nil, "--64-bit", "Write 64-bit annotations.") do
|
|
1400 dstep_gen.do_64bit = true
|
|
1401 end
|
|
1402
|
|
1403 opts.on("-o", "--output FILE", "Write output to the given file.") do |opt|
|
|
1404 die "Output file can't be specified more than once" if dstep_gen.out_file
|
|
1405 dstep_gen.out_file = opt
|
|
1406 end
|
|
1407
|
|
1408 # opts.on("-d", "--output-dir PATH", "Write ouptut to the given paht, use this with the --framework option") do |opt|
|
|
1409 # die "Output directory can't be specified more than once" if dstep_gen.out_dir
|
|
1410 # dstep_gen.out_dir = opt
|
|
1411 # end
|
|
1412
|
|
1413 opts.on("-C", "--code CODE", "Add code") do |opt|
|
|
1414 dstep_gen.code_to_inject = opt
|
|
1415 end
|
|
1416
|
|
1417 help_msg = "Use the `-h' flag or for help."
|
|
1418
|
|
1419 opts.on("-h", "--help", "Show this message.") do
|
|
1420 puts opts, help_msg
|
|
1421 exit
|
|
1422 end
|
|
1423
|
|
1424 opts.on('-v', '--version', 'Show version.') do
|
|
1425 puts DStepGenerator::VERSION
|
|
1426 exit
|
|
1427 end
|
|
1428
|
|
1429 opts.separator ""
|
|
1430
|
|
1431 if ARGV.empty?
|
|
1432 die opts.banner
|
|
1433 else
|
|
1434 #begin
|
|
1435 opts.parse!(ARGV)
|
|
1436
|
|
1437 ARGV.each do |header|
|
|
1438 dstep_gen.add_header(header)
|
|
1439 end
|
|
1440
|
|
1441 dstep_gen.collect
|
|
1442 dstep_gen.generate
|
|
1443 # rescue => e
|
|
1444 # msg = e.message
|
|
1445 # msg = "Internal error" if msg.empty?
|
|
1446 #
|
|
1447 # die msg, opts.banner, help_msg
|
|
1448 # end
|
|
1449 end
|
|
1450 end
|
|
1451 end
|