diff options
Diffstat (limited to 'debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Main.py')
-rw-r--r-- | debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Main.py | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Main.py b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Main.py new file mode 100644 index 00000000..2769b771 --- /dev/null +++ b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Main.py @@ -0,0 +1,564 @@ +# +# Pyrex Top Level +# + +import os, re, sys +if sys.version_info[:2] < (2, 3): + print >>sys.stderr, "Sorry, Pyrex requires Python 2.3 or later" + sys.exit(1) + +import os +from time import time +import Builtin +import Code +import Errors +import Parsing +import Version +from Errors import PyrexError, CompileError, error +from Scanning import PyrexScanner +from Symtab import BuiltinScope, DefinitionScope, ImplementationScope +from Pyrex.Utils import set, replace_suffix, modification_time, \ + file_newer_than, castrate_file, map_suffix +from Filenames import cplus_suffix, pxd_suffixes, pyx_suffixes, \ + package_init_files, pyx_to_c_suffix + +verbose = 0 +debug_timestamps = 0 + +module_name_pattern = re.compile( + r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$") + +class Context: + # This class encapsulates the context needed for compiling + # one or more Pyrex implementation files along with their + # associated and imported declaration files. It holds + # the root of the module import namespace and the list + # of directories to search for include files. + # + # modules {string : DefinitionScope} + # include_directories [string] + + def __init__(self, include_directories): + self.modules = {"__builtin__" : Builtin.builtin_scope} + self.include_directories = include_directories + + def find_module(self, module_name, + relative_to = None, pos = None, need_pxd = 1): + # 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: + print "Context.find_module: module_name =", module_name, \ + "relative_to =", relative_to, "pos =", pos, "need_pxd =", need_pxd + scope = None + pxd_pathname = None + if "." not in module_name and relative_to: + if debug_find_module: + print "...trying relative import" + 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: + print "...trying absolute import" + scope = self + for name in module_name.split("."): + scope = scope.find_submodule(name) + if debug_find_module: + print "...scope =", scope + if not scope.pxd_file_loaded: + if debug_find_module: + print "...pxd not loaded" + scope.pxd_file_loaded = 1 + if not pxd_pathname: + if debug_find_module: + print "...looking for pxd file" + pxd_pathname = self.find_pxd_file(module_name, pos) + if debug_find_module: + print "......found ", pxd_pathname + if not pxd_pathname and need_pxd: + error(pos, "Cannot find .pxd file for module '%s'" % module_name) + if pxd_pathname: + try: + if debug_find_module: + print "Context.find_module: Parsing", pxd_pathname + pxd_tree = self.parse(pxd_pathname, scope, pxd = 1) + pxd_tree.analyse_declarations(scope) + except CompileError: + pass + return scope + + def find_pxd_file(self, qualified_name, pos): + # Search include path for the .pxd file corresponding to the + # given fully-qualified module name. + # 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. + return self.search_package_directories(qualified_name, pxd_suffixes, pos) + + 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_package_directories(qualified_name, pyx_suffixes, pos) + + def search_package_directories(self, qualified_name, suffixes, pos): + dotted_filenames = [qualified_name + suffix for suffix in suffixes] + if pos: + here = os.path.dirname(pos[0]) + for dotted_filename in dotted_filenames: + path = os.path.join(here, dotted_filename) + if os.path.exists(path): + return path + dirs = self.include_directories + if pos: + here = self.find_root_package_dir(pos[0]) + dirs = [here] + dirs + names = qualified_name.split(".") + package_names = names[:-1] + module_name = names[-1] + filenames = [module_name + suffix for suffix in suffixes] + for root in dirs: + for dotted_filename in dotted_filenames: + path = os.path.join(root, dotted_filename) + if os.path.exists(path): + return path + dir = self.descend_to_package_dir(root, package_names) + if dir: + for filename in filenames: + path = os.path.join(dir, filename) + if os.path.exists(path): + return path + for init_filename in package_init_files: + path = os.path.join(dir, module_name, init_filename) + if os.path.exists(path): + return path + + def find_root_package_dir(self, file_path): + # Given the full pathname of a source file, find the directory + # containing the top-level package that it ultimately belongs to. + dir = os.path.dirname(file_path) + while 1: + if not self.is_package_dir(dir): + return dir + parent = os.path.dirname(dir) + if parent == dir: + return dir + dir = parent + + def descend_to_package_dir(self, root_dir, package_names): + # Starting from the given root directory, look for a nested + # succession of package directories. Returns the full pathname + # of the innermost one, or None. + dir = root_dir + for name in package_names: + dir = os.path.join(dir, name) + if self.is_package_dir(dir): + return dir + + def is_package_dir(self, dir_path): + # Return true if the given directory is a package directory. + for filename in package_init_files: + path = os.path.join(dir_path, filename) + if os.path.exists(path): + return 1 + + def find_include_file(self, filename, pos): + # Search list of include directories for filename. + # Reports an error and returns None if not found. + path = self.search_include_directories(filename, pos) + if not path: + error(pos, "'%s' not found" % filename) + return path + + def search_include_directories(self, filename, pos): + # Search the list of include directories for the given + # file name. If a source file position is given, first + # searches the directory containing that file. Returns + # None if not found, but does not report an error. + dirs = self.include_directories + if pos: + here_dir = os.path.dirname(pos[0]) + dirs = [here_dir] + dirs + for dir in dirs: + path = os.path.join(dir, filename) + if os.path.exists(path): + return path + return None + + 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: + scope = DefinitionScope(name, + parent_module = None, context = self) + self.modules[name] = scope + return scope + + def parse(self, source_filename, scope, pxd): + # Parse the given source file and return a parse tree. + f = open(source_filename, "rU") + s = PyrexScanner(f, source_filename, scope = scope, context = self) + try: + tree = Parsing.p_module(s, pxd) + finally: + f.close() + if Errors.num_errors > 0: + raise CompileError + return tree + + def extract_module_name(self, path): + # 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 "." not in module_name: + if module_name == "__init__": + dir, module_name = os.path.split(dir) + names = [module_name] + while self.is_package_dir(dir): + parent, package_name = os.path.split(dir) + if parent == dir: + break + names.insert(0, package_name) + dir = parent + module_name = ".".join(names) + if not module_name_pattern.match(module_name): + raise CompileError((path, 0, 0), + "'%s' is not a valid module name" % module_name) + return module_name + + def dep_file_out_of_date(self, source_path): + dep_path = replace_suffix(source_path, ".dep") + if not os.path.exists(dep_path): + return 1 + dep_time = modification_time(dep_path) + return file_newer_than(source_path, dep_time) + + def c_file_out_of_date(self, source_path): + if debug_timestamps: + print "Checking whether", source_path, "is out of date" + c_path = map_suffix(source_path, pyx_to_c_suffix, ".c") + if not os.path.exists(c_path): + if debug_timestamps: + print "...yes, c file doesn't exist" + return 1 + c_time = modification_time(c_path) + if file_newer_than(source_path, c_time): + if debug_timestamps: + print "...yes, newer than c file" + return 1 + pos = [source_path] + module_name = self.extract_module_name(source_path) + pxd_path = self.find_pxd_file(module_name, pos) + if pxd_path and file_newer_than(pxd_path, c_time): + if debug_timestamps: + print "...yes, pxd file newer than c file" + return 1 + dep_path = replace_suffix(source_path, ".dep") + if not os.path.exists(dep_path): + if debug_timestamps: + print "...yes, dep file does not exist" + 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 file_newer_than(dep_path, c_time): + if debug_timestamps: + print "...yes,", dep_path, "newer than c file" + return 1 + if debug_timestamps: + print "...no" + + def find_cimported_module_names(self, source_path): + for kind, name in self.read_dependency_file(source_path): + if kind == "cimport": + yield name + + def read_dependency_file(self, source_path): + dep_path = replace_suffix(source_path, ".dep") + if os.path.exists(dep_path): + f = open(dep_path, "rU") + for line in f.readlines(): + chunks = line.strip().split(" ", 1) + if len(chunks) == 2: + yield chunks + f.close() + + def compile(self, source, options = None): + # Compile a Pyrex implementation file in this context + # and return a CompilationResult. + if not options: + options = default_options + result = CompilationResult() + cwd = os.getcwd() + source = os.path.join(cwd, source) + if options.use_listing_file: + result.listing_file = replace_suffix(source, ".lis") + Errors.open_listing_file(result.listing_file, + echo_to_stderr = options.errors_to_stderr) + else: + Errors.open_listing_file(None) + if options.output_file: + result.c_file = os.path.join(cwd, options.output_file) + else: + if options.cplus: + result.c_file = replace_suffix(source, cplus_suffix) + else: + result.c_file = map_suffix(source, pyx_to_c_suffix, ".c") + module_name = self.extract_module_name(source) + initial_pos = (source, 1, 0) + def_scope = self.find_module(module_name, pos = initial_pos, need_pxd = 0) + imp_scope = ImplementationScope(def_scope) + errors_occurred = False + try: + tree = self.parse(source, imp_scope, pxd = 0) + tree.process_implementation(imp_scope, options, result) + except CompileError: + errors_occurred = True + Errors.close_listing_file() + result.num_errors = Errors.num_errors + if result.num_errors > 0: + errors_occurred = True + if errors_occurred and result.c_file: + try: + st = os.stat(source) + castrate_file(result.c_file, st) + except EnvironmentError: + pass + result.c_file = None + if result.c_file and not options.c_only and c_compile: + result.object_file = c_compile(result.c_file, + verbose_flag = options.show_version, + cplus = options.cplus) + if not options.obj_only and c_link: + result.extension_file = c_link(result.object_file, + extra_objects = options.objects, + verbose_flag = options.show_version, + cplus = options.cplus) + return result + +#------------------------------------------------------------------------ +# +# Main Python entry points +# +#------------------------------------------------------------------------ + +class CompilationOptions: + """ + Options to the Pyrex compiler: + + 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 + generate_pxi boolean Generate .pxi file for public declarations + recursive boolean Recursively find and compile dependencies + timestamps boolean Only compile changed source files. If None, + defaults to true when recursive is true. + verbose boolean Always print source names being compiled + quiet boolean Don't print source names in recursive mode + + Following options are experimental and only used on MacOSX: + + c_only boolean Stop after generating C file (default) + obj_only boolean Stop after compiling to .o file + objects [string] Extra .o files to link with + cplus boolean Compile as c++ code + """ + + def __init__(self, defaults = None, c_compile = 0, c_link = 0, **kw): + self.include_path = [] + self.objects = [] + if defaults: + if isinstance(defaults, CompilationOptions): + defaults = defaults.__dict__ + else: + defaults = default_options + self.__dict__.update(defaults) + self.__dict__.update(kw) + if c_compile: + self.c_only = 0 + if c_link: + self.obj_only = 0 + + +class CompilationResult: + """ + Results from the Pyrex compiler: + + 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 + api_file string or None The generated C API .h file + 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 + """ + + def __init__(self): + self.c_file = None + self.h_file = None + self.i_file = None + self.api_file = None + self.listing_file = None + self.object_file = None + self.extension_file = None + + +class CompilationResultSet(dict): + """ + Results from compiling multiple Pyrex source files. A mapping + from source file paths to CompilationResult instances. Also + has the following attributes: + + num_errors integer Total number of compilation errors + """ + + num_errors = 0 + + def add(self, source, result): + self[source] = result + self.num_errors += result.num_errors + + +def compile_single(source, options): + """ + compile_single(source, options) + + Compile the given Pyrex implementation file and return a CompilationResult. + Always compiles a single file; does not perform timestamp checking or + recursion. + """ + context = Context(options.include_path) + return context.compile(source, options) + +def compile_multiple(sources, options): + """ + compile_multiple(sources, options) + + 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. + """ + sources = [os.path.abspath(source) for source in sources] + processed = set() + results = CompilationResultSet() + context = Context(options.include_path) + recursive = options.recursive + timestamps = options.timestamps + if timestamps is None: + timestamps = recursive + verbose = options.verbose or ((recursive or timestamps) and not options.quiet) + for source in sources: + if source not in processed: + if not timestamps or context.c_file_out_of_date(source): + if verbose: + print >>sys.stderr, "Compiling", source + result = context.compile(source, options) + results.add(source, result) + 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: + print >>sys.stderr, \ + "Cannot find .pyx file for cimported module '%s'" % module_name + return results + +def compile(source, options = None, c_compile = 0, c_link = 0, **kwds): + """ + compile(source [, options], [, <option> = <value>]...) + + 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. + """ + options = CompilationOptions(defaults = options, c_compile = c_compile, + c_link = c_link, **kwds) + if isinstance(source, basestring) and not options.timestamps \ + and not options.recursive: + return compile_single(source, options) + else: + return compile_multiple(source, options) + +#------------------------------------------------------------------------ +# +# Main command-line entry point +# +#------------------------------------------------------------------------ + +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: + options = CompilationOptions(default_options) + sources = args + if options.show_version: + print >>sys.stderr, "Pyrex version %s" % Version.version + try: + result = compile(sources, options) + if result.num_errors > 0: + any_failures = 1 + except EnvironmentError, e: + print >>sys.stderr, e + any_failures = 1 + if any_failures: + sys.exit(1) + +#------------------------------------------------------------------------ +# +# Set the default options depending on the platform +# +#------------------------------------------------------------------------ + +default_options = dict( + show_version = 0, + use_listing_file = 0, + errors_to_stderr = 1, + c_only = 1, + obj_only = 1, + cplus = 0, + output_file = None, + generate_pxi = 0, + recursive = 0, + timestamps = None, + verbose = 0, + quiet = 0) + +if sys.platform == "mac": + from Pyrex.Mac.MacSystem import c_compile, c_link, CCompilerError + default_options['use_listing_file'] = 1 +elif sys.platform == "darwin": + from Pyrex.Mac.DarwinSystem import c_compile, c_link, CCompilerError +else: + c_compile = None + c_link = None + + |