cmake


CMake学习笔记,原文链接如下(原文更详细):
点此处直达原文

1、CMake的使用

  • CMake支持大写、小写、混合大小写的命令。
1.1 CMake中的注释
  • CMake 使用’ # '进行行注释.
# 这是一个CMake注释
  • CMake 使用’ #[[ ]] '进行块注释。
#[[
块注释内容
]]

1.2 CMake中的命令

  • 1、cmake_minimum_required:指定使用的 cmake 的最低版本
    cmake_minimum_required(VERSION 3.0) # 表示CMake最低使用3.0版本
  • 2、project:定义工程名称
    project(MyProject) # 定义工程名为"MyProject"
  • 3、add_executable:生成可执行程序文件
    add_executable(run 源文件名称) # 生成一个名为"run"的可执行文件,这里可执行文件名和工程名并无关系,且源文件可以是多个如:add_executable(run test.cpp test.h main.cpp)
  • 4、set: 定义变量
set(FILE_LIST test.h test.cpp file.cpp file.h)		# 定义了"FILE_LIST"变量,其值为:"test.h test.cpp file.cpp file.h"多个变量之前使用空格
add_executable(run ${FILE_LIST})		# 使用"FILE_LIST"变量等同于"add_executable(run test.h test.cpp file.cpp file.h)"
  • 5、aux_source_directory:搜索某个路径下的所有源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)		# 搜索src文件夹下的所有源文件,并保存在"SRC_LIST"变量中若:此文件夹下有如下源文件:test.cc test.h ama.cc ama.h,"SRC_LIST"值就是"test.cc test.h ama.cc ama.h"
  • 6、file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型):搜索指定文件
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)		# 搜索目录下所有的.cpp文件并将其存储在'MAIN_SRC'变量中
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)		# 搜索目录下所有的.h文件并将其存储在'MAIN_HEAD'变量中
GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。
  • 7、include_directories:包含头文件
    include_directories(头文件存储路径) # 包含头文件路径后可以找到源文件对应的头文件
  • 8、add_library(库名称 STATIC 源文件) :生成静态库
    add_library(test STATIC test.cc object.cc) # 将会生成库名为"libtest.a"的静态库
  • 9、add_library(库名称 SHARED 源文件) :生成动态库
    add_library(test SHARED test.cc object.cc) # 将会生成库名为"libtest.so"的动态库
  • 10、link_directories(库存放路径):指定库存放路径
    link_directories(static_lib_path) # 将会在"static_lib_path"目录下查找要链接的库
  • 11、link_libraries(静态库名):链接静态库
    link_libraries(test) # 链接"libtest.a",可以链接多个静态库,库名之前使用空格隔开即可
  • 12、target_link_libraries(<target> <PRIVATE|PUBLIC|INTERFACE> <name>... ) # 链接动态库,其中"target":要连接库的项目名称,“PRIVATE|PUBLIC|INTERFACE”:表示动态库的访问权限,“name”:要链接的动态库名
  • 动态库链接访问权限说明:
PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。            
PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库
INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。

target_link_libraries(main public pthread test) # 表示将libpthread.so、libtest.so动态库链接到main,并且动态库的访问权限是PUBLIC

  • 13、message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...):在cmake中打印输出
(无) :重要消息
STATUS :非重要消息
WARNING:CMake 警告, 会继续执行
AUTHOR_WARNING:CMake 警告 (dev), 会继续执行
SEND_ERROR:CMake 错误, 继续执行,但是会跳过生成的步骤
FATAL_ERROR:CMake 错误, 终止所有处理过程
  • 14、add_definitions(-D宏名称):自定义宏
add_definitions(-DMESSAGE)		#	自定义了名为:'MESSAGE'的宏。自定义宏默认是关闭的,使用:" cmake -DMESSAGE=ON .. "则会在编译时打开自定义宏,使用" cmake .. "则不会打开自定义宏
// 自定义宏使用说明:
#include <iostream>

int main()
{
#ifdef MESSAGE
    std::cout<<"hello cpp!"<<std::endl;
#endif
 	std::cout<<"hello world!"<<std::endl;
    return 0;
}
// 在自定义了宏之后如果编译时将其打开怎会输出:
// hello cpp!
// hello world!
// 如果不打开则输出:
// hello world!

