目录
3.1 Cmake中的PUBLIC、PRIVATE、INTERFACE
一个c++工程可能会涉及到很多的基础库,但是c++不像python一样可以直接import,因此引入了Cmake,将多个库链接起来。
参考:CMake系列讲解 - 总目录(由浅入深,实例讲解)_cmake 项目目录-CSDN博客
1.c++的编译过程
使用g++
等编译工具,从源码生成最终的可执行文件一般有这几步:预处理(Preprocess)、编译(Compile)、汇编(assemble)、链接(link)
预处理: 处理一些#号定义的命令或语句(如#define、#include、#ifdef等),生成.i文件
编译:进行词法分析、语法分析和语义分析等,生成.s的汇编文件
汇编:将对应的汇编指令翻译成机器指令,生成二进制.o目标文件
链接:调用链接器对程序需要调用的库进行链接。链接分为两种
静态链接
在链接期,将静态链接库中的内容直接装填到可执行程序中。
在程序执行时,这些代码都会被装入该进程的虚拟地址空间中。
动态链接
在链接期,只在可执行程序中记录与动态链接库中共享对象的映射信息。
在程序执行时,动态链接库的全部内容被映射到该进程的虚拟地址空间。其本质就是将链接的过程推迟到运行时处理
输入g++ --help
可以看到对应命令:
-E Preprocess only; do not compile, assemble or link.
-S Compile only; do not assemble or link.
-c Compile and assemble, but do not link.
-o <file> Place the output into <file>.
例1: 对于简单的文件,没有引入其他库的,如下程序main.cpp,编译过程如下所示
#include <iostream>
int main() {
std::cout << "Hello World!" << std::endl;
return 0;
}
第一步:预处理 :C++中预处理指令以 #
开头。在预处理阶段,会对#define
进行宏展开,处理#if,#else
等条件编译指令,递归处理#include
。这一步需要我们添加所有头文件的引用路径。
# 将xx.cpp源文件预处理成xx.i文件(文本文件),其中main.cpp , main.i为文件路径
g++ -E main.cpp -o main.i
第二步:编译:检查代码的规范性和语法错误等,检查完毕后把代码翻译成汇编语言文件。
# 将xx.i文件编译为xx.s的汇编文件(文本文件)
g++ -S main.i -o main.s
第三步:汇编:基于汇编语言文件生成二进制格式的目标文件。
# 将xx.s文件汇编成xx.o的二进制目标文件
g++ -c main.s -o main.o
第四步:链接:将目标代码与所依赖的库文件进行关联或者组装,合成一个可执行文件
# 将xx.o二进制文件进行链接,最终生成可执行程序
g++ main.o -o main
最后将生成的可执行文件路径直接输入终端便可以执行
2.Cmake的安装
在ubuntun系统中
方法一:
sudo apt install cmake -y
方法二:指定版本的安装
# 以v3.25.1版本为例
git clone -b v3.25.1 https://github.com/Kitware/CMake.git
cd CMake
# 你使用`--prefix`来指定安装路径,或者去掉`--prefix`,安装在默认路径。
./bootstrap --prefix=<安装路径> && make && sudo make install
# 验证Cmake版本
cmake --version
3.Cmake常见语句
1.cmake_minimum_required() cmake的最低要求版本
2.project() 指定camke工程的名字,此外他还可以指定版本号
3.add_library(name STATIC/SHARED path) 命令用来使用指定的源文件向工程中添加一个目标库。name为库名,STATIC/SHARED为静态库或动态库,path为路径,可加引号,也可以不加。
Linux系统下,静态库的库文件全名为 lib库名称.a ,动态库的库文件全名为 lib库名称.so
4.add_executable(name path) 使用指定的源文件创建出一个可执行文件,相当于相当于g++ path -o name;。 name为可执行文件的文件名,path为文件路径,可加引号,也可以不加
5.target_include_directories(tatget_name PUBLIC "c/path") 为指定的目标(target_name)添加头文件的包含目录("c/path")。指定编译目标时需要搜索头文件的路径,以确保编译器可以找到所需的头文件.
6.include_directories("c/path") 用于指定包含头文件目录,但一般用target_include_directories而不是include_directories
7.target_link_directories(target PUBLIC "../account_dir/build")
在使用target_link_libraries()链接库与目标时,库和目标可能不在同一文件夹下,使用此命令将库文件目录添加进来
8.target_link_libraries(tatget PUBLIC item) 该命令用于用于将目标与所需的库进行链接。它用于指定一个目标(例如可执行文件或库)需要依赖的其他库,以便在构建过程中正确地链接这些库。target:要链接库的目标,必须是由add_executable()或add_library()等命令创建的
注意:编写CMakeLists.txt文件时,要先写add_executable(),即生成可执行文件,因为只有可执行文件生成了,后面才可以给可执行文件链接库,链接指定目录,即target_link_directories(),target_link_libraries()等指令
可以看下文使用案例中看具体用法
3.1 Cmake中的PUBLIC、PRIVATE、INTERFACE
CMake中经常使用target_...()
类似的命令,一般这样的命令支持通过PUBLIC
、PRIVATE
、INTERFACE
关键字来控制传播。
以
target_link_libraries(A
PUBLIC/PRIVATE/INTERFACEB)
为例,从理解的角度来看
PRIVATE
:依赖项B
仅链接到目标A
,如果有C
链接了A
,C
不会链接B
INTERFACE
:依赖项B
并不链接到目标A
,如果有C
链接了A
,C
会链接B
PUBLIC
:依赖项B
链接到目标A
,如果有C
链接了A
,C
也会链接B
示例:
# 创建库
add_library(C c.cpp)
add_library(D d.cpp)
add_library(B b.cpp)
# C是B的PUBLIC依赖项
target_link_libraries(B PUBLIC C)
# D是B的PRIVATE依赖项
target_link_libraries(B PRIVATE D)
# 添加可执行文件
add_executable(A a.cpp)
# 将B链接到A
target_link_libraries(A B)
因为C是B的PUBLIC依赖项,所以C会传播到A
因为D是B的PRIVATE依赖性,所以D不会传播到A
3.2 set(), unset(), message()
1.set(变量名 "值") 设置变量
2.unset(变量名) 删除变量,变量删除后,其值为空
3.message(<mode> "<message>") 输出<message>,相当于print,输出内容也可以不加引号 ,
<mode>
可选,用于指定消息的类型,常用的类型包括:
STATUS
:用于显示普通消息,通常用于输出状态信息或变量的值。WARNING
:用于显示警告消息。AUTHOR_WARNING
:用于显示作者级别的警告消息。SEND_ERROR
:用于显示错误消息,并中断 CMake 过程。FATAL_ERROR
:用于显示严重错误消息,并中断 CMake 过程。DEPRECATION
:用于显示关于已弃用特性的消息。
示例
message("hello")
message("hel" lo) #输出hello,会做拼接
set(VAR1 "变量1")
message("VAR1=" ${VAR1}) #VAR1=变量1(外部访问)
message("VAR1=${VAR1}") #VAR1=变量1(内部拼接)
message("\${VAR1}=${VAR1}") #${VAR1}=变量1(使用\转义)
unset(VAR1) #删除变量
message("\${VAR1}=${VAR1}") #${VAR1}= (删除变量后,输出为空)
message(STATUS "CMAKE_INSTALL_PREFIX默认值: ${CMAKE_INSTALL_PREFIX}")
3.3 set的另一种用法:
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
此时set类似于python中的argparse,设置变量的值,
<variable>:要设置的缓存变量的名称。
<value>:要为缓存变量设置的值(可以有多个)。
CACHE:指定该变量是一个缓存变量。
<type>:变量的类型,可以是以下之一:
BOOL:布尔类型,即开/关(ON/OFF)值。cmake-gui 提供一个复选框。
FILEPATH:指向磁盘上文件的路径。cmake-gui 提供一个文件对话框。
PATH:指向磁盘上目录的路径。cmake-gui 提供一个文件对话框。
STRING:字符串类型,即文本行。cmake-gui 提供一个文本字段或下拉选择(如果设置了 STRINGS 缓存变量属性)。
INTERNAL:内部类型,即文本行。cmake-gui 不显示内部变量。内部变量可用于持久保存跨
运行的变量。使用此类型会隐含使用 FORCE。
<docstring> :对于变量的描述
FORCE:可选项,用于强制覆盖现有的缓存变量。缓存默认是不覆盖的,如果在调用之前缓存
项不存在,或者给出了FORCE选项,那么缓存项将被设置为给定的值。
用户通过命令行选项"-D变量名=<value>"来创建缓存变量,如下:
cmake -S . -B build -DVAR1=hello
3.4Cmake的一些常见内置变量
message("${PROJECT_NAME}") #项目名称
message("${CMAKE_SOURCE_DIR}") #源码目录
message("${CMAKE_BINARY_DIR}") #编译目录
message("${CMAKE_CURRENT_LIST_FILE}") #当前CMakeLists.txt文件路径
可以通过set设计以上的值
set(BUILD_SHARED_LIBS ON) #设置是否构建动态库,默认为off,即构建静态库,设为on后,构建动态库
#生成库
因为通过set设置了构建动态库,所以生成的为动态库
add_library(${PROJECT_NAME} Account.cpp Account.h)
3.5生成器表达式
1.条件表达式:$<condition:true_string>,当condition为真时,返回true_string,否则返回空字符串
注意:生成表达式被展开是在生成构建系统的时候,因此不能通过解析配置“CMakeLists.txt”阶段的“message”命令打印。
2.变量查询
$<TARGET_EXISTS: target> 判断目标是否存在
$<CONFIG:Debug> 判断当前构建类型是否为Debug
3.目标查询
$<TARGET_FILE:target> 获取编译目标的文件路径
$<TARGET_FILE_NAME:target> 获取编译目标的文件名
3.6函数和宏
宏内部的变量可以在宏外访问
函数内部的变量不可以在函数外访问,因为函数为独立作用域
# 定义一个宏,宏名为my_macro,没有参数
macro(my_macro)
message("宏内部的信息")
set(macro_var "宏内部变量test")
endmacro(my_macro)
message(${macro_var}) #可以在宏外面访问宏的内部变量
#有参数的宏
macro(second_macro arg1 arg2)
message("第一个参数:${arg1}, 第二个参数:${arg2}")
endfunction(second_macro)
#调用宏
second_macro("hello" "world")
# 定义一个函数,函数名为second_func,无参数
function(my_func)
message("函数内部的信息")
set(func_var "内部变量test")
endfunction(my_func)
massage(func_var) #访问不了函数内部的变量,因为函数为一个独立作用域
#有参数的函数
function(second_func arg1 arg2)
message("第一个参数:${arg1}, 第二个参数:${arg2}")
endfunction(second_func)
#调用函数
second_func("hello" "world")
4.Cmake使用案例
例2:简单例子,文件中不含有其他库,如例1中的main.cpp,用Cmake来执行,由于main.cpp中没涉及到其他库,因此不用静态链接或动态链接
第一步:创建CMakeLists.txt文件,一般与main.cpp在同一文件夹,文件内容如下
cmake_minimum_required(VERSION 3.10) #Cmake 最低版本要求号
project(
first_camke #项目名称
VERSION 1.0.0 #此项目的版本号
DESCRIPTION "项目描述" #项目描述
LANGUAGES CXX #项目所使用的语言,注意languages是复数加s,CXX代表C++
)
#添加一个可执行程序(生成可执行程序),main2是可执行程序名称,main2.cpp是源文件,相当于g++ main2.cpp -o main2;
add_executable(main2 main2.cpp)
第二步:执行 cmake -S . -B build 指令
cmake -S <source-dir> -B <build-dir>
是 CMake 命令的一种用法,用于指定 CMake 构建系统的源码目录和构建目录。-S <source-dir>:指定 CMakeLists.txt 所在的源码目录。<source-dir> 是包含 CMakeLists.txt 文件的目录路径。CMake 将在该目录中查找和读取 CMakeLists.txt 文件,并基于其中的指令来配置项目的构建过程。
-B <build-dir>:指定生成的构建文件(Makefile、Visual Studio 项目文件等)存放的目录。<build-dir> 是用于生成和存储构建文件的目录路径。在这个目录中执行生成系统的命令(如 make、cmake --build)会根据 CMakeLists.txt 中的指令生成项目的可执行文件或库文件。
运行
cmake -S <source-dir> -B <build-dir>
时,CMake 将会在<source-dir>
中寻找 CMakeLists.txt 文件并解析其中的内容,然后在<build-dir>
中生成相应的构建文件,以便进行项目的构建和编译。
第三步:执行cmake --build build指令
cmake -B <build-dir>
在<build-dir>文件夹中生成构建文件
这个命令的执行结果通常是将项目源代码编译成可执行文件或库文件,具体取决于 CMakeLists.txt 文件中的配置和指令。
第四步:找到上一步<build-dir>文件夹中的生成的可执行文件,在终端输入地址运行
例3:复杂案例,main.cpp中包含Account类的头文件Account.h与源文件Account.cpp
main.cpp,Account.h,Account.cpp三个文件的结构与内容如下
├── account_dir
│ ├── Account.cpp
│ └── Account.h
└── main
└── main.cpp
main.app的内容如下
#include "Account.h"
#include <iostream>
using namespace std;
//argc 代表argument cout, 参数数量
//argc 代表argument vector, 参数列表
int main()
{
Account account1;
cout << "This is main 函数" <<endl;
return 0;
}
Account.h的内容如下
#ifndef Account_H
#define Account_H
class Account
{
private:
/* data */
public:
Account(/* args */);
~Account();
};
#endif //Account_H
Account.cpp的内容如下
#include "Account.h"
#include <iostream>
Account::Account(){
std::cout << "构造函数Account::Account()" << std::endl;
}
Account::~Account(){
std::cout << "析构函数Account::~Account()" << std::endl;
}
Account.h文件中的#ifndef Account_H解释:
#ifndef Account_H:这是一个预处理指令,用于检查名为 Account_H 的宏是否已经被定义。如果 Account_H 没有被定义过(即未定义),则会执行 #ifndef 后面的代码块。
#define Account_H:如果 Account_H 没有被定义过(即上面的条件成立),则会定义 Account_H 这个宏,防止再次包含同一个头文件时,其内容被重复定义。
这两个指令通常与 #endif 配合使用,用于创建头文件的保护包装,代码格式如下:
#ifndef Account_H
#define Account_H// 这里放置头文件的内容
#endif // Account_H
在上面的例子中,当第一次包含 Account.h 头文件时,Account_H 宏未定义,因此 #ifndef Account_H 条件为真,然后 #define Account_H 定义了 Account_H 宏。接下来的代码会被包含在 #ifndef 和 #endif 之间。
当再次包含 Account.h 头文件时,由于 Account_H 宏已经被定义过,因此 #ifndef Account_H 条件为假,预处理器会跳过 #ifndef 和 #endif 之间的代码,防止头文件内容被重复定义。
这种预处理指令的使用可以有效地避免头文件被多次包含,从而防止因重复包含导致的编译错误或定义冲突。
方法1:
第一步:终端cd到account_dir文件夹中,在account_dir文件夹中创建CMakeLists.txt文件,内容如下,此处可使用静态库或动态库:
cmake_minimum_required(VERSION 3.10)
#项目信息
project(Account)
#生成链接库
add_library(Account SHARED Account.cpp)
#三个参数分别为:库名称,库类型,源文件路径
# STATIC表示静态库, Linux系统下静态库的库文件全名为 lib库名称.a 此例中为libAccount.a
# SHARED表示动态库, Linux系统下动态库的库文件全名为 lib库名称.so 此例中为libAccount.so
第二步:在account_dir文件夹中,依次执行cmake -S . -B build和cmake --build build指令,在build文件夹下生成静态库文件
libAccount.a或动态库文件
libAccount.so
第三步:终端cd到main文件夹中,在main文件夹中创建CMakeLists.txt文件,内容如下
cmake_minimum_required(VERSION 3.10)
#项目信息
project(main)
#添加可执行文件
add_executable(main main.cpp)
注意:生成可执行文件在前,只有生成可执行文件,后面才可以给可执行文件链接库文件等。
#为目标添加头文件目录,注:PUBLIC为属性方式
target_include_directories(main PUBLIC "../account_dir")
#但是此项目main.cpp与库文件libAccount.b属于不同的文件夹,因此在链接之前,需要将库文件所在的目录添加进来
target_link_directories(main PUBLIC "../account_dir/build")
#为目标添加库,将库文件libAccount.a链接,第一个参数为此项目名称,必须是由add_executable()或add_library()等命令创建的,
#第二个参数为库名称,注意不是libAccount.b,而是上一个CMakeLists.txt文件中的库名称Account
target_link_libraries(main Account)
第四步:在main文件夹中,依次执行cmake -S . -B build和cmake --build build指令,在build文件夹下生成
可执行文件,然后在终端输入可执行文件路径即可。
方法2:
第一步:account_dir与main文件夹的同级目录下新建CMakeLists.txt文件,CMakeLists.txt文件内容如下
cmake_minimum_required(VERSION 3.10)
project(main)
#添加动态库
add_library(Account SHARED "account_dir/Account.cpp" "account_dir/Account.h")
#添加可执行文件
add_executable(main "main/main.cpp")
#添加头文件目录
target_include_directories(main PUBLIC "account_dir")
#链接库
target_link_libraries(main Account)
第二步:依次执行cmake -S . -B build和cmake --build build指令,
在build文件夹下生成可执行文件main,在终端输入其地址即可运行。
5.安装(install)
安装就是将库文件,可执行文件生成在指定的目录。因为有时项目需要将库文件,可执行文件单独出来。
例4:
头文件源文件的路径如下图所示
├── CMakeLists.txt
├── include
│ ├── dlib.h
│ └── slib.h
├── main.cpp
└── src
├── dlib.cpp
└── slib.cpp
各个文件的内容如下:
dlib.h头文件内容
void dlib_test();
slib.h头文件内容
void slib_test();
dlib.cpp头文件内容
#include "dlib.h"
#include <iostream>
void dlib_test(){
std::cout << "动态库dlib_test函数被调用" << std::endl;
}
slib.cpp头文件内容
#include "slib.h"
#include <iostream>
void slib_test(){
std::cout << "静态库slib_test函数被调用" << std::endl;
}
CMakeLists.txt内容如下
cmake_minimum_required(VERSION 3.10)
project(install_demo)
#添加公共头文件目录
include_directories(include)
#添加静态库
add_library(slib STATIC src/slib.cpp include/slib.h)
#添加动态库
add_library(dlib SHARED src/dlib.cpp include/dlib.h)
#使用install将库文件,可执行文件安装到其他目录,执行可执行文件时,可能会找不到库文件,添加下面两行代码
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib) #第二个参数路径为库文件的安装路径
#添加可执行文件
add_executable(${PROJECT_NAME} "main.cpp") #${PROJECT_NAME}值为install_demo
#链接库
target_link_libraries(${PROJECT_NAME} slib dlib)
#安装(install) ,安装可以理解为将库文件,可执行文件生成在指定的目录,而不是你的项目中
install(
TARGETS ${PROJECT_NAME} slib dlib #静态库,动态库,可执行文件的名字都为target
RUNTIME DESTINATION bin #RUNTIME 代表可执行文件,DESTINATION代表目的地,即要安装的位置, 后面跟自定义的路径,如bin
LIBRARY DESTINATION lib #LIBRARY为静态库
ARCHIVE DESTINATION lib #ARCHIVE为动态库
)
#后三行代表将相应的文件安装到指定的目录
#注意:bin,lib文件夹有前缀:CMAKE_INSTALL_PREFIX,默认值为/usr/local,因此bin,lib的完整路径为/usr/local/bin,/usr/local/lib
message(STATUS "CMAKE_INSTALL_PREFIX默认值: ${CMAKE_INSTALL_PREFIX}")
#运行方式,终端依次输入:
# cmake -S . -B build -DCMAKE_INSTALL_PREFIX=installed
# cmake --build build
# cmake --install build
#安装头文件,因为有时也需要将项目的头文件安装到指定目录。
#方法1:使用install将文件安装到指定目录
# install(DIRECTORY include/ DESTINATION head_file) #其中include/为要安装的头文件目录,head_file为安装位置,其也有前缀CMAKE_INSTALL_PREFIX.
#方法2:设置公共头文件:PUBLIC_HEADER,并将install改为如下
# set_target_properties(slib PROPERTIES PUBLIC_HEADER include/slib.h)
# set_target_properties(dlib PROPERTIES PUBLIC_HEADER include/dlib.h)
# install(
# TARGETS ${PROJECT_NAME} slib dlib
# RUNTIME DESTINATION bin
# LIBRARY DESTINATION lib
# ARCHIVE DESTINATION lib
# PUBLIC_HEADER DESTINATION include #公共头文件
# )
6.寻找依赖库(find_package)
寻找依赖库指的是c++项目中添加别的库,类似于python中的import,寻找依赖库包括添加已有的库,如open-cv,也包括添加我们自己构建的静态库或动态库,如libAccount.a 或libAccount.so
6.1添加自己构建的静态库或动态库
方法1:编写一个 Find库名称.cmake 文件,适用于导入非cmake安装的项目
方法2:使用install安装,生成 库名称Config.cmake文件,适用于导入自己开发的cmake项目
例5:(使用的是方法1)
├── custom_mod #文件夹
│ ├── CMakeLists.txt
│ ├── include
│ │ └── dlib.h
│ ├── installed
│ │ ├── include
│ │ │ └── dlib.h
│ │ └── lib
│ │ └── libdlib.so
│ └── src
│ └── dlib.cpp
└── find_demo #文件夹
├── cmake
│ └── Finddlib.cmake
├── CMakeLists.txt
└── main.cpp
如上结构图所示,我们在custom_mod文件夹(dlib.h,dlib.cpp内容同例4)中构建了一个静态库libdlib.so,我们想要在find_demo中的main.cpp文件中添加libdlib.so库。
第1步:构建CMakeLists.txt与Finddlib.cmake文件,这两个文件是交互的,即文件中的变量通用,他们的内容如下:
CMakeLists.txt内容
cmake_minimum_required(VERSION 3.10)
project(main)
#设置CMAKE_MODULE_PATH,以便find_package查找,cmake文件夹下需要有Finddlib.cmake文件
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
message("cmake_module_path: ${CMAKE_MODULE_PATH}")
#设置一个缓存变量,用于在命令行设置dlib的安装路径,给Finddlib.cmake使用
set(DLIB_INSTALL_PATH "./" CACHE PATH "dlib的安装路径")
message(STATUS "dlib的安装路径为: ${DLIB_INSTALL_PATH}")
find_package(dlib REQUIRED)
if(dlib_FOUND)
message("dlib found")
message("dlib include dir: ${dlib_INCLUDE_DIR}")
message("dlib lib: ${dlib_LIBRARY}")
message("dlib version: ${dlib_VERSION}")
message("dlib author: ${dlib_AUTHOR}")
message("dlib lib dir: ${dlib_LIBRARY_DIR}")
else()
message("dlib not found")
endif()
#设置RPATH,否则install后,运行时找不到动态库
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ${dlib_LIBRARY_DIR}) #第二个参数路径为库文件的安装路径
# set(CMAKE_INSTALL_RPATH /home/rui/VScode/custom_mod/installed/lib) #第二个参数路径为库文件的安装路径
message(STATUS "dlib的库文件路径为: ${dlib_LIBRARY_DIR}")
#添加可执行文件
add_executable(main main.cpp)
#添加头文件
target_include_directories(main PUBLIC ${dlib_INCLUDE_DIR})
#链接动态库
target_link_libraries(main ${dlib_LIBRARY})
Finddlib.cmake文件内容
#寻找dlib.h
find_path(dlib_INCLUDE_DIR dlib.h PATHS ${DLIB_INSTALL_PATH}/include) #最后一项为头文件dlib.h目录
#寻找libdlib.so
# find_library(dlib_LIBRARY dlib PATHS ${DLIB_INSTALL_PATH}/lib) #最后一项为库文件libdlib.so目录
find_library(dlib_LIBRARY dlib PATHS /home/rui/VScode/custom_mod/installed/lib)
#如果dlib_INCLUDE_DIR 和 dlib_LIBRARY都找到了,那么设置dlib_FOUND为TRUE
if(dlib_INCLUDE_DIR AND dlib_LIBRARY)
set(dlib_FOUND TRUE)
set(dlib_VERSION 1.0.0) #dlib版本号
set(dlib_AUTHOR "Lee") #dlib的作者,这两项可有可无
get_filename_component(dlib_LIBRARY_DIR ${dlib_LIBRARY} DIRECTORY) #将${dlib_LIBRARY}的地址赋值给dlib_LIBRARY_DIR,便于CMakeLists,txt文件使用
endif()
第2步:依次在终端输入下列指令,在build文件夹中生成可执行文件main,在终端输入地址即可运行。
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=main_file_installed
-DDLIB_INSTALL_PATH=/home/rui/VScode/custom_mod/installed
cmake --build build
cmake --install build
注意:好像没用到CMAKE_INSTALL_PREFIX变量,变量DLIB_INSTALL_PATH的值为要添加的libdlib.so库文件的所在目录
6.2添加已有的库,如open-cv
安装opencv:sudo apt install libopencv-dev
通过 find_package(OpenCV REQUIRED) 直接寻找就能找到。
#CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.10)
project(demo_opencv)
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
message("OpenCV have found")
message("libraries:${OpenCV_LIBS}")
message("include_path:${OpenCV_INCLUDE_DIRS}")
else()
message("could not find OpenCV")
endif()
例6:main.cpp文件中引入opencv库
新建CMakeLists.txt文件如下:
cmake_minimum_required(VERSION 3.10)
project(demo_opencv)
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
message("OpenCV have found")
message("libraries:${OpenCV_LIBS}")
message("include_path:${OpenCV_INCLUDE_DIRS}")
else()
message("could not find OpenCV")
endif()
add_executable(demo_opencv main.cpp)
target_include_directories(demo_opencv PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(demo_opencv ${OpenCV_LIBS})
main.cpp文件内容如下
#include <iostream>
#include <opencv2/opencv.hpp>
int main(int argc, char **argv)
{
if (argc != 2)
{
std::cout << "请输入图片路径" << std::endl;
return -1;
}
else
{
std::cout << "图片路径为:" << argv[1] << std::endl;
cv::Mat image = cv::imread(argv[1]);
cv::Mat image_resized;
cv::resize(image, image_resized, cv::Size(400, 400));
cv::cvtColor(image_resized, image_resized, cv::COLOR_BGR2GRAY);
cv::imwrite("resized.png", image_resized);
std::cout << "图片已处理完毕,并保存为resized.png" << std::endl;
}
return 0;
}
//执行
// cmake -S . -B build
// cmake --build build
// cmake --install build CMakeLists.txt文件没有涉及到安装,所以不用这条指令
// build1/demo_opencv /home/rui/VScode/vgg.jpg
终端依次输入
cmake -S . -B build
cmake --build build
cmake --install build CMakeLists.txt文件没有涉及到安装,所以不用这条指令
build1/demo_opencv /home/rui/VScode/vgg.jpg第一个为可执行文件路径,第二个为main函数参数
7.Cmake的快捷使用
有时我们在终端中一个个去输cmake -S . -B build,cmake --build build等指令特别麻烦,我们可以使用快捷的方法
第1步:写好c++文件以及CMakeLists.txt文件后,点击左上角 “终端” —>“运行生成任务”,选择编译器:/usr/bin/g++后面的小齿轮,生成tasks.json文件,文件的内容如下:
#tasks.json文件内容
{
"version": "2.0.0",
"tasks": [
// 1.cmake配置
{
"type": "cppbuild",
"label": "CMake配置",
"command": "cmake", // cmake命令
"args": [ //参数
"-S.", // 源码目录
"-Bbuild", // 编译目录
"-DCMAKE_BUILD_TYPE=Debug" // 编译类型, 注意"-DCMAKE_BUILD_TYPE=Debug"要设置为Debug模式
],
"options": {
"cwd": "${workspaceFolder}" // 工作目录
},
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
},
},
// 2.cmake构建
{
"type": "cppbuild",
"label": "CMake构建",
"command": "cmake", // cmake命令
"args": [
"--build", // 构建
"build", // 构建目录
],
"options": {
"cwd": "${workspaceFolder}" // 工作目录
},
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
},
"dependsOn": [
"CMake配置" // 依赖CMake配置,先执行CMake配置
]
},
// 3.删除build目录
{
"type": "shell",
"label": "删除build目录",
"command": "rm -rf build",
"options": {
"cwd": "${workspaceFolder}" // 工作目录
},
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
},
},
// 4.运行可执行文件
{
"type": "shell",
"label": "运行可执行文件",
"command": "./build/cmake_debug",
"options": {
"cwd": "${workspaceFolder}" // 工作目录
},
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
},
"dependsOn":["CMake构建"]
},
]
}
第2步:点击左上角 “终端” —>“运行生成任务”,便可出现我们文件tasks.json文件中的定义的四条指令(label),即1.CMake配置,2.CMake构建,3.删除build目录,4.运行可执行文件。
CMake配置等价于cmake -S . -B build指令
CMake构建等价于cmake --build build指令
删除build目录 则会删除build目录
运行可执行文件等价于我们将可执行文件路径输入终端运行可执行文件
我们依次点击CMake配置,CMake构建,运行可执行文件指令即可执行,或者直接点击运行可执行文件指令执行,因为可执行文件指令依赖于CMake构建,而CMake构建依赖于CMake配置(tasks.json文件中的dependsOn)
8.Cmake的调试
在7的基础上,我们设置Cmake的4条快捷指令,1.CMake配置,2.CMake构建,3.删除build目录,4.运行可执行文件,接下来,我们创建launch.json调试文件,如下图所示,选择第二行“lldb”
launch.json调试文件的内容如下:
"program":生成的可执行文件的路径
"preLaunchTask": 链接到7中的"CMake构建",即生成可执行文件
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request":"launch",
"name": "C++ CMake Debug",
"program": "${workspaceRoot}/build/cmake_debug", //可执行文件的路径
"args": [],
"cwd":"${workspaceFolder}",
"preLaunchTask": "CMake构建" //链接到CMake构建指令
}
]
}
然后选择C++ CMake Debug调试。