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.
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.
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.
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.
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.
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.