Ubuntu环境下将MTCNN的C++程序封装成.so动态库文件和.a静态库文件

近期现学现做一个在Ubuntu环境下将C++程序封装成动态.so库文件和静态.a库文件的小项目, 期间不知道掉了多少坑,所以在这里记录下来与诸君共勉

一、静态库和动态库

本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行,库可分成静态库和动态库(共享库),在Windows系统下,分别对应着xx.lib和xx.dll文件,在linux系统下,分别是xx.a和xx.so文件。

1.静态库

静态库的命名形式是libname.a.静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。它的优点是,编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进可执行文件了。同样它的不足,如果静态函数库改变了,那么你的程序必须重新编译,而且体积也较大。

2.动态库

动态库名字一般是libname.so.相对于静态函数库,共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小由于函数库没有被整合进你的程序,而是程序运行时动态申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。而且如果多个应用程序都要使用同一函数库,动态库就非常适合,可以减少应用程序的体积。

静态库和动态库的主要区别在于:静态库是在程序编译时被链接到目标代码中,而动态库是在程序运行时才被载入。

二、生成动态库和静态库

一开始找的MTCNN的源代码是依赖于opencv和openblas环境,说起配置环境又是一把辛酸泪,在这里不再赘述,源码中头文件和cpp文件数量不多,所以采用的是用g++的方式生成库文件,可是在测试的时候,能够在没有配置opencv的环境下直接调用程序所需要的库文件,但是却不能在没有openblas的环境下调用libopenblas库文件,所以当时就放弃了这种方案。

1.用g++方式分别创建静态和动态库文件

在linux系统下,是用命令ar处理A.o目标文件生成静态库文件,需要指令如下:

1.g++ -c  A.cpp  -o A.o
2.ar -cr libA.a A.o
3.ar -r libABC.a  *.o

第一条指令是编译A.cpp 生成 A.o文件

第二条指令是生成静态库文件,在-cr后面的参数就是库文件的名字

第三条指令是将目录下的所有的.o文件合并生成静态库

在linux下编译时,通过 -shared 参数可以生成动态库.so文件,如下:

g++ -shared -fPIC -o libA.so A.o

-shared 该选项指定生成动态连接库,不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的,所以动态载入时是通过代码拷贝的方式来满足不同
进程的需要,而不能达到真 正代码段共享的目的。

2.使用cmake方式创建静态库和动态库文件

很幸运地是,我们又找到了一个不依赖任何第三方库的C++源程序代码,可是此源码的头文件和CPP文件的数量巨大,而且代码具有层次感,其中还有子文件夹,所以在这个时候,用Cmake方式创建库文件是很高效间接的手段。

采用out-of-source编译的方式,按照习惯,建立一个build目录,将源程序文件放入build目录下,并在build目录下编写CMakeLists.txt,这个文件是cmake的构建定义文件,文件名是大小写相关的。为了能同时生成动态库文件和静态库文件,CMakeLists.txt文件中的相应内容如下:

1.add_library(name SHARED source1, source2, ..., sourceN)
2.add_library(name_static STATIC source1, source2, ... , sourceN)
3.set_target_properties(name_static PROPERTIES OUTPUT_NAME "name")
4.set_target_properties(name_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
5.set_target_properties(name PROPERTIES CLEAN_DIRECT_OUTPUT 1)
6.set_target_properties(name PROPERTIES VERSION 1.2 SOVERSION 1)
7.install(TARGETS name name_static 
          LIBRARY DESTINATION lib
          ARCHIVE DESTINATION lib)
8.install(DIRECTORY ${titile_H} DESTINATION include/tH)

第一条指令是生成动态库(扩展名为.so),类型关键字是SHARED,并不需要写全libname.so,只需要填写name即可,cmake系统会自动生成libname.so。

第二条指令是在支持动态库的基础上为工程添加一个静态库,因为静态库和动态库同名时,构建静态库的指令是无效的,所以把上面的name修改为name_static,就可以构建一个libname_static的静态库;然而我们需要的是名字相同的静态库和动态库,因为target的唯一性,所以就不能通过add_library指令实现,所以用到第三条指令

第三条指令是为了能够同时得到libname.so/libname.a两个库文件,但是因为cmake在构建一个新的target时,会尝试清理掉具有相同命名的库文件,所以,在构建libname.a的时候会将libname.so库文件清理掉,因此需要再次使用set_target_properties定义的CLEAN_DIRECT_OUTPUT属性,如第四条和第五条指令所示,至此,我们再次进行构建,就会发现在目录中同时生成libname.so动态库文件和libname.a静态库文件

第六条指令是因为按照规则,动态库是应当包含一个版本号的, 为了实现动态库版本号,仍然需要使用SET_TARGET_PROPERTIES指令,其中VERSON指代动态库版本,SOVERSION指代API版本。

第七条指令是将动态库和静态库文件安装到系统目录,才能够真正地让其他人开发使用,我们将库文件安装到<prefix>/lib目录下

第八条指令是将头文件安装到<prefix>/include/tH目录中。

在终端进入build目录的上级目录,输入命令行,命令如下:

cmake build

make

sudo make install

至此,我们就可以将头文件和库文件分别安装到系统目录/usr/local/include/tH/和usr/local/lib中了。

三、外部引用动态库和静态库和头文件

构建和安装动态库和静态库之后,为了测试库文件是否被外部调用,需要编写源文件main.cpp进行函数调用测试。同样,我们还是使用cmake方式进行编译

3.1 外部引用静态库文件

1.INCLUDE_DIRECTORIES(头文件在系统中的位置)
2.ADD_EXECUTABLE(main source/main.cpp)
3.TARGET_LINK_LIBRARIES(main libfaceDetection.a)

第一条指令是引用头文件搜索路径

第二条指令的作用是生成一个名为main的可执行文件

第三条指令是位target添加静态库

3.2 外部引用动态库文件

因为编译安装将动态库安装到/usr/local/lib目录下,对于动态库的外部引用有些麻烦,稍后补上





猜你喜欢

转载自blog.csdn.net/weixin_42111770/article/details/80595633