前言1:makefile文件是什么?
Makefile 可以简单的认为是一个工程文件的编译规则,描述了整个工程的编译和链接等规则。其中包含了那些文件需要编译,那些文件不需要编译,那些文件需要先编译,那些文件需要后编译,那些文件需要重建等等。编译整个工程需要涉及到的,我们在 Makefile 中都可以进行描述。换句话说,有了 Makefile 可以使得我们的项目工程的编译变得自动化,不用每次都手动输入一堆源文件和参数。
前言2:makefile的执行逻辑:
make工作时的执行步骤入下:
1、读入所有的Makefile。
2、读入被include的其它Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。
正题:下面是项目中,遇到的相关的知识点:
一、函数 foreach
foreach用来做循环用的,Makefile中的foreach函数几乎是仿照于Unix标准Shell (/bin/sh)中的for语句,或是C-Shell(/bin/csh)中的foreach语句而构建的。它的语法是:
foreach <var>;,<list>;,<text>;
这个函数的意思是,把参数;中的单词逐一取出放到参数;所指定的变量中,然后再执行< text>;所包含的表达式。每一次 ;会返回一个字符串,循环过程中, ;的所返回的每个字符串会 以空格分隔,最后当整个循环结束时, ;所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。所以, ;最好是一个变量名,;可以是一个表达式,而 ;中一般会使用 ;这个参数来依次枚举;中的单词。举个例子:
#源文件
#source file path
SRC_PATH :=../ ../common/ ../detector/ ../tracker/sort/ ../tracker/featureExtractor/ ../judge ../api/src
#get all source files
SRCS := $(foreach spath, $(SRC_PATH), $(wildcard $(spath)*.c*) )
二、“=”、“:=”、“?=”、和 “+=”的区别
1)“=”
make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。看例子:
x = foo
y = $(x) bar
x = xyz
在上例中,y的值将会是 xyz bar ,而不是 foo bar 。
2)“:=”
“:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。
x := foo
y := $(x) bar
x := xyz
在上例中,y的值将会是 foo bar ,而不是 xyz bar 了。
3) “?=”
“?=”表示如果该变量没有被赋值,则赋予等号后的值。举例:
VIR ?= new_value
如果VIR在之前没有被赋值,那么现在VIR的值就为new_value
VIR := old_value
VIR ?= new_value
这种情况下,VIR的值就是old_value
4) “+=”
“+=”和平时写代码的理解是一样的,表示将等号后面的值添加到前面的变量上
三、条件判断关键字
关键字 | 功能 |
---|---|
ifeq | 判断参数是否不相等,相等为 true,不相等为 false。 |
ifneq | 判断参数是否不相等,不相等为 true,相等为 false。 |
ifdef | 判断是否有值,有值为 true,没有值为 false。 |
ifndef | 判断是否有值,没有值为 true,有值为 false。 |
四、gcc生成.so文件及.so文件的使用
1、生成.so
g++ hello.cpp -shared -fPIC -o libhello.so
g++ -I../include -shared -fPIC -g -std=c++11 func.cpp -o ../bin/libfunc.so -lpthread -L../bin/
其中,
1)必须要有的配置项:
-shared:表示生成动态链接库
-I:头文件路径;
.cpp源文件
-l:依赖库的名字;
-L:依赖库的路径;
2)根据需要选填的:
-o:生成库的路径及名称(不填会自动默认一个)
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的,所以动态载入时是通过代码拷贝的方式来满足不同的调用,而不能达到真正的代码段共享的目的;
-g:保留调试信息,可gdb调试;
-std=c++11:c++11标准来编译;
2、生成可执行文件
g++ -I../include -fPIC -g -std=c++11 main.cpp -o ../bin/demo -lpthread -L../bin/
和生产.so原理一样,只是比生成.so少-shared参数
五、-Wl,-soname 链接选项作用
-Wl 在 GCC/G++ 中是为了将后面的 option 传递给 链接器。
在生成可执行文件的时候,有时候会找不到某非系统路径下的库,这时候在makefile里通过-Wl,–rpath命令,可以让可执行文件找到非系统路径的库,示例:
LDFLAGS += $(LIBS) -Wl,–rpath=…/facedetect/lib/ -Wl,–rpath=…/detector/
六、调用opencv
方法1、用啥链接啥
最简单的方法,就是把:opencv的头文件路径、用到的lib名称、lib对应路径,全部链接到makefile里,例如:
# 对应需要链接的库
LIBS += -lopencv_core -lopencv_imgproc -lopencv_calib3d -lopencv_videostab -lopencv_ml -lopencv_objdetect
LIBS += -lopencv_features2d -lopencv_flann -lopencv_highgui -lopencv_imgproc
LIBS += -lopencv_photo -lopencv_stitching -lopencv_superres -lopencv_video
# 包含对应的文件的路径
LDFLAGS += -L$(SOURCE_ROOT)/lib/opencvce
INCLUDE += -I $(SOURCE_ROOT)/inc/opencv
但是该写法的问题是:有的时候,你自己都不知道具体用了opencv的哪些/个库、或者甚至不同版本有哪些库,所以就不好写。所以有了下面简单粗暴的写法,那就是把opencv全部库,都通过pkg-config的方式写上。当然缺点就是浪费磁盘、内存等资源。
方法2、全部都要
参考链接:Linux在 Makefile 中使用 pkg-config 添加库的方法
pkg-config的方法,会去系统路径寻找对应库的xx.pc文件,例如opencv的/usr/local/lib/pkgconfig/opencv.pc文件,读取依赖库的头文件、库名称等信息,省时省力。除了opencv,其他一些库也可以这样调用。
调用方法:
LIBOPENCV_INC = $(shell pkg-config --cflags opencv) #opencv的头文件
LIBOPENCV_LIBS = $(shell pkg-config --libs opencv) #opencv的库名称
...
CFLAGS += $(LIBOPENCV_INC)
LIBS += $(LIBOPENCV_LIBS) -lstdc++
$(target): $(obj)
$(cc) -o $(target) $(obj) $(CFLAGS) $(LIBS)
rm -rf $(obj)
%.o: %.c $(deps)
$(cc) $(CFLAGS) -c $< -o $@
补充1:
附录opencv.pc文件的内容(其实该文件都是安装opencv的时候自动生成的,这里贴出来,只是为了理解这种链接方式)
# Package Information for pkg-config
prefix=/usr/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir_old=${prefix}/include/opencv
includedir_new=${prefix}/include
Name: OpenCV
Description: Open Source Computer Vision Library
Version: 3.4.15
Libs: -L${exec_prefix}/lib -lopencv_dnn -lopencv_highgui -lopencv_ml -lopencv_objdetect -lopencv_shape -lopencv_stitching -lopencv_superres -lopencv_videostab -lopencv_calib3d -lopencv_videoio -lopencv_imgcodecs -lopencv_features2d -lopencv_video -lopencv_photo -lopencv_imgproc -lopencv_flann -lopencv_core
Libs.private: -ldl -lm -lpthread -lrt
Cflags: -I${includedir_old} -I${includedir_new}
补充2 如果报错:
make: pkg-config: Command not found
安装pkg-config:
sudo apt-get install pkg-config
补充3 如果报错
Package opencv was not found in the pkg-config search path.
Perhaps you should add the directory containing `opencv.pc'
to the PKG_CONFIG_PATH environment variable
No package 'opencv' found
因为环境变量未设置,如下添加:
vim ~/.bashrc
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:~/linux/mylib/opencv-3.4.1/install/lib/pkgconfig
source ~/.bashrc
//查看路径
pkg-config --libs --cflags opencv
//输出
-I/home/pjw/linux/mylib/opencv-3.4.1/install/include/opencv -I/home/pjw/linux/mylib/opencv-3.4.1/install
七、其他报错信息
*undefined reference to symbol
*libfastrtps.so.1:error adding symbols: DSO missing from command line
原因:提示说符号没有找到定义,但后面指出在libfastrtps.so.1动态库中找到该符号,(DSO missing from command line) 意思就是你没有在makefile指定要使用该库,加上 -llibfastrtps 指定一下即可;
错误提示说得清清楚楚了,就是 makefile 缺少了指定库 的命令语句!
附:makefile备份
CC = gcc
CPP = g++
RM = rm -f
#支持debug
## debug flag
DBG_ENABLE := 1
# #检查当前系统
# OS = $(shell uname)
#输出目标路径及名称
## target file name
TARGET_LIB:=../api/lib/libKeySafeLinger.so
TARGET := run
# linux环境下操作特大文件
## macro define
DEFS := __LINUX__ OS_LINUX _FILE_OFFSET_BITS=64
#--------------------------------SDK外-依赖--------------------------------
#opencv
LIBOPENCV_INC = $(shell pkg-config --cflags opencv)
LIBOPENCV_LIBS = $(shell pkg-config --libs opencv)
# #cuda
LIBCUDA_INC = /usr/local/cuda/include/ /usr/local/cuda/targets/x86_64-linux/lib/
# LIBCUDA_LIBPATH = /usr/local/cuda/lib64/ #cuda安装在系统路径下,库的路径可以不填,注释
#cudnn
LIBCUDNN_INC = /usr/local/cuda/include/ #cudnn头文件在cuda include路径下,cuda已经引用过该路径,可以注释
# LIBCUDNN_LIBPATH = /usr/lib/x86_64-linux-gnu/ #系统路径,注释
#libtorch
LIBTORCH_INC = /home/h/libtorch/include/ /home/h/libtorch/include/torch/csrc/api/include/
LIBTORCH_LIBPATH = /home/h/libtorch/lib/
#tensorRT
LIBTENSORRT_INC = /home/h/Downloads/TensorRT-7.2.2.3/include/
LIBTENSORRT_LIBPATH = /home/h/Downloads/TensorRT-7.2.2.3/lib/
#eigen
LIBEIGEN_INC = /usr/include/eigen3/
#汇总
#头文件路径 +库名称 +库路径
INCLUDE_PATH += $(LIBOPENCV_INC) $(LIBCUDA_INC) $(LIBCUDNN_INC) $(LIBTORCH_INC) $(LIBTENSORRT_INC) $(LIBEIGEN_INC)
LIBS +=$(LIBOPENCV_LIBS) -lcuda -lcudart -lc10 -lc10_cuda -ltorch -ltorch_cpu -ltorch_cuda -lshm -lnvinfer -lnvinfer_plugin -ldecodeplugin -lnvonnxparser -lyaml-cpp -luuid
LIBRARY_PATH += $(LIBTORCH_LIBPATH) $(LIBTENSORRT_LIBPATH)
#LIBRARY_PATH += $(LIBCUDA_LIBPATH) $(LIBCUDNN_LIBPATH) $(LIBTORCH_LIBPATH) $(LIBTENSORRT_LIBPATH)
#------------------------------SDK内-资源-------------------------------
#头文件路径 +库名称 +库路径
INCLUDE_PATH += ../detector/ ../common/ ../tracker/sort/ ../tracker/featureExtractor/ ../judge/ ../api/include/ ../facedetect/include/
LIBS += -lyolov5_trt -lboost_system -lboost_thread
LIBRARY_PATH += ../facedetect/lib/ ../detector/
#源文件路径
SRC_PATH :=../api/src/ ../common/ ../detector/ ../tracker/sort/ ../tracker/featureExtractor/ ../judge/
#获取全部.c和.cpp源文件
SRCS := $(foreach spath, $(SRC_PATH), $(wildcard $(spath)*.c*) )
#全部源文件编译成.o文件
## all .o based on all .c/.cpp
OBJS = $(SRCS:.c=.o)
OBJS := $(OBJS:.cpp=.o)
#是否要用gdb来debug
## debug for debug info, when use gdb to debug
ifeq (1, ${DBG_ENABLE})
CFLAGS += -D_DEBUG -g -DDEBUG=1
else
CFLAGS += -O3 -DNDEBUG
endif
CFLAGS += -fPIC $(foreach m, $(DEFS), -D$(m))
#所有头文件路径 -I
CFLAGS += $(foreach dir, $(INCLUDE_PATH), -I$(dir))
CXXFLAGS += -std=c++14 $(CFLAGS) -std=c++14
#所有库路径-L + 所有库名称-l
LDFLAGS += -Wl,--no-as-needed -lpthread $(foreach lib, $(LIBRARY_PATH), -L$(lib)) #-Wl,--no-as-needed命令必须要有,否则无法使用gpu
LDFLAGS += $(LIBS) -Wl,--rpath=../facedetect/lib/ -Wl,--rpath=../detector/ -Wl,--rpath=/home/h/libtorch/lib/
default: all
%.o: %.c
$(CC) $(CFLAGS) -std=c99 -c $< -o $@
%.o: %.cpp
$(CPP) $(CXXFLAGS) -c $< -o $@
all: $(OBJS)
$(CPP) $(CXXFLAGS) -shared -o $(TARGET_LIB) $(OBJS) $(LDFLAGS)
$(CPP) -std=c++14 -g -fPIC -I../api/include/ -I../tracker/featureExtractor/ -I$(LIBTENSORRT_INC) ../main.cpp -o $(TARGET) -L../api/lib/ -lKeySafeLinger
clean:
$(RM) $(OBJS) $(TARGET) $(TARGET_LIB)