1.3 CMake中的宏

  • 1、 CMAKE_CXX_STANDARD:指定使用的C++标准
    set(CMAKE_CXX_STANDARD 11) # 将"CMAKE_CXX_STANDARD"赋值为11,表示使用C++11
  • 2、EXECUTABLE_OUTPUT_PATH:指定可执行程序生成路径
    set(EXECUTABLE_OUTPUT_PATH "./bin") # 表示可执行程序或动态库将存放在当前目录下的bin文件夹中,如果bin文件夹不存在会自动创建
  • 3、CMAKE_CURRENT_SOURCE_DIR: 宏表示当前访问的 CMakeLists.txt 文件所在的路径。
  • 4、PROJECT_SOURCE_DIR:使用cmake时后面的目录,一般是工程根目录
  • 5、LIBRARY_OUTPUT_PATH:指定静态库或动态库的生成路径
    set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) #设置静态库或动态库的生成路径
  • 6、PROJECT_BINARY_DIR: 执行cmake命令的目录
  • 7、CMAKE_CURRENT_BINARY_DIR target 编译目录
  • 8、PROJECT_NAME: 返回通过PROJECT指令定义的项目名称
  • 9、CMAKE_BINARY_DIR: 项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径

1.4 CMake中的嵌套

  • 1、对于大型项目,不会是所有源文件在一个文件夹,所有的头文件在一个文件夹,一般都是根据其功能将其存放在不同的文件夹中,当然每个文件夹里也会嵌套其它的文件夹,那么CMake是如何管理这样的项目呢?一起看看吧。
  • 2、如下目录结构
├── build
├── calc
│   ├── add.cpp
│   ├── CMakeLists.txt
│   ├── div.cpp
│   ├── mult.cpp
│   └── sub.cpp
├── CMakeLists.txt
├── include
│   ├── calc.h
│   └── sort.h
├── sort
│   ├── CMakeLists.txt
│   ├── insert.cpp
│   └── select.cpp
├── test1
│   ├── calc.cpp
│   └── CMakeLists.txt
└── test2
    ├── CMakeLists.txt
    └── sort.cpp
 目录说明:
 include 目录:头文件目录
calc 目录:目录中的四个源文件对应的加、减、乘、除算法,对应的头文件是include中的calc.h
sort 目录 :目录中的两个源文件对应的是插入排序和选择排序算法,对应的头文件是include中的sort.h
test1 目录:测试目录,对加、减、乘、除算法进行测试
test2 目录:测试目录,对排序算法进行测试
  • 3、节点关系:Linux的目录是树状结构,所以嵌套的 CMake 也是一个树状结构,最顶层的 CMakeLists.txt 是根节点,其次都是子节点。因此,我们需要了解一些关于 CMakeLists.txt 文件变量作用域的一些信息:
    (1)、根节点CMakeLists.txt中的变量全局有效
    (2)、父节点CMakeLists.txt中的变量可以在子节点中使用
    (3)、子节点CMakeLists.txt中的变量只能在当前节点中使用
  • 4、接下来我们还需要知道在 CMake 中父子节点之间的关系是如何建立的,这里需要用到一个 CMake 命令:
    add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) :指定子目录
source_dir:指定了CMakeLists.txt源文件和代码文件的位置,其实就是指定子目录
binary_dir:指定了输出文件的路径,一般不需要指定,忽略即可。
EXCLUDE_FROM_ALL:在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外。用户必须显式构建在子路径下的目标。
通过这种方式CMakeLists.txt文件之间的父子关系就被构建出来了。
  • 5、在上面的目录中我们要做如下事情:
通过 test1 目录中的测试文件进行计算器相关的测试
通过 test2 目录中的测试文件进行排序相关的测试
现在相当于是要进行模块化测试,对于calc和sort目录中的源文件来说,可以将它们先编译成库文件(可以是静态库也可以是动态库)然后在提供给测试文件使用即可。库文件的本质其实还是代码,只不过是从文本格式变成了二进制格式。
  • 6、根目录中的 CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(test)
