『C/C++养成计划』C++中的静态库和动态库|GCC|Makefile|CMake|编译OpenCV

本文详细介绍了C++中静态库和动态库的概念、生成与使用,包括GCC编译选项如-fPIC和-shared,以及Makefile和CMake的使用。通过一个简单的计算库示例,演示了如何制作静态库libcalc.a和动态库libcalc.so,并解决动态库运行时加载问题。最后提到了Linux环境下使用CMake编译OpenCV库的方法。
摘要由CSDN通过智能技术生成
C++中的静态库和动态库|GCC|Makefile|CMake|编译OpenCV学习!

一. 什么是库

  • 库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤:

在这里插入图片描述

二. 静态库

  • 之所以成为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接
  • 试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:
  • 静态库对函数库的链接是放在编译时期完成的。
  • 程序在运行时与函数库再无瓜葛,移植方便。
  • 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
  • 在 Linux 中静态库由程序 ar(gcc工具集里的) 生成,现在静态库已经不像之前那么普遍了,这主要是由于程序都在使用动态库。关于静态库的命名规则如下:
  • 在 Linux 中静态库以 lib 作为前缀,以.a 作为后缀,中间是库的名字自己指定即可,即: libxxx.a
  • 在 Windows 中静态库一般以 lib 作为前缀,以 lib 作为后缀,中间是库的名字需要自己指定,即: libxxx.lib

1.1. 静态库生成

  • 生成静态库,需要先对源文件进行汇编操作 (使用参数 -c) 得到二进制格式的目标文件 (.o 格式), 然后在通过 ar 工具将目标文件打包就可以得到静态库文件了 (libxxx.a)。
  • 使用 ar 工具创建静态库的时候需要三个参数:

在这里插入图片描述

在这里插入图片描述

  • 生成静态链接库的具体步骤如下:
  • 1. 需要将源文件进行汇编,得到 .o 文件,需要使用参数 -c
# 执行如下操作, 默认生成二进制的 .o 文件
# -c 参数位置没有要求
gcc 源文件(*.c) -c	
  • 2. 将得到的 .o 进行打包,得到静态库
ar rcs 静态库的名字(libxxx.a) 原材料(*.o)
  • 3. 发布静态库
# 发布静态库
	1. 提供头文件 **.h
	2. 提供制作出来的静态库 libxxx.a

1.2. 静态库制作举例

1.2.1 准备测试程序

  • 在某个目录中有如下的源文件,用来实现一个简单的计算器:
# 目录结构 add.c div.c mult.c sub.c -> 算法的源文件, 函数声明在头文件 head.h
# main.c中是对接口的测试程序, 制作库的时候不需要将 main.c 算进去
.
├── add.c
├── div.c
├── include
│   └── head.h
├── main.c
├── mult.c
└── sub.c
  • 加法计算源文件 add.c:
#include <stdio.h>
#include "head.h"

int add(int a, int b)
{
    return a+b;
}
  • 减法计算源文件 sub.c:
#include <stdio.h>
#include "head.h"

int subtract(int a, int b)
{
    return a-b;
}
  • 乘法计算源文件 mult.c:
#include <stdio.h>
#include "head.h"

int multiply(int a, int b)
{
    return a*b;
}
  • 除法计算源文件 div.c:
#include <stdio.h>
#include "head.h"

double divide(int a, int b)
{
    return (double)a/b;
}
  • 头文件 head.h:
  • 在C头文件中写上#ifndef(或者简写为ifdef)的作用是为了防止头文件的重复包含。当一个C源文件(或者另一个头文件)包含了该头文件时,预处理器会先检查头文件中是否已经定义了指定的标识符。如果已经定义了,则表示该头文件已经被包含过了,预处理器会跳过对该头文件的再次包含。
#ifndef _HEAD_H
#define _HEAD_H
// 加法
int add(int a, int b);
// 减法
int subtract(int a, int b);
// 乘法
int multiply(int a, int b);
// 除法
double divide(int a, int b);
#endif
  • 测试文件 main.c
#include <stdio.h>
#include "head.h"

