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