前言

GCC(GNU Compiler Collection,GNU编译器合集)是linux以及其他类UNIX平台上进行开源项目,软件开发等必不可少的工具链组成之一。(其他的还有clang以及llvm等编译器)

对于程序员以及系统管理员而言,经常需要从软件的源码进行手动编译安装,

不论是configure脚本,还是make工具,最终都需要调用gcc(或者其它编译器)来进行实际的编译工作。

另外,各种软件也经常需要使用gcc的新版特性,并且与旧版gcc共存,根据实际的需求进行调用。

实验环境:VMware Workstation Pro 14(试用版)
系统平台:
CentOS release 6.9 (Final)             内核  2.6.32-696.el6.x86_64

软件包:minimal最小化安装

基础知识

glibc

GNU C Library 
https://www.gnu.org/
GNU C库项目提供了 GNU系统和GNU / Linux系统的核心库,以及许多其他使用Linux作为内核的系统。glibc是Linux系统中最底层的API,几乎其它任何的运行库都要依赖glibc。这些库提供了关键的API,
包括ISO C11,POSIX.1-2008,BSD,操作系统特定的API等等。这些API包括openreadwrite,malloc,printf, getaddrinfo,dlopen,pthread_create, crypt,login,exit等基础设施。
GNU C库被设计成一个向后兼容、便携、高性能的ISO C库。它旨在遵循所有相关标准,包括ISO C11,POSIX.1-2008和IEEE 754-2008。

该项目始于1988年,已有近30年的历史。

没有这个软件包,Linux系统将不能正常工作。

默认最小化安装已经随系统安装好

#ldd --version
ldd (GNU libc) 2.12
#rpm -qa|grep glibc
glibc-common-2.12-1.209.el6.x86_64
glibc-2.12-1.209.el6.x86_64
glibc-headers-2.12-1.209.el6.x86_64
glibc-devel-2.12-1.209.el6.x86_64

Glibc是非交叉编译环境下,从源码编译安装gcc必备的。

如果是交叉编译的环境,即在一种硬件架构/操作系统上编译(宿主机),但是生成的二进制可执行文件要在另一种硬件架构/操作系统上运行(目标机),

在这种情况下,不能依赖宿主机器上已编译的C库,最好是自行下载glibc的源码并编译。

glibc-static

GNU C标准库的静态版,只有在用于 -static 静态链接时才需要,
glibc-static是非交叉编译环境下,从源码编译安装gcc必备的,而且它依赖于Glibc(要先装Glibc)

默认最小化安装并不会预装glibc-static

libstdc++

兼容GCC的C++标准库

libc++是针对clang编译器特别重写的C++标准库,那libstdc++自然就是gcc的事儿了。libstdc++与gcc的关系就像clang与libc++。

默认最小化安装已经随系统安装好

#rpm -qa|grep libstdc++
libstdc++-4.4.7-18.el6.x86_64
libstdc++-devel-4.4.7-18.el6.x86_64

#rpm -ql libstdc++-4.4.7-18.el6.x86_64
/usr/lib64/libstdc++.so.6
/usr/lib64/libstdc++.so.6.0.13

gcc

因为它原本只能处理C语言。GCC在发布后很快地得到扩展,变得可处理C++。之后也变得可处理Fortran、Pascal、Objective-C、Java、Ada,Go与其他语言。

许多操作系统,包括许多类Unix系统,如Linux及BSD家族都采用GCC作为标准编译器。

默认最小化安装并不会预装gcc

从源码编译安装新版gcc前,要求系统上已经安装有旧版gcc

gcc-c++

GNU编译器合集的c++增强支持包,它向gcc的c++编译器提供了更多对当前c++标准规范的支持,包含模板和错误处理。

默认最小化安装并不会预装gcc-c++

gcc-c++ 还需要额外的 libstdc++ 支持

准备编译环境

为了能顺利的yum安装,重新配置了光盘和aliyun的yum源

请自行参考
https://mirrors.aliyun.com/help/centos

安装编译依赖软件包

因此,编译安装新版本的gcc组件,需要准备以下3个软件包

gcc gcc-c++ glibc-static

#yum install gcc gcc-c++ glibc-static

会自动解决依赖关系
image
验证这3个软件包的状态

#rpm -qa|grep gcc;rpm -qa|grep glibc-static
libgcc-4.4.7-18.el6.x86_64
gcc-4.4.7-18.el6.x86_64
gcc-c++-4.4.7-18.el6.x86_64
glibc-static-2.12-1.209.el6_9.2.x86_64

#which gcc;which g++
/usr/bin/gcc
/usr/bin/g++

