Static checks with CMake/CDash (iwyu, clang-tidy, lwyu, cpplint and cppcheck)
One of the great features of CMake/CTest/CDash is the ability to setup useful but rarely used tools to automatically run on a project and report the results to a web page. For example, valgrind is a great tool to run dynamic checks on C/C++ code catching tough to find memory errors. Since valgrind is slow to run, developers often times only run it when they suspect a problem in the code. CMake/CTest/CDash have supported running nightly valgrind tests for years making sure an entire code base is checked each night. Individual developers on a project don’t even have to know how to run valgrind, they just have to look at the results on a web page. Recently, several open source tools have been developed to perform various static or compile time analysis of C++ code. These tools are able to ingest the C++ source code and discover various defects in the source code without running it. As of CMake 3.9, five different tools of this type are supported directly by CMake/CTest:
- iwyu (include what you use) : This parses C++ source files and determines the exact include files required to compile that file, no more no less.
- clang-tidy: is a clang-based C++ “linter” tool. Its purpose is to provide an extensible framework for diagnosing and fixing typical programming errors, like style violations, interface misuse, or bugs that can be deduced via static analysis. clang-tidy is modular and provides a convenient interface for writing new checks.
- lwyu (link what you use): This is a built in CMake feature that uses options of ld and ldd to print out if executables link more libraries than they actually require.
- cpplint: a C++ style checker following Google’s C++ style guide.
- cppcheck: a static analysis tool for C/C++ code. Cppcheck primarily detects the types of bugs that the compilers normally do not detect. The goal is to detect only real errors in the code (i.e. have zero false positives).
All of these tools except for lwyu basically shadow the compiler. By shadowing, it means that for each compiler invocation in a build process CMake will run both the analysis tool and the compiler command line. This allows for the entire project to be built including code wrappers and anything else done during the build, but at the same time checking the code base with the static analysis tool. This can then be used to send the analysis output to a CDash dashboard. By integrating these tools into CMake, any CMake based project is able to take advantage of these tools. This integration with the build system provides significant benefits to larger code bases where a simple checker-tool *.cxx command is not feasible.
These tools are all enabled using target properties. Although these properties can be set on individual targets by modifying CMakeLists.txt code, a better approach is to initialize the properties by using a global CMake cache variable. This allows you to run these tools on a CMake project without having to modify the projects CMakeLists.txt files. Those variables can be set on the cmake command line using -Dvar=value. The variables could also be set from a cache inside a ctest -S style script which are used for setting up CDash builds.
The target properties and the global initialization variables for the static checking tools available at the time of writing this blog are as follows:
- <LANG>_CLANG_TIDY can be set with cache variable CMAKE_<LANG>_CLANG_TIDY
- <LANG>_CPPCHECK can be set with cache variable CMAKE_<LANG>_CPPCHECK
- <LANG>_CPPLINT can be set with cache variable CMAKE_<LANG>_CPPLINT
- <LANG>_INCLUDE_WHAT_YOU_USE can be set with cache variable CMAKE_<LANG>_INCLUDE_WHAT_YOU_USE
- LINK_WHAT_YOU_USE can be set with cache variable CMAKE_LINK_WHAT_YOU_USE
<LANG> denotes the language that is being checked, and is usually either C (for C code) or CXX (for C++ code). All of the <LANG> properties are expected to contain CMake semi-colon separated list containing the checker tool command line to run with any options. At a minimum only the path to the tool itself is required. CMake will add the options to send the source file and other required options to the tool. However, if extra options are required then they can be provided. Examples are a good way to show how to use these options, so in the next section an example will be given for each tool.
Clang Tidy
cmake “-DCMAKE_CXX_CLANG_TIDY=/usr/bin/clang-tidy-3.9;-checks=*” ../path/to/source
This will run /usr/bin/clang-tidy-3.9 -checks=* on each of the C++ source files in the project being built.
CppCheck
cmake “-DCMAKE_CXX_CPPCHECK=/usr/bin/cppcheck;–std=c++11” ../path/to/source
This will run /usr/bin/cppcheck;–std=c++11″ –source=/path/to/source/file.cxx on each c++ file in the project being built.
CppLint
cmake “-DCMAKE_CXX_CPPLINT=/usr/local/bin/cpplint;–linelength=79” ..
This will run /usr/local/bin/cpplint –linelength=79 on each c++ file in the project being built.
IWYU
cmake “-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=/usr/bin/iwyu;–transitive_includes_only” ..
This will run /usr/bin/iwyu –transitive_includes_only on each c++ file in the project being built.
LWYU
cmake -DCMAKE_LINK_WHAT_YOU_USE=TRUE ..
This will modify the flags to ld to show any libraries being linked into targets that are not contributing symbols to the target being linked.
The output of these tools will be prefixed with Warning: so that CDash will automatically recognize it as a warning. The warnings will show up in the build column in CMake as each of the commands is run during the build.
In summary, CMake provides easy access to many popular compile time code analyzers.
Nice article, I’ve never thought of doing this. I’ll have to try it out. Thanks.
What would be the best way to have different Clang Tidy checks on different targets without hard coding the path to clang-tidy as the first argument in a CXX_CLANG_TIDY target property. Use a custom variable for clang_tidy path?
I suppose you could read the property from the target then edit the value and reset it. That way you could just modify the arguments and not the path to clang-tidy.
What a shame, I love having static tools built into CMake. It’s AWESOME! However, I just wish it was as easy as simply assign properties for a given target without having to worry about the path of Clang Tidy in the CMakeLists.txt and simply assign Clang-tidy during config time.
Is there platform/generator restrictions ? I cannot manage to make cppcheck works on a ‘Visual Studio 15 2017’ generated project.
Just a question, will this respect .clang-tidy config files that many projects have?
Also, any plans for clazy support? (https://github.com/KDE/clazy)
Thanks for this really nice article. It’s nearly impossible to find this information in the documentation without knowing what to search. One remark, though, with this approach the clang-tidy results are actually reported as build errors / warnings in cdash. It would be much more useful if they were reported as another section, such as “static analysis”, the same way valgrind results are in a “dynamic analysis” section. Is there a way to achieve this ?
Not currently. This is a good idea though, and you’re not the first one to suggest it. I recorded a GitHub issue for CDash to make sure it doesn’t get lost in the shuffle: https://github.com/Kitware/CDash/issues/768
Unfortunately, there seem no way to add dynamic file specific context information in the invocation command line. However, this would be very useful if the output should be redirected into specific file. See also the discussion at https://discourse.cmake.org/t/parallel-cppcheck-invocation-corrupts-common-output-file/3071