Skip to content

Commit

Permalink
configure_file(): Allow multiple inputs in command mode
Browse files Browse the repository at this point in the history
Closes: #5893
  • Loading branch information
xclaesse committed Sep 6, 2019
1 parent 7b9c348 commit 308b127
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 42 deletions.
5 changes: 4 additions & 1 deletion docs/markdown/Reference-manual.md
Expand Up @@ -251,7 +251,10 @@ These are all the supported keyword arguments:
`output`. Available since v0.41.0.
- `command` as explained above, if specified, Meson does not create
the file itself but rather runs the specified command, which allows
you to do fully custom file generation.
you to do fully custom file generation. Since *0.52.0* the command can contain
file objects and more than one file can be passed to the `input` keyword
argument, see [`custom_target()`](#custom_target) for details about string
substitutions.
- `copy` *(added 0.47.0)* as explained above, if specified Meson only
copies the file from input to output.
- `format` *(added 0.46.0)* the format of defines. It defaults to `meson`, and so substitutes
Expand Down
73 changes: 32 additions & 41 deletions mesonbuild/interpreter.py
Expand Up @@ -3673,30 +3673,20 @@ def func_configure_file(self, node, args, kwargs):
raise InterpreterException('"format" possible values are "c" or "nasm".')

# Validate input
inputfile = None
ifile_abs = None
if 'input' in kwargs:
inputfile = kwargs['input']
if isinstance(inputfile, list):
if len(inputfile) != 1:
m = "Keyword argument 'input' requires exactly one file"
raise InterpreterException(m)
inputfile = inputfile[0]
if not isinstance(inputfile, (str, mesonlib.File)):
raise InterpreterException('Input must be a string or a file')
if isinstance(inputfile, str):
inputfile = mesonlib.File.from_source_file(self.environment.source_dir,
self.subdir, inputfile)
ifile_abs = inputfile.absolute_path(self.environment.source_dir,
self.environment.build_dir)
elif 'command' in kwargs and '@INPUT@' in kwargs['command']:
raise InterpreterException('@INPUT@ used as command argument, but no input file specified.')
inputs = self.source_strings_to_files(extract_as_list(kwargs, 'input'))
inputs_abs = []
for f in inputs:
if isinstance(f, mesonlib.File):
inputs_abs.append(f.absolute_path(self.environment.source_dir,
self.environment.build_dir))
else:
raise InterpreterException('Inputs can only be strings or file objects')
# Validate output
output = kwargs['output']
if not isinstance(output, str):
raise InterpreterException('Output file name must be a string')
if ifile_abs:
values = mesonlib.get_filenames_templates_dict([ifile_abs], None)
if inputs_abs:
values = mesonlib.get_filenames_templates_dict(inputs_abs, None)
outputs = mesonlib.substitute_values([output], values)
output = outputs[0]
ofile_rpath = os.path.join(self.subdir, output)
Expand All @@ -3722,20 +3712,22 @@ def func_configure_file(self, node, args, kwargs):
elif not isinstance(conf, ConfigurationDataHolder):
raise InterpreterException('Argument "configuration" is not of type configuration_data')
mlog.log('Configuring', mlog.bold(output), 'using configuration')
if inputfile is not None:
if len(inputs) > 1:
raise InterpreterException('At most one input file can given in configuration mode')
if inputs:
os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
file_encoding = kwargs.setdefault('encoding', 'utf-8')
missing_variables, confdata_useless = \
mesonlib.do_conf_file(ifile_abs, ofile_abs, conf.held_object,
mesonlib.do_conf_file(inputs_abs[0], ofile_abs, conf.held_object,
fmt, file_encoding)
if missing_variables:
var_list = ", ".join(map(repr, sorted(missing_variables)))
mlog.warning(
"The variable(s) %s in the input file '%s' are not "
"present in the given configuration data." % (
var_list, inputfile), location=node)
var_list, inputs[0]), location=node)
if confdata_useless:
ifbase = os.path.basename(ifile_abs)
ifbase = os.path.basename(inputs_abs[0])
mlog.warning('Got an empty configuration_data() object and found no '
'substitutions in the input file {!r}. If you want to '
'copy a file to the build dir, use the \'copy:\' keyword '
Expand All @@ -3747,10 +3739,7 @@ def func_configure_file(self, node, args, kwargs):
# We use absolute paths for input and output here because the cwd
# that the command is run from is 'unspecified', so it could change.
# Currently it's builddir/subdir for in_builddir else srcdir/subdir.
if ifile_abs:
values = mesonlib.get_filenames_templates_dict([ifile_abs], [ofile_abs])
else:
values = mesonlib.get_filenames_templates_dict(None, [ofile_abs])
values = mesonlib.get_filenames_templates_dict(inputs_abs, [ofile_abs])
# Substitute @INPUT@, @OUTPUT@, etc here.
cmd = mesonlib.substitute_values(kwargs['command'], values)
mlog.log('Configuring', mlog.bold(output), 'with command')
Expand All @@ -3763,26 +3752,28 @@ def func_configure_file(self, node, args, kwargs):
file_encoding = kwargs.setdefault('encoding', 'utf-8')
with open(dst_tmp, 'w', encoding=file_encoding) as f:
f.writelines(res.stdout)
if ifile_abs:
shutil.copymode(ifile_abs, dst_tmp)
if inputs_abs:
shutil.copymode(inputs_abs[0], dst_tmp)
mesonlib.replace_if_different(ofile_abs, dst_tmp)
elif 'copy' in kwargs:
if len(inputs_abs) != 1:
raise InterpreterException('Exactly one input file must be given in copy mode')
os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
shutil.copyfile(ifile_abs, ofile_abs)
shutil.copymode(ifile_abs, ofile_abs)
shutil.copyfile(inputs_abs[0], ofile_abs)
shutil.copymode(inputs_abs[0], ofile_abs)
else:
# Not reachable
raise AssertionError
# If the input is a source file, add it to the list of files that we
# need to reconfigure on when they change. FIXME: Do the same for
# files() objects in the command: kwarg.
if inputfile and not inputfile.is_built:
# Normalize the path of the conffile (relative to the
# source root) to avoid duplicates. This is especially
# important to convert '/' to '\' on Windows
conffile = os.path.normpath(inputfile.relative_name())
if conffile not in self.build_def_files:
self.build_def_files.append(conffile)
# need to reconfigure on when they change.
for f in chain(inputs, kwargs.get('command', [])):
if isinstance(f, mesonlib.File) and not f.is_built:
# Normalize the path of the conffile (relative to the
# source root) to avoid duplicates. This is especially
# important to convert '/' to '\' on Windows
conffile = os.path.normpath(f.relative_name())
if conffile not in self.build_def_files:
self.build_def_files.append(conffile)
# Install file if requested, we check for the empty string
# for backwards compatibility. That was the behaviour before
# 0.45.0 so preserve it.
Expand Down
6 changes: 6 additions & 0 deletions test cases/common/14 configure file/meson.build
Expand Up @@ -283,3 +283,9 @@ configure_file(output : 'config9b.h',
)

test('test9', executable('prog9', 'prog9.c'))

check_inputs = find_program('check_inputs.py')
configure_file(output : 'check_inputs.txt',
input : ['prog.c', files('prog2.c', 'prog4.c')],
command : [check_inputs, '@OUTPUT@', '@INPUT0@', '@INPUT@', files('prog5.c')]
)

0 comments on commit 308b127

Please sign in to comment.