Main.py 25.7 KB
Newer Older
William Stein's avatar
William Stein committed
1
#
2
#   Cython Top Level
William Stein's avatar
William Stein committed
3 4
#

5
import os, sys, re, codecs
Stefan Behnel's avatar
Stefan Behnel committed
6 7
if sys.version_info[:2] < (2, 4):
    sys.stderr.write("Sorry, Cython requires Python 2.4 or later\n")
William Stein's avatar
William Stein committed
8 9 10
    sys.exit(1)

import Errors
11 12
# Do not import Parsing here, import it when needed, because Parsing imports
# Nodes, which globally needs debug command line options initialized to set a
13
# conditional metaclass. These options are processed by CmdLine called from
14 15
# main() in this file.
# import Parsing
William Stein's avatar
William Stein committed
16
import Version
17
from Scanning import PyrexScanner, FileSourceDescriptor
18
from Errors import PyrexError, CompileError, error, warning
Stefan Behnel's avatar
Stefan Behnel committed
19
from Symtab import ModuleScope
20
from Cython import Utils
21
import Options
22

23 24
module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$")

William Stein's avatar
William Stein committed
25 26
verbose = 0

27
class CompilationData(object):
28 29 30 31 32 33 34 35 36 37 38 39 40
    #  Bundles the information that is passed from transform to transform.
    #  (For now, this is only)

    #  While Context contains every pxd ever loaded, path information etc.,
    #  this only contains the data related to a single compilation pass
    #
    #  pyx                   ModuleNode              Main code tree of this compilation.
    #  pxds                  {string : ModuleNode}   Trees for the pxds used in the pyx.
    #  codewriter            CCodeWriter             Where to output final code.
    #  options               CompilationOptions
    #  result                CompilationResult
    pass

41
class Context(object):
William Stein's avatar
William Stein committed
42
    #  This class encapsulates the context needed for compiling
43
    #  one or more Cython implementation files along with their
William Stein's avatar
William Stein committed
44 45 46 47 48 49
    #  associated and imported declaration files. It includes
    #  the root of the module import namespace and the list
    #  of directories to search for include files.
    #
    #  modules               {string : ModuleScope}
    #  include_directories   [string]
Stefan Behnel's avatar
Stefan Behnel committed
50
    #  future_directives     [object]
51
    #  language_level        int     currently 2 or 3 for Python 2/3
52

53 54
    cython_scope = None

55 56
    def __init__(self, include_directories, compiler_directives, cpp=False,
                 language_level=2, options=None, create_testscope=True):
57 58 59
        # cython_scope is a hack, set to False by subclasses, in order to break
        # an infinite loop.
        # Better code organization would fix it.
Vitja Makarov's avatar
Vitja Makarov committed
60

61
        import Builtin, CythonScope
62
        self.modules = {"__builtin__" : Builtin.builtin_scope}
63
        self.cython_scope = CythonScope.create_cython_scope(self)
64
        self.modules["cython"] = self.cython_scope
William Stein's avatar
William Stein committed
65
        self.include_directories = include_directories
Stefan Behnel's avatar
Stefan Behnel committed
66
        self.future_directives = set()
67
        self.compiler_directives = compiler_directives
Robert Bradshaw's avatar
Robert Bradshaw committed
68
        self.cpp = cpp
69
        self.options = options
70

71 72
        self.pxds = {} # full name -> node tree

