13. CMake工具的使用

news/2025/2/11 19:40:58/文章来源:https://www.cnblogs.com/FlurryHeart/p/18706466

一、什么是CMake工具

  CMake 是一个跨平台的构建系统生成器,主要用于管理和自动化软件项目的构建过程。它通过读取项目中的 CMakeLists.txt 文件来生成适用于不同编译器和操作系统的构建文件。

  对于大型或复杂的项目,直接编写和维护 Makefile 文件可能会变得非常复杂且容易出错,并且 Makefile 通常只适用于类 Unix 系统。此时,我们可以通过 CMake 的 CMakeLists.txt 文件提供的一种更高层次的抽象来使项目结构更加清晰,并减少了手动编写复杂规则的需求。 CMake 工具可以为不同的操作系统生成对应的构建系统文件,这使得 CMake 更易于在不同平台上进行开发和部署。CMake 内置了强大的依赖关系处理机制,能够自动检测并配置所需的外部库,这简化了第三方库的集成过程。相比之下,Makefile 需要开发者手动指定路径和链接选项。

  首先,我们需要在终端中通过 apt 命令来安装 CMake 工具。

sudo apt install -y cmake

  我们新建一个 hello.h 头文件,它的内容如下:

#ifndef __HELLO_H__
#define __HELLO_H__#include <stdio.h>void say_hello(char *name);#endif // !__HELLO_H__

  然后,我们在新建一个 hello.c 源文件,它的内容如下:

#include "hello.h"void say_hello(char *name)
{printf("Hello %s, nice to meet you!\n", name);
}

  接着,我们在新建一个 main.c 源文件,它的内容如下:

#include "hello.h"int main(void)
{say_hello("Sakura");return 0;
}

  然后,我们在新建一个 CMakeLists.txt 文件,它的内容如下:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)#[[
定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认支持所有语言)
如果不需要,这些都是可以省略的,只需要指定出工程名即可。
project(PROJECT_NAME[VERSION 版本号][DESCRIPTION 描述信息][PROJECT_HOMEPAGE_URL 主页地址][LANGUAGE 语言1 语言2 ...]
)
]]
project(TemplateVERSION 1.0.1DESCRIPTION 这是一个简易的C程序HOMEPAGE_URL https://www.cnblogs.com/FlurryHeartLANGUAGES C
)#[[
生成可执行程序,这里的可执行程序名和 project 中的项目名没有任何关系
add_executable(可执行程序 源文件名称1 [源文件名称2] ...)
源文件名可以是一个也可以是多个,如果有多个,可以通过空格或者 ; 分隔
]]
add_executable(main main.c hello.c)

  编写完 CMakeLists.txt 文件后,我们需要在终端中执行 cmake 命令。

cmake CMakeLists.txt 

  在执行完 cmake 命令后,我们可以看到在当前文件夹下多了 CMakeFiles 目录CMakeCache.txtcmake_install.cmakeMakefile 文件。

执行cmake命令后的结果

  如果,我们希望那些 CMakeFiles 目录CMakeCache.txtcmake_install.cmakeMakefile 文件生成一个指定的文件夹下,我们可以先创建一个空的文件夹,然后在该文件下运行 CMake。

在指定目录下生成中间文件

如果不指定 project 的 LANGUAGES 为 C 的话,可能报 No CMAKE_CXX_COMPILER could be found 错误,这是因为当前 Ubuntu 环境中没有 C++ 的编译器 g++。此时,我们可以使用 apt 安装:sudo apt install g++

二、变量的使用

  在 CMake 中,我们可以使用 set 来定义变量。在 CMake 中,变量默认都是字符串类型的。

  定义变量的格式如下:

# 定义变量,变量的值可以是一个也可以是多个,如果有多个,可以通过空格或者 ; 分隔
set(变量名 值1 [值2 ...])

  定义完变量之后,我们可以通过 $ 来使用变量。

# 变量取值
${变量名}

  修改 CMakeLists.txt 文件的内容:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# set定义变量
