Skip to content

Commit

Permalink
ninjabackend: add support for "ninja clippy"
Browse files Browse the repository at this point in the history
Add a target that builds all crates that could be extern to others,
and then reruns clippy.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
bonzini authored and dcbaker committed Dec 19, 2024
1 parent 27c567d commit 5768ccb
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 2 deletions.
20 changes: 20 additions & 0 deletions docs/markdown/howtox.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,26 @@ And then pass it through the variable (remember to use absolute path):
$ SCANBUILD=$(pwd)/my-scan-build.sh ninja -C builddir scan-build
```

## Use clippy

If your project includes Rust targets, you can invoke clippy like this:

```console
$ meson setup builddir
$ ninja -C builddir clippy
```

Clippy will also obey the `werror` [builtin option](Builtin-options.md#core-options).

By default Meson uses as many concurrent processes as there are cores
on the test machine. You can override this with the environment
variable `MESON_NUM_PROCESSES`.

Meson will look for `clippy-driver` in the same directory as `rustc`,
or try to invoke it using `rustup` if `rustc` points to a `rustup`
binary. If `clippy-driver` is not detected properly, you can add it to
a [machine file](Machine-files.md).

## Use profile guided optimization

Using profile guided optimization with GCC is a two phase
Expand Down
5 changes: 5 additions & 0 deletions docs/markdown/snippets/clippy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Meson can run "clippy" on Rust projects

Meson now defines a `clippy` target if the project uses the Rust programming
language. The target runs clippy on all Rust sources, using the `clippy-driver`
program from the same Rust toolchain as the `rustc` compiler.
2 changes: 1 addition & 1 deletion docs/markdown/snippets/num-processes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ the amount of parallel jobs to run; this was useful when `meson test` is
invoked through `ninja test` for example. With this version, a new variable
`MESON_NUM_PROCESSES` is supported with a broader scope: in addition to
`meson test`, it is also used by the `external_project` module and by
Ninja targets that invoke `clang-tidy` and `clang-format`.
Ninja targets that invoke `clang-tidy`, `clang-format` and `clippy`.
6 changes: 6 additions & 0 deletions mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -2040,6 +2040,12 @@ def compiler_to_generator_args(self, target: build.BuildTarget,
commands += [input]
return commands

def have_language(self, langname: str) -> bool:
for for_machine in MachineChoice:
if langname in self.environment.coredata.compilers[for_machine]:
return True
return False

def compiler_to_generator(self, target: build.BuildTarget,
compiler: 'Compiler',
sources: _ALL_SOURCES_TYPE,
Expand Down
22 changes: 21 additions & 1 deletion mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@ class RustCrate:

display_name: str
root_module: str
crate_type: str
target_name: str
edition: RUST_EDITIONS
deps: T.List[RustDep]
cfg: T.List[str]
Expand Down Expand Up @@ -1878,6 +1880,7 @@ def __generate_sources_structure(self, root: Path, structured_sources: build.Str
return orderdeps, first_file

def _add_rust_project_entry(self, name: str, main_rust_file: str, args: CompilerArgs,
crate_type: str, target_name: str,
from_subproject: bool, proc_macro_dylib_path: T.Optional[str],
deps: T.List[RustDep]) -> None:
raw_edition: T.Optional[str] = mesonlib.first(reversed(args), lambda x: x.startswith('--edition'))
Expand All @@ -1895,6 +1898,8 @@ def _add_rust_project_entry(self, name: str, main_rust_file: str, args: Compiler
len(self.rust_crates),
name,
main_rust_file,
crate_type,
target_name,
edition,
deps,
cfg,
Expand Down Expand Up @@ -2134,7 +2139,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False):

self._add_rust_project_entry(target.name,
os.path.abspath(os.path.join(self.environment.build_dir, main_rust_file)),
args,
args, cratetype, target_name,
bool(target.subproject),
proc_macro_dylib_path,
project_deps)
Expand Down Expand Up @@ -3640,6 +3645,20 @@ def generate_dist(self) -> None:
elem.add_item('pool', 'console')
self.add_build(elem)

def generate_clippy(self) -> None:
if 'clippy' in self.all_outputs or not self.have_language('rust'):
return

cmd = self.environment.get_build_command() + \
['--internal', 'clippy', self.environment.build_dir]
elem = self.create_phony_target('clippy', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', cmd)
elem.add_item('pool', 'console')
for crate in self.rust_crates.values():
if crate.crate_type in {'rlib', 'dylib', 'proc-macro'}:
elem.add_dep(crate.target_name)
self.add_build(elem)

def generate_scanbuild(self) -> None:
if not environment.detect_scanbuild():
return
Expand Down Expand Up @@ -3707,6 +3726,7 @@ def generate_utils(self) -> None:
self.generate_scanbuild()
self.generate_clangformat()
self.generate_clangtidy()
self.generate_clippy()
self.generate_tags('etags', 'TAGS')
self.generate_tags('ctags', 'ctags')
self.generate_tags('cscope', 'cscope')
Expand Down
18 changes: 18 additions & 0 deletions unittests/allplatformstests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4887,6 +4887,24 @@ def output_name(name, type_):
@skip_if_not_language('rust')
@unittest.skipIf(not shutil.which('clippy-driver'), 'Test requires clippy-driver')
def test_rust_clippy(self) -> None:
if self.backend is not Backend.ninja:
raise unittest.SkipTest('Rust is only supported with ninja currently')
# When clippy is used, we should get an exception since a variable named
# "foo" is used, but is on our denylist
testdir = os.path.join(self.rust_test_dir, '1 basic')
self.init(testdir)
self.build('clippy')

self.wipe()
self.init(testdir, extra_args=['--werror', '-Db_colorout=never'])
with self.assertRaises(subprocess.CalledProcessError) as cm:
self.build('clippy')
self.assertTrue('error: use of a blacklisted/placeholder name `foo`' in cm.exception.stdout or
'error: use of a disallowed/placeholder name `foo`' in cm.exception.stdout)

@skip_if_not_language('rust')
@unittest.skipIf(not shutil.which('clippy-driver'), 'Test requires clippy-driver')
def test_rust_clippy_as_rustc(self) -> None:
if self.backend is not Backend.ninja:
raise unittest.SkipTest('Rust is only supported with ninja currently')
# When clippy is used, we should get an exception since a variable named
Expand Down

0 comments on commit 5768ccb

Please sign in to comment.