Skip to content

Commit

Permalink
separate mesondefine and cmakedefine logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan200101 authored and dcbaker committed Dec 10, 2024
1 parent 54cab09 commit 8156e12
Showing 1 changed file with 125 additions and 45 deletions.
170 changes: 125 additions & 45 deletions mesonbuild/utils/universal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,46 @@ def join_args(args: T.Iterable[str]) -> str:
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]]:
if variable_format == 'meson':
return do_replacement_meson(regex, line, confdata)
elif variable_format in {'cmake', 'cmake@'}:
return do_replacement_cmake(regex, line, variable_format == 'cmake@', confdata)
else:
raise MesonException('Invalid variable format')

def do_replacement_meson(regex: T.Pattern[str], line: str,
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()

def variable_replace(match: T.Match[str]) -> str:
# Pairs of escape characters before '@', '\@', '${' or '\${'
if match.group(0).endswith('\\'):
num_escapes = match.end(0) - match.start(0)
return '\\' * (num_escapes // 2)
# \@escaped\@ variables
elif match.groupdict().get('escaped') is not None:
return match.group('escaped')[1:-2]+'@'
else:
# Template variable to be replaced
varname = match.group('variable')
var_str = ''
if varname in confdata:
var, _ = confdata.get(varname)
if isinstance(var, str):
var_str = var
elif isinstance(var, int):
var_str = str(var)
else:
msg = f'Tried to replace variable {varname!r} value with ' \
f'something other than a string or int: {var!r}'
raise MesonException(msg)
else:
missing_variables.add(varname)
return var_str
return re.sub(regex, variable_replace, line), missing_variables

def do_replacement_cmake(regex: T.Pattern[str], line: str, at_only: bool,
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()

def variable_replace(match: T.Match[str]) -> str:
Expand All @@ -1181,7 +1221,7 @@ def variable_replace(match: T.Match[str]) -> str:
num_escapes = match.end(0) - match.start(0)
return '\\' * (num_escapes // 2)
# Handle cmake escaped \${} tags
elif variable_format == 'cmake' and match.group(0) == '\\${':
elif not at_only and match.group(0) == '\\${':
return '${'
# \@escaped\@ variables
elif match.groupdict().get('escaped') is not None:
Expand All @@ -1194,7 +1234,7 @@ def variable_replace(match: T.Match[str]) -> str:
var, _ = confdata.get(varname)
if isinstance(var, str):
var_str = var
elif variable_format.startswith("cmake") and isinstance(var, bool):
elif isinstance(var, bool):
var_str = str(int(var))
elif isinstance(var, int):
var_str = str(var)
Expand All @@ -1207,11 +1247,36 @@ def variable_replace(match: T.Match[str]) -> str:
return var_str
return re.sub(regex, variable_replace, line), missing_variables

def do_define(regex: T.Pattern[str], line: str, confdata: 'ConfigurationData',
variable_format: Literal['meson', 'cmake', 'cmake@'], subproject: T.Optional[SubProject] = None) -> str:
cmake_bool_define = False
if variable_format != "meson":
cmake_bool_define = "cmakedefine01" in line
def do_define_meson(regex: T.Pattern[str], line: str, confdata: 'ConfigurationData',
subproject: T.Optional[SubProject] = None) -> str:

arr = line.split()
if len(arr) != 2:
raise MesonException('#mesondefine does not contain exactly two tokens: %s' % line.strip())

varname = arr[1]
try:
v, _ = confdata.get(varname)
except KeyError:
return '/* #undef %s */\n' % varname

if isinstance(v, str):
result = f'#define {varname} {v}'.strip() + '\n'
result, _ = do_replacement_meson(regex, result, confdata)
return result
elif isinstance(v, bool):
if v:
return '#define %s\n' % varname
else:
return '#undef %s\n' % varname
elif isinstance(v, int):
return '#define %s %d\n' % (varname, v)
else:
raise MesonException('#mesondefine argument "%s" is of unknown type.' % varname)

def do_define_cmake(regex: T.Pattern[str], line: str, confdata: 'ConfigurationData', at_only: bool,
subproject: T.Optional[SubProject] = None) -> str:
cmake_bool_define = 'cmakedefine01' in line

def get_cmake_define(line: str, confdata: 'ConfigurationData') -> str:
arr = line.split()
Expand All @@ -1230,12 +1295,10 @@ def get_cmake_define(line: str, confdata: 'ConfigurationData') -> str:
return ' '.join(define_value)

arr = line.split()
if len(arr) != 2:
if variable_format == 'meson':
raise MesonException('#mesondefine does not contain exactly two tokens: %s' % line.strip())
elif subproject is not None:
from ..interpreterbase.decorators import FeatureNew
FeatureNew.single_use('cmakedefine without exactly two tokens', '0.54.1', subproject)

if len(arr) != 2 and subproject is not None:
from ..interpreterbase.decorators import FeatureNew
FeatureNew.single_use('cmakedefine without exactly two tokens', '0.54.1', subproject)

varname = arr[1]
try:
Expand All @@ -1246,26 +1309,13 @@ def get_cmake_define(line: str, confdata: 'ConfigurationData') -> str:
else:
return '/* #undef %s */\n' % varname

if isinstance(v, str) or variable_format != "meson":
if variable_format == 'meson':
result = v
else:
if not cmake_bool_define and not v:
return '/* #undef %s */\n' % varname
if not cmake_bool_define and not v:
return '/* #undef %s */\n' % varname

result = get_cmake_define(line, confdata)
result = f'#define {varname} {result}'.strip() + '\n'
result, _ = do_replacement(regex, result, variable_format, confdata)
return result
elif isinstance(v, bool):
if v:
return '#define %s\n' % varname
else:
return '#undef %s\n' % varname
elif isinstance(v, int):
return '#define %s %d\n' % (varname, v)
else:
raise MesonException('#mesondefine argument "%s" is of unknown type.' % varname)
result = get_cmake_define(line, confdata)
result = f'#define {varname} {result}'.strip() + '\n'
result, _ = do_replacement_cmake(regex, result, at_only, confdata)
return result

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
Expand All @@ -1291,20 +1341,50 @@ def get_variable_regex(variable_format: Literal['meson', 'cmake', 'cmake@'] = 'm
def do_conf_str(src: str, data: T.List[str], confdata: 'ConfigurationData',
variable_format: Literal['meson', 'cmake', 'cmake@'],
subproject: T.Optional[SubProject] = None) -> T.Tuple[T.List[str], T.Set[str], bool]:
def line_is_valid(line: str, variable_format: str) -> bool:
if variable_format == 'meson':
if variable_format == 'meson':
return do_conf_str_meson(src, data, confdata, subproject)
elif variable_format in {'cmake', 'cmake@'}:
return do_conf_str_cmake(src, data, confdata, variable_format == 'cmake@', subproject)
else:
raise MesonException('Invalid variable format')

def do_conf_str_meson(src: str, data: T.List[str], confdata: 'ConfigurationData',
subproject: T.Optional[SubProject] = None) -> T.Tuple[T.List[str], T.Set[str], bool]:

regex = get_variable_regex('meson')

search_token = '#mesondefine'

result: T.List[str] = []
missing_variables: T.Set[str] = set()
# Detect when the configuration data is empty and no tokens were found
# during substitution so we can warn the user to use the `copy:` kwarg.
confdata_useless = not confdata.keys()
for line in data:
if line.lstrip().startswith(search_token):
confdata_useless = False
line = do_define_meson(regex, line, confdata, subproject)
else:
if '#cmakedefine' in line:
return False
else: # cmake format
if '#mesondefine' in line:
return False
return True
raise MesonException(f'Format error in {src}: saw "{line.strip()}" when format set to "meson"')
line, missing = do_replacement_meson(regex, line, confdata)
missing_variables.update(missing)
if missing:
confdata_useless = False
result.append(line)

return result, missing_variables, confdata_useless

def do_conf_str_cmake(src: str, data: T.List[str], confdata: 'ConfigurationData', at_only: bool,
subproject: T.Optional[SubProject] = None) -> T.Tuple[T.List[str], T.Set[str], bool]:

variable_format: Literal['cmake', 'cmake@'] = 'cmake'
if at_only:
variable_format = 'cmake@'

regex = get_variable_regex(variable_format)

search_token = '#mesondefine'
if variable_format != 'meson':
search_token = '#cmakedefine'
search_token = '#cmakedefine'

result: T.List[str] = []
missing_variables: T.Set[str] = set()
Expand All @@ -1314,11 +1394,11 @@ def line_is_valid(line: str, variable_format: str) -> bool:
for line in data:
if line.lstrip().startswith(search_token):
confdata_useless = False
line = do_define(regex, line, confdata, variable_format, subproject)
line = do_define_cmake(regex, line, confdata, at_only, subproject)
else:
if not line_is_valid(line, variable_format):
if '#mesondefine' in line:
raise MesonException(f'Format error in {src}: saw "{line.strip()}" when format set to "{variable_format}"')
line, missing = do_replacement(regex, line, variable_format, confdata)
line, missing = do_replacement_cmake(regex, line, at_only, confdata)
missing_variables.update(missing)
if missing:
confdata_useless = False
Expand Down

0 comments on commit 8156e12

Please sign in to comment.