set(src_list main.c hello.c)# 生成可执行程序
add_executable(main ${src_list})

  我们还可以使用 list 命令 对字符串进行拼接

list(APPEND 变量名 字符串1 [字符串2 ...])

  同样,我们可以使用 list 命令 移除字符串

list(REMOVE_ITEM 要操作的列表 字符串1 [字符串2 ...])

  如果我们要 获取列表的长度 可以通过以下语法:

list(LENGTH 要操作的列表 存储列表的长度的变量)

  如果我们要 读取列表中指定索引的的元素 可以通过以下语法:

list(GET 要操作的列表 索引值1 [索引值2 ...] 存储指定索引的新列表)

  列表元素的索引从 0 开始编号,索引 0 的元素为列表中的第一个元素。索引也可以是负数,-1 表示列表的最后一个元素,-2 表示列表倒数第二个元素,以此类推。当索引(不管是正还是负)超过列表的长度,运行会报错。

  我们还可以 将列表中的元素用连接符(字符串)连接起来组成一个字符串

list(JOIN 要操作的列表 指定的连接符 存储返回的字符串的变量)

  我们还可以 查找列表是否存在指定的元素。如果找到,则在列表中的索引,如果未找到,则返回 -1。

list(FIND 要操作的列表 要查找的值 存储返回结果的变量)

  然后,我们还可以 将元素追加到列表中.

list(APPEND 要操作的列表 值1 [值2 ...])

  如果我们要将 元素插入到列表的 0 索引位置,可以通过以下语法:

list(PREPEND 要操作的列表 值1 [值2 ...])

  如果我们要 在列表中指定的位置插入若干元素,可以通过以下语法:

list(INSERT 要操作的列表 要插入位置的索引值 值1 [值2 ...])

  我们还可以 将列表的最后一个元素移除

list(POP_BACK 要操作的列表 [保存弹出值的变量])

  同样,我们可以 将列表中第一个元素移除

list(POP_FRONT 要操作的列表 [保存弹出值的变量])

  我们还可以 将指定索引的元素从列表中移除

list(REMOVE_AT 要操作的列表 索引值1 [索引值2 ...])

  我们还可以 移除列表中的重复元素

list(REMOVE_DUPLICATES 要操作的列表)

  我们还可以将 列表翻转

list(REVERSE 要操作的列表)

  同样,我们还可以 对列表进行排序

list (SORT 要操作的列表 [COMPARE 指定排序方法] [CASE 指明是否大小写敏感] [ORDER 指明排序的顺序])
  • COMPARE指定排序方法,有如下几种值可选:
    • STRING按照字母顺序进行排序,为默认的排序方法。
    • FILE_BASENAME:如果是一系列路径名,会 使用 basename 进行排序
    • NATURAL使用自然数顺序排序
  • CASE指明是否大小写敏感,有如下几种值可选:
    • SENSITIVE: 按照大小写敏感的方式进行排序,为默认值。
    • INSENSITIVE按照大小写不敏感方式进行排序
  • ORDER指明排序的顺序,有如下几种值可选:
    • ASCENDING按照升序排列,为默认值。
    • DESCENDING按照降序排列

三、指定使用的C标准

  在编写 C 语言程序的时候,可能会用到 C11、C17、C23 等新特性,那么就需要在编译的时候在编译命令中制定出要使用哪个标准:C 标准对应有一宏叫做 CMAKE_C_STANDARD(C++ 对应的宏是 CMAKE_CXX_STANDARD)。

  在 CMake 中想要指定 C 标准有两种方式:一种是在 CMakeLists.txt 中通过 set 命令指定。

# 设置C编译器使用的标准
set(CMAKE_C_STANDARD 11)

  另一种是在执行 cmake 命令的时候指定出这个宏的值。

cmake CMakeLists.txt -D CMAKE_C_STANDARD=11

四、指定可执行程序的生成目录

  在 CMake 中,我们可以使用 EXECUTABLE_OUTPUT_PATH指定可执行文件的生成目录

