mesonbuild / meson

The Meson Build System
http://mesonbuild.com
Apache License 2.0
5.35k stars 1.53k forks source link

Support for clang-tidy (or other linters) #2383

Closed jhasse closed 4 years ago

jhasse commented 6 years ago

CMake has support for additional linting by clang-tidy: http://www.mariobadr.com/using-clang-tidy-with-cmake-36.html

I was wondering if something like this could be done for Meson, so that header dependencies etc. still work.

FSMaxB commented 6 years ago

Meson already produces the compile_commands.json that is required for clang-tidy but sadly there are too many command line options in there.

I don't know why exactly, but clang-tidy chokes on -pipe, various -W diagnostic flags and more.

FSMaxB commented 6 years ago

Actually if you remove all -pipe from the compile_commands.json then you can run clang-tidy as long as you pass -clang-diagnostic-unused-command-line-argument to the list of checks.

agauniyal commented 6 years ago

@FSMaxB how did you make it work? Something like -

run_target('clang-tidy', command : ['clang-tidy', '-checks="*" -p=' +
            join_paths(meson.build_root())])

doesn't works for me ๐Ÿ˜•

FSMaxB commented 6 years ago

@agauniyal https://github.com/1984not-GmbH/molch/blob/f06980fa7e65bcc6ac06b72caec39e8771ed2ffc/ci/clang-tidy.sh I'm not proud of it, but it works for me.

ghost commented 6 years ago

Iโ€˜d second that request and add iwyu as a request.

It would be really good to have easy support for automatic fixes being applied as well. Setting up the python script is a real pain in my experience.

๐Ÿ™๐Ÿผ thanks

NickeZ commented 5 years ago

Using clang-tidy-6.0 the following works for me:

src_files = files('asourcefile.cpp')
clangtidy = find_program('clang-tidy', required: false)
if clangtidy.found()
    run_target(
        'tidy',
        command: [
            clangtidy,
            '-checks=*',
            '-p', meson.build_root()
        ] + src_files)
endif
kaimast commented 5 years ago

@NickeZ thanks! This kinda works for me too.

I'm just curious if clang throws these errors for you too; error: unknown argument: '-pipe' [clang-diagnostic-error]

I don't think I specify this option anywhere, but maybe meson does? This seems to still have the original problem from above.

NickeZ commented 5 years ago

@kaimast Actually I don't get that issue. I'm using llvm 6.0, which version are you using? (But even with 3.9 it works for me)

ghost commented 5 years ago

If you have to manually provide the src_files this somewhat defeats the benefit of a build system.

kaimast commented 5 years ago

It's kind of necessary though. For example, I use google test and the macros generate code that is not very modern, so I need to ignore those tests when linting.

Usually you already have a variable that holds all your sourcefiles anyways, so supporting this in meson is still pretty useful.

kaimast commented 5 years ago

@NickeZ strange. I'm using 6.0 too. Are you using "ninja tidy" or some other command to run the linter?

ghost commented 5 years ago

So your Tests supposedly are in a dedicated folder or follow a certain naming based on which they should be excluded/ or the other way around where source directories would be included. (But not on a file by file basis otherwise itโ€™s pretty much just a bash script)

kaimast commented 5 years ago

Any progress on this? Or any way I could help? I still have the same problem on 0.48.2

jpakkane commented 5 years ago

The problem with this is not the actual target but the fact that you almost always want to specify a specific command line argument (specifically what tests you want to run and which header directories to use). This is in contrast to scan-build, which consists of only one and the same command that you always run. Enabling all checks just drowns you in warnings on almost all code bases. Doing this right would take possibly two new options just for this. Unfortunately we already have too many options and cramming even more in makes the whole thing unwieldy.

If you just want to replicate what CMake (as linked above) does, then NickeZ's snippet is pretty much the same thing.

jhasse commented 5 years ago

clang-tidy reads the file .clang-tidy for options, so there doesn't have to be a way to pass them.

jpakkane commented 5 years ago

You always need to pass the files you want to process, though. Sadly clang-tidy does not seem to have a flag for "process all files in compile_commands.json". Even if it did, passing all files becomes very slow even with medium sized projects. Then you'd want to use run-clang-tidy instead to get the parallelism.

jhasse commented 5 years ago

Well, for a Meson project I would expect that all C/C++ files are passed that are also being compiled.

FSMaxB commented 5 years ago

@jhasse: All C/C++-Files that are not part of a subproject more likely.

phillipjohnston commented 5 years ago

@NickeZ's solution works well, except for the fact that meson is inserting the -pipe flag, which does in fact prevent an error. Is there a way to disable that flag?

