【CMake快速入门】

cmake是干嘛的

CMake 是一个项目构建工具,并且是跨平台的。关于项目构建我们所熟知的还有Makefile(通过 make 命令进行项目的构建),大多是IDE软件都集成了make,比如:VS 的 nmake、linux 下的 GNU make、Qt 的 qmake等,如果自己动手写 makefile,会发现,makefile 通常依赖于当前的编译平台,而且编写 makefile 的工作量比较大,解决依赖关系时也容易出错。

而 CMake 恰好能解决上述问题, 其允许开发者指定整个工程的编译流程,在根据编译平台,自动生成本地化的Makefile和工程文件,最后用户只需make编译即可,所以可以把CMake看成一款自动生成 Makefile的工具,其编译流程如下图:

在这里插入图片描述

cmake的使用

注释
# 注释行
# [[注释块。
    注释块]]
构建

假设程序的文件夹如下

$ tree
.
├── add.c
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c
cmake_minimum_required(VERSION 3.0)#最低版本要求
project(project_s1) #定义工程名称(并可指定工程的版本、工程描述、web主页地址、支持的语言,以上可忽略)
add_executable(app add.c div.c main.c mult.c sub.c) #定义工程会生成一个可执行程序源文件名可以是一个也可以是多个,如有多个可用空格或;间隔
#add_executable(app add.c;div.c;main.c;mult.c;sub.c)
使用set定义变量
# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST add.c  div.c   main.c  mult.c  sub.c)
# 方式2: 各个源文件之间使用分号 ; 间隔
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app  ${SRC_LIST})#使用${变量名}进行取值
指定c++版本
#增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
#增加-std=c++14
set(CMAKE_CXX_STANDARD 14)
#增加-std=c++17
set(CMAKE_CXX_STANDARD 17)
指定输出路径:

在CMake中指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH,它的值还是通过set命令进行设置:

set(HOME /home/user/project/build) #使用绝对路径
#set(HOME ./build) 使用相对路径 ./对应makefile对应的文件夹
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
搜索文件
方式1:
aux_source_directory(< dir > < variable >)
#dir:要搜索的目录
#variable:将从dir目录下搜索到的源文件列表存储到该变量中

使用:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索 src 目录下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(app  ${SRC_LIST})

方式2:

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
#GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
#GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
# CMAKE_CURRENT_SOURCE_DIR 宏表示当前访问的 CMakeLists.txt 文件所在的路径。
#关于要搜索的文件路径和类型可加双引号,也可不加:
file(GLOB MAIN_HEAD "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
包含头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
静态库或动态库的制作和使用
制作动态库/静态库:
add_library(库名称 STATIC 源文件1 [源文件2] ...) #制作静态库
add_library(库名称 SHARED 源文件1 [源文件2] ...) #制作动态库
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) #设置生成动态库路径
链接到静态库
link_libraries(<static lib> [<static lib>...])
#如果该静态库不是系统提供的(自己制作或者使用第三方提供的静态库)可能出现静态库找不到的情况,此时可以将静态库的路径也指定出来:
link_directories(<lib path>)
  • 参数1:指定出要链接的静态库的名字
    • 可以是全名 libxxx.a
    • 也可以是掐头(lib)去尾(.a)之后的名字 xxx
  • 参数2-N:要链接的其它静态库的名字
链接到动态库(也可以链接静态库)
target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
  • target:指定要加载动态库的文件的名字

    • 该文件可能是一个源文件
    • 该文件可能是一个动态库文件
    • 该文件可能是一个可执行文件
  • PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC

    • 如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可。

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

      • PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。

      • PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库

      • INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。

链接系统动态库
target_link_libraries(app pthread)
  • app: 对应的是最终生成的可执行程序的名字
  • pthread:这是可执行程序要加载的动态库,这个库是系统提供的线程库,全名为libpthread.so,在指定的时候一般会掐头(lib)去尾(.so)。
链接第三方动态库

一般来说非系统的动态库编译器并不知道位置,所以要指定出来

