1import os 2import sys 3import itertools 4import imp 5from distutils.command.build_ext import build_ext as _du_build_ext 6from distutils.file_util import copy_file 7from distutils.ccompiler import new_compiler 8from distutils.sysconfig import customize_compiler, get_config_var 9from distutils.errors import DistutilsError 10from distutils import log 11 12from setuptools.extension import Library 13from setuptools.extern import six 14 15try: 16 # Attempt to use Cython for building extensions, if available 17 from Cython.Distutils.build_ext import build_ext as _build_ext 18 # Additionally, assert that the compiler module will load 19 # also. Ref #1229. 20 __import__('Cython.Compiler.Main') 21except ImportError: 22 _build_ext = _du_build_ext 23 24# make sure _config_vars is initialized 25get_config_var("LDSHARED") 26from distutils.sysconfig import _config_vars as _CONFIG_VARS 27 28 29def _customize_compiler_for_shlib(compiler): 30 if sys.platform == "darwin": 31 # building .dylib requires additional compiler flags on OSX; here we 32 # temporarily substitute the pyconfig.h variables so that distutils' 33 # 'customize_compiler' uses them before we build the shared libraries. 34 tmp = _CONFIG_VARS.copy() 35 try: 36 # XXX Help! I don't have any idea whether these are right... 37 _CONFIG_VARS['LDSHARED'] = ( 38 "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup") 39 _CONFIG_VARS['CCSHARED'] = " -dynamiclib" 40 _CONFIG_VARS['SO'] = ".dylib" 41 customize_compiler(compiler) 42 finally: 43 _CONFIG_VARS.clear() 44 _CONFIG_VARS.update(tmp) 45 else: 46 customize_compiler(compiler) 47 48 49have_rtld = False 50use_stubs = False 51libtype = 'shared' 52 53if sys.platform == "darwin": 54 use_stubs = True 55elif os.name != 'nt': 56 try: 57 import dl 58 use_stubs = have_rtld = hasattr(dl, 'RTLD_NOW') 59 except ImportError: 60 pass 61 62if_dl = lambda s: s if have_rtld else '' 63 64 65def get_abi3_suffix(): 66 """Return the file extension for an abi3-compliant Extension()""" 67 for suffix, _, _ in (s for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION): 68 if '.abi3' in suffix: # Unix 69 return suffix 70 elif suffix == '.pyd': # Windows 71 return suffix 72 73 74class build_ext(_build_ext): 75 def run(self): 76 """Build extensions in build directory, then copy if --inplace""" 77 old_inplace, self.inplace = self.inplace, 0 78 _build_ext.run(self) 79 self.inplace = old_inplace 80 if old_inplace: 81 self.copy_extensions_to_source() 82 83 def copy_extensions_to_source(self): 84 build_py = self.get_finalized_command('build_py') 85 for ext in self.extensions: 86 fullname = self.get_ext_fullname(ext.name) 87 filename = self.get_ext_filename(fullname) 88 modpath = fullname.split('.') 89 package = '.'.join(modpath[:-1]) 90 package_dir = build_py.get_package_dir(package) 91 dest_filename = os.path.join(package_dir, 92 os.path.basename(filename)) 93 src_filename = os.path.join(self.build_lib, filename) 94 95 # Always copy, even if source is older than destination, to ensure 96 # that the right extensions for the current Python/platform are 97 # used. 98 copy_file( 99 src_filename, dest_filename, verbose=self.verbose, 100 dry_run=self.dry_run 101 ) 102 if ext._needs_stub: 103 self.write_stub(package_dir or os.curdir, ext, True) 104 105 def get_ext_filename(self, fullname): 106 filename = _build_ext.get_ext_filename(self, fullname) 107 if fullname in self.ext_map: 108 ext = self.ext_map[fullname] 109 use_abi3 = ( 110 six.PY3 111 and getattr(ext, 'py_limited_api') 112 and get_abi3_suffix() 113 ) 114 if use_abi3: 115 so_ext = _get_config_var_837('EXT_SUFFIX') 116 filename = filename[:-len(so_ext)] 117 filename = filename + get_abi3_suffix() 118 if isinstance(ext, Library): 119 fn, ext = os.path.splitext(filename) 120 return self.shlib_compiler.library_filename(fn, libtype) 121 elif use_stubs and ext._links_to_dynamic: 122 d, fn = os.path.split(filename) 123 return os.path.join(d, 'dl-' + fn) 124 return filename 125 126 def initialize_options(self): 127 _build_ext.initialize_options(self) 128 self.shlib_compiler = None 129 self.shlibs = [] 130 self.ext_map = {} 131 132 def finalize_options(self): 133 _build_ext.finalize_options(self) 134 self.extensions = self.extensions or [] 135 self.check_extensions_list(self.extensions) 136 self.shlibs = [ext for ext in self.extensions 137 if isinstance(ext, Library)] 138 if self.shlibs: 139 self.setup_shlib_compiler() 140 for ext in self.extensions: 141 ext._full_name = self.get_ext_fullname(ext.name) 142 for ext in self.extensions: 143 fullname = ext._full_name 144 self.ext_map[fullname] = ext 145 146 # distutils 3.1 will also ask for module names 147 # XXX what to do with conflicts? 148 self.ext_map[fullname.split('.')[-1]] = ext 149 150 ltd = self.shlibs and self.links_to_dynamic(ext) or False 151 ns = ltd and use_stubs and not isinstance(ext, Library) 152 ext._links_to_dynamic = ltd 153 ext._needs_stub = ns 154 filename = ext._file_name = self.get_ext_filename(fullname) 155 libdir = os.path.dirname(os.path.join(self.build_lib, filename)) 156 if ltd and libdir not in ext.library_dirs: 157 ext.library_dirs.append(libdir) 158 if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs: 159 ext.runtime_library_dirs.append(os.curdir) 160 161 def setup_shlib_compiler(self): 162 compiler = self.shlib_compiler = new_compiler( 163 compiler=self.compiler, dry_run=self.dry_run, force=self.force 164 ) 165 _customize_compiler_for_shlib(compiler) 166 167 if self.include_dirs is not None: 168 compiler.set_include_dirs(self.include_dirs) 169 if self.define is not None: 170 # 'define' option is a list of (name,value) tuples 171 for (name, value) in self.define: 172 compiler.define_macro(name, value) 173 if self.undef is not None: 174 for macro in self.undef: 175 compiler.undefine_macro(macro) 176 if self.libraries is not None: 177 compiler.set_libraries(self.libraries) 178 if self.library_dirs is not None: 179 compiler.set_library_dirs(self.library_dirs) 180 if self.rpath is not None: 181 compiler.set_runtime_library_dirs(self.rpath) 182 if self.link_objects is not None: 183 compiler.set_link_objects(self.link_objects) 184 185 # hack so distutils' build_extension() builds a library instead 186 compiler.link_shared_object = link_shared_object.__get__(compiler) 187 188 def get_export_symbols(self, ext): 189 if isinstance(ext, Library): 190 return ext.export_symbols 191 return _build_ext.get_export_symbols(self, ext) 192 193 def build_extension(self, ext): 194 ext._convert_pyx_sources_to_lang() 195 _compiler = self.compiler 196 try: 197 if isinstance(ext, Library): 198 self.compiler = self.shlib_compiler 199 _build_ext.build_extension(self, ext) 200 if ext._needs_stub: 201 cmd = self.get_finalized_command('build_py').build_lib 202 self.write_stub(cmd, ext) 203 finally: 204 self.compiler = _compiler 205 206 def links_to_dynamic(self, ext): 207 """Return true if 'ext' links to a dynamic lib in the same package""" 208 # XXX this should check to ensure the lib is actually being built 209 # XXX as dynamic, and not just using a locally-found version or a 210 # XXX static-compiled version 211 libnames = dict.fromkeys([lib._full_name for lib in self.shlibs]) 212 pkg = '.'.join(ext._full_name.split('.')[:-1] + ['']) 213 return any(pkg + libname in libnames for libname in ext.libraries) 214 215 def get_outputs(self): 216 return _build_ext.get_outputs(self) + self.__get_stubs_outputs() 217 218 def __get_stubs_outputs(self): 219 # assemble the base name for each extension that needs a stub 220 ns_ext_bases = ( 221 os.path.join(self.build_lib, *ext._full_name.split('.')) 222 for ext in self.extensions 223 if ext._needs_stub 224 ) 225 # pair each base with the extension 226 pairs = itertools.product(ns_ext_bases, self.__get_output_extensions()) 227 return list(base + fnext for base, fnext in pairs) 228 229 def __get_output_extensions(self): 230 yield '.py' 231 yield '.pyc' 232 if self.get_finalized_command('build_py').optimize: 233 yield '.pyo' 234 235 def write_stub(self, output_dir, ext, compile=False): 236 log.info("writing stub loader for %s to %s", ext._full_name, 237 output_dir) 238 stub_file = (os.path.join(output_dir, *ext._full_name.split('.')) + 239 '.py') 240 if compile and os.path.exists(stub_file): 241 raise DistutilsError(stub_file + " already exists! Please delete.") 242 if not self.dry_run: 243 f = open(stub_file, 'w') 244 f.write( 245 '\n'.join([ 246 "def __bootstrap__():", 247 " global __bootstrap__, __file__, __loader__", 248 " import sys, os, pkg_resources, imp" + if_dl(", dl"), 249 " __file__ = pkg_resources.resource_filename" 250 "(__name__,%r)" 251 % os.path.basename(ext._file_name), 252 " del __bootstrap__", 253 " if '__loader__' in globals():", 254 " del __loader__", 255 if_dl(" old_flags = sys.getdlopenflags()"), 256 " old_dir = os.getcwd()", 257 " try:", 258 " os.chdir(os.path.dirname(__file__))", 259 if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), 260 " imp.load_dynamic(__name__,__file__)", 261 " finally:", 262 if_dl(" sys.setdlopenflags(old_flags)"), 263 " os.chdir(old_dir)", 264 "__bootstrap__()", 265 "" # terminal \n 266 ]) 267 ) 268 f.close() 269 if compile: 270 from distutils.util import byte_compile 271 272 byte_compile([stub_file], optimize=0, 273 force=True, dry_run=self.dry_run) 274 optimize = self.get_finalized_command('install_lib').optimize 275 if optimize > 0: 276 byte_compile([stub_file], optimize=optimize, 277 force=True, dry_run=self.dry_run) 278 if os.path.exists(stub_file) and not self.dry_run: 279 os.unlink(stub_file) 280 281 282if use_stubs or os.name == 'nt': 283 # Build shared libraries 284 # 285 def link_shared_object( 286 self, objects, output_libname, output_dir=None, libraries=None, 287 library_dirs=None, runtime_library_dirs=None, export_symbols=None, 288 debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, 289 target_lang=None): 290 self.link( 291 self.SHARED_LIBRARY, objects, output_libname, 292 output_dir, libraries, library_dirs, runtime_library_dirs, 293 export_symbols, debug, extra_preargs, extra_postargs, 294 build_temp, target_lang 295 ) 296else: 297 # Build static libraries everywhere else 298 libtype = 'static' 299 300 def link_shared_object( 301 self, objects, output_libname, output_dir=None, libraries=None, 302 library_dirs=None, runtime_library_dirs=None, export_symbols=None, 303 debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, 304 target_lang=None): 305 # XXX we need to either disallow these attrs on Library instances, 306 # or warn/abort here if set, or something... 307 # libraries=None, library_dirs=None, runtime_library_dirs=None, 308 # export_symbols=None, extra_preargs=None, extra_postargs=None, 309 # build_temp=None 310 311 assert output_dir is None # distutils build_ext doesn't pass this 312 output_dir, filename = os.path.split(output_libname) 313 basename, ext = os.path.splitext(filename) 314 if self.library_filename("x").startswith('lib'): 315 # strip 'lib' prefix; this is kludgy if some platform uses 316 # a different prefix 317 basename = basename[3:] 318 319 self.create_static_lib( 320 objects, basename, output_dir, debug, target_lang 321 ) 322 323 324def _get_config_var_837(name): 325 """ 326 In https://github.com/pypa/setuptools/pull/837, we discovered 327 Python 3.3.0 exposes the extension suffix under the name 'SO'. 328 """ 329 if sys.version_info < (3, 3, 1): 330 name = 'SO' 331 return get_config_var(name) 332