1"""Cython.Distutils.old_build_ext 2 3Implements a version of the Distutils 'build_ext' command, for 4building Cython extension modules. 5 6Note that this module is deprecated. Use cythonize() instead. 7""" 8 9__revision__ = "$Id:$" 10 11import sys 12import os 13from distutils.errors import DistutilsPlatformError 14from distutils.dep_util import newer, newer_group 15from distutils import log 16from distutils.command import build_ext as _build_ext 17from distutils import sysconfig 18 19 20try: 21 from __builtin__ import basestring 22except ImportError: 23 basestring = str 24 25 26# FIXME: the below does not work as intended since importing 'Cython.Distutils' already 27# imports this module through 'Cython/Distutils/build_ext.py', so the condition is 28# always false and never prints the warning. 29""" 30import inspect 31import warnings 32 33def _check_stack(path): 34 try: 35 for frame in inspect.getouterframes(inspect.currentframe(), 0): 36 if path in frame[1].replace(os.sep, '/'): 37 return True 38 except Exception: 39 pass 40 return False 41 42 43if (not _check_stack('setuptools/extensions.py') 44 and not _check_stack('pyximport/pyxbuild.py') 45 and not _check_stack('Cython/Distutils/build_ext.py')): 46 warnings.warn( 47 "Cython.Distutils.old_build_ext does not properly handle dependencies " 48 "and is deprecated.") 49""" 50 51extension_name_re = _build_ext.extension_name_re 52 53show_compilers = _build_ext.show_compilers 54 55class Optimization(object): 56 def __init__(self): 57 self.flags = ( 58 'OPT', 59 'CFLAGS', 60 'CPPFLAGS', 61 'EXTRA_CFLAGS', 62 'BASECFLAGS', 63 'PY_CFLAGS', 64 ) 65 self.state = sysconfig.get_config_vars(*self.flags) 66 self.config_vars = sysconfig.get_config_vars() 67 68 69 def disable_optimization(self): 70 "disable optimization for the C or C++ compiler" 71 badoptions = ('-O1', '-O2', '-O3') 72 73 for flag, option in zip(self.flags, self.state): 74 if option is not None: 75 L = [opt for opt in option.split() if opt not in badoptions] 76 self.config_vars[flag] = ' '.join(L) 77 78 def restore_state(self): 79 "restore the original state" 80 for flag, option in zip(self.flags, self.state): 81 if option is not None: 82 self.config_vars[flag] = option 83 84 85optimization = Optimization() 86 87 88class old_build_ext(_build_ext.build_ext): 89 90 description = "build C/C++ and Cython extensions (compile/link to build directory)" 91 92 sep_by = _build_ext.build_ext.sep_by 93 user_options = _build_ext.build_ext.user_options[:] 94 boolean_options = _build_ext.build_ext.boolean_options[:] 95 help_options = _build_ext.build_ext.help_options[:] 96 97 # Add the pyrex specific data. 98 user_options.extend([ 99 ('cython-cplus', None, 100 "generate C++ source files"), 101 ('cython-create-listing', None, 102 "write errors to a listing file"), 103 ('cython-line-directives', None, 104 "emit source line directives"), 105 ('cython-include-dirs=', None, 106 "path to the Cython include files" + sep_by), 107 ('cython-c-in-temp', None, 108 "put generated C files in temp directory"), 109 ('cython-gen-pxi', None, 110 "generate .pxi file for public declarations"), 111 ('cython-directives=', None, 112 "compiler directive overrides"), 113 ('cython-gdb', None, 114 "generate debug information for cygdb"), 115 ('cython-compile-time-env', None, 116 "cython compile time environment"), 117 118 # For backwards compatibility. 119 ('pyrex-cplus', None, 120 "generate C++ source files"), 121 ('pyrex-create-listing', None, 122 "write errors to a listing file"), 123 ('pyrex-line-directives', None, 124 "emit source line directives"), 125 ('pyrex-include-dirs=', None, 126 "path to the Cython include files" + sep_by), 127 ('pyrex-c-in-temp', None, 128 "put generated C files in temp directory"), 129 ('pyrex-gen-pxi', None, 130 "generate .pxi file for public declarations"), 131 ('pyrex-directives=', None, 132 "compiler directive overrides"), 133 ('pyrex-gdb', None, 134 "generate debug information for cygdb"), 135 ]) 136 137 boolean_options.extend([ 138 'cython-cplus', 'cython-create-listing', 'cython-line-directives', 139 'cython-c-in-temp', 'cython-gdb', 140 141 # For backwards compatibility. 142 'pyrex-cplus', 'pyrex-create-listing', 'pyrex-line-directives', 143 'pyrex-c-in-temp', 'pyrex-gdb', 144 ]) 145 146 def initialize_options(self): 147 _build_ext.build_ext.initialize_options(self) 148 self.cython_cplus = 0 149 self.cython_create_listing = 0 150 self.cython_line_directives = 0 151 self.cython_include_dirs = None 152 self.cython_directives = None 153 self.cython_c_in_temp = 0 154 self.cython_gen_pxi = 0 155 self.cython_gdb = False 156 self.no_c_in_traceback = 0 157 self.cython_compile_time_env = None 158 159 def __getattr__(self, name): 160 if name[:6] == 'pyrex_': 161 return getattr(self, 'cython_' + name[6:]) 162 else: 163 return _build_ext.build_ext.__getattr__(self, name) 164 165 def __setattr__(self, name, value): 166 if name[:6] == 'pyrex_': 167 return setattr(self, 'cython_' + name[6:], value) 168 else: 169 # _build_ext.build_ext.__setattr__(self, name, value) 170 self.__dict__[name] = value 171 172 def finalize_options(self): 173 _build_ext.build_ext.finalize_options(self) 174 if self.cython_include_dirs is None: 175 self.cython_include_dirs = [] 176 elif isinstance(self.cython_include_dirs, basestring): 177 self.cython_include_dirs = \ 178 self.cython_include_dirs.split(os.pathsep) 179 if self.cython_directives is None: 180 self.cython_directives = {} 181 # finalize_options () 182 183 def run(self): 184 # We have one shot at this before build_ext initializes the compiler. 185 # If --pyrex-gdb is in effect as a command line option or as option 186 # of any Extension module, disable optimization for the C or C++ 187 # compiler. 188 if self.cython_gdb or [1 for ext in self.extensions 189 if getattr(ext, 'cython_gdb', False)]: 190 optimization.disable_optimization() 191 192 _build_ext.build_ext.run(self) 193 194 def check_extensions_list(self, extensions): 195 # Note: might get called multiple times. 196 _build_ext.build_ext.check_extensions_list(self, extensions) 197 for ext in self.extensions: 198 ext.sources = self.cython_sources(ext.sources, ext) 199 200 def cython_sources(self, sources, extension): 201 """ 202 Walk the list of source files in 'sources', looking for Cython 203 source files (.pyx and .py). Run Cython on all that are 204 found, and return a modified 'sources' list with Cython source 205 files replaced by the generated C (or C++) files. 206 """ 207 new_sources = [] 208 cython_sources = [] 209 cython_targets = {} 210 211 # Setup create_list and cplus from the extension options if 212 # Cython.Distutils.extension.Extension is used, otherwise just 213 # use what was parsed from the command-line or the configuration file. 214 # cplus will also be set to true is extension.language is equal to 215 # 'C++' or 'c++'. 216 #try: 217 # create_listing = self.cython_create_listing or \ 218 # extension.cython_create_listing 219 # cplus = self.cython_cplus or \ 220 # extension.cython_cplus or \ 221 # (extension.language != None and \ 222 # extension.language.lower() == 'c++') 223 #except AttributeError: 224 # create_listing = self.cython_create_listing 225 # cplus = self.cython_cplus or \ 226 # (extension.language != None and \ 227 # extension.language.lower() == 'c++') 228 229 create_listing = self.cython_create_listing or \ 230 getattr(extension, 'cython_create_listing', 0) 231 line_directives = self.cython_line_directives or \ 232 getattr(extension, 'cython_line_directives', 0) 233 no_c_in_traceback = self.no_c_in_traceback or \ 234 getattr(extension, 'no_c_in_traceback', 0) 235 cplus = self.cython_cplus or getattr(extension, 'cython_cplus', 0) or \ 236 (extension.language and extension.language.lower() == 'c++') 237 cython_gen_pxi = self.cython_gen_pxi or getattr(extension, 'cython_gen_pxi', 0) 238 cython_gdb = self.cython_gdb or getattr(extension, 'cython_gdb', False) 239 cython_compile_time_env = self.cython_compile_time_env or \ 240 getattr(extension, 'cython_compile_time_env', None) 241 242 # Set up the include_path for the Cython compiler: 243 # 1. Start with the command line option. 244 # 2. Add in any (unique) paths from the extension 245 # cython_include_dirs (if Cython.Distutils.extension is used). 246 # 3. Add in any (unique) paths from the extension include_dirs 247 includes = list(self.cython_include_dirs) 248 try: 249 for i in extension.cython_include_dirs: 250 if i not in includes: 251 includes.append(i) 252 except AttributeError: 253 pass 254 255 # In case extension.include_dirs is a generator, evaluate it and keep 256 # result 257 extension.include_dirs = list(extension.include_dirs) 258 for i in extension.include_dirs: 259 if i not in includes: 260 includes.append(i) 261 262 # Set up Cython compiler directives: 263 # 1. Start with the command line option. 264 # 2. Add in any (unique) entries from the extension 265 # cython_directives (if Cython.Distutils.extension is used). 266 directives = dict(self.cython_directives) 267 if hasattr(extension, "cython_directives"): 268 directives.update(extension.cython_directives) 269 270 # Set the target file extension for C/C++ mode. 271 if cplus: 272 target_ext = '.cpp' 273 else: 274 target_ext = '.c' 275 276 # Decide whether to drop the generated C files into the temp dir 277 # or the source tree. 278 279 if not self.inplace and (self.cython_c_in_temp 280 or getattr(extension, 'cython_c_in_temp', 0)): 281 target_dir = os.path.join(self.build_temp, "pyrex") 282 for package_name in extension.name.split('.')[:-1]: 283 target_dir = os.path.join(target_dir, package_name) 284 else: 285 target_dir = None 286 287 newest_dependency = None 288 for source in sources: 289 (base, ext) = os.path.splitext(os.path.basename(source)) 290 if ext == ".py": 291 # FIXME: we might want to special case this some more 292 ext = '.pyx' 293 if ext == ".pyx": # Cython source file 294 output_dir = target_dir or os.path.dirname(source) 295 new_sources.append(os.path.join(output_dir, base + target_ext)) 296 cython_sources.append(source) 297 cython_targets[source] = new_sources[-1] 298 elif ext == '.pxi' or ext == '.pxd': 299 if newest_dependency is None \ 300 or newer(source, newest_dependency): 301 newest_dependency = source 302 else: 303 new_sources.append(source) 304 305 if not cython_sources: 306 return new_sources 307 308 try: 309 from Cython.Compiler.Main \ 310 import CompilationOptions, \ 311 default_options as cython_default_options, \ 312 compile as cython_compile 313 from Cython.Compiler.Errors import PyrexError 314 except ImportError: 315 e = sys.exc_info()[1] 316 print("failed to import Cython: %s" % e) 317 raise DistutilsPlatformError("Cython does not appear to be installed") 318 319 module_name = extension.name 320 321 for source in cython_sources: 322 target = cython_targets[source] 323 depends = [source] + list(extension.depends or ()) 324 if(source[-4:].lower()==".pyx" and os.path.isfile(source[:-3]+"pxd")): 325 depends += [source[:-3]+"pxd"] 326 rebuild = self.force or newer_group(depends, target, 'newer') 327 if not rebuild and newest_dependency is not None: 328 rebuild = newer(newest_dependency, target) 329 if rebuild: 330 log.info("cythoning %s to %s", source, target) 331 self.mkpath(os.path.dirname(target)) 332 if self.inplace: 333 output_dir = os.curdir 334 else: 335 output_dir = self.build_lib 336 options = CompilationOptions(cython_default_options, 337 use_listing_file = create_listing, 338 include_path = includes, 339 compiler_directives = directives, 340 output_file = target, 341 cplus = cplus, 342 emit_linenums = line_directives, 343 c_line_in_traceback = not no_c_in_traceback, 344 generate_pxi = cython_gen_pxi, 345 output_dir = output_dir, 346 gdb_debug = cython_gdb, 347 compile_time_env = cython_compile_time_env) 348 result = cython_compile(source, options=options, 349 full_module_name=module_name) 350 else: 351 log.info("skipping '%s' Cython extension (up-to-date)", target) 352 353 return new_sources 354 355 # cython_sources () 356 357# class build_ext 358