configure_file: Add a new action 'copy'

This will copy the file to the build directory without trying to read
it or substitute values into it.

Also do this optimization if the configuration_data() object passed to
the `configuration:` kwarg is empty, and print a warning about it.

See also: https://github.com/mesonbuild/meson/issues/1542
pull/3383/head
Nirbheek Chauhan 7 years ago
parent b3f74b9c3a
commit a00433fdbc
  1. 40
      mesonbuild/interpreter.py
  2. 2
      run_unittests.py
  3. 4
      test cases/common/114 multiple dir configure file/meson.build
  4. 2
      test cases/common/114 multiple dir configure file/subdir/meson.build
  5. 10
      test cases/common/16 configure file/check_file.py
  6. BIN
      test cases/common/16 configure file/invalid-utf8.bin.in
  7. 33
      test cases/common/16 configure file/meson.build
  8. 2
      test cases/common/27 library versions/subdir/meson.build
  9. 2
      test cases/common/76 configure file in custom target/src/meson.build
  10. 2
      test cases/failing/27 output subdir/meson.build
  11. 1
      test cases/unit/21 warning location/meson.build

@ -1665,7 +1665,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'}, 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'},
'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'}, 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'},
'build_target': known_build_target_kwargs, 'build_target': known_build_target_kwargs,
'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install', 'format'}, 'configure_file': {'input', 'output', 'configuration', 'command', 'copy', 'install_dir', 'capture', 'install', 'format'},
'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'}, 'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'},
'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'}, 'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'},
'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'}, 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'},
@ -3126,10 +3126,19 @@ root and issuing %s.
raise InterpreterException("configure_file takes only keyword arguments.") raise InterpreterException("configure_file takes only keyword arguments.")
if 'output' not in kwargs: if 'output' not in kwargs:
raise InterpreterException('Required keyword argument "output" not defined.') raise InterpreterException('Required keyword argument "output" not defined.')
if 'configuration' in kwargs and 'command' in kwargs: actions = set(['configuration', 'command', 'copy']).intersection(kwargs.keys())
raise InterpreterException('Must not specify both "configuration" ' if len(actions) == 0:
'and "command" keyword arguments since ' raise InterpreterException('Must specify an action with one of these '
'they are mutually exclusive.') 'keyword arguments: \'configuration\', '
'\'command\', or \'copy\'.')
elif len(actions) == 2:
raise InterpreterException('Must not specify both {!r} and {!r} '
'keyword arguments since they are '
'mutually exclusive.'.format(*actions))
elif len(actions) == 3:
raise InterpreterException('Must specify one of {!r}, {!r}, and '
'{!r} keyword arguments since they are '
'mutually exclusive.'.format(*actions))
if 'capture' in kwargs: if 'capture' in kwargs:
if not isinstance(kwargs['capture'], bool): if not isinstance(kwargs['capture'], bool):
raise InterpreterException('"capture" keyword must be a boolean.') raise InterpreterException('"capture" keyword must be a boolean.')
@ -3177,6 +3186,20 @@ root and issuing %s.
raise InterpreterException('Output file name must not contain a subdirectory.') raise InterpreterException('Output file name must not contain a subdirectory.')
(ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, output)) (ofile_path, ofile_fname) = os.path.split(os.path.join(self.subdir, output))
ofile_abs = os.path.join(self.environment.build_dir, ofile_path, ofile_fname) ofile_abs = os.path.join(self.environment.build_dir, ofile_path, ofile_fname)
# Optimize copies by not doing substitution if there's nothing to
# substitute, and warn about this legacy hack
if 'configuration' in kwargs:
conf = kwargs['configuration']
if not isinstance(conf, ConfigurationDataHolder):
raise InterpreterException('Argument "configuration" must be of type configuration_data')
if ifile_abs and not conf.keys():
del kwargs['configuration']
kwargs['copy'] = True
mlog.warning('Got an empty configuration_data() object: '
'optimizing copy automatically; if you want to '
'copy a file to the build dir, use the \'copy:\' '
'keyword argument added in 0.46.0', location=node)
# Perform the appropriate action
if 'configuration' in kwargs: if 'configuration' in kwargs:
conf = kwargs['configuration'] conf = kwargs['configuration']
if not isinstance(conf, ConfigurationDataHolder): if not isinstance(conf, ConfigurationDataHolder):
@ -3217,8 +3240,13 @@ root and issuing %s.
if ifile_abs: if ifile_abs:
shutil.copymode(ifile_abs, dst_tmp) shutil.copymode(ifile_abs, dst_tmp)
mesonlib.replace_if_different(ofile_abs, dst_tmp) mesonlib.replace_if_different(ofile_abs, dst_tmp)
elif 'copy' in kwargs:
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)
else: else:
raise InterpreterException('Configure_file must have either "configuration" or "command".') # Not reachable
raise AssertionError
# If the input is a source file, add it to the list of files that we # 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 # need to reconfigure on when they change. FIXME: Do the same for
# files() objects in the command: kwarg. # files() objects in the command: kwarg.

@ -1827,7 +1827,7 @@ int main(int argc, char **argv) {
r'meson.build:6: WARNING: a warning of some sort', r'meson.build:6: WARNING: a warning of some sort',
r'sub' + os.path.sep + r'meson.build:4: WARNING: subdir warning', r'sub' + os.path.sep + r'meson.build:4: WARNING: subdir warning',
r'meson.build:7: WARNING: Module unstable-simd has no backwards or forwards compatibility and might not exist in future releases.', r'meson.build:7: WARNING: Module unstable-simd has no backwards or forwards compatibility and might not exist in future releases.',
r"meson.build:10: WARNING: The variable(s) 'MISSING' in the input file conf.in are not present in the given configuration data.", r"meson.build:11: WARNING: The variable(s) 'MISSING' in the input file conf.in are not present in the given configuration data.",
r'meson.build:1: WARNING: Passed invalid keyword argument "invalid".', r'meson.build:1: WARNING: Passed invalid keyword argument "invalid".',
]: ]:
self.assertRegex(out, re.escape(expected)) self.assertRegex(out, re.escape(expected))

