OLD | NEW |
(Empty) | |
| 1 """Cython.Distutils.build_ext |
| 2 |
| 3 Implements a version of the Distutils 'build_ext' command, for |
| 4 building Cython extension modules.""" |
| 5 |
| 6 # This module should be kept compatible with Python 2.3. |
| 7 |
| 8 __revision__ = "$Id:$" |
| 9 |
| 10 import sys |
| 11 import os |
| 12 import re |
| 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 |
| 21 |
| 22 extension_name_re = _build_ext.extension_name_re |
| 23 |
| 24 show_compilers = _build_ext.show_compilers |
| 25 |
| 26 class Optimization(object): |
| 27 def __init__(self): |
| 28 self.flags = ( |
| 29 'OPT', |
| 30 'CFLAGS', |
| 31 'CPPFLAGS', |
| 32 'EXTRA_CFLAGS', |
| 33 'BASECFLAGS', |
| 34 'PY_CFLAGS', |
| 35 ) |
| 36 self.state = sysconfig.get_config_vars(*self.flags) |
| 37 self.config_vars = sysconfig.get_config_vars() |
| 38 |
| 39 |
| 40 def disable_optimization(self): |
| 41 "disable optimization for the C or C++ compiler" |
| 42 badoptions = ('-O1', '-O2', '-O3') |
| 43 |
| 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) |
| 48 |
| 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 |
| 54 |
| 55 |
| 56 optimization = Optimization() |
| 57 |
| 58 |
| 59 class build_ext(_build_ext.build_ext): |
| 60 |
| 61 description = "build C/C++ and Cython extensions (compile/link to build dire
ctory)" |
| 62 |
| 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 |
| 67 |
| 68 # Add the pyrex specific data. |
| 69 user_options.extend([ |
| 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"), |
| 84 ('cython-gdb', None, |
| 85 "generate debug information for cygdb"), |
| 86 ('cython-compile-time-env', None, |
| 87 "cython compile time environment"), |
| 88 |
| 89 # For backwards compatibility. |
| 90 ('pyrex-cplus', None, |
| 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"), |
| 104 ('pyrex-gdb', None, |
| 105 "generate debug information for cygdb"), |
| 106 ]) |
| 107 |
| 108 boolean_options.extend([ |
| 109 'cython-cplus', 'cython-create-listing', 'cython-line-directives', |
| 110 'cython-c-in-temp', 'cython-gdb', |
| 111 |
| 112 # For backwards compatibility. |
| 113 'pyrex-cplus', 'pyrex-create-listing', 'pyrex-line-directives', |
| 114 'pyrex-c-in-temp', 'pyrex-gdb', |
| 115 ]) |
| 116 |
| 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 |
| 129 |
| 130 def __getattr__(self, name): |
| 131 if name[:6] == 'pyrex_': |
| 132 return getattr(self, 'cython_' + name[6:]) |
| 133 else: |
| 134 return _build_ext.build_ext.__getattr__(self, name) |
| 135 |
| 136 def __setattr__(self, name, value): |
| 137 if name[:6] == 'pyrex_': |
| 138 return setattr(self, 'cython_' + name[6:], value) |
| 139 else: |
| 140 # _build_ext.build_ext.__setattr__(self, name, value) |
| 141 self.__dict__[name] = value |
| 142 |
| 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 () |
| 153 |
| 154 def run(self): |
| 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++ |
| 158 # compiler. |
| 159 if self.cython_gdb or [1 for ext in self.extensions |
| 160 if getattr(ext, 'cython_gdb', False)]: |
| 161 optimization.disable_optimization() |
| 162 |
| 163 _build_ext.build_ext.run(self) |
| 164 |
| 165 def build_extensions(self): |
| 166 # First, sanity-check the 'extensions' list |
| 167 self.check_extensions_list(self.extensions) |
| 168 |
| 169 for ext in self.extensions: |
| 170 ext.sources = self.cython_sources(ext.sources, ext) |
| 171 self.build_extension(ext) |
| 172 |
| 173 def cython_sources(self, sources, extension): |
| 174 """ |
| 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. |
| 179 """ |
| 180 try: |
| 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 |
| 186 except ImportError: |
| 187 e = sys.exc_info()[1] |
| 188 print("failed to import Cython: %s" % e) |
| 189 raise DistutilsPlatformError("Cython does not appear to be installed
") |
| 190 |
| 191 new_sources = [] |
| 192 cython_sources = [] |
| 193 cython_targets = {} |
| 194 |
| 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 |
| 199 # 'C++' or 'c++'. |
| 200 #try: |
| 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++') |
| 212 |
| 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_p
xi', 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) |
| 225 |
| 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 |
| 232 try: |
| 233 for i in extension.cython_include_dirs: |
| 234 if not i in includes: |
| 235 includes.append(i) |
| 236 except AttributeError: |
| 237 pass |
| 238 for i in extension.include_dirs: |
| 239 if not i in includes: |
| 240 includes.append(i) |
| 241 |
| 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) |
| 249 |
| 250 # Set the target_ext to '.c'. Cython will change this to '.cpp' if |
| 251 # needed. |
| 252 if cplus: |
| 253 target_ext = '.cpp' |
| 254 else: |
| 255 target_ext = '.c' |
| 256 |
| 257 # Decide whether to drop the generated C files into the temp dir |
| 258 # or the source tree. |
| 259 |
| 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) |
| 265 else: |
| 266 target_dir = None |
| 267 |
| 268 newest_dependency = None |
| 269 for source in sources: |
| 270 (base, ext) = os.path.splitext(os.path.basename(source)) |
| 271 if ext == ".py": |
| 272 # FIXME: we might want to special case this some more |
| 273 ext = '.pyx' |
| 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 |
| 283 else: |
| 284 new_sources.append(source) |
| 285 |
| 286 if not cython_sources: |
| 287 return new_sources |
| 288 |
| 289 module_name = extension.name |
| 290 |
| 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) |
| 299 if rebuild: |
| 300 log.info("cythoning %s to %s", source, target) |
| 301 self.mkpath(os.path.dirname(target)) |
| 302 if self.inplace: |
| 303 output_dir = os.curdir |
| 304 else: |
| 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, |
| 311 cplus = cplus, |
| 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) |
| 320 else: |
| 321 log.info("skipping '%s' Cython extension (up-to-date)", target) |
| 322 |
| 323 return new_sources |
| 324 |
| 325 # cython_sources () |
| 326 |
| 327 # class build_ext |
OLD | NEW |