首发于工程部署

以VS2019-OpenCV编译为例谈谈C/C++编译那些事

7 人赞同了该文章

前置知识

C/C++ 与 python 的区别

python 有自带的包管理器(pip/conda),我们在需要一个库(框架)时可以使用 pip 工具在其包管理器的服务器上进行下载与安装,下载后的包由 python 指定了固定位置,再使用例如 CPython 解释器解释 python 文件时,解释器会自动找对应的位置,依据包进行导入,使用十分方便
C++ 并没有像 python 那样被广泛使用的包管理器(不过已经有相应的轮子,如 vcpkg),我们在装 C++ 库时,需要下载其源码,对其源码编译后在自己的项目内再设置好编译结果文件的路径进行包导入,使用起来较为不方便

C/C++ 编译与链接

  • 编译
    我们在写好项目源码后,需要对项目中所有源文件(如 C++ 项目源码的 cpp 文件)进行编译转化为机器指令、二进制字节码后才能运行使用,这一过程需要编译器的参与,如 windows 系统下 cpp 文件经过编译器编译后产生 obj 文件,这就是编译的中间文件
  • 链接
    对编译生成的 obj 文件进行信息的补充,利用链接器形成最终的可执行文件,可以直接在对应操作系统直接使用并执行,这就是链接过程
  • 注意要点
    • 编译和链接为了简化通常统称为编译,编译器也同时完成了编译和链接两项操作,形成最终的可执行文件
    • 编辑器、编译器、IDE 的区别
      • 编辑器:如 Notepad++、VSCode 等,只提供文本编译功能,具体代码编译还需要自行设置编译器等
      • 编译器:将源文件翻译成可执行文件的应用
      • IDE(如 windows 系统下的 visual studio):集成了编辑器与编译器的功能,可以编写源文件后运行得到可执行文件,展示最终结果



主流操作系统下可执行文件的种类

  • Windows 系统
    • exe 文件:可以直接运行使用
    • dll 文件:动态链接库
    • lib 文件:静态链接库


  • Linux 系统
    • elf/coff 文件:可以直接运行使用
    • so 文件:动态链接库
    • a 文件:静态链接库


  • tips
    • 动态链接库与静态链接库的区别(以 Windows 系统为例):动态链接库在真正的 exe 运行时才会去找对应的 dll 文件,因此只有在运行时才会需要使用这些动态 dll 库,而静态链接库 lib 文件在链接时使用,链接器将库编译后的代码直接插入到 exe 程序当中
    • 动态编译与静态编译的区别:动态编译框架源码时会同时生成 dll 与 lib 文件,其中代码部分编译为dll,lib中只有链接时需要使用到的必要的接口信息 而静态编译后只有 lib 文件,动态编译相当于把静态编译生成的大 lib 文件“拆分成两块”变为 dll 与只含接口信息的 lib 文件


主流操作系统本身的 C/C++ 编译器

  • Linux 系统:gcc 编译器,最初专门为 Linux 系统开发,在 Linux 系统下编译源码
  • Windows 系统的编译器:MSBuild 编译器 (俗称VS编译器) (也就是集成在微软 Visual Studio 这个 IDE 中的编译器),专门为 Windows 系统开发,我们如果在 Windows 系统编译项目源码,尽量选择原生的 VS 编译器(原因在下面一节,因为如果利用这个下面一节说的跨平台的”桥梁“,”桥梁“可能在转换时有一些 Windows API 与 Linux API 并不能很好地兼容),因此原生编译器更好

C/C++ 程序跨系统、跨架构的三种模式

由于多数 C++ 程序需要涉及操作系统底层 API 的调用,因此多数应用程序均需要为不同系统与架构设置。如我们在下载程序时通常会有 Windows 下载与 Linux 系统下载多种安装(乃至于每种的 CPU 架构的安装包都各不相同),这些 C++ 应用程序在编译源码过程时均需要设置不同的环境,比较麻烦。设想一下,如果有一个“桥梁”,可以帮助我们方便地把面向某个系统的源码移植到另一个,或者直接设计一些跨平台的API以供开发者使用,岂不是可以简化开发流程,快速实现跨平台、跨架构?下面我将介绍这三种模式
  • 主流的”桥梁“工具链:如 Msys、Cygwin 或 MinGW 等
    因为 Windows 系统统治市场,使用人数基数大,因此较多团队尝试将Linux下的工具链移植到 Windows,并形成了相应的工具包。例如我们可以在 Windows 系统下下载 MinGW,就可以利用 gcc、clang等工具在 Windows 系统下编译源码(尽管他们是 Linux 系统自带的编译器),他们就是由工具包维护团队积极维护的”桥梁“工具链
  • 大型 C++ 库框架:Qt 跨平台、跨架构的实现
    Qt 针对每一种操作系统平台与架构都设计了一套接口,用兼容层设计屏蔽了底层差异。我们在写 Qt C++ 程序时只需要调用其接口而无需在意平台,Qt 编译时会自行判断所使用的平台与架构进行对应模式的编译。Qt 由于面向对象设计较为复杂,类库设计覆盖面广,因此较为庞大而显得比较”重“,不如桥梁编译器灵巧,但代码编写起来十分方便
  • 虚拟机模式
    如 VMWare,HyperV,WSL等,我们可以在 Windows 系统内运行 Linux 系统,在 Linux 系统内使用 Linux 程序

