Built-in `clang-tidy` target does not build pch files required to build the sources
Describe the bug
If you run ninja -C build clang-tidy it will not work if you are using pch files if you have not run a full build yet, particularly with clang, since the pch files will not yet be built, and there is no dependency between the pch targets and the clang-tidy target. Furthermore, there is no way to name the pch targets in meson's DSL so custom clang-tidy target authors also have this problem.
A workaround is that custom clang-tidy target authors can write horrible scripts that pull the PCH targets out of compile_commands.json and execute them if they are out of date.
It would be possible to make meson do something like it does for test here: https://github.com/mesonbuild/meson/issues/1704 but I would not consider this a particularly nice solution personally, since it is a significant UX regression for users who already have a slightly stale build but with up to date PCH targets.
To Reproduce
Create a meson.build as follows:
project('ohno', 'cpp')
shared_module('oops', files('oops.cc'), cpp_pch : './pch/pch.hh')
Create empty files oops.cc, pch/pch.hh.
meson/repro2 » CC=clang CXX=clang meson setup --wipe build
The Meson build system
Version: 1.5.1
Source dir: /home/jade/src/co/meson/repro2
Build dir: /home/jade/src/co/meson/repro2/build
Build type: native build
Project name: ohno
Project version: undefined
C++ compiler for the host machine: clang (clang 18.1.8 "clang version 18.1.8")
C++ linker for the host machine: clang ld.bfd 2.42.0
Host machine cpu family: x86_64
Host machine cpu: x86_64
Build targets in project: 1
Found ninja-1.12.1 at /usr/bin/ninja
meson/repro2 » ninja -C build clang-tidy
ninja: Entering directory `build'
[0/1] /usr/bin/meson --internal clangtidy /home/jade/src/co/meson/repro2 /home/jade/src/co/meson/repro2/build
2 errors generated.
Error while processing /home/jade/src/co/meson/repro2/oops.cc.
error: PCH file 'liboops.so.p/pch.hh.pch' not found: module file not found [clang-diagnostic-error]
error: unable to read PCH file liboops.so.p/pch.hh.pch: 'No such file or directory' [clang-diagnostic-error]
FAILED: meson-internal__clang-tidy
/usr/bin/meson --internal clangtidy /home/jade/src/co/meson/repro2 /home/jade/src/co/meson/repro2/build
ninja: build stopped: subcommand failed.
Note to be careful if you are throwing this repro in a random subdir of another git repo: the .cc file needs to be git added to be seen by the meson clang-tidy target.
Expected behavior
I expect the clang-tidy target to depend on all the PCHes in the project, and if this is done it must be possible to create such dependencies if reimplementing clang-tidy as a custom target as well, ideally without listing every target in the project in a list by hand. There's also a design constraint of what to do of generated code that is #include'd into the files; it is not just PCH targets that are a problem for meson's clang-tidy targets, but pch is the most obvious one.
system parameters
- Is this a cross build or just a plain native build (for the same computer)? Nope
- what operating system (e.g. MacOS Catalina, Windows 10, CentOS 8.0, Ubuntu 18.04, etc.): Arch Linux
- what Python version are you using e.g. 3.8.0: 3.12.4
- what
meson --version: 1.5.1 - what
ninja --versionif it's a Ninja build: 1.12.1
I am revising my outlook on this to "its a bug": it seems that custom_target had this changed, but not shared_module. https://mesonbuild.com/Reference-manual_functions.html#custom_target_build_by_default
I feel ethically obliged to write down my workaround to this for whoever runs into this next. It's is a custom_target that always rebuilds that calls another ninja to build those targets manually.
#!/usr/bin/env python3
import subprocess
def get_targets_of_rule(build_root: str, rule_name: str) -> list[str]:
return subprocess.check_output(['ninja', '-C', build_root, '-t', 'targets', 'rule', rule_name]).decode().strip().splitlines()
def ninja_build(build_root: str, targets: list[str]):
subprocess.check_call(['ninja', '-C', build_root, '--', *targets])
def main():
import argparse
ap = argparse.ArgumentParser(description='Builds required targets for clang-tidy')
ap.add_argument('build_root', help='Ninja build root', type=str)
args = ap.parse_args()
targets = [t for t in get_targets_of_rule(args.build_root, 'CUSTOM_COMMAND') if t.endswith('gen.hh')]
ninja_build(args.build_root, targets)
if __name__ == '__main__':
main()
build_all_generated_headers = custom_target(
command : [
python,
meson.current_source_dir() / 'build_required_targets.py',
meson.global_build_root(),
],
output : 'generated_headers.stamp',
build_by_default : false,
build_always_stale : true,
)
if lix_clang_tidy_so_found
run_clang_tidy_args = [
'-load',
lix_clang_tidy_so,
'-p',
# We have to workaround a run-clang-tidy bug too, so we must give the
# directory name rather than the actual compdb file.
# https://github.com/llvm/llvm-project/issues/101440
meson.current_build_dir(),
'-quiet',
]
run_target(
'clang-tidy',
command : [
run_clang_tidy,
run_clang_tidy_args,
'-warnings-as-errors',
'*',
],
depends : [
build_all_generated_headers,
],
)
run_target(
'clang-tidy-fix',
command : [
run_clang_tidy,
run_clang_tidy_args,
'-fix',
],
depends : [
build_all_generated_headers,
],
)
endif
https://gerrit.lix.systems/c/lix/+/1697/6