add new cppcheck example with updated dockerfiles

This commit is contained in:
Thom Troy 2017-07-02 23:20:41 +01:00
parent 59159c4c38
commit ef0d61aa0e
16 changed files with 395 additions and 5 deletions

View File

@ -0,0 +1,17 @@
cmake_minimum_required (VERSION 3.0)
project(cppcheck_analysis)
# Have cmake create a compile database
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Add a custom CMake Modules directory
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules
${CMAKE_MODULE_PATH})
# find the cppcheck binary
find_package(CppCheck)
# Add sub directories
add_subdirectory(subproject1)
add_subdirectory(subproject2)

View File

@ -0,0 +1,183 @@
= 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/main.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 pervious 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.
### 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

View File

@ -0,0 +1,94 @@
# Locate cppcheck
#
# This module defines
# CPPCHECK_BIN, where to find cppcheck
#
# To help find the binary you can set CPPCHECK_ROOT_DIR to search a custom path
# Exported argumets include
# CPPCHECK_FOUND, if false, do not try to link to cppcheck --- if (CPPCHECK_FOUND)
#
# CPPCHECK_THREADS_ARG - Number of threads to use (e.g. -j 3)
# CPPCHECK_PROJECT_ARG - The project to use (compile_comands.json)
# CPPCHECK_BUILD_DIR_ARG - The build output directory
# CPPCHECK_ERROR_EXITCODE_ARG - The exit code if an error is found
# CPPCHECK_SUPPRESSIONS - A suppressiosn file to use
# CPPCHECK_CHECKS_ARGS - The checks to run
# CPPCHECK_OTHER_ARGS - Any other arguments
# CPPCHECK_COMMAND - The full command to run the default cppcheck configuration
#
# find the cppcheck binary
# if custom path check there first
if(CPPCHECK_ROOT_DIR)
find_program(CPPCHECK_BIN
NAMES
cppcheck
PATHS
"${CPPCHECK_ROOT_DIR}"
NO_DEFAULT_PATH)
endif()
find_program(CPPCHECK_BIN NAMES cppcheck)
if(CPPCHECK_BIN)
execute_process(COMMAND ${CPPCHECK_BIN} --version
OUTPUT_VARIABLE CPPCHECK_VERSION
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CPPCHECK_THREADS_ARG "-j4" CACHE STRING "The number of threads to use")
set(CPPCHECK_PROJECT_ARG "--project=${PROJECT_BINARY_DIR}/compile_commands.json")
set(CPPCHECK_BUILD_DIR_ARG "--cppcheck-build-dir=${PROJECT_BINARY_DIR}/analysis/cppcheck" CACHE STRING "The build directory to use")
if(EXISTS "${CMAKE_SOURCE_DIR}/.cppcheck_suppressions")
set(CPPCHECK_SUPPRESSIONS "--suppressions-list=${CMAKE_SOURCE_DIR}/.cppcheck_suppressions" CACHE STRING "The suppressions file to use")
else()
set(CPPCHECK_SUPPRESSIONS "" CACHE STRING "The suppressions file to use")
endif()
set(CPPCHECK_ERROR_EXITCODE_ARG "--error-exitcode=1" CACHE STRING "The exitcode to use if an error is found")
set(CPPCHECK_CHECKS_ARGS "" CACHE STRING "Arguments for the checks to run")
set(CPPCHECK_OTHER_ARGS "--quiet" CACHE STRING "Other arguments")
set(CPPCHECK_ALL_ARGS
${CPPCHECK_THREADS_ARG}
${CPPCHECK_PROJECT_ARG}
${CPPCHECK_BUILD_DIR_ARG}
${CPPCHECK_ERROR_EXITCODE_ARG}
${CPPCHECK_SUPPRESSIONS}
${CPPCHECK_CHECKS_ARGS}
${CPPCHECK_OTHER_ARGS}
)
set(CPPCHECK_COMMAND
${CPPCHECK_BIN}
${CPPCHECK_ALL_ARGS}
)
#TODO add version check on the binary
endif()
# handle the QUIETLY and REQUIRED arguments and set YAMLCPP_FOUND to TRUE if all listed variables are TRUE
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
CPPCHECK
DEFAULT_MSG
CPPCHECK_BIN)
mark_as_advanced(
CPPCHECK_BIN
CPPCHECK_THREADS_ARG
CPPCHECK_PROJECT_ARG
CPPCHECK_BUILD_DIR_ARG
CPPCHECK_ERROR_EXITCODE_ARG
CPPCHECK_SUPPRESSIONS
CPPCHECK_CHECKS_ARGS
CPPCHECK_OTHER_ARGS)
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()

View File

@ -0,0 +1,2 @@
#!/bin/bash
mkdir -p build && cd build && cmake -DCPPCHECK_ERROR_EXITCODE_ARG="" .. && make cppcheck-analysis

View File

@ -0,0 +1,10 @@
# Set the project name
project (subproject1)
# Create a sources variable with a link to all cpp files to compile
set(SOURCES
main1.cpp
)
# Add an executable with the above sources
add_executable(${PROJECT_NAME} ${SOURCES})

View File

