makefile编写问题记录--opencv调用、动态链接库/可执行文件生成、foreach等函数说明、条件判断关键字等

前言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) 

你可能感兴趣的:(c/c++语言,linux系统使用,Deep,Learning工程部署,unix,服务器)