5 from distutils.command.build_ext import build_ext as _du_build_ext
6 from distutils.file_util import copy_file
7 from distutils.ccompiler import new_compiler
8 from distutils.sysconfig import customize_compiler, get_config_var
9 from distutils.errors import DistutilsError
10 from distutils import log
12 from setuptools.extension import Library
13 from setuptools.extern import six
16 # Attempt to use Cython for building extensions, if available
17 from Cython.Distutils.build_ext import build_ext as _build_ext
19 _build_ext = _du_build_ext
21 # make sure _config_vars is initialized
22 get_config_var("LDSHARED")
23 from distutils.sysconfig import _config_vars as _CONFIG_VARS
26 def _customize_compiler_for_shlib(compiler):
27 if sys.platform == "darwin":
28 # building .dylib requires additional compiler flags on OSX; here we
29 # temporarily substitute the pyconfig.h variables so that distutils'
30 # 'customize_compiler' uses them before we build the shared libraries.
31 tmp = _CONFIG_VARS.copy()
33 # XXX Help! I don't have any idea whether these are right...
34 _CONFIG_VARS['LDSHARED'] = (
35 "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup")
36 _CONFIG_VARS['CCSHARED'] = " -dynamiclib"
37 _CONFIG_VARS['SO'] = ".dylib"
38 customize_compiler(compiler)
41 _CONFIG_VARS.update(tmp)
43 customize_compiler(compiler)
50 if sys.platform == "darwin":
55 use_stubs = have_rtld = hasattr(dl, 'RTLD_NOW')
59 if_dl = lambda s: s if have_rtld else ''
62 def get_abi3_suffix():
63 """Return the file extension for an abi3-compliant Extension()"""
64 for suffix, _, _ in (s for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION):
65 if '.abi3' in suffix: # Unix
67 elif suffix == '.pyd': # Windows
71 class build_ext(_build_ext):
73 """Build extensions in build directory, then copy if --inplace"""
74 old_inplace, self.inplace = self.inplace, 0
76 self.inplace = old_inplace
78 self.copy_extensions_to_source()
80 def copy_extensions_to_source(self):
81 build_py = self.get_finalized_command('build_py')
82 for ext in self.extensions:
83 fullname = self.get_ext_fullname(ext.name)
84 filename = self.get_ext_filename(fullname)
85 modpath = fullname.split('.')
86 package = '.'.join(modpath[:-1])
87 package_dir = build_py.get_package_dir(package)
88 dest_filename = os.path.join(package_dir,
89 os.path.basename(filename))
90 src_filename = os.path.join(self.build_lib, filename)
92 # Always copy, even if source is older than destination, to ensure
93 # that the right extensions for the current Python/platform are
96 src_filename, dest_filename, verbose=self.verbose,
100 self.write_stub(package_dir or os.curdir, ext, True)
102 def get_ext_filename(self, fullname):
103 filename = _build_ext.get_ext_filename(self, fullname)
104 if fullname in self.ext_map:
105 ext = self.ext_map[fullname]
108 and getattr(ext, 'py_limited_api')
109 and get_abi3_suffix()
112 so_ext = _get_config_var_837('EXT_SUFFIX')
113 filename = filename[:-len(so_ext)]
114 filename = filename + get_abi3_suffix()
115 if isinstance(ext, Library):
116 fn, ext = os.path.splitext(filename)
117 return self.shlib_compiler.library_filename(fn, libtype)
118 elif use_stubs and ext._links_to_dynamic:
119 d, fn = os.path.split(filename)
120 return os.path.join(d, 'dl-' + fn)
123 def initialize_options(self):
124 _build_ext.initialize_options(self)
125 self.shlib_compiler = None
129 def finalize_options(self):
130 _build_ext.finalize_options(self)
131 self.extensions = self.extensions or []
132 self.check_extensions_list(self.extensions)
133 self.shlibs = [ext for ext in self.extensions
134 if isinstance(ext, Library)]
136 self.setup_shlib_compiler()
137 for ext in self.extensions:
138 ext._full_name = self.get_ext_fullname(ext.name)
139 for ext in self.extensions:
140 fullname = ext._full_name
141 self.ext_map[fullname] = ext
143 # distutils 3.1 will also ask for module names
144 # XXX what to do with conflicts?
145 self.ext_map[fullname.split('.')[-1]] = ext
147 ltd = self.shlibs and self.links_to_dynamic(ext) or False
148 ns = ltd and use_stubs and not isinstance(ext, Library)
149 ext._links_to_dynamic = ltd
151 filename = ext._file_name = self.get_ext_filename(fullname)
152 libdir = os.path.dirname(os.path.join(self.build_lib, filename))
153 if ltd and libdir not in ext.library_dirs:
154 ext.library_dirs.append(libdir)
155 if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs:
156 ext.runtime_library_dirs.append(os.curdir)
158 def setup_shlib_compiler(self):
159 compiler = self.shlib_compiler = new_compiler(
160 compiler=self.compiler, dry_run=self.dry_run, force=self.force
162 _customize_compiler_for_shlib(compiler)
164 if self.include_dirs is not None:
165 compiler.set_include_dirs(self.include_dirs)
166 if self.define is not None:
167 # 'define' option is a list of (name,value) tuples
168 for (name, value) in self.define:
169 compiler.define_macro(name, value)
170 if self.undef is not None:
171 for macro in self.undef:
172 compiler.undefine_macro(macro)
173 if self.libraries is not None:
174 compiler.set_libraries(self.libraries)
175 if self.library_dirs is not None:
176 compiler.set_library_dirs(self.library_dirs)
177 if self.rpath is not None:
178 compiler.set_runtime_library_dirs(self.rpath)
179 if self.link_objects is not None:
180 compiler.set_link_objects(self.link_objects)
182 # hack so distutils' build_extension() builds a library instead
183 compiler.link_shared_object = link_shared_object.__get__(compiler)
185 def get_export_symbols(self, ext):
186 if isinstance(ext, Library):
187 return ext.export_symbols
188 return _build_ext.get_export_symbols(self, ext)
190 def build_extension(self, ext):
191 ext._convert_pyx_sources_to_lang()
192 _compiler = self.compiler
194 if isinstance(ext, Library):
195 self.compiler = self.shlib_compiler
196 _build_ext.build_extension(self, ext)
198 cmd = self.get_finalized_command('build_py').build_lib
199 self.write_stub(cmd, ext)
201 self.compiler = _compiler
203 def links_to_dynamic(self, ext):
204 """Return true if 'ext' links to a dynamic lib in the same package"""
205 # XXX this should check to ensure the lib is actually being built
206 # XXX as dynamic, and not just using a locally-found version or a
207 # XXX static-compiled version
208 libnames = dict.fromkeys([lib._full_name for lib in self.shlibs])
209 pkg = '.'.join(ext._full_name.split('.')[:-1] + [''])
210 return any(pkg + libname in libnames for libname in ext.libraries)
212 def get_outputs(self):
213 return _build_ext.get_outputs(self) + self.__get_stubs_outputs()
215 def __get_stubs_outputs(self):
216 # assemble the base name for each extension that needs a stub
218 os.path.join(self.build_lib, *ext._full_name.split('.'))
219 for ext in self.extensions
222 # pair each base with the extension
223 pairs = itertools.product(ns_ext_bases, self.__get_output_extensions())
224 return list(base + fnext for base, fnext in pairs)
226 def __get_output_extensions(self):
229 if self.get_finalized_command('build_py').optimize:
232 def write_stub(self, output_dir, ext, compile=False):
233 log.info("writing stub loader for %s to %s", ext._full_name,
235 stub_file = (os.path.join(output_dir, *ext._full_name.split('.')) +
237 if compile and os.path.exists(stub_file):
238 raise DistutilsError(stub_file + " already exists! Please delete.")
240 f = open(stub_file, 'w')
243 "def __bootstrap__():",
244 " global __bootstrap__, __file__, __loader__",
245 " import sys, os, pkg_resources, imp" + if_dl(", dl"),
246 " __file__ = pkg_resources.resource_filename"
248 % os.path.basename(ext._file_name),
249 " del __bootstrap__",
250 " if '__loader__' in globals():",
252 if_dl(" old_flags = sys.getdlopenflags()"),
253 " old_dir = os.getcwd()",
255 " os.chdir(os.path.dirname(__file__))",
256 if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"),
257 " imp.load_dynamic(__name__,__file__)",
259 if_dl(" sys.setdlopenflags(old_flags)"),
260 " os.chdir(old_dir)",
267 from distutils.util import byte_compile
269 byte_compile([stub_file], optimize=0,
270 force=True, dry_run=self.dry_run)
271 optimize = self.get_finalized_command('install_lib').optimize
273 byte_compile([stub_file], optimize=optimize,
274 force=True, dry_run=self.dry_run)
275 if os.path.exists(stub_file) and not self.dry_run:
279 if use_stubs or os.name == 'nt':
280 # Build shared libraries
282 def link_shared_object(
283 self, objects, output_libname, output_dir=None, libraries=None,
284 library_dirs=None, runtime_library_dirs=None, export_symbols=None,
285 debug=0, extra_preargs=None, extra_postargs=None, build_temp=None,
288 self.SHARED_LIBRARY, objects, output_libname,
289 output_dir, libraries, library_dirs, runtime_library_dirs,
290 export_symbols, debug, extra_preargs, extra_postargs,
291 build_temp, target_lang
294 # Build static libraries everywhere else
297 def link_shared_object(
298 self, objects, output_libname, output_dir=None, libraries=None,
299 library_dirs=None, runtime_library_dirs=None, export_symbols=None,
300 debug=0, extra_preargs=None, extra_postargs=None, build_temp=None,
302 # XXX we need to either disallow these attrs on Library instances,
303 # or warn/abort here if set, or something...
304 # libraries=None, library_dirs=None, runtime_library_dirs=None,
305 # export_symbols=None, extra_preargs=None, extra_postargs=None,
308 assert output_dir is None # distutils build_ext doesn't pass this
309 output_dir, filename = os.path.split(output_libname)
310 basename, ext = os.path.splitext(filename)
311 if self.library_filename("x").startswith('lib'):
312 # strip 'lib' prefix; this is kludgy if some platform uses
314 basename = basename[3:]
316 self.create_static_lib(
317 objects, basename, output_dir, debug, target_lang
321 def _get_config_var_837(name):
323 In https://github.com/pypa/setuptools/pull/837, we discovered
324 Python 3.3.0 exposes the extension suffix under the name 'SO'.
326 if sys.version_info < (3, 3, 1):
328 return get_config_var(name)