week01-CMake

  1. 1. 1. 入门案例
  2. 2. 2. CMake 构建项目之路
    1. 2.1. 2.1 cmake_minimum_required
    2. 2.2. 2.2 project
    3. 2.3. 2.3 add_executable / add_library
    4. 2.4. 2.4 set
    5. 2.5. 2.5 message
    6. 2.6. 2.6 get_filename_component
    7. 2.7. 2.7 aux_source_directory
    8. 2.8. 2.8 target_sources
    9. 2.9. 2.9 target_include_directories
    10. 2.10. 2.10 CMake 添加子工程
    11. 2.11. 2.11 CMake 内置的常用宏或变量
    12. 2.12. 2.12 CMake Module
    13. 2.13. 2.13 配置&构建

1. 入门案例

创建如下工程目录

1
2
3
helloProject
|-------CMakeLists.txt
|-------hello.cpp

CMakeLists.txt

1
2
3
4
5
cmake_minimum_required(VERSION 3.10)

project(hello)

add_executable(${PROJECT_NAME} hello.cpp)

hello.cpp

1
2
3
4
5
6
7
#include<iostream>

int main()
{
std::cout << "hello world! welcome to WPS" << std::endl;
return 0;
}

在 hello.cpp 的目录下,输入构建命令:

1
2
3
4
mkdir Debug
cd Debug
cmake -B . -S ../hello
cmake --build .

2. CMake 构建项目之路

2.1 cmake_minimum_required

1
2
# 指定 cmake 的最小版本
cmake_minimum_required(VERSION x.x.x)
  • 该命令为可选,但强烈 建议添加
  • 当 CMakeLists.txt 中使用了 高版本 CMake 特有的命令时,必须添加该命令,用于提醒用户 升级到对应版本后 再执行 CMake。

2.2 project

1
2
3
4
5
6
7
# 指定项目信息
project(
Demo
LANGUAGES CXX C
DESCRIPTION "My own game code"
VERSION 0.1.0
)
  • 该命令非强制性,但 建议 所有项目 都添加
  • 第一个参数(项目名)为必填,其余参数可选。
  • 定义项目名后,CMake 会自动定义两个通用变量:
    • PROJECT_BINARY_DIR:工程 构建 所在目录
    • PROJECT_SOURCE_DIR:工程 源码 所在目录

2.3 add_executable / add_library

add_executable

  • 功能:在项目中添加一个 可执行文件 的目标
  • 语法:
1
2
3
4
add_executable(<name> <options>... <sources>...)

示例:
add_executable(demo demo.cpp)#生成可执行文件
  • 参数解释:
    • <**name**> :指定可执行文件的名称,既是生成的可执行文件名,也是 CMake 中 引用 该目标的名称
    • <**options**> :可选参数,用于指定特定选项/属性
    • <**sources**> :源文件列表,用于构建可执行文件,支持 C、C++、汇编等类型

add_library

  • 生成静态库
1
2
3
add_library(<静态库目标名> STATIC <源文件列表>)
# 示例
add_library(mylib_static STATIC src/mylib.cpp)
  • 生成动态库
1
2
3
add_library(<动态库目标名> SHARED <源文件列表>)
# 示例
add_library(mylib_shared SHARED src/mylib.cpp)

2.4 set

  • 功能:用于设置 变量的值
  • 语法:
1
set(<variable> <value>... [PARENT_SCOPE])
  • 示例:
1
2
set(var "Hello, CMake!")
${var} # 引用变量的值
1
set(CMAKE_CXX_STANDARD 11) # 设置C++标准为C++11

2.5 message

  • 功能:用于在 CMake 中打印相应的信息
  • 语法:
1
message([<mode>] "message text" [arg1 [arg2 ...]])
  • mode 用于指定输出消息的 模式,常用 mode 参数:
    • STATUS:以 普通 信息的形式输出
    • WARNING:以 警告 信息的形式输出
    • AUTHOR_WARNING:以 作者警告 信息的形式输出
    • SEND_ERROR:以 错误 信息的形式输出,并 停止构建 过程
    • FATAL_ERROR:以 致命错误 信息的形式输出,并 立即终止构建 过程
  • 完整示例:
1
2
3
4
5
6
7
8
9
10
11
cmake_minimum_required(VERSION 3.10)

project(hello)

set(SRC_FILE hello.cpp)

