本文介绍如何将 OCLint 静态代码分析工具集成到 CMake 构建系统中。通过自定义 CMake 脚本,自动运行 OCLint 检查并生成报告,确保代码在编译前经过静态分析,及时发现和解决潜在问题,提升代码质量和安全性。
OCLint 是一个静态代码分析工具,通过词法分析和语法树生成来解析 C、C++ 和 Objective-C 代码。它应用预定义规则集,进行语义分析和静态检查,检测代码风格、复杂性、潜在错误、性能和安全问题。
下载链接:OCLint v0.13.1
解压并设置环境变量后即可运行:
tar -xzvf oclint-0.13.1-x86_64-linux-4.4.0-112-generic.tar.gz
export PATH=$PATH:/path/to/oclint/bin
---
# 禁用的规则列表
disable-rules:
- LongLine
- LongMethod
- HighNPathComplexity
- HighCyclomaticComplexity
- DeepNestedBlock
- HighNcssMethod
- LongParameterList
# 需要启用的规则
rules:
- RedundantVoidArgument
- UseBoolLiteral
- UseEqualsDefault
- UseNullptr
- MissingOverride
- ExplicitConstructor
- CppStyleCasts
- BracesAroundStatements
- ClassNamingConvention
- StructNamingConvention
- TypedefNamingConvention
- EnumNamingConvention
- NonConstParameter
- CertDcl21Cpp
- UndelegatedConstructor
- MacroParentheses
- MacroRepeatedSideEffects
- ForwardDeclarationNamespace
- BoolPointerImplicitConversion
- MisplacedWideningCast
- NarrowingConversion
- ReinterpretCast
- UnconventionalAssignOperator
- AvoidPrivateStaticMembers
- DeadCode
- DeprecatedObjCImplementedProtocols
- DuplicateMethodMatch
- GotoStatement
- InvertedLogic
- LongVariableName
- MagicNumber
- MissingBreakInSwitchStatement
- NestedBlockDepth
- NilAssignedToNonPointer
- ParameterReassignment
- PreferEarlyExit
- RedundantConditionalOperator
- RedundantIfStatement
- RedundantNilCheck
- ReturnFromFinallyBlock
- ShortVariableName
- SwitchStatementsShouldHaveDefault
- TooManyFields
- TooManyMethods
- TooManyParameters
- UnreachableCode
- UnusedMethodParameter
- UnusedLocalVariable
- UseContainerLiteral
- UseEarlyExit
- UselessParentheses
以下是对给定OCLint配置文件的解读:
这些规则被禁用,是为了避免在代码分析过程中被检查到:
这些规则被启用,是为了在代码分析过程中进行检查:
compile_commands.json
为了使用 Cppcheck 的 --project
选项,你需要一个 compile_commands.json
文件。这个文件是一个编译数据库,包含项目中所有源文件的编译信息。
如果你的项目使用 CMake 构建,可以通过以下命令生成 compile_commands.json
文件:
cd /path/to/your/project
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
运行上述命令后,会在项目的构建目录中生成一个 compile_commands.json
文件。
oclint-json-compilation-database -p . -- -extra-arg=-std=c++14 -report-type text -o oclint_report.text
通过将 OCLint 与 CMake 集成,可以在编译阶段自动运行代码分析,早期发现潜在问题。本节将介绍如何在 CMake 构建系统中集成 OCLint,并解释相关脚本的工作原理。
cmake_minimum_required(VERSION 3.10)
# Project name
project(ExampleProject)
# Generate compile_commands.json to enable OCLint checks
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# example target
add_executable(example example.cpp)
# Find all .cpp files in the project source directory, excluding the build directory
file(GLOB_RECURSE ALL_CPP_FILES ${CMAKE_SOURCE_DIR}/*.cpp)
list(FILTER ALL_CPP_FILES EXCLUDE REGEX "${CMAKE_BINARY_DIR}/.*")
# OCLint custom targets for running and checking results
add_custom_target(run_oclint
COMMAND oclint-json-compilation-database -p ${CMAKE_BINARY_DIR} -- -extra-arg=-std=c++14 -report-type text -o ${CMAKE_BINARY_DIR}/oclint_report.txt ${ALL_CPP_FILES}
COMMAND /bin/bash -c "if ! grep -q 'FilesWithViolations=0' ${CMAKE_BINARY_DIR}/oclint_report.txt; then touch ${CMAKE_BINARY_DIR}/oclint_failed; fi"
COMMENT "Running OCLint"
VERBATIM)
add_custom_target(check_oclint
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/oclint_failed
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target run_oclint
COMMAND ${CMAKE_COMMAND} -E cat ${CMAKE_BINARY_DIR}/oclint_report.txt
COMMAND /bin/bash -c "if [ -f ${CMAKE_BINARY_DIR}/oclint_failed ]; then echo 'OCLint errors found.'; exit 1; else echo 'No OCLint issues found.'; fi"
COMMENT "Checking OCLint results"
VERBATIM)
# Ensure run_oclint and check_oclint are run before building any target
add_dependencies(example check_oclint)
# Function to add OCLint pre-build check
function(add_oclint_pre_build target_name)
add_custom_command(TARGET ${target_name} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Running OCLint pre-build check for ${target_name}"
COMMAND /bin/bash -c "if [ -f ${CMAKE_BINARY_DIR}/oclint_failed ]; then echo 'Stopping build due to OCLint errors.'; exit 1; else echo 'No OCLint issues found. Continuing build.'; fi"
COMMENT "Checking for OCLint issues before building ${target_name}"
VERBATIM)
endfunction()
# Add OCLint pre-build check for targets
add_oclint_pre_build(example)
CMake 基本设置:
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
: 生成 compile_commands.json
文件,提供编译数据库,方便 OCLint 使用。目标文件和源文件:
file(GLOB_RECURSE ALL_CPP_FILES ${CMAKE_SOURCE_DIR}/*.cpp)
: 递归查找项目源目录下的所有 .cpp
文件,并将其存储在 ALL_CPP_FILES
变量中。这一步确保所有的源文件都包含在 OCLint 的检查范围内。list(FILTER ALL_CPP_FILES EXCLUDE REGEX "${CMAKE_BINARY_DIR}/.*")
: 排除构建目录中的文件,避免 OCLint 检查 CMake 自身生成的文件。定义 OCLint 自定义目标:
add_custom_target(run_oclint ...)
:
COMMAND oclint-json-compilation-database -p ${CMAKE_BINARY_DIR} -- -extra-arg=-std=c++14 -report-type text -o ${CMAKE_BINARY_DIR}/oclint_report.txt ${ALL_CPP_FILES}
:运行 OCLint,检查所有的 .cpp
文件,并将结果输出到 oclint_report.txt
文件中。COMMAND /bin/bash -c "if ! grep -q 'FilesWithViolations=0' ${CMAKE_BINARY_DIR}/oclint_report.txt; then touch ${CMAKE_BINARY_DIR}/oclint_failed; fi"
:检查 oclint_report.txt
中是否包含 FilesWithViolations=0
。如果不包含,则创建 oclint_failed
文件作为错误标志。COMMENT "Running OCLint"
:提供执行过程中显示的注释。VERBATIM
:确保命令字符串的精确性,不对其进行解释或修改。检查 OCLint 结果并终止构建:
add_custom_target(check_oclint ...)
:
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/oclint_failed
:删除 oclint_failed
文件,确保每次检查前都是干净的状态。COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target run_oclint
:构建 run_oclint
目标,实际执行 OCLint 检查。COMMAND ${CMAKE_COMMAND} -E cat ${CMAKE_BINARY_DIR}/oclint_report.txt
:显示 oclint_report.txt
的内容。COMMAND /bin/bash -c "if [ -f ${CMAKE_BINARY_DIR}/oclint_failed ]; then echo 'OCLint errors found.'; exit 1; else echo 'No OCLint issues found.'; fi"
:检查是否存在 oclint_failed
文件。如果存在,则输出错误信息并终止构建;否则,继续构建过程。COMMENT "Checking OCLint results"
:提供执行过程中显示的注释。VERBATIM
:确保命令字符串的精确性。确保依赖关系正确:
add_dependencies(example check_oclint)
:定义 example
目标依赖于 check_oclint
目标。这确保在构建 example
之前,先运行 OCLint 检查。添加预构建检查:
function(add_oclint_pre_build target_name)
:定义一个函数,用于为指定的目标添加 OCLint 预构建检查。
add_custom_command(TARGET ${target_name} PRE_BUILD ...)
:
COMMAND ${CMAKE_COMMAND} -E echo "Running OCLint pre-build check for ${target_name}"
:在构建前输出一条消息,表示正在进行 OCLint 预构建检查。COMMAND /bin/bash -c "if [ -f ${CMAKE_BINARY_DIR}/oclint_failed ]; then echo 'Stopping build due to OCLint errors.'; exit 1; else echo 'No OCLint issues found. Continuing build.'; fi"
:运行一个 Bash 脚本,检查 oclint_failed
文件是否存在。如果存在,则终止构建并输出错误消息;如果不存在,则继续构建。COMMENT "Checking for OCLint issues before building ${target_name}"
:提供执行过程中显示的注释。VERBATIM
:确保命令字符串的精确性。为目标添加预构建检查:
add_oclint_pre_build(example)
:为 example
目标添加 OCLint 预构建检查,确保在每次构建之前进行代码分析。这样配置可以确保在构建过程中,如果有任何 OCLint 检查失败,会在终端中直接显示相关的错误和警告信息,并且不会继续构建过程。
有错误时的结果
$ make example
[ 33%] Checking OCLint results
[100%] Running OCLint
[100%] Built target run_oclint
OCLint Report
Summary: TotalFiles=2 FilesWithViolations=1 P1=0 P2=0 P3=1
/home/test/Desktop/oclint_example/example.cpp:47:3: unused local variable [unused|P3] The local variable 'unusedVariable' is unused.
[OCLint (http://oclint.org) v0.13.1]
OCLint errors found.
CMakeFiles/check_oclint.dir/build.make:70: recipe for target 'CMakeFiles/check_oclint' failed
make[3]: *** [CMakeFiles/check_oclint] Error 1
CMakeFiles/Makefile2:136: recipe for target 'CMakeFiles/check_oclint.dir/all' failed
make[2]: *** [CMakeFiles/check_oclint.dir/all] Error 2
CMakeFiles/Makefile2:91: recipe for target 'CMakeFiles/example.dir/rule' failed
make[1]: *** [CMakeFiles/example.dir/rule] Error 2
Makefile:124: recipe for target 'example' failed
make: *** [example] Error 2
无错误时的结果
$ make example
[ 33%] Checking OCLint results
[100%] Running OCLint
[100%] Built target run_oclint
OCLint Report
Summary: TotalFiles=2 FilesWithViolations=0 P1=0 P2=0 P3=0
[OCLint (http://oclint.org) v0.13.1]
No OCLint issues found.
Built target check_oclint
Built target example