#rpm -ql glibc-static
/usr/lib64/libBrokenLocale.a
/usr/lib64/libanl.a
/usr/lib64/libc.a
/usr/lib64/libc_stubs.a
/usr/lib64/libcrypt.a
/usr/lib64/libdl.a
/usr/lib64/libm.a
/usr/lib64/libnsl.a
/usr/lib64/libpthread.a
/usr/lib64/libresolv.a
/usr/lib64/librt.a
/usr/lib64/libutil.a

编译器( cc1 )预链接器( collect2 )链接器 ( ld ) 汇编器 ( as )

cc1负责对源文件实际的编译工作;

collect2进行预链接处理,实际的链接由链接器ld完成;

安装到以下路径

#ls /usr/libexec/gcc/x86_64-redhat-linux/4.4.7
cc1  cc1plus  collect2

#which ld
/usr/bin/ld

头文件在以下目录

#ls /usr/lib/gcc/x86_64-redhat-linux/4.4.7/include/
abmintrin.h  bmmintrin.h     f16cintrin.h  iso646.h     mmintrin.h   pmmintrin.h     stdbool.h    tbmintrin.h  wmmintrin.h
ammintrin.h  cpuid.h         float.h       limits.h     mm_malloc.h  popcntintrin.h  stddef.h     tmmintrin.h  x86intrin.h
avxintrin.h  cross-stdarg.h  fma4intrin.h  lwpintrin.h  nmmintrin.h  smmintrin.h     stdfix.h     unwind.h     xmmintrin.h
bmiintrin.h  emmintrin.h     immintrin.h   mm3dnow.h    omp.h        stdarg.h        syslimits.h  varargs.h    xopintrin.h

gcc4.4.7版本开始,官方推荐使用单独的目录,而不是在解压gcc源码后的目录中进行编译,

原因在于:

优化以及生成性能更好的二进制可执行文件;

避免不可预期的错误发生;

避免污染源码所在目录;

因此,我们需要在文件系统的指定目录下建立1个用于编译的目录

并使用configure脚本的 --prefix=/usr/local/[自定义的路径名称] 来指定安装目录,这样,gcc的可执行文件,库文件,头文件就会井然有序分别存放在相应的子目录下,

以后不用时直接 rm -rf 整个安装路径就可以了。这就是编译安装的好处,你清晰的知道所有产生的文件来自哪里。

以下操作如无特殊说明 ,全部在/app/sdb目录操作。

#mkdir gcc-build

下载gcc源码包

CentOS 7 使用4.8.5,一般情况下这个版本的编译器已经满足需要了。

考虑到与Centos 7的兼容性,这里选择了gcc 4.8.5

去官网下载gcc4.8.5源码包

ftp://gcc.gnu.org/pub/gcc/releases

对应版本的目录下会有md5.sum等校验信息文件,下载到本地后,运行md5sum进行比对,如果值不一样,则说明下载过程中有错误,需要重新下载。
#md5sum gcc-4.8.5.tar.gz 
bfe56e74d31d25009c8fb55fd3ca7e01  gcc-4.8.5.tar.gz

下载编译依赖包

编译安装 GCC 需要依赖 cloog gmp mpc mpfr isl

其中 isl 并不是编译gcc必需,但是可以优化生成的可执行文件,因此推荐下载。

特别提醒:

不同版本软件的相互组合,可能并不会预期的正常编译,导致后面的make第一阶段make退出。下面是我测试了几种组合得到的结果,尽可能的使用了高版本。

联合编译时间较长,希望这表能帮助你节约时间。

image

GNU软件都可以到以下网址下载,isl不是GNU项目,只能到官方网下载
https://ftp.gnu.org/

mpfr
https://ftp.gnu.org/gnu/mpfr/

gmp
https://gmplib.org/

mpc
http://www.multiprecision.org/mpc/

cloog
http://www.cloog.org/

isl
http://isl.gforge.inria.fr/
全部下载好了
#tree
.
├── gcc-build
└── src
    ├── cloog-0.18.4.tar.gz
    ├── gcc-4.8.5.tar.gz
    ├── gmp-6.1.2.tar.xz
    ├── isl-0.14.tar.gz
    ├── mpc-1.0.3.tar.gz
    └── mpfr-3.1.4.tar.gz

解压gcc源码包

#tar xvf src/gcc-4.8.5.tar.gz -C /app/sdb/

将这5个依赖库的源码包,解压到gcc源码目录下,由于gcc编译规则只能识别不带版本号的子目录名称,

因此,还需要将其重命名。这样在gcc编译的时候,会自动编译相关的组件

