= CppCheck Static Analysis using Compile Commands
:toc:
:toc-placement!:
toc::[]
# Introduction
This example shows how to call the
http://cppcheck.sourceforge.net/[CppCheck] tool to do static analysis.
This shows how to use projects and a compile database.
Projects are available from cppcheck v1.77
It includes code to
* Find the cppcheck binary
* Generate an overall `make cppcheck-analysis` target to do static
analysis on all sub-projects.
The files included in this example are:
```
$ tree
.
├── cmake
│ └── modules
│ └── FindCppCheck.cmake
├── CMakeLists.txt
├── subproject1
│ ├── CMakeLists.txt
│ └── main1.cpp
└── subproject2
├── CMakeLists.txt
└── main2.cpp
```
* link:CMakeLists.txt[] - Top level CMakeLists.txt
* link:cmake/modules/FindCppCheck.cmake[] - A custom package module to find CppCheck
* link:subproject1/CMakeLists.txt[] - CMake commands for subproject 1
* link:subproject1/main1.cpp[] - source for a subproject with no errors
* link:subproject2/CMakeLists.txt[] - CMake commands for subproject 2
* link:subproject2/main2.cpp[] - source for a subproject that includes errors
# Requirements
To run this example you must have CppCheck of at least v1.77 installed. This is not
available by default on Ubuntu but can be compiled using the following command.
[source,bash]
----
$ wget https://github.com/danmar/cppcheck/archive/1.79.tar.gz \
&& tar xvf 1.79.tar.gz \
&& cd cppcheck-1.79 \
&& mkdir build \
&& cd build \
&& cmake .. \
&& sudo make install
----
# Concepts
## Adding Custom Package Modules
As with the previous example I use a custom module to find CppCheck. This version is slightly different to the previous one and
will automatically add a `make cppcheck-analysis` target.
[source,cmake]
----
if(CPPCHECK_FOUND)
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/analysis/cppcheck)
add_custom_target(cppcheck-analysis
COMMAND ${CPPCHECK_COMMAND})
message("cppcheck found. Use cppccheck-analysis targets to run it")
else()
message("cppcheck not found. No cppccheck-analysis targets")
endif()
----
The variables available have also changed. For full details on the commands see the `FindCppCheck.cmake` module. Below are a subset of the available options:
### Suppressions
Adding a suppression file called `.cppcheck-suppressions` which must be in your +CMAKE_SOURCE_DIR+
### Error Exitcode
When cppcheck finds an error it can cause it to exit with a specific error. In this
example, by default, it will exit with `1`. To change this you can set the
+CPPCHECK_ERROR_EXITCODE_ARG+ argument when running CMake.
### Exitcode suppressions
Sometimes you wish to display an error in the log, but to not have that error cause a failed build. To do this you can create a file `.cppcheck_exitcode_suppressions` and add suppressions to it. This file must be in your +CMAKE_SOURCE_DIR+
### CppCheck arguments
The default enabled checks are `--enabled=warning`. To change this you can override the `CPPCHECK_CHECK_ARGS` variable before calling `find(cppcheck)`.
### Excluding files / folders
Many projects include some vendored 3rd party code. To exclude this from you check you can create a list `CPPCHECK_EXCLUDES` before calling the find module. This will add all files and folders in the list into the list of excluded folders.
### CppCheck build dir
In this example, we set +CPPCHECK_BUILD_DIR_ARG+, to `${PROJECT_BINARY_DIR}/analysis/cppcheck`. This will output details of the build to this folder and can be used to
increase the speed of rechecks if a file hasn't changed
## Compile Database
CMake allows you to export all https://cmake.org/cmake/help/v3.5/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html[compile commands]
that are used to build the project into a file called `compile_commands.json`
This can be done by setting the +CMAKE_EXPORT_COMPILE_COMMANDS+ variable to +ON+
as below:
[source,cmake]
----
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
----
The JSON file will look like:
[source,json]
----
[
{
"directory": "/home/user/development/project",
"command": "/usr/bin/c++ ... -c ../foo/foo.cc",
"file": "../foo/foo.cc"
},
...
{
"directory": "/home/user/development/project",
"command": "/usr/bin/c++ ... -c ../foo/bar.cc",
"file": "../foo/bar.cc"
}
]
----
[NOTE]
====
This is only available for the `Makefile` and `ninja` generators.
====
## CppCheck Projects
Starting with CppCheck v1.77, you can pass the `--project` flag pointing to the
compile database. This will cause CppCheck to run on al your cpp files using the same
include directories and compiler flags as your normal build.
[source,bash]
----
cppcheck --project=compile_comands.json
----
This will check all files in your project and sub-projects. There will be no analysis target per sub-project as with our previous example.
# Building the example
[source,bash]
----
$ mkdir build
$ cd build/
$ cmake ..
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found CPPCHECK: /usr/local/bin/cppcheck
cppcheck found. Use cppccheck-analysis targets to run it
-- Configuring done
-- Generating done
-- Build files have been written to: /data/code/04-static-analysis/cppcheck-compile-commands/build
$ make cppcheck-analysis
Scanning dependencies of target cppcheck-analysis
[/data/code/04-static-analysis/cppcheck-compile-commands/subproject2/main2.cpp:7]: (error) Array 'tmp[10]' accessed at index 11, which is out of bounds.
make[3]: *** [CMakeFiles/cppcheck-analysis] Error 1
make[2]: *** [CMakeFiles/cppcheck-analysis.dir/all] Error 2
make[1]: *** [CMakeFiles/cppcheck-analysis.dir/rule] Error 2
make: *** [cppcheck-analysis] Error 2