CMake使用方法(详解版)下

目录

示例

根目录

calculate目录

test_calc目录

sort目录

test_sort目录

构建项目

流程控制

条件判断

循环

示例

参考


在大型项目中,CMake通过CMakeLists.txt文件来管理项目的结构。这些文件通常分布在项目的不同目录层级中,每个CMakeLists.txt文件负责该目录下源代码的编译、链接等构建任务。

  • 顶层CMakeLists.txt:通常包含对整个项目的全局设置,如指定CMake的最小版本、设置项目名称和版本号、配置全局编译选项等。
  • 模块级CMakeLists.txt:在项目的各个模块或子目录中,这些文件负责具体模块的编译和链接,可以包含模块特有的编译选项、源文件列表、库依赖等。

示例

如图,以下是一个大型项目使用CMake时项目结构示例

  • bin目录用于存放可执行文件
  • build目录用于存放cmake生成的构建的文件
  • include目录存放头文件
  • lib目录存放生成库文件

在这个结构中,源文件目录都包含一个CMakeLists.txt文件,用于管理该目录下的构建任务。顶层CMakeLists.txt文件负责整个项目的全局设置,而模块级和测试级的CMakeLists.txt文件则负责具体模块的编译和测试任务。即需要做的事情如下:

1、通过 test_calc目录中的测试文件进行计算相关的测试
2、通过 test2_sort目录中的测试文件进行排序相关的测试

Linux的目录是树状结构,所以嵌套的 CMake 也是一个树状结构,最顶层的 CMakeLists.txt 是根节点,其次都是子节点。因此,我们需要了解一些关于 CMakeLists.txt 文件变量作用域的一些信息:

    根节点CMakeLists.txt中的变量全局有效
    父节点CMakeLists.txt中的变量可以在子节点中使用
    子节点CMakeLists.txt中的变量只能在当前节点中使用

在CMake中,父子节点之间的关系主要通过目录结构和add_subdirectory()命令来建立。

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

  • source_dir:指定了CMakeLists.txt源文件和代码文件的位置,即子目录的路径。
  • binary_dir(可选):指定了输出文件的路径,一般不需要指定,可以忽略。
  • EXCLUDE_FROM_ALL(可选):表示在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外。
  • 作用:用于在CMake中添加子目录。这个命令告诉CMake在构建过程中,除了当前目录(父目录)外,还需要处理指定的子目录。

示例:如有以下目录结构:

project/  
├── CMakeLists.txt (父目录)  
└── subdirectory/  
    └── CMakeLists.txt (子目录)

在父目录的CMakeLists.txt文件中,通过以下命令包含子目录:

add_subdirectory(subdirectory)

这样,子目录中的CMakeLists.txt文件就能够访问和继承父目录中的设置和变量了。

根目录

根目录中的 CMakeLists.txt文件内容如下:

# 设置CMake的最低版本要求  
cmake_minimum_required(VERSION 3.10)  
# 设置项目名称和版本  
project(CMAKE_TEST)

#生成debug版本, 可以进行gdb调试
set(CMAKE_BUILD_TYPE "Debug")

# 指定C++标准  C++14 
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
#指定输出的路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#在include目录下寻找头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
#设置项目库文件的输出路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
#设置项目库文件搜索路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
#设置静态库的名字
set(CALC_LIB calc)  
set(SORT_LIB sort)  
#设置可执行文件的名字
set(CALC test_calc)
set(SORT test_sort)
#添加子目录
add_subdirectory(calculate)
add_subdirectory(sort)
add_subdirectory(test_calc)
add_subdirectory(test_sort)

在根节点对应的文件中主要做了两件事情:定义全局变量和添加子目录。

  • 定义的全局变量主要是给子节点使用,目的是为了提高子节点中的CMakeLists.txt文件的可读性和可维护性,避免冗余并降低出差的概率。
  •  一共添加了四个子目录,每个子目录中都有一个CMakeLists.txt文件,这样它们的父子关系就被确定下来了。

calculate目录

calculate目录中的 CMakeLists.txt文件内容如下:

set(SRC_LIST add.cpp mult.cpp sub.cpp)
add_library(${CALC_LIB} STATIC ${SRC_LIST})

第一行:定义变量,将SRC_LIST的值设置为包含add.cppmult.cppsub.cpp这三个文件名的列表。

第二行:生成静态库,静态库名字CALC_LIB是在根节点文件中定义的,并且根节点文件中定义生成的静态库会被放在根目录下的lib目录中。

test_calc目录

test_calc目录中的 CMakeLists.txt文件内容如下:

set(SRC_LIST calc.cpp)
add_executable(${CALC} ${SRC_LIST})
target_link_libraries(${CALC} ${CALC_LIB})

第一行:定义SRC_LIST的值为calc.cpp。

第二行:生成可执行程序,CALC变量是在根节点文件中定义的,并且根节点文件中定义生成的可执行程序会被放在根目录下的bin目录中。

第三行:指定可执行程序要链接的库文件,CALC_LIB是在根节点文件中定义的

此处的可执行程序链接的是静态库,最终静态库会被打包到可执行程序中,可执行程序启动之后,静态库也就随之被加载到内存中了。

链接静态库,也可以使用link_libraries()命令

link_libraries(library1 library2 ...)

  • library1library2等是库文件的名字(不包括前缀lib和后缀如.so.dll.a等,除非你的库文件就是这样命名的),或者是CMake能够找到的库目标的名称。
  • 作用:用于向目标(如可执行文件或库)添加链接时所需的库