# 定义变量
# 静态库生成的路径
set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 测试程序生成的路径
set(EXEC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# 头文件目录
set(HEAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 静态库的名字
set(CALC_LIB calc)
set(SORT_LIB sort)
# 可执行程序的名字
set(APP_NAME_1 test1)
set(APP_NAME_2 test2)
# 添加子目录
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)
  • 在根节点对应的文件中主要做了两件事情:定义全局变量和添加子目录。定义的全局变量主要是给子节点使用,目的是为了提高子节点中的CMakeLists.txt文件的可读性和可维护性,避免冗余并降低出差的概率。一共添加了四个子目录,每个子目录中都有一个CMakeLists.txt文件,这样它们的父子关系就被确定下来了。
  • 7、calc 目录中的 CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
aux_source_directory(./ SRC)			# 搜索当前目录(calc目录)下的所有源文件
include_directories(${HEAD_PATH})		# 包含头文件路径,HEAD_PATH是在根节点文件中定义的
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})  		# 设置库的生成的路径,LIB_PATH是在根节点文件中定义的
add_library(${CALC_LIB} STATIC ${SRC})  		# 生成静态库,静态库名字CALC_LIB是在根节点文件中定义的
  • 8、sort 目录中的 CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})		# 生成动态库,动态库名字SORT_LIB是在根节点文件中定义的
  • 9、test1 目录中的 CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})		# 指定头文件路径,HEAD_PATH变量是在根节点文件中定义的
link_directories(${LIB_PATH})
link_libraries(${CALC_LIB})			# 指定可执行程序要链接的静态库,CALC_LIB变量是在根节点文件中定义的
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})    # 指定可执行程序生成的路径,EXEC_PATH变量是在根节点文件中定义的
add_executable(${APP_NAME_1} ${SRC})  		# 生成可执行程序,APP_NAME_1变量是在根节点文件中定义的
  • 10、test2 目录中的 CMakeLists.txt文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(SORTTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})			# 包含头文件路径,HEAD_PATH变量是在根节点文件中定义的
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH}) 		# 指定可执行程序生成的路径,EXEC_PATH变量是在根节点文件中定义的
link_directories(${LIB_PATH})			# 指定可执行程序要链接的动态库的路径,LIB_PATH变量是在根节点文件中定义的
add_executable(${APP_NAME_2} ${SRC})		# 生成可执行程序,APP_NAME_2变量是在根节点文件中定义的
target_link_libraries(${APP_NAME_2} ${SORT_LIB})		# 指定可执行程序要链接的动态库的名字
  • 11、构建项目:一切准备就绪之后,开始构建项目,进入到根节点目录的build 目录中,执行cmake 命令,如下:
cmake ..
cmake --build . 		(或:make)
  • 12、可以看到在build目录中生成了一些文件和目录,静态库、动态库、可执行程序会生成在对应目录下,如下所示:
build
├── calc                  # 目录
├── CMakeCache.txt        # 文件
├── CMakeFiles            # 目录
├── cmake_install.cmake   # 文件
├── Makefile              # 文件
├── sort                  # 目录
├── test1                 # 目录
└── test2                 # 目录
  • 13、注意:在项目中,如果将程序中的某个模块制作成了动态库或者静态库并且在CMakeLists.txt 中指定了库的输出目录,而后其它模块又需要加载这个生成的库文件,此时直接使用就可以了,如果没有指定库的输出路径或者需要直接加载外部提供的库文件,此时就需要使用 link_directories 将库文件路径指定出来。

1.5 CMake中的流程控制

  • 在 CMake 的 CMakeLists.txt 中也可以进行流程控制,也就是说可以像写 shell 脚本那样进行条件判断和循环。
    1、条件判断:关于条件判断其语法格式如下:
if(<condition>)
  <commands>
elseif(<condition>) # 可选快, 可以重复
  <commands>
else()              # 可选快
  <commands>
endif()
    • 在进行条件判断的时候,如果有多个条件,那么可以写多个elseif,最后一个条件可以使用else,但是开始和结束是必须要成对出现的,分别为:if和endif。
  • 2、基本表达式
if(<expression>)
如果是基本表达式,expression 有以下三种情况:常量、变量、字符串。
如果是1, ON, YES, TRUE, Y, 非零值,非空字符串时,条件判断返回True
如果是 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND,空字符串时,条件判断返回False
  • 3、逻辑判断
NOT
if(NOT <condition>)
其实这就是一个取反操作,如果条件condition为True将返回False,如果条件condition为False将返回True。

AND
if(<cond1> AND <cond2>)
如果cond1和cond2同时为True,返回True否则返回False。