add_executable(${PROJECT_NAME} ${SRC_FILE})

message(STATUS "SRC_FILE = ${SRC_FILE}")
message(STATUS "PROJECT_BINARY_DIR = ${PROJECT_BINARY_DIR}")
message(STATUS "PROJECT_SOURCE_DIR = ${PROJECT_SOURCE_DIR}")

2.6 get_filename_component

  • 功能:用于对 文件路径 进行 解析,提取文件名、目录、扩展名等相关信息
  • 语法:
1
get_filename_component(<VAR> <FileName> <COMPONENT> [ABSOLUTE] [CACHE])

参数解释

  • <**VAR**> :存储解析 结果变量名

  • <**FileName**> :要解析的文件 路径,可以是 绝对 路径,也可以是 相对 路径

  • <**COMPONENT**> :指定要提取的文件路径信息的组件,常见取值有以下几种:

    • PATH:提取文件路径中的 目录 部分
    • NAME:提取文件的 完整名称(包含 扩展名
    • EXT:提取文件的 扩展名
    • ABSOLUTE:将 相对 路径转换为 绝对 路径
  • [CACHE]:可选参数,指定后将结果存入CMake缓存

  • 示例:

1
2
3
4
5
6
7
# 获取当前CMake文件所在目录的完整路径
# CMAKE_CURRENT_LIST_DIR 表示当前CMakeLists.txt所在目录
get_filename_component(CURRENT_DIR_PATH ${CMAKE_CURRENT_LIST_DIR} ABSOLUTE)

# 从完整路径中提取目录名称
get_filename_component(CURRENT_DIR_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
project(${CURRENT_DIR_NAME})

2.7 aux_source_directory

  • 功能:发现目录中 所有的文件
  • 语法:
1
aux_source_directory(<dir> <variable>)
  • 示例:
1
2
3
4
5
6
7
8
9
10
cmake_minimum_required(VERSION 3.10)

project(firstproject)

aux_source_directory(. SRCS) # 当前目录中的文件路径,都存储到SRCS

message(STATUS ${SRCS})

#add_executable(firstproject main.cpp calc.h calc.cpp)
add_executable(firstproject ${SRCS})
  • 重要提示
    属于「省事但略黑盒」的写法,不推荐 使用。
    推荐使用 target_sourcesset(SOURCES …) 逐项列出,依赖关系清晰、可控,也方便 IDE/工具分析。

2.8 target_sources

  • 功能:用于为 CMake 目标 添加源文件,增强 CMake 列表文件的模块化,让源文件管理更灵活
  • 语法:
1
2
3
4
target_sources(<target>
<INTERFACE|PUBLIC|PRIVATE>
<source1> <source2> ...
)

参数解释

  • <**target**> :要求添加的目标

  • PRIVATE:源文件 仅对目标自身可见,不会传播到链接该目标的其他目标,会填充SOURCES 属性

  • PUBLIC:源文件既用于目标自身的构建,也会 提供给链接该目标的其他目标使用,会 同时 填充 SOURCESINTERFACE_SOURCES 属性

  • INTERFACE:指定这些源文件对其他链接到该目标的目标可见,但 不会包含在目标本身的构建中,通常用于 接口库,会填充 INTERFACE_SOURCES 属性

  • <**source1**> <**source2**>: 需要添加的源码

  • 示例:

1
2
3
add_library(my_library STATIC lib1.cpp lib2.cpp)
# 添加更多源文件到 my_library
target_sources(my_library PRIVATE lib3.cpp lib4.cpp)

2.9 target_include_directories

  • 功能:指定要包含的头文件目录,头文件目录包含了用于编译源代码的头文件。针对目标添加头文件,用于保持项目组织结构清晰、解决头文件依赖问题
  • 语法:
1
2
3
4
5
target_include_directories(<target>
[SYSTEM]
[AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE>
dir1 [dir2 ...])

参数解释

  • <**target**> :目标名称,必须是通过 add_executable()/add_library() 先创建出来的可执行文件或库

  • SYSTEM:可选参数,指定被包含的目录为 系统目录,避免编译器产生警告

  • AFTER/BEFORE:可选关键字,BEFORE 指定包含目录应该在默认目录之前被添加到编译器的搜索路径;AFTER 则是默认情况,将包含目录添加到已有的包含目录之后

  • INTERFACE|PUBLIC|PRIVATE:作用域,规则同 target_sources
    PRIVATE: 只对当前目标的 源文件 使用
    INTERFACE: 目标对应的 头文件 使用
    PUBLIC: 目标对应的 头文件源文件 都使用

  • dir1, dir2:要添加为包含目录的 路径列表,可以是目录名、绝对路径或相对路径

  • 示例:

1
2
set(HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../Person)
target_include_directories(${PROJECT_NAME} PRIVATE ${HEADER_DIR})

2.10 CMake 添加子工程

  • 核心命令:add_subdirectory
1
2
  
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • 说明:
    add_subdirectory 是 CMake 中用于包含其他子目录的命令,允许将其他目录中的 CMakeLists.txt 文件包含到当前项目的构建过程中,适用于结构复杂、需要分多个子模块/子项目的大型项目。
  • 目录结构示例:
1
2
3
4
5
6
7
8
top_project/
├── CMakeLists.txt
├── src/
│ ├── CMakeLists.txt
│ └── main.cpp
└── subdirectory/
├── CMakeLists.txt
└── subdir_source.cpp
  • 顶层 CMakeLists.txt 示例:
1
2
3
4
5
6
7
8
# /top_project/CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(YourProject)

# 添加子目录
add_subdirectory(src)
add_subdirectory(src/subdirectory)

2.11 CMake 内置的常用宏或变量

CMAKE_SYSTEM_NAME

  • 标识当前操作系统(如 Linux、Windows、Darwin(macOS))
  • 示例:
1
2
3
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Linux 特定配置
endif()

CMAKE_BUILD_TYPE

  • 构建类型(如 DebugRelease
  • 示例:
1
2
3
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# Debug 模式配置
endif()

编译位数

  • 用于判断32位/64位编译
  • 示例:
1
2
3
4
5
6
7
8
9
10
11
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(CMAKE_64 TRUE)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(CMAKE_32 TRUE)
endif()

# 或者
if(NOT CMAKE_CL_64)
# 32位编译
wps_add_subdirectory(dcinfotool)
endif()

2.12 CMake Module

  • 文件后缀名:.cmake

  • include(xxx)或放在模块路径里由find_package() 等加载使用,常用于抽取公共逻辑、宏、函数

  • 示例:自定义模块 cmake/helpers.cmake

1
2
3
4
5
6
7
8
9
10
11
function(add_strict_warning_flags target)
if(MSVC)
target_compile_options(${target} PRIVATE /W4 /WX)
else()
target_compile_options(${target} PRIVATE -Wall -Wextra -Werror)
endif()
endfunction()

macro(log_message msg)
message(STATUS "Configure: ${msg}")
endmacro()

顶层 CMakeLists.txt 使用示例

1
2
3
4
5
6
cmake_minimum_required(VERSION 3.10)
project(MyApp LANGUAGES CXX)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/helpers.cmake)
log_configure_status("project configured")
add_executable(app main.cpp)
add_strict_warning_flags(app)

2.13 配置&构建

配置

1
2
3
4
mkdir Debug && cd Debug

::把代码构建xx.sln文件
cmake .. -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Debug ../Coding

常用构建参数说明
-S <path-to-source> :指定 源代码目录
-B <path-to-build> :指定 构建目录
-G <generator> :指定 生成器(如”Ninja”,”Visual Studio 17 2022”)
-A <platform> :指定 目标平台架构(仅Visual Studio生成器,如 x64、Win32、ARM64)

构建

打开MSVC编译命令行终端(以实际安装路径为准):

1
2
3
4
5
::Visual Studio 2019
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvarsamd64_x86.bat"

::Visual Studio 2022
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsamd64_x86.bat"

运行命令:

1
2
3
4
::注意目录
msbuild /m PersonMain.sln /p:Platform=x64 /p:Configuration=Debug
::更通用的写法
cmake --build .

多配置生成器(Visual Studio、Xcode):
构建时用--config指明编译类型

1
2
cmake --build . --config Debug -j 8
cmake --build . --config Release --clean-first

常用构建参数

  • –target :指定构建目标(如 all,clean, 特定可执行文件等)
  • –config :指定构建配置(Debug/Release 等),对于多配置生成器
  • –parallel [] 或 -j [ ] :并行构建(N 为并行任务数)
  • –clean-first: 先清理再构建