#tar xvf src/cloog-0.18.4.tar.gz -C gcc-4.8.5/
#tar xvf src/gmp-6.1.2.tar.xz -C gcc-4.8.5/
#tar xvf src/isl-0.14.tar.gz -C gcc-4.8.5/
#tar xvf src/mpc-1.0.3.tar.gz -C gcc-4.8.5/
#tar xvf src/mpfr-3.1.4.tar.bz2 -C gcc-4.8.5/

#mv gcc-4.8.5/cloog-0.18.4 gcc-4.8.5/cloog
#mv gcc-4.8.5/gmp-6.1.2/ gcc-4.8.5/gmp
#mv gcc-4.8.5/isl-0.14/ gcc-4.8.5/isl
#mv gcc-4.8.5/mpc-1.0.3/ gcc-4.8.5/mpc
#mv gcc-4.8.5/mpfr-3.1.4/ gcc-4.8.5/mpfr

完成之后,目录结构应该是类似以下的:
gcc-4.8.5/
├── cloog
├── gmp
├── isl
├── mpc
├── mpfr

由于之前使用yum安装gcc旧版本编译器的时候,安装了一些与编译依赖包相同的包,会不会产生冲突?

结论是:不会,因为这5个依赖库是编译gcc源码时才需要的,而且使用单独的编译目录来存储中间编译好的文件,就是为了避免这种问题。

当然,还有更简单的方法,运行源码包中的contrib/download_prerequisites这个脚本,会自动下载相应的依赖包。可以查看这个脚本内容。一般来说,较新的源码包会下载较新的依赖包。

不过,下载速度真的是慢,建议自行下载吧。

至此,准备工作就绪。

编译安装gcc

参数说明

--prefix=/usr/local/gcc-4.8.5
           新版本最终安装的目录

--with-pkgversion=version
            指定一个标识你的包的字符串。您可能希望包含内部版本号或生成日期。这个版本的字符串将被包含在输出中gcc --version。这个后缀不会替换默认的版本字符串,只会替代'GCC'部分。

默认值是'GCC”。

--enable-bootstrap  
          也就是进行冗余的编译检查工作。非交叉编译环境下,默认已经将该值设为 enable,可以不用显式指定;交叉编译环境下,需要显式将其值设为 disable。

--enable-checking=release 
          以软件发布版的标准来对编译时生成的代码进行一致性检查;设置该选项为enable并不会改变编译器生成的二进制结果,
          但是会导致编译的时间增加;该选项仅支持gcc编译器;

总体而言,对于上面这个选项,机器的硬件配置较低,以及不愿等待太久编译时间的童鞋,可以设置为 disable;但是这会增加产生未预期的错误的风险,所以应该慎用。
可以同时设置 --disable-bootstrap 与  --disable-checking,这对编译过程的提速很有帮助。

--enable-languages=c,c++  
        目前,可以使用任何如下: all,default,ada,c,c++,fortran,go,jit,lto,objc,obj-c++。如果 all被指定,则所有可用的语言都被建立。
        jit语言是一个例外 的语言。这里按需只开启了c和c++,因为支持的语言越多,就需要安装越多的相应静态与动态库,还有五花八门的依赖库,这会让管理变得困难,体积也会变得庞大。

--disable-multilib 
        生成多个平台的代码,比如:64bit机器,同时可以产生32和64两种格式;如果你的操作系统是64位,默认就已经设置为 enable,这意味着用gcc编译其它源文件时可以通过
        -m32 选项来决定是否生成32位机器代码。如果在64位系统上,要禁止生成32位代码, 设置 --disable-multilib。

--enable-gather-detailed-mem-stats
        允许收集详细的内存使用信息,如果设置该参数为 enable,则将来编译好的gcc可执行程序,可以通过 -fmem-report 选项来输出编译其它程序时的实时内存使用情况。

--with-long-double-128 
        指定long double类型的默认值是128位。如果使用--without-long-double-128, long double将默认64位,与double类型相同。
        如果使用GNU C Library 2.4或更高版本,则默认为128位。

生成编译文件

#cd gcc-build/
#/app/sdb/gcc-4.8.5/configure --prefix=/usr/local/gcc-4.8.5 --enable-checking=release --enable-bootstrap --enable-languages=c,c++ --disable-multilib --enable-gather-detailed-mem-stats --with-long-double-128

会在gcc-build目录生成以下4个文件
#ls
config.log  config.status  Makefile  serdep.tmp

使用vim等编辑工具打开config.log,查找error关键字,搜索configure检测到的潜在配置错误和警告,这对后面的make阶段能否正常编译至关重要,因此要谨慎对待。如果没有任何与error相关的警告信息,那么就认为可以执行 make 命令。
image

