blob: c9bddd53c1cfbec7fce4bbfab576b71cbf0715aa [file] [log] [blame]
import os
import setuptools
import distutils
from contextlib import contextmanager
import subprocess
BUILD_DIR = 'build'
# on the fly create a ninja file in build/ and then
# run it when run() is called.
class NinjaBuilder(object):
def __init__(self, name):
import ninja
if not os.path.exists(BUILD_DIR):
os.mkdir(BUILD_DIR)
self.ninja_program = os.path.join(ninja.BIN_DIR, 'ninja')
self.name = name
self.filename = os.path.join(BUILD_DIR, 'build.{}.ninja'.format(name))
self.writer = ninja.Writer(open(self.filename, 'w'))
self.writer.rule('do_cmd', '$cmd')
self.writer.rule('compile', '$cmd')
self.compdb_targets = []
def run(self):
import ninja
self.writer.close()
subprocess.check_call([self.ninja_program, '-f', self.filename])
compile_db_path = os.path.join(BUILD_DIR, '{}_compile_commands.json'.format(self.name))
with open(compile_db_path, 'w') as compile_db:
subprocess.check_call([self.ninja_program, '-f', self.filename,
'-t', 'compdb', 'compile'], stdout=compile_db)
# weird build logic in build develop causes some things to be run
# twice so make sure even after we run the command we still
# reset this to a valid state
# don't use the same name or you can't inspect the real ninja files
self.__init__(self.name + "_")
class ninja_build_ext(setuptools.command.build_ext.build_ext):
def _build_default(self, ext):
return setuptools.command.build_ext.build_ext.build_extension(self, ext)
def build_extension(self, ext):
builder = NinjaBuilder(ext.name)
@contextmanager
def patch(obj, attr_name, val):
orig_val = getattr(obj, attr_name)
setattr(obj, attr_name, val)
try:
yield
finally:
setattr(obj, attr_name, orig_val)
orig_compile = distutils.unixccompiler.UnixCCompiler._compile
orig_link = distutils.unixccompiler.UnixCCompiler.link
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
depfile = os.path.splitext(obj)[0] + '.d'
def spawn(cmd):
builder.writer.build(
[obj], 'compile', [src],
variables={
'cmd': cmd,
'depfile': depfile,
'deps': 'gcc'
})
extra_postargs = extra_postargs + ['-MMD', '-MF', depfile]
with patch(self, 'spawn', spawn):
orig_compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts)
def link(self, target_desc, objects,
output_filename, output_dir=None, libraries=None,
library_dirs=None, runtime_library_dirs=None,
export_symbols=None, debug=0, extra_preargs=None,
extra_postargs=None, build_temp=None, target_lang=None):
builder.run()
orig_link(self, target_desc, objects,
output_filename, output_dir, libraries,
library_dirs, runtime_library_dirs,
export_symbols, debug, extra_preargs,
extra_postargs, build_temp, target_lang)
with patch(distutils.unixccompiler.UnixCCompiler, '_compile', _compile):
with patch(distutils.unixccompiler.UnixCCompiler, 'link', link):
with patch(self, 'force', True):
self._build_default(ext)