CMake实战:从Hello World到跨平台库构建(附完整代码示例)
如果你刚开始接触C++项目构建,面对不同平台、不同编译器、复杂的依赖关系,是不是经常感到头疼?手动编写Makefile不仅繁琐,而且难以维护,跨平台更是噩梦。别担心,CMake正是为了解决这些问题而生的。它不是一个编译器,而是一个构建系统生成器,让你用一份简单的配置文件,就能在Windows、Linux、macOS上生成对应的构建文件(如Visual Studio的.sln、Unix的Makefile、Ninja的.build.ninja等)。无论你是想管理一个简单的Hello World程序,还是构建包含多个静态库、动态库的复杂项目,CMake都能帮你优雅地搞定。
这篇文章就是为你准备的。我会带你从最基础的单个文件项目开始,一步步构建一个包含可执行文件、静态库、动态库的跨平台C++项目。过程中,我会重点解决多目录源码组织、头文件搜索路径设置、不同平台下的库生成等实际问题,并提供可以直接复制粘贴的CMakeLists.txt模板。读完这篇文章,你不仅能掌握CMake的核心用法,还能建立起管理真实项目的信心。
1. 环境准备与第一个CMake项目
在开始之前,确保你的系统已经安装了CMake。你可以通过命令行检查版本:
cmake --version
我建议使用CMake 3.10或更高版本,以支持更多现代特性。如果还没安装,可以去CMake官网下载对应平台的安装包,或者使用包管理器(如Ubuntu的apt install cmake、macOS的brew install cmake)进行安装。
1.1 从最简单的Hello World开始
让我们从一个最经典的程序开始。创建一个新目录hello_cmake,在里面新建两个文件:
main.cpp
#include <iostream>
int main() {
std::cout << "Hello, CMake World!" << std::endl;
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(HelloCMake LANGUAGES CXX)
add_executable(hello_cmake main.cpp)
这个CMakeLists.txt只有三行,但每一行都至关重要:
cmake_minimum_required(VERSION 3.10):指定CMake的最低版本要求。这能确保你的脚本在不同机器上行为一致,避免因版本差异导致的问题。project(HelloCMake LANGUAGES CXX):定义项目名称和使用的语言。LANGUAGES CXX明确告诉CMake这是一个C++项目,它会自动查找C++编译器。add_executable(hello_cmake main.cpp):告诉CMake要生成一个名为hello_cmake的可执行文件,源文件是main.cpp。
注意:
project()命令会隐式定义一些有用的变量,比如PROJECT_NAME(项目名)、PROJECT_SOURCE_DIR(项目源码目录)等,后续我们会用到。
现在,在项目目录下创建一个build目录(这是个好习惯,保持源码目录干净),然后进入build目录执行CMake:
mkdir build && cd build
cmake ..
你会看到CMake检测编译器、生成构建系统的过程。完成后,执行构建:
cmake --build .
# 或者在Unix-like系统上直接
make
最后运行生成的可执行文件:
./hello_cmake # Linux/macOS
# 或者在Windows上
hello_cmake.exe
看到"Hello, CMake World!"输出,恭喜你,第一个CMake项目成功了!
1.2 理解CMake的构建流程
CMake的工作流程可以概括为三个阶段:
- 配置阶段:CMake读取
CMakeLists.txt,解析其中的指令,检查系统环境(编译器、库等),生成中间配置。 - 生成阶段:根据配置,生成对应平台的构建文件(如Makefile、.sln等)。
- 构建阶段:调用生成的构建文件实际编译代码。
这种"配置-生成-构建"的分离设计,正是CMake跨平台能力的核心。你永远不直接操作Makefile或Visual Studio项目文件,而是通过CMakeLists.txt这个统一的接口。
在实际开发中,我强烈推荐使用外部构建(Out-of-source build),就像我们刚才做的在build目录下执行cmake ..。这样做的好处是:
- 源码目录保持干净,不会被编译中间文件污染
- 可以同时为不同配置(Debug/Release)或不同平台创建多个
build目录 - 清理构建结果只需删除
build目录即可
2. 管理多文件与目录结构
真实项目很少只有一个源文件。当项目规模增长时,合理的目录结构至关重要。让我们创建一个更接近实际的项目结构:
my_project/
├── CMakeLists.txt # 根目录的CMakeLists.txt
├── include/ # 公共头文件
│ └── utils/
│ └── math_utils.h
├── src/ # 主程序源码
│ ├── main.cpp
│ └── utils/
│ └── math_utils.cpp
└── lib/ # 库源码
├── calculator/
│ ├── calculator.h
│ └── calculator.cpp
└── logger/
├── logger.h
└── logger.cpp
2.1 组织头文件与源文件
对于这样的结构,根目录的CMakeLists.txt需要相应调整:
cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 添加可执行文件
add_executable(my_app
src/main.cpp
src/utils/math_utils.cpp
)
# 添加头文件搜索路径
target_include_directories(my_app PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/lib/calculator
${CMAKE_CURRENT_SOURCE_DIR}/lib/logger
)
这里有几个关键点:
set(CMAKE_CXX_STANDARD 17):明确要求C++17标准。加上CMAKE_CXX_STANDARD_REQUIRED ON确保编译器必须支持,否则报错。CMAKE_CXX_EXTENSIONS OFF:禁用编译器扩展,确保代码可移植。target_include_directories():为特定目标(这里是my_app)指定头文件搜索路径。PRIVATE表示这些路径只用于编译my_app本身。
提示:CMake 3.0之后推荐使用
target_xxx系列命令(如target_include_directories、target_compile_options)而不是全局命令(如include_directories)。这样能更精确地控制每个目标的属性,避免污染。

&spm=1001.2101.3001.5002&articleId=151481668&d=1&t=3&u=64ca256b737f436b8ff9edec4f329a77)
2万+

被折叠的 条评论
为什么被折叠?



