前言
GCC(GNU Compiler Collection,GNU编译器合集)是linux以及其他类UNIX平台上进行开源项目,软件开发等必不可少的工具链组成之一(工具链的其他成员包括 binutils,Glibc,libstdc++ 等)
另 外,对于程序员以及系统管理员而言,经常需要从软件的源码手动编译安装,而不论是configure脚本,还是make工具/makefile文件,最终 都需要调用gcc(或者其它编译器)来进行实际的编译工作,因此,经常需要使用gcc的新版特性,并且与旧版gcc共存,根据实际的需求进行调用。
在本篇博文中,我们以centos6.5 32位 系统上已安装的旧版gcc二进制可执行文件以及Glibc C库为基础,从网上下载最新版的gcc源码包手动编译安装,进行简单的测试,并且与make工具整合,从configure脚本进行参数传递,优化编译等。
整个过程简单明了,图文代码并茂,希望能起到抛砖引玉的作用,激发广大爱好者编程与学习的热情。
《编译,安装与测试》
前面我们多次提到:gcc应该在单独的目录里面进行配置和编译,不要让生成的中间文件污染了源码目录,除非你决定在整个安装过程结束后,删除源码目录和编译目录。
我们使用单独的编译目录 compile-dir 来存储经由 make 命令编译生成的二进制文件,然后用make install 命令,将这些文件复制到最终的安装目录:
/usr/local/new-exec-gcc-4
.9.1
首先,进入编译目录,在该目录下,以绝对路径的形式调用gcc源码目录下的configure脚本文件,在其后面指定编译参数,其中:
--prefix=/usr/local/new-exec-gcc-4.9.1/
就是上面讲到的最终安装路径。
--enable-bootstrap
这里引用网上一些文献对该参数的解释:用第一次编译生成的程序进行第二次编译,然后用再次生成的程序进行第三次编译,并且检查比较第二次和第三次结果的正确性,也就是进行冗余的编译检查工作。
非交叉编译环境下,默认已经将该值设为 enable,可以不用显示指定;交叉编译环境下,需要显示将其值设为 disable。
--enable-checking=release 以软件发布版的标准来对编译时生成的代码进行一致性检查;设置该选项为 enable并不会改变编译器生成的二进制结果,但是会导致编译的时间增加;该选项仅支持gcc编译器;
总体而言,对于上面这个选项,机器的硬件配置较低,以及不愿等待太久编译时间的童鞋,可以设置为 disable;但是这会增加产生未预期的错误的风险,所以应该慎用。
可以同时设置 --disable-bootstrap
与 --disable-checking,这对编译过程的提速很有帮助。
--enable-threads=posix 顾名思义,启用posix标准的线程支持
要让程序能在符合POSIX规范的linux发布版上正确运行,就应该启用该选项,取决于宿主或目标操作系统的类型,其它可用值有:aix,dec,solaris,win32等,如果你是其它的类UNIX系统,就需要设置相应的值。
--enable-languages=c,c++
支持的高级语言类型和运行时库,可以设置的所有语言包括 ada,c,c++,Fortran,java,objc,obj-c++,GO 等语言。这里只开启了c和c++,因为支持的语言越多,就需要安装越多的相应静态与动态库,还有五花八门的依赖库,这会让管理变得困难,体积也会变得庞大。
--disable-multilib 很多文章对这个参数的解释让人摸不着头脑。简单的讲,如果你的操作系统是32位,默认就已经设置为 disable,这意味着gcc仅能生成32位的可执行程序;如果你的操作系统是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位(16字节!);设置为 without,则 long double类型将为64位(8字节),这将与普通的 double 类型一样。
基于 Glib 2.4以上版本编译时,默认已经是128位。
[root@centos6-5vm 桌面]# cd /extracted-src-dir/gcc-4.9.1/ [root@centos6-5vm gcc-4.9.1]# cd /compile-dir/ [root@centos6-5vm compile-dir]# pwd /compile-dir [root@centos6-5vm compile-dir]# /extracted-src-dir/gcc-4.9.1/configure --prefix=/usr/local/new-exec-gcc-4.9.1/ --enable-bootstrap --enable-checking=release --enable-threads=posix --enable-languages=c,c++ --enable-gather-detailed-mem-stats -with-long-double-128
如果以上面给出的参数集来运行configure脚本,会在 compile-dir 目录下生成
config.log,config.status,Makefile,serdep.tmp四个文件,用gedit或者其它文本编辑器打开 config.log,以 error 为关键字符串,搜索configure检测到的潜在配置错误和警告,这对后面的make阶段能否正常编译至关重要:如果没有任何与error相关的警告信息,那么就认为可以执行 make 命令。
在当前工作目录下,执行 make 命令来启动编译进程,整个编译过程花费的时间很大程度上取决于你的系统硬件配置,以及指定给configure脚本的参数,
我的硬件配置如下,大概也花了近40分钟才编译完成:
在此基础上,我用下面的命令来加速编译过程,其中 -j 指定同时开启的进程数,要充分发挥多核处理器的并行执行优势,这个值应该是处理器芯片上物理核心的2倍。根据测试,上面的硬件配置并行执行8个进程编译,大约20分钟多一些就编译完成了,所以,CPU要给力才行。
[root@centos6-5vm compile-dir]# make -j 8
另外,如果在make中途出错退出,应该执行下面指令清空 compile-dir 目录下的相关文件,包括makefile,
[root@centos6-5vm compile-dir]# make distclean
然后根据错误提示修改传递给 configure 脚本的参数,或者安装相关依赖库,支持包等等,重新运行configure脚本,检查 config.log文件的内容,最后再次执行 make命令。
执行下面的命令将编译好的可执行文件,库文件等,从 compile-dir 复制到
/usr/local/new-exec-gcc-4.9.1/
目录下面,完成最后的安装。
[root@centos6-5vm compile-dir]# make install
如果你的硬盘空间比较吃紧,或者有良好的磁盘清理习惯,那么不妨删除 compile-dir 以及 extracted-src-dir 目录中的内容,并且备份 downloaded-src-dir 目录中的所有6个源码包,日后有需要时可以再次从源码编译安装。
《测试,优化,整合make,新旧版本gcc共存》
为了方便后面的测试以及与make的整合,我们先用符号链接的方式,实现新旧版本gcc共存系统上,按需调用。
常规情况下,要用新版gcc来编译C源文件或用其它高级语言编写的源文件,需要以
/usr/local/new-exec-gcc-4.9.1/bin/gcc
这种绝对路径的形式来调用,如果仅输入命令gcc,则shell会调用旧版的gcc,也就是 /usr/bin/gcc 下的gcc,这是因为新版的gcc可执行文件所在绝对路径还没有加入shell的命令搜索路径中,而搜索路径是一种环境变量,因此按理讲应该将其加入环境变量,但是这样就会覆盖原有的gcc,或者产生歧义,shell无法判断用户的意图是要使用那一个。
使用符号链接的方案可以避免上述问题。
依旧是先确定一下旧版gcc的可执行文件所在路径:
[root@centos6-5vm bin]# cd / [root@centos6-5vm /]# which gcc /usr/bin/gcc [root@centos6-5vm /]# whereis gcc gcc: /usr/bin/gcc /usr/lib/gcc /usr/libexec/gcc /usr/share/man/man1/gcc.1.gz
进入 /usr/bin 目录,创建一个指向新版gcc可执行文件所在路径的符号链接,记得要同时创建新版g++的相应链接:
[root@centos6-5vm /]# cd /usr/bin [root@centos6-5vm bin]# pwd /usr/bin [root@centos6-5vm bin]# ln -s /usr/local/new-exec-gcc-4.9.1/bin/gcc new-gcc-4-9-1 [root@centos6-5vm bin]# ln -s /usr/local/new-exec-gcc-4.9.1/bin/g++ new-g++4-9-1
如此一来,shell还是搜索 /usr/bin 下的可执行文件,但是根据用户输入的命令来调用相应的文件,例如,用户输入 gcc,则调用 /usr/bin/gcc,用户输入 new-gcc-4-9-1,则调用相应符号链接指向的原始文件。下面的截图验证了这一部分讲述的内容:
再次确认:
接下来,需要将新版gcc的库文件安装路径,添加到“库的搜索路径”(LD_LIBRARY_PATH)这个环境变量中,日后链接器就会使用新添加的库路径中的动态库以及启动文件,来链接经由汇编器生成的目标文件,前面在make install结束时,系统也给出了类似的提示信息(参考前面的截图):
[root@centos6-5vm 桌面]# vim /etc/profile
向文件中添加以下内容,并且保存退出。
#to add the libraries of new version gcc 4.9.1 to the environment variable export LD_LIBRARY_PATH="/usr/local/new-exec-gcc-4.9.1/lib"
需要重启系统,配置项才会生效,要立即生效而不重启,执行下面的命令:
[root@centos6-5vm etc]# source /etc/profile
对 /etc/profile 文件内容的改动将影响系统中的所有用户以及包括 bash shell在内的常用 shell,这意味着,在进行团队协作软件开发,结对编程时,上面的设置能确保使用各自用户账户登录系统的程序员能在开发过程中共享新添加的库,还可以避免因为链接到的库不一致而产生冲突。
此后,我们可以立即执行下面的命令来打印并输出 $LD_LIBRARY_PATH 的当前值,从而验证上面讲到的相关内容:
[root@centos6-5vm 桌面]# echo $LD_LIBRARY_PATH /usr/local/new-exec-gcc-4.9.1/lib
顺便一提,如果你使用类似下面的命令来直接在当前开启的shell设置 LD_LIBRARY_PATH 环境变量,那么:
关闭shell后,所做的更改将会丢失,往后需要再次添加
登出 X-window ,使用其它账户登入,需要再次添加
系统重启后,需要再次添加
[root@centos6-5vm /]# declare -x LD_LIBRARY_PATH="/usr/local/new-exec-gcc-4.9.1/lib"
《新版gcc与make,configure配置脚本的协作编译》
现在,可以通过新版本的gcc来编译单独的C/C++源文件,但是对于大型的开源项目,其中有成百上千个源文件,不可能手动一个个编译,幸好成体系的软件项目都带有 configure脚本,可以显式给 configure脚本指定要使用的编译器,这是通过向configure脚本传递环境变量 CC来实现的,而CC的值可以在传递给 configure前临时定义,详情容后再作介绍
因为有些中小型项目的源码包里面没有提供 configure脚本,因此如果不在configure阶段指定编译器,那么在其后的make阶段指定也行,因为make也读取CC的值来判断要使用那种编译器:
[root@centos6-5vm 桌面]# make -p | grep CC make: *** 没有指明目标并且找不到 makefile。 停止。 LINK.o = $(CC) $(LDFLAGS) $(TARGET_ARCH) CC = cc CPP = $(CC) -E
[root@centos6-5vm 桌面]# make -p | grep CXX make: *** 没有指明目标并且找不到 makefile。 停止。 LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) COMPILE.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c CXX = g++
可以看到,make默认使用 CC作为C编译器;使用CXX作为C++编译器。
而cc是 /usr/bin 下的符号链接,它就指向 /usr/bin 下的旧版 gcc编译器:
另外,CXX的值 g++,是 /usr/bin下的旧版C++编译器:
我们需要将 cc指向新安装的gcc的二进制可执行文件。
在这之前,先删除掉系统原来创建的到旧版gcc的链接:
[root@centos6-5vm 桌面]# cd /usr/bin [root@centos6-5vm bin]# rm -f cc [root@centos6-5vm bin]# ln -s /usr/local/new-exec-gcc-4.9.1/bin/gcc cc
再次查看 cc 的属性,确认变更已经生效:
还记得前面我们将自定义名称的新版编译器(new-gcc-4-9-1)链接到同一个二进制可执行文件吗:
[root@centos6-5vm bin]# ln -s /usr/local/new-exec-gcc-4.9.1/bin/gcc new-gcc-4-9-1
这并不会产生冲突,现在, cc和new-gcc-4-9-1实际都指向同一个新版的gcc,而关键是,make这种自动编译大型项目的工具只会使用cc,因此修改cc指向的程序来让make调用新版的gcc编译软件源码就显得尤为重要。
下面这张截图验证了上面我们的劳动成果:
另一种办法是,从make的配置输出可知,cc的值赋给了环境变量CC,因此直接把cc用新版的gcc编译器的绝对路径替换掉也可以:
修改 /home/[user]/.bashrc 文件(单次修改永久有效,但仅对该用户账户的shell环境有效);
例如,当前用户是 root ,执行命令 su - shayi ,将shell环境和用户都切换到 shayi,执行 make -p | grep CC,在输出中可以看到, CC=cc
然后启动vim修改 /home/shayi/.bashrc ,在其中添加
export CC="/usr/local/new-exec-gcc-4.9.1/bin/gcc"
保存退出,在当前shell执行 exit,退出shayi用户,回到 root用户,再次执行
make -p | grep CC ,在输出中看到 CC=cc,因为修改 shayi 的bashrc仅仅影响shayi用户的环境变量,
再次执行 su - shayi ,将shell环境和用户切换到shayi,执行 make -p | grep CC,可以看到,
CC = /usr/local/new-exec-gcc-4.9.1/bin/gcc,所做的变更已经在退出该用户并且重新登录后生效。
或者系统级别的全局配置文件 /etc/profile (单次修改永久有效,而且对包括root超级用户在内的所有系统中的账户的shell环境均有效)
在文件中添加下面2行内容,:
export CC="/usr/local/new-exec-gcc-4.9.1/bin/gcc" export CXX="/usr/local/new-exec-gcc-4.9.1/bin/g++"
本段小结:
通过修改make数据库中定义的和C编译器相关的符号链接,或者环境变量,我们实现了新版gcc与make协作,自动“批量”编译项目中的C源文件。
而且,往后需要让make调用旧版gcc批量编译时,只需将相关的符号链接,或者环境变量,替换成旧版gcc的可执行文件所在路径就可以了。
这赋予软件开发人员以及程序员极大的灵活性,真正做到了自动化按需调用。