aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@redhat.com>2019-07-27 17:25:38 +0400
committerJussi Pakkanen <jpakkane@gmail.com>2019-09-30 22:17:50 +0300
commit01569fee2e8130b3ac54659c119e73180d3dafee (patch)
tree7fc3b94f3aa921e548455caca7f118ad42b0f677 /mesonbuild
parente32b0f8fbbd16b6cdd795a9a596eb56813620055 (diff)
downloadmeson-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.py85
-rw-r--r--mesonbuild/interpreter.py24
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')