目录
2.2 源码目录配置(src/CMakeLists.txt)
2.3 测试配置(tests/CMakeLists.txt)
前言
本文通过一个中等复杂度 C++ 项目案例,演示如何基于现代 CMake 最佳实践管理项目。涵盖构建、测试、安装、跨平台支持等全流程,提供可直接复用的工程模板。
一、项目结构与代码实现
1.1 项目结构
MyProject/
├── CMakeLists.txt # 根配置(V3.15+)
├── src/
│ ├── CMakeLists.txt # 库与可执行文件
│ ├── main.cpp
│ ├── lib/
│ │ ├── module1.cpp
│ │ └── module2.cpp
│ └── include/
│ └── mylib.h
└── tests/
├── CMakeLists.txt # 集成 CTest
└── test_main.cpp
1.2 核心代码实现
src/main.cpp
#include <iostream>
#include "mylib.h"
int main() {
std::cout << "Hello, Modern CMake!" << std::endl;
print_module1_info();
print_module2_info();
return 0;
}
src/include/mylib.h
#ifndef MYLIB_H
#define MYLIB_H
void print_module1_info();
void print_module2_info();
#endif
src/lib/module1.cpp
#include "mylib.h"
#include <iostream>
void print_module1_info() {
std::cout << "Module 1 v1.2.3" << std::endl;
}
src/lib/module2.cpp
#include "mylib.h"
#include <iostream>
void print_module2_info() {
std::cout << "Module 2 v2.3.4" << std::endl;
}
二、现代 CMake 配置
2.1 根目录配置(CMakeLists.txt)
cmake_minimum_required(VERSION 3.15)
project(MyProject VERSION 1.0 LANGUAGES CXX)
# 设置默认构建类型
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE)
endif()
message(STATUS "Build configuration: ${CMAKE_BUILD_TYPE}")
# 添加子模块
add_subdirectory(src)
add_subdirectory(tests)
# 安装配置
include(GNUInstallDirs)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
2.2 源码目录配置(src/CMakeLists.txt)
# 创建静态库
add_library(MyLib STATIC
lib/module1.cpp
lib/module2.cpp
)
# 头文件配置(支持安装)
target_include_directories(MyLib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
# C++ 标准配置
target_compile_features(MyLib PUBLIC cxx_std_17)
# 可执行文件配置
add_executable(MyExecutable main.cpp)
target_link_libraries(MyExecutable PRIVATE MyLib)
# 安装规则
install(TARGETS MyLib
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(TARGETS MyExecutable
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
# 构建完成提示(跨平台)
add_custom_command(TARGET MyExecutable POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo
"Build output: $<TARGET_FILE_DIR:MyExecutable>/$<TARGET_FILE_NAME:MyExecutable>"
)
# 一键运行目标
add_custom_target(run
COMMAND $<TARGET_FILE:MyExecutable>
DEPENDS MyExecutable
COMMENT "Running main executable..."
)
2.3 测试配置(tests/CMakeLists.txt)
enable_testing()
# 现代 GTest 集成
find_package(GTest REQUIRED CONFIG)
add_executable(TestMyLib test_main.cpp)
target_link_libraries(TestMyLib
PRIVATE
MyLib
GTest::GTest
GTest::Main
)
# 注册测试用例
add_test(NAME MyLibTest COMMAND TestMyLib)
set_tests_properties(MyLibTest
PROPERTIES
TIMEOUT 30
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/bin
)
tests/test_main.cpp
#include <gtest/gtest.h>
#include "mylib.h"
TEST(ModuleTest, BasicAssertions) {
EXPECT_NO_THROW(print_module1_info());
EXPECT_NO_THROW(print_module2_info());
}
三、构建与测试流程
3.1 标准构建流程
# 1. 创建构建目录
mkdir build && cd build
# 2. 配置项目
cmake -DCMAKE_BUILD_TYPE=Debug ..
# 3. 编译项目 使用生成的构建系统文件进行编译,假设生成了 Makefile
make
# 4. 运行程序(两种方式)
./bin/MyExecutable # 直接运行
make run # 通过自定义目标运行
# 5. 执行测试(两种方式)
./bin/TestMyLib # 直接运行测试
ctest --output-on-failure # 通过 CTest 运行
3.2 安装与打包
# 安装到系统目录
cmake --install . --prefix /usr/local
# 生成安装包
cpack -G TGZ # 生成 tar.gz 包
cpack -G DEB # 生成 Debian 包
四、高级应用场景
4.1 交叉编译配置
基础版工具链文件(toolchain.cmake)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
完整版工具链文件(arm-toolchain.cmake)
Cmake
# 目标平台配置
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
# 工具链路径
set(CMAKE_C_COMPILER "/opt/toolchains/arm-gcc/bin/arm-linux-gnueabihf-gcc")
set(CMAKE_CXX_COMPILER "/opt/toolchains/arm-gcc/bin/arm-linux-gnueabihf-g++")
# 系统根目录
set(CMAKE_SYSROOT "/opt/sysroots/arm-linux")
# 查找规则
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
使用方式
cmake -DCMAKE_TOOLCHAIN_FILE=arm-toolchain.cmake ..
4.2 静态分析集成
通过静态分析集成,您可以在项目开发早期发现并修复问题,从而显著提高代码质量和开发效率。
# 在根 CMakeLists.txt 添加
option(ENABLE_CODE_ANALYSIS "Enable static analysis" OFF)
if(ENABLE_CODE_ANALYSIS)
find_program(CLANG_TIDY clang-tidy)
find_program(CPPCHECK cppcheck)
if(CLANG_TIDY)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY}
-checks=*,-modernize-use-trailing-return-type
)
endif()
if(CPPCHECK)
set(CMAKE_CXX_CPPCHECK ${CPPCHECK}
--enable=all
--suppress=missingInclude
--inline-suppr
)
endif()
endif()
五、新旧实践对比
六、扩展建议
6.1 模块化开发
# 数学模块
add_subdirectory(math)
# 网络模块
add_subdirectory(network)
# 主程序链接模块
target_link_libraries(MyExecutable
PRIVATE
MathLib
NetworkLib
)
6.2 性能优化
# 在根 CMakeLists.txt 添加
if(CMAKE_BUILD_TYPE STREQUAL "Release")
add_compile_options(-march=native -O3)
endif()
# LTO 配置
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_supported)
if(ipo_supported)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
6.3 文档生成
find_package(Doxygen)
if(DOXYGEN_FOUND)
doxygen_add_docs(docs
${PROJECT_SOURCE_DIR}/src/include
COMMENT "Generate API documentation"
)
endif()