diff options
Diffstat (limited to 'scripts/rust/rustc_args.py')
| -rw-r--r-- | scripts/rust/rustc_args.py | 187 |
1 files changed, 168 insertions, 19 deletions
diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index e4cc9720e1..5525b3886f 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -25,31 +25,110 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. """ import argparse +from dataclasses import dataclass import logging - -from typing import List - - -def generate_cfg_flags(header: str) -> List[str]: +from pathlib import Path +from typing import Any, Iterable, List, Mapping, Optional, Set + +try: + import tomllib +except ImportError: + import tomli as tomllib + +STRICT_LINTS = {"unknown_lints", "warnings"} + + +class CargoTOML: + tomldata: Mapping[Any, Any] + workspace_data: Mapping[Any, Any] + check_cfg: Set[str] + + def __init__(self, path: Optional[str], workspace: Optional[str]): + if path is not None: + with open(path, 'rb') as f: + self.tomldata = tomllib.load(f) + else: + self.tomldata = {"lints": {"workspace": True}} + + if workspace is not None: + with open(workspace, 'rb') as f: + self.workspace_data = tomllib.load(f) + if "workspace" not in self.workspace_data: + self.workspace_data["workspace"] = {} + + self.check_cfg = set(self.find_check_cfg()) + + def find_check_cfg(self) -> Iterable[str]: + toml_lints = self.lints + rust_lints = toml_lints.get("rust", {}) + cfg_lint = rust_lints.get("unexpected_cfgs", {}) + return cfg_lint.get("check-cfg", []) + + @property + def lints(self) -> Mapping[Any, Any]: + return self.get_table("lints", True) + + def get_table(self, key: str, can_be_workspace: bool = False) -> Mapping[Any, Any]: + table = self.tomldata.get(key, {}) + if can_be_workspace and table.get("workspace", False) is True: + table = self.workspace_data["workspace"].get(key, {}) + + return table + + +@dataclass +class LintFlag: + flags: List[str] + priority: int + + +def generate_lint_flags(cargo_toml: CargoTOML, strict_lints: bool) -> Iterable[str]: + """Converts Cargo.toml lints to rustc -A/-D/-F/-W flags.""" + + toml_lints = cargo_toml.lints + + lint_list = [] + for k, v in toml_lints.items(): + prefix = "" if k == "rust" else k + "::" + for lint, data in v.items(): + level = data if isinstance(data, str) else data["level"] + priority = 0 if isinstance(data, str) else data.get("priority", 0) + if level == "deny": + flag = "-D" + elif level == "allow": + flag = "-A" + elif level == "warn": + flag = "-W" + elif level == "forbid": + flag = "-F" + else: + raise Exception(f"invalid level {level} for {prefix}{lint}") + + # This may change if QEMU ever invokes clippy-driver or rustdoc by + # hand. For now, check the syntax but do not add non-rustc lints to + # the command line. + if k == "rust" and not (strict_lints and lint in STRICT_LINTS): + lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority)) + + if strict_lints: + for lint in STRICT_LINTS: + lint_list.append(LintFlag(flags=["-D", lint], priority=1000000)) + + lint_list.sort(key=lambda x: x.priority) + for lint in lint_list: + yield from lint.flags + + +def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str]: """Converts defines from config[..].h headers to rustc --cfg flags.""" - def cfg_name(name: str) -> str: - """Filter function for C #defines""" - if ( - name.startswith("CONFIG_") - or name.startswith("TARGET_") - or name.startswith("HAVE_") - ): - return name - return "" - with open(header, encoding="utf-8") as cfg: config = [l.split()[1:] for l in cfg if l.startswith("#define")] cfg_list = [] for cfg in config: - name = cfg_name(cfg[0]) - if not name: + name = cfg[0] + if f'cfg({name})' not in cargo_toml.check_cfg: continue if len(cfg) >= 2 and cfg[1] != "1": continue @@ -59,7 +138,6 @@ def generate_cfg_flags(header: str) -> List[str]: def main() -> None: - # pylint: disable=missing-function-docstring parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", action="store_true") parser.add_argument( @@ -71,12 +149,83 @@ def main() -> None: required=False, default=[], ) + parser.add_argument( + metavar="TOML_FILE", + action="store", + dest="cargo_toml", + help="path to Cargo.toml file", + nargs='?', + ) + parser.add_argument( + "--workspace", + metavar="DIR", + action="store", + dest="workspace", + help="path to root of the workspace", + required=False, + default=None, + ) + parser.add_argument( + "--features", + action="store_true", + dest="features", + help="generate --check-cfg arguments for features", + required=False, + default=None, + ) + parser.add_argument( + "--lints", + action="store_true", + dest="lints", + help="generate arguments from [lints] table", + required=False, + default=None, + ) + parser.add_argument( + "--rustc-version", + metavar="VERSION", + dest="rustc_version", + action="store", + help="version of rustc", + required=False, + default="1.0.0", + ) + parser.add_argument( + "--strict-lints", + action="store_true", + dest="strict_lints", + help="apply stricter checks (for nightly Rust)", + default=False, + ) args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) logging.debug("args: %s", args) + + rustc_version = tuple((int(x) for x in args.rustc_version.split('.')[0:2])) + if args.workspace: + workspace_cargo_toml = Path(args.workspace, "Cargo.toml").resolve() + cargo_toml = CargoTOML(args.cargo_toml, str(workspace_cargo_toml)) + else: + cargo_toml = CargoTOML(args.cargo_toml, None) + + if args.lints: + for tok in generate_lint_flags(cargo_toml, args.strict_lints): + print(tok) + + if rustc_version >= (1, 80): + if args.lints: + for cfg in sorted(cargo_toml.check_cfg): + print("--check-cfg") + print(cfg) + if args.features: + for feature in cargo_toml.get_table("features"): + if feature != "default": + print("--check-cfg") + print(f'cfg(feature,values("{feature}"))') + for header in args.config_headers: - for tok in generate_cfg_flags(header): + for tok in generate_cfg_flags(header, cargo_toml): print(tok) |