cmake 与 cmakelist

大型库中通常开发者都会为项目写一个 cmakelist.txt,指示编译器在编译时的行为、文件顺序与关联信息等
  • cmake 语言:一门用于编写编译描述文件的语言,依据其语法写 cmakelist
  • cmakelist:可以理解为用 cmake 语言写好的,供解析使用的项目文件(编译描述文件)
  • cmake 软件:解析 cmakelist 的软件,将 cmakelist 翻译为各自平台的项目文件,并调用各自平台原生编译器进行编译


编译 C/C++ 框架模板(以 OpenCV-VS2019 为例)


重要的事情写在前面:先找找有没有别人编译好的对应自己所需要的操作系统与编译器的文件,GitHub 或者谷歌搜索"库名-prebuild",找一找有没有对应平台对应编译器的文件,如果有先下载保存,尝试直接在项目内引用编译好的文件即可,十分方便。

eg. 我已经将 OpenCV4.5.4-VS2019-Windows 编译好的文件放在 GitHub,大家可以自行下载使用。

如果没有在互联网上找到可以使用的预编译库,再遵守下面的步骤自行编译。

  • 下载官方对应版本的源代码
    可以在 github 对应 release 中 asset 处下载自己所需版本的 source code (如 OpenCV4.5.4)
  • 在项目主目录(本例也就是 opencv-4.5.4 目录下)创建一个文件夹名为 build 用来存放最终编译后的可执行文件与编译中间文件
  • 打开图形化的 cmake 界面,source 部分选择源码所在目录,如 opencv 源码都在 opencv-4.5.4 目录下,选择该目录即可,build 部分目录选择刚才创建的 opencv-4.5.4 目录下的 build 目录用来存放编译后的文件。随后点击 configuration 按钮选择你要是用的编译器,由于我要在 Windows Visual Studio 中使用,版本为 VS2019,故我选择了 VS2019,随后点击 Finish 等待。
  • 编译结束后中央区域出现下图,在这里可以勾选你所需要的模块,删除不需要的模块(对于 OpenCV 这个库来说,这里最好勾选 BUILD_opencv_world 选项,原因后面说)
组件选项卡


  • 勾选结束后点击 Generate 完成等待完成编译
  • 编译结束后,关闭 cmake,发现 opencv-4.5.4 目录下的 build 目录下出现了大量编译中间文件与结果文件,在 Windows 系统下以一个 VS 项目的格式呈现,找到 OpenCV.sln 使用 Visual Studio 打开这个项目
VS 编译 OpenCV


在红框标记的数字 1 处分别先选择 Debug 与 Release 模式,点击右侧数字 2 处 CMakeTargets 目录下的 ALL_BUILD 与 INSTALL 目录,右键选择生成。注意数字 1 处 debug 与 release 两种模式,数字 2 处两个目录均需要生成,相当于顺序生成四次。最终完成了 cmake 编译后的 VS 编译,获得了最终的可执行文件,编译结束。


寻找编译后的可执行文件并在 VS 项目内进行配置

我们需要的三类重要的文件,也就是生成结果

由于cmake生成目录较为杂乱,这三类文件需要自己翻找目录寻找;如果已经找到了别人编译好的,直接进入下一步在 VS 中配置即可

  • include 目录下的所有头文件
    这个目录下指示着该库头文件,我们在自己的源代码 #include 时需要让编译器知道这些头文件的位置。该目录一般是源码主目录下的 include 目录,如对于 OpenCV 为 opencv4.5.4 目录下的 include 文件,但 OpenCV 此处用的并不是这个,而是 opencv-4.5.4\build\install\include 目录才为对应的 include 目录,将该目录拷贝出来
  • 所有静态链接库 lib 文件
    对于 opencv 来说,该目录为 opencv-4.5.4\build\lib,我们自己的项目需要用 debug 编译即拷贝 debug目录的文件,需要用 release 则使用 release 目录下的所有 lib 文件。选择完成后将这些 lib 文件放在一个文件夹里。这些 lib 文件分别对应着 OpenCV 的某一个库的动态链接,所以我们自己的项目用到哪些 OpenCV 库那么我们就在自己的项目中引用该库对应的 lib 即可。如果不能确定就把所有的 lib 文件均拷贝出来在项目中一一设置,这时我们刚才勾选的 BUILD_opencv_world 就起作用了,这时会有一个 opencv_world454.lib作为集成所有其它 lib 文件的”总库“,我们可以只在自己的项目中设置这个 lib 文件,否则还需要一个一个添加,比较麻烦
  • 所有动态链接库的 dll 文件
    由于 cmake 默认动态编译,因此除了 lib 文件外还生成了 dll 文件,我们还需要根据找到这些动态链接库加入我们自己的项目便于项目引用。一般这些 dll 文件和 lib 文件的前面的文件名一样,我们找到 lib 文件了可以利用文件搜索改后缀名找这些 dll 文件的位置。OpenCV 的动态链接库在 opencv-4.5.4\build\install\x64\vc16\bin 中,文件名有 d 的是 debug 模式,没有 d 的是 release 模式,这些 dll 与那些 lib 恰好对应


