aboutsummaryrefslogtreecommitdiff
path: root/ninjabackend.py
diff options
context:
space:
mode:
Diffstat (limited to 'ninjabackend.py')
-rw-r--r--ninjabackend.py112
1 files changed, 96 insertions, 16 deletions
diff --git a/ninjabackend.py b/ninjabackend.py
index 2fa60df..01223ab 100644
--- a/ninjabackend.py
+++ b/ninjabackend.py
@@ -114,6 +114,13 @@ class NinjaBackend(backends.Backend):
self.source_suffix_in_objs = True
self.ninja_filename = 'build.ninja'
self.fortran_deps = {}
+ self.all_outputs = {}
+
+ def check_outputs(self, elem):
+ for n in elem.outfilenames:
+ if n in self.all_outputs:
+ raise MesonException('Multiple producers for Ninja target "%s". Please rename your targets.' % n)
+ self.all_outputs[n] = True
def generate(self, interp):
self.interpreter = interp
@@ -259,7 +266,8 @@ class NinjaBackend(backends.Backend):
obj_list.append(self.generate_single_compile(target, outfile, src, True, unity_deps + header_deps))
linker = self.determine_linker(target, src_list)
elem = self.generate_link(target, outfile, outname, obj_list, linker, pch_objects)
- self.generate_shlib_aliases(target, self.get_target_dir(target), outfile, elem)
+ self.generate_shlib_aliases(target, self.get_target_dir(target))
+ elem.write(outfile)
self.processed_targets[name] = True
def process_target_dependencies(self, target, outfile):
@@ -326,17 +334,42 @@ class NinjaBackend(backends.Backend):
elem.add_item('COMMAND', cmd)
elem.add_item('description', 'Generating %s with a custom command.' % target.name)
elem.write(outfile)
+ self.check_outputs(elem)
self.processed_targets[target.name + target.type_suffix()] = True
def generate_run_target(self, target, outfile):
runnerscript = os.path.join(self.environment.get_script_dir(), 'commandrunner.py')
- elem = NinjaBuildElement(target.name, 'CUSTOM_COMMAND', [])
- cmd = [sys.executable, runnerscript, self.environment.get_source_dir(), self.environment.get_build_dir(),
- target.subdir, target.command] + target.args
+ deps = []
+ arg_strings = []
+ for i in target.args:
+ if isinstance(i, str):
+ arg_strings.append(i)
+ elif isinstance(i, build.BuildTarget):
+ deps.append(self.get_target_filename(i))
+ else:
+ raise MesonException('Unreachable code.')
+ elem = NinjaBuildElement(target.name, 'CUSTOM_COMMAND', deps)
+ cmd = [sys.executable, runnerscript, self.environment.get_source_dir(), self.environment.get_build_dir(), target.subdir]
+ texe = target.command
+ try:
+ texe = texe.held_object
+ except AttributeError:
+ pass
+ if isinstance(texe, build.Executable):
+ abs_exe = os.path.join(self.environment.get_build_dir(), self.get_target_filename(texe))
+ deps.append(self.get_target_filename(texe))
+ if self.environment.is_cross_build() \
+ and self.environment.cross_info.config['binaries'].get('exe_wrapper', None) is not None:
+ cmd += [self.environment.cross_info.config['binaries']['exe_wrapper']]
+ cmd.append(abs_exe)
+ else:
+ cmd.append(target.command)
+ cmd += target.args
elem.add_item('COMMAND', cmd)
elem.add_item('description', 'Running external command %s.' % target.name)
elem.add_item('pool', 'console')
elem.write(outfile)
+ self.check_outputs(elem)
self.processed_targets[target.name + target.type_suffix()] = True
def generate_po(self, outfile):
@@ -349,6 +382,7 @@ class NinjaBackend(backends.Backend):
elem.add_item('FILELIST', os.path.join(self.environment.get_source_dir(), input_file))
elem.add_item('OUTDIR', os.path.join(self.environment.get_source_dir(), subdir))
elem.write(outfile)
+ self.check_outputs(elem)
for l in languages:
infile = os.path.join(self.environment.get_source_dir(), subdir, l + '.po')
outfilename = os.path.join(subdir, l + '.gmo')
@@ -356,6 +390,7 @@ class NinjaBackend(backends.Backend):
lelem.add_item('INFILE', infile)
lelem.add_item('OUTFILE', outfilename)
lelem.write(outfile)
+ self.check_outputs(lelem)
def generate_coverage_rules(self, outfile):
(gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools()
@@ -372,6 +407,7 @@ class NinjaBackend(backends.Backend):
'-o', os.path.join(self.environment.get_log_dir(), 'coverage.txt')])
elem.add_item('DESC', 'Generating text coverage report.')
elem.write(outfile)
+ self.check_outputs(elem)
if lcov_exe and genhtml_exe:
added_rule = True
phony_elem = NinjaBuildElement('coverage-html', 'phony', 'coveragereport/index.html')
@@ -385,6 +421,7 @@ class NinjaBackend(backends.Backend):
'--legend', '--show-details', 'coverage.info']
elem.add_item('COMMAND', command)
elem.add_item('DESC', 'Generating HTML coverage report.')
+ self.check_outputs(elem)
elem.write(outfile)
if not added_rule:
mlog.log(mlog.red('Warning:'), 'coverage requested but neither gcovr nor lcov/genhtml found.')
@@ -412,6 +449,7 @@ class NinjaBackend(backends.Backend):
self.generate_custom_install_script(d)
self.generate_subdir_install(d)
elem.write(outfile)
+ self.check_outputs(elem)
ofile = open(install_data_file, 'wb')
pickle.dump(d, ofile)
@@ -517,6 +555,7 @@ class NinjaBackend(backends.Backend):
elem.add_item('DESC', 'Running test suite.')
elem.add_item('pool', 'console')
elem.write(outfile)
+ self.check_outputs(elem)
if valgrind:
velem = NinjaBuildElement('test-valgrind', 'CUSTOM_COMMAND', ['all', 'PHONY'])
@@ -524,6 +563,7 @@ class NinjaBackend(backends.Backend):
velem.add_item('DESC', 'Running test suite under Valgrind.')
velem.add_item('pool', 'console')
velem.write(outfile)
+ self.check_outputs(velem)
def generate_rules(self, outfile):
outfile.write('# Rules for compiling.\n\n')
@@ -600,6 +640,7 @@ class NinjaBackend(backends.Backend):
elem.add_dep([os.path.join(self.get_target_private_dir(target), i) for i in class_list])
elem.add_item('ARGS', commands)
elem.write(outfile)
+ self.check_outputs(elem)
def generate_cs_resource_tasks(self, target, outfile):
args = []
@@ -615,6 +656,7 @@ class NinjaBackend(backends.Backend):
elem.add_item('COMMAND', ['resgen', rel_sourcefile, ofilename])
elem.add_item('DESC', 'Compiling resource %s.' % rel_sourcefile)
elem.write(outfile)
+ self.check_outputs(elem)
deps.append(ofilename)
a = '-resource:' + ofilename
else:
@@ -654,6 +696,7 @@ class NinjaBackend(backends.Backend):
elem = NinjaBuildElement(outputs, 'cs_COMPILER', rel_srcs)
elem.add_dep(deps)
elem.add_item('ARGS', commands)
+ self.check_outputs(elem)
elem.write(outfile)
def generate_single_java_compile(self, src, target, compiler, outfile):
@@ -666,6 +709,7 @@ class NinjaBackend(backends.Backend):
element = NinjaBuildElement(rel_obj, compiler.get_language() + '_COMPILER', rel_src)
element.add_item('ARGS', args)
element.write(outfile)
+ self.check_outputs(element)
return plain_class_path
def generate_java_link(self, outfile):
@@ -689,6 +733,7 @@ class NinjaBackend(backends.Backend):
element = NinjaBuildElement(rel_vapi, valac.get_language() + '_COMPILER', rel_s)
element.add_item('ARGS', args)
element.write(outfile)
+ self.check_outputs(element)
fastvapis[s] = (vapibase, rel_vapi)
return fastvapis
@@ -751,6 +796,7 @@ class NinjaBackend(backends.Backend):
element.add_orderdep(vapi_order_deps)
element.add_dep(extra_dep_files)
element.write(outfile)
+ self.check_outputs(element)
return generated_c
def generate_rust_target(self, target, outfile):
@@ -790,6 +836,7 @@ class NinjaBackend(backends.Backend):
element.add_item('targetdep', depfile)
element.add_item('cratetype', cratetype)
element.write(outfile)
+ self.check_outputs(element)
def generate_static_link_rules(self, is_cross, outfile):
if self.build.has_language('java'):
@@ -832,11 +879,17 @@ class NinjaBackend(backends.Backend):
langname == 'rust' or langname == 'cs':
continue
crstr = ''
+ cross_args = []
if is_cross:
crstr = '_CROSS'
+ try:
+ cross_args = self.environment.cross_info.config['properties'][langname + '_link_args']
+ except KeyError:
+ pass
rule = 'rule %s%s_LINKER\n' % (langname, crstr)
- command = ' command = %s $ARGS %s $in $LINK_ARGS $aliasing\n' % \
+ command = ' command = %s %s $ARGS %s $in $LINK_ARGS $aliasing\n' % \
(' '.join(compiler.get_linker_exelist()),\
+ ' '.join(cross_args),\
' '.join(compiler.get_linker_output_args('$out')))
description = ' description = Linking target $out'
outfile.write(rule)
@@ -957,8 +1010,15 @@ rule FORTRAN_DEP_HACK
if d != '$out' and d != '$in':
d = qstr % d
quoted_depargs.append(d)
- command = " command = %s $ARGS %s %s %s $in\n" % \
+ cross_args = []
+ if is_cross:
+ try:
+ cross_args = self.environment.cross_info.config['properties'][langname + '_args']
+ except KeyError:
+ pass
+ command = " command = %s %s $ARGS %s %s %s $in\n" % \
(' '.join(compiler.get_exelist()),\
+ ' '.join(cross_args),
' '.join(quoted_depargs),\
' '.join(compiler.get_output_args('$out')),\
' '.join(compiler.get_compile_only_args()))
@@ -983,6 +1043,13 @@ rule FORTRAN_DEP_HACK
crstr = ''
rule = 'rule %s%s_PCH\n' % (langname, crstr)
depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE')
+ cross_args = []
+ if is_cross:
+ try:
+ cross_args = self.environment.cross_info.config['properties'][langname + '_args']
+ except KeyError:
+ pass
+
quoted_depargs = []
for d in depargs:
if d != '$out' and d != '$in':
@@ -992,8 +1059,9 @@ rule FORTRAN_DEP_HACK
output = ''
else:
output = ' '.join(compiler.get_output_args('$out'))
- command = " command = %s $ARGS %s %s %s $in\n" % \
+ command = " command = %s %s $ARGS %s %s %s $in\n" % \
(' '.join(compiler.get_exelist()),\
+ ' '.join(cross_args),\
' '.join(quoted_depargs),\
output,\
' '.join(compiler.get_compile_only_args()))
@@ -1086,6 +1154,7 @@ rule FORTRAN_DEP_HACK
elem.add_dep(self.get_target_filename(exe))
elem.add_item('COMMAND', cmdlist)
elem.write(outfile)
+ self.check_outputs(elem)
def scan_fortran_module_outputs(self, target):
compiler = None
@@ -1233,6 +1302,7 @@ rule FORTRAN_DEP_HACK
if srcfile == src:
depelem = NinjaBuildElement(modfile, 'FORTRAN_DEP_HACK', rel_obj)
depelem.write(outfile)
+ self.check_outputs(depelem)
commands += compiler.get_module_outdir_args(self.get_target_private_dir_abs(target))
element = NinjaBuildElement(rel_obj, compiler_name, rel_src)
@@ -1257,6 +1327,7 @@ rule FORTRAN_DEP_HACK
element.add_item('DEPFILE', dep_file)
element.add_item('ARGS', commands)
element.write(outfile)
+ self.check_outputs(element)
return rel_obj
# Fortran is a bit weird (again). When you link against a library, just compiling a source file
@@ -1322,6 +1393,7 @@ rule FORTRAN_DEP_HACK
elem.add_item('ARGS', commands)
elem.add_item('DEPFILE', dep)
elem.write(outfile)
+ self.check_outputs(elem)
return pch_objects
def generate_shsym(self, outfile, target):
@@ -1332,6 +1404,7 @@ rule FORTRAN_DEP_HACK
if self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler():
elem.add_item('CROSS', '--cross-host=' + self.environment.cross_info.config['host_machine']['system'])
elem.write(outfile)
+ self.check_outputs(elem)
def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]):
if isinstance(target, build.StaticLibrary):
@@ -1395,6 +1468,7 @@ rule FORTRAN_DEP_HACK
elem = NinjaBuildElement(outname, linker_rule, obj_list)
elem.add_dep(dep_targets)
elem.add_item('LINK_ARGS', commands)
+ self.check_outputs(elem)
return elem
def determine_rpath_dirs(self, target):
@@ -1411,19 +1485,19 @@ rule FORTRAN_DEP_HACK
return os.path.join(self.get_target_private_dir_abs(t), self.get_target_filename(t) + '.symbols')
return self.get_target_filename(t)
- def generate_shlib_aliases(self, target, outdir, outfile, elem):
+ def generate_shlib_aliases(self, target, outdir):
basename = target.get_filename()
aliases = target.get_aliaslist()
- aliascmd = []
- if shutil.which('ln'):
+ if not mesonlib.is_windows():
for alias in aliases:
- aliasfile = os.path.join(outdir, alias)
- cmd = ["&&", 'ln', '-s', '-f', basename, aliasfile]
- aliascmd += cmd
+ aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias)
+ try:
+ os.remove(aliasfile)
+ except Exception:
+ pass
+ os.symlink(basename, aliasfile)
else:
- mlog.log("Library versioning disabled because host does not support symlinks.")
- elem.add_item('aliasing', aliascmd)
- elem.write(outfile)
+ mlog.debug("Library versioning disabled because host does not support symlinks.")
def generate_gcov_clean(self, outfile):
gcno_elem = NinjaBuildElement('clean-gcno', 'CUSTOM_COMMAND', 'PHONY')
@@ -1432,6 +1506,7 @@ rule FORTRAN_DEP_HACK
gcno_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcno'])
gcno_elem.add_item('description', 'Deleting gcno files')
gcno_elem.write(outfile)
+ self.check_outputs(gcno_elem, gcno_elem)
gcda_elem = NinjaBuildElement('clean-gcda', 'CUSTOM_COMMAND', 'PHONY')
script_root = self.environment.get_script_dir()
@@ -1439,6 +1514,7 @@ rule FORTRAN_DEP_HACK
gcda_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcda'])
gcda_elem.add_item('description', 'Deleting gcda files')
gcda_elem.write(outfile)
+ self.check_outputs(gcda_elem)
def is_compilable_file(self, filename):
if filename.endswith('.cpp') or\
@@ -1464,6 +1540,7 @@ rule FORTRAN_DEP_HACK
infilename = os.path.join(self.build_to_src, target.get_source_subdir(), src)
elem = NinjaBuildElement(outfilename, rule.name, infilename)
elem.write(outfile)
+ self.check_outputs(elem)
if self.is_compilable_file(outfilename):
src_deps.append(outfilename)
else:
@@ -1476,6 +1553,7 @@ rule FORTRAN_DEP_HACK
elem = NinjaBuildElement('all', 'phony', targetlist)
elem.write(outfile)
+ self.check_outputs(elem)
default = 'default all\n\n'
outfile.write(default)
@@ -1491,6 +1569,7 @@ rule FORTRAN_DEP_HACK
elem.add_dep('clean-gcda')
elem.add_dep('clean-gcno')
elem.write(outfile)
+ self.check_outputs(elem)
deps = [os.path.join(self.build_to_src, df) \
for df in self.interpreter.get_build_def_files()]
@@ -1510,3 +1589,4 @@ rule FORTRAN_DEP_HACK
elem = NinjaBuildElement(deps, 'phony', '')
elem.write(outfile)
+ self.check_outputs(elem)