Cython has moved to github.
cython-devel
view Cython/Compiler/Main.py @ 2194:b9d8cecc8975
general optimisation support for calls to builtin types and their methods
currently providing optimisations for
- getattr(o,a)
- getattr(o,a,d)
- X.append(o)
- L.append(o)
- list.append(L,x)
currently providing optimisations for
- getattr(o,a)
- getattr(o,a,d)
- X.append(o)
- L.append(o)
- list.append(L,x)
| author | Stefan Behnel <scoder@users.berlios.de> |
|---|---|
| date | Sun Mar 29 13:27:55 2009 +0200 (3 years ago) |
| parents | da8144d5b27b |
| children | 608a39903f79 |
line source
1 #
2 # Cython Top Level
3 #
5 import os, sys, re, codecs
6 if sys.version_info[:2] < (2, 3):
7 sys.stderr.write("Sorry, Cython requires Python 2.3 or later\n")
8 sys.exit(1)
10 try:
11 set
12 except NameError:
13 # Python 2.3
14 from sets import Set as set
16 from time import time
17 import Code
18 import Errors
19 import Parsing
20 import Version
21 from Scanning import PyrexScanner, FileSourceDescriptor
22 from Errors import PyrexError, CompileError, InternalError, error
23 from Symtab import BuiltinScope, ModuleScope
24 from Cython import Utils
25 from Cython.Utils import open_new_file, replace_suffix
26 import CythonScope
28 module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$")
30 verbose = 0
32 def dumptree(t):
33 # For quick debugging in pipelines
34 print t.dump()
35 return t
37 class CompilationData(object):
38 # Bundles the information that is passed from transform to transform.
39 # (For now, this is only)
41 # While Context contains every pxd ever loaded, path information etc.,
42 # this only contains the data related to a single compilation pass
43 #
44 # pyx ModuleNode Main code tree of this compilation.
45 # pxds {string : ModuleNode} Trees for the pxds used in the pyx.
46 # codewriter CCodeWriter Where to output final code.
47 # options CompilationOptions
48 # result CompilationResult
49 pass
51 class Context(object):
52 # This class encapsulates the context needed for compiling
53 # one or more Cython implementation files along with their
54 # associated and imported declaration files. It includes
55 # the root of the module import namespace and the list
56 # of directories to search for include files.
57 #
58 # modules {string : ModuleScope}
59 # include_directories [string]
60 # future_directives [object]
62 def __init__(self, include_directories, pragma_overrides):
63 #self.modules = {"__builtin__" : BuiltinScope()}
64 import Builtin, CythonScope
65 self.modules = {"__builtin__" : Builtin.builtin_scope}
66 self.modules["cython"] = CythonScope.create_cython_scope(self)
67 self.include_directories = include_directories
68 self.future_directives = set()
69 self.pragma_overrides = pragma_overrides
71 self.pxds = {} # full name -> node tree
73 standard_include_path = os.path.abspath(
74 os.path.join(os.path.dirname(__file__), '..', 'Includes'))
75 self.include_directories = include_directories + [standard_include_path]
77 def create_pipeline(self, pxd, py=False):
78 from Visitor import PrintTree
79 from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
80 from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform
81 from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
82 from ParseTreeTransforms import InterpretCompilerDirectives, TransformBuiltinMethods
83 from ParseTreeTransforms import ComprehensionTransform, AlignFunctionDefinitions
84 from ParseTreeTransforms import GilCheck
85 from AutoDocTransforms import EmbedSignature
86 from Optimize import FlattenInListTransform, SwitchTransform, IterationTransform
87 from Optimize import OptimiseBuiltinCalls, ConstantFolding, FinalOptimizePhase
88 from Buffer import IntroduceBufferAuxiliaryVars
89 from ModuleNode import check_c_declarations
91 # Temporary hack that can be used to ensure that all result_code's
92 # are generated at code generation time.
93 import Visitor
94 class ClearResultCodes(Visitor.CythonTransform):
95 def visit_ExprNode(self, node):
96 self.visitchildren(node)
97 node.result_code = "<cleared>"
98 return node
100 if pxd:
101 _check_c_declarations = None
102 _specific_post_parse = PxdPostParse(self)
103 else:
104 _check_c_declarations = check_c_declarations
105 _specific_post_parse = None
107 if py and not pxd:
108 _align_function_definitions = AlignFunctionDefinitions(self)
109 else:
110 _align_function_definitions = None
112 return [
113 NormalizeTree(self),
114 PostParse(self),
115 _specific_post_parse,
116 InterpretCompilerDirectives(self, self.pragma_overrides),
117 _align_function_definitions,
118 ConstantFolding(),
119 FlattenInListTransform(),
120 WithTransform(self),
121 DecoratorTransform(self),
122 AnalyseDeclarationsTransform(self),
123 EmbedSignature(self),
124 TransformBuiltinMethods(self),
125 IntroduceBufferAuxiliaryVars(self),
126 _check_c_declarations,
127 AnalyseExpressionsTransform(self),
128 OptimiseBuiltinCalls(),
129 # ComprehensionTransform(),
130 IterationTransform(),
131 SwitchTransform(),
132 FinalOptimizePhase(self),
133 GilCheck(),
134 # ClearResultCodes(self),
135 # SpecialFunctions(self),
136 # CreateClosureClasses(context),
137 ]
139 def create_pyx_pipeline(self, options, result, py=False):
140 def generate_pyx_code(module_node):
141 module_node.process_implementation(options, result)
142 result.compilation_source = module_node.compilation_source
143 return result
145 def inject_pxd_code(module_node):
146 from textwrap import dedent
147 stats = module_node.body.stats
148 for name, (statlistnode, scope) in self.pxds.iteritems():
149 # Copy over function nodes to the module
150 # (this seems strange -- I believe the right concept is to split
151 # ModuleNode into a ModuleNode and a CodeGenerator, and tell that
152 # CodeGenerator to generate code both from the pyx and pxd ModuleNodes.
153 stats.append(statlistnode)
154 # Until utility code is moved to code generation phase everywhere,
155 # we need to copy it over to the main scope
156 module_node.scope.utility_code_list.extend(scope.utility_code_list)
157 return module_node
159 return ([
160 create_parse(self),
161 ] + self.create_pipeline(pxd=False, py=py) + [
162 inject_pxd_code,
163 generate_pyx_code,
164 ])
166 def create_pxd_pipeline(self, scope, module_name):
167 def parse_pxd(source_desc):
168 tree = self.parse(source_desc, scope, pxd=True,
169 full_module_name=module_name)
170 tree.scope = scope
171 tree.is_pxd = True
172 return tree
174 from CodeGeneration import ExtractPxdCode
176 # The pxd pipeline ends up with a CCodeWriter containing the
177 # code of the pxd, as well as a pxd scope.
178 return [parse_pxd] + self.create_pipeline(pxd=True) + [
179 ExtractPxdCode(self),
180 ]
182 def create_py_pipeline(self, options, result):
183 return self.create_pyx_pipeline(options, result, py=True)
186 def process_pxd(self, source_desc, scope, module_name):
187 pipeline = self.create_pxd_pipeline(scope, module_name)
188 result = self.run_pipeline(pipeline, source_desc)
189 return result
191 def nonfatal_error(self, exc):
192 return Errors.report_error(exc)
194 def run_pipeline(self, pipeline, source):
195 err = None
196 data = source
197 try:
198 for phase in pipeline:
199 if phase is not None:
200 data = phase(data)
201 except CompileError, err:
202 # err is set
203 Errors.report_error(err)
204 except InternalError, err:
205 # Only raise if there was not an earlier error
206 if Errors.num_errors == 0:
207 raise
208 return (err, data)
210 def find_module(self, module_name,
211 relative_to = None, pos = None, need_pxd = 1):
212 # Finds and returns the module scope corresponding to
213 # the given relative or absolute module name. If this
214 # is the first time the module has been requested, finds
215 # the corresponding .pxd file and process it.
216 # If relative_to is not None, it must be a module scope,
217 # and the module will first be searched for relative to
218 # that module, provided its name is not a dotted name.
219 debug_find_module = 0
220 if debug_find_module:
221 print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (
222 module_name, relative_to, pos, need_pxd))
224 scope = None
225 pxd_pathname = None
226 if not module_name_pattern.match(module_name):
227 if pos is None:
228 pos = (module_name, 0, 0)
229 raise CompileError(pos,
230 "'%s' is not a valid module name" % module_name)
231 if "." not in module_name and relative_to:
232 if debug_find_module:
233 print("...trying relative import")
234 scope = relative_to.lookup_submodule(module_name)
235 if not scope:
236 qualified_name = relative_to.qualify_name(module_name)
237 pxd_pathname = self.find_pxd_file(qualified_name, pos)
238 if pxd_pathname:
239 scope = relative_to.find_submodule(module_name)
240 if not scope:
241 if debug_find_module:
242 print("...trying absolute import")
243 scope = self
244 for name in module_name.split("."):
245 scope = scope.find_submodule(name)
246 if debug_find_module:
247 print("...scope =", scope)
248 if not scope.pxd_file_loaded:
249 if debug_find_module:
250 print("...pxd not loaded")
251 scope.pxd_file_loaded = 1
252 if not pxd_pathname:
253 if debug_find_module:
254 print("...looking for pxd file")
255 pxd_pathname = self.find_pxd_file(module_name, pos)
256 if debug_find_module:
257 print("......found ", pxd_pathname)
258 if not pxd_pathname and need_pxd:
259 error(pos, "'%s.pxd' not found" % module_name)
260 if pxd_pathname:
261 try:
262 if debug_find_module:
263 print("Context.find_module: Parsing %s" % pxd_pathname)
264 source_desc = FileSourceDescriptor(pxd_pathname)
265 err, result = self.process_pxd(source_desc, scope, module_name)
266 if err:
267 raise err
268 (pxd_codenodes, pxd_scope) = result
269 self.pxds[module_name] = (pxd_codenodes, pxd_scope)
270 except CompileError:
271 pass
272 return scope
274 def find_pxd_file(self, qualified_name, pos):
275 # Search include path for the .pxd file corresponding to the
276 # given fully-qualified module name.
277 # Will find either a dotted filename or a file in a
278 # package directory. If a source file position is given,
279 # the directory containing the source file is searched first
280 # for a dotted filename, and its containing package root
281 # directory is searched first for a non-dotted filename.
282 return self.search_include_directories(qualified_name, ".pxd", pos)
284 def find_pyx_file(self, qualified_name, pos):
285 # Search include path for the .pyx file corresponding to the
286 # given fully-qualified module name, as for find_pxd_file().
287 return self.search_include_directories(qualified_name, ".pyx", pos)
289 def find_include_file(self, filename, pos):
290 # Search list of include directories for filename.
291 # Reports an error and returns None if not found.
292 path = self.search_include_directories(filename, "", pos,
293 include=True)
294 if not path:
295 error(pos, "'%s' not found" % filename)
296 return path
298 def search_include_directories(self, qualified_name, suffix, pos,
299 include=False):
300 # Search the list of include directories for the given
301 # file name. If a source file position is given, first
302 # searches the directory containing that file. Returns
303 # None if not found, but does not report an error.
304 # The 'include' option will disable package dereferencing.
305 dirs = self.include_directories
306 if pos:
307 file_desc = pos[0]
308 if not isinstance(file_desc, FileSourceDescriptor):
309 raise RuntimeError("Only file sources for code supported")
310 if include:
311 dirs = [os.path.dirname(file_desc.filename)] + dirs
312 else:
313 dirs = [self.find_root_package_dir(file_desc.filename)] + dirs
315 dotted_filename = qualified_name + suffix
316 if not include:
317 names = qualified_name.split('.')
318 package_names = names[:-1]
319 module_name = names[-1]
320 module_filename = module_name + suffix
321 package_filename = "__init__" + suffix
323 for dir in dirs:
324 path = os.path.join(dir, dotted_filename)
325 if os.path.exists(path):
326 return path
327 if not include:
328 package_dir = self.check_package_dir(dir, package_names)
329 if package_dir is not None:
330 path = os.path.join(package_dir, module_filename)
331 if os.path.exists(path):
332 return path
333 path = os.path.join(dir, package_dir, module_name,
334 package_filename)
335 if os.path.exists(path):
336 return path
337 return None
339 def find_root_package_dir(self, file_path):
340 dir = os.path.dirname(file_path)
341 while self.is_package_dir(dir):
342 parent = os.path.dirname(dir)
343 if parent == dir:
344 break
345 dir = parent
346 return dir
348 def is_package_dir(self, dir):
349 package_init = os.path.join(dir, "__init__.py")
350 return os.path.exists(package_init) or \
351 os.path.exists(package_init + "x") # same with .pyx
353 def check_package_dir(self, dir, package_names):
354 package_dir = os.path.join(dir, *package_names)
355 if not os.path.exists(package_dir):
356 return None
357 for dirname in package_names:
358 dir = os.path.join(dir, dirname)
359 if not self.is_package_dir(dir):
360 return None
361 return package_dir
363 def c_file_out_of_date(self, source_path):
364 c_path = Utils.replace_suffix(source_path, ".c")
365 if not os.path.exists(c_path):
366 return 1
367 c_time = Utils.modification_time(c_path)
368 if Utils.file_newer_than(source_path, c_time):
369 return 1
370 pos = [source_path]
371 pxd_path = Utils.replace_suffix(source_path, ".pxd")
372 if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time):
373 return 1
374 for kind, name in self.read_dependency_file(source_path):
375 if kind == "cimport":
376 dep_path = self.find_pxd_file(name, pos)
377 elif kind == "include":
378 dep_path = self.search_include_directories(name, pos)
379 else:
380 continue
381 if dep_path and Utils.file_newer_than(dep_path, c_time):
382 return 1
383 return 0
385 def find_cimported_module_names(self, source_path):
386 return [ name for kind, name in self.read_dependency_file(source_path)
387 if kind == "cimport" ]
389 def is_package_dir(self, dir_path):
390 # Return true if the given directory is a package directory.
391 for filename in ("__init__.py", "__init__.pyx"):
392 path = os.path.join(dir_path, filename)
393 if os.path.exists(path):
394 return 1
396 def read_dependency_file(self, source_path):
397 dep_path = Utils.replace_suffix(source_path, ".dep")
398 if os.path.exists(dep_path):
399 f = open(dep_path, "rU")
400 chunks = [ line.strip().split(" ", 1)
401 for line in f.readlines()
402 if " " in line.strip() ]
403 f.close()
404 return chunks
405 else:
406 return ()
408 def lookup_submodule(self, name):
409 # Look up a top-level module. Returns None if not found.
410 return self.modules.get(name, None)
412 def find_submodule(self, name):
413 # Find a top-level module, creating a new one if needed.
414 scope = self.lookup_submodule(name)
415 if not scope:
416 scope = ModuleScope(name,
417 parent_module = None, context = self)
418 self.modules[name] = scope
419 return scope
421 def parse(self, source_desc, scope, pxd, full_module_name):
422 if not isinstance(source_desc, FileSourceDescriptor):
423 raise RuntimeError("Only file sources for code supported")
424 source_filename = Utils.encode_filename(source_desc.filename)
425 # Parse the given source file and return a parse tree.
426 try:
427 f = Utils.open_source_file(source_filename, "rU")
428 try:
429 s = PyrexScanner(f, source_desc, source_encoding = f.encoding,
430 scope = scope, context = self)
431 tree = Parsing.p_module(s, pxd, full_module_name)
432 finally:
433 f.close()
434 except UnicodeDecodeError, msg:
435 #import traceback
436 #traceback.print_exc()
437 error((source_desc, 0, 0), "Decoding error, missing or incorrect coding=<encoding-name> at top of source (%s)" % msg)
438 if Errors.num_errors > 0:
439 raise CompileError
440 return tree
442 def extract_module_name(self, path, options):
443 # Find fully_qualified module name from the full pathname
444 # of a source file.
445 dir, filename = os.path.split(path)
446 module_name, _ = os.path.splitext(filename)
447 if "." in module_name:
448 return module_name
449 if module_name == "__init__":
450 dir, module_name = os.path.split(dir)
451 names = [module_name]
452 while self.is_package_dir(dir):
453 parent, package_name = os.path.split(dir)
454 if parent == dir:
455 break
456 names.append(package_name)
457 dir = parent
458 names.reverse()
459 return ".".join(names)
461 def setup_errors(self, options):
462 if options.use_listing_file:
463 result.listing_file = Utils.replace_suffix(source, ".lis")
464 Errors.open_listing_file(result.listing_file,
465 echo_to_stderr = options.errors_to_stderr)
466 else:
467 Errors.open_listing_file(None)
469 def teardown_errors(self, err, options, result):
470 source_desc = result.compilation_source.source_desc
471 if not isinstance(source_desc, FileSourceDescriptor):
472 raise RuntimeError("Only file sources for code supported")
473 Errors.close_listing_file()
474 result.num_errors = Errors.num_errors
475 if result.num_errors > 0:
476 err = True
477 if err and result.c_file:
478 try:
479 Utils.castrate_file(result.c_file, os.stat(source_desc.filename))
480 except EnvironmentError:
481 pass
482 result.c_file = None
483 if result.c_file and not options.c_only and c_compile:
484 result.object_file = c_compile(result.c_file,
485 verbose_flag = options.show_version,
486 cplus = options.cplus)
487 if not options.obj_only and c_link:
488 result.extension_file = c_link(result.object_file,
489 extra_objects = options.objects,
490 verbose_flag = options.show_version,
491 cplus = options.cplus)
493 def create_parse(context):
494 def parse(compsrc):
495 source_desc = compsrc.source_desc
496 full_module_name = compsrc.full_module_name
497 initial_pos = (source_desc, 1, 0)
498 scope = context.find_module(full_module_name, pos = initial_pos, need_pxd = 0)
499 tree = context.parse(source_desc, scope, pxd = 0, full_module_name = full_module_name)
500 tree.compilation_source = compsrc
501 tree.scope = scope
502 tree.is_pxd = False
503 return tree
504 return parse
506 def create_default_resultobj(compilation_source, options):
507 result = CompilationResult()
508 result.main_source_file = compilation_source.source_desc.filename
509 result.compilation_source = compilation_source
510 source_desc = compilation_source.source_desc
511 if options.output_file:
512 result.c_file = os.path.join(compilation_source.cwd, options.output_file)
513 else:
514 if options.cplus:
515 c_suffix = ".cpp"
516 else:
517 c_suffix = ".c"
518 result.c_file = Utils.replace_suffix(source_desc.filename, c_suffix)
519 return result
521 def run_pipeline(source, options, full_module_name = None):
522 # Set up context
523 context = Context(options.include_path, options.pragma_overrides)
525 # Set up source object
526 cwd = os.getcwd()
527 source_desc = FileSourceDescriptor(os.path.join(cwd, source))
528 full_module_name = full_module_name or context.extract_module_name(source, options)
529 source = CompilationSource(source_desc, full_module_name, cwd)
531 # Set up result object
532 result = create_default_resultobj(source, options)
534 # Get pipeline
535 if source_desc.filename.endswith(".py"):
536 pipeline = context.create_py_pipeline(options, result)
537 else:
538 pipeline = context.create_pyx_pipeline(options, result)
540 context.setup_errors(options)
541 err, enddata = context.run_pipeline(pipeline, source)
542 context.teardown_errors(err, options, result)
543 return result
545 #------------------------------------------------------------------------
546 #
547 # Main Python entry points
548 #
549 #------------------------------------------------------------------------
551 class CompilationSource(object):
552 """
553 Contains the data necesarry to start up a compilation pipeline for
554 a single compilation unit.
555 """
556 def __init__(self, source_desc, full_module_name, cwd):
557 self.source_desc = source_desc
558 self.full_module_name = full_module_name
559 self.cwd = cwd
561 class CompilationOptions(object):
562 """
563 Options to the Cython compiler:
565 show_version boolean Display version number
566 use_listing_file boolean Generate a .lis file
567 errors_to_stderr boolean Echo errors to stderr when using .lis
568 include_path [string] Directories to search for include files
569 output_file string Name of generated .c file
570 generate_pxi boolean Generate .pxi file for public declarations
571 recursive boolean Recursively find and compile dependencies
572 timestamps boolean Only compile changed source files. If None,
573 defaults to true when recursive is true.
574 verbose boolean Always print source names being compiled
575 quiet boolean Don't print source names in recursive mode
576 pragma_overrides dict Overrides for pragma options (see Options.py)
578 Following options are experimental and only used on MacOSX:
580 c_only boolean Stop after generating C file (default)
581 obj_only boolean Stop after compiling to .o file
582 objects [string] Extra .o files to link with
583 cplus boolean Compile as c++ code
584 """
586 def __init__(self, defaults = None, c_compile = 0, c_link = 0, **kw):
587 self.include_path = []
588 self.objects = []
589 if defaults:
590 if isinstance(defaults, CompilationOptions):
591 defaults = defaults.__dict__
592 else:
593 defaults = default_options
594 self.__dict__.update(defaults)
595 self.__dict__.update(kw)
596 if c_compile:
597 self.c_only = 0
598 if c_link:
599 self.obj_only = 0
602 class CompilationResult(object):
603 """
604 Results from the Cython compiler:
606 c_file string or None The generated C source file
607 h_file string or None The generated C header file
608 i_file string or None The generated .pxi file
609 api_file string or None The generated C API .h file
610 listing_file string or None File of error messages
611 object_file string or None Result of compiling the C file
612 extension_file string or None Result of linking the object file
613 num_errors integer Number of compilation errors
614 compilation_source CompilationSource
615 """
617 def __init__(self):
618 self.c_file = None
619 self.h_file = None
620 self.i_file = None
621 self.api_file = None
622 self.listing_file = None
623 self.object_file = None
624 self.extension_file = None
625 self.main_source_file = None
628 class CompilationResultSet(dict):
629 """
630 Results from compiling multiple Pyrex source files. A mapping
631 from source file paths to CompilationResult instances. Also
632 has the following attributes:
634 num_errors integer Total number of compilation errors
635 """
637 num_errors = 0
639 def add(self, source, result):
640 self[source] = result
641 self.num_errors += result.num_errors
644 def compile_single(source, options, full_module_name = None):
645 """
646 compile_single(source, options, full_module_name)
648 Compile the given Pyrex implementation file and return a CompilationResult.
649 Always compiles a single file; does not perform timestamp checking or
650 recursion.
651 """
652 return run_pipeline(source, options, full_module_name)
655 def compile_multiple(sources, options):
656 """
657 compile_multiple(sources, options)
659 Compiles the given sequence of Pyrex implementation files and returns
660 a CompilationResultSet. Performs timestamp checking and/or recursion
661 if these are specified in the options.
662 """
663 sources = [os.path.abspath(source) for source in sources]
664 processed = set()
665 results = CompilationResultSet()
666 recursive = options.recursive
667 timestamps = options.timestamps
668 if timestamps is None:
669 timestamps = recursive
670 verbose = options.verbose or ((recursive or timestamps) and not options.quiet)
671 for source in sources:
672 if source not in processed:
673 # Compiling multiple sources in one context doesn't quite
674 # work properly yet.
675 if not timestamps or context.c_file_out_of_date(source):
676 if verbose:
677 sys.stderr.write("Compiling %s\n" % source)
679 result = run_pipeline(source, options)
680 results.add(source, result)
681 processed.add(source)
682 if recursive:
683 for module_name in context.find_cimported_module_names(source):
684 path = context.find_pyx_file(module_name, [source])
685 if path:
686 sources.append(path)
687 else:
688 sys.stderr.write(
689 "Cannot find .pyx file for cimported module '%s'\n" % module_name)
690 return results
692 def compile(source, options = None, c_compile = 0, c_link = 0,
693 full_module_name = None, **kwds):
694 """
695 compile(source [, options], [, <option> = <value>]...)
697 Compile one or more Pyrex implementation files, with optional timestamp
698 checking and recursing on dependecies. The source argument may be a string
699 or a sequence of strings If it is a string and no recursion or timestamp
700 checking is requested, a CompilationResult is returned, otherwise a
701 CompilationResultSet is returned.
702 """
703 options = CompilationOptions(defaults = options, c_compile = c_compile,
704 c_link = c_link, **kwds)
705 if isinstance(source, basestring) and not options.timestamps \
706 and not options.recursive:
707 return compile_single(source, options, full_module_name)
708 else:
709 return compile_multiple(source, options)
711 #------------------------------------------------------------------------
712 #
713 # Main command-line entry point
714 #
715 #------------------------------------------------------------------------
717 def main(command_line = 0):
718 args = sys.argv[1:]
719 any_failures = 0
720 if command_line:
721 from CmdLine import parse_command_line
722 options, sources = parse_command_line(args)
723 else:
724 options = CompilationOptions(default_options)
725 sources = args
727 if options.show_version:
728 sys.stderr.write("Cython version %s\n" % Version.version)
729 if options.working_path!="":
730 os.chdir(options.working_path)
731 try:
732 result = compile(sources, options)
733 if result.num_errors > 0:
734 any_failures = 1
735 except (EnvironmentError, PyrexError), e:
736 sys.stderr.write(str(e) + '\n')
737 any_failures = 1
738 if any_failures:
739 sys.exit(1)
743 #------------------------------------------------------------------------
744 #
745 # Set the default options depending on the platform
746 #
747 #------------------------------------------------------------------------
749 default_options = dict(
750 show_version = 0,
751 use_listing_file = 0,
752 errors_to_stderr = 1,
753 c_only = 1,
754 obj_only = 1,
755 cplus = 0,
756 output_file = None,
757 annotate = False,
758 generate_pxi = 0,
759 working_path = "",
760 recursive = 0,
761 timestamps = None,
762 verbose = 0,
763 quiet = 0,
764 pragma_overrides = {},
765 emit_linenums = False,
766 )
767 if sys.platform == "mac":
768 from Cython.Mac.MacSystem import c_compile, c_link, CCompilerError
769 default_options['use_listing_file'] = 1
770 elif sys.platform == "darwin":
771 from Cython.Mac.DarwinSystem import c_compile, c_link, CCompilerError
772 else:
773 c_compile = None
774 c_link = None