# 指定可执行文件的生成目录
set(EXECUTABLE_OUTPUT_PATH 可执行文件的生成目录)
# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# 指定C编译器的使用的C标准
set(CMAKE_C_STANDARD 11)# 指定可执行程序的生成目录
# PROJECT_SOURCE_DIR 是指执行cmake命令时,后面携带的那个路径,即CMakeLists.txt文件所在的目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)# 生成可执行程序
add_executable(main main.c hello.c)

指定可执行程序的生成目录

五、搜索文件

  如果一个项目里边的源文件很多,在编写 CMakeLists.txt 文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦也不现实。所以,在 CMake 中为我们提供了搜索文件的命令,可以使用 aux_source_directory 命令或者 file 命令。

  在 CMake 中使用 aux_source_directory 命令可以查找某个路径下的所有源文件,命令格式为:

# 搜素文件
aux_source_directory(directory variable)

  其中,directory 是指 要搜索的目录variable 用来 将从 directory 目录下搜索到的源文件列表存储到该变量中

  在 CMake 中也可以使用 file 命令可以查找某个路径下的所有源文件,命令格式为:

# 搜索文件,多个文件之间可以通过空格或 ; 分隔
file(GLOB/GLOB_RECURSE variable file1 [file2 ...])

  其中,GLOB 用来 将指定目录下搜索到的满足条件的所有文件名 file 生成一个列表,并将其存储到变量 variable中GLOB_RECURSE 用来 递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中file 用来 指定要搜索的文件路径和文件类型

  修改 CMakeLists.txt 文件的内容:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# 指定C编译器的使用的C标准
