需求:项目通过一次编译出的程序要能够适配任何版本Linux发行版。
通过程序的编译运行原理可以知道有几个技术问题:
编译可执行程序需要依赖的本地库的版本必须低于目标机。
许多地方用到了C++语言11以及更高版本的新特性,所以编译器版本必须是新的。
但是一个Linux操作系统安装的默认gcc/g++依赖的是同版本的c/c++标准库。
通过寻找解决方案:
在高版本的Linux发行版上编译程序,然后把程序依赖的库都安装到目标机上,让程序寻找指定的库。
解决方案
优点:自由使用所需的Linux发行版,环境良好,开发调试都是随心所欲。
缺点:安装包会比前者多一些系统库,导致安装包较大。
通过衡量,我决定前期采用第二种方案,毕竟谁也不想在2019年还整天面对一个90年代的Linux操作系统。然后后期项目成熟以后,需要优化程序体积,才能派的上用场。
以下为第二种解决方案的要点,详情请参阅man手册:man 1 ld中 关于-rpath,-rpath-link,--dynamic-linker的选项:
编译时指定<可执行程序运行时查找动态链接器的路径>,给gcc/g++添加参数[使其自动传递给ld]: -Wl,--dynamic-linker=../lib64/ld-linux-x86-64.so.2
编译时指定<动态库运行时查找库的目录>,给gcc/g++添加参数[使其自动传递给ld]:-Wl,-rpath=$ORIGIN
编译时指定<可执行程序运行时查找库的目录>,给gcc/g++添加参数[使其自动传递给ld]:-Wl,-rpath=../lib64/
编译完成后把可执行程序放入bin目录,程序依赖的所有库和链接器放入lib64目录。
打包,并拷贝至目标机上解压,进入bin目录,执行可执行程序。
注:上述路径可根据不同环境适当调整。
以下为一个简单样例:
项目结构如下:
/root/Project
├── bin // 编译出的可执行程序放这里
├── lib64 // 可执行程序依赖的所有库放这里
└── src // 源代码目录
├── demo // 主程序目录
│ └── helloworld.cpp
└── libsimple // 一个动态库,被主程序依赖。
├── libsimple.cpp
└── libsimple.h
其中 helloworld.cpp代码如下:
#include "libsimple.h"
int main(void)
{
return print();
}
libsimple.h代码如下:
#ifndef LIB_SIMPLE
#define LIB_SIMPLE
extern int print();
#endif // LIB_SIMPLE
libsimple.cpp代码如下:
#include "libsimple.h"
#include <iostream>
int print()
{
std::cout << "libsimple print" << std::endl;
return 0;
}
首先编译libsimple.cpp成libsimple.so:
g++ /root/Project/src/libsimple/libsimple.cpp -o /root/Project/lib64/libsimple.so -shared -fPIC -Wl,-rpath=../lib64/ -Wl,-rpath=$ORIGIN
然后编译helloworld.cpp成可执行程序hw:
g++ /root/Project/src/demo/helloworld.cpp -I /root/Project/src/libsimple -L /root/Project/lib64 -lsimple -o /root/Project/bin/hw -Wl,-rpath=../lib64/ -Wl,--dynamic-linker=../lib64/ld-linux-x86-64.so.2
接着把hw依赖的所有库都拷贝至/root/Project/lib64目录:
例如我的环境下
ldd /root/Project/bin/hw
linux-vdso.so.1 => (0x00007fff553fb000)
libsimple.so => ../lib64/libsimple.so (0x00007fdd63ae3000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fdd637d5000)
libm.so.6 => /lib64/libm.so.6 (0x00007fdd634d3000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fdd632bd000)
libc.so.6 => /lib64/libc.so.6 (0x00007fdd62eef000)
../lib64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x0000556098101000)
可以看到依赖了5个动态库,和1个链接器。(linux-vdso.so.1不需要拷贝) 把它们拷贝成功:
cp /lib64/libstdc++.so.6 /root/Project/lib64
cp /lib64/libm.so.6 /root/Project/lib64
cp /lib64/libgcc_s.so.1 /root/Project/lib64
cp /lib64/libc.so.6 /root/Project/lib64
cp /lib64/ld-linux-x86-64.so.2 /root/Project/lib64
此时Project目录应该如下:
/root/Project
├── bin
│ └── hw
├── etc
├── lib64
│ ├── ld-linux-x86-64.so.2
│ ├── libc.so.6
│ ├── libgcc_s.so.1
│ ├── libm.so.6
│ ├── libsimple.so
│ └── libstdc++.so.6
└── src
├── demo
│ └── helloworld.cpp
└── libsimple
├── libsimple.cpp
└── libsimple.h
现在就可以打包了,记得不要把src目录打进包里。
拷贝至任意目标机上解压,进入bin目录执行./hw
输出如下:
libsimple print
我们一次编译后的程序即可在任何Linux发行版上运行!