CMake C++ 构建系统
bash sudo apt update sudo apt install cmake
**macOS (Homebrew)**
```bash
brew install cmake
Windows 从 cmake.org/download 下载安装包,安装时勾选“Add CMake to the system PATH”。
验证安装:
cmake --version
必备工具
你需要一个 C++ 编译器(如 GCC、Clang 或 MSVC)以及一个构建工具(make、ninja 或 Visual Studio)。通常系统已自带,无需额外安装。
第一个 CMake 项目
项目结构
hello/
├── CMakeLists.txt
└── main.cpp
main.cpp
#include <iostream>
int main() {
std::cout << "Hello, CMake!" << std::endl;
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(HelloWorld)
add_executable(hello main.cpp)
构建并运行
外部构建(推荐):在项目根目录新建 build 文件夹并进入,执行配置和构建。
mkdir build && cd build
cmake .. # 生成构建文件
cmake --build . # 执行编译
./hello # 运行程序
cmake .. 表示读取上层目录的 CMakeLists.txt,生成文件到当前目录。这样不会污染源代码目录。
CMakeLists.txt 基础语法
所有 CMake 配置以命令的形式编写,命令不区分大小写,但参数通常大小写敏感。
最小版本与项目声明
cmake_minimum_required(VERSION 3.10) # 指定最低 CMake 版本
project(MyProject VERSION 1.0 LANGUAGES CXX) # 项目名、版本、语言
project()会设置一系列变量,如PROJECT_NAME、PROJECT_VERSION等。
添加可执行目标
add_executable(app_name source1.cpp source2.cpp ...)
例如:
add_executable(myapp main.cpp utils.cpp)
设置 C++ 标准
set(CMAKE_CXX_STANDARD 17) # 要求 C++17
set(CMAKE_CXX_STANDARD_REQUIRED ON) # 强制要求,不满足则报错
set(CMAKE_CXX_EXTENSIONS OFF) # 禁用编译器扩展
更现代的写法是使用目标属性:
target_compile_features(myapp PUBLIC cxx_std_17)
头文件与源文件管理
添加头文件搜索路径
include_directories(include) # 影响整个目录,已不推荐
target_include_directories(myapp PRIVATE include) # 仅对目标 myapp 生效,推荐
PRIVATE 表示该头文件仅在编译本目标时使用,不传播给依赖它的其他目标。
PUBLIC 会将路径传播给依赖者(适用于库的公共头文件),INTERFACE 仅向依赖者暴露(适用于纯头文件库)。
自动收集源文件
不推荐在顶层使用通配符,因为文件增删后 CMake 不会自动重新生成。简单项目可手动列出。
若要使用,可以这样:
file(GLOB SOURCES src/*.cpp src/*.h)
add_executable(myapp ${SOURCES})
但更推荐明确列出,或结合 CONFIGURE_DEPENDS(CMake 3.12+):
file(GLOB SOURCES CONFIGURE_DEPENDS src/*.cpp)
添加库
静态库与动态库
# 静态库
add_library(mylib STATIC src/lib.cpp)
# 动态库
add_library(mylib SHARED src/lib.cpp)
# 由 BUILD_SHARED_LIBS 变量决定类型(默认静态)
add_library(mylib src/lib.cpp)
库的头文件
通常把公共头文件放在单独的 include/ 目录,并设置头文件路径。
add_library(mylib src/lib.cpp)
target_include_directories(mylib PUBLIC include)
链接库
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib)
PRIVATE:myapp 使用 mylib,但不将 mylib 传播给 myapp 的依赖者。
PUBLIC:myapp 的依赖者也能使用 mylib(如 mylib 被暴露在 myapp 的公开头文件中)。
INTERFACE:不自己使用,仅向依赖者传播,常见于纯头文件库。
模块化项目与子目录
当项目变大时,使用 add_subdirectory 集成子项目。
myproject/
├── CMakeLists.txt
├── main.cpp
└── math/
├── CMakeLists.txt
├── include/
│ └── math.h
└── src/
└── math.cpp
顶层 CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(ModularApp)
add_subdirectory(math) # 先处理子目录
add_executable(app main.cpp)
target_link_libraries(app PRIVATE math)
math/CMakeLists.txt
add_library(math src/math.cpp)
target_include_directories(math PUBLIC include)
子目录中的目标 math 会自动成为全局可见的目标,可以在顶层链接。
查找外部包
使用 find_package 查找预装库或通过 CMake 导出的包。
find_package(OpenCV REQUIRED) # 必须找到,否则报错
find_package(Boost COMPONENTS filesystem system REQUIRED)
# 链接
target_link_libraries(myapp PRIVATE ${OpenCV_LIBS} Boost::filesystem Boost::system)
很多包会定义导入目标(如 Boost::filesystem),使用它们更安全,能自动处理头文件路径和依赖。
对于没有 CMake 包的系统库,可以使用手动指定变量:
find_library(LIBNAME libname)
target_link_libraries(myapp PRIVATE ${LIBNAME})
或使用 PkgConfig:
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
target_link_libraries(myapp PRIVATE ${GTK3_LIBRARIES})
target_include_directories(myapp PRIVATE ${GTK3_INCLUDE_DIRS})
构建类型与调试
CMake 支持多种构建类型(通过 CMAKE_BUILD_TYPE 设置)。
cmake -DCMAKE_BUILD_TYPE=Debug .. # 调试版本,含符号,不优化
cmake -DCMAKE_BUILD_TYPE=Release .. # 发布版本,优化,无调试符号
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. # 带符号的优化版本
可在 CMakeLists.txt 中设置默认:
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
编译器警告与优化
target_compile_options(myapp PRIVATE -Wall -Wextra) # GCC/Clang
# MSVC 可用 /W4
if(MSVC)
target_compile_options(myapp PRIVATE /W4)
endif()
生成器表达式可以更优雅地处理跨平台选项,但基础项目直接使用条件判读即可。
安装与部署
通过 install 命令指定需要安装的文件和目标。
install(TARGETS myapp DESTINATION bin)
install(TARGETS mylib LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
install(DIRECTORY include/ DESTINATION include)
构建后执行安装:
cmake --install . --prefix /usr/local