使用 Cppcheck 进行静态代码分析:完整的 shell 脚本与 CMake 集成实例

使用 Cppcheck 进行静态代码分析:完整的 shell 脚本与 CMake 集成实例

本文介绍如何使用 Cppcheck 进行静态代码分析,涵盖了 Cppcheck 的安装、基本配置和检测原理,并通过具体的 CMake 脚本示例,展示了如何在构建过程中自动运行 Cppcheck 分析,确保在编译阶段捕获并修正代码问题.

1. 安装 Cppcheck

1.1. 克隆 Cppcheck 仓库

首先,从 GitHub 克隆 Cppcheck 仓库:

git clone https://github.com/danmar/cppcheck.git
cd cppcheck

1.2. 构建和安装 Cppcheck

使用以下命令构建和安装 Cppcheck:

mkdir build
cd build
cmake ..
make
sudo make install

2. 检测原理

Cppcheck 使用多种技术来分析 C/C++ 代码,包括但不限于以下几种:

  1. 语法解析:Cppcheck 使用自定义的语法解析器来理解 C/C++ 代码的结构。这包括函数、类、变量的定义和使用等。

  2. 数据流分析:Cppcheck 分析变量和数据在代码中的流动情况,以检测未初始化变量、空指针引用等问题。

  3. 符号执行:通过模拟代码执行路径,Cppcheck 能够发现逻辑错误和潜在的运行时问题。

  4. 配置驱动:Cppcheck 允许用户通过配置文件和命令行参数定制检查规则和抑制特定的警告,以减少误报并专注于真正的问题。

  5. 编译数据库:Cppcheck 支持读取编译数据库(如 compile_commands.json),从而准确地了解项目的编译选项和文件依赖关系。

通过以上技术,Cppcheck 能够在不需要编译代码的情况下,提供深入且全面的代码质量检查。

3. 脚本介绍

下面是一个用于运行 Cppcheck 的 Bash 脚本。该脚本配置了多个选项和抑制规则,以减少误报并生成详细的检查器报告。

#!/bin/bash

# cppcheck 命令行选项
OPTIONS="--enable=style \
         --enable=performance \
         --enable=portability \
         --check-level=exhaustive"

# 根据你的配置,添加相应的检查项
EXTRA_OPTIONS="--enable=unusedFunction"

# 根据你的配置,添加相应的抑制选项
SUPPRESS="--suppress=information \
          --suppress=missingIncludeSystem \
          --suppress=unmatchedSuppression \
          --suppress=passedByValue \
          --suppress=unusedFunction \
          --suppress=uninitMemberVar \
          --suppress=uninitvar \
          --suppress=nullPointer \
          --suppress=shadowVariable \
          --suppress=variableScope \
          --suppress=duplicateExpression \
          --suppress=useInitializationList"

# 包含路径
INCLUDE="-I /usr/include -I/usr/local/include"

# 如果需要设置最大函数复杂度,可以取消下面这行的注释
# OPTIONS="$OPTIONS --max-complexity=10"

# 运行 cppcheck
cppcheck --project=compile_commands.json $OPTIONS $EXTRA_OPTIONS $SUPPRESS $INCLUDE


4. Shell脚本详解

4.1. 基本选项

OPTIONS="--enable=style \
         --enable=performance \
         --enable=portability \
         --check-level=exhaustive"
  • --enable=style:启用代码风格检查。
  • --enable=performance:启用性能检查。
  • --enable=portability:启用可移植性检查。
  • --check-level=exhaustive:进行详尽的检查,分析所有代码分支,尽管这样可能会增加分析时间。

4.2. 额外选项

EXTRA_OPTIONS="--enable=unusedFunction"
  • --enable=unusedFunction:启用未使用函数的检查。

4.3. 抑制选项

SUPPRESS="--suppress=information \
          --suppress=missingInclude \
          --suppress=missingIncludeSystem \
          --suppress=unmatchedSuppression \
          --suppress=passedByValue \
          --suppress=unusedFunction \
          --suppress=uninitMemberVar \
          --suppress=uninitvar \
          --suppress=nullPointer \
          --suppress=shadowVariable \
          --suppress=variableScope \
          --suppress=duplicateExpression \
          --suppress=useInitializationList"