int main()
{
    int a = 20;
    int b = 12;
    printf("a = %d, b = %d\n", a, b);
    printf("a + b = %d\n", add(a, b));
    printf("a - b = %d\n", subtract(a, b));
    printf("a * b = %d\n", multiply(a, b));
    printf("a / b = %f\n", divide(a, b));
    return 0;
}

1.2.2 生成静态库

  • 第一步:将源文件 add.c, div.c, mult.c, sub.c 进行汇编,得到二进制目标文件 add.o, div.o, mult.o, sub.o
# 1. 生成.o
gcc add.c div.c mult.c sub.c -c
sub.c:2:18: fatal error: head.h: No such file or directory
compilation terminated.

# 提示头文件找不到, 添加参数 -I 重新头文件路径即可
gcc add.c div.c mult.c sub.c -c -I ./include/

# 查看目标文件是否已经生成
tree
.
├── add.c
├── add.o            # 目标文件
├── div.c
├── div.o            # 目标文件
├── include
│   └── head.h
├── main.c
├── mult.c
├── mult.o           # 目标文件
├── sub.c
└── sub.o            # 目标文件
  • 第二步:将生成的目标文件通过 ar 工具打包生成静态库
# 2. 将生成的目标文件 .o 打包成静态库
$ ar rcs libcalc.a a.o b.o c.o    # a.o b.o c.o在同一个目录中可以写成 *.o

# 查看目录中的文件
$ tree
.
├── add.c
├── add.o
├── div.c
├── div.o
├── include
│   └── `head.h  ===> 和静态库一并发布
├── `libcalc.a   ===> 生成的静态库
├── main.c
├── mult.c
├── mult.o
├── sub.c
└── sub.o
  • 第三步:将生成的的静态库 libcalc.a 和库对应的头文件 head.h 一并发布给使用者就可以了。
# 3. 发布静态库
	1. head.h    => 函数声明
	2. libcalc.a => 函数定义(二进制格式)

1.3. 静态库的使用

  • 当我们得到了一个可用的静态库之后,需要将其放到一个目录中,然后根据得到的头文件编写测试代码,对静态库中的函数进行调用。
# 1. 首先拿到了发布的静态库
	`head.h` 和 `libcalc.a`
	
# 2. 将静态库, 头文件, 测试程序放到一个目录中准备进行测试
.
├── head.h          # 函数声明
├── libcalc.a       # 函数定义(二进制格式)
└── main.c          # 函数测试
  • 编译测试程序,得到可执行文件。
