三、cmake语法-提高篇

目录

上一章节

一、前言

二、库文件的制作

1、静态库

2、动态库

3、指定库文件输出路径

三、库文件的使用

1、静态库的使用

1.1、指定库文件路径

2、动态库的使用

四、日志打印

1、消息等级:

五、变量操作

1、字符串的拼接

(1)set命令

[(2)list 命令](#(2)list 命令)

2、字符列表中剔除字符串

[3、List 命令介绍](#3、List 命令介绍)

[(1)LENGTH 获取字符串长度](#(1)LENGTH 获取字符串长度)

[(2)GET 列表通过索引获取字符串](#(2)GET 列表通过索引获取字符串)

(3)JOIN指定连接符将列表中字符串连接起来

(4)FIND在列表中查找字符串,并返回索引

(5)INSERT在列表指定位置后插入若干元素

(6)PREPEND在列表最前面插入元素

(7)POP_BACK移除列表最后一个元素

(8)POP_FRONT移除列表第一个元素

(9)REMOVE_AT移除列表指定位置元素

(10)ROMOVE_DUPLICATES移除重复元素

(11)REVERSE列表翻转

(12)SORT列表排序

六、自定义宏

[1、通过gcc, 命令编译指定宏](#1、通过gcc, 命令编译指定宏)

2、通过CMakeLists.txt中指定

七、总结


上一章节

https://blog.csdn.net/weixin_36323170/article/details/151336498?spm=1001.2014.3001.5501https://blog.csdn.net/weixin_36323170/article/details/151336498?spm=1001.2014.3001.5501

一、前言

开启本文之前先做个知识点的梳理,这里主要介绍库文件的制作与使用、日志打印、变量操作、宏定义等

二、库文件的制作

前面介绍了主要是生成可执行文件,但是我们在实际工作中会遇到,给别人提供第三方库的场景,这里就需要将开发的源代码编译成库文件,这里介绍在cmake下如何制作库文件

1、静态库

制作静态库命令如下:

bash 复制代码
add_library(库名称 STATIC 源文件1 [源文件2] ...) 

编译生成的库文件常见名称构成如下:

linux下:lib+库名称+.a

windows下:lib+库名称+.lib,这里必须是window自带的msvc原生工具链,cl.exe编译生成的;

如果使用mingw在windows系统下编译生成的依然是.a结尾的库,这里使用仍然要在相同工具链下引用才可以,否则会出现二进制不兼容。

例如:

bash 复制代码
cmake_minimum_required(VERSION 3.10)

# 项目名称和支持的语言(显式指定C语言)
project(CMTEST LANGUAGES C)

# 设置C标准版本(根据需要调整,例如C99、C11)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) # 指定库文件输出目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LST)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

add_library(calc STATIC  ${SRC_LST})  

生成静态库:

2、动态库

动态库制作命令

bash 复制代码
add_library(库名称 SHARED 源文件1 [源文件2] ...) 

Linux下: lib+库名称+.so

Windows下:lib+库文件+.dll

例如:

bash 复制代码
cmake_minimum_required(VERSION 3.10)

# 项目名称和支持的语言(显式指定C语言)
project(CMTEST LANGUAGES C)

# 设置C标准版本(根据需要调整,例如C99、C11)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) # 指定库文件输出目录
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LST) # 将源文件指定给变量SRC_LST
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

add_library(calc SHARED  ${SRC_LST}) # 制作动态库并指定名称为calc

生成库文件:

3、指定库文件输出路径

LIBRARY_OUTPUT_PATH:该关键字用于指定库文件的输出路径;

三、库文件的使用

库文件使用必须有两个部分,首先是头文件(获取到库文件中所包含的函数声明,以及类对象等信息), 再一个就是库文件。

例如下目录结构:

1、静态库的使用

链接静态库命令如下:

bash 复制代码
link_libraries(<static lib> [<static lib>...])

这里指定链接库,此处参数可做如下的要求:

参数1:指定链接库的名字,(1)、可以是全名例如 libcalc.a ; (2)、也可以是掐头去尾,留下calc

参数2:缺省,可以在一条命令中指定多个静态库

如果该库是自定义的,或者第三方提供的,这里不是系统库,有可能存在找不到的情况;这里有两个解决办法

(1)、使用环境变量,将该库的存放文件夹路径放到环境路径下;

(2)、自己在CMakeLists.txt中添加该库存放路径,最好是相对当前项目的相对路径,这样保证在与他人合作时。路径统一,推荐此种方法;

1.1、指定库文件路径

bash 复制代码
link_directories(<lib path>) # 可以一次性指定多个静态库/动态库的路径

例如下CMakeLists.txt

bash 复制代码
cmake_minimum_required(VERSION 3.10)

# 项目名称和支持的语言(显式指定C语言)
project(CMTEST LANGUAGES C)

# 设置C标准版本(根据需要调整,例如C99、C11)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.c) # 获取当前目录下的所有C源文件
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib) # 设置静态库目录
link_libraries(calc)# 链接静态库

add_executable(testapp ${SRC}) 

编译-》链接 生成可执行文件

静态库编译后会直接编译到可执行文件中,因此在使用可执行文件时,不依赖静态库;

2、动态库的使用

在正常软件开发过程中,需要多人协作,有时候会将各个功能模块分开,这里各自维护各自的动态库,再最终软件运行时,将这些动态库串联起来,这里就要提到动态库的使用。

target_link_libraries: 链接动态库/静态库,用于指定编译一个源文件/库文件/可执行文件时需要链接的动态库

bash 复制代码
target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...
    )

(1)、target: 指定要加载库文件的名字

这里可以是一个源文件/动态库/静态库/可执行文件。

(2)、PRIVATE|PUBLIC|INTERFACE:动态库访问权限,默认PUBLIC

若动态库之间没有相互依赖关联,则默认用PUBLIC

PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用,可以理解为当前目标 "使用" 了该库,并且把该库的接口等资源"暴露" 给了依赖自己的目标(即其他目标依赖当前目标时,相当于间接依赖了这个库) ;

PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库,对于第三方使用当前目标库依赖时,屏蔽了目标库依赖的库;

INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。可以理解为当前目标本身不用这个库,但要求所有依赖自己的目标必须依赖这个库(相当于 "转发" 依赖);

(3)、动态库的链接具有传递性,如果动态库 A 链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中定义的方法。

bash 复制代码
target_link_libraries(A B C)
target_link_libraries(D A)
# 这里D相当于链接了BC

(4)、编译可执行文件链接动态库时,链接要写在可执行文件后面,指定动态库时需要掐头去尾,去除开头的lib跟结尾的.dll/.so

bash 复制代码
cmake_minimum_required(VERSION 3.10)

# 项目名称和支持的语言(显式指定C语言)
project(CMTEST LANGUAGES C)

# 设置C标准版本(根据需要调整,例如C99、C11)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.c) # 获取当前目录下的所有C源文件
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

link_directories(${CMAKE_CURRENT_SOURCE_DIR}/bin) # 设置动态库目录

add_executable(testapp ${SRC}) 

target_link_libraries(testapp PUBLIC calc) # 链接动态库

编译生成的可执行文件,运行时需要将动态库放置到同级路径下,否则会报如下错误:

(5)、动态库跟静态库差异,动态库不会编译到目标文件中,但是静态库会直接编译进去,因此最终生成目标文件,静态库编译方式生成的文件较大,动态库链接方式生成的较小;

四、日志打印

命令:message

bash 复制代码
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)

1、消息等级:

(无) :重要消息

STATUS :非重要消息

WARNING:CMake 警告, 会继续执行

AUTHOR_WARNING:CMake 警告 (dev), 会继续执行

SEND_ERROR:CMake 错误, 继续执行,但是会跳过生成的步骤

FATAL_ERROR:CMake 错误, 终止所有处理过程

bash 复制代码
# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")

五、变量操作

1、字符串的拼接

(1)set命令

bash 复制代码
set(变量名 ${参数1}  ${参数2} ...) # 将后面的参数1/2拼接成一个字符串,赋值给变量

这里如果原来变量有值会被覆盖, 可以是常量字符串,也可以是变量字符串

bash 复制代码
set(temp hello world)
set(temp1 ${temp} gg)
message(${temp})
message(${temp1})

(2)list 命令

bash 复制代码
list(APPEND <list> [<element> ... ])

指定字符串list, 采用追加发方式,将后续成员字符追加到list后面;

list在管理字符串时,采用链表的方式存储,字符串之间用','分隔,但是在message命令打印时,整体是一个字符串

2、字符列表中剔除字符串

使用List 命令

bash 复制代码
list(REMOVE_ITEM <list> <value> [<value> ...])

在字符串list,中删除后续的value值的字符串,可以是一个也可以是多个;

3、List 命令介绍

(1)LENGTH 获取字符串长度

bash 复制代码
list(LENGTH <list> <output variable>) # list 当前操作列表, <output variable>创建新的变量,用于存储长度

(2)GET 列表通过索引获取字符串

bash 复制代码
list(GET <list> <element index> [<element index> ...] <output variable>)

<list>:当前操作列表

<element index>:列表元素的索引, 索引0开始编号,0表示第一元素;负数表示从最后一个元素开始,-1表示最后一个元素,以此类推;索引不管正负不能超过实际长度,否则运行报错;

<output variable>:新创建的变量,存储指定索引元素的返回结果,也是一个列表

(3)JOIN指定连接符将列表中字符串连接起来

bash 复制代码
list (JOIN <list> <glue> <output variable>)

<list>:当前操作列表

<glue>:指定连接符

<output variable>:创建新变量,存储返回的字符串

(4)FIND在列表中查找字符串,并返回索引

bash 复制代码
list(FIND <list> <value> <output variable>)

<value>:列表中搜索的字符串

<output variable>:新创建的变量,若搜索到字符串,则返回该字符串的索引,否则返回-1

(5)INSERT在列表指定位置后插入若干元素

bash 复制代码
list(INSERT <list> <element_index> <element> [<element> ...])

(6)PREPEND在列表最前面插入元素

bash 复制代码
list (PREPEND <list> [<element> ...])

(7)POP_BACK移除列表最后一个元素

bash 复制代码
list (POP_BACK <list> [<out-var>...])

(8)POP_FRONT移除列表第一个元素

bash 复制代码
list (POP_FRONT <list> [<out-var>...])

(9)REMOVE_AT移除列表指定位置元素

bash 复制代码
list (REMOVE_AT <list> <index> [<index> ...])

(10)ROMOVE_DUPLICATES移除重复元素

bash 复制代码
list (REMOVE_DUPLICATES <list>)

(11)REVERSE列表翻转

bash 复制代码
list(REVERSE <list>)

(12)SORT列表排序

bash 复制代码
list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])

