1 | # Copyright Bruno da Silva de Oliveira 2003. Use, modification and |
---|
2 | # distribution is subject to the Boost Software License, Version 1.0. |
---|
3 | # (See accompanying file LICENSE_1_0.txt or copy at |
---|
4 | # http:#www.boost.org/LICENSE_1_0.txt) |
---|
5 | |
---|
6 | """ |
---|
7 | Pyste version %s |
---|
8 | |
---|
9 | Usage: |
---|
10 | pyste [options] interface-files |
---|
11 | |
---|
12 | where options are: |
---|
13 | --module=<name> The name of the module that will be generated; |
---|
14 | defaults to the first interface filename, without |
---|
15 | the extension. |
---|
16 | -I <path> Add an include path |
---|
17 | -D <symbol> Define symbol |
---|
18 | --multiple Create various cpps, instead of only one |
---|
19 | (useful during development) |
---|
20 | --out=<name> Specify output filename (default: <module>.cpp) |
---|
21 | in --multiple mode, this will be a directory |
---|
22 | --no-using Do not declare "using namespace boost"; |
---|
23 | use explicit declarations instead |
---|
24 | --pyste-ns=<name> Set the namespace where new types will be declared; |
---|
25 | default is the empty namespace |
---|
26 | --debug Writes the xml for each file parsed in the current |
---|
27 | directory |
---|
28 | --cache-dir=<dir> Directory for cache files (speeds up future runs) |
---|
29 | --only-create-cache Recreates all caches (doesn't generate code). |
---|
30 | --generate-main Generates the _main.cpp file (in multiple mode) |
---|
31 | --file-list A file with one pyste file per line. Use as a |
---|
32 | substitute for passing the files in the command |
---|
33 | line. |
---|
34 | --gccxml-path=<path> Path to gccxml executable (default: gccxml) |
---|
35 | --no-default-include Do not use INCLUDE environment variable for include |
---|
36 | files to pass along gccxml. |
---|
37 | -h, --help Print this help and exit |
---|
38 | -v, --version Print version information |
---|
39 | """ |
---|
40 | |
---|
41 | import sys |
---|
42 | import os |
---|
43 | import getopt |
---|
44 | import exporters |
---|
45 | import SingleCodeUnit |
---|
46 | import MultipleCodeUnit |
---|
47 | import infos |
---|
48 | import exporterutils |
---|
49 | import settings |
---|
50 | import gc |
---|
51 | import sys |
---|
52 | from policies import * |
---|
53 | from CppParser import CppParser, CppParserError |
---|
54 | import time |
---|
55 | import declarations |
---|
56 | |
---|
57 | __version__ = '0.9.30' |
---|
58 | |
---|
59 | def RecursiveIncludes(include): |
---|
60 | 'Return a list containg the include dir and all its subdirectories' |
---|
61 | dirs = [include] |
---|
62 | def visit(arg, dir, names): |
---|
63 | # ignore CVS dirs |
---|
64 | if os.path.split(dir)[1] != 'CVS': |
---|
65 | dirs.append(dir) |
---|
66 | os.path.walk(include, visit, None) |
---|
67 | return dirs |
---|
68 | |
---|
69 | |
---|
70 | def GetDefaultIncludes(): |
---|
71 | if 'INCLUDE' in os.environ: |
---|
72 | include = os.environ['INCLUDE'] |
---|
73 | return include.split(os.pathsep) |
---|
74 | else: |
---|
75 | return [] |
---|
76 | |
---|
77 | |
---|
78 | def ProcessIncludes(includes): |
---|
79 | if sys.platform == 'win32': |
---|
80 | index = 0 |
---|
81 | for include in includes: |
---|
82 | includes[index] = include.replace('\\', '/') |
---|
83 | index += 1 |
---|
84 | |
---|
85 | |
---|
86 | def ReadFileList(filename): |
---|
87 | f = file(filename) |
---|
88 | files = [] |
---|
89 | try: |
---|
90 | for line in f: |
---|
91 | line = line.strip() |
---|
92 | if line: |
---|
93 | files.append(line) |
---|
94 | finally: |
---|
95 | f.close() |
---|
96 | return files |
---|
97 | |
---|
98 | |
---|
99 | def ParseArguments(): |
---|
100 | |
---|
101 | def Usage(): |
---|
102 | print __doc__ % __version__ |
---|
103 | sys.exit(1) |
---|
104 | |
---|
105 | try: |
---|
106 | options, files = getopt.getopt( |
---|
107 | sys.argv[1:], |
---|
108 | 'R:I:D:vh', |
---|
109 | ['module=', 'multiple', 'out=', 'no-using', 'pyste-ns=', 'debug', 'cache-dir=', |
---|
110 | 'only-create-cache', 'version', 'generate-main', 'file-list=', 'help', |
---|
111 | 'gccxml-path=', 'no-default-include']) |
---|
112 | except getopt.GetoptError, e: |
---|
113 | print |
---|
114 | print 'ERROR:', e |
---|
115 | Usage() |
---|
116 | |
---|
117 | default_includes = GetDefaultIncludes() |
---|
118 | includes = [] |
---|
119 | defines = [] |
---|
120 | module = None |
---|
121 | out = None |
---|
122 | multiple = False |
---|
123 | cache_dir = None |
---|
124 | create_cache = False |
---|
125 | generate_main = False |
---|
126 | gccxml_path = 'gccxml' |
---|
127 | |
---|
128 | for opt, value in options: |
---|
129 | if opt == '-I': |
---|
130 | includes.append(value) |
---|
131 | elif opt == '-D': |
---|
132 | defines.append(value) |
---|
133 | elif opt == '-R': |
---|
134 | includes.extend(RecursiveIncludes(value)) |
---|
135 | elif opt == '--module': |
---|
136 | module = value |
---|
137 | elif opt == '--out': |
---|
138 | out = value |
---|
139 | elif opt == '--no-using': |
---|
140 | settings.namespaces.python = 'boost::python::' |
---|
141 | settings.USING_BOOST_NS = False |
---|
142 | elif opt == '--pyste-ns': |
---|
143 | settings.namespaces.pyste = value + '::' |
---|
144 | elif opt == '--debug': |
---|
145 | settings.DEBUG = True |
---|
146 | elif opt == '--multiple': |
---|
147 | multiple = True |
---|
148 | elif opt == '--cache-dir': |
---|
149 | cache_dir = value |
---|
150 | elif opt == '--only-create-cache': |
---|
151 | create_cache = True |
---|
152 | elif opt == '--file-list': |
---|
153 | files += ReadFileList(value) |
---|
154 | elif opt in ['-h', '--help']: |
---|
155 | Usage() |
---|
156 | elif opt in ['-v', '--version']: |
---|
157 | print 'Pyste version %s' % __version__ |
---|
158 | sys.exit(2) |
---|
159 | elif opt == '--generate-main': |
---|
160 | generate_main = True |
---|
161 | elif opt == '--gccxml-path': |
---|
162 | gccxml_path = value |
---|
163 | elif opt == '--no-default-include': |
---|
164 | default_includes = [] |
---|
165 | else: |
---|
166 | print 'Unknown option:', opt |
---|
167 | Usage() |
---|
168 | |
---|
169 | includes[0:0] = default_includes |
---|
170 | if not files: |
---|
171 | Usage() |
---|
172 | if not module: |
---|
173 | module = os.path.splitext(os.path.basename(files[0]))[0] |
---|
174 | if not out: |
---|
175 | out = module |
---|
176 | if not multiple: |
---|
177 | out += '.cpp' |
---|
178 | for file in files: |
---|
179 | d = os.path.dirname(os.path.abspath(file)) |
---|
180 | if d not in sys.path: |
---|
181 | sys.path.append(d) |
---|
182 | |
---|
183 | if create_cache and not cache_dir: |
---|
184 | print 'Error: Use --cache-dir to indicate where to create the cache files!' |
---|
185 | Usage() |
---|
186 | sys.exit(3) |
---|
187 | |
---|
188 | if generate_main and not multiple: |
---|
189 | print 'Error: --generate-main only valid in multiple mode.' |
---|
190 | Usage() |
---|
191 | sys.exit(3) |
---|
192 | |
---|
193 | ProcessIncludes(includes) |
---|
194 | return includes, defines, module, out, files, multiple, cache_dir, create_cache, \ |
---|
195 | generate_main, gccxml_path |
---|
196 | |
---|
197 | |
---|
198 | def PCHInclude(*headers): |
---|
199 | code = '\n'.join(['#include <%s>' % x for x in headers]) |
---|
200 | infos.CodeInfo(code, 'pchinclude') |
---|
201 | |
---|
202 | |
---|
203 | def CreateContext(): |
---|
204 | 'create the context where a interface file will be executed' |
---|
205 | context = {} |
---|
206 | context['Import'] = Import |
---|
207 | # infos |
---|
208 | context['Function'] = infos.FunctionInfo |
---|
209 | context['Class'] = infos.ClassInfo |
---|
210 | context['Include'] = lambda header: infos.CodeInfo('#include <%s>\n' % header, 'include') |
---|
211 | context['PCHInclude'] = PCHInclude |
---|
212 | context['Template'] = infos.ClassTemplateInfo |
---|
213 | context['Enum'] = infos.EnumInfo |
---|
214 | context['AllFromHeader'] = infos.HeaderInfo |
---|
215 | context['Var'] = infos.VarInfo |
---|
216 | # functions |
---|
217 | context['rename'] = infos.rename |
---|
218 | context['set_policy'] = infos.set_policy |
---|
219 | context['exclude'] = infos.exclude |
---|
220 | context['set_wrapper'] = infos.set_wrapper |
---|
221 | context['use_shared_ptr'] = infos.use_shared_ptr |
---|
222 | context['use_auto_ptr'] = infos.use_auto_ptr |
---|
223 | context['holder'] = infos.holder |
---|
224 | context['add_method'] = infos.add_method |
---|
225 | context['final'] = infos.final |
---|
226 | context['export_values'] = infos.export_values |
---|
227 | # policies |
---|
228 | context['return_internal_reference'] = return_internal_reference |
---|
229 | context['with_custodian_and_ward'] = with_custodian_and_ward |
---|
230 | context['return_value_policy'] = return_value_policy |
---|
231 | context['reference_existing_object'] = reference_existing_object |
---|
232 | context['copy_const_reference'] = copy_const_reference |
---|
233 | context['copy_non_const_reference'] = copy_non_const_reference |
---|
234 | context['return_opaque_pointer'] = return_opaque_pointer |
---|
235 | context['manage_new_object'] = manage_new_object |
---|
236 | context['return_by_value'] = return_by_value |
---|
237 | context['return_self'] = return_self |
---|
238 | # utils |
---|
239 | context['Wrapper'] = exporterutils.FunctionWrapper |
---|
240 | context['declaration_code'] = lambda code: infos.CodeInfo(code, 'declaration-outside') |
---|
241 | context['module_code'] = lambda code: infos.CodeInfo(code, 'module') |
---|
242 | context['class_code'] = infos.class_code |
---|
243 | return context |
---|
244 | |
---|
245 | |
---|
246 | def Begin(): |
---|
247 | # parse arguments |
---|
248 | includes, defines, module, out, interfaces, multiple, cache_dir, create_cache, generate_main, gccxml_path = ParseArguments() |
---|
249 | # run pyste scripts |
---|
250 | for interface in interfaces: |
---|
251 | ExecuteInterface(interface) |
---|
252 | # create the parser |
---|
253 | parser = CppParser(includes, defines, cache_dir, declarations.version, gccxml_path) |
---|
254 | try: |
---|
255 | if not create_cache: |
---|
256 | if not generate_main: |
---|
257 | return GenerateCode(parser, module, out, interfaces, multiple) |
---|
258 | else: |
---|
259 | return GenerateMain(module, out, OrderInterfaces(interfaces)) |
---|
260 | else: |
---|
261 | return CreateCaches(parser) |
---|
262 | finally: |
---|
263 | parser.Close() |
---|
264 | |
---|
265 | |
---|
266 | def CreateCaches(parser): |
---|
267 | # There is one cache file per interface so we organize the headers |
---|
268 | # by interfaces. For each interface collect the tails from the |
---|
269 | # exporters sharing the same header. |
---|
270 | tails = JoinTails(exporters.exporters) |
---|
271 | |
---|
272 | # now for each interface file take each header, and using the tail |
---|
273 | # get the declarations and cache them. |
---|
274 | for interface, header in tails: |
---|
275 | tail = tails[(interface, header)] |
---|
276 | declarations = parser.ParseWithGCCXML(header, tail) |
---|
277 | cachefile = parser.CreateCache(header, interface, tail, declarations) |
---|
278 | print 'Cached', cachefile |
---|
279 | |
---|
280 | return 0 |
---|
281 | |
---|
282 | |
---|
283 | _imported_count = {} # interface => count |
---|
284 | |
---|
285 | def ExecuteInterface(interface): |
---|
286 | old_interface = exporters.current_interface |
---|
287 | if not os.path.exists(interface): |
---|
288 | if old_interface and os.path.exists(old_interface): |
---|
289 | d = os.path.dirname(old_interface) |
---|
290 | interface = os.path.join(d, interface) |
---|
291 | if not os.path.exists(interface): |
---|
292 | raise IOError, "Cannot find interface file %s."%interface |
---|
293 | |
---|
294 | _imported_count[interface] = _imported_count.get(interface, 0) + 1 |
---|
295 | exporters.current_interface = interface |
---|
296 | context = CreateContext() |
---|
297 | context['INTERFACE_FILE'] = os.path.abspath(interface) |
---|
298 | execfile(interface, context) |
---|
299 | exporters.current_interface = old_interface |
---|
300 | |
---|
301 | |
---|
302 | def Import(interface): |
---|
303 | exporters.importing = True |
---|
304 | ExecuteInterface(interface) |
---|
305 | exporters.importing = False |
---|
306 | |
---|
307 | |
---|
308 | def JoinTails(exports): |
---|
309 | '''Returns a dict of {(interface, header): tail}, where tail is the |
---|
310 | joining of all tails of all exports for the header. |
---|
311 | ''' |
---|
312 | tails = {} |
---|
313 | for export in exports: |
---|
314 | interface = export.interface_file |
---|
315 | header = export.Header() |
---|
316 | tail = export.Tail() or '' |
---|
317 | if (interface, header) in tails: |
---|
318 | all_tails = tails[(interface,header)] |
---|
319 | all_tails += '\n' + tail |
---|
320 | tails[(interface, header)] = all_tails |
---|
321 | else: |
---|
322 | tails[(interface, header)] = tail |
---|
323 | |
---|
324 | return tails |
---|
325 | |
---|
326 | |
---|
327 | |
---|
328 | def OrderInterfaces(interfaces): |
---|
329 | interfaces_order = [(_imported_count[x], x) for x in interfaces] |
---|
330 | interfaces_order.sort() |
---|
331 | interfaces_order.reverse() |
---|
332 | return [x for _, x in interfaces_order] |
---|
333 | |
---|
334 | |
---|
335 | |
---|
336 | def GenerateMain(module, out, interfaces): |
---|
337 | codeunit = MultipleCodeUnit.MultipleCodeUnit(module, out) |
---|
338 | codeunit.GenerateMain(interfaces) |
---|
339 | return 0 |
---|
340 | |
---|
341 | |
---|
342 | def GenerateCode(parser, module, out, interfaces, multiple): |
---|
343 | # prepare to generate the wrapper code |
---|
344 | if multiple: |
---|
345 | codeunit = MultipleCodeUnit.MultipleCodeUnit(module, out) |
---|
346 | else: |
---|
347 | codeunit = SingleCodeUnit.SingleCodeUnit(module, out) |
---|
348 | # stop referencing the exporters here |
---|
349 | exports = exporters.exporters |
---|
350 | exporters.exporters = None |
---|
351 | exported_names = dict([(x.Name(), None) for x in exports]) |
---|
352 | |
---|
353 | # order the exports |
---|
354 | order = {} |
---|
355 | for export in exports: |
---|
356 | if export.interface_file in order: |
---|
357 | order[export.interface_file].append(export) |
---|
358 | else: |
---|
359 | order[export.interface_file] = [export] |
---|
360 | exports = [] |
---|
361 | interfaces_order = OrderInterfaces(interfaces) |
---|
362 | for interface in interfaces_order: |
---|
363 | exports.extend(order[interface]) |
---|
364 | del order |
---|
365 | del interfaces_order |
---|
366 | |
---|
367 | # now generate the code in the correct order |
---|
368 | #print exported_names |
---|
369 | tails = JoinTails(exports) |
---|
370 | for i in xrange(len(exports)): |
---|
371 | export = exports[i] |
---|
372 | interface = export.interface_file |
---|
373 | header = export.Header() |
---|
374 | if header: |
---|
375 | tail = tails[(interface, header)] |
---|
376 | declarations, parsed_header = parser.Parse(header, interface, tail) |
---|
377 | else: |
---|
378 | declarations = [] |
---|
379 | parsed_header = None |
---|
380 | ExpandTypedefs(declarations, exported_names) |
---|
381 | export.SetDeclarations(declarations) |
---|
382 | export.SetParsedHeader(parsed_header) |
---|
383 | if multiple: |
---|
384 | codeunit.SetCurrent(export.interface_file, export.Name()) |
---|
385 | export.GenerateCode(codeunit, exported_names) |
---|
386 | # force collect of cyclic references |
---|
387 | exports[i] = None |
---|
388 | del declarations |
---|
389 | del export |
---|
390 | gc.collect() |
---|
391 | # finally save the code unit |
---|
392 | codeunit.Save() |
---|
393 | if not multiple: |
---|
394 | print 'Module %s generated' % module |
---|
395 | return 0 |
---|
396 | |
---|
397 | |
---|
398 | def ExpandTypedefs(decls, exported_names): |
---|
399 | '''Check if the names in exported_names are a typedef, and add the real class |
---|
400 | name in the dict. |
---|
401 | ''' |
---|
402 | for name in exported_names.keys(): |
---|
403 | for decl in decls: |
---|
404 | if isinstance(decl, declarations.Typedef): |
---|
405 | exported_names[decl.type.FullName()] = None |
---|
406 | |
---|
407 | def UsePsyco(): |
---|
408 | 'Tries to use psyco if possible' |
---|
409 | try: |
---|
410 | import psyco |
---|
411 | psyco.profile() |
---|
412 | except: pass |
---|
413 | |
---|
414 | |
---|
415 | def main(): |
---|
416 | start = time.clock() |
---|
417 | UsePsyco() |
---|
418 | status = Begin() |
---|
419 | print '%0.2f seconds' % (time.clock()-start) |
---|
420 | sys.exit(status) |
---|
421 | |
---|
422 | |
---|
423 | if __name__ == '__main__': |
---|
424 | main() |
---|