123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- #!/usr/bin/env python3
- """Generate rustc arguments for meson rust builds.
- This program generates --cfg compile flags for the configuration headers passed
- as arguments.
- Copyright (c) 2024 Linaro Ltd.
- Authors:
- Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- """
- import argparse
- from dataclasses import dataclass
- import logging
- 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."""
- 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[0]
- if f'cfg({name})' not in cargo_toml.check_cfg:
- continue
- if len(cfg) >= 2 and cfg[1] != "1":
- continue
- cfg_list.append("--cfg")
- cfg_list.append(name)
- return cfg_list
- def main() -> None:
- parser = argparse.ArgumentParser()
- parser.add_argument("-v", "--verbose", action="store_true")
- parser.add_argument(
- "--config-headers",
- metavar="CONFIG_HEADER",
- action="append",
- dest="config_headers",
- help="paths to any configuration C headers (*.h files), if any",
- 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, cargo_toml):
- print(tok)
- if __name__ == "__main__":
- main()
|