Cython has moved to github.

cython-devel

view Cython/Distutils/build_ext.py @ 3703:219528868027

Check dependencies defined for the extension to determine if cython
compile is necessary.
author Eric Huss <eric@huss.org>
date Mon Jun 01 12:35:22 2009 -0700 (2 years ago)
parents 1768b2a386a7
children 4b4b0077bd7b
line source
1 """Cython.Distutils.build_ext
3 Implements a version of the Distutils 'build_ext' command, for
4 building Cython extension modules."""
6 # This module should be kept compatible with Python 2.1.
8 __revision__ = "$Id:$"
10 import sys, os, re
11 from types import *
12 from distutils.core import Command
13 from distutils.errors import *
14 from distutils.sysconfig import customize_compiler, get_python_version
15 from distutils.dep_util import newer, newer_group
16 from distutils import log
17 from distutils.dir_util import mkpath
18 from distutils.command import build_ext as _build_ext
20 extension_name_re = _build_ext.extension_name_re
22 show_compilers = _build_ext.show_compilers
24 class build_ext(_build_ext.build_ext):
26 description = "build C/C++ and Cython extensions (compile/link to build directory)"
28 sep_by = _build_ext.build_ext.sep_by
29 user_options = _build_ext.build_ext.user_options
30 boolean_options = _build_ext.build_ext.boolean_options
31 help_options = _build_ext.build_ext.help_options
33 # Add the pyrex specific data.
34 user_options.extend([
35 ('pyrex-cplus', None,
36 "generate C++ source files"),
37 ('pyrex-create-listing', None,
38 "write errors to a listing file"),
39 ('pyrex-line-directives', None,
40 "emit source line directives"),
41 ('pyrex-include-dirs=', None,
42 "path to the Cython include files" + sep_by),
43 ('pyrex-c-in-temp', None,
44 "put generated C files in temp directory"),
45 ('pyrex-gen-pxi', None,
46 "generate .pxi file for public declarations"),
47 ('pyrex-directives=', None,
48 "compiler directive overrides"),
49 ])
51 boolean_options.extend([
52 'pyrex-cplus', 'pyrex-create-listing', 'pyrex-line-directives', 'pyrex-c-in-temp'
53 ])
55 def initialize_options(self):
56 _build_ext.build_ext.initialize_options(self)
57 self.pyrex_cplus = 0
58 self.pyrex_create_listing = 0
59 self.pyrex_line_directives = 0
60 self.pyrex_include_dirs = None
61 self.pyrex_directives = None
62 self.pyrex_c_in_temp = 0
63 self.pyrex_gen_pxi = 0
65 def finalize_options (self):
66 _build_ext.build_ext.finalize_options(self)
67 if self.pyrex_include_dirs is None:
68 self.pyrex_include_dirs = []
69 elif type(self.pyrex_include_dirs) is StringType:
70 self.pyrex_include_dirs = \
71 self.pyrex_include_dirs.split(os.pathsep)
72 if self.pyrex_directives is None:
73 self.pyrex_directives = {}
74 # finalize_options ()
76 def build_extensions(self):
77 # First, sanity-check the 'extensions' list
78 self.check_extensions_list(self.extensions)
79 for ext in self.extensions:
80 ext.sources = self.cython_sources(ext.sources, ext)
81 self.build_extension(ext)
83 def cython_sources(self, sources, extension):
84 """
85 Walk the list of source files in 'sources', looking for Cython
86 source files (.pyx and .py). Run Cython on all that are
87 found, and return a modified 'sources' list with Cython source
88 files replaced by the generated C (or C++) files.
89 """
90 try:
91 from Cython.Compiler.Main \
92 import CompilationOptions, \
93 default_options as pyrex_default_options, \
94 compile as cython_compile
95 from Cython.Compiler.Errors import PyrexError
96 except ImportError:
97 e = sys.exc_info()[1]
98 print("failed to import Cython: %s" % e)
99 raise DistutilsPlatformError("Cython does not appear to be installed")
101 new_sources = []
102 pyrex_sources = []
103 pyrex_targets = {}
105 # Setup create_list and cplus from the extension options if
106 # Cython.Distutils.extension.Extension is used, otherwise just
107 # use what was parsed from the command-line or the configuration file.
108 # cplus will also be set to true is extension.language is equal to
109 # 'C++' or 'c++'.
110 #try:
111 # create_listing = self.pyrex_create_listing or \
112 # extension.pyrex_create_listing
113 # cplus = self.pyrex_cplus or \
114 # extension.pyrex_cplus or \
115 # (extension.language != None and \
116 # extension.language.lower() == 'c++')
117 #except AttributeError:
118 # create_listing = self.pyrex_create_listing
119 # cplus = self.pyrex_cplus or \
120 # (extension.language != None and \
121 # extension.language.lower() == 'c++')
123 create_listing = self.pyrex_create_listing or \
124 getattr(extension, 'pyrex_create_listing', 0)
125 line_directives = self.pyrex_line_directives or \
126 getattr(extension, 'pyrex_line_directives', 0)
127 cplus = self.pyrex_cplus or getattr(extension, 'pyrex_cplus', 0) or \
128 (extension.language and extension.language.lower() == 'c++')
129 pyrex_gen_pxi = self.pyrex_gen_pxi or getattr(extension, 'pyrex_gen_pxi', 0)
131 # Set up the include_path for the Cython compiler:
132 # 1. Start with the command line option.
133 # 2. Add in any (unique) paths from the extension
134 # pyrex_include_dirs (if Cython.Distutils.extension is used).
135 # 3. Add in any (unique) paths from the extension include_dirs
136 includes = self.pyrex_include_dirs
137 try:
138 for i in extension.pyrex_include_dirs:
139 if not i in includes:
140 includes.append(i)
141 except AttributeError:
142 pass
143 for i in extension.include_dirs:
144 if not i in includes:
145 includes.append(i)
147 # Set up Cython compiler directives:
148 # 1. Start with the command line option.
149 # 2. Add in any (unique) entries from the extension
150 # pyrex_directives (if Cython.Distutils.extension is used).
151 directives = self.pyrex_directives
152 if hasattr(extension, "pyrex_directives"):
153 directives.update(extension.pyrex_directives)
155 # Set the target_ext to '.c'. Cython will change this to '.cpp' if
156 # needed.
157 if cplus:
158 target_ext = '.cpp'
159 else:
160 target_ext = '.c'
162 # Decide whether to drop the generated C files into the temp dir
163 # or the source tree.
165 if not self.inplace and (self.pyrex_c_in_temp
166 or getattr(extension, 'pyrex_c_in_temp', 0)):
167 target_dir = os.path.join(self.build_temp, "pyrex")
168 else:
169 target_dir = None
171 newest_dependency = None
172 for source in sources:
173 (base, ext) = os.path.splitext(os.path.basename(source))
174 if ext == ".py":
175 # FIXME: we might want to special case this some more
176 ext = '.pyx'
177 if ext == ".pyx": # Cython source file
178 output_dir = target_dir or os.path.dirname(source)
179 new_sources.append(os.path.join(output_dir, base + target_ext))
180 pyrex_sources.append(source)
181 pyrex_targets[source] = new_sources[-1]
182 elif ext == '.pxi' or ext == '.pxd':
183 if newest_dependency is None \
184 or newer(source, newest_dependency):
185 newest_dependency = source
186 else:
187 new_sources.append(source)
189 if not pyrex_sources:
190 return new_sources
192 module_name = extension.name
194 for source in pyrex_sources:
195 target = pyrex_targets[source]
196 depends = [source] + extension.depends
197 rebuild = self.force or newer_group(depends, target, 'newer')
198 if not rebuild and newest_dependency is not None:
199 rebuild = newer(newest_dependency, target)
200 if rebuild:
201 log.info("cythoning %s to %s", source, target)
202 self.mkpath(os.path.dirname(target))
203 options = CompilationOptions(pyrex_default_options,
204 use_listing_file = create_listing,
205 include_path = includes,
206 compiler_directives = directives,
207 output_file = target,
208 cplus = cplus,
209 emit_linenums = line_directives,
210 generate_pxi = pyrex_gen_pxi)
211 result = cython_compile(source, options=options,
212 full_module_name=module_name)
213 else:
214 log.info("skipping '%s' Cython extension (up-to-date)", target)
216 return new_sources
218 # cython_sources ()
220 # class build_ext