Ensure rust2rpm doesn't autogenerate architecture-dependent patches
The logic in the cfg-expression evaluator checked *too many cases*, some of which would have resulted in generation of broken patches and packages. We need to ensure that the evaluation is independent of the host architecture (i.e. we need to keep *all* dependencies that are valid on *any* of our build targets, even if they end up being unused on *some* of them).
This commit is contained in:
parent
cc4e5fd341
commit
f0d04b09e8
2 changed files with 47 additions and 27 deletions
|
@ -1,8 +1,5 @@
|
||||||
import ast
|
import ast
|
||||||
import ctypes
|
|
||||||
import functools
|
import functools
|
||||||
import platform
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import pyparsing as pp
|
import pyparsing as pp
|
||||||
from pyparsing import ParseException
|
from pyparsing import ParseException
|
||||||
|
@ -63,43 +60,69 @@ def cfg_grammar():
|
||||||
|
|
||||||
|
|
||||||
@functools.cache
|
@functools.cache
|
||||||
def evaluate_variable(name):
|
def evaluate_predicate(name: str, value: str) -> bool:
|
||||||
|
# based on: https://doc.rust-lang.org/reference/conditional-compilation.html
|
||||||
|
|
||||||
match name:
|
match name:
|
||||||
case "target_arch":
|
case "target_arch":
|
||||||
return platform.machine()
|
# Needs to be ignored, as we cannot generate patches that are
|
||||||
|
# different depending on the host architecture - except if the
|
||||||
|
# target architecture is "wasm32", which we don't support.
|
||||||
|
return value != "wasm32"
|
||||||
|
|
||||||
|
case "target_feature":
|
||||||
|
# The "target_feature" predicate can be ignored as well, since the
|
||||||
|
# valid values for this predicate are architecture-dependent.
|
||||||
|
return True
|
||||||
|
|
||||||
case "target_os":
|
case "target_os":
|
||||||
return "linux"
|
return value == "linux"
|
||||||
|
|
||||||
case "target_family":
|
case "target_family":
|
||||||
return "unix"
|
return value == "unix"
|
||||||
|
|
||||||
case "unix":
|
|
||||||
return evaluate_variable("target_family") == "unix"
|
|
||||||
|
|
||||||
case "windows":
|
|
||||||
return evaluate_variable("target_family") == "windows"
|
|
||||||
|
|
||||||
case "target_env":
|
case "target_env":
|
||||||
# Key-value option set with further disambiguating information about the
|
# The "target_env" predicate is used to disambiguate target
|
||||||
# target platform with information about the ABI or libc used
|
# platforms based on its C library / C ABI (i.e. we can ignore
|
||||||
return ...
|
# "msvc" and "musl"), and if there's no need to disambiguate, the
|
||||||
|
# value can be the empty string.
|
||||||
|
return value in ["", "gnu"]
|
||||||
|
|
||||||
case "target_endian":
|
case "target_endian":
|
||||||
return sys.byteorder
|
# Needs to be ignored, as we cannot generate patches that are
|
||||||
|
# different depending on the host architecture.
|
||||||
|
return True
|
||||||
|
|
||||||
case "target_pointer_width":
|
case "target_pointer_width":
|
||||||
return str(ctypes.sizeof(ctypes.c_void_p) * 8)
|
# Needs to be ignored, as we cannot generate patches that are
|
||||||
|
# different depending on the host architecture.
|
||||||
|
return True
|
||||||
|
|
||||||
case "target_vendor":
|
case "target_vendor":
|
||||||
return "unknown"
|
# On linux systems, "target_vendor" is always "unknown".
|
||||||
|
return value == "unknown"
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
log.warn(f"Ignoring unknown variable {name!r} in cfg-expression.")
|
log.warn(f'Ignoring invalid predicate \'"{name}" = "{value}"\' in cfg-expression.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def evaluate(expr, nested=False):
|
@functools.cache
|
||||||
|
def evaluate_atom(name: str) -> bool:
|
||||||
|
match name:
|
||||||
|
case "unix":
|
||||||
|
return True
|
||||||
|
case "windows":
|
||||||
|
return False
|
||||||
|
case _:
|
||||||
|
log.warn(
|
||||||
|
f"Ignoring invalid atom {name!r} in cfg-expression. "
|
||||||
|
+ 'Only "unix" and "windows" are valid names for atoms.'
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate(expr, nested=False) -> bool:
|
||||||
if hasattr(expr, "asList"):
|
if hasattr(expr, "asList"):
|
||||||
expr = expr.asList() # compat with pyparsing 2.7.x
|
expr = expr.asList() # compat with pyparsing 2.7.x
|
||||||
match expr:
|
match expr:
|
||||||
|
@ -113,11 +136,9 @@ def evaluate(expr, nested=False):
|
||||||
return any(evaluate(arg, True) for arg in args)
|
return any(evaluate(arg, True) for arg in args)
|
||||||
case [variable, value] if nested:
|
case [variable, value] if nested:
|
||||||
v = ast.literal_eval(value)
|
v = ast.literal_eval(value)
|
||||||
x = evaluate_variable(variable)
|
return evaluate_predicate(variable, v)
|
||||||
return x == v
|
|
||||||
case [variable] if nested:
|
case [variable] if nested:
|
||||||
x = evaluate_variable(variable)
|
return evaluate_atom(variable)
|
||||||
return x
|
|
||||||
case _:
|
case _:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ def test_pyparsing_run_tests():
|
||||||
[
|
[
|
||||||
('cfg(target_os = "macos")', False),
|
('cfg(target_os = "macos")', False),
|
||||||
("cfg(any(foo, bar))", False),
|
("cfg(any(foo, bar))", False),
|
||||||
('cfg(all(unix, target_pointer_width = "16"))', False),
|
('cfg(all(unix, target_pointer_width = "16"))', True),
|
||||||
("cfg(not(foo))", True),
|
("cfg(not(foo))", True),
|
||||||
("cfg(unix)", True),
|
("cfg(unix)", True),
|
||||||
("cfg(not(unix))", False),
|
("cfg(not(unix))", False),
|
||||||
|
@ -36,7 +36,6 @@ def test_pyparsing_run_tests():
|
||||||
('cfg(all(target_os = "linux"))', True),
|
('cfg(all(target_os = "linux"))', True),
|
||||||
('cfg(any(target_os = "linux", target_os = "macos"))', True),
|
('cfg(any(target_os = "linux", target_os = "macos"))', True),
|
||||||
('cfg(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))', True),
|
('cfg(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))', True),
|
||||||
('cfg(all(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))', False),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_expressions(expr, expected):
|
def test_expressions(expr, expected):
|
||||||
|
|
Loading…
Reference in a new issue