add support for cmakedefine in configure_file()

The added format argument for configure_file allows to specify the kind of
file that is treated. It defaults to 'meson', but can also have the 'cmake'
or 'cmake@' value to treat config.h.in files in the cmake format with #cmakedefine
statements.
pull/2332/merge
David Fort 7 years ago committed by Jussi Pakkanen
parent aed11affd3
commit 6dea177774
  1. 7
      docs/markdown/Reference-manual.md
  2. 14
      mesonbuild/interpreter.py
  3. 29
      mesonbuild/mesonlib.py
  4. 16
      test cases/common/16 configure file/config7.h.in
  5. 12
      test cases/common/16 configure file/meson.build
  6. 10
      test cases/common/16 configure file/prog7.c

@ -186,7 +186,12 @@ 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.
- `format` *(added 0.46.0)* the format of defines. It defaults to `meson`, and so substitutes
`#mesondefine` statements and variables surrounded by `@` characters, you can also use `cmake`
to replace `#cmakedefine` statements and variables with the `${variable}` syntax. Finally you can use
`cmake@` in which case substitutions will apply on `#cmakedefine` statements and variables with
the `@variable@` syntax.
- `input` the input file name. If it's not specified in configuration
mode, all the variables in the `configuration:` object (see above)
are written to the `output:` file.

@ -1463,7 +1463,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'},
'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'},
'build_target': known_build_target_kwargs,
'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'},
'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install', 'format'},
'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'},
'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'},
@ -2885,6 +2885,16 @@ root and issuing %s.
if 'command' not in kwargs:
raise InterpreterException('"capture" keyword requires "command" keyword.')
if 'format' in kwargs:
format = kwargs['format']
if not isinstance(format, str):
raise InterpreterException('"format" keyword must be a string.')
else:
format = 'meson'
if format not in ('meson', 'cmake', 'cmake@'):
raise InterpreterException('"format" possible values are "meson", "cmake" or "cmake@".')
# Validate input
inputfile = None
ifile_abs = None
@ -2924,7 +2934,7 @@ root and issuing %s.
if inputfile is not None:
os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
missing_variables = mesonlib.do_conf_file(ifile_abs, ofile_abs,
conf.held_object)
conf.held_object, format)
if missing_variables:
var_list = ", ".join(map(repr, sorted(missing_variables)))
mlog.warning(

@ -541,8 +541,13 @@ def has_path_sep(name, sep='/\\'):
return True
return False
def do_replacement(regex, line, confdata):
def do_replacement(regex, line, format, confdata):
missing_variables = set()
start_tag = '@'
backslash_tag = '\\@'
if format == 'cmake':
start_tag = '${'
backslash_tag = '\\${'
def variable_replace(match):
# Pairs of escape characters before '@' or '\@'
@ -550,8 +555,8 @@ def do_replacement(regex, line, confdata):
num_escapes = match.end(0) - match.start(0)
return '\\' * (num_escapes // 2)
# Single escape character and '@'
elif match.group(0) == '\\@':
return '@'
elif match.group(0) == backslash_tag:
return start_tag
# Template variable to be replaced
else:
varname = match.group(1)
@ -591,7 +596,7 @@ def do_mesondefine(line, confdata):
raise MesonException('#mesondefine argument "%s" is of unknown type.' % varname)
def do_conf_file(src, dst, confdata):
def do_conf_file(src, dst, confdata, format):
try:
with open(src, encoding='utf-8') as f:
data = f.readlines()
@ -599,14 +604,24 @@ def do_conf_file(src, dst, confdata):
raise MesonException('Could not read input file %s: %s' % (src, str(e)))
# Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define
# Also allow escaping '@' with '\@'
regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@')
if format in ['meson', 'cmake@']:
regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@')
elif format == 'cmake':
regex = re.compile(r'(?:\\\\)+(?=\\?\$)|\\\${|\${([-a-zA-Z0-9_]+)}')
else:
raise MesonException('Format "{}" not handled'.format(format))
search_token = '#mesondefine'
if format != 'meson':
search_token = '#cmakedefine'
result = []
missing_variables = set()
for line in data:
if line.startswith('#mesondefine'):
if line.startswith(search_token):
line = do_mesondefine(line, confdata)
else:
line, missing = do_replacement(regex, line, confdata)
line, missing = do_replacement(regex, line, format, confdata)
missing_variables.update(missing)
result.append(line)
dst_tmp = dst + '~'

@ -0,0 +1,16 @@
/* No escape */
#define MESSAGE1 "${var1}"
/* Single escape means no replace */
#define MESSAGE2 "\${var1}"
/* Replace pairs of escapes before '@' or '\@' with escape characters
* (note we have to double number of pairs due to C string escaping)
*/
#define MESSAGE3 "\\\\${var1}"
/* Pairs of escapes and then single escape to avoid replace */
#define MESSAGE4 "\\\\\${var1}"
/* Check escape character outside variables */
#define MESSAGE5 "\\ ${ \${ \\\\${ \\\\\${"

@ -137,3 +137,15 @@ cfile = configure_file(input : 'config.h.in',
output : 'do_not_get_installed.h',
install_dir : '',
configuration : conf)
# Test escaping with cmake format
conf7 = configuration_data()
conf7.set('var1', 'foo')
conf7.set('var2', 'bar')
configure_file(
input : 'config7.h.in',
output : '@BASENAME@',
format : 'cmake',
configuration : conf7
)
test('test7', executable('prog7', 'prog7.c'))

@ -0,0 +1,10 @@
#include <string.h>
#include <config7.h>
int main(int argc, char **argv) {
return strcmp(MESSAGE1, "foo")
|| strcmp(MESSAGE2, "${var1}")
|| strcmp(MESSAGE3, "\\foo")
|| strcmp(MESSAGE4, "\\${var1}")
|| strcmp(MESSAGE5, "\\ ${ ${ \\${ \\${");
}
Loading…
Cancel
Save