OR
if(<cond1> OR <cond2>)
如果cond1和cond2两个条件中至少有一个为True,返回True,如果两个条件都为False则返回False。
  • 4、 比较
(1)、基于数值的比较:
if(<variable|string> LESS <variable|string>)
if(<variable|string> GREATER <variable|string>)
if(<variable|string> EQUAL <variable|string>)
if(<variable|string> LESS_EQUAL <variable|string>)
if(<variable|string> GREATER_EQUAL <variable|string>)
LESS:如果左侧数值小于右侧,返回True
GREATER:如果左侧数值大于右侧,返回True
EQUAL:如果左侧数值等于右侧,返回True
LESS_EQUAL:如果左侧数值小于等于右侧,返回True
GREATER_EQUAL:如果左侧数值大于等于右侧,返回True

(2)、基于字符串的比较
if(<variable|string> STRLESS <variable|string>)
if(<variable|string> STRGREATER <variable|string>)
if(<variable|string> STREQUAL <variable|string>)
if(<variable|string> STRLESS_EQUAL <variable|string>)
if(<variable|string> STRGREATER_EQUAL <variable|string>)
STRLESS:如果左侧字符串小于右侧,返回True
STRGREATER:如果左侧字符串大于右侧,返回True
STREQUAL:如果左侧字符串等于右侧,返回True
STRLESS_EQUAL:如果左侧字符串小于等于右侧,返回True
STRGREATER_EQUAL:如果左侧字符串大于等于右侧,返回True
  • 5、文件操作
(1)、判断文件或者目录是否存在:
if(EXISTS path-to-file-or-directory)
如果文件或者目录存在返回True,否则返回False。

(2)、判断是不是目录:
if(IS_DIRECTORY path)
此处目录的 path 必须是绝对路径
如果目录存在返回True,目录不存在返回False。

(3)、判断是不是软连接:
if(IS_SYMLINK file-name)
此处的 file-name 对应的路径必须是绝对路径
如果软链接存在返回True,软链接不存在返回False。
软链接相当于 Windows 里的快捷方式

(4)、判断是不是绝对路径:
if(IS_ABSOLUTE path)
关于绝对路径:
如果是Linux,该路径需要从根目录开始描述
如果是Windows,该路径需要从盘符开始描述
如果是绝对路径返回True,如果不是绝对路径返回False。
  • 6、 循环
    • 在 CMake 中循环有两种方式,分别是:foreach和while。
  • (1)、foreach
使用 foreach 进行循环,语法格式如下:
foreach(<loop_var> <items>)
    <commands>
endforeach()
通过foreach我们就可以对items中的数据进行遍历,然后通过loop_var将遍历到的当前的值取出,在取值的时候有以下几种用法:

(1)、方法1
foreach(<loop_var> RANGE <stop>)
RANGE:关键字,表示要遍历范围
stop:这是一个正整数,表示范围的结束值,在遍历的时候从 0 开始,最大值为 stop。
loop_var:存储每次循环取出的值

举例说明:
cmake_minimum_required(VERSION 3.2)
project(test)
# 循环
foreach(item RANGE 10)
    message(STATUS "当前遍历的值为: ${item}" )
endforeach()

(2)、方法2
foreach(<loop_var> RANGE <start> <stop> [<step>])
这是上面方法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()

(3)、方法3
foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
这是foreach的另一个变体,通过这种方式我们可以对更加复杂的数据进行遍历,前两种方式只适用于对某个正整数范围内的遍历。
IN:关键字,表示在 xxx 里边
LISTS:关键字,对应的是列表list,通过set、list可以获得
ITEMS:关键字,对应的也是列表
loop_var:存储每次循环取出的值

举例说明:
cmake_minimum_required(VERSION 3.2)
project(test)
# 创建 list
set(WORD a b c d)
set(NAME ace sabo luffy)
# 遍历 list
foreach(item IN LISTS WORD NAME)
    message(STATUS "当前遍历的值为: ${item}" )
endforeach()

-(2)、while

    • 除了使用foreach也可以使用 while 进行循环,关于循环结束对应的条件判断的书写格式和if/elseif 是一样的。while的语法格式如下:
while(<condition>)
    <commands>
endwhile()
while循环比较简单,只需要指定出循环结束的条件即可:

举例说明:
cmake_minimum_required(VERSION 3.5)
project(test)
# 创建一个列表 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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值