link_directories(${PROJECT_SOURCE_DIR}/lib) #存放第三方库的位置 需要写在add_executable前
add_executable(app ${SRC_LIST}) 
target_link_libraries(app pthread calc) 
两者的区别

link_libraries用在add_executable之前,target_link_libraries用在add_executable之后

字符串操作
打印日志消息
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 错误, 终止所有处理过程
set拼接字符串
set(变量名1 ${变量名1} ${变量名2} ...)#关于上面的命令其实就是将从第二个参数开始往后所有的字符串进行拼接,最后将结果存储到第一个参数中,如果第一个参数中原来有数据会对原数据就行覆盖。
list拼接字符串
list(APPEND <list> [<element> ...])#list命令的功能比set要强大,字符串拼接只是它的其中一个功能,所以需要在它第一个参数的位置指定出我们要做的操作,APPEND表示进行数据追加,后边的参数和set就一样了。
  • 在CMake中,使用set命令可以创建一个list。一个在list内部是一个由分号;分割的一组字符串。例如,set(var a b c d e)命令将会创建一个list:a;b;c;d;e,但是最终打印变量值的时候得到的是abcde。
功能
PROJECT_SOURCE_DIR使用cmake命令后紧跟的目录,一般是工程的根目录
PROJECT_BINARY_DIR执行cmake命令的目录
CMAKE_CURRENT_SOURCE_DIR当前处理的CMakeLists.txt所在的路径
CMAKE_CURRENT_BINARY_DIRtarget 编译目录
EXECUTABLE_OUTPUT_PATH重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH重新定义目标链接库文件的存放位置
PROJECT_NAME返回通过PROJECT指令定义的项目名称
CMAKE_BINARY_DIR项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径
嵌套cmake

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

节点关系

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

  • 根节点CMakeLists.txt中的变量全局有效
  • 父节点CMakeLists.txt中的变量可以在子节点中使用
  • 子节点CMakeLists.txt中的变量只能在当前节点中使用
添加子目录
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • source_dir:指定了CMakeLists.txt源文件和代码文件的位置,其实就是指定子目录
  • binary_dir:指定了输出文件的路径,一般不需要指定,忽略即可。
  • EXCLUDE_FROM_ALL:在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外。用户必须显式构建在子路径下的目标。

通过这种方式CMakeLists.txt文件之间的父子关系就被构建出来了。

  • 46
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CMake是一个跨平台的开源构建工具,它能够自动生成各种不同平台的构建脚本,简化了项目的构建过程。下面是一个300字快速入门CMake的实战教程。 首先,安装CMake并确保它被正确添加到系统的环境变量中。 接下来,在你的项目根目录下创建一个CMakeLists.txt文件,这个文件是CMake的配置文件,用来指导CMake生成项目的构建脚本。在CMakeLists.txt文件中,首先指定项目的名称和最低版本要求,例如: ``` cmake_minimum_required(VERSION 3.10) project(MyProject) ``` 然后,添加你的项目的源文件和头文件,使用add_executable或add_library命令指定它们的路径,例如: ``` add_executable(MyApp main.cpp foo.cpp bar.cpp) ``` 接下来,你可以添加依赖库,使用target_link_libraries命令指定它们的路径,例如: ``` target_link_libraries(MyApp ${CMAKE_DL_LIBS}) ``` 此外,你还可以指定编译选项,例如: ``` set(CMAKE_CXX_FLAGS "-std=c++11 -Wall") ``` 最后,你可以通过命令行执行cmake命令来生成构建脚本,并执行构建过程,例如: ``` mkdir build cd build cmake .. make ``` 在build目录中,你会找到生成的构建脚本及其生成的可执行文件(或库文件)。 通过这个简单的实战教程,你可以快速入门CMake,并且开始使用它来管理你的项目的构建过程。当然,在实际应用中,你还可以进行更多高级配置,例如添加条件编译、安装目标等。希望这个简短的回答能够给你一个基本的CMake入门指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值