aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Jolly <Matt.Jolly@footclan.ninja>2024-06-06 22:03:13 +1000
committerJussi Pakkanen <jpakkane@gmail.com>2024-06-23 11:58:36 +0300
commit410bdf8c6c72fd1a2772e86a2a14298f64f1377b (patch)
tree12e0d548f25164c436f14bb6829ff202aad21cb2
parent8967090149cb89f7b82fc6c8f72764975912fa58 (diff)
downloadmeson-410bdf8c6c72fd1a2772e86a2a14298f64f1377b.zip
meson-410bdf8c6c72fd1a2772e86a2a14298f64f1377b.tar.gz
meson-410bdf8c6c72fd1a2772e86a2a14298f64f1377b.tar.bz2
`configure_file`: update \@ escape logic
When configuring a 'meson' or 'cmake@' style file, add a case for escaped variables using matched pairs of `\@` i.e. `\@foo\@ -> @foo@`. The match for @var@ has been amended with a negative lookbehind to ensure that any occurrances of `\@foo@` are not evaluated to `\bar`. The previous behaviour, matching `\@` and escaping only that character, had undesirable side effects including mangling valid perl when configuring files. Closes: https://github.com/mesonbuild/meson/issues/7165
-rw-r--r--mesonbuild/utils/universal.py39
-rw-r--r--test cases/common/14 configure file/config6.h.in37
-rw-r--r--test cases/common/14 configure file/meson.build2
-rw-r--r--test cases/common/14 configure file/prog6.c12
4 files changed, 64 insertions, 26 deletions
diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py
index c831169..6aee268 100644
--- a/mesonbuild/utils/universal.py
+++ b/mesonbuild/utils/universal.py
@@ -1182,24 +1182,21 @@ def do_replacement(regex: T.Pattern[str], line: str,
variable_format: Literal['meson', 'cmake', 'cmake@'],
confdata: T.Union[T.Dict[str, T.Tuple[str, T.Optional[str]]], 'ConfigurationData']) -> T.Tuple[str, T.Set[str]]:
missing_variables: T.Set[str] = set()
- if variable_format == 'cmake':
- start_tag = '${'
- backslash_tag = '\\${'
- else:
- start_tag = '@'
- backslash_tag = '\\@'
def variable_replace(match: T.Match[str]) -> str:
- # Pairs of escape characters before '@' or '\@'
+ # Pairs of escape characters before '@', '\@', '${' or '\${'
if match.group(0).endswith('\\'):
num_escapes = match.end(0) - match.start(0)
return '\\' * (num_escapes // 2)
- # Single escape character and '@'
- elif match.group(0) == backslash_tag:
- return start_tag
- # Template variable to be replaced
+ # Handle cmake escaped \${} tags
+ elif variable_format == 'cmake' and match.group(0) == '\\${':
+ return '${'
+ # \@escaped\@ variables
+ elif match.groupdict().get('escaped') is not None:
+ return match.group('escaped')[1:-2]+'@'
else:
- varname = match.group(1)
+ # Template variable to be replaced
+ varname = match.group('variable')
var_str = ''
if varname in confdata:
var, _ = confdata.get(varname)
@@ -1280,11 +1277,23 @@ def do_define(regex: T.Pattern[str], line: str, confdata: 'ConfigurationData',
def get_variable_regex(variable_format: Literal['meson', 'cmake', 'cmake@'] = 'meson') -> T.Pattern[str]:
# Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define
- # Also allow escaping '@' with '\@'
if variable_format in {'meson', 'cmake@'}:
- regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@')
+ # Also allow escaping pairs of '@' with '\@'
+ regex = re.compile(r'''
+ (?:\\\\)+(?=\\?@) # Matches multiple backslashes followed by an @ symbol
+ | # OR
+ (?<!\\)@(?P<variable>[-a-zA-Z0-9_]+)@ # Match a variable enclosed in @ symbols and capture the variable name; no matches beginning with '\@'
+ | # OR
+ (?P<escaped>\\@[-a-zA-Z0-9_]+\\@) # Match an escaped variable enclosed in @ symbols
+ ''', re.VERBOSE)
else:
- regex = re.compile(r'(?:\\\\)+(?=\\?\$)|\\\${|\${([-a-zA-Z0-9_]+)}')
+ regex = re.compile(r'''
+ (?:\\\\)+(?=\\?\$) # Match multiple backslashes followed by a dollar sign
+ | # OR
+ \\\${ # Match a backslash followed by a dollar sign and an opening curly brace
+ | # OR
+ \${(?P<variable>[-a-zA-Z0-9_]+)} # Match a variable enclosed in curly braces and capture the variable name
+ ''', re.VERBOSE)
return regex
def do_conf_str(src: str, data: T.List[str], confdata: 'ConfigurationData',
diff --git a/test cases/common/14 configure file/config6.h.in b/test cases/common/14 configure file/config6.h.in
index 9719f87..0a91542 100644
--- a/test cases/common/14 configure file/config6.h.in
+++ b/test cases/common/14 configure file/config6.h.in
@@ -1,19 +1,40 @@
/* No escape */
#define MESSAGE1 "@var1@"
-/* Single escape means no replace */
-#define MESSAGE2 "\@var1@"
+/* Escaped whole variable */
+#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@"
+/* Pairs of escapes and then an escaped variable */
+#define MESSAGE4 "\\\\\@var1\@"
-/* Check escaped variable does not overlap following variable */
-#define MESSAGE5 "\@var1@var2@"
+/* We don't gobble \@ prefixing some text */
+#define MESSAGE5 "\\\\@var1"
-/* Check escape character outside variables */
-#define MESSAGE6 "\\ @ \@ \\\\@ \\\\\@"
+/* Check escape character outside variables
+ \ @ \@ */
+#define MESSAGE6 "\\ @ \\\\@"
+
+/* Catch any edge cases */
+
+/* no substitution - not a variable */
+#define MESSAGE7 "@var1"
+
+/* Escaped variable followed by another variable */
+#define MESSAGE8 "\\\\@var1@var2@"
+
+/* Variable followed by another variable */
+#define MESSAGE9 "@var1@var2@"
+
+/* Variable followed by another variable and escaped */
+#define MESSAGE10 "@var1@var2\\\\@"
+
+/* Lots of substitutions in a row*/
+#define MESSAGE11 "@var1@@var2@@var3@@var4@"
+
+/* This should never happen in the real world, right? */
+#define MESSAGE12 "@var1@var2\\\\@var3@var4\\\\@"
diff --git a/test cases/common/14 configure file/meson.build b/test cases/common/14 configure file/meson.build
index 90a468f..8e7d429 100644
--- a/test cases/common/14 configure file/meson.build
+++ b/test cases/common/14 configure file/meson.build
@@ -143,6 +143,8 @@ test('test5', executable('prog5', 'prog5.c'))
conf6 = configuration_data()
conf6.set('var1', 'foo')
conf6.set('var2', 'bar')
+conf6.set('var3', 'baz')
+conf6.set('var4', 'qux')
configure_file(
input : 'config6.h.in',
output : '@BASENAME@',
diff --git a/test cases/common/14 configure file/prog6.c b/test cases/common/14 configure file/prog6.c
index 57f5586..b39f9da 100644
--- a/test cases/common/14 configure file/prog6.c
+++ b/test cases/common/14 configure file/prog6.c
@@ -4,8 +4,14 @@
int main(void) {
return strcmp(MESSAGE1, "foo")
|| strcmp(MESSAGE2, "@var1@")
- || strcmp(MESSAGE3, "\\foo")
+ || strcmp(MESSAGE3, "\\@var1@")
|| strcmp(MESSAGE4, "\\@var1@")
- || strcmp(MESSAGE5, "@var1bar")
- || strcmp(MESSAGE6, "\\ @ @ \\@ \\@");
+ || strcmp(MESSAGE5, "\\@var1")
+ || strcmp(MESSAGE6, "\\ @ \\@")
+ || strcmp(MESSAGE7, "@var1")
+ || strcmp(MESSAGE8, "\\@var1bar")
+ || strcmp(MESSAGE9, "foovar2@")
+ || strcmp(MESSAGE10, "foovar2\\@")
+ || strcmp(MESSAGE11, "foobarbazqux")
+ || strcmp(MESSAGE12, "foovar2\\@var3@var4\\@");
}