conftest.cpp:11:2: error: #error -static-libstdc++ not implemented 忽略它也能正常编译,未能找到解决的方法。

如果需要重新configure或make中途出错退出,最好把当前目录下的所有东西都删除干净。
也可以执行下面指令清空 编译目录下的相关文件,包括makefile

# make distclean

编译文件

make命令中的-j 指定同时开启的进程数,要充分发挥多核处理器的并行执行优势,这个值应该是处理器芯片上物理核心的2倍。CPU给不给力就看这个了。

#lscpu |grep -A 1 'Model name'
Model name:            Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz
Stepping:              4
这条指令重点是make -j 8 ,前后加的date指令是为了统计编译时间的。

#date +%F-%T >> /app/time.txt && make -j 8 && date +%F-%T >> /app/time.txt
如果看到以下类似字样,恭喜你,编译通过了。
make[4]: Leaving directory `/app/sdb/gcc-build/x86_64-unknown-linux-gnu/libmudflap'
make[3]: Leaving directory `/app/sdb/gcc-build/x86_64-unknown-linux-gnu/libmudflap'
make[2]: Leaving directory `/app/sdb/gcc-build/x86_64-unknown-linux-gnu/libmudflap'
make[1]: Leaving directory `/app/sdb/gcc-build'

最终整个编译目录占用了2.7GB空间,消耗的时间在40~50分钟内

编译后安装

# make install

image

根据编译前的设定,在指定目录生成了对应的gcc文件夹

#ls /usr/local/gcc-4.8.5/
bin  include  lib  lib64  libexec  share

查验版本

#/usr/local/gcc-4.8.5/bin/gcc -dumpversion
4.8.5

#/usr/local/gcc-4.8.5/bin/g++ -dumpversion
4.8.5

#/usr/local/gcc-4.8.5/bin/c++ -dumpversion
4.8.5

添加LD_LIBRARY_PATH

需要将新版gcc的库文件安装路径,添加到“库的搜索路径”(LD_LIBRARY_PATH)这个环境变量中,前面在make install结束时,系统也给出了类似的提示信息(参考前面的截图):

image

临时生效:

#export LD_LIBRARY_PATH="/usr/local/gcc-4.8.5/lib64:$LD_LIBRARY_PATH"

永久生效:

不建议直接修改ld.so.conf,推荐的做法是在ld.so.conf.d目录下按程序名建立以.conf结尾的文件,把库路径写入即可。
#echo '/usr/local/gcc-4.8.5/lib64' > /etc/ld.so.conf.d/gcc4.8.5.conf

#ldconfig    更新加载动态库cache

出现以下提示,把这个文件删除了或忽略它
ldconfig: /usr/local/gcc-4.8.5/lib64/libstdc++.so.6.0.19-gdb.py is not an ELF file - it has the wrong magic bytes at the start.

在我实际使用中,到这一步都已经可以正常编译安装mariadb 10.2.12了。并不需要添加新gcc的环境变量。

添加环境变量

#vim /etc/profile.d/env.sh
PATH=/usr/local/gcc-4.8.5/bin:$PATH
#source /etc/profile.d/env.sh

到这里,系统会优先根据PATH路径的先后顺序直接加载使用最新版本的gcc工具。已经可以正常工作了。可以查看后面的测试章节。

如果是需要gcc多版本共存的情况,请直接跳至下面的 多版本gcc共存 开始阅读。

make这种自动编译大型项目的工具只会使用cc作为编辑器,而cc则是指向旧的gcc程序,

#make -p|grep "CC ="
CC = cc

#ll /usr/bin/cc
lrwxrwxrwx. 1 root root 3 Jan 27 10:07 /usr/bin/cc -> gcc

以下是网上说的更新某个动态库,我并没有这么做,也能正常的把mariadb 10.2.12编译成功。留着备用吧。

这里的操作未执行:

#strings /usr/lib64/libstdc++.so.6 | grep GLIBC
看最后一个,这个与libstdc++.so.6.0.13的尾号是相对应的
GLIBCXX_3.4.13
#ln -sf /usr/local/gcc-4.8.5/lib64/libstdc++.so.6.0.19 /usr/lib64/libstdc++.so.6

至此,前面生成的一些编译目录和源码相关文件都可以按你的需要进行清理了。

多版本gcc共存

有时候由于各种原因需要将不同版本的gcc编译器都同时存在系统中,方便进行调用。如果只是开发单独在编译的时候需要的话,仅仅将对应版本路径直接执行就可以了,个人单独配置LD_LIBRARY_PATH。

当是以系统级别整体切换的时候,那就需要用到一个小工具了。

