第4章:构建系统入门:CMake 的安装与基本语法

说到跨平台开发,构建系统是绕不开的话题。我个人习惯用 CMake,原因很简单——它几乎成了事实标准。你想想看,从嵌入式到服务器,从桌面应用到移动端,CMake 都能搞定。今天我们就来聊聊它的安装和基本语法。

4.1 为什么是 CMake?

我记得刚入行那会儿,项目里用的是 Makefile。每个平台写一套,Windows 用 nmake,Linux 用 GNU Make,macOS 还得折腾 Xcode 的 project 文件。那叫一个痛苦。后来接触到 CMake,才发现原来构建可以这么清爽。

CMake 的核心思想很简单:一次编写,到处构建。你只需要写一份 CMakeLists.txt,它就能自动生成对应平台的构建文件。说白了,它是个「构建系统的生成器」。

核心优势:

  • 跨平台:Windows、Linux、macOS、Android、iOS 全支持
  • 跨编译器:GCC、Clang、MSVC、MinGW 无缝切换
  • 生态丰富:几乎所有 C++ 库都提供 CMake 支持
  • 可扩展:支持自定义模块和函数

4.2 安装 CMake

安装 CMake 其实没什么难度,但不同平台略有差异。我建议你直接装最新稳定版,别用系统自带的旧版本——我在项目中遇到过因为 CMake 版本太低导致某些特性用不了的尴尬。

Windows 平台

  1. 去 cmake.org/download/ 下载 .msi 安装包
  2. 安装时勾选「Add CMake to the system PATH」
  3. 安装完成后打开命令行,输入 cmake --version 验证

小技巧:Windows 上我习惯同时装 CMake 和 CMake GUI。GUI 工具对新手友好,可以直观地看到所有配置选项。

Linux 平台

# Ubuntu/Debian
sudo apt update
sudo apt install cmake

# CentOS/RHEL
sudo yum install cmake

# 或者从源码编译(推荐)
wget https://github.com/Kitware/CMake/releases/download/v3.28.0/cmake-3.28.0.tar.gz
tar -xzf cmake-3.28.0.tar.gz
cd cmake-3.28.0
./bootstrap && make && sudo make install

macOS 平台

# 用 Homebrew 安装(最简单)
brew install cmake

# 或者下载 .dmg 安装包
# 从 cmake.org 下载后直接拖到 Applications 文件夹

注意:macOS 上如果遇到「cmake: command not found」,记得检查 PATH 是否包含 /usr/local/bin。我曾经在这上面浪费了半小时。

4.3 CMake 基本语法

CMake 的语法其实不复杂。它用的是自己的脚本语言,但核心概念就那么几个。我们从一个最简单的例子开始。

4.3.1 第一个 CMakeLists.txt

# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(HelloWorld)

add_executable(hello main.cpp)

这三行干了什么?

  • cmake_minimum_required:指定最低 CMake 版本。嗯,这里要注意,版本号别写太高,否则别人用旧版 CMake 会报错。
  • project:定义项目名称。这个名称会影响到后续的一些变量,比如 PROJECT_NAME。
  • add_executable:告诉 CMake 我们要生成一个可执行文件,源文件是 main.cpp。

然后怎么用?

mkdir build
cd build
cmake ..
cmake --build .

为什么要有 build 目录?我个人习惯把构建产物和源码分开,这样 git clean 的时候不会误删源码。这叫「out-of-source build」,是 CMake 推荐的做法。

4.3.2 变量与缓存

CMake 里变量用 set() 来定义:

set(SOURCES main.cpp utils.cpp network.cpp)
add_executable(myapp ${SOURCES})

这里 ${SOURCES} 就是引用变量。你想想看,如果源文件有几十个,一个个列在 add_executable 里多痛苦?用变量管理就清爽多了。

还有一种特殊变量叫「缓存变量」,它可以在命令行里修改:

set(MY_VAR "default" CACHE STRING "Description of my var")

