Jason Pan

TS1.2 / CentOS6.2 上构建高GCC编译环境

潘忠显 / 2021-04-13


1. 背景

因此需要在tlinux1.2基础镜像基础上搭建有高版本GCC(9.3.0)和Bazel(4.0)的环境。

2. 基础镜像

在devcloud上申请容器。容器管理中可以选择操作-更多-修改镜像,然后选择tlinux1.2的镜像。

这个tlinux1.2镜像的一些特点:

  1. 在Centos6.2基础上做的修改
  2. 内核版本 3.10.107
  3. glibc 版本是 2.12
  4. 内核不支持SO_REUSEPORT
  5. 镜像中/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.011.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文件中。