alternatives

alternatives是dpkg的实用工具,用来维护系统命令的符号链接,以决定系统默认使用什么命令。

使用此工具管理命令时,必须要配置环境变量的PATH路径。如:/usr/local/gcc-4.8.5/bin

alternatives [options] --install link name path priority [--slave link name path]...  [--initscript service]
alternatives [options] --remove name path
alternatives [options] --set name path
alternatives [options] --auto name
alternatives [options] --display name
alternatives [options] --config name

具体用法,请man

#ll /usr/sbin/update-alternatives 
lrwxrwxrwx. 1 root root 12 Dec 13 20:15 /usr/sbin/update-alternatives -> alternatives

语法说明:

image

还原gcc环境变量

#gcc -v
gcc version 4.4.7 20120313 (Red Hat 4.4.7-18) (GCC) 

把系统旧的程序或文件变更为以版本号命名的方式

#mv /usr/bin/gcc /usr/bin/gcc-4.4.7
#mv /usr/bin/g++ /usr/bin/g++-4.4.7
#mv /usr/bin/c++ /usr/bin/c++-4.4.7

注册旧的版本至alternatives

#alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.4.7 50
#alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.4.7 50
#alternatives --install /usr/bin/c++ c++ /usr/bin/c++-4.4.7 50

注册新的版本至alternatives

#alternatives --install /usr/bin/gcc gcc /usr/local/gcc-4.8.5/bin/gcc 60
#alternatives --install /usr/bin/g++ g++ /usr/local/gcc-4.8.5/bin/g++ 60
#alternatives --install /usr/bin/c++ c++ /usr/local/gcc-4.8.5/bin/c++ 60
#alternatives --display gcc                             #gcc就是上面定义的名称
gcc - status is auto.                                   #当前使用的模式
 link currently points to /usr/local/gcc-4.8.5/bin/gcc  #当前使用的链接
/usr/bin/gcc-4.4.7 - priority 50                        #版本1的链接
/usr/local/gcc-4.8.5/bin/gcc - priority 60              #版本2的链接
Current `best' version is /usr/local/gcc-4.8.5/bin/gcc. #优先级高的选项

如何手动切换版本

自动切换配合--auto动作,并设置好优先级数字,数字越大,优先级越高。手动方式切换后,将会取消自动切换。

从手动切换回自动,alternatives --auto gcc

#alternatives --config gcc

There are 2 programs which provide 'gcc'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/bin/gcc-4.4.7
   2           /usr/local/gcc-4.8.5/bin/gcc

Enter to keep the current selection[+], or type selection number: 2

---

#alternatives --config g++

There are 2 programs which provide 'g++'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/bin/g++-4.4.7
   2           /usr/local/gcc-4.8.5/bin/g++

Enter to keep the current selection[+], or type selection number: 2

---

#alternatives --config c++

There are 2 programs which provide 'c++'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/bin/c++-4.4.7
   2           /usr/local/gcc-4.8.5/bin/c++

Enter to keep the current selection[+], or type selection number: 2

切换结果

#gcc -dumpversion
4.8.5

#g++ -dumpversion
4.8.5

#c++ -dumpversion
4.8.5

测试

这是使用系统旧版本4.4.7 gcc编译cmake时的提示,并且报错退出。

#../cmake-3.10.2/configure --prefix=/usr/local/cmake-3.10.2
---------------------------------------------
CMake 3.10.2, Copyright 2000-2017 Kitware, Inc. and Contributors
Found GNU toolchain
C compiler on this system is: gcc  
---------------------------------------------
Error when bootstrapping CMake:
Cannot find a C++ compiler supporting C++11 on this system.
Please specify one using environment variable CXX.
See cmake_bootstrap.log for compilers attempted.

这是使用新版本4.8.5 gcc编译cmake时的提示,已经没有报错了

#../cmake-3.10.2/configure --prefix=/usr/local/cmake-3.10.2
---------------------------------------------
CMake 3.10.2, Copyright 2000-2017 Kitware, Inc. and Contributors
Found GNU toolchain
C compiler on this system is: gcc  
C++ compiler on this system is: g++  -std=gnu++1y 
Makefile processor on this system is: gmake
g++ has setenv
g++ has unsetenv
g++ does not have environ in stdlib.h
g++ has stl wstring
g++ has <ext/stdio_filebuf.h>
编译MariaDB 10.2.12,因为这个版本明确指名需要使用gcc 的 c++11特性
可以明确的看到版本号并且调用成功

-- Running cmake version 3.10.2
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Looking for SHM_HUGETLB
-- Looking for SHM_HUGETLB - found
-- MariaDB 10.2.12