在我们自己要用该库(如 OpenCV)的 Visual Studio 项目中设置这些文件

  • 例如我们创建了一个叫 test 的 C++ 控制台项目,在 test.cpp 输入以下 OpenCV 测试代码
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{
 Mat src = imread("C:\\Users\\ABC\\Desktop\\test\\test.jpg");
 imshow("input", src);
 waitKey();
 destroyAllWindows();
}
  • 自己项目中 OpenCV 库的配置
项目目录安排参考示意图


  • 首先配置头文件,注意此处有上面的代码#include <opencv2/opencv.hpp>,因此我们需要首先在 test 工程文件下设置刚才已经编译好的代码 include 库,比如我在 test.sln 同级目录下创建一个 include 目录,在 include 目录下在设置一个 opencv 目录,随后把第一步准备好的 include 文件放在这里,其次配置静态与动态链接库,把刚才准备的文件按照这个格式放好
  • 这里放文件要注意,如果我们的项目用 release 运行,那么就需要放编译好的 release 版本的动静态链接库,随后在 VS 配置时也要用 release 环境进行配置
  • 打开自己的 VS项目,如果选择 release 模式运行,首先点击项目-项目属性,如图
项目属性选项卡位置


  • 随后在 VC++ 目录下设置包含目录为 include 目录,这个目录下放着 OpenCV 的编译好的 include 目录,也就是我们之前创建好的目录(对应目录图的蓝色框那个目录,其它库同理在这里添加)
设置 include 文件的目录为包含目录,指示项目要在该目录下寻找包含的头文件


  • 随后在链接器-输入中配置附加依赖项为静态链接库,把已经编译好的所有要用到的 lib 文件放在此处,这时 opencv_world 的好处就显示出来了,如果有这个文件此处只需要设置一次,否则需要把所有的 lib 文件一个一个添加(对应目录图中的黄色文件)
在附加依赖项处编辑添加编译好的静态依赖库


  • 最后把编译的动态库 dll 放在项目 x64 下,运行此处的 exe 时会动态的在这里需要的 OpenCV 动态库
  • 设置好检测路径是否正确,观察到包含目录刚好与#include <opencv2/opencv.hpp>连接起来
  • 点击生成,成功生成无报错;点击本地 windows 调试器动态运行代码,图片显示
测试代码通过,图片正常显示


结语

我已经预编译好的 OpenCV4.5.4-vs2019 库 GitHub 链接

感谢列表:yqs112358

发布于 2022-07-28 11:45

内容所属专栏

「真诚赞赏,手留余香」
还没有人赞赏,快来当第一个赞赏的人吧!
理性发言,友善互动

2 条评论
默认
最新
知乎用户gFWvS8

请问,预编译好了之后,如果在项目中用到opencv的话,还需要在VS中配置相关文件,有没有方法直接把dll,lib,头文件丢到项目文件夹下,就能直接用呢?

2022-08-29 · 上海
一苇以航
你好,回答如下
1.对于动态库dll文件,可以把需要用到的放到与运行主程序exe同级目录下,运行exe的时候会优先在同级目录下找。如果报缺少dll错误,就利用文件搜索去编译好的库找到这个dll拷过来,不要相信网络上互相抄袭的博客说dll放在system32目录下(同级目录找不到才会去找环境变量,不要动系统变量)
2.对于lib文件,除了博客的方法以外,可以右键项目那里,添加-现有项,然后直接去找那些lib选中也可以导入进来,和博客里输入完整路径是一样的(可能会方便一点)
3.对于头文件,必须自己在项目下配置包含目录,然后保证这个目录和#include能拼成一个完整路径的文件就行了
你说的问题没有方便的解决方案的,每个用到opencv项目必须自己在VS里面按照博客那样手动配置依赖的,实际一般就是缺啥补啥[爱]
2022-08-29 · 江苏