Hello, world

创建一个 CMakeLists.txt,写入:

1
2
3
4
5
6
7
8
9
10
11
12
cmake_minimum_required(VERSION 3.10)

project(
hello
VERSION 0.0.1
LANGUAGES C CXX
)

add_executable(
main
main.cpp
)

这里省略了 main.cpp 程序。

按照惯例,创建一个 build 文件夹,切换到该路径下,cmake ..。基于 Visual Studio 的环境,会自动生成 .sln 工程。项目名称就是 project 中指定的 hello

拆分文件夹

现在把 main.cpp 放到一个新建的 src 目录中,并且新建一个 include 作为头文件的文件夹,并在 src 目录中新建一个 CMakeLists.txt,写入:

1
2
3
4
add_executable(
main
main.cpp
)

最外层的(原先的)的 CMakeLists.txt 加入:

1
2
include_directories(include)
add_subdirectory(src)

GoogleTest

现在引入 GoogleTest 作为例子。目前 GoogleTest 1.15.2 要求 C++ 标准不低于 C++14,于是在项目层级的 CMakeLists.txt 中设定该条件(这里设定是 C++17):

1
2
3
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

给用户一个选项是否开启单元测试:

1
2
option(ENABLE_UNIT_TESTS "Enable unit tests" ON)
message(STATUS "Enable testing: ${ENABLE_UNIT_TESTS}")

对这个条件进行判断,再进行后续操作:

1
2
3
if (ENABLE_UNIT_TESTS)
# 后面内容都粘贴在这里
endif()

导入 FetchContent 模块,并指定 GoogleTest 的地址,并导入:

1
2
3
4
5
6
7
8
9
if (ENABLE_UNIT_TESTS)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.15.2
)
FetchContent_MakeAvailable(googletest)
endif()

写一个最简单的测试库(注意对应的文件):

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
// include/sum_integers.hpp
#pragma once

#include <vector>

int sum_integers(const std::vector<int> integers);

// src/sum_integers.cpp
#include "sum_integers.hpp"

int sum_integers(const std::vector<int> integers)
{
auto sum = 0;
for (auto i : integers)
{
sum += i;
}
return sum;
}

// src/main.cpp
#include <iostream>
#include <string>
#include <vector>

#include "sum_integers.hpp"

int main(int argc, char *argv[])
{
std::vector<int> integers;
for (auto i = 1; i < argc; i++)
{
integers.push_back(std::stoi(argv[i]));
}

auto sum = sum_integers(integers);

std::cout << sum << std::endl;
return 0;
}

src 中的 CMakeLists.txt 改成:

1
2
3
4
5
6
7
# example library
add_library(sum_integers sum_integers.cpp)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/bin)
# main code
add_executable(sum_up main.cpp)

target_link_libraries(sum_up sum_integers)

写一个测试

新建一个 tests 文件夹,用于编写存放单元测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// tests/test.cpp
#include "sum_integers.hpp"
#include "gtest/gtest.h"
#include <vector>

TEST(example, sum_zero)
{
auto integers = {1, -1, 2, -2, 3, -3};
auto result = sum_integers(integers);
ASSERT_EQ(result, 0);
}

TEST(example, sum_five)
{
auto integers = {1, 2, 3, 4, 5};
auto result = sum_integers(integers);
ASSERT_EQ(result, 15);
}

为了更加清晰的划分,在 tests 目录中添加 CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (ENABLE_UNIT_TESTS)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.15.2
)
FetchContent_MakeAvailable(googletest)

add_executable(cpp_test test.cpp)

target_link_libraries(cpp_test
PRIVATE
sum_integers
gtest_main
)

add_test(NAME cpp_test COMMAND cpp_test)
endif()

顶层的 CMakeLists.txt 可以更宏观的描述整个项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cmake_minimum_required(VERSION 3.15)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

project(HelloWorld)
enable_testing()

option(ENABLE_UNIT_TESTS "Enable unit tests" ON)
message(STATUS "Enable testing: ${ENABLE_UNIT_TESTS}")

include_directories(include)
add_subdirectory(src)

if (ENABLE_UNIT_TESTS)
add_subdirectory(tests)
endif()

可以使用 ctest 运行并收集测试报告:

1
2
3
4
5
6
7
8
> ctest -C Release
Test project E:/vs/project-build/build
Start 1: cpp_test
1/1 Test #1: cpp_test ......................... Passed 0.03 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) = 0.04 sec

常用的命令

add_library

定义一个库目标,可以是静态库、共享库或模块库。

1
add_library(MyLibrary STATIC mylib.cpp)

为目标添加链接库。

1
target_link_libraries(MyExecutable PRIVATE MyLibrary)

add_custom_command

添加定制命令,例如用于复制:

1
2
3
4
5
6
add_custom_command(TARGET toyfun_grpc POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_FILE:toyfun_grpc>"
"${TARGET_DIR}"
COMMENT "Copying toyfun_grpc.dll to ${TARGET_DIR}"
)

Vcpkg

Vcpkg 是由 Microsoft 和 C++ 社区维护的免费开源 C/C++ 包管理器,可在 Windows、macOS 和 Linux 上运行。它是核心的 C++ 工具,使用 C++ 和 CMake 脚本编写。它旨在解决管理 C/C++ 库的独特难题。

Conan

Conan 是一个开源的、跨平台的 C/C++ 包管理器,旨在简化和自动化 C/C++ 项目的依赖管理,并且可以在 Windows、macOS 和 Linux 上运行。Conan 提供了一个灵活且强大的工具集,帮助开发者管理库的构建、打包和分发。

参考资料

  1. cmake 官方文档
  2. CMake 与 gtest:自动下载 gtest 与搭建测试驱动环境
  3. vcpkg 概述
  4. vcpkg 官网
  5. Conan 官网