diff --git a/assert/image-20241109130700149.png b/assert/image-20241109130700149.png new file mode 100644 index 0000000..6444c8d Binary files /dev/null and b/assert/image-20241109130700149.png differ diff --git a/assert/image-20241109131136773.png b/assert/image-20241109131136773.png new file mode 100644 index 0000000..91b3367 Binary files /dev/null and b/assert/image-20241109131136773.png differ diff --git a/cmake.md b/cmake.md new file mode 100644 index 0000000..ef12ad0 --- /dev/null +++ b/cmake.md @@ -0,0 +1,325 @@ +# CMake 使用 + +## 概述 + +cmake的使用围绕着 编译选项 头文件 库文件搜索路径,需要编译哪些源文件,生成动态库,静态库和可执行文件 + +### cmake 命令 + +```shell +# 配置阶段 +-B # 构建生成的文件存放目录 +-S # CMakeLists.txt 文件所在目录 +-G # 所选工具集 可以通过--help 查看支持哪些工具集 +# 如果需要传递给cmake参数,-Dxxxx=xxx +# 编译 +--build # 后面就是构建生成的文件存放目录 +--install # 安装 后面是建生成的文件存放目录 --prefix 安装路径 +``` + +### cmake方法 + +工程相关的方法看下面的例子 + +```cmake +set(key value) #定义变量 也可以拼接变量 +# 使用时通过 ${key} 取值 +set(变量名1 ${变量名1} ${变量名2} ...) + +list(APPEND [ ...]) +# list 也可以移除 +list(REMOVE_ITEM [ ...]) + +include(path/xx.cmake) # 加载指定路径下的.cmake文件,并执行其中的命令 +add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) +# source_dir:指定了CMakeLists.txt源文件和代码文件的位置,其实就是指定子目录 +# binary_dir:指定了输出文件的路径,一般不需要指定,忽略即可。 +# EXCLUDE_FROM_ALL:在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外。用户必须显式构建在子路径下的目标。 +``` + +### cmake中常见的预设变量 + +下面的列表中为大家整理了一些CMake中常用的宏: + +| 宏 | 含义 | +| ------------------------ | ------------------------------------------------------------ | +| CMAKE_CURRENT_SOURCE_DIR | 当前处理的CMakeLists.txt所在的路径 | +| CMAKE_CURRENT_BINARY_DIR | target 编译目录 | +| PROJECT_SOURCE_DIR | 使用cmake命令后紧跟的目录,一般是工程的根目录 | +| EXECUTABLE_OUTPUT_PATH | 重新定义目标二进制可执行文件的存放位置 | +| LIBRARY_OUTPUT_PATH | 重新定义目标链接库文件的存放位置 | +| PROJECT_NAME | 返回通过PROJECT指令定义的项目名称 | +| CMAKE_BINARY_DIR | 项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径 | +| | | +| | | + +```cmake +# 当前处理的 CMakeLists.txt 文件所在的路径 +message("CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}") +# 顶层 CMakeLists.txt 文件所在的路径(源代码根目录) +message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}") +# 顶层构建目录路径 +message("CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") +# 当前处理的 CMakeLists.txt 所在的源代码目录路径 +message("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}") +# 当前处理的 CMakeLists.txt 文件的构建目录路径 +message("CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}") +# 项目名称(由顶层 project() 定义) +message("CMAKE_PROJECT_NAME: ${CMAKE_PROJECT_NAME}") +# CMake 可执行文件路径 +message("CMAKE_COMMAND: ${CMAKE_COMMAND}") +# C 编译器路径 +message("CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}") +# C++ 编译器路径 +message("CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}") +# 安装路径前缀 +message("CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") +CMAKE_CXX_STANDARD # c++ 使用标准 +CMAKE_C_STANDARD # C语言标准 +``` + +### 日志 + +在CMake中可以用用户显示一条消息,该命令的名字为message: + +```cmake +message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...) +``` + ++ (无) :重要消息 + ++ STATUS :非重要消息 + ++ WARNING:CMake 警告, 会继续执行 + ++ AUTHOR_WARNING:CMake 警告 (dev), 会继续执行 + ++ SEND_ERROR:CMake 错误, 继续执行,但是会跳过生成的步骤 + ++ FATAL_ERROR:CMake 错误, 终止所有处理过程 + + CMake的命令行工具会在stdout上显示STATUS消息,在stderr上显示其他所有消息。CMake的GUI会在它的log区域显示所有消息。 + +CMake警告和错误消息的文本显示使用的是一种简单的标记语言。文本没有缩进,超过长度的行会回卷,段落之间以新行做为分隔符。 + +```cmake +# 输出一般日志信息 +message(STATUS "source path: ${PROJECT_SOURCE_DIR}") +# 输出警告信息 +message(WARNING "source path: ${PROJECT_SOURCE_DIR}") +# 输出错误信息 +message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}") +``` + + + +#### 输出一般日志信息 +message(STATUS "source path: ${PROJECT_SOURCE_DIR}") +#### 输出警告信息 +message(WARNING "source path: ${PROJECT_SOURCE_DIR}") +#### 输出错误信息 +message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}") + +### 构建基本工程 + +```shell +# 文件 +│ CMakeLists.txt +│ main.cpp +│ tools.cpp +│ +└─include + tools.h +``` + +生成可执行文件 + +```cmake +cmake_minimum_required(VERSION 3.14)# 指定使用的 cmake 的最低版本 +# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可 +project(tools) +set(CMAKE_CXX_STANDARD 17) +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_LIST_DIR}/bin) # 设置可执行文件的输出路径 + +link_directories("C:/thirdpart/lib") # 搜索库文件目录 每一条命令只能包含一个路径 +# ${CMAKE_CURRENT_LIST_DIR} 变量是最常用的变量之一,表示当前处理的CMakeLists.txt所在的路径,也就是项目的根目录 +include_directories(${CMAKE_CURRENT_LIST_DIR}/include) # 搜索头文件目录 每一条命令只能包含一个路径 + +file(GLOB headers include/*.h*) # 搜索include文件夹下所有h hpp头文件 +file(GLOB src ${CMAKE_CURRENT_LIST_DIR}/*.cpp) # 将当前目录下所有cpp文件后缀的源文件, 赋值给src +# GLOB 参数 搜索给定路径下的所有匹配的文件,并将文件列表存储在变量中,不会递归搜索子目录,参数 GLBO_RECURSE 会递归搜索子目录 +# aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/ src) # 将指定目录下的所有源文件的文件名存入变量名 src 中 与file功能一致 + +add_executable(${PROJECT_NAME} ${src} ${headers}) # 生成可执行文件 ${PROJECT_NAME} 为当前项目的名称 ${src} 为源文件列表 +target_link_libraries(${PROJECT_NAME} PUBLIC datachannel) # 链接库 以公有的方式链接 datachannel 库 + +# add_library(${PROJECT_NAME} SHARED ${src} ${headers}) # 生成动态库 +# add_library(${PROJECT_NAME} STATIC ${src} ${headers}) # 生成静态库 + + +# 构建生成vs工程 +# cd root/ cmake -B build -G "Visual Studio 17 2022" -S . +# cmake --build build --config Debug + + +``` + +生成的vs工程 + +![image-20241109130700149](/assert\image-20241109130700149.png) + +可以看出来,只有将源文件加载到了工程中,看不到头文件 + +需要搜索头文件,并加入生成可执行文件的依赖 + +```cmake +file(GLOB headers include/*.h*) # 搜索include文件夹下所有h hpp头文件 +add_executable(${PROJECT_NAME} ${src} ${headers}) # 生成可执行文件不仅需要源文件还需要头文件 tip:只是针对vs +``` + +再次构建编译后, 可以看到头文件也加载进来了 + +![image-20241109131136773](/assert\image-20241109131136773.png) + +针对构建特定目标,需要加载进来特定头文件等,而非全局 + +```cmake +# target_XXXXX +target_link_directories(目标名字 PUBLIC | PRIVAET | INTERFACE xxxx) # 访问权限,默认为PUBLIC +target_include_directories() +target_link_libraries() + +# 就是跟下面的功能一样,只不过上面是针对特定目标 +link_directories() +include_directories() +link_libraries() + +#eg: +add_library(test1 STATIC test1.cpp) +target_include_directories(test1 PUBLIC include/test1) + +add_library(test2 STATIC test2.cpp) +target_include_directories(test1 PUBLIC include/test2) +# test1 需要用到include/test1中的头文件,而test2依赖他所需要的头文件 +# 当然不管需要不需要 直接全部用include_directories 全部加载进来也是可以的 + +# target_link_libraries 是更推荐的方式,因为它允许更精确的控制和管理链接库的依赖,特别是在大型项目中,它能够避免全局设置可能带来的问题。 +# link_libraries 虽然简单,但在复杂的项目中可能会导致意外的问题,通常适用于简单的项目或临时设置。 +# 建议在 CMake 项目中优先使用 target_link_libraries +``` + ++ PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。 ++ PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库 ++ INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。 + +### 流程控制 + +在 CMake 的 CMakeLists.txt 中也可以进行流程控制,也就是说可以像写 shell 脚本那样进行条件判断和循环。 + +#### 条件判断 + +```cmake +# 关于条件判断其语法格式如下: +if() + +elseif() # 可选快, 可以重复 + +else() # 可选快 + +endif() +# 在进行条件判断的时候,如果有多个条件,那么可以写多个elseif,最后一个条件可以使用else,但是开始和结束是必须要成对出现的,分别为:if和endif。 +``` +基本表达式 + +```cmake +# 基本表达式 +if() # NOT AND OR +#]=] +如果是基本表达式,expression 有以下三种情况:常量、变量、字符串。 +如果是1, ON, YES, TRUE, Y, 非零值,非空字符串时,条件判断返回True +如果是 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND,空字符串时,条件判断返回False +#]=] +# 逻辑判断 +if( ) +# 其实这就是一个取反操作,如果条件condition为True将返回False,如果条件condition为False将返回True。 +如果cond1和cond2两个条件中至少有一个为True,返回True,如果两个条件都为False则返回False。 +``` +比较 + +```cmake +if( LESS ) +if( GREATER ) +if( EQUAL ) +if( LESS_EQUAL ) +if( GREATER_EQUAL ) +# LESS:如果左侧数值小于右侧,返回True +# GREATER:如果左侧数值大于右侧,返回True +# EQUAL:如果左侧数值等于右侧,返回True +# LESS_EQUAL:如果左侧数值小于等于右侧,返回True +# GREATER_EQUAL:如果左侧数值大于等于右侧,返回True + + +# 基于字符串的比较 + +if( STRLESS ) +if( STRGREATER ) +if( STREQUAL ) +if( STRLESS_EQUAL ) +if( STRGREATER_EQUAL ) +# STRLESS:如果左侧字符串小于右侧,返回True +# STRGREATER:如果左侧字符串大于右侧,返回True +# STREQUAL:如果左侧字符串等于右侧,返回True +# STRLESS_EQUAL:如果左侧字符串小于等于右侧,返回True +#TRGREATER_EQUAL:如果左侧字符串大于等于右侧,返回True +``` + +#### 循环 + +在 CMake 中循环有两种方式,分别是:foreach和while。 +使用 foreach 进行循环,语法格式如下: + +```cmake +foreach( ) + +endforeach() +``` + +通过foreach我们就可以对items中的数据进行遍历,然后通过loop_var将遍历到的当前的值取出,在取值的时候有以下几种用法: + +```cmake +# 方法1 +foreach( RANGE ) +# RANGE:关键字,表示要遍历范围 +# stop:这是一个正整数,表示范围的结束值,在遍历的时候从 0 开始,最大值为 stop。 +# loop_var:存储每次循环取出的值 +cmake_minimum_required(VERSION 3.2) +project(test) +# 循环 +foreach(item RANGE 10) + message(STATUS "当前遍历的值为: ${item}" ) +endforeach() + +# 方法二 +foreach( RANGE []) +#[=[ +这是上面方法1的加强版,我们在遍历一个整数区间的时候,除了可以指定起始范围,还可以指定步长。 +RANGE:关键字,表示要遍历范围 +start:这是一个正整数,表示范围的起始值,也就是说最小值为 start +stop:这是一个正整数,表示范围的结束值,也就是说最大值为 stop +step:控制每次遍历的时候以怎样的步长增长,默认为1,可以不设置 +loop_var:存储每次循环取出的值 +#]=] +cmake_minimum_required(VERSION 3.2) +project(test) + +foreach(item RANGE 10 30 2) + message(STATUS "当前遍历的值为: ${item}" ) +endforeach() + +``` + + + +## 工程实践 + +### 导出安装