CMake

概述

CMake 是一个跨平台的开源构建工具,使用 CMake 能够方便地管理依赖多个库的目录层次结构并生成 makefile 和使用 GNU make 来编译和连接程序。

入门

CMakeLists.txt 内容如下:

1
2
3
cmake_minimum_required(VERSION 3.18) # 设置 cmake 的最低版本
project(learn-cmake) # 设置项目名字
add_executable(main main.cpp) # 设定编译生成的二进制文件名字

上面这三个命令是必须的,有这三行就可以编译最简单的一个 main.cpp 了。

进阶

对于复杂项目,可在项目根目录放一个CMakeLists.txt文件,用于指定全局配置,在子项目里写各自的CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 顶级 CMakeLists.txt
cmake_minimum_required(VERSION 3.25) # 指定 cmake 最低版本
project(learn-cmake VERSION 0.1 LANGUAGES C CXX) # 指定项目名、版本和编程语言(C 和 C++ 混合编译)

set(CMAKE_INCLUDE_CURRENT_DIR ON) # 将当前路径添加为头文件包含路径,非必要

set(CMAKE_C_STANDARD 11) # 指定 C 版本
set(CMAKE_C_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD 17) # 指定 C++ 版本
set(CMAKE_CXX_STANDARD_REQUIRED True)

message(STATUS ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_VERSION})

# 添加头文件包含路径
include_directories(${CMAKE_SOURCE_DIR}/3rd/include)
# 添加库链接路径
link_directories(
${CMAKE_SOURCE_DIR}/3rd/lib/${CMAKE_SYSTEM_NAME}
${CMAKE_SOURCE_DIR}/3rd/lib/${CMAKE_SYSTEM_NAME}/${CMAKE_BUILD_TYPE}
${CMAKE_SOURCE_DIR}/3rd/lib/${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}
${CMAKE_SOURCE_DIR}/3rd/lib/${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}/${CMAKE_BUILD_TYPE}
)
# 链接库
if (LINUX)
link_libraries(pthread)
elseif(APPLE)
elseif(WIN32)
endif()

add_subdirectory(tools) # 子项目

file(CREATE_LINK ${CMAKE_SOURCE_DIR}/data ${CMAKE_BINARY_DIR}/data SYMBOLIC)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 子项目 CMakeLists.txt
cmake_minimum_required(VERSION 3.25) # 指定 cmake 最低版本
project(tools VERSION 0.1 LANGUAGES C CXX) # 指定项目名、版本和编程语言(C 和 C++ 混合编译)

set(CMAKE_INCLUDE_CURRENT_DIR ON) # 将当前路径添加为头文件包含路径,非必要

set(CMAKE_C_STANDARD 11) # 指定 C 版本
set(CMAKE_C_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD 17) # 指定 C++ 版本
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 指定 find_package 查找路径
if (LINUX)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "/opt/Qt/6.4.1/gcc_64")
elseif(APPLE)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "/Applications/Qt/6.4.2/macos")
set(CMAKE_OSX_ARCHITECTURES x86_64 arm64)
elseif(WIN32)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:/Qt/6.4.2/msvc2019_64")
endif()
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets WebEngineWidgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets WebEngineWidgets)

# 添加头文件包含路径
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/utils
)
# 添加库链接路径
link_directories()
# 链接库
link_libraries()

# 收集源码文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}src SOURCES)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src/utils SOURCES)

# 编译二进制
add_executable(${PROJECT_NAME} ${SOURCES})
add_library(${PROJECT_NAME}-shared SHARED ${SOURCES}) # 动态库
add_library(${PROJECT_NAME}-static STATIC ${SOURCES}) # 静态库

# 重命名库文件
set_target_properties(${PROJECT_NAME}-shared PROPERTIES OUTPUT_NAME qtopenglwidget)
# set_target_properties(${PROJECT_NAME} PROPERTIES CLEAN_DIRECT_OUTPUT 1) # 清理原库文件
set_target_properties(${PROJECT_NAME}-static PROPERTIES OUTPUT_NAME qtopenglwidget)

add_subdirectory(test) # 项目测试用例

# 收集头文件
file(GLOB HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h)
file(COPY ${HEADERS} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 子项目 CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(${PROJECT_NAME}-test VERSION 0.1 LANGUAGES C CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 指定 rpath
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
set(CMAKE_INSTALL_RPATH ..)
elseif(${CMAKE_BUILD_TYPE} MATCHES "Release")
set(CMAKE_INSTALL_RPATH .)
endif()

# 收集源码文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} TEST_SRCS)

# 编译二进制
add_executable(${PROJECT_NAME} ${TEST_SRCS})
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/../include
)
target_link_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/../
)
target_link_libraries(${PROJECT_NAME}
gtest gmock libtools.so
)