COMPARE:指定排序方法。有如下几种值可选:

STRING:按照字母顺序进行排序,为默认的排序方法

FILE_BASENAME:如果是一系列路径名,会使用basename进行排序

NATURAL:使用自然数顺序排序

CASE:指明是否大小写敏感。有如下几种值可选:

SENSITIVE: 按照大小写敏感的方式进行排序,为默认值

INSENSITIVE:按照大小写不敏感方式进行排序

ORDER:指明排序的顺序。有如下几种值可选:

ASCENDING:按照升序排列,为默认值

DESCENDING:按照降序排列

六、自定义宏

我们在代码运行时添加一些宏,从而控制代码内部的执行逻辑,可以灵活控制编译过程中的执行逻辑,是项目管理和功能开关的常用手段。

例如:

bash 复制代码
#include <stdio.h>
#define NUMBER  3

int main()
{
    int a = 10;
#ifdef DEBUG  // 这里对该宏进行判断,如果被定义了,这执行这部分代码,没定义,相当于此处为注释掉的代码
    printf("这里是编译调试...\n");
#endif
    for(int i=0; i<NUMBER; ++i)
    {
        printf("hello, word!!!\n");
    }
    return 0;
}

针对这个宏,可以采用如下两个方式定义:

1、通过gcc, 命令编译指定宏