这些选项用于抑制特定类型的警告,减少误报。例如:

  • --suppress=missingIncludeSystem:忽略缺少系统包含文件的警告。
  • --suppress=uninitMemberVar:忽略未初始化成员变量的警告。

4.4. 包含路径

INCLUDE="-I /usr/include -I/usr/local/include"

指定包含路径,以确保分析过程中能找到所有必要的头文件。

5. 检测实例

5.1. 使用 CMake 生成 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 文件。

5.2. 运行 Cppcheck

cppcheck --project=compile_commands.json $OPTIONS $EXTRA_OPTIONS $SUPPRESS $INCLUDE --checkers-report=checkers_report.txt
  • --project=compile_commands.json:指定项目的编译数据库文件。
  • --checkers-report=checkers_report.txt:生成包含所有检查器详细信息的报告文件。

6. 结果示例

Checking /home/test/cppcheck/example.cpp ...
Checking /home/test/cppcheck/example.cpp: ALLOC_HOOK_VERSION=0xfe9abbf;__PIC__=1...
/home/test/cppcheck/example.cpp:13:10: style: Variable 'intPtr' can be declared as pointer to const [constVariablePointer]
    int* intPtr = new int[100];  // 分配int类型的内存
         ^
/home/test/cppcheck/example.cpp:18:13: style: Variable 'doublePtr' can be declared as pointer to const [constVariablePointer]
    double* doublePtr = new double(3.14);  // 分配double类型的内存
            ^
/home/test/cppcheck/example.cpp:23:18: style: Variable 'strPtr' can be declared as pointer to const [constVariablePointer]
    std::string* strPtr = new std::string("Hello, World!");  // 分配字符串类型的内存
                 ^