因为link_libraries()会全局地影响后续所有目标的链接库,这可能导致意外的链接依赖。因此,CMake实践更推荐使用target_link_libraries()来代替link_libraries(),因为它允许你为特定的目标指定链接库,从而避免了全局污染。

sort目录

sort目录中的 CMakeLists.txt文件内容如下:

aux_source_directory(. SRC_LIST)
add_library(${SORT_LIB} SHARED ${SRC_LIST})

第一行:搜索当前目录下的所有源文件,并将其存储在SRC_LIST中。

第二行:生成动态库,动态库名字SORT_LIB是在根节点文件中定义的,并且根节点文件中定义生成的动态库会被放在根目录下的lib目录中。

在生成库文件的时候,这个库可以是静态库也可以是动态库,一般需要根据实际情况来确定。如果生成的库比较大,建议将其制作成动态库。

test_sort目录

test_sort目录中的 CMakeLists.txt文件内容如下:

set(SRC_LIST sort.cpp)
add_executable(${SORT} ${SRC_LIST})
target_link_libraries(${SORT} ${SORT_LIB})

第一行: 定义变量SRC_LIST并赋值为sort.cpp

第二行:生成可执行程序,SORT变量是在根节点文件中定义的,并且根节点文件中定义生成的可执行程序会被放在根目录下的bin目录中。

第三行:指定可执行程序要链接的库文件,SORT_LIB是在根节点文件中定义的

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

构建项目

进入根目录下的build目录中,开始构建项目

 如上图可以得到如下信息:

1、在项目根目录的lib目录中生成了静态库libcalc.a
2、在项目根目录的lib目录中生成了动态库libsort.so
3、在项目根目录的bin目录中生成了可执行程序test_calc
4、在项目根目录的bin目录中生成了可执行程序test_sort

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

流程控制

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

条件判断

CMake中的条件判断主要通过if语句实现,其基本语法如下:

if(<condition>)  
    # 条件为真时执行的命令  
    <commands>  
elseif(<condition>) # 可选块,可以重复  
    # 前面的if或elseif条件不满足时,检查这里的条件  
    <commands>  
else() # 可选块  
    # 所有的if和elseif条件都不满足时执行的命令  
    <commands>  
endif()

在条件判断中,如果有多个条件,那么可以写多个elseif,最后一个条件可以使用else,但是开始和结束是必须要成对出现的,分别为:if和endif其中,<condition>可以是各种表达式,包括基本表达式、逻辑判断、比较、文件操作等。 

  1. 基本表达式
    if(<expression>)
    
    • 常量:如1ONYESTRUEY等表示真(True),0OFFNOFALSENIGNORENOTFOUND、空字符串等表示假(False)。
    • 变量:变量的值如果是非零值或非空字符串,则条件为真;否则为假。
    • 字符串:处理方式与变量类似,但注意字符串的比较有专门的比较操作符。
  2. 逻辑判断
    • NOT:逻辑取反。
      if(NOT <condition>)
    • AND:逻辑与,所有条件都为真时,整个表达式为真。
      if(<cond1> AND <cond2>)
    • OR:逻辑或,至少有一个条件为真时,整个表达式为真。
      if(<cond1> OR <cond2>)
  3. 比较
    • 数值比较:LESSGREATEREQUALLESS_EQUALGREATER_EQUAL
      if(<variable|string> LESS <variable|string>)
    • 字符串比较:STRLESSSTRGREATERSTREQUALSTRLESS_EQUALSTRGREATER_EQUAL
      if(<variable|string> STRLESS <variable|string>)
    • 文件和目录存在性检查:EXISTSIS_DIRECTORYIS_SYMLINKIS_ABSOLUTE等。
      if(EXISTS path-to-file-or-directory)

循环

CMake中的循环主要通过foreachwhile命令实现,语法格式如下:

foreach循环的基本语法如下:

foreach(<loop_var> <items>)  
    # 对items中的每个元素执行以下命令,每次循环将当前元素的值赋给loop_var  
    <commands>  
endforeach()

 <items>可以是一个范围(使用RANGE关键字指定起始值和结束值,可选指定步长),也可以是一个列表或字符串(使用LISTSITEMS关键字)。

while的语法格式如下:

while(<condition>)
    <commands>
endwhile()

 while循环比较简单,只需要指定出循环结束的条件即可

示例

条件判断示例:

set(MY_VAR 42)  
if(MY_VAR)  
    message("MY_VAR is true")  
endif()  
  
set(FILE_PATH "${PROJECT_SOURCE_DIR}/myfile.txt")  
if(EXISTS ${FILE_PATH})  
    message("${FILE_PATH} exists.")  
else()  
    message("${FILE_PATH} does not exist.")  
endif()

循环示例:

# 遍历数字范围  
foreach(item RANGE 1 5)  
    message(STATUS "Current item: ${item}")  
endforeach()  
  
# 遍历列表  
set(my_list "apple" "banana" "cherry")  
foreach(fruit IN LISTS my_list)  
    message(STATUS "Fruit: ${fruit}")  
endforeach()
# 创建一个列表 NAME
set(NAME luffy sanji zoro nami robin)
# 得到列表长度
list(LENGTH NAME LEN)
# 循环
while(${LEN} GREATER  0)
    message(STATUS "names = ${NAME}")
    # 弹出列表头部元素
    list(POP_FRONT NAME)
    # 更新列表长度
    list(LENGTH NAME LEN)
endwhile()

输出结果为:

参考

CMake 保姆级教程(下)--- 爱编程的大丙 b站

  • 25
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值