Cython has moved to github.
cython-devel
view Cython/Compiler/Main.py @ 1157:075511424c24
Automatic embedding of signatures in docstring (#2)
| author | LisandroDalcin |
|---|---|
| date | Fri Sep 19 16:28:36 2008 +0200 (3 years ago) |
| parents | 7b8970eb4837 |
| children | 947f6697b088 |
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:
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:
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):
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 ResolveOptions
83 from AutoDocTransforms import EmbedSignature
84 from Optimize import FlattenInListTransform, SwitchTransform, OptimizeRefcounting
85 from Buffer import IntroduceBufferAuxiliaryVars
86 from ModuleNode import check_c_classes
88 if pxd:
89 _check_c_classes = None
90 _specific_post_parse = PxdPostParse(self)
91 else:
92 _check_c_classes = check_c_classes
93 _specific_post_parse = None
95 return [
96 NormalizeTree(self),
97 PostParse(self),
98 _specific_post_parse,
99 ResolveOptions(self, self.pragma_overrides),
100 EmbedSignature(self),
101 FlattenInListTransform(),
102 WithTransform(self),
103 DecoratorTransform(self),
104 AnalyseDeclarationsTransform(self),
105 IntroduceBufferAuxiliaryVars(self),
106 _check_c_classes,
107 AnalyseExpressionsTransform(self),
108 SwitchTransform(),
109 OptimizeRefcounting(self),
110 # SpecialFunctions(self),
111 # CreateClosureClasses(context),
112 ]
114 def create_pyx_pipeline(self, options, result):
115 def generate_pyx_code(module_node):
116 module_node.process_implementation(options, result)
117 result.compilation_source = module_node.compilation_source
118 return result
120 def inject_pxd_code(module_node):
121 from textwrap import dedent
122 stats = module_node.body.stats
123 for name, (statlistnode, scope) in self.pxds.iteritems():
124 # Copy over function nodes to the module
125 # (this seems strange -- I believe the right concept is to split
126 # ModuleNode into a ModuleNode and a CodeGenerator, and tell that
127 # CodeGenerator to generate code both from the pyx and pxd ModuleNodes.
128 stats.append(statlistnode)
129 # Until utility code is moved to code generation phase everywhere,
130 # we need to copy it over to the main scope
131 module_node.scope.utility_code_list.extend(scope.utility_code_list)
132 return module_node
134 return ([
135 create_parse(self),
136 ] + self.create_pipeline(pxd=False) + [
137 inject_pxd_code,
138 generate_pyx_code,
139 ])
141 def create_pxd_pipeline(self, scope, module_name):
142 def parse_pxd(source_desc):
143 tree = self.parse(source_desc, scope, pxd=True,
144 full_module_name=module_name)
145 tree.scope = scope
146 tree.is_pxd = True
147 return tree
149 from CodeGeneration import ExtractPxdCode
151 # The pxd pipeline ends up with a CCodeWriter containing the
152 # code of the pxd, as well as a pxd scope.
153 return [parse_pxd] + self.create_pipeline(pxd=True) + [
154 ExtractPxdCode(self),
155 ]
157 def process_pxd(self, source_desc, scope, module_name):
158 pipeline = self.create_pxd_pipeline(scope, module_name)
159 result = self.run_pipeline(pipeline, source_desc)
160 return result
162 def nonfatal_error(self, exc):
163 return Errors.report_error(exc)
165 def run_pipeline(self, pipeline, source):
166 err = None
167 data = source
168 try:
169 for phase in pipeline:
170 if phase is not None:
171 data = phase(data)
172 except CompileError, err:
173 # err is set
174 Errors.report_error(err)
175 except InternalError, err:
176 # Only raise if there was not an earlier error
177 if Errors.num_errors == 0:
178 raise
179 return (err, data)
181 def find_module(self, module_name,
182 relative_to = None, pos = None, need_pxd = 1):
183 # Finds and returns the module scope corresponding to
184 # the given relative or absolute module name. If this
185 # is the first time the module has been requested, finds
186 # the corresponding .pxd file and process it.
187 # If relative_to is not None, it must be a module scope,
188 # and the module will first be searched for relative to
189 # that module, provided its name is not a dotted name.
190 debug_find_module = 0
191 if debug_find_module:
192 print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (
193 module_name, relative_to, pos, need_pxd))
195 scope = None
196 pxd_pathname = None
197 if not module_name_pattern.match(module_name):
198 if pos is None:
199 pos = (module_name, 0, 0)
200 raise CompileError(pos,
201 "'%s' is not a valid module name" % module_name)
202 if "." not in module_name and relative_to:
203 if debug_find_module:
204 print("...trying relative import")
205 scope = relative_to.lookup_submodule(module_name)
206 if not scope:
207 qualified_name = relative_to.qualify_name(module_name)
208 pxd_pathname = self.find_pxd_file(qualified_name, pos)
209 if pxd_pathname:
210 scope = relative_to.find_submodule(module_name)
211 if not scope:
212 if debug_find_module:
213 print("...trying absolute import")
214 scope = self
215 for name in module_name.split("."):
216 scope = scope.find_submodule(name)
217 if debug_find_module:
218 print("...scope =", scope)
219 if not scope.pxd_file_loaded:
220 if debug_find_module:
221 print("...pxd not loaded")
222 scope.pxd_file_loaded = 1
223 if not pxd_pathname:
224 if debug_find_module:
225 print("...looking for pxd file")
226 pxd_pathname = self.find_pxd_file(module_name, pos)
227 if debug_find_module:
228 print("......found ", pxd_pathname)
229 if not pxd_pathname and need_pxd:
230 error(pos, "'%s.pxd' not found" % module_name)
231 if pxd_pathname:
232 try:
233 if debug_find_module:
234 print("Context.find_module: Parsing %s" % pxd_pathname)
235 source_desc = FileSourceDescriptor(pxd_pathname)
236 err, result = self.process_pxd(source_desc, scope, module_name)
237 if err:
238 raise err
239 (pxd_codenodes, pxd_scope) = result
240 self.pxds[module_name] = (pxd_codenodes, pxd_scope)
241 except CompileError:
242 pass
243 return scope
245 def find_pxd_file(self, qualified_name, pos):
246 # Search include path for the .pxd file corresponding to the
247 # given fully-qualified module name.
248 # Will find either a dotted filename or a file in a
249 # package directory. If a source file position is given,
250 # the directory containing the source file is searched first
251 # for a dotted filename, and its containing package root
252 # directory is searched first for a non-dotted filename.
253 return self.search_include_directories(qualified_name, ".pxd", pos)
255 def find_pyx_file(self, qualified_name, pos):
256 # Search include path for the .pyx file corresponding to the
257 # given fully-qualified module name, as for find_pxd_file().
258 return self.search_include_directories(qualified_name, ".pyx", pos)
260 def find_include_file(self, filename, pos):
261 # Search list of include directories for filename.
262 # Reports an error and returns None if not found.
263 path = self.search_include_directories(filename, "", pos,
264 include=True)
265 if not path:
266 error(pos, "'%s' not found" % filename)
267 return path
269 def search_include_directories(self, qualified_name, suffix, pos,
270 include=False):
271 # Search the list of include directories for the given
272 # file name. If a source file position is given, first
273 # searches the directory containing that file. Returns
274 # None if not found, but does not report an error.
275 # The 'include' option will disable package dereferencing.
276 dirs = self.include_directories
277 if pos:
278 file_desc = pos[0]
279 if not isinstance(file_desc, FileSourceDescriptor):
280 raise RuntimeError("Only file sources for code supported")
281 if include:
282 dirs = [os.path.dirname(file_desc.filename)] + dirs
283 else:
284 dirs = [self.find_root_package_dir(file_desc.filename)] + dirs
286 dotted_filename = qualified_name + suffix
287 if not include:
288 names = qualified_name.split('.')
289 package_names = names[:-1]
290 module_name = names[-1]
291 module_filename = module_name + suffix
292 package_filename = "__init__" + suffix
294 for dir in dirs:
295 path = os.path.join(dir, dotted_filename)
296 if os.path.exists(path):
297 return path
298 if not include:
299 package_dir = self.check_package_dir(dir, package_names)
300 if package_dir is not None:
301 path = os.path.join(package_dir, module_filename)
302 if os.path.exists(path):
303 return path
304 path = os.path.join(dir, package_dir, module_name,
305 package_filename)
306 if os.path.exists(path):
307 return path
308 return None
310 def find_root_package_dir(self, file_path):
311 dir = os.path.dirname(file_path)
312 while self.is_package_dir(dir):
313 parent = os.path.dirname(dir)
314 if parent == dir:
315 break
316 dir = parent
317 return dir
319 def is_package_dir(self, dir):
320 package_init = os.path.join(dir, "__init__.py")
321 return os.path.exists(package_init) or \
322 os.path.exists(package_init + "x") # same with .pyx
324 def check_package_dir(self, dir, package_names):
325 package_dir = os.path.join(dir, *package_names)
326 if not os.path.exists(package_dir):
327 return None
328 for dirname in package_names:
329 dir = os.path.join(dir, dirname)
330 if not self.is_package_dir(dir):
331 return None
332 return package_dir
334 def c_file_out_of_date(self, source_path):
335 c_path = Utils.replace_suffix(source_path, ".c")
336 if not os.path.exists(c_path):
337 return 1
338 c_time = Utils.modification_time(c_path)
339 if Utils.file_newer_than(source_path, c_time):
340 return 1
341 pos = [source_path]
342 pxd_path = Utils.replace_suffix(source_path, ".pxd")
343 if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time):
344 return 1
345 for kind, name in self.read_dependency_file(source_path):
346 if kind == "cimport":
347 dep_path = self.find_pxd_file(name, pos)
348 elif kind == "include":
349 dep_path = self.search_include_directories(name, pos)
350 else:
351 continue
352 if dep_path and Utils.file_newer_than(dep_path, c_time):
353 return 1
354 return 0
356 def find_cimported_module_names(self, source_path):
357 return [ name for kind, name in self.read_dependency_file(source_path)
358 if kind == "cimport" ]
360 def is_package_dir(self, dir_path):
361 # Return true if the given directory is a package directory.
362 for filename in ("__init__.py", "__init__.pyx"):
363 path = os.path.join(dir_path, filename)
364 if os.path.exists(path):
365 return 1
367 def read_dependency_file(self, source_path):
368 dep_path = Utils.replace_suffix(source_path, ".dep")
369 if os.path.exists(dep_path):
370 f = open(dep_path, "rU")
371 chunks = [ line.strip().split(" ", 1)
372 for line in f.readlines()
373 if " " in line.strip() ]
374 f.close()
375 return chunks
376 else:
377 return ()
379 def lookup_submodule(self, name):
380 # Look up a top-level module. Returns None if not found.
381 return self.modules.get(name, None)
383 def find_submodule(self, name):
384 # Find a top-level module, creating a new one if needed.
385 scope = self.lookup_submodule(name)
386 if not scope:
387 scope = ModuleScope(name,
388 parent_module = None, context = self)
389 self.modules[name] = scope
390 return scope
392 def parse(self, source_desc, scope, pxd, full_module_name):
393 if not isinstance(source_desc, FileSourceDescriptor):
394 raise RuntimeError("Only file sources for code supported")
395 source_filename = Utils.encode_filename(source_desc.filename)
396 # Parse the given source file and return a parse tree.
397 try:
398 f = Utils.open_source_file(source_filename, "rU")
399 try:
400 s = PyrexScanner(f, source_desc, source_encoding = f.encoding,
401 scope = scope, context = self)
402 tree = Parsing.p_module(s, pxd, full_module_name)
403 finally:
404 f.close()
405 except UnicodeDecodeError, msg:
406 #import traceback
407 #traceback.print_exc()
408 error((source_desc, 0, 0), "Decoding error, missing or incorrect coding=<encoding-name> at top of source (%s)" % msg)
409 if Errors.num_errors > 0:
410 raise CompileError
411 return tree
413 def extract_module_name(self, path, options):
414 # Find fully_qualified module name from the full pathname
415 # of a source file.
416 dir, filename = os.path.split(path)
417 module_name, _ = os.path.splitext(filename)
418 if "." in module_name:
419 return module_name
420 if module_name == "__init__":
421 dir, module_name = os.path.split(dir)
422 names = [module_name]
423 while self.is_package_dir(dir):
424 parent, package_name = os.path.split(dir)
425 if parent == dir:
426 break
427 names.append(package_name)
428 dir = parent
429 names.reverse()
430 return ".".join(names)
432 def setup_errors(self, options):
433 if options.use_listing_file:
434 result.listing_file = Utils.replace_suffix(source, ".lis")
435 Errors.open_listing_file(result.listing_file,
436 echo_to_stderr = options.errors_to_stderr)
437 else:
438 Errors.open_listing_file(None)
440 def teardown_errors(self, err, options, result):
441 source_desc = result.compilation_source.source_desc
442 if not isinstance(source_desc, FileSourceDescriptor):
443 raise RuntimeError("Only file sources for code supported")
444 Errors.close_listing_file()
445 result.num_errors = Errors.num_errors
446 if result.num_errors > 0:
447 err = True
448 if err and result.c_file:
449 try:
450 Utils.castrate_file(result.c_file, os.stat(source_desc.filename))
451 except EnvironmentError:
452 pass
453 result.c_file = None
454 if result.c_file and not options.c_only and c_compile:
455 result.object_file = c_compile(result.c_file,
456 verbose_flag = options.show_version,
457 cplus = options.cplus)
458 if not options.obj_only and c_link:
459 result.extension_file = c_link(result.object_file,
460 extra_objects = options.objects,
461 verbose_flag = options.show_version,
462 cplus = options.cplus)
464 def create_parse(context):
465 def parse(compsrc):
466 source_desc = compsrc.source_desc
467 full_module_name = compsrc.full_module_name
468 initial_pos = (source_desc, 1, 0)
469 scope = context.find_module(full_module_name, pos = initial_pos, need_pxd = 0)
470 tree = context.parse(source_desc, scope, pxd = 0, full_module_name = full_module_name)
471 tree.compilation_source = compsrc
472 tree.scope = scope
473 tree.is_pxd = False
474 return tree
475 return parse
477 def create_default_resultobj(compilation_source, options):
478 result = CompilationResult()
479 result.main_source_file = compilation_source.source_desc.filename
480 result.compilation_source = compilation_source
481 source_desc = compilation_source.source_desc
482 if options.output_file:
483 result.c_file = os.path.join(compilation_source.cwd, options.output_file)
484 else:
485 if options.cplus:
486 c_suffix = ".cpp"
487 else:
488 c_suffix = ".c"
489 result.c_file = Utils.replace_suffix(source_desc.filename, c_suffix)
490 return result
492 def run_pipeline(source, options, full_module_name = None):
493 # Set up context
494 context = Context(options.include_path, options.pragma_overrides)
496 # Set up source object
497 cwd = os.getcwd()
498 source_desc = FileSourceDescriptor(os.path.join(cwd, source))
499 full_module_name = full_module_name or context.extract_module_name(source, options)
500 source = CompilationSource(source_desc, full_module_name, cwd)
502 # Set up result object
503 result = create_default_resultobj(source, options)
505 # Get pipeline
506 pipeline = context.create_pyx_pipeline(options, result)
508 context.setup_errors(options)
509 err, enddata = context.run_pipeline(pipeline, source)
510 context.teardown_errors(err, options, result)
511 return result
513 #------------------------------------------------------------------------
514 #
515 # Main Python entry points
516 #
517 #------------------------------------------------------------------------
519 class CompilationSource(object):
520 """
521 Contains the data necesarry to start up a compilation pipeline for
522 a single compilation unit.
523 """
524 def __init__(self, source_desc, full_module_name, cwd):
525 self.source_desc = source_desc
526 self.full_module_name = full_module_name
527 self.cwd = cwd
529 class CompilationOptions:
530 """
531 Options to the Cython compiler:
533 show_version boolean Display version number
534 use_listing_file boolean Generate a .lis file
535 errors_to_stderr boolean Echo errors to stderr when using .lis
536 include_path [string] Directories to search for include files
537 output_file string Name of generated .c file
538 generate_pxi boolean Generate .pxi file for public declarations
539 recursive boolean Recursively find and compile dependencies
540 timestamps boolean Only compile changed source files. If None,
541 defaults to true when recursive is true.
542 verbose boolean Always print source names being compiled
543 quiet boolean Don't print source names in recursive mode
544 pragma_overrides dict Overrides for pragma options (see Options.py)
546 Following options are experimental and only used on MacOSX:
548 c_only boolean Stop after generating C file (default)
549 obj_only boolean Stop after compiling to .o file
550 objects [string] Extra .o files to link with
551 cplus boolean Compile as c++ code
552 """
554 def __init__(self, defaults = None, c_compile = 0, c_link = 0, **kw):
555 self.include_path = []
556 self.objects = []
557 if defaults:
558 if isinstance(defaults, CompilationOptions):
559 defaults = defaults.__dict__
560 else:
561 defaults = default_options
562 self.__dict__.update(defaults)
563 self.__dict__.update(kw)
564 if c_compile:
565 self.c_only = 0
566 if c_link:
567 self.obj_only = 0
570 class CompilationResult:
571 """
572 Results from the Cython compiler:
574 c_file string or None The generated C source file
575 h_file string or None The generated C header file
576 i_file string or None The generated .pxi file
577 api_file string or None The generated C API .h file
578 listing_file string or None File of error messages
579 object_file string or None Result of compiling the C file
580 extension_file string or None Result of linking the object file
581 num_errors integer Number of compilation errors
582 compilation_source CompilationSource
583 """
585 def __init__(self):
586 self.c_file = None
587 self.h_file = None
588 self.i_file = None
589 self.api_file = None
590 self.listing_file = None
591 self.object_file = None
592 self.extension_file = None
593 self.main_source_file = None
596 class CompilationResultSet(dict):
597 """
598 Results from compiling multiple Pyrex source files. A mapping
599 from source file paths to CompilationResult instances. Also
600 has the following attributes:
602 num_errors integer Total number of compilation errors
603 """
605 num_errors = 0
607 def add(self, source, result):
608 self[source] = result
609 self.num_errors += result.num_errors
612 def compile_single(source, options, full_module_name = None):
613 """
614 compile_single(source, options, full_module_name)
616 Compile the given Pyrex implementation file and return a CompilationResult.
617 Always compiles a single file; does not perform timestamp checking or
618 recursion.
619 """
620 return run_pipeline(source, options, full_module_name)
623 def compile_multiple(sources, options):
624 """
625 compile_multiple(sources, options)
627 Compiles the given sequence of Pyrex implementation files and returns
628 a CompilationResultSet. Performs timestamp checking and/or recursion
629 if these are specified in the options.
630 """
631 sources = [os.path.abspath(source) for source in sources]
632 processed = set()
633 results = CompilationResultSet()
634 recursive = options.recursive
635 timestamps = options.timestamps
636 if timestamps is None:
637 timestamps = recursive
638 verbose = options.verbose or ((recursive or timestamps) and not options.quiet)
639 for source in sources:
640 if source not in processed:
641 # Compiling multiple sources in one context doesn't quite
642 # work properly yet.
643 if not timestamps or context.c_file_out_of_date(source):
644 if verbose:
645 sys.stderr.write("Compiling %s\n" % source)
647 result = run_pipeline(source, options)
648 results.add(source, result)
649 processed.add(source)
650 if recursive:
651 for module_name in context.find_cimported_module_names(source):
652 path = context.find_pyx_file(module_name, [source])
653 if path:
654 sources.append(path)
655 else:
656 sys.stderr.write(
657 "Cannot find .pyx file for cimported module '%s'\n" % module_name)
658 return results
660 def compile(source, options = None, c_compile = 0, c_link = 0,
661 full_module_name = None, **kwds):
662 """
663 compile(source [, options], [, <option> = <value>]...)
665 Compile one or more Pyrex implementation files, with optional timestamp
666 checking and recursing on dependecies. The source argument may be a string
667 or a sequence of strings If it is a string and no recursion or timestamp
668 checking is requested, a CompilationResult is returned, otherwise a
669 CompilationResultSet is returned.
670 """
671 options = CompilationOptions(defaults = options, c_compile = c_compile,
672 c_link = c_link, **kwds)
673 if isinstance(source, basestring) and not options.timestamps \
674 and not options.recursive:
675 return compile_single(source, options, full_module_name)
676 else:
677 return compile_multiple(source, options)
679 #------------------------------------------------------------------------
680 #
681 # Main command-line entry point
682 #
683 #------------------------------------------------------------------------
685 def main(command_line = 0):
686 args = sys.argv[1:]
687 any_failures = 0
688 if command_line:
689 from CmdLine import parse_command_line
690 options, sources = parse_command_line(args)
691 else:
692 options = CompilationOptions(default_options)
693 sources = args
695 if options.show_version:
696 sys.stderr.write("Cython version %s\n" % Version.version)
697 if options.working_path!="":
698 os.chdir(options.working_path)
699 try:
700 result = compile(sources, options)
701 if result.num_errors > 0:
702 any_failures = 1
703 except (EnvironmentError, PyrexError), e:
704 sys.stderr.write(str(e) + '\n')
705 any_failures = 1
706 if any_failures:
707 sys.exit(1)
711 #------------------------------------------------------------------------
712 #
713 # Set the default options depending on the platform
714 #
715 #------------------------------------------------------------------------
717 default_options = dict(
718 show_version = 0,
719 use_listing_file = 0,
720 errors_to_stderr = 1,
721 c_only = 1,
722 obj_only = 1,
723 cplus = 0,
724 output_file = None,
725 annotate = False,
726 generate_pxi = 0,
727 working_path = "",
728 recursive = 0,
729 timestamps = None,
730 verbose = 0,
731 quiet = 0,
732 pragma_overrides = {},
733 emit_linenums = False,
734 )
735 if sys.platform == "mac":
736 from Cython.Mac.MacSystem import c_compile, c_link, CCompilerError
737 default_options['use_listing_file'] = 1
738 elif sys.platform == "darwin":
739 from Cython.Mac.DarwinSystem import c_compile, c_link, CCompilerError
740 else:
741 c_compile = None
742 c_link = None