set(CMAKE_C_STANDARD 11)# 搜索源码文件
# PROJECT_SOURCE_DIR 是指执行cmake命令时,后面携带的那个路径,即CMakeLists.txt文件所在的目录
# aux_source_directory(${PROJECT_SOURCE_DIR} SRC_DIRS)# 搜索源码文件
# CMAKE_CURRENT_SOURCE_DIR 是指当前CMakeLists.txt所在的目录
file(GLOB SRC_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/*.c)# 指定可执行程序的生成目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)# 生成可执行程序
add_executable(main ${SRC_DIRS})

关于要搜索的文件路径和类型可加双引号,也可不加。

六、指定头文件路径

  在编译项目源文件的时候,很多时候都需要将源文件对应的头文件路径指定出来,这样才能保证在编译过程中编译器能够找到这些头文件,并顺利通过编译。此时,我们可以通过 CMake 的 include_directories 来指定头文件路径。

# 指定头文件路径,多个路径直接可以用空格或 ; 分隔
include_directories(头文件路径1 [头文件路径2 ...])

  这里,我们将在 CMakeLists.txt 文件所在的目录下新建两个目录【src】和【inc】。然后,将 main.c 和 hello.c 文件移动到【src】目录中,将 hello.h 文件移动到【inc】目录中。

  修改 CMakeLists.txt 文件的内容:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# 指定C编译器的使用的C标准
set(CMAKE_C_STANDARD 11)# 搜索源码文件
# CMAKE_CURRENT_SOURCE_DIR 是指当前CMakeLists.txt所在的目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_DIRS)# 指定头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)# 指定可执行程序的生成目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)# 生成可执行程序
add_executable(main ${SRC_DIRS})

指定头文件路径

七、制作链接库

  有些时候我们编写的源代码并不需要将他们编译生成可执行程序,而是生成一些静态库或动态库提供给第三方使用。

6.1、制作静态链接库

  在 CMake中,如果要制作静态库,需要使用的命令如下:

# 生成静态库
add_library(库名称 STATIC 源文件1 [源文件2 ...]) 

  在 Linux 中,静态库名字分为三部分:lib + 库名字 + .a,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。在 Windows 中虽然库名和 Linux 格式不同,但也只需指定出名字即可。

  这里,我们建一个【hello】文件夹,用来当作链接库的根目录,然后将【src】和【inc】文件夹移动到【hello】文件夹,接着将【src】中的 main.c 移出【hello】文件夹。

移动文件到指定模块中

  修改 CMakeLists.txt 文件的内容:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# 指定C编译器的使用的C标准
set(CMAKE_C_STANDARD 11)# 搜索源码文件
# CMAKE_CURRENT_SOURCE_DIR 是指当前CMakeLists.txt所在的目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/hello/src SRC_DIRS)# 指定头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/hello/inc)# 指定静态生成库的名字
add_library(hello STATIC ${SRC_DIRS})

制作静态链接库

6.2、制作动态链接库

  在 CMake中,如果要制作动态库,需要使用的命令如下:

# 生成动态库
add_library(库名称 SHARED 源文件1 [源文件2 ...]) 

  在 Linux 中,动态库名字分为三部分:lib + 库名字 + .so,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。在 Windows 中虽然库名和 Linux 格式不同,但也只需指定出名字即可。

  修改 CMakeLists.txt 文件的内容:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# 指定C编译器的使用的C标准
set(CMAKE_C_STANDARD 11)# 搜索源码文件
# CMAKE_CURRENT_SOURCE_DIR 是指当前CMakeLists.txt所在的目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/hello/src SRC_DIRS)# 指定头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/hello/inc)# 指定动态生成库的名字
add_library(hello SHARED ${SRC_DIRS})

制作动态链接库

6.3、指定链接库的输出路径

  在 CMake 中,我们可以使用 LIBRARY_OUTPUT_PATH 宏指定链接库的输出路径。

# 指定链接库的输出路径
set(LIBRARY_OUTPUT_PATH 库的输出路径)

  修改 CMakeLists.txt 文件的内容:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# 指定C编译器的使用的C标准
set(CMAKE_C_STANDARD 11)# 搜索源码文件
# CMAKE_CURRENT_SOURCE_DIR 是指当前CMakeLists.txt所在的目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/hello/src SRC_DIRS)# 指定头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/hello/inc)# 设置链接库生成路径
set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)# 指定静态生成库的名字
add_library(hello STATIC ${SRC_DIRS})

指定链接库的生成目录

七、链接库文件

  在编写程序的过程中,可能会用到一些系统提供的动态库或者自己制作出的动态库或者静态库文件,CMake 中也为我们提供了相关的加载链接库的命令。

  • 静态库 会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
  • 动态库 在生成可执行程序的时候,动态库不会被打包到可执行程序内部。当可执行程序启动之后动态库也不会被加载到内存,只有可执行程序调用了动态库中的函数的时候,动态库才会被加载到内存中,且多个进程可以共用内存中的同一个动态库,所以动态库又叫共享库。

  在 CMake中,链接库的命令如下:

# 链接库
link_libraries(链接库名1 [链接库2 ...])

  在 CMake中,我们还可以使用如下命令链接库:

# 链接库
target_link_libraries(目标 [PRIVATE|PUBLIC|INTERFACE 链接库1 ...])

  其中,目标是指要链接动态库的文件,它可以是一个 源文件,也可以是一个 库文件,还可以是一个 可执行文件。链接动态库时,我们需要指定不同的访问权限,默认为 PUBLIC

  • PUBLIC:在 PUBLIC 后面的库会被链接到前面的目标对象中,并且里面的符号也会被导出,提供给第三方使用(当前目标可以访问该库,而且任何链接到当前目标的其它目标也可以访问它)。
  • PRIVATE:在 PRIVATE 后面的库仅被链接到前面的目标对象中,并且终结掉,第三方不能感知你调了啥库(这个库只对当前目标有意义,而不会被传递给其它链接了此目标的目标)。
  • INTERFACE:在 INTERFACE 后面引入的库不会被链接到前面的目标对象中,只会导出符号(当其它目标链接到我时,它们需要这些设置,但我自己不需要)。
target_link_libraries(A PUBLIC B PRIVATE C INTERFACE D)
target_link_libraries(E A)

  动态库的链接具有传递性,如果动态库 A PUBLIC 链接动态库 B、PRIVATE 链接动态库 C,INTERFACE 链接动态库 D,而目标 E PUBLIC 链接动态库A。此时目标 E 相当于也链接了动态库 B,并可以使用动态库 B 中定义的方法。由于动态库 A INTERFACE 链接了动态库 D,因此目标 E 也可以使用动态库 D 中的设置。但是由于动态库 A PRIVATE 链接了动态库 C,这就导致目标 E 不知道 动态库 A 链接了动态库 C。

  如果该链接库不是系统提供的(自己制作或者使用第三方提供的静态库)可能出现链接库找不到的情况,此时可以将链接库的路径也指定出来:

# 指定链接库的路径
link_directories(静态库路径1 [静态库路径2 ...])

  修改 CMakeLists.txt 文件的内容:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# 指定C编译器的使用的C标准
set(CMAKE_C_STANDARD 11)# 搜索源码文件
# CMAKE_CURRENT_SOURCE_DIR 是指当前CMakeLists.txt所在的目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC_DIRS)# 指定头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/hello/inc)# 指定链接库的路径
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)# 链接库
link_libraries(hello)# 指定可执行程序的生成目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)# 生成可执行程序
add_executable(main ${SRC_DIRS})# 链接库
# target_link_libraries(main hello)

八、日志信息

  在 CMake 中,我们可以使用 message 命令显示一条消息。

message([打印消息的类别] "日记信息")

  打印消息的类别取值如下:

  • (无) :用于打印消息(信息白色)。
  • STATUS :编译时状态信息,左边以 -- 开头(信息白色)。
  • DEBUG:针对开发人员的调试信息(信息白色)。
  • TRACE:日志级别的临时信息(信息白色)。
  • WARNING:CMake 警告,继续编译(信息红色)。
  • AUTHOR_WARNING:开发者警告,继续编译(信息红色)。
  • SEND_ERROR:CMake 错误,继续编译,但是会停止生成(信息红色)。
  • FATAL_ERROR:CMake 错误,停止编译和生成(信息红色)。
  • EPRECATION:如果使用 set() 方法设置 CMAKE_ERROR_DEPRECATED 为 true(不区分大小写),编译出错,否则继续编译。

九、宏的使用

  在 gcc/g++ 命令中通过参数 -D 指定出要定义的宏的名字,这样就相当于在代码中定义了一个宏。在 CMake 中我们也可以做类似的事情,对应的命令叫做 add_definitions

add_definitions(-D 宏名称1 [-D 宏名称2 ...])

  修改 main.c 文件的内容:

#include "hello.h"int main(void)
{
#ifdef DEBUT_PROGRAM_BEGINprintf("program start!\n");
#endifsay_hello("Sakura");#ifdef DEBUT_PROGRAM_ENDprintf("progran end!\n");
#endifreturn 0;
}

  修改 CMakeLists.txt 文件的内容:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# 指定C编译器的使用的C标准
set(CMAKE_C_STANDARD 11)# 搜索源码文件
# CMAKE_CURRENT_SOURCE_DIR 是指当前CMakeLists.txt所在的目录
file(GLOB SRC_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/hello/src/*.c ${CMAKE_CURRENT_SOURCE_DIR}/*.c)# 指定头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/hello/inc)# 使用宏
add_definitions(-D DEBUT_PROGRAM_BEGIN -D DEBUT_PROGRAM_END)# 指定可执行程序的生成目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)# 生成可执行程序
add_executable(main ${SRC_DIRS})

  CMake 中帮我们定义了一些常用宏。

说明
PROJECT_SOURCE_DIR 使用 CMake 命令后紧跟的目录,一般是工程的根目录
PROJECT_BINARY_DIR 执行 CMake命令的目录
CMAKE_CURRENT_SOURCE_DIR 当前处理的 CMakeLists.txt 所在的路径
CMAKE_CURRENT_BINARY_DIR target 编译目录
EXECUTABLE_OUTPUT_PATH 重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH 重新定义目标链接库文件的存放位置
PROJECT_NAME 返回通过 PROJECT 指令定义的项目名称
CMAKE_BINARY_DIR 项目实际构建路径,假设在 build 目录进行的构建,那么得到的就是这个目录的路径

十、CMake的嵌套

  如果项目很大,或者项目中有很多的源码目录,在通过 CMake 管理项目的时候如果只使用一个 CMakeLists.txt,那么这个文件相对会比较复杂,有一种化繁为简的方式就是给每个源码目录都添加一个 CMakeLists.txt 文件(头文件目录不需要),这样每个文件都不会太复杂,而且更灵活,更容易维护。

  嵌套的 CMake 也是一个树状结构,最顶层的 CMakeLists.txt 是根节点,其次都是子节点。根节点 CMakeLists.txt 中的变量全局有效,父节点 CMakeLists.txt 中的变量可以在子节点中使用,子节点 CMakeLists.txt 中的变量只能在当前节点中使用。

  接下来我们还需要知道在 CMake 中父子节点之间的关系是如何建立的,这里需要用到 add_subdirectory 命令:

add_subdirectory(子节点对应的目录 [指定输出文件的路径] [在子路径下的目标默认不会被包含到父路径的ALL目标里])

  然后,我们修改根节点的 CMakeLists.txt 文件的内容:

# 指定CMake工具的最低版本
cmake_minimum_required(VERSION 3.15)# 定义工程名称
project(Template LANGUAGES C)# 指定C编译器的使用的C标准
set(CMAKE_C_STANDARD 11)# 搜索源码文件
# CMAKE_CURRENT_SOURCE_DIR 是指当前CMakeLists.txt所在的目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC_DIRS)# 指定头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/hello/inc)# 定义一个变量,用来存放链接库的路径
set(LIB_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/lib)# 添加子节点
add_subdirectory(hello)# 指定可执行程序的生成目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)# 生成可执行程序
add_executable(main ${SRC_DIRS})# 指定链接库的路径
link_directories(${LIB_DIRS})# 链接库
target_link_libraries(main hello)

  接着,我们在【hello】文件夹下新建一个 CMakeLists.txt 文件,该子文件主要用于生成链接库。

# 搜索源码文件
# CMAKE_CURRENT_SOURCE_DIR 是指当前CMakeLists.txt所在的目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_DIRS)# 指定头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)# 设置链接库生成路径
set(LIBRARY_OUTPUT_PATH ${LIB_DIRS})# 指定静态生成库的名字
add_library(hello STATIC ${SRC_DIRS})

在项目中,如果将程序中的某个模块制作成了动态库或者静态库并且在 CMakeLists.txt 中指定了库的输出目录,而后其它模块又需要加载这个生成的库文件,此时直接使用就可以了,如果没有指定库的输出路径或者需要直接加载外部提供的库文件,此时就需要使用 link_directories 将库文件路径指定出来。

十一、流程控制

  在 CMake 的 CMakeLists.txt 中也可以进行流程控制,也就是说可以像写 shell 脚本那样进行条件判断和循环。

11.1、条件判断

  关于条件判断其语法格式如下:

if(表达式1)要执行的命令1
[elseif(表达式2)要执行的命令2
]
...
[else()要执行的命令n
]
endif()

  在进行条件判断的时候,如果有多个条件,那么可以写多个 elseif,最后一个条件可以使用 else,但是开始和结束是必须要成对出现的,分别为:ifendif

  其中,表达式可以是 基本表达式,取值主要有以下三种情况:常量变量字符串。如果是 1ONYESTRUEY非零值非空字符串 时,条件判断返回 True。如果是 0OFFNOFALSENIGNORENOTFOUND空字符串 时,条件判断返回 False

  表达式还可以是 逻辑表达式NOT 表示 取反AND 表示 OR 表示

# 取反操作,如果条件为True将返回False,如果条件为False将返回True
NOT 表达式# 与操作,如果两个条件同时为True,返回True,否则返回False
表达式1 AND 表达式2# 或操作,两个条件中至少有一个为True,返回True,如果两个条件都为False,则返回False
表达式1 OR 表达式2

  表达式还可以是 关系表达式,主要有基于数值和基于字符串的比较。

  基于数值的关系表达式如下:

# 如果左侧数值小于右侧,返回True
数值表达式1 LESS 数值表达式2# 如果左侧数值大于右侧,返回True
数值表达式1 GREATER 数值表达式2# 如果左侧数值等于右侧,返回True
数值表达式1 EQUAL 数值表达式2# 如果左侧数值小于等于右侧,返回True
数值表达式1 LESS_EQUAL 数值表达式2# 如果左侧数值大于等于右侧,返回True
数值表达式1 GREATER_EQUAL 数值表达式2

  基于字符串的关系表达式如下:

# 如果左侧字符串小于右侧,返回True
字符串表达式1 STRLESS 字符串表达式2# 如果左侧字符串大于右侧,返回True
字符串表达式1 STRGREATER 字符串表达式2# 如果左侧字符串等于右侧,返回True
字符串表达式1 STREQUAL 字符串表达式2# 如果左侧字符串小于等于右侧,返回True
字符串表达式1 STRLESS_EQUAL 字符串表达式2# 如果左侧字符串大于等于右侧,返回True
字符串表达式1 STRGREATER_EQUAL 字符串表达式2

  我们还可以基于 文件操作 来判断。

# 判断文件或者目录是否存在,如果文件或者目录存在返回True,否则返回False
EXISTS 文件或目录# 判断是不是目录,此处目录的路径必须是绝对路径,如果目录存在返回True,目录不存在返回False
IS_DIRECTORY 文件路径# 判断是不是软连接,此处对应的路径必须是绝对路径,如果软链接存在返回True,软链接不存在返回False
IS_SYMLINK 文件名# 判断是不是绝对路径,如果是绝对路径返回True,如果不是绝对路径返回False
IS_ABSOLUTE 文件路径

  其中,还有一些其它的条件判断。

# 判断某个元素是否在列表中,如果这个元素在列表中返回True,否则返回False
元素 IN_LIST 列表# 比较两个路径是否相等,如果这个元素在列表中返回True,否则返回False
文件路径1 PATH_EQUAL 文件路径2

11.2、循环

  在 CMake 中循环有两种方式,分别是:foreachwhile

11.2.1、foreach循环

  使用 foreach 进行循环,语法格式如下:

foreach(迭代变量 可迭代对象)要执行的命令
endforeach()

  通过 foreach 我们就可以对可迭代对象中的数据进行遍历,然后通过迭代变量将遍历到的当前的值取出,在取值的时候有以下几种用法:

# 在对一个整数区间进行遍历的时候,得到的范围从0到范围的结束值,右侧是闭区间包含范围的结束值
foreach(迭代变量 RANGE 范围的结束值)# 得到的范围是从0到范围的开始值到范围的结束值,左右两侧都是闭区间,步长增长默认为1,可以不设置
foreach(迭代变量 RANGE 范围的起始值 范围的结束值 [步长增长])foreach(迭代变量 IN LISTS [列表名1 ...])
foreach(迭代变量 IN ITEMS [变量名1 ...])# 遍历多个列表,并把指定列表中的元素取出来
# 如果指定了多个迭代变量名,它们的数量应该和列表的数量相等
# 如果只给出了一个迭代变量value,那么它将一系列的value_N变量来存储对应列表中的当前项,也就是说value_0对应第一个列表,value_1 对应第二个列表,以此类推......
foreach(迭代变量 ... IN ZIP_LISTS 列表名1)

11.2.2、while循环

  while 的语法格式如下:

while(表达式)要执行的命令
endwhile()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/881341.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

「PMOI-5」奇怪的方程 题解

哎哎,感觉是很典的题啊,但还是不会。 一些无脑的转化 首先转化成二维数组,原题中 \(2n\) 个方程相当于必须满足每一行和每一列的数之和是定值,已被选的数可以让这个位置的行与列的总和分别减去这个数,然后直接令它等于 \(0\),显然这是与原条件等价的。 另外我们可以发现有…

P11216 【MX-J8-T4】2048 题解

这是本蒟蒻的第一篇正式题解。 本题重点在于找到无解序列的充要条件,首先记 \(a_i\) 为游戏结束时第 \(i\) 个数取 \(2\) 的对数,\(t_i\) 为第 \(i\) 个数出现时间离散化后的值,显然 \(a\) 相邻两项不同,\(t\) 是一个排列,如果一个位置比它相邻位置大且出现时间小,直接按…

问一下,利用在线 DeepSeek 等 API 服务实现一个答题 APP

这是一个利用 Android 无障碍功能 + 悬浮窗 + 大模型的搜题应用原理就是利用无障碍读取屏幕内容,然后通过悬浮窗来显示答案简介 这是一个利用 Android 无障碍功能 + 悬浮窗 + 大模型的搜题应用 原理就是利用无障碍读取屏幕内容,然后通过悬浮窗来显示答案 众所周知我是一个学渣…

Window逆向之x86 ShellCode入门

免责声明: 由于传播、利用本公众号所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!前言 不少人对于ShellCode的认知是很浅的,只知道它是一…

【字符串、栈】string转double

stod函数 将string转为double string t = s.substr(i, j - i); double num = stod(t);例题:货币单位换算样例1 输入 2 20CNY53fen 53HKD87cents输出 6432说明: 20元53分+53港元87港分,换算成人民币分后汇总,为6432 样例2 输入 1 100CNY输出 10000说明: 100CNY转换后是1000…

ceph 16.2.15(Pacific)编译

目录获取ceph源码编译拉取submodule网络问题安装依赖do_cmake.sh编译vstart启动问题编译dashboard安装nodejs方法一 下载编译好的源码包方法二 nvm安装node(推荐)编译nodeenv其他boost下载慢总结以下流程在ubuntu22.04 和 openEuler20.03 都实际操作过获取ceph源码 从https:/…

【第四期书生大模型实战营】第2关 L0G2000 Python 基础知识

任务 任务概览任务类型 任务内容 预计耗时闯关任务 Leetcode 383(笔记中提交代码与leetcode提交通过截图) 20mins闯关任务 Vscode连接InternStudio debug笔记 10mins可选任务 pip安装到指定目录 10mins作业总共分为三个任务,两个闯关任务均完成视作闯关成功。 请将作业发布到知…

Redis 持久化策略及其优缺点

原文:Redis 有哪 2 种持久化方式?分别的优缺点是什么?,补充了 Redis 默认的持久化配置Redis 的读写操作都是在内存中,所以 Redis 性能才会高,但是当 Redis 重启后,内存中的数据就会丢失,那为了保证内存中的数据不会丢失,Redis 实现了数据持久化的机制,这个机制会把数…

Innotop:一款MySQL监控工具

在现代数据库管理中,MySQL作为广泛应用的开源关系数据库,已成为各类企业、开发者和数据库管理员(DBA)日常工作中不可或缺的工具。然而,随着数据库规模的增大和查询量的增加,MySQL服务器的性能监控变得尤为重要。为了确保数据库的高效运行和及时排除潜在问题,DBA们需要依…

Windows本地部署deepseek(小白向)

下载Ollama官网下载:ollama.com 点击Download选中Windows版本,点击Download for Windows下载是需要跳转github的,如果无法访问,需要尝试使用github加速器 推荐使用加速器FastGithub(免费,开源) 这里提供国内清华镜像下载 https://cloud.tsinghua.edu.cn/d/df482a15afb64…

SSE、EventSource了解

EventSource接口是web内容和服务器发送事件通信的接口 一个EventSource实例会对HTTP服务器开启一个持久化的连接,以text/event-stream格式发送事件 该连接会一直保持开启直到调用EventSource.close()关闭EventSource是一种Web API,用于建立和服务器之间的【单向持久化】连接 …

基于GD32的简易示波器

基于GD32的简易示波器项目学习 根据立创训练营项目:[简易数字示波器设计(入门版) - 立创开源硬件平台(https://oshwhub.com/course-examples/yi-qi-yi-biao-jian-yi-shu-zi-shi-bo-qi-she-ji-cha-jian-ban) 技术点:原理图绘制,PCB设计,打板,焊接。外部中断,定时器中断,…