1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
'''
Enhances distutils with:
- better C++ support
- incremental builds (w/o header dependency handling)
- parallel builds
'''
import os
import sys
import distutils.sysconfig
import distutils.unixccompiler
# threaded parallel map (optional)
pmap = map
try:
import _osx_support
_osx_support._UNIVERSAL_CONFIG_VARS += ('CXXFLAGS', 'LDCXXSHARED',)
_osx_support._COMPILER_CONFIG_VARS += ('LDCXXSHARED',)
except ImportError:
_osx_support = None
distutils.unixccompiler.UnixCCompiler.executables.update({
'compiler_cxx' : ["c++"],
'compiler_so_cxx' : ["c++"],
'linker_so_cxx' : ["c++", "-shared"],
'linker_exe_cxx' : ["c++"],
})
def set_parallel_jobs(N):
'''
Set the number of parallel build jobs.
N=1 : single threaded
N=0 : use number of CPUs
'''
global pmap
if N == 1:
pmap = map
else:
from multiprocessing import pool
pmap = pool.ThreadPool(N or None).map
def monkeypatch(parent, name):
'''
Decorator to replace a function or class method. Makes the
unpatched function available as <patchedfunction>._super
'''
def wrapper(func):
orig = getattr(parent, name)
func._super = orig
func.__name__ = name
setattr(parent, name, func)
return func
return wrapper
@monkeypatch(distutils.sysconfig, 'customize_compiler')
def customize_compiler(compiler):
customize_compiler._super(compiler)
if compiler.compiler_type != "unix":
return
(cxx, ccshared, ldcxxshared) = \
distutils.sysconfig.get_config_vars('CXX', 'CCSHARED', 'LDCXXSHARED')
cxx = os.environ.get('CXX') or cxx
cxxflags = os.environ.get('CXXFLAGS', '') + ' ' + os.environ.get('CPPFLAGS', '')
ldcxxshared = os.environ.get('LDCXXSHARED', ldcxxshared or '') + \
' ' + os.environ.get('LDFLAGS', '') + \
' ' + os.environ.get('CXXFLAGS', '') + \
' ' + os.environ.get('CPPFLAGS', '')
cxx_cmd = cxx + ' ' + cxxflags
# C++11 by default
if '-std=' not in cxx_cmd:
cxx_cmd += ' -std=c++11'
compiler.set_executables(
compiler_cxx=cxx_cmd,
compiler_so_cxx=cxx_cmd + ' ' + ccshared,
linker_so_cxx=ldcxxshared,
linker_exe_cxx=cxx)
@monkeypatch(distutils.unixccompiler.UnixCCompiler, 'compile')
def compile(self, sources, output_dir=None, macros=None,
include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None,
depends=None):
'''
Enable parallel and incremental build.
To do a clean build, please remove the "build" directory.
'''
if os.getenv('DEBUG', ''):
debug = 1
macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
output_dir, macros, include_dirs, sources, depends, extra_postargs)
cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
compiler_so = self.compiler_so
compiler_so_cxx = self.compiler_so_cxx
if sys.platform == 'darwin' and _osx_support is not None:
compiler_so = _osx_support.compiler_fixup(compiler_so, cc_args + extra_postargs)
compiler_so_cxx = _osx_support.compiler_fixup(compiler_so_cxx, cc_args + extra_postargs)
def _single_compile(obj):
try:
src, ext = build[obj]
except KeyError:
return
try:
if not self.force and \
os.path.getmtime(obj) > \
os.path.getmtime(src):
return
except OSError:
pass
# _compile
compiler = compiler_so_cxx \
if self.detect_language(src) == 'c++' \
else compiler_so
try:
self.spawn(compiler + cc_args + [src, '-o', obj] + extra_postargs)
except distutils.errors.DistutilsExecError as msg:
raise distutils.errors.CompileError(msg)
for _ in pmap(_single_compile, objects):
pass
return objects
|