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.3.
13 from distutils.core import Command
14 from distutils.errors import DistutilsPlatformError
15 from distutils.sysconfig import customize_compiler, get_python_version
16 from distutils.dep_util import newer, newer_group
17 from distutils import log
18 from distutils.dir_util import mkpath
19 from distutils.command import build_ext as _build_ext
20 from distutils import sysconfig
22 extension_name_re = _build_ext.extension_name_re
24 show_compilers = _build_ext.show_compilers
26 class Optimization(object):
36 self.state = sysconfig.get_config_vars(*self.flags)
37 self.config_vars = sysconfig.get_config_vars()
40 def disable_optimization(self):
41 "disable optimization for the C or C++ compiler"
42 badoptions = ('-O1', '-O2', '-O3')
44 for flag, option in zip(self.flags, self.state):
45 if option is not None:
46 L = [opt for opt in option.split() if opt not in badoptions]
47 self.config_vars[flag] = ' '.join(L)
49 def restore_state(self):
50 "restore the original state"
51 for flag, option in zip(self.flags, self.state):
52 if option is not None:
53 self.config_vars[flag] = option
56 optimization = Optimization()
69 class build_ext(_build_ext.build_ext):
71 description = "build C/C++ and Cython extensions (compile/link to build directory)"
73 sep_by = _build_ext.build_ext.sep_by
74 user_options = _build_ext.build_ext.user_options
75 boolean_options = _build_ext.build_ext.boolean_options
76 help_options = _build_ext.build_ext.help_options
78 # Add the pyrex specific data.
81 "generate C++ source files"),
82 ('pyrex-create-listing', None,
83 "write errors to a listing file"),
84 ('pyrex-line-directives', None,
85 "emit source line directives"),
86 ('pyrex-include-dirs=', None,
87 "path to the Cython include files" + sep_by),
88 ('pyrex-c-in-temp', None,
89 "put generated C files in temp directory"),
90 ('pyrex-gen-pxi', None,
91 "generate .pxi file for public declarations"),
92 ('pyrex-directives=', None,
93 "compiler directive overrides"),
95 "generate debug information for cygdb"),
98 boolean_options.extend([
99 'pyrex-cplus', 'pyrex-create-listing', 'pyrex-line-directives',
100 'pyrex-c-in-temp', 'pyrex-gdb',
103 def initialize_options(self):
104 _build_ext.build_ext.initialize_options(self)
106 self.pyrex_create_listing = 0
107 self.pyrex_line_directives = 0
108 self.pyrex_include_dirs = None
109 self.pyrex_directives = None
110 self.pyrex_c_in_temp = 0
111 self.pyrex_gen_pxi = 0
112 self.pyrex_gdb = False
113 self.no_c_in_traceback = 0
115 def finalize_options (self):
116 _build_ext.build_ext.finalize_options(self)
117 if self.pyrex_include_dirs is None:
118 self.pyrex_include_dirs = []
119 elif isinstance(self.pyrex_include_dirs, basestring):
120 self.pyrex_include_dirs = \
121 self.pyrex_include_dirs.split(os.pathsep)
122 if self.pyrex_directives is None:
123 self.pyrex_directives = {}
124 # finalize_options ()
127 # We have one shot at this before build_ext initializes the compiler.
128 # If --pyrex-gdb is in effect as a command line option or as option
129 # of any Extension module, disable optimization for the C or C++
131 if (self.pyrex_gdb or any([getattr(ext, 'pyrex_gdb', False)
132 for ext in self.extensions])):
133 optimization.disable_optimization()
135 _build_ext.build_ext.run(self)
137 def build_extensions(self):
138 # First, sanity-check the 'extensions' list
139 self.check_extensions_list(self.extensions)
141 for ext in self.extensions:
142 ext.sources = self.cython_sources(ext.sources, ext)
143 self.build_extension(ext)
145 def cython_sources(self, sources, extension):
147 Walk the list of source files in 'sources', looking for Cython
148 source files (.pyx and .py). Run Cython on all that are
149 found, and return a modified 'sources' list with Cython source
150 files replaced by the generated C (or C++) files.
153 from Cython.Compiler.Main \
154 import CompilationOptions, \
155 default_options as pyrex_default_options, \
156 compile as cython_compile
157 from Cython.Compiler.Errors import PyrexError
159 e = sys.exc_info()[1]
160 print("failed to import Cython: %s" % e)
161 raise DistutilsPlatformError("Cython does not appear to be installed")
167 # Setup create_list and cplus from the extension options if
168 # Cython.Distutils.extension.Extension is used, otherwise just
169 # use what was parsed from the command-line or the configuration file.
170 # cplus will also be set to true is extension.language is equal to
173 # create_listing = self.pyrex_create_listing or \
174 # extension.pyrex_create_listing
175 # cplus = self.pyrex_cplus or \
176 # extension.pyrex_cplus or \
177 # (extension.language != None and \
178 # extension.language.lower() == 'c++')
179 #except AttributeError:
180 # create_listing = self.pyrex_create_listing
181 # cplus = self.pyrex_cplus or \
182 # (extension.language != None and \
183 # extension.language.lower() == 'c++')
185 create_listing = self.pyrex_create_listing or \
186 getattr(extension, 'pyrex_create_listing', 0)
187 line_directives = self.pyrex_line_directives or \
188 getattr(extension, 'pyrex_line_directives', 0)
189 no_c_in_traceback = self.no_c_in_traceback or \
190 getattr(extension, 'no_c_in_traceback', 0)
191 cplus = self.pyrex_cplus or getattr(extension, 'pyrex_cplus', 0) or \
192 (extension.language and extension.language.lower() == 'c++')
193 pyrex_gen_pxi = self.pyrex_gen_pxi or getattr(extension, 'pyrex_gen_pxi', 0)
194 pyrex_gdb = self.pyrex_gdb or getattr(extension, 'pyrex_gdb', False)
195 # Set up the include_path for the Cython compiler:
196 # 1. Start with the command line option.
197 # 2. Add in any (unique) paths from the extension
198 # pyrex_include_dirs (if Cython.Distutils.extension is used).
199 # 3. Add in any (unique) paths from the extension include_dirs
200 includes = self.pyrex_include_dirs
202 for i in extension.pyrex_include_dirs:
203 if not i in includes:
205 except AttributeError:
207 for i in extension.include_dirs:
208 if not i in includes:
211 # Set up Cython compiler directives:
212 # 1. Start with the command line option.
213 # 2. Add in any (unique) entries from the extension
214 # pyrex_directives (if Cython.Distutils.extension is used).
215 directives = self.pyrex_directives
216 if hasattr(extension, "pyrex_directives"):
217 directives.update(extension.pyrex_directives)
219 # Set the target_ext to '.c'. Cython will change this to '.cpp' if
226 # Decide whether to drop the generated C files into the temp dir
227 # or the source tree.
229 if not self.inplace and (self.pyrex_c_in_temp
230 or getattr(extension, 'pyrex_c_in_temp', 0)):
231 target_dir = os.path.join(self.build_temp, "pyrex")
232 for package_name in extension.name.split('.')[:-1]:
233 target_dir = os.path.join(target_dir, package_name)
237 newest_dependency = None
238 for source in sources:
239 (base, ext) = os.path.splitext(os.path.basename(source))
241 # FIXME: we might want to special case this some more
243 if ext == ".pyx": # Cython source file
244 output_dir = target_dir or os.path.dirname(source)
245 new_sources.append(os.path.join(output_dir, base + target_ext))
246 pyrex_sources.append(source)
247 pyrex_targets[source] = new_sources[-1]
248 elif ext == '.pxi' or ext == '.pxd':
249 if newest_dependency is None \
250 or newer(source, newest_dependency):
251 newest_dependency = source
253 new_sources.append(source)
255 if not pyrex_sources:
258 module_name = extension.name
260 for source in pyrex_sources:
261 target = pyrex_targets[source]
262 depends = [source] + list(extension.depends or ())
263 rebuild = self.force or newer_group(depends, target, 'newer')
264 if not rebuild and newest_dependency is not None:
265 rebuild = newer(newest_dependency, target)
267 log.info("cythoning %s to %s", source, target)
268 self.mkpath(os.path.dirname(target))
270 output_dir = os.curdir
272 output_dir = self.build_lib
273 options = CompilationOptions(pyrex_default_options,
274 use_listing_file = create_listing,
275 include_path = includes,
276 compiler_directives = directives,
277 output_file = target,
279 emit_linenums = line_directives,
280 c_line_in_traceback = not no_c_in_traceback,
281 generate_pxi = pyrex_gen_pxi,
282 output_dir = output_dir,
283 gdb_debug = pyrex_gdb)
284 result = cython_compile(source, options=options,
285 full_module_name=module_name)
287 log.info("skipping '%s' Cython extension (up-to-date)", target)