Static analyzers as seperate targets


I currently use clang-tidy, cppcheck and incldue-what-you-use cmake integrations, but there are a few problems:

  1. It’s somewhat annoying when just prototyping because of all the output
  2. It’s slow as hell when recompiling
  3. It will need a recompilation of everything to produce the errors. So building twice will not reprint previous warnings from the tool. A clean is needed (which is especially annoying as I use FetchContent and don’t need all deps to be rebuilt again)

I thought about it and actually I would much more prefer separate targets for static analyzers. So once I’m happy with my code, everything compiles, I can run static analyzers.
This would be especially nice with CMakePresets:

            "name": "analyse",
            "inherits": "build-ninja-clang-debug",
            "configurePreset": "default",
            "targets": [

The problem is that the tools need a list of source files or I believe, in case of include what you use, even a complete compilation.
The built in variables like CMAKE_CXX_CPPCHECK make this handy, as they feed the source files to the tools during build.

So what I currently do is controlling CMAKE_CXX_CPPCHECK and others with variables. And usually I have them OFF, but then make them ON. Which is better, but still not nice:

  • It recompiles everything including dependencies
  • It still not produces the same output on multiple runs → Once it is compiled, warnings will obviously not show up again
  • It might be uncessary for things like clang-tidy and cppcheck to actually compile. I think these work without it

Any ideas how I could do it better? As I said having some targets, which run the tools on all source files and header files (and just them, not dependencies) and always produce the same warnings no matter how often you run the tool without changing anything would be ideal.

1 Like

You can do different CMake configurations in different build directories.

I don’t quite though see how that fixes my issue.

The output of analyzers will still show only for the files I recompile. So in order to get output I usually have to clean and then everything will be recompiled.

Anyway, because compilation is required, having a specific target will not solve the problem.

Having different build directories enable you to work in an optimum environment (i.e. no useless recompilation, speed) for the development and a dedicated environment for the various checks. For these environments, a clean + compilation make sense for full check…

How would I solve the problem with the dependencies? I think this would really be the missing piece for this to work.
I use FetchContent a lot and always building all dependencies is not good.

It would be nice to have a separate target that only executed the rules for static analysis (where this is a separate tool invocation from invoking the compiler for real). For comparison, Qt has this for its QML CMake API. For each QML module (aka target), you also get a ${target}_qmllint target, and a global all_qmllint target which depends on all the individual ${target}_qmllint targets.

Regarding how to do a partial clean, what I tend to do is delete the part of the build tree that corresponds to the targets I want to rebuild. I then re-run CMake which ensures everything can build again and the next build will build just the bits I removed instead of everything. This relies on having your build structured well, but hopefully if you’re using FetchContent to bring in your dependencies, this is already likely to be the case (since your dependencies will be in their own separate directories automatically).

1 Like