error: unknown argument: '-pipe' [clang-diagnostic-error]
kaimast commented 5 years ago

Yeah the -pipe is indeed the major issue for me too.

I think we need a proper Meson module that invokes the linter and cleans up the compile commands beforehand. That is essentially what I am doing now except I use a bash script. See below what I do

Meson part

clangtidy = find_program('clang-tidy', required: false)

if clangtidy.found()
    run_target(
        'tidy',
        command: [
            'scripts/clang-tidy.sh',
            clangtidy.path(),
            meson.source_root(),
            meson.build_root()
        ] + <CPP FILES>,
    depends: <PROJECT NAME>)
endif

Bash script

#! /bin/bash

# Pick any flags you like here
CHECKS='-hicpp-*,-readability-implicit-bool-conversion,-cppcoreguidelines-*,-clang-diagnostic*,-llvm-*,-bugprone-*,-modernize-*,-misc-*'

BIN=$1 && shift
PROJECT_ROOT=$1 && shift
MESON_ROOT=$1 && shift

# Execute in a different directory to ensure we don't mess with the meson config
TIDY_DIR=${PROJECT_ROOT}/build-tidy

mkdir -p ${TIDY_DIR}
cp  ${MESON_ROOT}/compile_commands.json ${TIDY_DIR}

# Replace meson commands clang does not understand
sed -i 's/-pipe//g' ${TIDY_DIR}/compile_commands.json

echo "Running clang checks: ${CHECKS}"
$BIN -checks=${CHECKS} -warnings-as-errors=* -p ${TIDY_DIR} $@

Ideally the meson code should look something like this imho

clang_tidy = import('clang_tidy')

if clang_tidy.found():
       clang_tidy.run_on(<PROJECT NAME>)
endif
toanju commented 5 years ago

Adding some observations. https://bugs.llvm.org/show_bug.cgi?id=37315 seems to be related to the -pipe issues since actually removing -Xclang works as well. Furthermore setting the option b_colorout=never which still sets -Xclang works as well. Is -Xclang really necessary here?

mhhollomon commented 5 years ago

@kaimast - thank you for the inspiration. I wound up doing things a bit differently. I wanted everything in the build directory, I needed to be able to pass other flags to clang-tidy, and I wanted something fairly generic. So, the bash script I came up with is:

#!/usr/bin/bash

BIN=$1 && shift
BUILD_ROOT=$1 && shift

# Execute in a different directory to ensure we don't mess with the meson config
TIDY_DIR="${BUILD_ROOT}/:clang-tidy"

mkdir -p ${TIDY_DIR}
cp  ${BUILD_ROOT}/compile_commands.json ${TIDY_DIR}

# Replace meson commands clang does not understand
sed -i 's/-pipe//g' ${TIDY_DIR}/compile_commands.json

$BIN -p ${TIDY_DIR} $*
martin-ejdestig commented 5 years ago

There is also https://bugs.llvm.org/show_bug.cgi?id=37281 to consider.

Also, none of the run_target() examples above parallelizes. Would be nice if Meson took care of that.

martin-ejdestig commented 5 years ago

FYI... forgot that I filed a clang-tidy bug for -the pipe and clang-diagnostic error but found it now again when searching for the -pipe problem in bugzilla. Can be found here:

https://bugs.llvm.org/show_bug.cgi?id=37315

XVilka commented 4 years ago

Seems there is exactly zero reaction from Clang developers on this bug. Maybe it makes sense to write a message to their mailing list instead? I think they ignore most of the bugzilla bugs (judging from my own experience).

martin-ejdestig commented 4 years ago

The problem with this is not the actual target but the fact that you almost always want to specify a specific command line argument (specifically what tests you want to run and which header directories to use). This is in contrast to scan-build, which consists of only one and the same command that you always run. Enabling all checks just drowns you in warnings on almost all code bases. Doing this right would take possibly two new options just for this. Unfortunately we already have too many options and cramming even more in makes the whole thing unwieldy

@jpakkane What two options are you thinking about?

I think everything should be possible to configure in .clang-tidy. What checks to enable definitely is. The header filter is a bit problematic due to https://bugs.llvm.org/show_bug.cgi?id=37281, but could say "set header filter in .clang-tidy like this for it to work with Meson" and/or extract and modify it if it looks like it has paths relative to the source root (e.g. ^src/ -> ^../src/).

If a clang-tidy target should be available or not could be handled just like you do in 1fca654055d3502d2db9c5aad66a522beaa1df19 . Only add it if there is a .clang-tidy in the root.