@ -0,0 +1,7 @@
#include <iostream>
int main(int argc, char *argv[])
{
std::cout << "Hello Main1!" << std::endl;
return 0;
}

View File

@ -0,0 +1,10 @@
# Set the project name
project (subproject2)
# Create a sources variable with a link to all cpp files to compile
set(SOURCES
main2.cpp
)
# Add an executable with the above sources
add_executable(${PROJECT_NAME} ${SOURCES})

View File

@ -0,0 +1,9 @@
#include <iostream>
int main(int argc, char *argv[])
{
std::cout << "Hello Main2!" << std::endl;
char tmp[10];
tmp[11] = 's';
return 0;
}

View File

@ -8,6 +8,7 @@ toc::[]
This example shows how to call the
http://cppcheck.sourceforge.net/[CppCheck] tool to do static analysis.
This shows how to make an analysis target for each project in your repository.
It includes code to
@ -318,6 +319,8 @@ make: *** [analysis] Error 2
# Extra Notes
## Multiple Folders
If you have a multiple folders levels, where one folder just points to
sub folders, such as below:
@ -343,3 +346,20 @@ add_subdirectory(project1)
add_subdirectory(project2)
set(ALL_ANALYSIS_TARGETS "${ALL_ANALYSIS_TARGETS}" PARENT_SCOPE)
----
## Include Directories
In the +add_analysis+ macro in `analysis.cmake` we extract the +INCLUDE_DIRECTORIES+ from the
target and add them to the call to cppcheck.
[source,cmake]
----
get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
foreach(dir ${dirs})
LIST(APPEND cppcheck_includes "-I${dir}")
endforeach()
----
This works for basic examples but if you use some CMake features such as
generator expressions this will not add the include directory.

View File

@ -0,0 +1,2 @@
#!/bin/bash
mkdir -p build && cd build && cmake .. && make analysis

View File

@ -4,7 +4,7 @@
# variables and give them sudo access
#
ret=false
output=`getent passwd devuser 2&>1`
output=`getent passwd devuser 2>&1`
result=$?
if [ $result -ne 0 ] ; then
echo "Creating devuser"
@ -23,4 +23,4 @@ if [ $result -ne 0 ] ; then
echo "%sudo ALL=NOPASSWD: ALL" >> /etc/sudoers
fi
exec /bin/su - devuser -c "$@"
exec gosu devuser "$@"

View File

@ -7,7 +7,6 @@ RUN apt-get update && apt-get install -y build-essential \
libboost-all-dev \
libprotobuf-dev \
protobuf-compiler \
cppcheck \
clang-3.6 \
ninja-build \
wget \
@ -16,6 +15,11 @@ RUN apt-get update && apt-get install -y build-essential \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& apt-get autoremove -y
RUN cd /usr/local/src \
&& wget https://github.com/tianon/gosu/releases/download/1.10/gosu-amd64 \
&& mv gosu-amd64 /usr/local/bin/gosu \
&& chmod +x /usr/local/bin/gosu
ADD setup.sh /setup.sh
RUN chmod +x /setup.sh
@ -29,6 +33,18 @@ RUN cd /usr/local/src \
&& cd .. \
&& rm -rf cmake*
# cppcheck
RUN cd /usr/local/src \
&& 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 .. \
&& make install \
&& cd ../.. && rm -rf cppcheck*
CMD ["/bin/bash"]
ENTRYPOINT ["/setup.sh"]

View File

@ -28,6 +28,10 @@ RUN cd /usr/local/src \
&& rm -rf tini-* \
&& rm -rf v0.9.0.tar.gz
RUN cd /usr/local/src \
&& wget https://github.com/tianon/gosu/releases/download/1.10/gosu-amd64 \
&& mv gosu-amd64 /usr/local/bin/gosu \
&& chmod +x /usr/local/bin/gosu
ADD setup.sh /setup.sh
RUN chmod +x /setup.sh

View File

@ -1,4 +1,4 @@
# Container for building and testing cmake-examples with default cmake v2.8.12.2
# Container for building and testing cmake-examples with default cmake v3.5.1
FROM ubuntu:16.04
MAINTAINER Thom Troy
@ -8,7 +8,6 @@ RUN apt-get update && apt-get install -y build-essential \
libboost-all-dev \
libprotobuf-dev \
protobuf-compiler \
cppcheck \
clang-3.6 \
ninja-build \
wget \
@ -16,6 +15,22 @@ RUN apt-get update && apt-get install -y build-essential \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# cppcheck
RUN cd /usr/local/src \
&& 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 .. \
&& make install \
&& cd ../.. && rm -rf cppcheck*
RUN cd /usr/local/src \
&& wget https://github.com/tianon/gosu/releases/download/1.10/gosu-amd64 \
&& mv gosu-amd64 /usr/local/bin/gosu \
&& chmod +x /usr/local/bin/gosu
ADD setup.sh /setup.sh
RUN chmod +x /setup.sh

View File

@ -29,6 +29,7 @@ dirs=(./01-basic/A-hello-cmake \
./03-code-generation/protobuf \
./03-code-generation/configure-files \
./04-static-analysis/cppcheck \
./04-static-analysis/cppcheck \
./05-unit-testing/boost \
./06-installer/deb \
)