diff options
author | Marc-André Lureau <marcandre.lureau@redhat.com> | 2019-07-27 17:25:38 +0400 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2019-09-30 22:17:50 +0300 |
commit | 01569fee2e8130b3ac54659c119e73180d3dafee (patch) | |
tree | 7fc3b94f3aa921e548455caca7f118ad42b0f677 /mesonbuild | |
parent | e32b0f8fbbd16b6cdd795a9a596eb56813620055 (diff) | |
download | meson-01569fee2e8130b3ac54659c119e73180d3dafee.zip meson-01569fee2e8130b3ac54659c119e73180d3dafee.tar.gz meson-01569fee2e8130b3ac54659c119e73180d3dafee.tar.bz2 |
Add depfile to configure_file()
In qemu, minikconf generates a depfile that meson could use to
automatically reconfigure on dependency change.
Note: someone clever can perhaps find a way to express this with a
ninja rule & depfile=. I didn't manage, so I wrote a simple depfile
parser.
Diffstat (limited to 'mesonbuild')
-rw-r--r-- | mesonbuild/depfile.py | 85 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 24 |
2 files changed, 109 insertions, 0 deletions
diff --git a/mesonbuild/depfile.py b/mesonbuild/depfile.py new file mode 100644 index 0000000..7a896cd --- /dev/null +++ b/mesonbuild/depfile.py @@ -0,0 +1,85 @@ +# Copyright 2019 Red Hat, Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import collections + +def parse(lines): + rules = [] + targets = [] + deps = [] + in_deps = False + out = '' + for line in lines: + if not line.endswith('\n'): + line += '\n' + escape = None + for c in line: + if escape: + if escape == '$' and c != '$': + out += '$' + if escape == '\\' and c == '\n': + continue + out += c + escape = None + continue + if c == '\\' or c == '$': + escape = c + continue + elif c in (' ', '\n'): + if out != '': + if in_deps: + deps.append(out) + else: + targets.append(out) + out = '' + if c == '\n': + rules.append((targets, deps)) + targets = [] + deps = [] + in_deps = False + continue + elif c == ':': + targets.append(out) + out = '' + in_deps = True + continue + out += c + return rules + +Target = collections.namedtuple('Target', ['deps']) + +class DepFile: + def __init__(self, lines): + rules = parse(lines) + depfile = {} + for (targets, deps) in rules: + for target in targets: + t = depfile.setdefault(target, Target(deps=set())) + for dep in deps: + t.deps.add(dep) + self.depfile = depfile + + def get_all_dependencies(self, target, visited=None): + deps = set() + if not visited: + visited = set() + if target in visited: + return set() + visited.add(target) + target = self.depfile.get(target) + if not target: + return set() + deps.update(target.deps) + for dep in target.deps: + deps.update(self.get_all_dependencies(dep, visited)) + return deps diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 2a77eaa..e52ceae 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -24,6 +24,7 @@ from . import mesonlib from .mesonlib import FileMode, MachineChoice, Popen_safe, listify, extract_as_list, has_path_sep from .dependencies import ExternalProgram from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException +from .depfile import DepFile from .interpreterbase import InterpreterBase from .interpreterbase import check_stringlist, flatten, noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest @@ -1960,6 +1961,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'}, 'configuration', 'command', 'copy', + 'depfile', 'install_dir', 'install_mode', 'capture', @@ -3603,6 +3605,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self @FeatureNewKwargs('configure_file', '0.46.0', ['format']) @FeatureNewKwargs('configure_file', '0.41.0', ['capture']) @FeatureNewKwargs('configure_file', '0.50.0', ['install']) + @FeatureNewKwargs('configure_file', '0.52.0', ['depfile']) @permittedKwargs(permitted_kwargs['configure_file']) def func_configure_file(self, node, args, kwargs): if len(args) > 0: @@ -3648,6 +3651,13 @@ This will become a hard error in the future.''' % kwargs['input'], location=self if output_format not in ('c', 'nasm'): raise InterpreterException('"format" possible values are "c" or "nasm".') + if 'depfile' in kwargs: + depfile = kwargs['depfile'] + if not isinstance(depfile, str): + raise InterpreterException('depfile file name must be a string') + else: + depfile = None + # Validate input inputs = self.source_strings_to_files(extract_as_list(kwargs, 'input')) inputs_abs = [] @@ -3665,6 +3675,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self values = mesonlib.get_filenames_templates_dict(inputs_abs, None) outputs = mesonlib.substitute_values([output], values) output = outputs[0] + if depfile: + depfile = mesonlib.substitute_values([depfile], values)[0] ofile_rpath = os.path.join(self.subdir, output) if ofile_rpath in self.configure_file_outputs: mesonbuildfile = os.path.join(self.subdir, 'meson.build') @@ -3716,6 +3728,9 @@ This will become a hard error in the future.''' % kwargs['input'], location=self # that the command is run from is 'unspecified', so it could change. # Currently it's builddir/subdir for in_builddir else srcdir/subdir. values = mesonlib.get_filenames_templates_dict(inputs_abs, [ofile_abs]) + if depfile: + depfile = os.path.join(self.environment.get_scratch_dir(), depfile) + values['@DEPFILE@'] = depfile # Substitute @INPUT@, @OUTPUT@, etc here. cmd = mesonlib.substitute_values(kwargs['command'], values) mlog.log('Configuring', mlog.bold(output), 'with command') @@ -3731,6 +3746,15 @@ This will become a hard error in the future.''' % kwargs['input'], location=self if inputs_abs: shutil.copymode(inputs_abs[0], dst_tmp) mesonlib.replace_if_different(ofile_abs, dst_tmp) + if depfile: + mlog.log('Reading depfile:', mlog.bold(depfile)) + with open(depfile, 'r') as f: + df = DepFile(f.readlines()) + deps = df.get_all_dependencies(ofile_fname) + for dep in deps: + if dep not in self.build_def_files: + self.build_def_files.append(dep) + elif 'copy' in kwargs: if len(inputs_abs) != 1: raise InterpreterException('Exactly one input file must be given in copy mode') |