# 3. 编译测试程序 main.c
gcc main.c -o app
/tmp/ccR7Fk49.o: In function `main':
main.c:(.text+0x38): undefined reference to `add'
main.c:(.text+0x58): undefined reference to `subtract'
main.c:(.text+0x78): undefined reference to `multiply'
main.c:(.text+0x98): undefined reference to `divide'
collect2: error: ld returned 1 exit status

在这里插入图片描述

# 4. 编译的时候指定库信息
	-L: 指定库所在的目录(相对或者绝对路径)
	-l: 指定库的名字, 掐头(lib)去尾(.a) ==> calc
# -L -l, 参数和参数值之间可以有空格, 也可以没有  -L./ -lcalc
gcc main.c -o app -L ./ -l calc

# 查看目录信息, 发现可执行程序已经生成了
tree
.
├── app   		# 生成的可执行程序
├── head.h
├── libcalc.a
└── main.c

三. 动态库(共享库)

  • 动态链接库是程序运行时加载的库,当动态链接库正确部署之后,运行的多个程序可以使用同一个加载到内存中的动态库,因此在 Linux 中 动态链接库也可称之为共享库

3.1. 为什么需要动态库

为什么需要动态库,其实也是静态库的特点导致。

  • ①. 空间浪费是静态库的一个问题。
  • ②. 另一个问题是静态库对程序的更新、部署和发布页会带来麻烦如果静态库liba.a更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)

在这里插入图片描述

  • 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2. 生成动态链接库

生成动态链接库是直接使用 gcc 命令并且需要添加 -fPIC(-fpic)以及-shared 参数。 -fpic是一个选项,用于生成位置独立的代码(Position Independent Code,PIC)

  • -fPIC或-fpic 参数的作用是使得 gcc 生成的代码是与位置无关的,也就是使用相对位置。
  • -shared参数的作用是告诉编译器生成一个动态链接库

在这里插入图片描述

在这里插入图片描述

生成动态链接库的具体步骤如下:

  • ①将源文件进行汇编操作,需要使用参数-c, 还需要添加额外参数-fpic /-fPIC
# 得到若干个 .o文件
gcc 源文件(*.c) -c -fpic
  • ②将得到的 .o 文件打包成动态库,还是使用 gcc, 使用参数-shared指定生成动态库 (位置没有要求)
gcc -shared 与位置无关的目标文件(*.o) -o 动态库(libxxx.so)
  • ③发布动态库和头文件
# 发布
 	1. 提供头文件: xxx.h
 	2. 提供动态库: libxxx.so

3.3. 动态库制作举例

  • 在此还是以上面制作静态库使用的实例代码为例来制作动态库,代码目录如下:
# 举例, 示例目录如下:
# 目录结构 add.c div.c mult.c sub.c -> 算法的源文件, 函数声明在头文件 head.h
# main.c中是对接口的测试程序, 制作库的时候不需要将 main.c 算进去
.
├── add.c
├── div.c
├── include
│   └── head.h
├── main.c
├── mult.c
└── sub.c
  • 第一步:使用 gcc 将源文件进行汇编 (参数-c), 生成与位置无关的目标文件,需要使用参数 -fpic或者-fPIC
# 1. 将.c汇编得到.o, 需要额外的参数 -fpic/-fPIC
$ gcc add.c div.c mult.c sub.c -c -fpic -I ./include/

# 查看目录文件信息, 检查是否生成了目标文件
$ tree
.
├── add.c
├── add.o                # 生成的目标文件
├── div.c
├── div.o                # 生成的目标文件
├── include
│   └── head.h
├── main.c
├── mult.c
├── mult.o               # 生成的目标文件
├── sub.c
└── sub.o                # 生成的目标文件
  • 第二步:使用 gcc 将得到的目标文件打包生成动态库,需要使用参数 -shared
# 2. 将得到 .o 打包成动态库, 使用gcc , 参数 -shared
$ gcc -shared add.o div.o mult.o sub.o -o libcalc.so  

# 检查目录中是否生成了动态库
$ tree
.
├── add.c
├── add.o
├── div.c
├── div.o
├── include
│   └── `head.h   ===> 和动态库一起发布
├── `libcalc.so   ===> 生成的动态库
├── main.c
├── mult.c
├── mult.o
├── sub.c
└── sub.o
  • 其实上面两个步骤可以合并为一个命令:gcc -fPIC -shared -o libcalc.so -c add.c div.c mult.c sub.c -I ./include/
  • 第三步:发布生成的动态库和相关的头文件
# 3. 发布库文件和头文件
	1. head.h
	2. libcalc.so

3.4. 动态库的使用

当我们得到了一个可用的动态库之后,需要将其放到一个目录中,然后根据得到的头文件编写测试代码,对动态库中的函数进行调用。

# 1. 拿到发布的动态库
	`head.h   libcalc.so
# 2. 基于头文件编写测试程序, 测试动态库中提供的接口是否可用
	`main.c`
# 示例目录:
.
├── head.h          ==> 函数声明
├── libcalc.so      ==> 函数定义
└── main.c          ==> 函数测试

编译测试程序