@ -4,8 +4,8 @@ subdir('subdir')
configure_file(input : 'subdir/someinput.in', configure_file(input : 'subdir/someinput.in',
output : 'outputhere', output : 'outputhere',
configuration : configuration_data()) copy: true)
configure_file(input : cfile1, configure_file(input : cfile1,
output : '@BASENAME@', output : '@BASENAME@',
configuration : configuration_data()) copy: true)

@ -1,7 +1,7 @@
configure_file(input : 'someinput.in', configure_file(input : 'someinput.in',
output : 'outputsubdir', output : 'outputsubdir',
install : false, install : false,
configuration : configuration_data()) copy: true)
py3 = import('python3').find_python() py3 = import('python3').find_python()

@ -3,4 +3,12 @@
import os import os
import sys import sys
assert(os.path.exists(sys.argv[1])) if len(sys.argv) == 2:
assert(os.path.exists(sys.argv[1]))
elif len(sys.argv) == 3:
f1 = open(sys.argv[1], 'rb').read()
f2 = open(sys.argv[2], 'rb').read()
if f1 != f2:
raise RuntimeError('{!r} != {!r}'.format(f1, f2))
else:
raise AssertionError

@ -35,21 +35,27 @@ ofile = '@0@/config2.h'.format(meson.current_build_dir())
check_file = find_program('check_file.py') check_file = find_program('check_file.py')
# Configure in source root with command and absolute paths # Configure in source root with command and absolute paths
configure_file(input : 'dummy.dat', outf = configure_file(input : 'dummy.dat',
output : 'config2.h', output : 'config2.h',
command : [genprog, scriptfile, ifile, ofile], command : [genprog, scriptfile, ifile, ofile],
install_dir : 'share/appdir') install_dir : 'share/appdir')
run_command(check_file, join_paths(meson.current_build_dir(), 'config2.h')) ret = run_command(check_file, outf)
if ret.returncode() != 0
error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
endif
# Same again as before, but an input file should not be required in # Same again as before, but an input file should not be required in
# this case where we use a command/script to generate the output file. # this case where we use a command/script to generate the output file.
genscript2b = '@0@/generator-without-input-file.py'.format(meson.current_source_dir()) genscript2b = '@0@/generator-without-input-file.py'.format(meson.current_source_dir())
ofile2b = '@0@/config2b.h'.format(meson.current_build_dir()) ofile2b = '@0@/config2b.h'.format(meson.current_build_dir())
configure_file( outf = configure_file(
output : 'config2b.h', output : 'config2b.h',
command : [genprog, genscript2b, ofile2b], command : [genprog, genscript2b, ofile2b],
install_dir : 'share/appdir') install_dir : 'share/appdir')
run_command(check_file, join_paths(meson.current_build_dir(), 'config2b.h')) ret = run_command(check_file, outf)
if ret.returncode() != 0
error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
endif
found_script = find_program('generator.py') found_script = find_program('generator.py')
# More configure_file tests in here # More configure_file tests in here
@ -149,3 +155,22 @@ configure_file(
configuration : conf7 configuration : conf7
) )
test('test7', executable('prog7', 'prog7.c')) test('test7', executable('prog7', 'prog7.c'))
# Test empty configuration data object on invalid utf8 file
inf = 'invalid-utf8.bin.in'
outf = configure_file(input : inf,
output : 'invalid-utf8.bin',
configuration : configuration_data())
ret = run_command(check_file, inf, outf)
if ret.returncode() != 0
error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
endif
# Test copy of a binary file
outf = configure_file(input : inf,
output : 'somebinary.bin',
copy : true)
ret = run_command(check_file, inf, outf)
if ret.returncode() != 0
error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
endif

@ -3,6 +3,6 @@
# because there is no structure in the build dir. # because there is no structure in the build dir.
genlib = configure_file(input : '../lib.c', genlib = configure_file(input : '../lib.c',
output : 'genlib.c', output : 'genlib.c',
configuration : configuration_data()) copy: true)
shared_library('genlib', genlib, shared_library('genlib', genlib,
install : false) install : false)

@ -12,7 +12,7 @@ endif
compiler = configure_file(input : 'mycompiler.py', compiler = configure_file(input : 'mycompiler.py',
output : 'mycompiler2.py', output : 'mycompiler2.py',
configuration : configuration_data()) copy: true)
custom_target('thing2', custom_target('thing2',
output : 'final2.dat', output : 'final2.dat',

@ -2,4 +2,4 @@ project('outdir path', 'c')
configure_file(input : 'foo.in', configure_file(input : 'foo.in',
output : 'subdir/foo', output : 'subdir/foo',
configuration : configuration_data()) copy: true)

@ -7,4 +7,5 @@ warning('a warning of some sort')
import('unstable-simd') import('unstable-simd')
conf_data = configuration_data() conf_data = configuration_data()
conf_data.set('NOTMISSING', 1)
configure_file(input: 'conf.in' , output: 'conf', configuration: conf_data) configure_file(input: 'conf.in' , output: 'conf', configuration: conf_data)

Loading…
Cancel
Save