The only thing I can come to think of that I would want configurable in Meson itself is some way to say "exclude these sources". Not running clang-tidy on source in the build directory and subprojects by default would make it so that is not even needed in most cases. A regex option that defaults to match source in the subprojects and build folder? (In CMake they work around this by placing a mostly empty dummy .clang-tidy in folder of source that should not be checked. See e.g. https://gitlab.kitware.com/cmake/cmake/commit/b13bc8659f87567b1b091806d42f5023b2a6b48b . Ugh!)

jpakkane commented 4 years ago

If a clang-tidy target should be available or not could be handled just like you do in 1fca654 . Only add it if there is a .clang-tidy in the root.

I did not know clang-tidy had this functionality (it's not particularly prominent in the docs). Adding a clang-tidy target that behaves just like the clang-format target (i.e. run it on all sources using the given .clang-tidy file) is something we can do fairly easily.

stderr-enst commented 4 years ago

Just a +1 on having a clang-tidy target as well - would be really neat if it worked like clang-format and you can just throw it at your whole project. With the script/run_target approach above you kind of want a global source-file list of your project, which would be against meson principles I guess. :)

nhooyr commented 4 years ago

Can confirm I'm still getting the -pipe argument undefined error.

Is it possible we could just remove -Xclang from the default args like @toanju mentioned?

jhasse commented 4 years ago

Shouldn't the new clang-tidy target only include source files I use in my build? This way it checks way too much stuff. It even ignores .gitignore and therefore checks cache files from my language server.

It also doesn't use Ninja jobs but implements its own parallelism.

Ctrl+C doesn't stop the job from running.

This is not what I had in mind and doesn't even match CMake's support. Please re-open.

jpakkane commented 4 years ago

Does #6083 fix things for you?

jhasse commented 4 years ago

Not really, as I could have ran run-clang-tidy.py myself before (also it isn't in the PATH on most distros, if I'm not mistaken, so there's still some tinkering needed). It also uses its own parallelism and doesn't track header-dependencies.

mattyclarkson commented 4 years ago

By default it shouldn't run on subprojects. I get huge amounts of false positives. As @jhasse said, respecting .gitignore would be sensible.

severen commented 4 years ago

I can confirm that on Arch run-clang-tidy.py is not in $PATH (instead it is in /usr/share/clang). I agree with @jhasse that this issue should be reopened. I don't think the current support for clang-tidy is sufficient or what the average person would expect.

XVilka commented 3 years ago

Ignoring some files like for clang-format target would be awesome too (apart from .gitignore), e.g. for 3rd-party submodules, copied code, etc. See also https://github.com/mesonbuild/meson/issues/7271

jlevon commented 8 months ago

Does anyone have a workaround for the way meson has chosen to run clang-tidy on every source file individually (including headers!) ?

marek22k commented 3 months ago

How can I now run clang-tidy with meson? Is there a documentation about it?

dcbaker commented 3 months ago

@marek22k

meson setup builddir
ninja -C builddir clang-tidy

You can also use meson compile instead of ninja, or msbuild if you're using the vs backend, but I don't remember the syntax off the top of my head

marek22k commented 3 months ago

Thank you. To include clang-tidy in meson.build: What is the current syntax?

Still the following as in some comments?

clangtidy = find_program('clang-tidy', required: false)

if clangtidy.found()
    run_target(
        'tidy',
        command: [
            'scripts/clang-tidy.sh',
            clangtidy.path(),
            meson.source_root(),
            meson.build_root()
        ] + <CPP FILES>,
    depends: <PROJECT NAME>)
endif
dcbaker commented 3 months ago

Meson will automatically generate a clang-tidy target for you if you have a .clang-tidy configuration at the root of your project and a you don't define your own target clang-tidy. I've never needed more control than that, so I'm not sure what you'd need to do to make it work.

marek22k commented 3 months ago

Thanks for the answer! Now it works. Is it also possible to run the whole thing without ninja. meson compile -C build clang-tidy does not work.

Edit: Ninja seems to execute meson --internal clangtidy . build. But is it "correct" if I execute --internal manually?

eli-schwartz commented 3 months ago

Is it also possible to run the whole thing without ninja. meson compile -C build clang-tidy does not work.

The meson compile command will always run ninja on your behalf after forwarding arguments.

It exists to abstract away the differences between different backends, i.e. you can use -- and document in README.md -- the same command to build with ninja, or to build with Visual Studio or XCode (assuming you passed the backend=vs or backend=xcode option at setup time).

Edit: Ninja seems to execute meson --internal clangtidy . build. But is it "correct" if I execute --internal manually?

It is possible that meson will change the command name, or argument passing conventions. An --internal command is only guaranteed to work using the same version of meson that generated the clang-tidy rule into build.ninja.

We cannot make any promises regarding whether or not we will, sometime in the future, decide on a need to refactor the internals.