meson icon indicating copy to clipboard operation
meson copied to clipboard

Built-in `clang-tidy` target does not build pch files required to build the sources

Open lf- opened this issue 1 year ago • 2 comments

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 --version if it's a Ninja build: 1.12.1

lf- avatar Jul 31 '24 23:07 lf-

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

lf- avatar Aug 01 '24 01:08 lf-

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

lf- avatar Aug 02 '24 22:08 lf-