| # SPDX-License-Identifier: Apache-2.0 | |
| # Copyright 2024 The Meson development team | |
| from __future__ import annotations | |
| from collections import defaultdict | |
| import os | |
| import tempfile | |
| import typing as T | |
| import subprocess | |
| from .run_tool import run_tool_on_targets, run_with_buffered_output | |
| from .. import build, mlog | |
| from ..mesonlib import MachineChoice, PerMachine | |
| from ..tooldetect import detect_ninja | |
| if T.TYPE_CHECKING: | |
| from ..compilers.rust import RustCompiler | |
| class ClippyDriver: | |
| def __init__(self, build: build.Build, tempdir: str, args: list[str]): | |
| self.tools: PerMachine[T.List[str]] = PerMachine([], []) | |
| self.warned: T.DefaultDict[str, bool] = defaultdict(lambda: False) | |
| self.tempdir = tempdir | |
| for machine in MachineChoice: | |
| compilers = build.environment.coredata.compilers[machine] | |
| if 'rust' in compilers: | |
| compiler = T.cast('RustCompiler', compilers['rust']) | |
| self.tools[machine] = compiler.get_rust_tool('clippy-driver') | |
| self.args = args | |
| def warn_missing_clippy(self, machine: str) -> None: | |
| if self.warned[machine]: | |
| return | |
| mlog.warning(f'clippy-driver not found for {machine} machine') | |
| self.warned[machine] = True | |
| def __call__(self, target: T.Dict[str, T.Any]) -> T.Iterable[T.Coroutine[None, None, int]]: | |
| for src_block in target['target_sources']: | |
| if 'compiler' in src_block and src_block['language'] == 'rust': | |
| clippy = getattr(self.tools, src_block['machine']) | |
| if not clippy: | |
| self.warn_missing_clippy(src_block['machine']) | |
| continue | |
| cmdlist = list(clippy) | |
| prev = None | |
| lints_cap = None | |
| for arg in src_block['parameters']: | |
| if prev == '--cap-lints': | |
| cmdlist.append(prev) | |
| lints_cap = arg | |
| prev = None | |
| elif prev: | |
| prev = None | |
| continue | |
| if arg in {'--emit', '--out-dir', '--cap-lints'}: | |
| prev = arg | |
| else: | |
| cmdlist.append(arg) | |
| # no use in running clippy if it wouldn't print anything anyway | |
| if lints_cap == 'allow': | |
| break | |
| cmdlist.extend(src_block['sources']) | |
| # the default for --emit is to go all the way to linking, | |
| # and --emit dep-info= is not enough for clippy to do | |
| # enough analysis, so use --emit metadata. | |
| cmdlist.append('--emit') | |
| cmdlist.append('metadata') | |
| cmdlist.append('--out-dir') | |
| cmdlist.append(self.tempdir) | |
| cmdlist += self.args | |
| yield run_with_buffered_output(cmdlist) | |
| def run(args: T.List[str]) -> int: | |
| os.chdir(args[0]) | |
| build_data = build.load(os.getcwd()) | |
| # Build as much of the project as possible, or else | |
| # we get errors about missing libraries in the build directory and | |
| # other related errors. | |
| subprocess.run(detect_ninja() + ['clippy-json-prereq', '-k0']) | |
| with tempfile.TemporaryDirectory() as d: | |
| return run_tool_on_targets(ClippyDriver(build_data, d, args[1:])) |