我们先来看CLFS2.0第一编译GCC,没有使用make bootstrap来编译,而是使用make all-gcc来编译,也就是只编译了一次,这是合情合理的,要知道这个gcc是交叉版本,也就是说它再编译出来的是目标体系平台的二进制文件,虽然可以 完成make bootstrap的第二步,但第三步是无法进行的,因为目标体系平台无法在当前平台上运行(make bootstrap,就是用第一遍编译的gcc来编译第二遍的gcc,再用第二遍的gcc编译第三遍的gcc,然后比较第二遍和第三遍的gcc,来确定编 译是否正确),因此这里只需要也只能编译一遍。
在LFS里,第一遍gcc只编译了c语言部分,是因为编译glibc只需要c语言就行了,你当然也可以编译其它的语言支持,但没有什么意义,因为第一遍的gcc会被第二遍替换掉,而且第一遍是依赖于主系统glibc的,所以在chroot后就不能用了。
在CLFS2.0里没有chroot的过程,所以无论是工具链中的第一遍还是第二遍编译,gcc所依赖的glibc都是主系统的glibc,但对后面的编译并不造成影响。
那为什么要编译两次呢?
CLFS2.0的工具链中第一遍只编译一个支持c的gcc,原因是要编译出一个支持交叉的c++,必须有一个编译好的用于目标体系平台的glibc,而不是只有glibc的头文件就可以的,好在编译glibc有c支持就够了,所以编译glibc也成了第一遍的gcc唯一的理由和作用。
在LFS中,工具链里第二遍的gcc是由第一遍的gcc来完成编译的。
在CLFS2.0中,我们知道第一遍编译的gcc就是交叉版本的gcc,如果由它来编译第二次的gcc,那么编译出来的就是目标体系平台的二进制文件, 是无法在当前体系平台上运行的,而我们还要用第二次编译的交叉版本的gcc来编译后面的内容,所以绝对不能用第一遍的gcc来编译第二遍的gcc。
那么是谁来编译第二次的gcc呢?
现在看这个问题应该是有点废话,目前就只剩下主系统的gcc了,也只有主系统的gcc现在能编译出在当前体系平台运行的交叉版本的gcc了。
现在明白了吧,工具链中gcc的第一次和第二次编译都是由主系统的gcc和binutils来完成的(之前没有提及binutils,只是为了理解方便,但实际上编译后是少不了链接过程的,这个过程是要binutils来完成的)。
到目前为止只有在编译glibc的时候用到了交叉版本的binutils,其它部分的链接都是由主系统的binutils来完成的。
现在对工具链中gcc的两次编译的目的和原因差不多搞清楚了,我们来看一下gcc两次编译参数的对比
第一次:
../gcc-4.1.1/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --disable-multilib \
--with-sysroot=${CLFS} --disable-nls --disable-shared \
--enable-languages=c
第二次:
../gcc-4.1.1/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --disable-multilib \
--with-sysroot=${CLFS} --disable-nls --enable-shared \
--enable-languages=c,c++ --enable-__cxa_atexit \
--enable-c99 --enable-long-long --enable-threads=posix
看来没什么特别需要说明的,非交叉编译用的参数基本上和LFS没什么太大区别,反正最重要的就是这个--with-sysroot,好了,三次--with-sysroot都出现了,虽然前面对这个参数也说明了一下,但不够详细,下面我就来说说对这个关键参数的理解。
我们在做LFS的过程中了解到gcc在编译过程中是默认从/usr/include中找头文件的来编译的,而binutils中的工具ld是从/lib /usr/lib、LD_LIBRARY_PATH、/etc/ld.so.conf等设置中所指定的路径搜索动态库或者静态库进行链接操作的,而要改变这种默认情况则可以通过参数指定、打补丁等方式来达到目的, 但是这样非常烦琐,所以就产生了LFS中工具链的方法,通过先做一个能自我编译的工具链,但这些工具链中的程序都是连接到类似/tools/lib这样的 目录下的库中,而且也是通过参数指定或者打补丁的方式来实现的,并不符合标准的/lib /usr/lib,所以后面在chroot后再用这个工具链来生成目标系统。
而现在我们用CLFS2.0的方法不需要再建立这个完整的工具链 了,只是建立一个交叉用的工具链,而这个工具链中的程序都是链接到/lib和/usr/lib里的库的,这样一个工具链是不能chroot的,但现在我们 要利用这个“不健全”的工具链来完成目标体系平台,就必须要用到更改默认路径的方式,这个方式就是--with-sysroot。
一个简单的理解就是,默认的路径实际上都是{--with-sysroot}/usr {--with-sysroot}/usr/lib {--with-sysroot}/usr/include这样的形式,只是在默认的情况下{--with-sysroot}表示的是空字符串,这样就变成了/usr /usr/lib /usr/include,而如果我们指定了--with-sysroot,比如--with-sysroot=/mnt/clfs,则默认路径就变成了/mnt/clfs/usr /mnt/clfs/usr/lib /mnt/clfs/usr/include,这样我们在编译时查找头文件以及在链接时查找动态或静态库就自动到--with-sysroot指定的路径下来完成。这个就是--with-sysroot参数的目的。
这里需要注意的是,--with-sysroot默认是只支持交叉编译的情况的,我们可以从代码中印证:
在gcc-core解压后的Makefile.in文件(你可以理解为是Makefile的一个模板文件)中有一段代码
# Default native SYSTEM_HEADER_DIR, to be overridden by targets.
NATIVE_SYSTEM_HEADER_DIR = /usr/include
# Default cross SYSTEM_HEADER_DIR, to be overridden by targets.
CROSS_SYSTEM_HEADER_DIR = @CROSS_SYSTEM_HEADER_DIR@
而对于CROSS_SYSTEM_HEADER_DIR的赋值在configure中有如下代码
CROSS_SYSTEM_HEADER_DIR='$(TARGET_SYSTEM_ROOT)$(NATIVE_SYSTEM_HEADER_DIR)'
而同在configure中对TARGET_SYSTEM_ROOT的赋值
TARGET_SYSTEM_ROOT=$with_sysroot
现在明白了吧,对于交叉方式,是默认支持--with-sysroot的,而普通的编译方式是不行的,但也不是说我们就没办法了,其实办法说起来也很简单,就是改代码、打补丁。
我们来看三次使用--with-sysroot的作用和目的
第一次,binutils下使用,目的是让binutils在查找库的时候到--with-sysroot指定的地方查,接着的glibc-headers和gcc都没有用到这个binutils,我们先放一下,看第二次使用;
第二次,第一次编译gcc下使用,目的是让这个gcc在编译的时候默认到{--with-sysroot}/usr/include下找头文件。
接着我们就开始编译目标体系平台下的glibc了,这个时候交叉版本的binutils和第一次编译的gcc都用上了,则我们也就清楚了,在编译这个 glibc的时候是到${CLFS}/usr/include里找头文件,到${CLFS}/lib等目录下链接库的,不过glibc是目标系统的第一个软件包,因此,它并不需要到${CLFS}/lib等目录下的库链接,但交叉版本的binutils还是顺利的完成了glibc编译目录下自己众多库文件的链接工作。这里binutils的--with-sysroot没有体现出来,但gcc的--with-sysroot已经发挥作用了。
第三次,实际上这次是为了替换掉第一次编译的gcc而“重复”的(原因前面已经讲过了),所以可以理解和第二次使用--with-sysroot是一样的。
到现在为止,gcc中的--with-sysroot已经体现出其作用了,但binutils什么时候才能发挥作用呢?
不要着急,很快就到了它的用武之地了。
现在我们就完成了交叉工具链了……(画外音:等等,还有两个包没说呢,怎么工具链就完成了?)
这里我们先把--with-sysroot的问题放下,现在出现了另外一个问题,在LFS过程中我们知道工具链除了binutils和gcc外还有很多大量的工具包,而在CLFS2.0中就只有file和groff两个包,是什么意思呢?
这里我们要全面了解工具链的作用以及这些工具包的作用,在LFS中的工具链的目的不光是为了能编译,而且是为了能够成为一个完整的自已自足的“系统”, 再进入(chroot)这个“系统”后,能够利用这种自已自足的能力创造新系统,而这个过程中,大量的工具包是少不了的,这也就是为什么LFS的方法中需 要在工具链阶段里加入大量的工具包。
而在CLFS2.0之所以没有加入大量的包是因为,CLFS2.0的方法里没有chroot这个环节,所 以使用主系统的工具就可以了,因此只需要gcc和binutils就可以完成任务了,而file和groff其实我觉得也是没有必要的,只要主系统中的 file和groff是符合要求的版本就可以了,如果没有符合的版本编译一个也是可以的,这里要注意的是file和groff是用主系统的gcc和binuitls完成编译链接的并依赖于主系统的glibc。