TS1.2 / CentOS6.2 上构建高GCC编译环境
潘忠显 / 2021-04-13
1. 背景
- 开发了一个项目,使用C++ 17开发,使用Bazel 4.0构建
- 线上还有很多机器使用tlinux1.2
- tlinux1.2自带GCC 4.4.6无法满足需求
- tlinux2上构建结果会依赖更高版本的glibc,放在tlinux1.2上无法运行
因此需要在tlinux1.2基础镜像基础上搭建有高版本GCC(9.3.0)和Bazel(4.0)的环境。
2. 基础镜像
在devcloud上申请容器。容器管理中可以选择操作-更多-修改镜像,然后选择tlinux1.2的镜像。
这个tlinux1.2镜像的一些特点:
- 在Centos6.2基础上做的修改
- 内核版本 3.10.107
- glibc 版本是 2.12
- 内核不支持
SO_REUSEPORT
- 镜像中/tools目录下边有比较丰富的工具,比如 jdk1.8.0 和python 3.6.9
内核版本查看
uname -r
glibc版本的查看
ldd --version
是否支持SO_REUSEPORT
虽然setsockopt(2)
说明中有说从3.9开始有,但是实际使用的发现在tlinux1.2上使用的时候,并没法这个宏的定义。
grep -Ril 'SO_REUSEPORT' /usr/include/
# /usr/include/asm-generic/socket.h
找到的socket.h文件的SO_REUSEPORT
是被注释的
#define SO_BSDCOMPAT 14
/* To add :#define SO_REUSEPORT 15 */
使用指定版本的Python
PYTHONHOME: Change the location of the standard Python libraries.
PYTHONPATH: Augment the default search path for module files.
export PYTHONHOME=/tools/python/3.6.8/
export PATH=$PYTHONHOME/bin/:$PATH
使用指定版本的JDK
export JAVA_HOME=/tools/jdk/1.8.0_161
export PATH=$JAVA_HOME/bin:$PATH
3. 安装GCC 9.3.0
这里封装一个脚本,可以通用的安装GCC版本,我尝试过 9.3.0
和 11.1.0
两个版本都是可以的。
将以下内容保存到 install-gcc.sh
文件,执行 sh install-gcc.sh 9.3.0
即可安装 9.3.0 版本。
# install-gcc.sh
set -e
gcc_version=$1
mkdir -p /tmp/gcc-tmp && cd /tmp/gcc-tmp
wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-${gcc_version}/gcc-${gcc_version}.tar.gz
tar zxf gcc-${gcc_version}.tar.gz
cd gcc-${gcc_version} && ./contrib/download_prerequisites
mkdir -p ../objdir && cd ../objdir
$PWD/../gcc-${gcc_version}/configure --enable-languages=c,c++ --prefix=/usr
make -j 8 && make install
cd ~ && rm -fr /tmp/gcc-tmp
上边直接指定 --prefix=/usr
,会直接替换/更新了老版本的 GCC 的软链,会省掉很多很多的麻烦。
如果不指定,默认的是装到 /usr/local
下边,但是 CentOS 的搜索路径却不是优先搜索这个目录。
官方安装指引链接。
4. 安装Bazel 4.0
需要按照后边#遇到的问题 小节中介绍的那样,先处理一下gcc,避免出现问题。
mkdir /tmp/bazel && cd /tmp/bazel
wget https://github.com/bazelbuild/bazel/releases/download/4.0.0/bazel-4.0.0-dist.zip
unzip bazel-4.0.0-dist.zip
env EXTRA_BAZEL_ARGS="--host_javabase=@local_jdk//:jdk" bash ./compile.sh
cp output/bazel /usr/bin/bazel
cd ~ && rm -rf /tmp/bazel
如果上边的过程遇到问题,看后边的#遇到的问题 !!!
官方安装指引链接
5. 更新 binutils
对gcc来说,只能将C代码编译为以.s结尾的文本形式的汇编文件,而.s到.o的过程,则需要由as来完成,而as属于gnu binutils软件包。
wget http://ftp.gnu.org/gnu/binutils/binutils-2.25.1.tar.bz2
tar -xjf binutils-2.25.1.tar.bz2
cd binutils-2.25.1
./configure --prefix=/usr
make -j
make -j install
参考链接:https://github.com/tarantool/tarantool/issues/4639
6. 安装git
unset PYTHONHOME
rpm --import /etc/pki/rpm-gpg/RPM*
yum install -y curl-devel openssl-devel
wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.18.0.tar.gz
tar zxf git-2.18.0.tar.gz
cd git-2.18.0
make configure
./configure --prefix=/usr
make -j && make install
官方安装指引链接。
7. 遇到的问题
**遇到问题,分析问题,解决问题。**如果缺少分析问题的过程,会花更长的时间去找到解决问题的方法,也会错过得到进步的过程。
clock_gettime
未定义的符号
提示如下:
build: undefined reference to symbol ‘clock_gettime’
分析和解决问题
“undefined reference to symbol"是什么错误?
undefined reference to symbol
往往是依赖的动态链接库没有找到对应的符号,也就是在链接阶段报错。
系统的头文件里边是有,不然会编译阶段报错。通常,这样的函数是可以有库函数,可通过man来查看的。
man clock_gettime
可以显示,并且其中明确写到,需要“Link with -lrt
”。
为什么在高版本的机器上编译没有问题?
查看最新的clock_gettime
文档(链接),其中有提到:
Link with -lrt (only for glibc versions before 2.17).
也就是高版本不用带这个-lrt
的也可以找到符号位置。
如何才能不修改项目代码的前提下,加上这个-lrt
?
将gcc文件做修改:
gcc_path=`which gcc`
mv $gcc_path ${gcc_path}.bin
echo "${gcc_path}.bin -lrt \"\$@\"" > $gcc_path
chmod +x $gcc_path
这里用到了$@,需要解释一下:
$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(” “)包含时,都以”$1" “$2” … “$n” 的形式输出所有参数。
但是当它们被双引号(" “)包含时,"$*” 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;"$@" 会将各个参数分开,以"$1" “$2” … “$n” 的形式输出所有参数。
PRId64未定义的问题
./src/main/tools/logging.h:44:27: error: expected ')' before 'PRId64' 44 | fprintf(stderr, "%" PRId64 ".%09ld: %s:%d: " fmt "\n", \ | ~ ^~~~~~
这个错误意味着PRId64
没有声明过。
我们知道这个PRId64
是用来打印uint64_t
类型的,相关的定义字<inttypes.h>
中。
直接知道查看文件/usr/include/inttypes.h
,发现其中有一段代码:
/* The ISO C99 standard specifies that these macros must only be
defined if explicitly requested. */
#if !defined __cplusplus || defined __STDC_FORMAT_MACROS
...
/* Macros for printing format specifiers. */
/* Decimal notation. */
# define PRId8 "d"
# define PRId16 "d"
# define PRId32 "d"
# define PRId64 __PRI64_PREFIX "d"
...
也就是说C99要求如果要使用,需要明确的指出。指出的方式就是通过定义__STDC_FORMAT_MACROS
这个宏即可。
这个可以参考上一小节,加默认的-lrt
的方式一样,将-D__STDC_FORMAT_MACROS
加到gcc文件中。