Table of Contents
一、基础知识
1.1 编译器
- gcc: gnu的C编译器
- g++:gnu的C++编译器。
对于简单工程,使用编译器直接在terminal中进行编译:
g++ main.cpp -o main //main.cpp为需要编译的源文件, [-o main]为输出文件名。
g++ main.cpp -o main `pkg-config --cflags --libs opencv` //使用pkg-config命令输出opencv的INCLUDE(--cflags)和LIBSPATH(--libs)
对于更复杂的工程,可以直接编写makefile后进行make, 或者使用cmake工具生成makefile,然后再make编译。
1.2 编译四步
- 预处理Pre-processing:把头文件写入cpp,生成.i的文档; (预处理器cpp)
- 编译Compiling:检查语法错误,把代码翻译成汇编语言,生成文档.s; (编译器egcs)
- 汇编Assembling:把编译生成的.s文件转为目标文件,生成.o的文档,即二进制的机器代码;(汇编器as)
- 链接Linkling:将每个cpp文件生成的.o文件以及函数库链接在一起,生成可执行文件 。C++支持分离式编译,对源文件分别编译成目标文件,再链接生成可执行文件。(链接器ld,即linker eDitor)
参考链接:俊华的博客:GCC 编译详解及liuchao1986105的博客:gcc编译选项
1.3 链接
这里着重讲一下链接。
函数库一般静态库(.a文件)和动态库(.so文件):
- 静态库在编译链接时,把库文件的代码全部加入可执行文件中,运行时无需库文件,但生成的文件比较大;
- 动态库在编译链接时不把库文件的代码加入到可执行文件中,而在程序执行时由运行时的链接文件加载库。这样的优点是:a. 不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题; b. 如果静态库更新了,所以使用它的应用程序都需要重新编译、发布给用户;而动态库在程序运行是才被载入,用户只需要更新动态库即可,增量更新。
gcc在编译时默认使用动态库。
链接动态库有四种方法:
a) 动态库添加到/lib或者/usr/lib文件夹中,系统会默认搜索这些路径;
b) 每次运行程序前,临时将动态库所在的路径添加到环境变量LD_LIBRARY_PATH中,例如:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:TensorRT-5.1.5.0/lib
此时,如果打开一个新的终端,则在之前的终端中添加的LD_LIBRARY_PATH无效。
c) 在配置文件中bashrc, /etc/profile或者/etc/ld.so.conf中添加动态链接库路径,例如:
sudo gedit ~/.bashrc
在文件末尾加入
export LD_LIBRARY_PATH=/home/yly/Software/TensorRT-5.1.5.0/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
可以通过如下命令查看LD_LIBRARY_PATH:
echo $LD_LIBRARY_PATH
d) 在Cmake或makefile中添加libspath。
参考链接: 阿进的写字台的博客:运行时动态库
接下来分别介绍cmake和makefile的使用方法。
二、MAKE
对于简单的、文件比较少的工程,直接编写makefile,逻辑清晰可控。
GNU make的官方使用说明:http://www.gnu.org/software/make/manual/make.html
2.1 指定头文件路径
INCLUDE = -I $(OPENCV_ROOT)/include
其中, -I表示将$(OPENCV_ROOT)/include作为第一个寻找头文件的目录,如果找不到,会搜索系统默认路径。
2.2 指定链接库
LIBSPATH= -L/usr/local/lib -lopencv_imgcodecs
其中,-L表示将/usr/local/lib设为第一个寻找库文件的目录;-lopencv_imgcodecs表示在该路径中寻找libopencv_imgcodecs.so动态库文件。
2.3 编译源文件
2.3.1 语法规则:
target … : prerequisites … recipe …
Makefile中清晰地指明了生成的目标文件名(target),目标文件的所有依赖文件(prerequisites),和生成规则(recipe)。
注意:recipe前要为<TAB>。
2.3.2 使用cpp生成 .o 文件,例如:
target.o:source.cpp
g++ $(INCLUDE) -c source.cpp -o target.o
使用g++编译器;
recipe中的$(INCLUDE)指定的是搜索cpp中#include包含的头文件的路径;
-o后指定生成的文件
-c 对指定的cpp文件进行编译和汇编(但不链接)
注意:
prerequistites中可不添加头文件,这样的问题是如果修改了头文件,Makefile不会识别到修改,因此不会重新编译。
如果想在prerequisites中加入头文件,例如:target.o:source.cpp header.h, Makefile并不支持自动在$(INCLUDE)路径下搜索prerequisites添加的头文件,因此,需要在prerequisites中给出header.h的路径