启示
- 编译
GCC native compiler
按照官方介绍并不难
步骤见后面实践脚本,以及官方编译指南链接
- GCC编译器编译其它程序组件时,会优先使用自身携带的库,例如,常用的自带库,libgcc_s.so、libstdc++
- 如果部署环境与编译要求存在比较大的依赖差异,则会诱发兼容性问题
- 高度推荐容器化部署,使得编译环境与部署环境高度一致
- 组件依赖会形成类似maven库中的依赖树,组件依赖关系各种编程语言均存在类似关系,而这与具体编程语言无关
- 在一个环境中使用不同的GCC编译版本,例如,高版本GCC编译器,尽量选择devtooolset-*系列rpm包;但如果devtoolset-*包中对齐的GCC版本并非最后结项版本,则可以选择自行编译GCC native compiler,而这个过程并不难
- 自行编译GCC native compiler,可以参考devtoolset-*系列的目录结构,和其enable切换环境脚本
- devtoolset-*早期版本容易对齐GCC结项版本,后期发行版失去维护后,较新的GCC高版本则一般没有对齐
为什么选择高版本编译器
- 更多的warning、error自动检查
部分warning可以转化成error项,结合静态代码检查工具形成代码开发工具链,以及自动编码规范
- 更准确的错误定位能力,和错误提示
- sanitizer检查能力
- 更优良的代码生成质量
- 新的语言特性
追求新语言特性的优先级最低,除非是必要、能够提高开发效率的语言特性,在一定程度上可以定死编译器的编译标准,作为编码规范的一部分
使用高版本编译器的兼容性问题
图解GCC高版本编译后的部署问题
GCC编译时优先使用自身携带库
- 这些依赖库体现为新的特性,或编译器定制化要求
- 不一致会形成依赖冲突,甚至程序不能运行
推荐部署结构
- 编译环境高度和部署环境一致
- 通过携带少量对齐库依赖,定制环境变量,达到与要求环境模拟一致,但并不污染其它应用
忽略差异依赖库的差异,依赖部署环境的向前、向后兼容能力,用覆盖测试进行保证
公之下策,实为上策!
下策看似不推荐,但是其实是大家用的比较多,也比较省事的做法。忽略了差异性可能带来的风险,而由功能覆盖测试保证产品的可用性。
但是,由于Linux社区强大的兼容性保证能力,得以很多跨版本编译、部署能够经得住功能覆盖测试
如何分析程序的依赖
# 注意LD_LIRBRARY_PATH设定为优先编译器相对于的库目录,也就是是devtoolset-* enable脚本所做的
ldd /path/to/excutable
- 由此分析出程序依赖的编译器
困难的三方组件
- 建议直接源码,或静态库依赖
- 重新编译三方库,或向能够编译它的第三方获取新的动态库版本
对于,具有第三方依赖的程序,更换编译器的代价会更大点,需要根据实际进行选择。
如果一开始,就可以将编译器标准界定的比较高,相当于从最开始就拥有了良好的开端!
从这里也说明一个问题,获取三方库,如果对方也存在复杂的依赖,三方库编译的编译器版本也是需要进行安全评估的
从使用GCC多版本再议环境变量传参的优劣
根据devtoolset-*组件enable脚本对于环境变量的设定,再次总结环境变量与命令选项在传参上的不同
- | 环境变量 | 命令选项 | 说明 |
---|---|---|---|
粒度 | 大 | 小 | 命令选项仅实施于个体程序 |
共享 | 共用 | 独用 | 环境变量在有一批程序共享使用时比较便捷 |
方便 | 自动传递 | 每次传递 | 环境变量一次设置,多次免参传递 |
- 尺有所短、寸有所长,在合适的场景使用环境变量传递参数信息非常有利,例如,切换GCC不同的版本
参考
- 官方编译GCC编译器指南
- GCC Releases
实操编译native compiler脚本
cd ~
# 建立下载源码目录
mkdir gcc
curl -O https://mirrors.ustc.edu.cn/gnu/gcc/gcc-9.5.0/gcc-9.5.0.tar.xz
tar xf gcc-9.5.0.tar.xz
# GCC top-level目录
cd gcc-9.5.0
# 下载依赖
./contrib/download_prerequisites
# 创建独立编译目录
cd ~/gcc
mkdir build
cd build
# 因为编译native compiler,除了必要定制项,其它直接以来configure自动配置
# 参考devtoolset-*中的目录结构
~/gcc/gcc-9.5.0/configure --prefix=/opt/gcc/9.5.0/root/usr --mandir=/opt/gcc/9.5.0/root/usr/share/man --infodir=/opt/gcc/9.5.0/root/usr/share/info --disable-multilib --enable-language=c,c++
# 并行编译
make -j8
–disable-multilib 撇开对于32位的支持,如果不需要的话
参考devtoolset-9的enable文件提供编译环境切换能力
# General environment variables
export PATH=/opt/gcc/9.5.0/root/usr/bin${PATH:+:${PATH}}
export MANPATH=/opt/gcc/9.5.0/root/usr/share/man:${MANPATH}
export INFOPATH=/opt/gcc/9.5.0/root/usr/share/info${INFOPATH:+:${INFOPATH}}
export PCP_DIR=/opt/gcc/9.5.0/root
# bz847911 workaround:
# we need to evaluate rpm's installed run-time % { _libdir }, not rpmbuild time
# or else /etc/ld.so.conf.d files?
rpmlibdir=$(rpm --eval "%{_libdir}")
# bz1017604: On 64-bit hosts, we should include also the 32-bit library path.
if [ "$rpmlibdir" != "${rpmlibdir/lib64/}" ]; then
rpmlibdir32=":/opt/gcc/9.5.0/root${rpmlibdir/lib64/lib}"
fi
# 库的依赖环境变量
export LD_LIBRARY_PATH=/opt/gcc/9.5.0/root$rpmlibdir$rpmlibdir32${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
export LD_LIBRARY_PATH=/opt/gcc/9.5.0/root$rpmlibdir$rpmlibdir32:/opt/gcc/9.5.0/root$rpmlibdir/dyninst$rpmlibdir32/dyninst${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
export PKG_CONFIG_PATH=/opt/gcc/9.5.0/root/usr/lib64/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}
- 切换编译环境,重要的是设置PATH和LD_LIBRARY_PATH两个环境变量
strip瘦身
strip /path/to/executable_file
- 主机上新编译的native compiler占用比较大的磁盘空间,例如,cc1 就200多M,可以用strip程序进行瘦身