[CMake] cmake入门

本文详细介绍了CMake的基本概念、优点、使用方法,包括指定工程名、编译文件管理、C++标准设置、库的静态和动态生成、链接以及库之间的链接策略。同时讨论了CMake与Make的区别,并分析了静态库和动态库的优缺点。
摘要由CSDN通过智能技术生成

目录

一. CMake概述

1.1 优点

1.2 使用

1.2.1 gcc静态库

二.CMake 的使用

2.1 注释

2.2 基础语法

 2.2.1 根目录和当前目录

2.2.2 指定工程名

2.2.3 编译文件独立存放

2.24 指定可执行文件程序名

2.3  set

2.3.1 set基础用法

2.3.2 指定使用的 C++ 标准

2.3.4 指定输出路径

2.4 搜索文件

2.4.1包含头文件

 三. 制作库和链接库

3.1 静态和动态库生成

3.1.2 指定库的输出路径

3.2静态库链接

3.2.1 链接静态库

 3.3 动态库链接

3.3.1动态库之间的相互链接

3.3.1 链接系统动态库

 3.3.2 链接第三方动态库

 3.4 优缺点

 3.4.1 静态库

3.4.1 动态库

四 日志


一. CMake概述

CMake 跨平台的开源构建工具。cmake可以根据CmakeLists.txt文件指定生成器而生成相应的构建系统文件,比如Makefile,Ninja,Visual Studio项目文件等。因此开发者只需编写一个 CMakeLists.txt 文件,就可以在不同的操作系统和编译器上构建项目。

简单来说CMake和make的区别就行,Cmake主要用于生成构建系统的文件,而Make则是实际执行构建的过程。并且Cmake也更加灵活,编写起来也更加容易。

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

1.1 优点

  • 跨平台
  • 能够管理大型项目
  • 简化编译构建过程和编译过程
  • 可扩展:可以为 cmake 编写特定功能的模块,扩充 cmake 功能

1.2 使用

1.2.1 gcc静态库

  • 编译源文件生成目标文件.o
gcc -c add.c div.c mult.c sub.c
  • 创建静态库
ar rcs libexample.a add.o div.o mult.o sub.o
  • 使用静态库
gcc main.c -o my_program -L. -lexample

1.2.2 gcc动态库

  • -fPIC 选项生成位置无关的代码,以便创建动态库。
gcc -fPIC -c add.c div.c mult.c sub.c
  • 创建动态库
gcc -shared -o libexample.so add.o div.o mult.o sub.o
  • 使用动态库:在编译的时候指定动态库相关的信息: 库的路径 -L, 库的名字 -l
gcc main.c -o my_program -L. -lexample

二.CMake 的使用

  • CMake 支持大写、小写、混合大小写的命令。如果在编写 CMakeLists.txt 文件时使用的工具有对应的命令提示,那么大小写随缘即可,不要太过在意。

2.1 注释

  • CMake 使用 #[[ ]] 形式进行块注释。
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)

2.2 基础语法

 2.2.1 根目录和当前目录

PROJECT_SOURCE_DIR和CMAKE_CURRENT_SOURCE_DIR:

  • `PROJECT_SOURCE_DIR` 是指整个项目的根目录
  • `CMAKE_CURRENT_SOURCE_DIR` 是指当前正在处理的 CMakeLists.txt 文件所在的目录。它们可以用来指定文件的路径,但使用时需要注意它们的含义和适用范围。

2.2.2 指定工程名

  • PROJECT_NAME:可以获得project()指定的工程名称 
  • project()是用来指定一个新的工程,如果当前CMakeList文件设置了该值,那么PROJECT_SOURCE_DIR就是当前的目录的值,否则就是上一层包含project()目录的值

2.2.3 编译文件独立存放

  • 我们可以在CMakeList.txt目录下创建一个build目录,然后编译时进入build目录,使用cmake .. 将生成的编译文件全部放在build目录下。

2.24 指定可执行文件程序名

# 样式1
add_executable(app add.c div.c main.c mult.c sub.c)
# 样式2
add_executable(app add.c;div.c;main.c;mult.c;sub.c)