73 74
        standard_include_path = os.path.abspath(os.path.normpath(
            os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
75 76
        self.include_directories = include_directories + [standard_include_path]

77
        self.set_language_level(language_level)
78

79
        self.gdb_debug_outputwriter = None
80 81 82 83

    def set_language_level(self, level):
        self.language_level = level
        if level >= 3:
84 85
            from Future import print_function, unicode_literals, absolute_import
            self.future_directives.update([print_function, unicode_literals, absolute_import])
86
            self.modules['builtins'] = self.modules['__builtin__']
87

88
    # pipeline creation functions can now be found in Pipeline.py
89 90

    def process_pxd(self, source_desc, scope, module_name):
91
        import Pipeline
92 93
        if isinstance(source_desc, FileSourceDescriptor) and source_desc._file_type == 'pyx':
            source = CompilationSource(source_desc, module_name, os.getcwd())
94 95 96
            result_sink = create_default_resultobj(source, self.options)
            pipeline = Pipeline.create_pyx_as_pxd_pipeline(self, result_sink)
            result = Pipeline.run_pipeline(pipeline, source)
97
        else:
98 99
            pipeline = Pipeline.create_pxd_pipeline(self, scope, module_name)
            result = Pipeline.run_pipeline(pipeline, source_desc)
100
        return result
101

102 103 104
    def nonfatal_error(self, exc):
        return Errors.report_error(exc)

105
    def find_module(self, module_name,
106
            relative_to = None, pos = None, need_pxd = 1, check_module_name = True):
William Stein's avatar
William Stein committed
107 108 109 110 111 112 113 114 115
        # Finds and returns the module scope corresponding to
        # the given relative or absolute module name. If this
        # is the first time the module has been requested, finds
        # the corresponding .pxd file and process it.
        # If relative_to is not None, it must be a module scope,
        # and the module will first be searched for relative to
        # that module, provided its name is not a dotted name.
        debug_find_module = 0
        if debug_find_module:
Stefan Behnel's avatar
Stefan Behnel committed
116 117
            print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (
                    module_name, relative_to, pos, need_pxd))
118

William Stein's avatar
William Stein committed
119 120
        scope = None
        pxd_pathname = None
121
        if check_module_name and not module_name_pattern.match(module_name):
Stefan Behnel's avatar
Stefan Behnel committed
122 123 124
            if pos is None:
                pos = (module_name, 0, 0)
            raise CompileError(pos,
125
                "'%s' is not a valid module name" % module_name)
William Stein's avatar
William Stein committed
126 127
        if "." not in module_name and relative_to:
            if debug_find_module:
Stefan Behnel's avatar
Stefan Behnel committed
128
                print("...trying relative import")
William Stein's avatar
William Stein committed
129 130 131 132 133 134 135 136
            scope = relative_to.lookup_submodule(module_name)
            if not scope:
                qualified_name = relative_to.qualify_name(module_name)
                pxd_pathname = self.find_pxd_file(qualified_name, pos)
                if pxd_pathname:
                    scope = relative_to.find_submodule(module_name)
        if not scope:
            if debug_find_module:
Stefan Behnel's avatar
Stefan Behnel committed
137
                print("...trying absolute import")
William Stein's avatar
William Stein committed
138 139 140 141
            scope = self
            for name in module_name.split("."):
                scope = scope.find_submodule(name)
        if debug_find_module:
Stefan Behnel's avatar
Stefan Behnel committed
142
            print("...scope =", scope)
William Stein's avatar
William Stein committed
143 144
        if not scope.pxd_file_loaded:
            if debug_find_module:
Stefan Behnel's avatar
Stefan Behnel committed
145
                print("...pxd not loaded")
William Stein's avatar
William Stein committed
146 147 148
            scope.pxd_file_loaded = 1
            if not pxd_pathname:
                if debug_find_module:
Stefan Behnel's avatar
Stefan Behnel committed
149
                    print("...looking for pxd file")
William Stein's avatar
William Stein committed
150 151
                pxd_pathname = self.find_pxd_file(module_name, pos)
                if debug_find_module:
Stefan Behnel's avatar
Stefan Behnel committed
152
                    print("......found ", pxd_pathname)
William Stein's avatar
William Stein committed
153
                if not pxd_pathname and need_pxd:
154 155 156 157 158
                    package_pathname = self.search_include_directories(module_name, ".py", pos)
                    if package_pathname and package_pathname.endswith('__init__.py'):
                        pass
                    else:
                        error(pos, "'%s.pxd' not found" % module_name)
William Stein's avatar
William Stein committed
159 160 161
            if pxd_pathname:
                try:
                    if debug_find_module:
Stefan Behnel's avatar
Stefan Behnel committed
162
                        print("Context.find_module: Parsing %s" % pxd_pathname)
163 164 165 166
                    rel_path = module_name.replace('.', os.sep) + os.path.splitext(pxd_pathname)[1]
                    if not pxd_pathname.endswith(rel_path):
                        rel_path = pxd_pathname # safety measure to prevent printing incorrect paths
                    source_desc = FileSourceDescriptor(pxd_pathname, rel_path)
167 168 169 170
                    err, result = self.process_pxd(source_desc, scope, module_name)
                    if err:
                        raise err
                    (pxd_codenodes, pxd_scope) = result
171
                    self.pxds[module_name] = (pxd_codenodes, pxd_scope)
William Stein's avatar
William Stein committed
172 173 174
                except CompileError:
                    pass
        return scope
175

176 177 178
    def find_pxd_file(self, qualified_name, pos):
        # Search include path for the .pxd file corresponding to the
        # given fully-qualified module name.
179 180 181 182 183
        # Will find either a dotted filename or a file in a
        # package directory. If a source file position is given,
        # the directory containing the source file is searched first
        # for a dotted filename, and its containing package root
        # directory is searched first for a non-dotted filename.
184
        pxd = self.search_include_directories(qualified_name, ".pxd", pos, sys_path=True)
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
        if pxd is None: # XXX Keep this until Includes/Deprecated is removed
            if (qualified_name.startswith('python') or
                qualified_name in ('stdlib', 'stdio', 'stl')):
                standard_include_path = os.path.abspath(os.path.normpath(
                        os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
                deprecated_include_path = os.path.join(standard_include_path, 'Deprecated')
                self.include_directories.append(deprecated_include_path)
                try:
                    pxd = self.search_include_directories(qualified_name, ".pxd", pos)
                finally:
                    self.include_directories.pop()
                if pxd:
                    name = qualified_name
                    if name.startswith('python'):
                        warning(pos, "'%s' is deprecated, use 'cpython'" % name, 1)
                    elif name in ('stdlib', 'stdio'):
                        warning(pos, "'%s' is deprecated, use 'libc.%s'" % (name, name), 1)
                    elif name in ('stl'):
                        warning(pos, "'%s' is deprecated, use 'libcpp.*.*'" % name, 1)
204 205
        if pxd is None and Options.cimport_from_pyx:
            return self.find_pyx_file(qualified_name, pos)
206
        return pxd
207 208 209 210 211

    def find_pyx_file(self, qualified_name, pos):
        # Search include path for the .pyx file corresponding to the
        # given fully-qualified module name, as for find_pxd_file().
        return self.search_include_directories(qualified_name, ".pyx", pos)
212

William Stein's avatar
William Stein committed
213 214 215
    def find_include_file(self, filename, pos):
        # Search list of include directories for filename.
        # Reports an error and returns None if not found.
216
        path = self.search_include_directories(filename, "", pos,
217
                                               include=True)
William Stein's avatar
William Stein committed
218 219 220
        if not path:
            error(pos, "'%s' not found" % filename)
        return path
221

222
    def search_include_directories(self, qualified_name, suffix, pos,
223
                                   include=False, sys_path=False):
224 225
        return Utils.search_include_directories(
            tuple(self.include_directories), qualified_name, suffix, pos, include, sys_path)
William Stein's avatar
William Stein committed
226

227
    def find_root_package_dir(self, file_path):
228
        return Utils.find_root_package_dir(file_path)
229

230
    def check_package_dir(self, dir, package_names):
231
        return Utils.check_package_dir(dir, tuple(package_names))
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

    def c_file_out_of_date(self, source_path):
        c_path = Utils.replace_suffix(source_path, ".c")
        if not os.path.exists(c_path):
            return 1
        c_time = Utils.modification_time(c_path)
        if Utils.file_newer_than(source_path, c_time):
            return 1
        pos = [source_path]
        pxd_path = Utils.replace_suffix(source_path, ".pxd")
        if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time):
            return 1
        for kind, name in self.read_dependency_file(source_path):
            if kind == "cimport":
                dep_path = self.find_pxd_file(name, pos)
            elif kind == "include":
                dep_path = self.search_include_directories(name, pos)
            else:
                continue
            if dep_path and Utils.file_newer_than(dep_path, c_time):
                return 1
        return 0
254

255 256 257
    def find_cimported_module_names(self, source_path):
        return [ name for kind, name in self.read_dependency_file(source_path)
                 if kind == "cimport" ]
258 259

    def is_package_dir(self, dir_path):
260
        return Utils.is_package_dir(dir_path)
261

262
    def read_dependency_file(self, source_path):
263
        dep_path = Utils.replace_suffix(source_path, ".dep")
264 265 266 267 268 269 270 271 272 273
        if os.path.exists(dep_path):
            f = open(dep_path, "rU")
            chunks = [ line.strip().split(" ", 1)
                       for line in f.readlines()
                       if " " in line.strip() ]
            f.close()
            return chunks
        else:
            return ()

William Stein's avatar
William Stein committed
274 275 276 277 278 279 280 281
    def lookup_submodule(self, name):
        # Look up a top-level module. Returns None if not found.
        return self.modules.get(name, None)

    def find_submodule(self, name):
        # Find a top-level module, creating a new one if needed.
        scope = self.lookup_submodule(name)
        if not scope:
282
            scope = ModuleScope(name,
William Stein's avatar
William Stein committed
283 284 285 286
                parent_module = None, context = self)
            self.modules[name] = scope
        return scope

287
    def parse(self, source_desc, scope, pxd, full_module_name):
288 289
        if not isinstance(source_desc, FileSourceDescriptor):
            raise RuntimeError("Only file sources for code supported")
290
        source_filename = source_desc.filename
Robert Bradshaw's avatar
Robert Bradshaw committed
291
        scope.cpp = self.cpp
William Stein's avatar
William Stein committed
292 293
        # Parse the given source file and return a parse tree.
        try:
294
            f = Utils.open_source_file(source_filename, "rU")
295
            try:
296
                import Parsing
297
                s = PyrexScanner(f, source_desc, source_encoding = f.encoding,
298
                                 scope = scope, context = self)
Stefan Behnel's avatar
Stefan Behnel committed
299
                tree = Parsing.p_module(s, pxd, full_module_name)
300 301
            finally:
                f.close()
302
        except UnicodeDecodeError, e:
Stefan Behnel's avatar
Stefan Behnel committed
303 304
            #import traceback
            #traceback.print_exc()
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
            line = 1
            column = 0
            msg = e.args[-1]
            position = e.args[2]
            encoding = e.args[0]

            for idx, c in enumerate(open(source_filename, "rb").read()):
                if c in (ord('\n'), '\n'):
                    line += 1
                    column = 0
                if idx == position:
                    break

                column += 1

            error((source_desc, line, column),
                  "Decoding error, missing or incorrect coding=<encoding-name> "
                  "at top of source (cannot decode with encoding %r: %s)" % (encoding, msg))

William Stein's avatar
William Stein committed
324 325 326 327
        if Errors.num_errors > 0:
            raise CompileError
        return tree

328
    def extract_module_name(self, path, options):
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
        # Find fully_qualified module name from the full pathname
        # of a source file.
        dir, filename = os.path.split(path)
        module_name, _ = os.path.splitext(filename)
        if "." in module_name:
            return module_name
        names = [module_name]
        while self.is_package_dir(dir):
            parent, package_name = os.path.split(dir)
            if parent == dir:
                break
            names.append(package_name)
            dir = parent
        names.reverse()
        return ".".join(names)
William Stein's avatar
William Stein committed
344

345
    def setup_errors(self, options, result):
346
        Errors.reset() # clear any remaining error state
347 348
        if options.use_listing_file:
            result.listing_file = Utils.replace_suffix(source, ".lis")
349
            path = result.listing_file
350
        else:
351 352 353
            path = None
        Errors.open_listing_file(path=path,
                                 echo_to_stderr=options.errors_to_stderr)
354

355
    def teardown_errors(self, err, options, result):
356 357 358
        source_desc = result.compilation_source.source_desc
        if not isinstance(source_desc, FileSourceDescriptor):
            raise RuntimeError("Only file sources for code supported")
359 360 361
        Errors.close_listing_file()
        result.num_errors = Errors.num_errors
        if result.num_errors > 0:
362 363
            err = True
        if err and result.c_file:
364 365 366 367 368 369
            try:
                Utils.castrate_file(result.c_file, os.stat(source_desc.filename))
            except EnvironmentError:
                pass
            result.c_file = None

370
def create_default_resultobj(compilation_source, options):
371
    result = CompilationResult()
372
    result.main_source_file = compilation_source.source_desc.filename
373
    result.compilation_source = compilation_source
374
    source_desc = compilation_source.source_desc
375
    if options.output_file:
376
        result.c_file = os.path.join(compilation_source.cwd, options.output_file)
377 378 379 380 381 382 383 384
    else:
        if options.cplus:
            c_suffix = ".cpp"
        else:
            c_suffix = ".c"
        result.c_file = Utils.replace_suffix(source_desc.filename, c_suffix)
    return result

385
def run_pipeline(source, options, full_module_name=None, context=None):
386
    import Pipeline
387

388 389
    if context is None:
        context = options.create_context()
390 391 392

    # Set up source object
    cwd = os.getcwd()
393 394
    abs_path = os.path.abspath(source)
    source_ext = os.path.splitext(source)[1]
395
    full_module_name = full_module_name or context.extract_module_name(source, options)
396

397 398 399 400 401 402 403
    if options.relative_path_in_code_position_comments:
        rel_path = full_module_name.replace('.', os.sep) + source_ext
        if not abs_path.endswith(rel_path):
            rel_path = source # safety measure to prevent printing incorrect paths
    else:
        rel_path = abs_path
    source_desc = FileSourceDescriptor(abs_path, rel_path)
404
    source = CompilationSource(source_desc, full_module_name, cwd)
405

406 407
    # Set up result object
    result = create_default_resultobj(source, options)
408

409 410 411 412 413 414
    if options.annotate is None:
        # By default, decide based on whether an html file already exists.
        html_filename = os.path.splitext(result.c_file)[0] + ".html"
        if os.path.exists(html_filename):
            line = codecs.open(html_filename, "r", encoding="UTF-8").readline()
            if line.startswith(u'<!-- Generated by Cython'):
Robert Bradshaw's avatar
Robert Bradshaw committed
415
                options.annotate = True
416

417
    # Get pipeline
418
    if source_ext.lower() == '.py' or not source_ext:
419
        pipeline = Pipeline.create_py_pipeline(context, options, result)
420
    else:
421
        pipeline = Pipeline.create_pyx_pipeline(context, options, result)
422

423
    context.setup_errors(options, result)
424
    err, enddata = Pipeline.run_pipeline(pipeline, source)
425
    context.teardown_errors(err, options, result)
426
    return result
427

Robert Bradshaw's avatar
Robert Bradshaw committed
428

William Stein's avatar
William Stein committed
429 430
#------------------------------------------------------------------------
#
431
#  Main Python entry points
William Stein's avatar
William Stein committed
432 433 434
#
#------------------------------------------------------------------------

435 436 437 438 439 440 441 442 443 444
class CompilationSource(object):
    """
    Contains the data necesarry to start up a compilation pipeline for
    a single compilation unit.
    """
    def __init__(self, source_desc, full_module_name, cwd):
        self.source_desc = source_desc
        self.full_module_name = full_module_name
        self.cwd = cwd

445
class CompilationOptions(object):
William Stein's avatar
William Stein committed
446
    """
447
    Options to the Cython compiler:
448

William Stein's avatar
William Stein committed
449 450 451 452 453
    show_version      boolean   Display version number
    use_listing_file  boolean   Generate a .lis file
    errors_to_stderr  boolean   Echo errors to stderr when using .lis
    include_path      [string]  Directories to search for include files
    output_file       string    Name of generated .c file
454
    generate_pxi      boolean   Generate .pxi file for public declarations
455 456 457
    recursive         boolean   Recursively find and compile dependencies
    timestamps        boolean   Only compile changed source files. If None,
                                defaults to true when recursive is true.
458
    verbose           boolean   Always print source names being compiled
459
    quiet             boolean   Don't print source names in recursive mode
460
    compiler_directives  dict      Overrides for pragma options (see Options.py)
461
    evaluate_tree_assertions boolean  Test support: evaluate parse tree assertions
462
    language_level    integer   The Python language level: 2 or 3
463

William Stein's avatar
William Stein committed
464 465
    cplus             boolean   Compile as c++ code
    """
466

467
    def __init__(self, defaults = None, **kw):
William Stein's avatar
William Stein committed
468 469
        self.include_path = []
        if defaults:
470 471 472 473 474
            if isinstance(defaults, CompilationOptions):
                defaults = defaults.__dict__
        else:
            defaults = default_options
        self.__dict__.update(defaults)
William Stein's avatar
William Stein committed
475 476
        self.__dict__.update(kw)

Robert Bradshaw's avatar
Robert Bradshaw committed
477 478
    def create_context(self):
        return Context(self.include_path, self.compiler_directives,
479
                       self.cplus, self.language_level, options=self)
Robert Bradshaw's avatar
Robert Bradshaw committed
480

William Stein's avatar
William Stein committed
481

482
class CompilationResult(object):
William Stein's avatar
William Stein committed
483
    """
484
    Results from the Cython compiler:
485

William Stein's avatar
William Stein committed
486 487 488
    c_file           string or None   The generated C source file
    h_file           string or None   The generated C header file
    i_file           string or None   The generated .pxi file
489
    api_file         string or None   The generated C API .h file
William Stein's avatar
William Stein committed
490 491 492 493
    listing_file     string or None   File of error messages
    object_file      string or None   Result of compiling the C file
    extension_file   string or None   Result of linking the object file
    num_errors       integer          Number of compilation errors
494
    compilation_source CompilationSource
William Stein's avatar
William Stein committed
495
    """
496

William Stein's avatar
William Stein committed
497 498 499 500
    def __init__(self):
        self.c_file = None
        self.h_file = None
        self.i_file = None
501
        self.api_file = None
William Stein's avatar
William Stein committed
502 503 504
        self.listing_file = None
        self.object_file = None
        self.extension_file = None
505
        self.main_source_file = None
William Stein's avatar
William Stein committed
506 507


508
class CompilationResultSet(dict):
William Stein's avatar
William Stein committed
509
    """
510 511 512
    Results from compiling multiple Pyrex source files. A mapping
    from source file paths to CompilationResult instances. Also
    has the following attributes:
513

514 515
    num_errors   integer   Total number of compilation errors
    """
516

517 518 519 520 521 522 523 524
    num_errors = 0

    def add(self, source, result):
        self[source] = result
        self.num_errors += result.num_errors


def compile_single(source, options, full_module_name = None):
William Stein's avatar
William Stein committed
525
    """
526
    compile_single(source, options, full_module_name)
527

528 529 530
    Compile the given Pyrex implementation file and return a CompilationResult.
    Always compiles a single file; does not perform timestamp checking or
    recursion.
William Stein's avatar
William Stein committed
531
    """
532 533
    return run_pipeline(source, options, full_module_name)

William Stein's avatar
William Stein committed
534

535 536 537
def compile_multiple(sources, options):
    """
    compile_multiple(sources, options)
538

539 540 541 542
    Compiles the given sequence of Pyrex implementation files and returns
    a CompilationResultSet. Performs timestamp checking and/or recursion
    if these are specified in the options.
    """
543 544
    # run_pipeline creates the context
    # context = options.create_context()
545 546 547 548 549 550 551
    sources = [os.path.abspath(source) for source in sources]
    processed = set()
    results = CompilationResultSet()
    recursive = options.recursive
    timestamps = options.timestamps
    if timestamps is None:
        timestamps = recursive
552
    verbose = options.verbose or ((recursive or timestamps) and not options.quiet)
553
    context = None
554 555
    for source in sources:
        if source not in processed:
556 557
            if context is None:
                context = options.create_context()
558 559 560
            if not timestamps or context.c_file_out_of_date(source):
                if verbose:
                    sys.stderr.write("Compiling %s\n" % source)
561

562
                result = run_pipeline(source, options, context=context)
563
                results.add(source, result)
564 565 566
                # Compiling multiple sources in one context doesn't quite
                # work properly yet.
                context = None
567 568 569 570 571 572 573 574 575 576 577
            processed.add(source)
            if recursive:
                for module_name in context.find_cimported_module_names(source):
                    path = context.find_pyx_file(module_name, [source])
                    if path:
                        sources.append(path)
                    else:
                        sys.stderr.write(
                            "Cannot find .pyx file for cimported module '%s'\n" % module_name)
    return results

578
def compile(source, options = None, full_module_name = None, **kwds):
579 580
    """
    compile(source [, options], [, <option> = <value>]...)
581

582 583 584 585 586 587
    Compile one or more Pyrex implementation files, with optional timestamp
    checking and recursing on dependecies. The source argument may be a string
    or a sequence of strings If it is a string and no recursion or timestamp
    checking is requested, a CompilationResult is returned, otherwise a
    CompilationResultSet is returned.
    """
588
    options = CompilationOptions(defaults = options, **kwds)
589 590 591 592
    if isinstance(source, basestring) and not options.timestamps \
            and not options.recursive:
        return compile_single(source, options, full_module_name)
    else:
593
        return compile_multiple(source, options)
594

William Stein's avatar
William Stein committed
595 596 597 598 599
#------------------------------------------------------------------------
#
#  Main command-line entry point
#
#------------------------------------------------------------------------
600 601
def setuptools_main():
    return main(command_line = 1)
William Stein's avatar
William Stein committed
602 603 604 605 606 607 608 609

def main(command_line = 0):
    args = sys.argv[1:]
    any_failures = 0
    if command_line:
        from CmdLine import parse_command_line
        options, sources = parse_command_line(args)
    else:
610
        options = CompilationOptions(default_options)
William Stein's avatar
William Stein committed
611
        sources = args
612

William Stein's avatar
William Stein committed
613
    if options.show_version:
Stefan Behnel's avatar
Stefan Behnel committed
614
        sys.stderr.write("Cython version %s\n" % Version.version)
Gary Furnish's avatar
-w  
Gary Furnish committed
615 616
    if options.working_path!="":
        os.chdir(options.working_path)
617 618 619
    try:
        result = compile(sources, options)
        if result.num_errors > 0:
William Stein's avatar
William Stein committed
620
            any_failures = 1
621 622 623
    except (EnvironmentError, PyrexError), e:
        sys.stderr.write(str(e) + '\n')
        any_failures = 1
William Stein's avatar
William Stein committed
624 625 626
    if any_failures:
        sys.exit(1)

627 628


William Stein's avatar
William Stein committed
629 630 631 632 633 634
#------------------------------------------------------------------------
#
#  Set the default options depending on the platform
#
#------------------------------------------------------------------------

635
default_options = dict(
William Stein's avatar
William Stein committed
636 637 638 639
    show_version = 0,
    use_listing_file = 0,
    errors_to_stderr = 1,
    cplus = 0,
640
    output_file = None,
641
    annotate = None,
642
    generate_pxi = 0,
Robert Bradshaw's avatar
Robert Bradshaw committed
643
    working_path = "",
644 645
    recursive = 0,
    timestamps = None,
646
    verbose = 0,
647
    quiet = 0,
648
    compiler_directives = {},
649
    evaluate_tree_assertions = False,
650
    emit_linenums = False,
651
    relative_path_in_code_position_comments = True,
652
    c_line_in_traceback = True,
653
    language_level = 2,
654
    gdb_debug = False,
655
    compile_time_env = None,
656
)