现有如下问题:
我们在第三方动态库(比如 boost 库)的基础上,开发了自己的动态库供公司内部项目使用。在使用自己的这个动态库的时候,该如何进行编译呢?即,依赖链条是这样的情况下:
程序–(依赖)–>libA.so–(依赖)–>libB.so
该如何进行编译。
为了研究这个问题,我们建立一个目录结构,写几个简单程序来模拟一下。以下内容将从构建动态库开始,一步步展示如何达成我们的目标。
建立试验目录结构、程序
目录结构
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
我们将在这个目录下,生成 libblayer.so, libulayer.so, main(可执行文件),其依赖链条是:
main–>libulayer.so–>libblayer.so
特别的,对于 main 的编译来说,其编译参数无需指定 libblayer.so,它只需关心自己直接依赖的 libulayer.so 即可(当然,由于 libulayer.so 依赖 libblayer.so,系统中必须安装 libblayer.so)。
程序
bottomLayer/bLayer.h
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
bottomLayer/bLayer.cpp
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
upperLayer/uLayer.h
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
upperLayer/uLayer.cpp
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
main.cpp
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
这几个程序都非常简单,无需多言。
开始逐层编译
现在,我们从最底层开始逐步往上层编译。
libblayer.so
- 1
- 1
libblayer.so 比较简单,直接生成就好了。-fPIC 是要生成位置无关的代码,-shared 是要生成动态库。
libulayer.so
- 1
- 1
编译 libulayer.so 的命令前半部分和 libblayer.so 是一样的,但是由于它需要依赖于 libblayer.so,所以加了 -L 和 -l 参数,让它去 ../bottomLayer/ 找 libblayer.so 。要注意的是, -L 和 -l 不加可能也可以编译通过,但是因为找不到所依赖的文件,在最后运行可执行文件的时候,一定会出错,所以这里必须加上这两个参数。
现在我们来看看生成的 libulayer.so 是怎么样的依赖关系:
- 1
- 2
- 3
- 1
- 2
- 3
什么鬼!libblayer.so 找不到?!不是指定了 -L 和 -l 两个参数了么?不要慌。-L 和 -l 指定的是编译时要依赖的库文件和路径,但是动态库是在运行时才加载的,系统会到默认的路径(比如 /usr/lib)下去查找动态库,但是我们的 libblayer.so 并不在那些目录中,因此 not found 。
由于我们只是在试验,并不打算真的安装这些库,所以我们可以编译的时候把路径硬编码,告诉程序去哪里找 libblayer.so 。因此生成 libulayer.so 的真正命令是:
- 1
- 1
-Wl,-rpath 是告诉程序,在运行的时候如果找不到动态库,可以去 /home/neko/coding/learn/bottomLayer/ 看看(这个是我本机的路径,应设为绝对路径)。现在我们再来 ldd 一下:
- 1
- 2
- 3
- 1
- 2
- 3
main 可执行文件
现在万事具备,只欠东风了,我们来编译 main 可执行文件:
- 1
- 1
可以注意到,main 的编译参数只关心自己直接用到的 libulayer.so,而无需关心更底层的 libblayer.so 。运行一下看看:
- 1
- 2
- 1
- 2
得到了正确结果。
使用 CMake
上文通过实例简单介绍了 Linux 动态库编译与多重依赖的解决方法,编译命令直接使用的是 gcc。下面,我们来看看怎么用 CMake 来完成这项工作。
和上面的步骤类似,我们先生成 libblayer.so,然后再生成 libulayer.so,最后生成 main 可执行文件。我们的目标是提供给用户这样一个目录结构的程序包:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
可执行程序 main 调用 libulayer.so,libulayer.so 根据需要调用 libblayer.so。这里我们无需把共享库安装到系统目录里,要让用户直接可以把 main 给运行起来。
libblayer.so
在 bottomLayer 目录下,新建文件 CMakeLists.txt 。输入如下内容:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
然后新建 build 文件夹,进入 build,进行编译
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
然后我们就得到了 libblayer.so,这个过程很简单很普通,不多讲。
libulayer.so
libulayer.so 需要依赖于 libblayer.so,CMakeLists.txt 如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
编译步骤和上面一样,我们将得到一个 libulayer.so。通过 readelf 命令,我们可以来检查 rpath 是否已经设置正确:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
从上面可以看到,libulayer.so 的 rpath 已经正确设置,而且它依赖于 libblayer.so。下面我们将开始最终的工作,生成 main 可执行文件。
main 可执行文件
在有了 libblayer.so 和 libulayer.so 以后,我们新建一个 libs 文件夹,把它们放进来,目录结构上文已述。
CMakeLists.txt 如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
至此,生成的多重依赖的可执行文件 main,可以正常的运行了。把 main,libs/libblayer.so, libs/libulayer.so 保持目录结构地移动到其它地方去,也都可以正常运行。目标达成。
本文链接:http://blog.csdn.net/shutdown_r_now/article/details/51991076
参考文档:http://blog.csdn.net/nodeathphoenix/article/details/9982209