变量

  • CMAKE_SOURCE_DIR 是 CMake 内置的变量之一,它表示项目根目录的路径。
    当使用 CMake 构建项目时,通常会将 CMakeLists.txt 文件放在项目根目录中,此时可以使用 CMAKE_SOURCE_DIR 变量来引用项目根目录中的文件或目录。例如,${CMAKE_SOURCE_DIR}/include 表示项目根目录中的 include 目录的路径。
    需要注意的是,CMAKE_SOURCE_DIR 表示的是项目根目录的路径,而不是当前处理的 CMakeLists.txt 文件所在的目录的路径。如果需要引用当前处理的 CMakeLists.txt 文件所在目录的路径,应该使用 CMAKE_CURRENT_LIST_DIR 变量。
  • CMAKE_CURRENT_SOURCE_DIR 是 CMake 内置的变量之一,它表示当前处理的 CMakeLists.txt 文件所在目录的路径。
    当使用 CMake 构建项目时,可以使用 CMAKE_CURRENT_SOURCE_DIR 来引用当前处理的 CMakeLists.txt 文件所在目录中的文件或目录。例如,${CMAKE_CURRENT_SOURCE_DIR}/src 表示当前处理的 CMakeLists.txt 文件所在目录中的 src 目录的路径。
    需要注意的是,CMAKE_CURRENT_SOURCE_DIR 表示的是当前处理的 CMakeLists.txt 文件所在目录的路径,而不是项目根目录的路径。如果需要引用项目根目录的路径,应该使用 CMAKE_SOURCE_DIR 变量。
  • CMAKE_CURRENT_LIST_DIR是一个 CMake 内置的变量,它表示当前处理的 CMakeLists.txt 文件所在的目录的路径。该变量的值在 CMake 运行时动态计算,因此它会随着处理的 CMakeLists.txt 文件的不同而发生变化。
    通过使用 CMAKE_CURRENT_LIST_DIR 变量,可以方便地引用当前处理的 CMakeLists.txt 文件所在目录中的文件或目录。例如,可以使用 ${CMAKE_CURRENT_LIST_DIR}/include 来引用当前处理的 CMakeLists.txt 文件所在目录中的 include 目录。
  • PROJECT_ROOTCMAKE_SOURCE_DIR 的区别
    PROJECT_ROOT 不是 CMake 内置的变量,它通常是由项目的开发者在 CMakeLists.txt 文件中定义的。与此不同,CMAKE_SOURCE_DIR 是 CMake 内置的变量,它指向项目源代码根目录的路径。
    因此,PROJECT_ROOTCMAKE_SOURCE_DIR 的区别在于其定义的位置和目的。PROJECT_ROOT 是由项目开发者定义的变量,可以用于自定义项目的根目录路径,而 CMAKE_SOURCE_DIR 是 CMake 内置的变量,指向项目源代码根目录的路径。
  • CMAKE_CURRENT_LIST_DIRCMAKE_CURRENT_SOURCE_DIR 的区别
    CMAKE_CURRENT_LIST_DIRCMAKE_CURRENT_SOURCE_DIR 都是 CMake 内置的变量,它们的作用是表示当前处理的 CMakeLists.txt 文件所在的目录的路径。
    区别在于,CMAKE_CURRENT_LIST_DIR 表示的是当前处理的 CMakeLists.txt 文件所在的目录的路径,而 CMAKE_CURRENT_SOURCE_DIR 表示的是当前处理的源文件所在的目录的路径。
    如果 CMakeLists.txt 文件包含在一个子目录中,则 CMAKE_CURRENT_LIST_DIRCMAKE_CURRENT_SOURCE_DIR 的值可能不同,因为它们表示的是不同的目录。例如,如果 CMakeLists.txt 文件位于项目根目录的 src 子目录中,而源文件位于 src 子目录的 lib 子目录中,则 CMAKE_CURRENT_LIST_DIR 的值为 ${PROJECT_SOURCE_DIR}/src,而 CMAKE_CURRENT_SOURCE_DIR 的值为 ${PROJECT_SOURCE_DIR}/src/lib
    因此,需要根据具体情况选择使用 CMAKE_CURRENT_LIST_DIR 还是 CMAKE_CURRENT_SOURCE_DIR。如果需要引用当前处理的 CMakeLists.txt 文件所在目录中的文件或目录,应该使用 CMAKE_CURRENT_LIST_DIR;如果需要引用当前源文件所在目录中的文件或目录,应该使用 CMAKE_CURRENT_SOURCE_DIR

配置

1
2
3
4
5
6
7
8
{
"cmake.configureEnvironment": {
"CMAKE_PREFIX_PATH": "C:/Qt/6.6.2/msvc2019_64"
},
"cmake.debugConfig": {
"args": []
}
}

CPack

参考文献


CMake
https://laplac2.github.io/tools/cmake/
作者
Laplace
发布于
2021年12月1日
许可协议