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()
59 class build_ext(_build_ext
.build_ext
):
61 description
= "build C/C++ and Cython extensions (compile/link to build directory)"
63 sep_by
= _build_ext
.build_ext
.sep_by
64 user_options
= _build_ext
.build_ext
.user_options
65 boolean_options
= _build_ext
.build_ext
.boolean_options
66 help_options
= _build_ext
.build_ext
.help_options
68 # Add the pyrex specific data.
70 ('cython-cplus', None,
71 "generate C++ source files"),
72 ('cython-create-listing', None,
73 "write errors to a listing file"),
74 ('cython-line-directives', None,
75 "emit source line directives"),
76 ('cython-include-dirs=', None,
77 "path to the Cython include files" + sep_by
),
78 ('cython-c-in-temp', None,
79 "put generated C files in temp directory"),
80 ('cython-gen-pxi', None,
81 "generate .pxi file for public declarations"),
82 ('cython-directives=', None,
83 "compiler directive overrides"),
85 "generate debug information for cygdb"),
86 ('cython-compile-time-env', None,
87 "cython compile time environment"),
89 # For backwards compatibility.
91 "generate C++ source files"),
92 ('pyrex-create-listing', None,
93 "write errors to a listing file"),
94 ('pyrex-line-directives', None,
95 "emit source line directives"),
96 ('pyrex-include-dirs=', None,
97 "path to the Cython include files" + sep_by
),
98 ('pyrex-c-in-temp', None,
99 "put generated C files in temp directory"),
100 ('pyrex-gen-pxi', None,
101 "generate .pxi file for public declarations"),
102 ('pyrex-directives=', None,
103 "compiler directive overrides"),
105 "generate debug information for cygdb"),
108 boolean_options
.extend([
109 'cython-cplus', 'cython-create-listing', 'cython-line-directives',
110 'cython-c-in-temp', 'cython-gdb',
112 # For backwards compatibility.
113 'pyrex-cplus', 'pyrex-create-listing', 'pyrex-line-directives',
114 'pyrex-c-in-temp', 'pyrex-gdb',
117 def initialize_options(self
):
118 _build_ext
.build_ext
.initialize_options(self
)
119 self
.cython_cplus
= 0
120 self
.cython_create_listing
= 0
121 self
.cython_line_directives
= 0
122 self
.cython_include_dirs
= None
123 self
.cython_directives
= None
124 self
.cython_c_in_temp
= 0
125 self
.cython_gen_pxi
= 0
126 self
.cython_gdb
= False
127 self
.no_c_in_traceback
= 0
128 self
.cython_compile_time_env
= None
130 def __getattr__(self
, name
):
131 if name
[:6] == 'pyrex_':
132 return getattr(self
, 'cython_' + name
[6:])
134 return _build_ext
.build_ext
.__getattr
__(self
, name
)
136 def __setattr__(self
, name
, value
):
137 if name
[:6] == 'pyrex_':
138 return setattr(self
, 'cython_' + name
[6:], value
)
140 # _build_ext.build_ext.__setattr__(self, name, value)
141 self
.__dict
__[name
] = value
143 def finalize_options (self
):
144 _build_ext
.build_ext
.finalize_options(self
)
145 if self
.cython_include_dirs
is None:
146 self
.cython_include_dirs
= []
147 elif isinstance(self
.cython_include_dirs
, basestring
):
148 self
.cython_include_dirs
= \
149 self
.cython_include_dirs
.split(os
.pathsep
)
150 if self
.cython_directives
is None:
151 self
.cython_directives
= {}
152 # finalize_options ()
155 # We have one shot at this before build_ext initializes the compiler.
156 # If --pyrex-gdb is in effect as a command line option or as option
157 # of any Extension module, disable optimization for the C or C++
159 if self
.cython_gdb
or [1 for ext
in self
.extensions
160 if getattr(ext
, 'cython_gdb', False)]:
161 optimization
.disable_optimization()
163 _build_ext
.build_ext
.run(self
)
165 def build_extensions(self
):
166 # First, sanity-check the 'extensions' list
167 self
.check_extensions_list(self
.extensions
)
169 for ext
in self
.extensions
:
170 ext
.sources
= self
.cython_sources(ext
.sources
, ext
)
171 self
.build_extension(ext
)
173 def cython_sources(self
, sources
, extension
):
175 Walk the list of source files in 'sources', looking for Cython
176 source files (.pyx and .py). Run Cython on all that are
177 found, and return a modified 'sources' list with Cython source
178 files replaced by the generated C (or C++) files.
181 from Cython
.Compiler
.Main \
182 import CompilationOptions
, \
183 default_options
as cython_default_options
, \
184 compile as cython_compile
185 from Cython
.Compiler
.Errors
import PyrexError
187 e
= sys
.exc_info()[1]
188 print("failed to import Cython: %s" % e
)
189 raise DistutilsPlatformError("Cython does not appear to be installed")
195 # Setup create_list and cplus from the extension options if
196 # Cython.Distutils.extension.Extension is used, otherwise just
197 # use what was parsed from the command-line or the configuration file.
198 # cplus will also be set to true is extension.language is equal to
201 # create_listing = self.cython_create_listing or \
202 # extension.cython_create_listing
203 # cplus = self.cython_cplus or \
204 # extension.cython_cplus or \
205 # (extension.language != None and \
206 # extension.language.lower() == 'c++')
207 #except AttributeError:
208 # create_listing = self.cython_create_listing
209 # cplus = self.cython_cplus or \
210 # (extension.language != None and \
211 # extension.language.lower() == 'c++')
213 create_listing
= self
.cython_create_listing
or \
214 getattr(extension
, 'cython_create_listing', 0)
215 line_directives
= self
.cython_line_directives
or \
216 getattr(extension
, 'cython_line_directives', 0)
217 no_c_in_traceback
= self
.no_c_in_traceback
or \
218 getattr(extension
, 'no_c_in_traceback', 0)
219 cplus
= self
.cython_cplus
or getattr(extension
, 'cython_cplus', 0) or \
220 (extension
.language
and extension
.language
.lower() == 'c++')
221 cython_gen_pxi
= self
.cython_gen_pxi
or getattr(extension
, 'cython_gen_pxi', 0)
222 cython_gdb
= self
.cython_gdb
or getattr(extension
, 'cython_gdb', False)
223 cython_compile_time_env
= self
.cython_compile_time_env
or \
224 getattr(extension
, 'cython_compile_time_env', None)
226 # Set up the include_path for the Cython compiler:
227 # 1. Start with the command line option.
228 # 2. Add in any (unique) paths from the extension
229 # cython_include_dirs (if Cython.Distutils.extension is used).
230 # 3. Add in any (unique) paths from the extension include_dirs
231 includes
= self
.cython_include_dirs
233 for i
in extension
.cython_include_dirs
:
234 if not i
in includes
:
236 except AttributeError:
238 for i
in extension
.include_dirs
:
239 if not i
in includes
:
242 # Set up Cython compiler directives:
243 # 1. Start with the command line option.
244 # 2. Add in any (unique) entries from the extension
245 # cython_directives (if Cython.Distutils.extension is used).
246 directives
= self
.cython_directives
247 if hasattr(extension
, "cython_directives"):
248 directives
.update(extension
.cython_directives
)
250 # Set the target_ext to '.c'. Cython will change this to '.cpp' if
257 # Decide whether to drop the generated C files into the temp dir
258 # or the source tree.
260 if not self
.inplace
and (self
.cython_c_in_temp
261 or getattr(extension
, 'cython_c_in_temp', 0)):
262 target_dir
= os
.path
.join(self
.build_temp
, "pyrex")
263 for package_name
in extension
.name
.split('.')[:-1]:
264 target_dir
= os
.path
.join(target_dir
, package_name
)
268 newest_dependency
= None
269 for source
in sources
:
270 (base
, ext
) = os
.path
.splitext(os
.path
.basename(source
))
272 # FIXME: we might want to special case this some more
274 if ext
== ".pyx": # Cython source file
275 output_dir
= target_dir
or os
.path
.dirname(source
)
276 new_sources
.append(os
.path
.join(output_dir
, base
+ target_ext
))
277 cython_sources
.append(source
)
278 cython_targets
[source
] = new_sources
[-1]
279 elif ext
== '.pxi' or ext
== '.pxd':
280 if newest_dependency
is None \
281 or newer(source
, newest_dependency
):
282 newest_dependency
= source
284 new_sources
.append(source
)
286 if not cython_sources
:
289 module_name
= extension
.name
291 for source
in cython_sources
:
292 target
= cython_targets
[source
]
293 depends
= [source
] + list(extension
.depends
or ())
294 if(source
[-4:].lower()==".pyx" and os
.path
.isfile(source
[:-3]+"pxd")):
295 depends
+= [source
[:-3]+"pxd"]
296 rebuild
= self
.force
or newer_group(depends
, target
, 'newer')
297 if not rebuild
and newest_dependency
is not None:
298 rebuild
= newer(newest_dependency
, target
)
300 log
.info("cythoning %s to %s", source
, target
)
301 self
.mkpath(os
.path
.dirname(target
))
303 output_dir
= os
.curdir
305 output_dir
= self
.build_lib
306 options
= CompilationOptions(cython_default_options
,
307 use_listing_file
= create_listing
,
308 include_path
= includes
,
309 compiler_directives
= directives
,
310 output_file
= target
,
312 emit_linenums
= line_directives
,
313 c_line_in_traceback
= not no_c_in_traceback
,
314 generate_pxi
= cython_gen_pxi
,
315 output_dir
= output_dir
,
316 gdb_debug
= cython_gdb
,
317 compile_time_env
= cython_compile_time_env
)
318 result
= cython_compile(source
, options
=options
,
319 full_module_name
=module_name
)
321 log
.info("skipping '%s' Cython extension (up-to-date)", target
)