/home/test/cppcheck/example.cpp:40:11: style: Variable 'mmapPtr' can be declared as pointer to const [constVariablePointer]
    void* mmapPtr = mmap(nullptr, mmapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

7. 与 CMake 集成

通过将 Cppcheck 与 CMake 集成,我们可以在编译阶段自动运行代码分析,从而在早期发现潜在问题。以下将介绍如何在 CMake 构建系统中集成 Cppcheck,并解释相关脚本的工作原理。

7.1 CMake 脚本详解

cmake_minimum_required(VERSION 3.10)

# Project name
project(ExampleProject)

# Generate compile_commands.json to enable clang-tidy and cppcheck
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Include directories
include_directories(${CMAKE_SOURCE_DIR})

# example target
add_executable(example example.cpp)

# Find all .cpp files in the project source directory
file(GLOB_RECURSE ALL_CPP_FILES ${CMAKE_SOURCE_DIR}/*.cpp)

# Run cppcheck and generate a flag file if there are any warnings or errors
add_custom_target(run_cppcheck
    COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/cppcheck_failed
    COMMAND cppcheck --project=${CMAKE_BINARY_DIR}/compile_commands.json
    --enable=style
    --enable=performance
    --enable=portability
    --check-level=exhaustive
    --enable=unusedFunction
    --suppress=information
    --suppress=missingIncludeSystem
    --suppress=unmatchedSuppression
    --suppress=passedByValue
    --suppress=unusedFunction
    --suppress=uninitMemberVar
    --suppress=uninitvar
    --suppress=nullPointer
    --suppress=shadowVariable
    --suppress=variableScope
    --suppress=duplicateExpression
    --suppress=useInitializationList
    -I /usr/include
    -I /usr/local/include
    --error-exitcode=1
    || ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/cppcheck_failed
    COMMENT "Running cppcheck"
    VERBATIM)

add_custom_target(check_cppcheck
    COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target run_cppcheck
    COMMENT "Checking cppcheck results"
    VERBATIM)

# Ensure run_cppcheck and check_cppcheck are run before building any target
add_dependencies(example check_cppcheck)

# Function to add Cppcheck pre-build check
function(add_cppcheck_pre_build target_name)
    add_custom_command(TARGET ${target_name} PRE_BUILD
        COMMAND ${CMAKE_COMMAND} -E echo "Running Cppcheck pre-build check for ${target_name}"
        COMMAND /bin/bash -c
        "if [ -f ${CMAKE_BINARY_DIR}/cppcheck_failed ]; \
        then echo 'Stopping build due to Cppcheck errors.'; \
        exit 1; else echo 'No Cppcheck issues found. Continuing build.'; fi"
        COMMENT "Checking for Cppcheck issues before building ${target_name}"
        VERBATIM)
endfunction()

# Add Cppcheck pre-build check for targets
add_cppcheck_pre_build(example)

7.2 CMake脚本细节解析

  1. CMake 基本设置

    • set(CMAKE_EXPORT_COMPILE_COMMANDS ON): 生成 compile_commands.json 文件,提供编译数据库,方便 Cppcheck 使用。
  2. 目标文件和包含路径

    • include_directories(${CMAKE_SOURCE_DIR}): 包含项目源目录,以便 Cppcheck 可以找到所有必要的头文件。
    • file(GLOB_RECURSE ALL_CPP_FILES ${CMAKE_SOURCE_DIR}/*.cpp): 递归查找项目源目录下的所有 .cpp 文件,并将其存储在 ALL_CPP_FILES 变量中。这一步确保所有的源文件都包含在 Cppcheck 的检查范围内。
  3. 定义 Cppcheck 自定义目标

    • add_custom_target(run_cppcheck ...):
      • COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/cppcheck_failed: 在运行 Cppcheck 之前,先删除可能存在的 cppcheck_failed 文件。
      • COMMAND cppcheck --project=${CMAKE_BINARY_DIR}/compile_commands.json ...: 运行 Cppcheck,并使用生成的 compile_commands.json 文件。通过各种 --enable--suppress 选项配置检查的详细程度和要忽略的警告类型。--error-exitcode=1 表示如果发现任何错误,Cppcheck 将返回一个非零退出代码。
      • || ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/cppcheck_failed: 如果 Cppcheck 检查失败,创建一个名为 cppcheck_failed 的文件作为标志。
      • COMMENT "Running cppcheck": 提供执行过程中显示的注释。
      • VERBATIM: 确保命令字符串的精确性,不对其进行解释或修改。
    • add_custom_target(check_cppcheck ...):
      • COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target run_cppcheck: 构建 run_cppcheck 目标,实际执行 Cppcheck 检查。
      • COMMENT "Checking cppcheck results": 提供执行过程中显示的注释。
      • VERBATIM: 确保命令字符串的精确性。
  4. 目标依赖关系

    • add_dependencies(example check_cppcheck): 定义 example 目标依赖于 check_cppcheck 目标。这确保在构建 example 之前,先运行 Cppcheck 检查。
  5. 添加预构建检查

    • function(add_cppcheck_pre_build target_name): 定义一个函数,用于为指定的目标添加 Cppcheck 预构建检查。
      • add_custom_command(TARGET ${target_name} PRE_BUILD ...):
        • COMMAND ${CMAKE_COMMAND} -E echo "Running Cppcheck pre-build check for ${target_name}": 在构建前输出一条消息,表示正在进行 Cppcheck 预构建检查。
        • COMMAND /bin/bash -c "if [ -f ${CMAKE_BINARY_DIR}/cppcheck_failed ]; then echo 'Stopping build due to Cppcheck errors.'; exit 1; else echo 'No Cppcheck issues found. Continuing build.'; fi": 运行一个 Bash 脚本,检查 cppcheck_failed 文件是否存在。如果存在,则终止构建并输出错误消息;如果不存在,则继续构建。
        • COMMENT "Checking for Cppcheck issues before building ${target_name}": 提供执行过程中显示的注释。
        • VERBATIM: 确保命令字符串的精确性。
  6. 为目标添加预构建检查

    • add_cppcheck_pre_build(example): 为 example 目标添加 Cppcheck 预构建检查,确保在每次构建之前进行代码分析。
  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值