In my previous post, I gave an introduction to the static analysis tool Cppcheck. In this post, I'm going to show how to integrate Cppcheck into the CMake build system to generate a cppcheck-analysis
target for the make / ninja build systems.
CMake
CMake is a meta build system that can be used to generate files for various build systems, including make and ninja. I would recommend to have a basic knowledge of how CMake works before reading this post. For an introduction to CMake, you can read my CMake tutorial.
FindCppcheck
In CMake you can find dependencies using a find(ModuleName)
command. This commands looks for a file FindModuleName.cmake
and will parse this file to find the dependency and add it's requirements. For Cppcheck, you can add a FindCppcheck.cmake
in a folder cmake/modules
off the root of your project.
You should then add the following to your root CMakeLists.txt
to tell it where to search for modules:
12# Add a custom CMake Modules directory
set
(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}
/
cmake
/
modules ${CMAKE_MODULE_PATH})
The code for the FindCppcheck.cmake is below
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128# Locate cppcheck and add a cppcheck-analysis target
#
# 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 (default -j4)
# CPPCHECK_PROJECT_ARG - The project to use (compile_comands.json)
# CPPCHECK_BUILD_DIR_ARG - The build output directory (default - ${PROJECT_BINARY_DIR}/analysis/cppcheck)
# CPPCHECK_ERROR_EXITCODE_ARG - The exit code if an error is found (default --error-exitcode=1)
# CPPCHECK_SUPPRESSIONS - A suppressiosn file to use (defaults to .cppcheck_suppressions)
# CPPCHECK_EXITCODE_SUPPRESSIONS - An exitcode suppressions file to use (defaults to .cppcheck_exitcode_suppressions)
# CPPCHECK_CHECKS_ARGS - The checks to run (defaults to --enable=warning)
# CPPCHECK_OTHER_ARGS - Any other arguments
# CPPCHECK_COMMAND - The full command to run the default cppcheck configuration
# CPPCHECK_EXCLUDES - A list of files or folders to exclude from the scan. Must be the full path
#
# if CPPCHECK_XML_OUTPUT is set to an output file name before calling this. CppCheck will create an xml file with that name
# 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"
)
# Don't show thise errors
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()
# Show these errors but don't fail the build
# These are mainly going to be from the "warning" category that is enabled by default later
if
(EXISTS
"${CMAKE_SOURCE_DIR}/.cppcheck_exitcode_suppressions"
)
set
(CPPCHECK_EXITCODE_SUPPRESSIONS
"--exitcode-suppressions=${CMAKE_SOURCE_DIR}/.cppcheck_exitcode_suppressions"
CACHE STRING
"The exitcode suppressions file to use"
)
else
()
set
(CPPCHECK_EXITCODE_SUPPRESSIONS "
" CACHE STRING "
The exitcode 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
"--enable=warning"
CACHE STRING
"Arguments for the checks to run"
)
set
(CPPCHECK_OTHER_ARGS "
" CACHE STRING "
Other arguments")
set
(_CPPCHECK_EXCLUDES)
## set exclude files and folders
foreach(ex ${CPPCHECK_EXCLUDES})
list
(APPEND _CPPCHECK_EXCLUDES
"-i${ex}"
)
endforeach(ex)
set
(CPPCHECK_ALL_ARGS
${CPPCHECK_THREADS_ARG}
${CPPCHECK_PROJECT_ARG}
${CPPCHECK_BUILD_DIR_ARG}
${CPPCHECK_ERROR_EXITCODE_ARG}
${CPPCHECK_SUPPRESSIONS}
${CPPCHECK_EXITCODE_SUPPRESSIONS}
${CPPCHECK_CHECKS_ARGS}
${CPPCHECK_OTHER_ARGS}
${_CPPCHECK_EXCLUDES}
)
# run cppcheck command with optional xml output for CI system
if
(NOT CPPCHECK_XML_OUTPUT)
set
(CPPCHECK_COMMAND
${CPPCHECK_BIN}
${CPPCHECK_ALL_ARGS}
)
else
()
set
(CPPCHECK_COMMAND
${CPPCHECK_BIN}
${CPPCHECK_ALL_ARGS}
-
-
xml
-
-
xml
-
version
=
2
2
> ${CPPCHECK_XML_OUTPUT})
endif()
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_EXITCODE_SUPPRESSIONS
CPPCHECK_CHECKS_ARGS
CPPCHECK_EXCLUDES
CPPCHECK_OTHER_ARGS)
# If found add a cppcheck-analysis target
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()
This file includes comments to describe the various sections, however, I'll include some common settings that you may wish to override before calling it.
Checks to enable
To change the level of enabled checks you can use the CPPCHECK_CHECKS_ARGS
variable. By default this is set to --enable=warning
. You can override it to any of the supported check levels in Cppcheck.
Suppression
As discussed in my previous post you can suppress some warnings in Cppcheck using a suppression file. By default the above script will look for a suppressions file in .cppcheck_suppressions
. If it exists it will add this to the command when calling cppcheck. To override this file you can change the CPPCHECK_SUPPRESSIONS
argument.
1`
set
(CPPCHECK_SUPPRESSIONS ${PROJECT_ROOT_DIR}
/
my_suppressions)
exitcode
When Cppcheck finds errors you can tell it what error code to use when exiting the program. With the above script the default exitcode is 1
. To override this you can use the CPPCHECK_ERROR_EXITCODE_ARG
. To set it to use the cppcheck default
1set
(CPPCHECK_ERROR_EXITCODE_ARG "")
XML output
Cppcheck supports outputting the results via XML. This can be helpful for CI systems to read the output and add a report. To enable XML output you can set the CPPCHECK_XML_OUTPUT
variable to the file you want to use:
1set
(CPPCHECK_XML_OUTPUT
"${PROJECT_BINARY_DIR}/analysis/cppcheck/cppcheck_analysis.xml"
)
Excluding files
If you have vendored third party code in your repository then you may not want to include that in your Cppcheck analysis. To exclude a file or folder you can create a list CPPCHECK_EXCLUDES
1234set
(CPPCHECK_EXCLUDES
${CMAKE_SOURCE_DIR}
/
3rd_party
${CMAKE_BINARY_DIR}
/
)
Calling FindCppcheck
To call cppcheck you might add something similar to the below snipped to your root CMakeLists.txt
:
12345678set
(CPPCHECK_XML_OUTPUT
"${PROJECT_BINARY_DIR}/analysis/cppcheck/cppcheck_analysis.xml"
)
set
(CPPCHECK_EXCLUDES
${CMAKE_SOURCE_DIR}
/
3rd_party
${CMAKE_BINARY_DIR}
/
)
find(Cppcheck)
This will then add the target make cppcheck-analysis
to your build system. By default this target will fail your build if there are any static analysis errors.
Conclusion
In this post, I've described how you can integrated Cppcheck into your build system using CMake. This allows you to provide a simplified interface for all developers and your CI system to easily call Cppcheck as they would any other build target.
The new cppcheck-analysis
target will allow for new developers to quickly call Cppcheck and also supports incremental static-analysis to encourage calling it on larger projects.
No comments:
Post a Comment