Path: blob/develop/src/sage_setup/command/sage_build_ext.py
4081 views
import os1import errno23# Import setuptools before importing distutils, so that setuptools4# can replace distutils by its own vendored copy.5import setuptools67from distutils import log8from setuptools.command.build_ext import build_ext9from distutils.dep_util import newer_group10try:11# Available since https://setuptools.pypa.io/en/latest/history.html#v59-0-012from setuptools.errors import DistutilsSetupError13except ImportError:14from distutils.errors import DistutilsSetupError15from sage_setup.run_parallel import execute_list_of_commands161718class sage_build_ext(build_ext):19def finalize_options(self):20build_ext.finalize_options(self)21self.check_flags()2223def run(self):24# Always run the Cythonize command before building extensions25self.run_command('build_cython')26build_ext.run(self)2728def check_flags(self):29"""30Sanity check the compiler flags used to build the extensions31"""32forbidden = None33if os.environ.get("SAGE_FAT_BINARY") == "yes":34# When building with SAGE_FAT_BINARY=yes, we should not35# enable CPU features which do not exist on every CPU.36# Such flags usually come from other libraries adding the37# flags to the pkgconfig configuration. So if you hit these38# errors, the problem is most likely with some external39# library and not with Sage.40import re41forbidden = re.compile(r"-march=|-mpcu=|-msse3|-msse4|-mpopcnt|-mavx")4243if forbidden is not None:44errors = 045for ext in self.extensions:46flags = ext.extra_compile_args47for flag in flags:48if forbidden.match(flag):49log.error("%s uses forbidden flag '%s'", ext.name, flag)50errors += 151if errors:52raise RuntimeError("forbidden flags used")5354def build_extensions(self):5556from distutils.debug import DEBUG5758if DEBUG:59print("self.compiler.compiler:")60print(self.compiler.compiler)61print("self.compiler.compiler_cxx:")62print(self.compiler.compiler_cxx) # currently not used63print("self.compiler.compiler_so:")64print(self.compiler.compiler_so)65print("self.compiler.linker_so:")66print(self.compiler.linker_so)67# There are further interesting variables...6869if DEBUG:70print("self.compiler.linker_so (after fixing library dirs):")71print(self.compiler.linker_so)7273# First, sanity-check the 'extensions' list74self.check_extensions_list(self.extensions)7576import time77t = time.time()7879compile_commands = []80for ext in self.extensions:81need_to_compile, p = self.prepare_extension(ext)82if need_to_compile:83compile_commands.append((self.build_extension, p))8485execute_list_of_commands(compile_commands)8687print("Total time spent compiling C/C++ extensions: %.2f seconds." % (time.time() - t))8889def prepare_extension(self, ext):90sources = ext.sources91if sources is None or not isinstance(sources, (list, tuple)):92raise DistutilsSetupError(("in 'ext_modules' option (extension '%s'), " +93"'sources' must be present and must be " +94"a list of source filenames") % ext.name)95sources = list(sources)9697fullname = self.get_ext_fullname(ext.name)98if self.inplace:99# ignore build-lib -- put the compiled extension into100# the source tree along with pure Python modules101102modpath = fullname.split('.')103package = '.'.join(modpath[0:-1])104base = modpath[-1]105106build_py = self.get_finalized_command('build_py')107package_dir = build_py.get_package_dir(package)108ext_filename = os.path.join(package_dir,109self.get_ext_filename(base))110relative_ext_filename = self.get_ext_filename(base)111else:112ext_filename = os.path.join(self.build_lib,113self.get_ext_filename(fullname))114relative_ext_filename = self.get_ext_filename(fullname)115116# while dispatching the calls to gcc in parallel, we sometimes117# hit a race condition where two separate build_ext objects118# try to create a given directory at the same time; whoever119# loses the race then seems to throw an error, saying that120# the directory already exists. so, instead of fighting to121# fix the race condition, we simply make sure the entire122# directory tree exists now, while we're processing the123# extensions in serial.124relative_ext_dir = os.path.split(relative_ext_filename)[0]125prefixes = ['', self.build_lib, self.build_temp]126for prefix in prefixes:127path = os.path.join(prefix, relative_ext_dir)128try:129os.makedirs(path)130except OSError as e:131assert e.errno == errno.EEXIST, 'Cannot create %s.' % path132depends = sources + ext.depends133if not (self.force or newer_group(depends, ext_filename, 'newer')):134log.debug("skipping '%s' extension (up-to-date)", ext.name)135need_to_compile = False136else:137log.info("building '%s' extension", ext.name)138need_to_compile = True139140return need_to_compile, (sources, ext, ext_filename)141142def build_extension(self, p):143144sources, ext, ext_filename = p145146# First, scan the sources for SWIG definition files (.i), run147# SWIG on 'em to create .c files, and modify the sources list148# accordingly.149sources = self.swig_sources(sources, ext)150151# Next, compile the source code to object files.152153# XXX not honouring 'define_macros' or 'undef_macros' -- the154# CCompiler API needs to change to accommodate this, and I155# want to do one thing at a time!156157# Two possible sources for extra compiler arguments:158# - 'extra_compile_args' in Extension object159# - CFLAGS environment variable (not particularly160# elegant, but people seem to expect it and I161# guess it's useful)162# The environment variable should take precedence, and163# any sensible compiler will give precedence to later164# command line args. Hence we combine them in order:165extra_args = ext.extra_compile_args or []166167macros = ext.define_macros[:]168for undef in ext.undef_macros:169macros.append((undef,))170171objects = self.compiler.compile(sources,172output_dir=self.build_temp,173macros=macros,174include_dirs=ext.include_dirs,175debug=self.debug,176extra_postargs=extra_args,177depends=ext.depends)178179# XXX -- this is a Vile HACK!180#181# The setup.py script for Python on Unix needs to be able to182# get this list so it can perform all the clean up needed to183# avoid keeping object files around when cleaning out a failed184# build of an extension module. Since Distutils does not185# track dependencies, we have to get rid of intermediates to186# ensure all the intermediates will be properly re-built.187#188self._built_objects = objects[:]189190# Now link the object files together into a "shared object" --191# of course, first we have to figure out all the other things192# that go into the mix.193if ext.extra_objects:194objects.extend(ext.extra_objects)195extra_args = ext.extra_link_args or []196197# Detect target language, if not provided198language = ext.language or self.compiler.detect_language(sources)199200self.compiler.link_shared_object(201objects, ext_filename,202libraries=self.get_libraries(ext),203library_dirs=ext.library_dirs,204runtime_library_dirs=ext.runtime_library_dirs,205extra_postargs=extra_args,206export_symbols=self.get_export_symbols(ext),207debug=self.debug,208build_temp=self.build_temp,209target_lang=language)210211212