然后你可以这样覆盖:

cmake .. -DMY_VAR="custom_value"

我在项目中经常用这个来切换 Debug/Release 模式,或者指定不同的第三方库路径。

4.3.3 条件判断

跨平台开发免不了要判断操作系统:

if(WIN32)
    message("Building on Windows")
    add_definitions(-DWINDOWS)
elseif(APPLE)
    message("Building on macOS")
    add_definitions(-DMACOS)
else()
    message("Building on Linux")
    add_definitions(-DLINUX)
endif()

除了 WIN32、APPLE,常用的还有:

  • UNIX:所有 Unix-like 系统(包括 macOS)
  • MSVC:Visual Studio 编译器
  • CMAKE_BUILD_TYPE:构建类型(Debug/Release)

避坑指南:我曾经在 macOS 上用 if(UNIX) 来判断 Linux,结果发现 macOS 也被包含进去了。后来才搞清楚,UNIX 在 CMake 里包括 macOS、Linux、BSD 等所有类 Unix 系统。要精确判断 Linux,得用 if(CMAKE_SYSTEM_NAME STREQUAL "Linux")。

4.3.4 循环与列表操作

有时候你需要批量处理文件:

set(FILES file1.cpp file2.cpp file3.cpp)
foreach(F ${FILES})
    message("Processing: ${F}")
endforeach()

列表操作也很常用:

list(APPEND SOURCES new_file.cpp)
list(REMOVE_ITEM SOURCES old_file.cpp)
list(LENGTH SOURCES len)
message("Source count: ${len}")

4.4 核心知识体系

下面这张图总结了 CMake 的核心概念和它们之间的关系:

CMake 核心知识体系 CMakeLists.txt 安装与配置 基本语法:变量、条件、循环 构建目标:可执行文件/库 Windows Linux macOS set() / 变量 if/else/endif foreach / list 核心思想:一次编写,到处构建

4.5 实战:一个跨平台的小项目

光说不练假把式。我们写一个完整的 CMakeLists.txt,支持跨平台编译:

cmake_minimum_required(VERSION 3.10)
project(CrossPlatformDemo VERSION 1.0.0 LANGUAGES CXX)

# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 收集源文件
file(GLOB SOURCES "src/*.cpp")

# 根据平台添加不同的源文件
if(WIN32)
    list(APPEND SOURCES "src/platform/win32.cpp")
    add_definitions(-DPLATFORM_WINDOWS)
elseif(APPLE)
    list(APPEND SOURCES "src/platform/macos.mm")
    add_definitions(-DPLATFORM_MACOS)
else()
    list(APPEND SOURCES "src/platform/linux.cpp")
    add_definitions(-DPLATFORM_LINUX)
endif()

# 创建可执行文件
add_executable(${PROJECT_NAME} ${SOURCES})

# 添加头文件路径
target_include_directories(${PROJECT_NAME} PRIVATE include)

# 链接第三方库(如果有)
# find_package(OpenCV REQUIRED)
# target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})

小提示:file(GLOB ...) 虽然方便,但 CMake 官方不推荐在大型项目中使用,因为它不会自动检测新增文件。我个人的做法是:小项目用 GLOB,大项目手动列出源文件。

4.6 常见问题与避坑

最后分享几个我踩过的坑:

  • 路径中的空格:Windows 上 Program Files 有空格,记得用双引号包起来,或者用 file(TO_CMAKE_PATH ...) 转换。
  • 大小写敏感:CMake 命令不区分大小写,但变量名区分。我习惯全小写命令,全大写变量。
  • 缓存问题:修改了 CMakeLists.txt 后,记得重新运行 cmake ..,有时候缓存没更新会导致奇怪的问题。我一般直接删掉 build 目录重新来。

嗯,CMake 的基本用法就这些。掌握了这些,你已经能应付大部分跨平台构建场景了。记住,CMake 的文档非常完善,遇到问题先查官方文档,比在网上乱搜高效得多。

可可精品资料库