2.3  set

2.3.1 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})

2.3.2 指定使用的 C++ 标准

#增加-std=11
set(CMAKE_CXX_STANDARD 11)
#或者还有一种方法 在执行cmake命令的时候使用
cmake xxxx.txt -DCMAKE_CXX_STANDARD=11

2.3.4 指定输出路径

  • 这里的set设置变量时可以使用相对路径
set(mydirectory /home/Linux/project)
set(EXECUTABLE_OUTPUT_PATH ${mydirectory}/bin)

#如果此处指定可执行程序生成路径相对路径 ./xxx/xxx,那么这个路径对应的就是 makefile 文件所在的那个目录而不是CMakeList.txt文件.

2.4 搜索文件

  • aux_source_directory: 查找某个路径下的所有源文件
  • aux_source_directory(< dir > < variable >)
    #dir:要搜索的目录
    #variable:将从 dir 目录下搜索到的源文件列表存储到该变量中
cmake_minimum_required(VERSION 3.0)
PROJECT(TEST)
include_directories(${PROJECT_SOURCE_DIR}/include)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/SRC src_list)
add_executable(${PROJECT_NAME} ${SRC_LIST})
  •   FILE: 和aux_source_directory作用一样。但file的功能更强大
  1. file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
  2. GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
  3. GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。
  4. 搜索当前目录的 src 目录下所有的源文件,并存储到变量中
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
#CMAKE_CURRENT_SOURCE_DIR 宏表示当前访问的 CMakeLists.txt 文件所在的路径。

