C++ 20 脚手架

安装依赖软件

MacOS

Homebrew 上的软件版本比较新,可以直接安装。

1
brew install cmake ninja llvm clang-format

Linux

  1. CMake 和 Ninja 的最佳安装方式是 pip
1
pip install cmake ninja
  1. 最新版的 LLVM 可以通过 PPA 安装
1
2
3
4
5
6
7
8
9
10
11
# 信任证书
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key \
| sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc

# 添加 nightly 源
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster main" \
| sudo tee /etc/apt/sources.list.d/llvm.list

# 安装包
sudo apt update
sudo apt install llvm clang clangd clang-tidy clang-format

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├── CMakeLists.txt
├── README.md
├── doc
│   └── README.md
├── include
│   └── common.h
├── src
│   ├── bar.cpp
│   └── foo.cpp
└── test
├── bar_test.cpp
├── foo_test.cpp
├── resource
│   └── dict.txt
└── test.cpp

测试代码

  1. 功能代码

    1
    2
    3
    4
    // src/foo.cpp
    int foo(int x) {
    return x + x;
    }

  2. 测试代码

    1
    2
    3
    4
    5
    6
    7
    // test/foo_test.cpp
    extern int foo(int x);
    #include <catch2/catch_test_macros.hpp>

    TEST_CASE("Foo") {
    REQUIRE(foo(2) == 4);
    }

  3. 测试入口

    1
    2
    3
    // test/test.cpp
    #define CATCH_CONFIG_RUNNER
    #include <catch2/catch_test_macros.hpp>

构建脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 3.28 版本开始支持 Modules
cmake_minimum_required(VERSION 3.28)
# 项目名称
project(cpp20)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 头文件目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# 功能代码
file(GLOB_RECURSE CPP_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)
# 测试代码
file(GLOB_RECURSE TEST_FILES ${PROJECT_SOURCE_DIR}/test/*.cpp)

# Catch2 测试框架
Include(FetchContent)
FetchContent_Declare(
Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.5.0 # or a later release
)
FetchContent_MakeAvailable(Catch2)

# 测试程序
add_executable(tests ${CPP_FILES} ${TEST_FILES})
target_link_libraries(tests PRIVATE
Catch2::Catch2WithMain)

set_property(TARGET tests PROPERTY CXX_STANDARD 20)

# 定义宏
target_compile_definitions(tests PRIVATE
TEST_RESOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/test/resource")

# 一些有用的告警选项
target_compile_options(tests PRIVATE
-Wall
-Wextra
-pedantic
-Werror
-Wno-unused-parameter
-Wno-unused-variable
-Wno-unused-but-set-variable
-Wno-unused-function
-Wno-unused-but-set-parameter
-Wno-unused-result
-Wno-unused-value
-Wno-unused-local-typedefs
-ftrapv)

include(CTest)
include(Catch)
catch_discover_tests(tests)

# 代码检查
find_program(CLANG_TIDY_EXE NAMES "clang-tidy" REQUIRED)
set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "--format-style=llvm")
set_target_properties(tests PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}")

构建步骤

  1. CMake 构建
1
2
3
4
5
6
7
8
9
10
cmake -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="-fprofile-instr-generate -fcoverage-mapping" \
-DCMAKE_C_FLAGS="-fprofile-instr-generate -fcoverage-mapping" \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_EXPORT_COMPILE_COMMANDS=yes \
-G Ninja

cmake --build build --config Debug
  1. 运行测试
1
2
cd build
LLVM_PROFILE_FILE="default.profraw" ./tests
  1. 覆盖率分析
1
2
3
4
5
6
7
8
9
10
llvm-profdata merge -sparse default.profraw -o default.profdata

llvm-cov show ./tests \
-ignore-filename-regex="build/|include/|test/" \
-instr-profile=default.profdata \
-format=html \
-output-dir=cov-html

# 覆盖率报告
open cov-html/index.html