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
114 def finalize_options (self):
115 _build_ext.build_ext.finalize_options(self)
116 if self.pyrex_include_dirs is None:
117 self.pyrex_include_dirs = []
118 elif isinstance(self.pyrex_include_dirs, basestring):
119 self.pyrex_include_dirs = \
120 self.pyrex_include_dirs.split(os.pathsep)
121 if self.pyrex_directives is None:
122 self.pyrex_directives = {}
123 # finalize_options ()
126 # We have one shot at this before build_ext initializes the compiler.
127 # If --pyrex-gdb is in effect as a command line option or as option
128 # of any Extension module, disable optimization for the C or C++
130 if (self.pyrex_gdb or any([getattr(ext, 'pyrex_gdb', False)
131 for ext in self.extensions])):
132 optimization.disable_optimization()
134 _build_ext.build_ext.run(self)
136 def build_extensions(self):
137 # First, sanity-check the 'extensions' list
138 self.check_extensions_list(self.extensions)
140 for ext in self.extensions:
141 ext.sources = self.cython_sources(ext.sources, ext)
142 self.build_extension(ext)
144 def cython_sources(self, sources, extension):
146 Walk the list of source files in 'sources', looking for Cython
147 source files (.pyx and .py). Run Cython on all that are
148 found, and return a modified 'sources' list with Cython source
149 files replaced by the generated C (or C++) files.
152 from Cython.Compiler.Main \
153 import CompilationOptions, \
154 default_options as pyrex_default_options, \
155 compile as cython_compile
156 from Cython.Compiler.Errors import PyrexError
158 e = sys.exc_info()[1]
159 print("failed to import Cython: %s" % e)
160 raise DistutilsPlatformError("Cython does not appear to be installed")
166 # Setup create_list and cplus from the extension options if
167 # Cython.Distutils.extension.Extension is used, otherwise just
168 # use what was parsed from the command-line or the configuration file.
169 # cplus will also be set to true is extension.language is equal to
172 # create_listing = self.pyrex_create_listing or \
173 # extension.pyrex_create_listing
174 # cplus = self.pyrex_cplus or \
175 # extension.pyrex_cplus or \
176 # (extension.language != None and \
177 # extension.language.lower() == 'c++')
178 #except AttributeError:
179 # create_listing = self.pyrex_create_listing
180 # cplus = self.pyrex_cplus or \
181 # (extension.language != None and \
182 # extension.language.lower() == 'c++')
184 create_listing = self.pyrex_create_listing or \
185 getattr(extension, 'pyrex_create_listing', 0)
186 line_directives = self.pyrex_line_directives or \
187 getattr(extension, 'pyrex_line_directives', 0)
188 cplus = self.pyrex_cplus or getattr(extension, 'pyrex_cplus', 0) or \
189 (extension.language and extension.language.lower() == 'c++')
190 pyrex_gen_pxi = self.pyrex_gen_pxi or getattr(extension, 'pyrex_gen_pxi', 0)
191 pyrex_gdb = self.pyrex_gdb or getattr(extension, 'pyrex_gdb', False)
192 # Set up the include_path for the Cython compiler:
193 # 1. Start with the command line option.
194 # 2. Add in any (unique) paths from the extension
195 # pyrex_include_dirs (if Cython.Distutils.extension is used).
196 # 3. Add in any (unique) paths from the extension include_dirs
197 includes = self.pyrex_include_dirs
199 for i in extension.pyrex_include_dirs:
200 if not i in includes:
202 except AttributeError:
204 for i in extension.include_dirs:
205 if not i in includes:
208 # Set up Cython compiler directives:
209 # 1. Start with the command line option.
210 # 2. Add in any (unique) entries from the extension
211 # pyrex_directives (if Cython.Distutils.extension is used).
212 directives = self.pyrex_directives
213 if hasattr(extension, "pyrex_directives"):
214 directives.update(extension.pyrex_directives)
216 # Set the target_ext to '.c'. Cython will change this to '.cpp' if
223 # Decide whether to drop the generated C files into the temp dir
224 # or the source tree.
226 if not self.inplace and (self.pyrex_c_in_temp
227 or getattr(extension, 'pyrex_c_in_temp', 0)):
228 target_dir = os.path.join(self.build_temp, "pyrex")
229 for package_name in extension.name.split('.')[:-1]:
230 target_dir = os.path.join(target_dir, package_name)
234 newest_dependency = None
235 for source in sources:
236 (base, ext) = os.path.splitext(os.path.basename(source))
238 # FIXME: we might want to special case this some more
240 if ext == ".pyx": # Cython source file
241 output_dir = target_dir or os.path.dirname(source)
242 new_sources.append(os.path.join(output_dir, base + target_ext))
243 pyrex_sources.append(source)
244 pyrex_targets[source] = new_sources[-1]
245 elif ext == '.pxi' or ext == '.pxd':
246 if newest_dependency is None \
247 or newer(source, newest_dependency):
248 newest_dependency = source
250 new_sources.append(source)
252 if not pyrex_sources:
255 module_name = extension.name
257 for source in pyrex_sources:
258 target = pyrex_targets[source]
259 depends = [source] + list(extension.depends or ())
260 rebuild = self.force or newer_group(depends, target, 'newer')
261 if not rebuild and newest_dependency is not None:
262 rebuild = newer(newest_dependency, target)
264 log.info("cythoning %s to %s", source, target)
265 self.mkpath(os.path.dirname(target))
267 output_dir = os.curdir
269 output_dir = self.build_lib
270 options = CompilationOptions(pyrex_default_options,
271 use_listing_file = create_listing,
272 include_path = includes,
273 compiler_directives = directives,
274 output_file = target,
276 emit_linenums = line_directives,
277 generate_pxi = pyrex_gen_pxi,
278 output_dir = output_dir,
279 gdb_debug = pyrex_gdb)
280 result = cython_compile(source, options=options,
281 full_module_name=module_name)
283 log.info("skipping '%s' Cython extension (up-to-date)", target)