bash 复制代码
gcc hello.c -DDEBUG -o app

2、通过CMakeLists.txt中指定

add_definitions

bash 复制代码
add_definitions(-D宏名称)

七、总结

至此,关于 CMake 语法提高篇的核心内容已梳理完毕。从库文件的构建到实际应用,再到变量、宏定义等高级特性的灵活运用,我们能清晰感受到 CMake 作为跨平台构建工具的强大与便捷 ------ 它不仅能通过统一的语法解决不同系统下库文件格式差异的问题,还能借助路径管理、条件判断等功能,让复杂项目的构建流程更具逻辑性和可维护性。

相关推荐
Yupureki4 小时前
从零开始的C++学习生活 19:C++复习课(5.4w字全解析)
c语言·数据结构·c++·学习·1024程序员节
默默的流星雨6 小时前
TARJAN相关
c++·算法·深度优先·图论
王RuaRua7 小时前
VScode C/C++环境配置
c语言·c++·vscode
橘子137 小时前
Linux线程同步(四)
linux·c++
想想吴9 小时前
10. 引用计数
c++·引用计数
yolo_guo9 小时前
opencv 学习: 04 通过ROI处理图片局部数据,以添加水印为例
linux·c++·opencv
顺顺 尼9 小时前
模板进阶和array
c++
一匹电信狗10 小时前
【牛客CM11】链表分割
c语言·开发语言·数据结构·c++·算法·leetcode·stl
困鲲鲲10 小时前
ROS2系列 (10) : C++话题通信节点——发布者示例
c++·ros2