 1d03e9771e
			
		
	
	
		1d03e9771e
		
	
	
	
	
		
			
			rustc will check that every reachable #[cfg] matches a list of the expected config names and values. Recent versions of rustc are also complaining about #[cfg(test)], even if it is basically a standard part of the language. So, always allow it. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
		
			
				
	
	
		
			236 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/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:
 | |
|             print("--check-cfg")
 | |
|             print("cfg(test)")
 | |
|             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()
 |