6 if sys.version_info[:2] < (2, 2):
7 print >>sys.stderr, "Sorry, Cython requires Python 2.2 or later"
13 from Scanning import PyrexScanner
15 from Errors import PyrexError, CompileError, error
17 from Symtab import BuiltinScope, ModuleScope
19 from Cython.Utils import replace_suffix
20 from Cython import Utils
25 # This class encapsulates the context needed for compiling
26 # one or more Cython implementation files along with their
27 # associated and imported declaration files. It includes
28 # the root of the module import namespace and the list
29 # of directories to search for include files.
31 # modules {string : ModuleScope}
32 # include_directories [string]
34 def __init__(self, include_directories):
35 #self.modules = {"__builtin__" : BuiltinScope()}
37 self.modules = {"__builtin__" : Builtin.builtin_scope}
38 self.include_directories = include_directories
40 def find_module(self, module_name,
41 relative_to = None, pos = None, need_pxd = 1):
42 # Finds and returns the module scope corresponding to
43 # the given relative or absolute module name. If this
44 # is the first time the module has been requested, finds
45 # the corresponding .pxd file and process it.
46 # If relative_to is not None, it must be a module scope,
47 # and the module will first be searched for relative to
48 # that module, provided its name is not a dotted name.
51 print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (
52 module_name, relative_to, pos, need_pxd))
55 if "." not in module_name and relative_to:
57 print("...trying relative import")
58 scope = relative_to.lookup_submodule(module_name)
60 qualified_name = relative_to.qualify_name(module_name)
61 pxd_pathname = self.find_pxd_file(qualified_name, pos)
63 scope = relative_to.find_submodule(module_name)
66 print("...trying absolute import")
68 for name in module_name.split("."):
69 scope = scope.find_submodule(name)
71 print("...scope =", scope)
72 if not scope.pxd_file_loaded:
74 print("...pxd not loaded")
75 scope.pxd_file_loaded = 1
78 print("...looking for pxd file")
79 pxd_pathname = self.find_pxd_file(module_name, pos)
81 print("......found ", pxd_pathname)
82 if not pxd_pathname and need_pxd:
83 error(pos, "'%s.pxd' not found" % module_name)
87 print("Context.find_module: Parsing %s" % pxd_pathname)
88 pxd_tree = self.parse(pxd_pathname, scope.type_names, pxd = 1,
89 full_module_name = module_name)
90 pxd_tree.analyse_declarations(scope)
95 def find_pxd_file(self, module_name, pos):
96 # Search include directories for the .pxd file
97 # corresponding to the given (full) module name.
98 if "." in module_name:
99 pxd_filename = "%s.pxd" % os.path.join(*module_name.split('.'))
101 pxd_filename = "%s.pxd" % module_name
102 return self.search_include_directories(pxd_filename, pos)
104 def find_include_file(self, filename, pos):
105 # Search list of include directories for filename.
106 # Reports an error and returns None if not found.
107 path = self.search_include_directories(filename, pos)
109 error(pos, "'%s' not found" % filename)
112 def search_include_directories(self, filename, pos):
113 # Search the list of include directories for the given
114 # file name. If a source file position is given, first
115 # searches the directory containing that file. Returns
116 # None if not found, but does not report an error.
117 dirs = self.include_directories
119 here_dir = os.path.dirname(pos[0])
120 dirs = [here_dir] + dirs
122 path = os.path.join(dir, filename)
123 if os.path.exists(path):
127 def lookup_submodule(self, name):
128 # Look up a top-level module. Returns None if not found.
129 return self.modules.get(name, None)
131 def find_submodule(self, name):
132 # Find a top-level module, creating a new one if needed.
133 scope = self.lookup_submodule(name)
135 scope = ModuleScope(name,
136 parent_module = None, context = self)
137 self.modules[name] = scope
140 def parse(self, source_filename, type_names, pxd, full_module_name):
141 # Parse the given source file and return a parse tree.
142 f = open(source_filename, "rU")
143 s = PyrexScanner(f, source_filename,
144 type_names = type_names, context = self)
146 tree = Parsing.p_module(s, pxd, full_module_name)
149 if Errors.num_errors > 0:
153 def extract_module_name(self, path, options):
154 # Get the module name out of a source file pathname.
155 _, tail = os.path.split(path)
156 name, _ = os.path.splitext(tail)
159 def compile(self, source, options = None, full_module_name = None):
160 # Compile a Pyrex implementation file in this context
161 # and return a CompilationResult.
163 options = default_options
164 result = CompilationResult()
167 if full_module_name is None:
168 full_module_name, _ = os.path.splitext(source.replace('/', '.'))
170 source = os.path.join(cwd, source)
172 if options.use_listing_file:
173 result.listing_file = replace_suffix(source, ".lis")
174 Errors.open_listing_file(result.listing_file,
175 echo_to_stderr = options.errors_to_stderr)
177 Errors.open_listing_file(None)
178 if options.output_file:
179 result.c_file = os.path.join(cwd, options.output_file)
185 result.c_file = replace_suffix(source, c_suffix)
189 c_stat = os.stat(result.c_file)
190 except EnvironmentError:
192 module_name = full_module_name # self.extract_module_name(source, options)
193 initial_pos = (source, 1, 0)
194 scope = self.find_module(module_name, pos = initial_pos, need_pxd = 0)
195 errors_occurred = False
197 tree = self.parse(source, scope.type_names, pxd = 0, full_module_name = full_module_name)
198 tree.process_implementation(scope, options, result)
200 errors_occurred = True
201 Errors.close_listing_file()
202 result.num_errors = Errors.num_errors
203 if result.num_errors > 0:
204 errors_occurred = True
205 if errors_occurred and result.c_file:
207 #os.unlink(result.c_file)
208 Utils.castrate_file(result.c_file, c_stat)
209 except EnvironmentError:
212 if result.c_file and not options.c_only and c_compile:
213 result.object_file = c_compile(result.c_file,
214 verbose_flag = options.show_version,
215 cplus = options.cplus)
216 if not options.obj_only and c_link:
217 result.extension_file = c_link(result.object_file,
218 extra_objects = options.objects,
219 verbose_flag = options.show_version,
220 cplus = options.cplus)
223 #------------------------------------------------------------------------
225 # Main Python entry point
227 #------------------------------------------------------------------------
229 class CompilationOptions:
231 Options to the Cython compiler:
233 show_version boolean Display version number
234 use_listing_file boolean Generate a .lis file
235 errors_to_stderr boolean Echo errors to stderr when using .lis
236 include_path [string] Directories to search for include files
237 output_file string Name of generated .c file
238 generate_pxi boolean Generate .pxi file for public declarations
240 Following options are experimental and only used on MacOSX:
242 c_only boolean Stop after generating C file (default)
243 obj_only boolean Stop after compiling to .o file
244 objects [string] Extra .o files to link with
245 cplus boolean Compile as c++ code
248 def __init__(self, defaults = None, **kw):
249 self.include_path = []
252 if isinstance(defaults, CompilationOptions):
253 defaults = defaults.__dict__
255 defaults = default_options
256 self.__dict__.update(defaults)
257 self.__dict__.update(kw)
260 class CompilationResult:
262 Results from the Cython compiler:
264 c_file string or None The generated C source file
265 h_file string or None The generated C header file
266 i_file string or None The generated .pxi file
267 api_file string or None The generated C API .h file
268 listing_file string or None File of error messages
269 object_file string or None Result of compiling the C file
270 extension_file string or None Result of linking the object file
271 num_errors integer Number of compilation errors
279 self.listing_file = None
280 self.object_file = None
281 self.extension_file = None
284 def compile(source, options = None, c_compile = 0, c_link = 0,
285 full_module_name = None):
287 compile(source, options = default_options)
289 Compile the given Cython implementation file and return
290 a CompilationResult object describing what was produced.
293 options = default_options
294 options = CompilationOptions(defaults = options)
299 context = Context(options.include_path)
300 return context.compile(source, options, full_module_name)
302 #------------------------------------------------------------------------
304 # Main command-line entry point
306 #------------------------------------------------------------------------
308 def main(command_line = 0):
312 from CmdLine import parse_command_line
313 options, sources = parse_command_line(args)
315 options = default_options
317 if options.show_version:
318 print >>sys.stderr, "Cython version %s" % Version.version
319 context = Context(options.include_path)
320 for source in sources:
322 result = context.compile(source, options)
323 if result.num_errors > 0:
325 except PyrexError, e:
326 print >>sys.stderr, e
331 #------------------------------------------------------------------------
333 # Set the default options depending on the platform
335 #------------------------------------------------------------------------
337 default_options = dict(
339 use_listing_file = 0,
340 errors_to_stderr = 1,
347 if sys.platform == "mac":
348 from Cython.Mac.MacSystem import c_compile, c_link, CCompilerError
349 default_options['use_listing_file'] = 1
350 elif sys.platform == "darwin":
351 from Cython.Mac.DarwinSystem import c_compile, c_link, CCompilerError