diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 1f1c3ca01..4d0af4bf0 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -551,6 +551,11 @@ class Backend(): else: if '@OUTDIR@' in i: i = i.replace('@OUTDIR@', outdir) + elif '@DEPFILE@' in i: + if target.depfile is None: + raise MesonException('Custom target %s has @DEPFILE@ but no depfile keyword argument.' % target.name) + dfilename = os.path.join(self.get_target_private_dir(target), target.depfile) + i = i.replace('@DEPFILE@', dfilename) elif '@PRIVATE_OUTDIR_' in i: match = re.search('@PRIVATE_OUTDIR_(ABS_)?([-a-zA-Z0-9.@:]*)@', i) source = match.group(0) @@ -558,7 +563,6 @@ class Backend(): lead_dir = '' else: lead_dir = self.environment.get_build_dir() - target_id = match.group(2) i = i.replace(source, os.path.join(lead_dir, outdir)) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 15f298b1e..eacfda480 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -366,7 +366,11 @@ int dummy; desc = 'Generating {0} with a {1} command.' if target.build_always: deps.append('PHONY') - elem = NinjaBuildElement(self.all_outputs, ofilenames, 'CUSTOM_COMMAND', srcs) + if target.depfile is None: + rulename = 'CUSTOM_COMMAND' + else: + rulename = 'CUSTOM_COMMAND_DEP' + elem = NinjaBuildElement(self.all_outputs, ofilenames, rulename, srcs) for i in target.depend_files: if isinstance(i, mesonlib.File): deps.append(i.rel_to_builddir(self.build_to_src)) @@ -394,6 +398,11 @@ int dummy; else: cmd_type = 'custom' + if target.depfile is not None: + rel_dfile = os.path.join(self.get_target_private_dir(target), target.depfile) + abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_private_dir(target)) + os.makedirs(abs_pdir, exist_ok=True) + elem.add_item('DEPFILE', rel_dfile) elem.add_item('COMMAND', cmd) elem.add_item('description', desc.format(target.name, cmd_type)) elem.write(outfile) @@ -636,7 +645,6 @@ int dummy; velem.write(outfile) # And then benchmarks. - benchmark_script = os.path.join(script_root, 'meson_benchmark.py') cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'benchmark', benchmark_data] elem = NinjaBuildElement(self.all_outputs, 'benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd) @@ -657,6 +665,14 @@ int dummy; outfile.write(' command = $COMMAND\n') outfile.write(' description = $DESC\n') outfile.write(' restat = 1\n\n') + # Ninja errors out if you have deps = gcc but no depfile, so we must + # have two rules for custom commands. + outfile.write('rule CUSTOM_COMMAND_DEP\n') + outfile.write(' command = $COMMAND\n') + outfile.write(' description = $DESC\n') + outfile.write(' deps = gcc\n') + outfile.write(' depfile = $DEPFILE\n') + outfile.write(' restat = 1\n\n') outfile.write('rule REGENERATE_BUILD\n') c = (quote_char + ninja_quote(sys.executable) + quote_char, quote_char + ninja_quote(self.environment.get_build_command()) + quote_char, diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 0922dfb2e..c92c99f59 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -931,6 +931,7 @@ class CustomTarget: 'build_always' : True, 'depends' : True, 'depend_files' : True, + 'depfile' : True, } def __init__(self, name, subdir, kwargs): @@ -939,6 +940,7 @@ class CustomTarget: self.dependencies = [] self.extra_depends = [] self.depend_files = [] # Files that this target depends on but are not on the command line. + self.depfile = None self.process_kwargs(kwargs) self.extra_files = [] self.install_rpath = '' @@ -983,6 +985,13 @@ class CustomTarget: raise InvalidArguments('Output must not contain a path segment.') if 'command' not in kwargs: raise InvalidArguments('Missing keyword argument "command".') + if 'depfile' in kwargs: + depfile = kwargs['depfile'] + if not isinstance(depfile, str): + raise InvalidArguments('Depfile must be a string.') + if os.path.split(depfile)[1] != depfile: + raise InvalidArguments('Depfile must be a plain filename without a subdirectory.') + self.depfile = depfile cmd = kwargs['command'] if not(isinstance(cmd, list)): cmd = [cmd] diff --git a/test cases/common/56 custom target/depfile/dep.py b/test cases/common/56 custom target/depfile/dep.py new file mode 100755 index 000000000..3a772ec84 --- /dev/null +++ b/test cases/common/56 custom target/depfile/dep.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import sys, os +from glob import glob + +_, srcdir, depfile, output = sys.argv + +depfiles = glob(os.path.join(srcdir, '*')) + +quoted_depfiles = [x.replace(' ', '\ ') for x in depfiles] + +open(output, 'w').write('I am the result of globbing.') +open(depfile, 'w').write('%s: %s\n' % (output, ' '.join(quoted_depfiles))) diff --git a/test cases/common/56 custom target/depfile/meson.build b/test cases/common/56 custom target/depfile/meson.build new file mode 100644 index 000000000..46bca7405 --- /dev/null +++ b/test cases/common/56 custom target/depfile/meson.build @@ -0,0 +1,7 @@ + + +mytarget = custom_target('depfile', + output : 'dep.dat', + depfile : 'dep.dat.d', + command : [find_program('dep.py'), meson.current_source_dir(), '@DEPFILE@', '@OUTPUT@'], +) diff --git a/test cases/common/56 custom target/meson.build b/test cases/common/56 custom target/meson.build index db81824ef..e216bae6c 100644 --- a/test cases/common/56 custom target/meson.build +++ b/test cases/common/56 custom target/meson.build @@ -13,3 +13,5 @@ command : [python, comp, '@INPUT@', '@OUTPUT@'], install : true, install_dir : 'subdir' ) + +subdir('depfile')