CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程),能够输出各种各样的makefile或者project文件。可以理解为,编写Makefile难度太大,CMake基于Makefile做了二次开发。
我使用的是Ubuntu 22.04.2,在终端输入
sudo apt install cmake
完成之后,检查版本号,看是否安装成功
cmake -version
结果是
zxm@ubuntu:~$ cmake -version cmake version 3.22.1
一、一个目录下有一个源文件(入门)
- CMAKE_CURRENT_LIST_DIR:CMakeLists 所在的路径
- CMAKE_BINARY_DIR 、PROJECT_BINARY_DIR、<projectname>_BINARY_DIR: 工程编译发生的目录。表示当前的构建目录,即执行 CMake 构建命令时所在的目录的路径。
- CMAKE_SOURCE_DIR、PROJECT_SOURCE_DIR、<projectname>_BINARY_DIR: 工程顶层目录。表示包含项目名的最近一个 CMakeLists.txt 文件所在的目录的路径。
- CMAKE_CURRENT_SOURCE_DIR:表示当前正在处理的 CMakeLists.txt 文件的所在目录的绝对路径。
- CMAKE_C_COMPILER:指定 C 编译器
- CMAKE_CXX_COMPILER:指定 C++ 编译器
- EXECUTABLE_OUTPUT_PATH:可执行文件输出的存放路径
- LIBRARY_OUTPUT_PATH:库文件输出的存放路径
- CMAKE_BUILD_TYPE: 构建的类型,例如 Debug(默认有-g) ,Release
1.2 语法介绍
cmake_minimum_required
用于指定 cmake 的最小版本要求。
cmake_minimum_required (VERSION 2.8)
project
通常作为 CMakeLists.txt 文件的第一条指令出现,用于定义项目的基本信息。通过设置属性,可以提供项目的名称、版本号、描述等信息,并指定项目所用的编程语言。
project(<project_name> [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] [DESCRIPTION <project_description>] [HOMEPAGE_URL <homepage_url>] [LANGUAGES <language>...])
其中,<project_name> 是项目的名称,是 project 指令的必填参数。
<major>, <minor>,<patch>,<tweak> 可选地指定项目的版本号。
除了名称和版本号外,project 指令还支持设置其他可选属性:
-DESCRIPTION:可选地设置项目的描述信息。
HOMEPAGE_URL:可选地设置项目的主页 URL。
LANGUAGES:可选地指定项目使用的编程语言。默认情况表示支持所有语言。
set
用于创建新的变量,修改已存在的变量的值,以及删除变量。
set(<variable_name> <value> CACHE <type> <docstring> [FORCE])
其中:
- <variable_name> 是要设置的变量名。
- <value> 是要设置的变量的值。
- CACHE 是一个可选的关键字,用于将变量设置为缓存变量,允许用户通过命令行或图形界面进行配置。
- <type> 是变量的类型,可以是 STRING、BOOL、PATH 等。
- <docstring> 是关于这个变量的描述文本,可选。
- FORCE 是一个可选的关键字,用于强制覆盖之前定义的同名变量的值。
举个例子
// 设置了一个名为 SRC_FILES 的变量,其值为一组源文件的路径。 set(SRC_FILES src/main.cpp src/foo.cpp) // 设置了一个名为 ENABLE_DEBUG 的缓存变量,其值为布尔类型,用于控制是否启用调试模式。 set(ENABLE_DEBUG TRUE CACHE BOOL "Enable debug mode") // 设置了一个名为 MY_PATH 的缓存变量,其值为路径类型,指定了某个目录的路径。 set(MY_PATH "/path/to/something" CACHE PATH "Path to something")
message
用于在配置过程中打印一些特定信息,方便调试和打印状态。
message([<mode>] "message to display" ...)
其中,<mode> 是一个可选参数,用于指定消息的模式。常见的模式有以下几种:
- STATUS:将消息以普通状态打印出来。
- WARNING:将消息以警告的方式打印出来。
- AUTHOR_WARNING:将消息以作者警告的方式打印出来。
- SEND_ERROR:将消息以错误的方式打印出来,并停止配置过程。
- FATAL_ERROR:将消息以致命错误的方式打印出来,并终止配置和构建过程。
add_executable
创建可执行文件目标,用于将一组源文件编译为可执行文件。
add_executable(<target_name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 [source2 ...])
其中
- <target_name> 是要创建的可执行文件的名称,可以自定义。
- [WIN32] 和 [MACOSX_BUNDLE] 是可选的标志,用于指定平台特定的属性。
- [EXCLUDE_FROM_ALL] 也是可选的标志,用于将目标从默认构建中排除。
举个例子
// 创建了一个名为 MyExecutable 的可执行文件目标, // 并指定了三个源文件 main.cpp、foo.cpp 和 bar.cpp。 add_executable(MyExecutable main.cpp foo.cpp bar.cpp)
1.3 最基础的实例
1)创建两个文件main.c和CMakeLists.txt
main.c里面编写
#include <stdio.h> int main(){ printf("This is CMake;\n"); return 0; }
CMakeLists.txt里面编写
# 单个目录的实现 # CMake最低版本号的要求 cmake_minimum_required(VERSION 2.8) # 工程项目的名称,注意不是执行文件名 project(Zxm) # 手动加入文件 ${变量名},比如${SRC_LIST} set(SRC_LIST main.c) # message 和 echo 类型 message(STATUS "PROJECT_BINARY_DIR DIR " ${PROJECT_BINARY_DIR}) message(STATUS "PROJECT_SOURCE_DIR DIR " ${PROJECT_SOURCE_DIR}) # 创建可执行文件,名为hello add_executable(hello ${SRC_LIST})
2)在终端输入cmake .,.表示当前目录
生成了下面几个文件
3)在终端输入make,生成可执行文件hello
3)执行./hello
4)如果还有一个main2.c,那么修改CMakeLists.txt,其他的一样
#单个目录的实现 # CMake最低版本号的要求 cmake_minimum_required(VERSION 2.8) # 工程项目的名称,注意不是执行文件名 project(Zxm) # 手动加入文件 ${变量名},比如${SRC_LIST} set(SRC_LIST main.c) set(SRC_LIST2 main2.c) # message 和 echo 类型 message(STATUS "PROJECT_BINARY_DIR DIR " ${PROJECT_BINARY_DIR}) message(STATUS "PROJECT_SOURCE_DIR DIR " ${PROJECT_SOURCE_DIR}) # 创建可执行文件,名为hello add_executable(hello ${SRC_LIST}) add_executable(hello2 ${SRC_LIST})
1.4 第一步优化:build目录
把很多生成的文件和源码混在一起,是一个很容易挨打的行为。我们可以单独创建一个子目录build,用来存放cmke后的结果。
实操流程
1)创建子目录build,进入到build
cd build/
2)终端输入cmake ..
# ..表示二级目录 cmake ..
3)终端输入make
这样就显得干净多了
1.5 第二步优化:src目录
为工程添加一个子目录src,用来放置工程源代码。结构如图
1.5.1 实操流程
1)创建子目录src,把main.c移到该目录下。并为任何子目录建立一个 CMakeLists.txt(跟工程1的一致)
#单个目录的实现 # CMake最低版本号的要求 cmake_minimum_required(VERSION 2.8) # 工程项目的名称,注意不是执行文件名 project(Zxm) # 手动加入文件 ${变量名},比如${SRC_LIST} set(SRC_LIST main.c) # message 和 echo 类型 message(STATUS "PROJECT_BINARY_DIR DIR " ${PROJECT_BINARY_DIR}) message(STATUS "PROJECT_SOURCE_DIR DIR " ${PROJECT_SOURCE_DIR}) message(STATUS "CMAKE_CURRENT_SOURCE_DIR " ${CMAKE_CURRENT_SOURCE_DIR}) # 创建可执行文件,名为hello add_executable(hello ${SRC_LIST})
2)在主目录下,建立一个 CMakeLists.txt。主要点是add_subdirectory(src bin)
#单个目录的实现 # CMake最低版本号的要求 cmake_minimum_required(VERSION 2.8) # 工程项目的名称,注意不是执行文件名 project(Zxm) # 手动加入文件 ${变量名},比如${SRC_LIST} set(SRC_LIST main.c) # message 和 echo 类型 message(STATUS "PROJECT_BINARY_DIR DIR " ${PROJECT_BINARY_DIR}) message(STATUS "PROJECT_SOURCE_DIR DIR " ${PROJECT_SOURCE_DIR}) # CMAKE_CURRENT_SOURCE_DIR它的CMakeLists.txt所在的当前源目 MESSAGE(STATUS "CMAKE_CURRENT_SOURCE_DIR " ${CMAKE_CURRENT_SOURCE_DIR}) # 添加子目录src,并将生成的二进制文件存放到 bin 目录中 add_subdirectory(src bin)
3)进入build目录,执行cmake .. 和 make
cd build cmake .. make
注意到生成的可执行文件是存放到 bin 目录中,因此需要在bin目录下执行
cd bin ./hello
1.5.2 add_subdirectory
用于向 CMake 构建系统添加一个子目录。这个子目录可以包含另一个独立的 CMakeLists.txt 文件,用于管理子目录下的源码、库文件或其他构建内容。
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
其中:
- source_dir:要添加为子目录的源码目录,需要包含一个独立的 CMakeLists.txt 文件。
- binary_dir:可选参数,指定生成的二进制文件(如可执行文件、库文件)所存放的目录。如果不提供该参数,则默认为在当前构建目录生成二进制文件。
- EXCLUDE_FROM_ALL:可选参数,表示将该子目录排除在默认构建(make 或 build)之外。
举个例子,假设我们的项目包含两个子目录,分别是 src 和 tests:
project/ ├── CMakeLists.txt ├── src/ │ ├── CMakeLists.txt │ └── ... └── tests/ ├── CMakeLists.txt └── ...
在主项目的 CMakeLists.txt 文件中,我们可以使用 add_subdirectory命令将这两个子目录添加进来:
add_subdirectory(src ) add_subdirectory(tests)
这样,在构建过程中,CMake 会先依次进入 src 目录和 tests 目录,执行这两个子目录下的 CMakeLists.txt 文件,分别进行对应的构建操作。