# 3. 编译测试程序
gcc main.c -o app
/tmp/ccwlUpVy.o: In function `main':
main.c:(.text+0x38): undefined reference to `add'
main.c:(.text+0x58): undefined reference to `subtract'
main.c:(.text+0x78): undefined reference to `multiply'
main.c:(.text+0x98): undefined reference to `divide'
collect2: error: ld returned 1 exit status

在这里插入图片描述

添加库信息相关参数,重新编译测试代码:

# 在编译的时候指定动态库相关的信息: 库的路径 -L, 库的名字 -l
gcc main.c -o app -L./ -lcalc

# 查看是否生成了可执行程序
tree
.
├── app 			# 生成的可执行程序
├── head.h
├── libcalc.so
└── main.c

# 执行生成的可执行程序, 错误提示 ==> 可执行程序执行的时候找不到动态库
$ ./app 
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

在这里插入图片描述

3.4. 解决动态库无法加载问题

3.4.1. 库的工作原理

在这里插入图片描述

3.4.2. 动态链接器

在这里插入图片描述

3.4.3. 解决方案

  • 可执行程序生成之后,根据动态链接器的搜索路径,我们可以提供三种解决方案,我们只需要将动态库的路径放到对应的环境变量或者系统配置文件中,同样也可以将动态库拷贝到系统库目录(或者是将动态库的软链接文件放到这些系统库目录中)。
  • 解决方法4: 在编译时指定库的路径:当您在编译app程序时,可以使用-rpath选项来指定运行时库的路径。例如下面的使用动态库的过程:
  • -Wl,-rpath= 是一个链接时的选项,用于指定在运行可执行文件时共享库的搜索路径。这里的 -Wl 告诉编译器将后面的选项传递给链接器。链接器负责将对象文件和库连接成可执行文件。-rpath 是链接器的一个选项,它设置了一个运行时的库搜索路径。这是在可执行文件中嵌入的,当你运行该可执行文件时,它会在这个路径中搜索需要的动态链接库。
  • 使用-rpath的优点是你不必依赖环境变量如 LD_LIBRARY_PATH 来指定库路径,这使得部署更加简单和可靠。但是, 需要注意的是这个路径是在编译时设置的,如果库的位置在部署时发生变化,可能需要重新编译
# 解决方案1
export LD_LIBRARY_PATH=/workspace/cpp_test/include:$LD_LIBRARY_PATH
# 解决方案3:ln -s 是一个软链接的的命令。他的功能是将摸一个文件在另外的位置建立链接。具体命令 ln -s 源文件 目标文件
ln -s /workspace/cpp_test/include/libcalc.so /usr/lib/libcalc.so
# 解决方案4
root@7847eccc8c18:/workspace/cpp_test/include# gcc main.c -o app -L./ -lcalc -Wl,-rpath=./
root@7847eccc8c18:/workspace/cpp_test/include# ./app 
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667

在这里插入图片描述

3.4.4. 验证

在这里插入图片描述

补充:GCC学习

  • GNU 是一个递归缩写,表示 “GNU’s Not Unix!”(GNU 不是 Unix),它是一个广泛的自由软件项目,由 Richard Stallman 在 1983 年发起,目的是创建一个类似 Unix 的完全自由的操作系统。自由软件是指用户有运行,复制,分发,研究,修改和改进软件的自由的软件。
  • GNU 项目包含了大量的软件,包括软件许可证(如 GNU 通用公共许可证,简称 GPL),操作系统工具和组件(如 shell 工具,核心工具等),编程库,编译器(如 GCC),以及其他各种工具和应用程序
  • GCC 是 Linux 下的编译工具集,是 GNU Compiler Collection 的缩写,包含 gcc、g++ 等编译器。这个工具集不仅包含编译器,还包含其他工具集,例如 ar、nm 等。

1.1 gcc 工作流程|常用命令|多文件编译|gcc和g++

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • gcc常用命令
  • -fPIC:表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码, 念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。
  • -shared:指定生成动态链接库。
  • -static:指定生成静态链接库。
  • -L:表示要连接的静态或冬天库所在的目录
  • -l:指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。
  • -ggdb:此选项将尽可能的生成gdb 的可以使用的调试信息。
  • -Wl,options :把参数(options)传递给链接器ld 。如果options 中间有逗号,就将options分成多个选项,然后传递给链接程序。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 多文件编译

在这里插入图片描述
在这里插入图片描述

  • gcc和g++

在这里插入图片描述

补充:Makefile学习

在这里插入图片描述

1.1 规则|工作原理|变量|模式匹配|函数

在这里插入图片描述

在这里插入图片描述

补充:CMake学习(项目构建工具)

1.1 CMake 概述|CMake的使用|预定义宏|嵌套的CMake|流程控制

在这里插入图片描述

1.2 例子:生成可执行文件

.
├── CMakeLists.txt
├── main.cpp
├── head.h
├── add.cpp
├── div.cpp
├── mult.cpp
└── sub.cpp
  • CMakeLists.txt文件内容如下:
# 指定使用的 cmake 的最低版本
cmake_minimum_required(VERSION 3.15)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言)
project(calc)
# 定义变量,使用set, 增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
set(SRC_LIST add.cpp div.cpp main.cpp mult.cpp sub.cpp)
# 指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH
set(MYHOME /workspace/shunwang/inference_c/)
set(EXECUTABLE_OUTPUT_PATH ${MYHOME}/bin)
# 定义工程会生成一个可执行程序
add_executable(app ${SRC_LIST})

  • main.cpp
#include "head.h"
#include <stdio.h>

int main()
{
    int a = 20;
    int b = 12;
    printf("a = %d, b = %d\n", a, b);
    printf("a + b = %d\n", add(a, b));
    printf("a - b = %d\n", subtract(a, b));
    printf("a * b = %d\n", multiply(a, b));
    printf("a / b = %f\n", divide(a, b));
    return 0;
}
  • add.cpp
#include "head.h"
#include <stdio.h>

int add(int a, int b)
{
    return a + b;
}
  • sub.cpp
#include "head.h"
#include <stdio.h>

// 你好
int subtract(int a, int b)
{
    return a - b;
}
  • mult.cpp
#include "head.h"
#include <stdio.h>

int multiply(int a, int b)
{
    return a * b;
}
  • div.cpp
#include "head.h"
#include <stdio.h>

double divide(int a, int b)
{
    return (double)a / b;
}
root@f0f54e2b881e:/workspace/shunwang/inference_c# mkdir build && cd build && cmake .. && make
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /workspace/shunwang/inference_c/build
Scanning dependencies of target app
[ 16%] Building CXX object CMakeFiles/app.dir/add.cpp.o
[ 33%] Building CXX object CMakeFiles/app.dir/div.cpp.o
[ 50%] Building CXX object CMakeFiles/app.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/app.dir/mult.cpp.o
[ 83%] Building CXX object CMakeFiles/app.dir/sub.cpp.o
[100%] Linking CXX executable ../bin/app
[100%] Built target app

root@f0f54e2b881e:/workspace/shunwang/inference_c/bin# ./app 
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667
  • 方式1:在 CMake 中使用aux_source_directory 命令可以查找某个路径下的所有源文件
    在这里插入图片描述
# 指定使用的 cmake 的最低版本
cmake_minimum_required(VERSION 3.15)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言)
project(calc)
# 定义变量,使用set, 增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
# set(SRC_LIST add.cpp div.cpp main.cpp mult.cpp sub.cpp)
aux_source_directory(${PROJECT_SOURCE_DIR} SRC_LIST)
# 指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH
set(MYHOME /workspace/shunwang/inference_c/)
set(EXECUTABLE_OUTPUT_PATH ${MYHOME}/bin)
# 定义工程会生成一个可执行程序
add_executable(app ${SRC_LIST})
  • 方式2:如果一个项目里边的源文件很多,在编写CMakeLists.txt文件的时候不可能将项目目录的各个文件一一罗列出来,这样太麻烦了。所以,在CMake中为我们提供了搜索文件的命令,他就是file(当然,除了搜索以外通过 file 还可以做其他事情)。
    在这里插入图片描述
# 指定使用的 cmake 的最低版本
cmake_minimum_required(VERSION 3.15)
# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言)
project(calc)
# 定义变量,使用set, 增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
# set(SRC_LIST add.cpp div.cpp main.cpp mult.cpp sub.cpp)
# aux_source_directory(${PROJECT_SOURCE_DIR} SRC_LIST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH
set(MYHOME /workspace/shunwang/inference_c)
set(EXECUTABLE_OUTPUT_PATH ${MYHOME}/bin)
# 定义工程会生成一个可执行程序
add_executable(app ${SRC_LIST})

.
├── CMakeLists.txt
├── bin
│   └── app
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── Makefile
│   └── cmake_install.cmake
├── include
│   └── head.h
└── src
    ├── add.cpp
    ├── div.cpp
    ├── main.cpp
    ├── mult.cpp
    └── sub.cpp
# 指定使用的 cmake 的最低版本
cmake_minimum_required(VERSION 3.15)

# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言)
project(calc)

# 定义变量,使用set, 增加-std=c++11
set(CMAKE_CXX_STANDARD 11)

# aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC_LIST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)

# 指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

# 指定头文件所在的路径
include_directories(${PROJECT_SOURCE_DIR}/include)

# 定义工程会生成一个可执行程序
add_executable(app ${SRC_LIST})

1.3 例子:制作静态库/动态库

# 指定使用的 cmake 的最低版本
cmake_minimum_required(VERSION 3.15)

# 定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言)
project(calc)

# 定义变量,使用set, 增加-std=c++11
set(CMAKE_CXX_STANDARD 11)

# aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC_LIST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)

# 指定可执行程序输出的路径,也对应一个宏,叫做EXECUTABLE_OUTPUT_PATH

# set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

# 指定头文件所在的路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 制作动态库或静态库 STATIC静态库、SHARED动态库
add_library(calc SHARED ${SRC_LIST})

# 定义工程会生成一个可执行程序;

# add_executable(app ${SRC_LIST})

├── CMakeLists.txt
├── bin # 可执行文件
│   └── app
├── build # cmake构建的东西
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── Makefile
│   └── cmake_install.cmake
├── include # 头文件
│   └── head.h
├── lib # 库文件
│   ├── libcalc.a
│   └── libcalc.so
├── main.cpp
└── src # 源代码
    ├── add.cpp
    ├── div.cpp
    ├── mult.cpp
    └── sub.cpp

1.4 例子:包含库文件

  • 在编写程序的过程中,可能会用到一些系统提供的动态库或者自己制作出的动态库或者静态库文件,cmake中也为我们提供了相关的加载动态库的命令

补充:Linux cmake 编译OpenCV库

mkdir build && cd build
# -D参数来设置CMake的配置
# -D CMAKE_BUILD_TYPE=Release:设置CMake的构建类型为Release,表示以发布版本的方式构建库。
# -D CMAKE_INSTALL_PREFIX=/workspace/sw/3rdparty:设置安装前缀为 /workspace/sw/3rdparty,表示将库安装在该路径下。
# -D OPENCV_GENERATE_PKGCONFIG=On:打开生成PKGCONFIG文件的功能,该文件用于描述OpenCV库的依赖关系和其他信息。
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/workspace/sw/3rdparty -D OPENCV_GENERATE_PKGCONFIG=ON ..
# j8 表示使用 8 个线程进行编译
make -j8
make install

在这里插入图片描述
在这里插入图片描述

  • 代码目录
.
├── 3rdparty
│   ├── opencv
│   │   ├── bin
│   │   ├── include # 
│   │   ├── lib
│   │   └── share
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.16.3
│   │   ├── cmake.check_cache
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── CMakeOutput.log
│   │   ├── CMakeTmp
│   │   ├── main.dir
│   │   ├── Makefile2
│   │   ├── Makefile.cmake
│   │   ├── progress.marks
│   │   └── TargetDirectories.txt
│   ├── cmake_install.cmake
│   ├── main
│   └── Makefile
├── CMakeLists.txt
├── imgs
│   └── 20230727-093619.jpg
└── main.cpp
  • CMakeLists.txt文件
cmake_minimum_required(VERSION 3.10)
project(cv_demo)

# 设置C++标准
set(CMAKE_CXX_STANDARD 11)

# 设置 OpenCV 路径变量
set(OpenCV_PATH ${PROJECT_SOURCE_DIR}/3rdparty/opencv)

# 查找OpenCV库
find_package(OpenCV PATHS ${OpenCV_PATH} NO_DEFAULT_PATH REQUIRED)

# 添加可执行文件
add_executable(main main.cpp)

# 链接OpenCV库
target_link_libraries(main ${OpenCV_LIBS})
  • main.cpp文件
#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
    // 读取图像
    cv::Mat image = cv::imread("/workspace/sw/inference_c/imgs/20230727-093619.jpg"); // 替换为你实际的图像路径

    if (image.empty()) {
        std::cerr << "Failed to read the image." << std::endl;
        return 1;
    }

    // 显示图像
    // cv::namedWindow("Image", cv::WINDOW_AUTOSIZE);
    // cv::imshow("Image", image);
    // cv::waitKey(0);
    std::cout << image.size << std::endl;

    return 0;
}

参考文献

以上内容主要来自大神“爱编程的大丙”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI大模型前沿研究

感谢您的打赏,我会继续努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值