首先安装的第一个软件包是 Binutils ,这非常重要,因为 Glibc 和 GCC 会针对可用的连接器和汇编器进行多种测试,以决定是否打开某些特性。
Binutils 的文档推荐用一个新建的目录来编译它,而不是在源码目录中:
mkdir -v ../binutils-build cd ../binutils-build
![[Note]](http://lamp.linux.gov.cn/Linux/LFS-6.2/images/note.png)
注意
如果你想使用本书余下部份列出的 SBU 值,那么现在就要测量一下编译本软件包的时间。你可以用类似下面这样的 time 命令:time { ./configure ... && make && make install; } 。
为编译 Binutils 做准备:
../binutils-2.16.1/configure --prefix=/tools --disable-nls
配置选项的含义:
-
--prefix=/tools
-
这个参数告诉 configure 脚本,应该把 Binutils 软件包中的程序安装到 /tools 目录中。
--disable-nls
-
这个参数禁止了国际化(通常简称i18n),静态程序不需要国际化的特性。
接下来编译它:
make
现在编译完成了。通常我们会运行测试套件,但是目前测试套件(Tcl, Expect, DejaGNU)尚未安装。而且在这里运行测试也没什么用处,因为第一遍安装的程序很快就会被第二遍的程序所覆盖。
安装软件包:
make install
接下来为后面"调整工具链"步骤准备连接器:
make -C ld clean make -C ld LIB_PATH=/tools/lib cp -v ld/ld-new /tools/bin
make 参数的含义:
-
-C ld clean
-
告诉 make 程序删除所有 ld 子目录中编译生成的文件。
-C ld LIB_PATH=/tools/lib
-
这个选项重新编译 ld 子目录中的所有文件。在命令行中指定 Makefile 的 LIB_PATH 变量值,使它明确指向临时工具目录,以覆盖默认值。这个变量的值指定了连接器的默认库搜索路径,它在这一章的稍后部分会用到。
5.4.1. 安装 GCC
GCC 的安装指南推荐用一个新建的目录来编译它,而不是在源码目录中:
mkdir -v ../gcc-build cd ../gcc-build
为编译 GCC 做准备:
../gcc-4.0.3/configure --prefix=/tools \ --with-local-prefix=/tools --disable-nls --enable-shared \ --enable-languages=c
配置选项的含义:
-
--with-local-prefix=/tools
-
这个参数的目的是把 /usr/local/include 目录从 gcc 的 include 搜索路径里删除。这并不是绝对必要,但我们想尽量减小宿主系统的影响,所以才这样做。
--enable-shared
-
这个参数咋一看有点违反直觉。但只有加上它,才能编译出 libgcc_s.so.1 和 libgcc_eh.a 。Glibc(下一个软件包)的配置脚本只有在找到libgcc_eh.a 时才能确保产生正确的结果。
--enable-languages=c
-
只编译 GCC 软件包中的 C 编译器。我们在本章里不需要其它编译器。
接下来编译它:
make bootstrap
make 参数的含义:
-
bootstrap
-
使用这个参数的目的不仅仅是编译 GCC ,而是重复编译它几次。它用第一次编译生成的程序来第二次编译自己,然后又用第二次编译生成的程序来第三次编译自己,最后比较第二次和第三次编译的结果,以确保编译器能毫无差错的编译自身,这通常表明编译是正确的。
编译现在完成了,通常我们会在这里运行测试套件,但是正如前面说过的,测试套件目前尚未安装,而且在这里运行测试没什么用处,因为第一遍安装的程序很快就会被第二遍的程序所覆盖。
安装软件包:
make install
最后,我们创建一个必要的符号连接。因为许多程序和脚本试图运行 cc 而不是 gcc ,这样做是为了让程序能在多种 Unix 平台上运行,并保持一致性。并不是每个人都安装 GNU C 编译器。只运行 cc 而不是 gcc 可以把选择 C 编译器的自由留给系统管理员,我们这里将指向gcc :
ln -vs gcc /tools/bin/cc
5.5.1. 安装 Linux-Libc-Headers
多年来的公共惯例是使用 /usr/include 目录下"原始的"内核头文件(直接来自于内核源码包),但是近年来,内核开发者强烈要求不要这样做,因此诞生了 Linux-Libc-Headers 项目,其目标是维护一个API(应用程序编程接口)版本稳定的 Linux 头文件。关于内核头文件变迁的历史,这里有一篇《[八卦故事]内核头文件传奇》,可以当作课外读物 :)
安装这些头文件:
cp -Rv include/asm-i386 /tools/include/asm cp -Rv include/linux /tools/include
如果您的机器不是 i386 兼容架构的,请相应的调整第一条命令。
5.6.1. 安装 Glibc
Glibc 文档推荐在源码目录之外的一个专门的编译目录下进行编译:
mkdir -v ../glibc-build cd ../glibc-build
接下来为编译 Glibc 做准备:
../glibc-2.3.6/configure --prefix=/tools \ --disable-profile --enable-add-ons \ --enable-kernel=2.6.0 --with-binutils=/tools/bin \ --without-gd --with-headers=/tools/include \ --without-selinux
配置选项的含义:-
--disable-profile
-
它关掉了 profiling 信息相关的库文件编译。如果你打算做 profiling ,就省掉这个参数。
--enable-add-ons
-
这个指示 Glibc 使用附加的 NPTL 包作为线程库。
--enable-kernel=2.6.0
-
这个告诉 Glibc 编译支持 2.6.x 内核的库。
--with-binutils=/tools/bin
-
这个参数并不是必需的。但它们能保证在编译 Glibc 时不会用错 Binutils 程序。
--without-gd
-
这个参数保证不生成 memusagestat 程序,这个程序会顽固地连接到宿主系统的库文件(libgd, libpng, libz 等等)。
--with-headers=/tools/include
-
这个参数指示 Glibc 按照前面刚刚安装到 tools 目录中的内核头文件编译自己,从而精确的知道内核的特性以根据这些特性对自己进行最佳化编译。
--without-selinux
-
当从一个含有 SELinux 特性的宿主系统(如 Fedora Core 3)编译时,Glibc 将会将 SELinux 支持编译进来。由于 LFS 工具链并不包含 SELinux 支持,所以一个含有 SELinux 特性的 Glibc 将会导致许多操作失败。所以这里明确禁用它。
在这个阶段你可能会看到下面的警告:
configure: WARNING: *** These auxiliary programs are missing or *** incompatible versions: msgfmt *** some features will be disabled. *** Check the INSTALL file for required versions.
抱怨说缺少或有不兼容的 msgfmt 程序,这没有什么大问题,不过有时候可能会在运行测试套件的时候出问题。msgfmt 程序是宿主系统 Gettext 应当提供的一部分。如果担心宿主系统的 msgfmt 有兼容性问题,你可以升级宿主系统的 Gettext ,也可以忽略这个问题而不去管它。
编译软件包:
make
Glibc GCC Binutils 三者是整个工具链的核心,因此如何对其进行定制就显得很重要。这里有一篇文章《Glibc 安装指南》,可以在漫长的编译过程中作为参考资料读一读。
现在编译完成了。正如前面说过的,在这里运行测试没什么用处,但是如果你坚持要测试的话可以运行下列命令:
make check
关于测试失败重要性的讨论请参考这里:节 6.9, "Glibc-2.3.6."
在这一章里,Glibc 的测试套件高度依赖于宿主系统的工具和环境,尤其是内核。因为这个原因,有时错误很难避免,但是无需太在意。第六章里面的 Glibc 才是我们最后所使用的,那里的 Glibc 需要通过绝大多数测试。但要注意的是,即使在第六章里,有的失败还是会出现,比如 math 测试。
当遇到一个错误时,记录下来,再用 make check 继续。测试套件会从出错的地方继续进行。你也可以用 make -k check 来一次把测试做完。但如果你这样做的话,就要把屏幕输出记录到文件里( make -k check > ck_log ),以便最后检查到底出了多少错,哪些测试出错了。
在安装 Glibc 的过程中,它会警告缺少 /tools/etc/ld.so.conf 文件。其实这没什么关系,不过下面的命令能修正它:
mkdir -v /tools/etc touch /tools/etc/ld.so.conf
安装软件包:
make install
不同的国家和文化,使用不同的习俗来交流。这样的习俗很多,从比较简单的时间和日期格式,到非常复杂的语言发音。GNU 程序的"internationalization"(国际化,又称"i18n",18表示中间的18个字母)是以 locale 来实现的。
现在临时的C库已经装好,接下来本章中要编译的所有工具应该连接到这些库上。为了达到这个目标,需要调整连接器和编译器的 specs 文件。
在第一遍编译 Binutils 快结束时已经调整过的连接器,现在需要被重新命名以便可以被正确的找到和使用。首先备份原来的连接器,然后用调整过的连接器来替代,最后还要创建一个指向 /tools/$(gcc -dumpmachine)/bin 中连接器副本的连接。
mv -v /tools/bin/{ld,ld-old} mv -v /tools/$(gcc -dumpmachine)/bin/{ld,ld-old} mv -v /tools/bin/{ld-new,ld} ln -sv /tools/bin/ld /tools/$(gcc -dumpmachine)/bin/ld
从现在开始,所有程序都将连接到 /tools/lib 中的库文件。
下面要做的是修正 GCC 的"specs"文件,使它指向新的动态连接器。一个简单的 sed 命令就能做到:
SPECFILE=`dirname $(gcc -print-libgcc-file-name)`/specs && gcc -dumpspecs > $SPECFILE && sed 's@^/lib/ld-linux.so.2@/tools&@g' $SPECFILE > tempspecfile && mv -vf tempspecfile $SPECFILE && unset SPECFILE
推荐你拷贝和粘贴上面的命令,而不是手动输入。当然你也可以手动编辑 specs 文件,只要把所有的"/lib/ld-linux.so.2"都替换成"/tools/lib/ld-linux.so.2"就行了。
请用你的眼睛亲自仔细检查一下 specs 文件,以确保上述修改的的确确生效了。
重要
如果你的系统平台上,动态连接器的名字不是 ld-linux.so.2 ,你必须把上面命令里的"ld-linux.so.2"换成你的系统平台上动态连接器的名字。参见节 5.2, "工具链技术说明,"。
在编译过程中,GCC 会运行 fixincludes 脚本来扫描系统头文件目录,并找出需要修正的头文件(比如包含语法错误),然后把修正后的文件放到 GCC 专属头文件目录里。因此,它可能会找出宿主系统中需要修正的头文件,并将修正后的结果放到 GCC 专属头文件目录里。由于本章的剩余部分仅需要使用当前已经安装好的 GCC 和 Glibc 的头文件,所以任何"修正后的"头文件都可以被安全的删除。并且这样做也有助于避免宿主系统中的头文件"污染"编译环境。运行下面的命令删除 GCC 专属头文件目录中的头文件(由于命令较长,推荐你拷贝和粘贴命令,而不是手动输入):
GCC_INCLUDEDIR=`dirname $(gcc -print-libgcc-file-name)`/include && find ${GCC_INCLUDEDIR}/* -maxdepth 0 -xtype d -exec rm -rvf '{}' \; && rm -vf `grep -l "DO NOT EDIT THIS FILE" ${GCC_INCLUDEDIR}/*` && unset GCC_INCLUDEDIR
小心
现在,需要停下来确认新工具链的基本功能(编译和连接)是否按预期工作,运行下面的命令做一个简单的合理性检查:
echo 'main(){}' > dummy.c cc dummy.c readelf -l a.out | grep ': /tools'
如果一切正常,应该不会出错,而且最后一个命令的结果应当是:
[Requesting program interpreter: /tools/lib/ld-linux.so.2]
注意,/tools/lib 应该是动态连接器的前缀。
如果输出不是像上面那样或者根本没有输出,那么就有大问题了。返回并检查前面的操作,找出问题,并改正过来。在改正之前,不要继续后面的部份,因为这样做没有意义。首先,再次上述合理性检查,用 gcc 代替 cc ,如果工作正常,那么是因为 /tools/bin/cc 这个符号链接丢失了。回头看看节 5.4, "GCC-4.0.3 - 第一遍,",并建立符号链接。接下来,确保 PATH 正确。检查时,运行 echo $PATH 并检查 /tools/bin 是否在列表的最前面。如果 PATH 错误,可能是因为你没有以 lfs 用户登录,或者在节 4.4, "设置工作环境."部分出错了。另外一个原因可能是上面修正 specs 文件时出错,如果这样,重新修改 specs 文件,复制粘贴时要小心仔细。
在确定一切正常后,删除测试文件:
rm -v dummy.c a.out
注意
下一小节中编译 TCL 时也将有助于检查工具连是否正确。如果 TCL 编译失败则表示之前安装的 Binutils 、GCC 或 Glibc 有问题,而不是 TCL 自身有问题。
5.8.1. 安装 Tcl
这个软件包和接下来安装的两个软件包(Expect 和 DejaGNU)是为了给运行 GCC 和 Binutils 的测试程序提供支持。仅为了测试而安装三个软件包,看起来似乎有点多余,但是看到那些最重要的工具正常工作,心理上会比较踏实。即使没有运行本章中测试程序(不是必需的),运行第六章中的测试时也需要这些软件包。
为编译 Tcl 做准备:
cd unix ./configure --prefix=/tools
编译软件包:
make
要测试结果,请运行:TZ=UTC make test 。已知 Tcl 的测试程序会在某些还未完全了解的宿主系统下出现测试失败的情况,因此,如果这里的测试失败了,不要紧,因为这并不关键。TZ=UTC 参数将时区设置为协调世界时(UTC),也就是格林尼治时间(GMT),但只是在运行测试程序的时候才这样设置,这将确保时钟测试正确。关于 TZ 环境变量的详细资料位于第七章。
安装软件包:
make install
安装 Tcl 头文件,下一个包(Expect)要使用 Tcl 的头文件。
make install-private-headers
现在创建一个必需的符号链接:
ln -sv tclsh8.4 /tools/bin/tclsh
安装的程序: tclsh(→tclsh8.4), tclsh8.4安装的库: libtcl8.4.so简要描述
tclsh8.4 Tcl 命令 shell
tclsh 指向 tclsh8.4 的链接
libtcl8.4.so Tcl 库文件
5.9. Expect-5.43.0
Expect 软件包包含一个通过执行脚本对话框与其它交互式程序通信的工具。
预计编译时间: 0.1 SBU所需磁盘空间: 4 MB5.9.1. 安装 Expect
先修正一个可能导致 GCC 测试程序假失败的 bug :
patch -Np1 -i ../expect-5.43.0-spawn-1.patch
为编译 Expect 做准备:
./configure --prefix=/tools --with-tcl=/tools/lib \ --with-tclinclude=/tools/include --with-x=no
配置选项的含义:
-
--with-tcl=/tools/lib
-
这个选项确保配置脚本找到的是安装在临时工具目录下的 Tcl ,而不是宿主系统里的。
--with-tclinclude=/tools/include
-
这个选项告诉 Expect 到哪里寻找 Tcl 的源代码目录和头文件。使用这个选项可以避免 configure 脚本因为找不到 Tcl 的源代码目录而导致的失败。
--with-x=no
-
这个选项告诉 configure 脚本不要搜索 Tk(Tcl的图形界面组件)或者 X Window 系统的库,这两者都可能位于宿主系统上。
编译软件包:
make
要测试结果,请运行:make test 。请注意,已知 Expect 的测试程序会在某些不在我们控制范围内的宿主系统下出现测试失败。因此,如果您运行这里的测试程序失败了也没关系,因为这并不关键。
安装软件包:
make SCRIPTS="" install
make 参数的含义:
-
SCRIPTS=""
-
这个选项防止安装 Expect 所补充的一些并不需要的脚本。
安装的程序: expect安装的库: libexpect-5.43.a
-