rust: build: generate lint flags from Cargo.toml

Cargo.toml makes it possible to describe the desired lint level settings
in a nice format.  We can extend this to Meson-built crates, by teaching
rustc_args.py to fetch lint and --check-cfg arguments from Cargo.toml.
--check-cfg arguments come from the unexpected_cfgs lint as well as crate
features

Start with qemu-api, since it already has a [lints.rust] table and
an invocation of rustc_args.py.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2024-11-07 10:02:15 +01:00
parent 1de82059aa
commit 97ed1e9c8e
3 changed files with 86 additions and 4 deletions

View file

@ -120,7 +120,8 @@ if have_rust
endif endif
if have_rust if have_rust
rustc_args = find_program('scripts/rust/rustc_args.py') rustc_args = [find_program('scripts/rust/rustc_args.py'),
'--rustc-version', rustc.version()]
rustfmt = find_program('rustfmt', required: false) rustfmt = find_program('rustfmt', required: false)
# Prohibit code that is forbidden in Rust 2024 # Prohibit code that is forbidden in Rust 2024

View file

@ -1,6 +1,6 @@
_qemu_api_cfg = run_command(rustc_args, _qemu_api_cfg = run_command(rustc_args,
'--config-headers', config_host_h, files('Cargo.toml'), '--config-headers', config_host_h, '--features', '--lints', files('Cargo.toml'),
capture: true, check: true).stdout().strip().split() capture: true, check: true).stdout().strip().splitlines()
# _qemu_api_cfg += ['--cfg', 'feature="allocator"'] # _qemu_api_cfg += ['--cfg', 'feature="allocator"']
if rustc.version().version_compare('>=1.77.0') if rustc.version().version_compare('>=1.77.0')

View file

@ -25,9 +25,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
import argparse import argparse
from dataclasses import dataclass
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Any, Iterable, Mapping, Optional, Set from typing import Any, Iterable, List, Mapping, Optional, Set
try: try:
import tomllib import tomllib
@ -61,6 +62,45 @@ class CargoTOML:
return table return table
@dataclass
class LintFlag:
flags: List[str]
priority: int
def generate_lint_flags(cargo_toml: CargoTOML) -> 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":
lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority))
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]: def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str]:
"""Converts defines from config[..].h headers to rustc --cfg flags.""" """Converts defines from config[..].h headers to rustc --cfg flags."""
@ -97,13 +137,54 @@ def main() -> None:
dest="cargo_toml", dest="cargo_toml",
help="path to Cargo.toml file", help="path to Cargo.toml file",
) )
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",
)
args = parser.parse_args() args = parser.parse_args()
if args.verbose: if args.verbose:
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
logging.debug("args: %s", args) logging.debug("args: %s", args)
rustc_version = tuple((int(x) for x in args.rustc_version.split('.')[0:2]))
cargo_toml = CargoTOML(args.cargo_toml) cargo_toml = CargoTOML(args.cargo_toml)
if args.lints:
for tok in generate_lint_flags(cargo_toml):
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 header in args.config_headers:
for tok in generate_cfg_flags(header, cargo_toml): for tok in generate_cfg_flags(header, cargo_toml):
print(tok) print(tok)