2.4.1包含头文件

  • include_directories` ([`AFTER|BEFORE`] [`SYSTEM`] dir1 [dir2 ...])**

将指定目录添加到编译器的头文件搜索路径之下,指定的目录被解释成当前源码路径的相对路径。

- 命令解析

  •   默认情况下,`include_directories`命令会将目录添加到列表最后,可以通过命令设置`CMAKE_INCLUDE_DIRECTORIES_BEFORE`变量为`ON`来改变它默认行为,将目录添加到列表前面。也可以在每次调用`include_directories`命令时使用`AFTER`或`BEFORE`选项来指定是添加到列表的前面或者后面。
  • 如果使用`SYSTEM`选项,会把指定目录当成系统的搜索目录。该命令作用范围只在当前的CMakeLists.txt以及子的CMakeList.txt

注意: 如果使用命令 SYSTEM 选项用于将指定目录视为系统搜索目录。那么目录下的头文件将被视为系统头文件,而不会被认为是普通的用户自定义头文件。这可以避免某些警告或冲突,特别是当您的项目与系统提供的头文件具有相同的名称时。

cmake_minimum_required(VERSION 3.18.2)
project(include_directories_test)
 
include_directories(sub) 
include_directories(sub2) #默认将sub2添加到列表最后
include_directories(BEFORE sub3) #可以临时改变行为,添加到列表最前面
 
get_property(dirs DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
message(">>> include_dirs=${dirs}") #打印一下目录情况
 
set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON) #改变默认行为,默认添加到列表前面
 
include_directories(sub4)
include_directories(AFTER sub5) #可以临时改变行为,添加到列表的最后
get_property(dirs DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
message(">>> SET DEFAULT TO BEFORE, include_dirs=${dirs}")

 三. 制作库和链接库

3.1 静态和动态库生成

  • 静态库名字分为三部分:lib+ 库名字 +.a,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。

add_library(库名称 STATIC 源文件1 [源文件2] ...) 

├── build
├── CMakeLists.txt
├── include           # 头文件目录
│   └── head.h
├── main.cpp          # 用于测试的源文件
└── src               # 源文件目录
    ├── add.cpp
    ├── div.cpp
    ├── mult.cpp
    └── sub.cpp

根据上面的目录结构,可以这样编写 CMakeLists.txt 文件:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc STATIC ${SRC_LIST})
# 动态库制作
# add_library(calc SHARED ${SRC_LIST})

3.1.2 指定库的输出路径

  • 动态库方式
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})

由于在 Linux 下生成的静态库默认不具有可执行权限,所以在指定静态库生成的路径的时候就不能使用 EXECUTABLE_OUTPUT_PATH 宏了,而应该使用 LIBRARY_OUTPUT_PATH,这个宏对应静态库文件和动态库文件都适用。

  • 静态库方式:这种方式也适用于动态库
​cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成动态库
#add_library(calc SHARED ${SRC_LIST})
# 生成静态库
add_library(calc STATIC ${SRC_LIST})

​

3.2静态库链接

├── build
├── CMakeLists.txt
├── include
│   └── head.h
├── lib
│   └── libcalc.a     # 制作出的静态库的名字
└── src
    └── main.cpp

3.2.1 链接静态库

link_libraries(<static lib> [<static lib>...])
cmake_minimum_required(VERSION 3.0)
project(CALC)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)

#包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 包含静态库路径
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(calc)
#需要注意的是,link_libraries()函数的参数是针对整个项目的,因此如果在同一个项目中有多个目标,它们都会使用相同的链接库列表。如果要为特定目标指定链接库,可以使用target_link_libraries()函数。
add_executable(app ${SRC_LIST})

 3.3 动态库链接

target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
  • target:指定要加载动态库的文件的名字
  • PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为 PUBLIC

如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可。
PUBLIC:在 public 后面的库会被 Link 到前面的 target 中,并且里面的符号也会被导出,提供给第三方使用。
PRIVATE:在 private 后面的库仅被 link 到前面的 target 中,并且终结掉,第三方不能感知你调了啥库
INTERFACE:在 interface 后面引入的库不会被链接到前面的 target 中,只会导出符号。

3.3.1动态库之间的相互链接

target_link_libraries(A B C)
target_link_libraries(D A)

第一行将target_link_libraries(A B C)库 B 和 C 添加为目标 A 的依赖项。这意味着当构建目标 A 时,它将链接到库 B 和 C。

第二行将target_link_libraries(D A)目标 A 添加为目标 D 的依赖项。这意味着当构建目标 D 时,它将首先构建目标 A(如果尚未构建),然后链接到其输出。

所以,这两行的整体效果是:

目标 A 依赖库 B 和 C。
目标 D 依赖于目标 A,后者又依赖于库 B 和 C。
构建目标 D 时,构建系统将确保以正确的顺序构建和链接所有依赖项。具体来说,它将首先构建目标 A(如果尚未构建),然后链接到库 B 和 C,最后链接到目标 A 的输出。

3.3.1 链接系统动态库

 cmake 中指定要链接的动态库的时候,应该将命令写到生成了可执行文件之后

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序要链接的动态库名字
target_link_libraries(app pthread)
  • app: 对应的是最终生成的可执行程序的名字
  • pthread:这是可执行程序要加载的动态库,这个库是系统提供的线程库,全名为 libpthread.so,在指定的时候一般会掐头(lib)去尾(.so)。

 3.3.2 链接第三方动态库

cmake_minimum_required(VERSION 3.0)
project(CALC)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/SHARE_TEST)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/include)

LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/lib)
add_executable(app ${SRC_LIST})
TARGET_LINK_LIBRARIES(app calc)

 3.4 优缺点

 3.4.1 静态库

优点:

  • 静态库被打包到应用程序中加载速度快
  • 发布程序无需提供静态库,移植方便

缺点:

  • 相同的库文件数据可能在内存中被加载多份,消耗系统资源,浪费内存
  • 库文件更新需要重新编译项目文件,生成新的可执行程序,浪费时间。

3.4.1 动态库

优点:

  • 可实现不同进程间的资源共享
  • 动态库升级简单,只需要替换库文件,无需重新编译应用程序
  • 程序猿可以控制何时加载动态库,不调用库函数动态库不会被加载

缺点:

  • 加载速度比静态库慢,以现在计算机的性能可以忽略
  • 发布程序需要提供依赖的动态库

四 日志

  • 在 CMake 中可以用用户显示一条消息,该命令的名字为 message:

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 警告和错误消息的文本显示使用的是一种简单的标记语言。文本没有缩进,超过长度的行会回卷,段落之间以新行做为分隔符。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值