从头构建Linux系统之五 —— 构建一个临时系统

本章将会详述如何构建一个最小化的Linux系统。这个系统只会包含足够用于构建最终LFS系统(第6章的内容)的工具,并且它能提供的工作环境比最小化环境要便利得多。

构建这个最小化系统需要两个步骤。第一步,编译一个新的、不依赖于宿主机的工具链(编译器、汇编器、连接器和程序库,以及一些实用工具)。第二步,使用上述的工具链编译其他的重要工具。

本章编译生成的文件将会安装在$LFS/tools目录之中,这些文件和下一章安装的文件,以及宿主机系统目录都是独立存放的。因为此处编译的源码包都是临时的,所以我们不允许这些文件污染即将构建的LFS系统。

一、工具链的技术说明

本章会介绍一些隐藏在总体构建方法之后的基本原理和技术细节。没有必要立即理解本章讲述的所有内容,因为在实际操作构建之后,就会对大多数内容有较为清晰的理解。在构建LFS系统的过程中,可以随时参考本章的内容。

本章的总体目标是创建一个临时区域,该区域包含一套构建工具,这些工具独立于宿主机系统。本系列教程的剩余章节(第6~9章)中的绝大多数命令都会在chroot环境之内运行,这样才能确保目标LFS系统的构建环境是干净和可靠的。对于初学者来说,本系列教程的构建过程不仅能够最小化系统风险,同时还具有较高的教育价值。

本章介绍的构建方法的一些关键性的技术要点,如下所示:

  • 通过修改LFS_TGT环境变量(格式为{UNAME_MACHINE}-${VENDOR}-linux-gnu)中的“vendor”字段,就可以对工作平台的名称进行微调,这样便可以确保首次编译Binutils和GCC能够生成兼容的交叉链接器和交叉编译器。交叉链接器和交叉编译器将会生成兼容于当前硬件的二进制文件,而不会生成兼容于另一种架构的二进制文件。上一章创建.bashrc文件时,已经指定了LFS_TGT环境变量。

  • 临时程序库是交叉编译的。由于交叉编译器的自身特性,它不能依赖于宿主机系统的任何东西,通过减少将宿主机的头文件或程序库合并入新工具的几率,这种方式能够避免污染目标系统的潜在风险。交叉编译器还可以在64位的硬件平台上构建32位和64位的程序库。

  • 需要小心谨慎地操作GCC的源码,告知编译器应当使用何种目标动态链接器。

构建临时工具链需要经过以下几个步骤:

  • Step-1:Binutils-2.27(第一阶段)

    首先,应当安装Binutils源码包,因为GCC和Glibc的configure脚本都会针对汇编器和链接器执行各种功能测试,这样才能确定需要启用或禁用的功能特性。实际上,绝大多数软件包在编译之前都需要执行各自的configure脚本。未能正确配置的GCC或Glibc可能会导致临时工具链产生不易察觉的错误,这些错误可能直到整个LFS系统接近构建完成的时候才会被发现。在构建LFS系统时,编译安装每个源码包之后,最好能运行相应的测试套件,从某些测试项的错误信息之中通常能够看出临时工具链可能存在的错误。

    Binutils会将自身的汇编器和链接器分别安装至两个地方:/tools/bin目录和/tools/$LFS_TGT/bin目录。存放在一个地方的工具是另一个地方的工具的硬链接。链接器的一个很重要的特性便是程序库的搜索顺序。通过ld命令的--verbose选项,便能获取搜索程序库的详细信息。例如,ld --verbose | grep SEARCH命令会输出当前的搜索路径和搜索顺序。当编译一个演示程序,并且让链接器使用--verbose选项时,便会显示ld命令将会链接哪些文件。例如,gcc dummy.c -Wl,--verbose 2>&1 | grep succeeded命令将会显示在链接期间,所有成功打开的文件。

  • Step-2:GCC-6.2.0(第一阶段)

    接下来,应当安装GCC源码包。在运行configure脚本期间可能会看到如下信息:

    1. checking what assembler to use... /tools/i686-lfs-linux-gnu/bin/as
    2. checking what linker to use... /tools/i686-lfs-linux-gnu/bin/ld

    这就是产生上述信息的重要原因。上述信息表明GCC的configure脚本并没有在PATH环境变量指定的目录之中搜索需要使用的工具。但是,在gcc自身的实际操作期间,没有必要使用相同的搜索路径。若要找出gcc将会使用何种标准链接器,请运行gcc -print-prog-name=ld命令。

    如果要从gcc获取详细信息,那么可以在编译一个演示程序时,使用gcc的-v命令行选项。例如,gcc -v dummy.c命令将会显示一些详细信息,包括预处理器、编译和汇编阶段,以及gcc头文件的搜索路径和搜索顺序。

  • Step-3:Linux-4.7.2 API Headers

    接下来,应当安装Linux API headers源码包。这些API头文件使得标准C的程序库(Glibc)能够使用Linux内核提供的功能特性。

  • Step-4:Glibc-2.24

    接下来,应当安装Glibc源码包。编译Glibc的最重要的因素是编译器、二进制工具和内核头文件。通常,编译器并不是一个麻烦的问题,因为Glibc总是会使用configure脚本的--host参数所指定的编译器;例如,本章使用的编译器便是x86_64-lfs-linux-gnu-gcc。二进制工具和内核头文件的问题可能会有些复杂。因此,不要冒任何风险,请使用现有的配置开关,强制选择正确的选项。运行configure脚本之后,请检查glibc-build目录中的config.make文件的内容,这个文件包含所有重要的详细信息。注意,可以通过CC="x86_64-lfs-linux-gnu-gcc"选项来控制使用哪些二进制工具,还可以通过-nostdinc-isystem选项来控制编译器的头文件搜索路径。这些配置项突出显示Glibc源码包的一个重要方面 —— Glibc的编译机制使其非常独立,通常不会依赖于工具链的默认配置。

  • Step-5:Binutils-2.27(第二阶段)

    在编译Binutils的第二阶段期间,可以使用--with-lib-path配置选项来控制ld的程序库搜索路径。

  • Step-6:GCC-6.2.0(第二阶段)

    在编译GCC的第二阶段期间,还需要修改GCC的源码文件,告诉GCC使用新建的动态链接器。如果不这样做的话,就会导致GCC程序将来自于宿主机系统的/lib/lib64目录的动态链接器的名称内嵌至其自身之中,这样便不能完全脱离宿主机系统。从这一点来看,核心工具链应当是自包含和自托管的。本章的其余源码包都是通过/tools目录之中的新建的Glibc编译的。

    下一章进入chroot环境之后,安装的第一个主要的源码包便是Glibc,因为Glibc具有上述的自给自足的特性。一旦将Glibc安装至/usr目录,本系列教程将会快速地转换工具链的默认配置,然后继续编译目标LFS系统的剩余部分。

二、配置LFS用户环境

第2章搭建宿主机系统时,使用root用户权限编译安装了GCC、Gawk和Xz依赖包,这三个依赖包的可执行文件安装在/usr/local/bin目录之中。第4章进行最终准备时,创建的.bashrc文件之中的PATH环境变量并没有包含/usr/local/bin目录,这就导致使用上述三个依赖包会出现问题,要么版本不正确,要么找不到可执行文件。

以lfs用户权限,编辑.bashrc文件:

  1. vi ~/.bashrc

将第6行的PATH环境变量修改为:

  1. PATH=/tools/bin:/usr/local/bin:/bin:/usr/bin

保存退出之后,再以lfs用户权限,使得.bashrc文件之中的环境变量生效:

  1. source ~/.bashrc

接下来,检查LFS用户环境是否满足需求。以root用户权限,将第2章创建的version-check.sh脚本拷贝至lfs用户的家目录之中,然后修改拷贝脚本的所有者:

  1. cp -v /root/Downloads/version-check.sh /home/lfs/
  2. chown -v lfs:lfs /home/lfs/version-check.sh

以lfs用户权限,运行version-check.sh脚本:

  1. ~/version-check.sh

version-check.sh脚本的输出信息如下图所示,则表示lfs用户环境的依赖关系满足需求:

检查LFS用户环境的版本需求

三、构建临时工具链

现在,构建临时工具链的时候到了!请以lfs用户权限,按照顺序执行本节描述的所有操作。

1. Binutils-2.27(第一阶段)

Binutils包含一个链接器、一个汇编器,以及用于处理目标文件的其他工具。

编译大约需要耗费1 SBU的时间,磁盘大约需要占用519 MB的空间。

Binutils是构建临时工具链时编译的第一个源码包,这是非常重要的。因为,Glibc和GCC都会针对可用的链接器和汇编器执行各种功能测试,这样才能确定能够启用或禁用的功能特性。此处,将会交叉编译安装Binutils源码包。

解压Binutils源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvjf binutils-2.27.tar.bz2
  3. cd binutils-2.27

Binutils的官方文档建议在一个专用目录之中进行编译,因此新建和进入专用目录:

  1. mkdir -v build
  2. cd build

此处,还需要获取SBU的时间值,这样才能估算出编译安装LFS各个源码包需要耗费的时间。SBU等于Binutils的配置编译选项、编译和安装的耗时之和。运行以下命令,配置Binutils的编译选项,然后编译和安装,并且获取SBU的时间值:

  1. time { \
  2. ../configure --prefix=/tools \
  3. --with-sysroot=$LFS \
  4. --with-lib-path=/tools/lib \
  5. --target=$LFS_TGT \
  6. --disable-nls \
  7. --disable-werror && \
  8. make && \
  9. mkdir -v /tools/lib && ln -sv lib /tools/lib64 && \
  10. make install; \
  11. }

编译安装完整之后,得到的SBU时间值如下图所示:

SBU时间值

由上图可知,在本系列教程的编译环境中,SBU的时间值约为213秒。

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –prefix=/tools
    这个选项会告诉configure脚本将Binutils程序安装至/tools目录之中。

  • –with-sysroot=$LFS
    对于交叉编译来说,这个选项会告诉构建系统必要时可以在$LFS指定的目录中查找目标系统的程序库。

  • –with-lib-path=/tools/lib
    这个选项指定应当给链接器配置使用的程序库路径。

  • –target=$LFS_TGT
    因为LFS_TGT环境变量中的机器描述信息和config.guess脚本的返回信息具有细微的不同之处,所以这个选项会告诉configure脚本,需要调整Binutils的构建系统,使其能够构建一个交叉链接器。

  • –disable-nls
    这个选项会禁用国际化功能,因为临时工具不需要使用i18n功能。

  • –disable-werror
    这个选项会使得编译器在遇到来自于宿主机编译器的警告信息时,不会停止编译过程。

最后,回退至$LFS/sources目录,删除Binutils的源码目录:

  1. cd $LFS/sources
  2. rm -rfv binutils-2.27

2. GCC-6.2.0(第一阶段)

GCC包含GNU编译器集合,包括C和C++的编译器。

编译大约需要耗费8.3 SBU的时间,磁盘大约需要占用2.5 GB的空间。

解压GCC源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvjf gcc-6.2.0.tar.bz2
  3. cd gcc-6.2.0

此时,GCC需要GMP、MPFR和MPC的源码包。由于宿主机系统不一定包含这三个开源软件,因此它们将会和GCC一同编译安装。将上述的三个源码包分别解压至GCC的源码目录,然后重命名解压得到的目录,这样便使得GCC的编译程序能够自动使用它们:

  1. tar -xf ../mpfr-3.1.4.tar.xz
  2. mv -v mpfr-3.1.4 mpfr
  3. tar -xf ../gmp-6.1.1.tar.xz
  4. mv -v gmp-6.1.1 gmp
  5. tar -xf ../mpc-1.0.3.tar.gz
  6. mv -v mpc-1.0.3 mpc

以下命令会修改GCC使用的默认动态链接器的位置,指定为安装在/tools目录中的动态链接器。这些命令还会从GCC的头文件搜索路径之中移除/usr/include目录。运行以下命令:

  1. for file in \
  2. $(find gcc/config -name linux64.h -o -name linux.h -o -name sysv4.h)
  3. do
  4. cp -uv $file{,.orig}
  5. sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \
  6. -e 's@/usr@/tools@g' $file.orig > $file
  7. echo '
  8. #undef STANDARD_STARTFILE_PREFIX_1
  9. #undef STANDARD_STARTFILE_PREFIX_2
  10. #define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/"
  11. #define STANDARD_STARTFILE_PREFIX_2 ""' >> $file
  12. touch $file.orig
  13. done

这些命令看起来比较复杂,需要逐行分析理解:

  • 第1行~第2行
    gcc/config目录之中,找出所有名为linux.hlinux64.hsysv4.h的文件。

  • 第4行
    备份每一个找到的文件,备份文件名和原文件名相同,但后缀名为“.orig”。

  • 第5行~第6行
    第一个sed表达式会在上述文件的每个“/lib/ld”、“/lib64/ld”或“/lib32/ld”之前,添加“/tools”前缀;而第二个sed表达式会将上述文件的每个“/usr”替换为“/tools”。

  • 第7行~第11行
    在上述文件的末尾,添加自定义的define语句,用于修改默认startfile的前缀。注意,“/tools/lib/”结尾的“/”字符是必需的。

  • 第12行
    使用touch命令更新备份文件的时间戳。

GCC的官方文档建议在一个专用目录之中进行编译,因此新建和进入专用目录:

  1. mkdir -v build
  2. cd build

配置GCC的编译选项:

  1. ../configure \
  2. --target=$LFS_TGT \
  3. --prefix=/tools \
  4. --with-glibc-version=2.11 \
  5. --with-sysroot=$LFS \
  6. --with-newlib \
  7. --without-headers \
  8. --with-local-prefix=/tools \
  9. --with-native-system-header-dir=/tools/include \
  10. --disable-nls \
  11. --disable-shared \
  12. --disable-multilib \
  13. --disable-decimal-float \
  14. --disable-threads \
  15. --disable-libatomic \
  16. --disable-libgomp \
  17. --disable-libmpx \
  18. --disable-libquadmath \
  19. --disable-libssp \
  20. --disable-libvtv \
  21. --disable-libstdcxx \
  22. --enable-languages=c,c++

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –with-newlib
    由于目前还没有可以正常使用的C程序库,因此在编译libgcc时,这个选项可以确保正确定义inhibit_libc常量。这个选项能够防止编译任何需要libc支持的源码。

  • –without-headers
    当创建一个完整的交叉编译器时,GCC需要兼容于目标系统的标准头文件。但是,此处不需要使用这些头文件。这个选项可以防止GCC查找这些头文件。

  • –with-local-prefix=/tools
    本地前缀就是GCC搜索本地安装的头文件的位置。默认值为/usr/local目录。将这个选项设置为/tools目录,能够从GCC的搜索路径之中,将宿主机的/usr/local目录排除在外。

  • –with-native-system-header-dir=/tools/include
    在默认情况下,GCC会在/usr/include目录中搜索系统头文件。这个选项连同sysroot选项,它们会将上述目录转换为$LFS/usr/include目录。但是,后续两节安装的头文件将会存放在$LFS/tools/include目录之中。这个选项可以确保gcc能够正确地找到这些头文件。在编译安装GCC的第二阶段,还会使用这个相同的选项,确保GCC不会查找宿主机系统的任何头文件。

  • –disable-shared
    这个选项会强迫GCC静态链接至自身的内部程序库,避免宿主机系统可能会造成的问题。

  • –disable-decimal-float, –disable-threads, –disable-libatomic, –disablelibgomp, –disable-libmpx, –disable-libquadmath, –disable-libssp, –disablelibvtv, –disable-libstdcxx
    这些选项会分别禁用十进制浮点扩展功能、多线程功能、libatomic、libgomp、libmpx、libquadmath、libssp、libvtv和C++标准程序库。当编译一个交叉编译器时,这些功能特性将会出错;当交叉编译临时的libc时,也没有必要使用这些功能特性。

  • –disable-multilib
    在x86_64平台上,LFS还不能支持multilib配置。

  • –enable-languages=c,c++
    这个选项可以确保仅仅构建C和C++的编译器。目前只会用到这两种编程语言。

编译GCC:

  1. make

安装GCC:

  1. make install

最后,回退至$LFS/sources目录,删除GCC的源码目录:

  1. cd $LFS/sources
  2. rm -rfv gcc-6.2.0

3. Linux-4.7.2 API Headers

Linux的API头文件(在linux-4.7.2.tar.xz文件之中)会对外公开Glibc使用的内核API接口。

编译大约需要耗费0.1 SBU的时间,磁盘大约需要占用750 MB的空间。

Linux内核需要对外公开应用程序编程接口(API),系统的C程序库(LFS的Glibc)会使用这些API接口。通过从Linux的内核源码的压缩包中提取各种C语言的头文件,便能对外公开Linux的API接口。

解压Linux内核源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf linux-4.7.2.tar.xz
  3. cd linux-4.7.2

确保没有陈旧的文件嵌入至源码包之中:

  1. make mrproper

此时,从源码目录抽取用户可见的内核头文件。它们会被放置在一个临时的本地目录之中,然后被拷贝至所需的位置,因为抽取过程会删除目标目录之中的任何已有的文件。运行以下命令:

  1. make INSTALL_HDR_PATH=dest headers_install
  2. cp -rv dest/include/* /tools/include

最后,回退至$LFS/sources目录,删除Linux内核的源码目录:

  1. cd $LFS/sources
  2. rm -rfv linux-4.7.2

4. Glibc-2.24

Glibc包含主要的C程序库。这个程序库提供了一些基本的例程,可用于分配内存、搜索目录、打开和关闭文件、读取和写入文件、操作字符串、模式匹配、算术运算等等。

编译大约需要耗费4.0 SBU的时间,磁盘大约需要占用715 MB的空间。

解压Glibc源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf glibc-2.24.tar.xz
  3. cd glibc-2.24

Glibc的官方文档建议在一个专用目录之中进行编译,因此新建和进入专用目录:

  1. mkdir -v build
  2. cd build

接下来,配置Glibc的编译选项:

  1. ../configure \
  2. --prefix=/tools \
  3. --host=$LFS_TGT \
  4. --build=$(../scripts/config.guess) \
  5. --enable-kernel=2.6.32 \
  6. --with-headers=/tools/include \
  7. libc_cv_forced_unwind=yes \
  8. libc_cv_c_cleanup=yes

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –host=$LFS_TGT, –build=$(../scripts/config.guess)
    组合使用这两个选项,使得Glibc的构建系统将其自身配置为交叉编译的,使用/tools目录之中的交叉链接器和交叉编译器。

  • –enable-kernel=2.6.32
    这个选项会告诉Glibc编译的程序库需要支持2.6.32版本,以及更新版本的Linux内核。Glibc不支持较老版本的Linux内核。

  • –with-headers=/tools/include
    这个选项会告诉Glibc,在编译其自身源码时,使用最近安装至/tools目录的头文件。因此,Glibc会准确地知道Linux内核具有哪些功能特性,然后可以相应地对其自身进行优化。

  • libc_cv_forced_unwind=yes
    当编译安装Binutils的第一阶段时,安装的链接器是交叉编译的,直到Glibc安装完成之后才能使用。这就意味着,对force-unwind支持的配置测试项将会失败,因为它依赖于一个可以正常工作的链接器。使用libc_cv_forced_unwind=yes变量就是为了通知configure脚本,force-unwind支持是可用的,不用运行相应的配置测试项。

  • libc_cv_c_cleanup=yes
    类似地,使用libc_cv_c_cleanup=yes变量就是为了通知configure脚本,C语言的清理处理支持是可用的,不用运行相应的配置测试项。

在配置编译选项期间,可能会出现以下的警告信息:

Glibc配置脚本的警告信息

通常,msgfmt程序缺失或不兼容是没有危害的。msgfmt程序是Gettext的一个组成部分,宿主机系统应当会安装这个组件。

编译Glibc:

  1. make

安装Glibc:

  1. make install

此时,必须停止继续操作,需要确保新建工具链的基本功能(编译和链接)能够正常工作:

  1. echo 'int main(){}' > dummy.c
  2. $LFS_TGT-gcc dummy.c
  3. readelf -l a.out | grep ': /tools'

如果上述最后一条命令的输出信息如下图所示,那么就表示新建工具链能够正常工作:

检查新建工具链的基本功能

清理测试文件:

  1. rm -v dummy.c a.out

最后,回退至$LFS/sources目录,删除Glibc的源码目录:

  1. cd $LFS/sources
  2. rm -rfv glibc-2.24

5. Libstdc++-6.2.0

Libstdc++是标准的C++程序库。g++编译器通过这个程序库才能保证工作正常。

编译大约需要耗费0.4 SBU的时间,磁盘大约需要占用896 MB的空间。

Libstdc++是GCC源码的一部分,首先应当解压GCC的源码包,然后进入gcc-6.2.0目录:

  1. cd $LFS/sources
  2. tar -xvjf gcc-6.2.0.tar.bz2
  3. cd gcc-6.2.0

创建一个专用于编译Libstdc++的目录,然后进入这个目录:

  1. mkdir -v build
  2. cd build

配置Libstdc++的编译选项:

  1. ../libstdc++-v3/configure \
  2. --host=$LFS_TGT \
  3. --prefix=/tools \
  4. --disable-multilib \
  5. --disable-nls \
  6. --disable-libstdcxx-threads \
  7. --disable-libstdcxx-pch \
  8. --with-gxx-include-dir=/tools/$LFS_TGT/include/c++/6.2.0

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –host=…
    这个选项指定使用刚刚构建的交叉编译器,而不是存放于/usr/bin目录的编译器。

  • –disable-libstdcxx-threads
    因为尚未构建C语言的线程库,所以C++也不能构建线程库。

  • –disable-libstdcxx-pch
    这个选项会防止安装预编译的头文件,当前步骤不需要使用这些头文件。

  • –with-gxx-include-dir=/tools/$LFS_TGT/include/c++/6.2.0
    这个选项指定标准头文件的存放位置,C++编译器会在这个目录之中搜索头文件。在平常的编译过程之中,这些信息会被自动传递给Libstdc++的configure脚本选项。但是在此处,必须显式地给定这些信息。

编译Libstdc++:

  1. make

安装Libstdc++:

  1. make install

最后,回退至$LFS/sources目录,删除GCC的源码目录:

  1. cd $LFS/sources
  2. rm -rfv gcc-6.2.0

6. Binutils-2.27(第二阶段)

Binutils包含一个链接器、一个汇编器,以及用于处理目标文件的其他工具。

编译大约需要耗费1.1 SBU的时间,磁盘大约需要占用533 MB的空间。

解压Binutils源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvjf binutils-2.27.tar.bz2
  3. cd binutils-2.27

Binutils的官方文档建议在一个专用目录之中进行编译,因此新建和进入专用目录:

  1. mkdir -v build
  2. cd build

配置Binutils的编译选项:

  1. CC=$LFS_TGT-gcc \
  2. AR=$LFS_TGT-ar \
  3. RANLIB=$LFS_TGT-ranlib \
  4. ../configure \
  5. --prefix=/tools \
  6. --disable-nls \
  7. --disable-werror \
  8. --with-lib-path=/tools/lib \
  9. --with-sysroot

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • CC=$LFS_TGT-gcc AR=$LFS_TGT-ar RANLIB=$LFS_TGT-ranlib
    因为此处实际是对Binutils进行本地编译,所以设置这些环境变量可以确保构建系统使用交叉编译器,以及相关的工具,而不会使用宿主机系统的编译器和工具。

  • –with-lib-path=/tools/lib
    在编译Binutils期间,这个选项可以指定程序库的搜索路径,会将/tools/lib目录传递给链接器。这个选项可以防止链接器在宿主机系统之中搜索程序库目录。

  • –with-sysroot
    这个选项使得链接器能够找到一些共享对象,在链接器的命令行中显式包含的共享对象将会使用这些共享对象。如果没有这个选项,那么在某些宿主机上可能就无法成功编译某些源码包。

编译Binutils:

  1. make

安装Binutils:

  1. make install

现在,需要配置链接器,为下一章的重新调整工具链的步骤做好准备:

  1. make -C ld clean
  2. make -C ld LIB_PATH=/usr/lib:/lib
  3. cp -v ld/ld-new /tools/bin

make命令参数的意义,如下所示:

  • -C ld clean
    这个选项告诉make程序删除ld子目录之中所有已编译的文件。

  • -C ld LIB_PATH=/usr/lib:/lib
    这个选项会重新构建ld子目录之中的所有东西。通过命令行指定的LIB_PATH变量会覆盖临时工具的默认值,使其指向正确的最终路径。这个变量的取值会指定链接器默认的程序库搜索路径。下一章会用到此处的准备工作。

最后,回退至$LFS/sources目录,删除Binutils的源码目录:

  1. cd $LFS/sources
  2. rm -rfv binutils-2.27

7. GCC-6.2.0(第二阶段)

GCC包含GNU的编译器集合,包括C和C++的编译器。

编译大约需要耗费11 SBU的时间,磁盘大约需要占用2.6 GB的空间。

解压GCC源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvjf gcc-6.2.0.tar.bz2
  3. cd gcc-6.2.0

在编译GCC的第一阶段时,安装了几个内部的系统头文件。通常,它们其中的一个limits.h头文件将会依次包含相应系统的limits.h头文件,在本章之中,包含/tools/include/limits.h文件。但是,在编译GCC的第一阶段时,/tools/include/limits.h并不存在,因此GCC安装的内部头文件是不完整的、自包含的文件,它没有包含系统头文件的扩展功能特性。对于构建临时的libc来说,上述的头文件已经足够了,但是编译GCC的第二阶段需要完整的内部头文件。运行以下命令,创建一个完整版本的内部头文件:

  1. cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
  2. `dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include-fixed/limits.h

再一次修改GCC需要使用的默认动态链接器的位置,使用安装于/tools目录的链接器:

  1. for file in \
  2. $(find gcc/config -name linux64.h -o -name linux.h -o -name sysv4.h)
  3. do
  4. cp -uv $file{,.orig}
  5. sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \
  6. -e 's@/usr@/tools@g' $file.orig > $file
  7. echo '
  8. #undef STANDARD_STARTFILE_PREFIX_1
  9. #undef STANDARD_STARTFILE_PREFIX_2
  10. #define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/"
  11. #define STANDARD_STARTFILE_PREFIX_2 ""' >> $file
  12. touch $file.orig
  13. done

GCC需要GMP、MPFR和MPC的源码包,将这三个源码包解压至GCC的源码目录之中,然后重命名解压得到的目录名:

  1. tar -xf ../mpfr-3.1.4.tar.xz
  2. mv -v mpfr-3.1.4 mpfr
  3. tar -xf ../gmp-6.1.1.tar.xz
  4. mv -v gmp-6.1.1 gmp
  5. tar -xf ../mpc-1.0.3.tar.gz
  6. mv -v mpc-1.0.3 mpc

GCC的官方文档建议在一个专用目录之中进行编译,因此新建和进入专用目录:

  1. mkdir -v build
  2. cd build

现在,配置GCC的编译选项:

  1. CC=$LFS_TGT-gcc \
  2. CXX=$LFS_TGT-g++ \
  3. AR=$LFS_TGT-ar \
  4. RANLIB=$LFS_TGT-ranlib \
  5. ../configure \
  6. --prefix=/tools \
  7. --with-local-prefix=/tools \
  8. --with-native-system-header-dir=/tools/include \
  9. --enable-languages=c,c++ \
  10. --disable-libstdcxx-pch \
  11. --disable-multilib \
  12. --disable-bootstrap \
  13. --disable-libgomp

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –enable-languages=c,c++
    这个选项可以确保C和C++的编译器都会被构建。

  • –disable-libstdcxx-pch
    这个选项可以防止构建Libstdc++的预编译头文件(PCH)。这些头文件会占用大量的磁盘空间,但是此处却不需要使用。

  • –disable-bootstrap
    对于本地编译GCC来说,默认需要进行“自举”编译。这不仅仅会编译GCC,而且还会编译若干次。它会使用第一轮编译生成的程序来第二次编译其自身,然后再编译第三次。第二次和第三次编译主要用于确认是否能够完美地重现第一次的编译过程。如果能够重现,那么就意味着编译正确。但是,LFS的编译方法应当提供一个可靠的编译器,不需要每次都进行自举编译。

编译GCC:

  1. make

安装GCC:

  1. make install

接下来,需要创建一个符号连接。很多程序和脚本都会运行cc命令,而不是gcc命令,这样可以保证程序的通用性,因此这些程序能够在所有类型的Unix系统上使用,而这些系统不可能总是会安装gcc编译器。运行cc命令使得系统管理员能够自由地选用不同的C编译器。运行以下命令:

  1. ln -sv gcc /tools/bin/cc

此时,必须停止继续操作,确保新建工具链的基本功能(编译和链接)能够正常工作:

  1. echo 'int main(){}' > dummy.c
  2. cc dummy.c
  3. readelf -l a.out | grep ': /tools'

如果上述最后一条命令的输出信息如下图所示,那么就表示新建工具链能够正常工作:

检查新建工具链的基本功能

清理测试文件:

  1. rm -v dummy.c a.out

最后,回退至$LFS/sources目录,删除GCC的源码目录:

  1. cd $LFS/sources
  2. rm -rfv gcc-6.2.0

四、构建其他的重要工具

经过不懈的努力,临时工具链终于构建完成了!本节将会使用这个临时工具链构建其他的重要工具,请以lfs用户权限,按照顺序执行本节描述的所有操作。

1. Tcl-core-8.6.6

TCL是Tool Command Language的缩写,它是一种工具命令语言。

编译大约需要耗费0.4 SBU的时间,磁盘大约需要占用40 MB的空间。

如果需要运行GCC、Binutils等其他源码包的测试套件,那么就需要编译安装TCL、Expect、DejaGNU和Check源码包。为了运行测试套件而安装这四个源码包,看起来比较浪费,但实际上非常值得,因为这样才能确保大多数重要的工具能够正常工作。虽然本章安装的源码包都不需要运行测试套件,但是下一章安装的源码包都必须运行各自的测试套件。

注意,此处编译安装的TCL源码包只是一个最小化的版本,只能用于运行LFS各个组件的测试套件。解压TCL源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvzf tcl-core8.6.6-src.tar.gz
  3. cd tcl8.6.6

配置TCL的编译选项:

  1. cd unix
  2. ./configure --prefix=/tools

编译TCL:

  1. make

安装TCL:

  1. make install

确保已安装的程序库具有可写权限,稍后可以删除其中的调试符号:

  1. chmod -v u+w /tools/lib/libtcl8.6.so

安装TCL的头文件,稍后编译安装Expect源码包需要使用这些头文件:

  1. make install-private-headers

创建必要的符号链接:

  1. ln -sv tclsh8.6 /tools/bin/tclsh

最后,回退至$LFS/sources目录,删除TCL的源码目录:

  1. cd $LFS/sources
  2. rm -rfv tcl8.6.6

2. Expect-5.45

Expect能够以一种脚本化对话的方式,实现和其他程序之间的交互功能。

编译大约需要耗费0.1 SBU的时间,磁盘大约需要占用4.3 MB的空间。

解压Expect源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvzf expect5.45.tar.gz
  3. cd expect5.45

强迫Expect的configure脚本使用/bin/stty,而不是在宿主机系统找到的/usr/local/bin/stty。这样才能保证工具链在最后一次构建时,测试套件工具能够正常工作:

  1. cp -v configure{,.orig}
  2. sed 's:/usr/local/bin:/bin:' configure.orig > configure

配置Expect的编译选项:

  1. ./configure --prefix=/tools \
  2. --with-tcl=/tools/lib \
  3. --with-tclinclude=/tools/include

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –with-tcl=/tools/lib
    这个选项确保configure脚本在临时的工具目录之中查找TCL工具,而不是在宿主机系统之中查找。

  • –with-tclinclude=/tools/include
    这个选项会告诉Expect应该在哪儿查找TCL的内部头文件。

编译Expect:

  1. make

安装Expect:

  1. make SCRIPTS="" install

make命令参数的意义,如下所示:

  • SCRIPTS=””
    这个选项会防止安装辅助的Expect脚本文件,此时不需要使用这些脚本文件。

最后,回退至$LFS/sources目录,删除TCL的源码目录:

  1. cd $LFS/sources
  2. rm -rfv expect5.45

3. DejaGNU-1.6

DejaGNU包含一个用于测试其他程序的框架。

编译大约需要耗费0.1 SBU的时间,磁盘大约需要占用3.2 MB的空间。

解压DejaGNU源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvzf dejagnu-1.6.tar.gz
  3. cd dejagnu-1.6

配置DejaGNU的编译选项:

  1. ./configure --prefix=/tools

编译安装DejaGNU:

  1. make install

检查安装是否成功:

  1. make check

若上述命令的返回信息如下图所示,则表示DejaGNU安装成功:

DejaGNU的测试

最后,回退至$LFS/sources目录,删除DejaGNU的源码目录:

  1. cd $LFS/sources
  2. rm -rfv dejagnu-1.6

4. Check-0.10.0

Check是一种C语言的单元测试框架。

编译大约需要耗费0.1 SBU的时间,磁盘大约需要占用9.5 MB的空间。

解压Check源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvzf check-0.10.0.tar.gz
  3. cd check-0.10.0

配置Check的编译选项:

  1. PKG_CONFIG= ./configure --prefix=/tools

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • PKG_CONFIG=
    这个选项会使得configure脚本忽略一些pkg-config选项,这些选项可能导致系统试图链接不在/tools目录之中的程序库。

编译Check:

  1. make

安装Check:

  1. make install

最后,回退至$LFS/sources目录,删除Check的源码目录:

  1. cd $LFS/sources
  2. rm -rfv check-0.10.0

5. Ncurses-6.0

Ncurses包含不依赖于终端的字符界面处理功能的程序库。

编译大约需要耗费0.5 SBU的时间,磁盘大约需要占用38 MB的空间。

解压Ncurses源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvzf ncurses-6.0.tar.gz
  3. cd ncurses-6.0

首先,确保configure脚本能够找到gawk工具:

  1. sed -i s/mawk// configure

配置Ncurses的编译选项:

  1. ./configure --prefix=/tools \
  2. --with-shared \
  3. --without-debug \
  4. --without-ada \
  5. --enable-widec \
  6. --enable-overwrite

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –without-ada
    这个选项可以确保不会编译对Ada编译器的相关支持,宿主机系统可能包含Ada编译器,但是进入chroot环境之后就不可用了。

  • –enable-overwrite
    这个选项会将Ncurses的头文件安装至/tools/include目录,而不是/tools/include/ncurses目录,这样便能确保编译其他的源码包时能够成功找到这些头文件。

  • –enable-widec
    这个选项会编译宽位字符的程序库(例如:libncursesw.so.6.0),而不是编译普通字符的程序库(例如:libncurses.so.6.0)。这些宽位字符的程序库可用于多字节语言和单字节(8位)语言,而普通字符的程序库只能用于单字节语言。宽位字符的程序库和普通字符的程序库在源码级别上是兼容的,但是在二进制级别上是不兼容的。

编译Ncurses:

  1. make

安装Ncurses:

  1. make install

最后,回退至$LFS/sources目录,删除Ncurses的源码目录:

  1. cd $LFS/sources
  2. rm -rfv ncurses-6.0

6. Bash-4.3.30

Bash是Bourne-Again SHell的缩写,它是一种Linux/Unix Shell。

编译大约需要耗费0.4 SBU的时间,磁盘大约需要占用54 MB的空间。

解压Bash源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvzf bash-4.3.30.tar.gz
  3. cd bash-4.3.30

配置Bash的编译选项:

  1. ./configure --prefix=/tools --without-bash-malloc

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –without-bash-malloc
    这个选项会禁用Bash的内存分配(malloc)功能,这个功能会导致内存碎片的问题。Bash将会使用Glibc提供的内存分配功能,Glibc提供的内存分配功能比较稳定。

编译Bash:

  1. make

安装Bash:

  1. make install

创建一个指向bash的符号链接:

  1. ln -sv bash /tools/bin/sh

最后,回退至$LFS/sources目录,删除Bash的源码目录:

  1. cd $LFS/sources
  2. rm -rfv bash-4.3.30

7. Bzip2-1.0.6

Bzip2包含用于压缩和解压文件的程序。对于文本文件来说,bzip2的压缩率要比传统的gzip高得多。

编译大约需要耗费0.1 SBU的时间,磁盘大约需要占用5.2 MB的空间。

解压Bzip2源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvzf bzip2-1.0.6.tar.gz
  3. cd bzip2-1.0.6

Bzip的源码包没有configure脚本,直接编译:

  1. make

安装Bzip2:

  1. make PREFIX=/tools install

最后,回退至$LFS/sources目录,删除Bzip2的源码目录:

  1. cd $LFS/sources
  2. rm -rfv bzip2-1.0.6

8. Coreutils-8.25

Coreutils包含用于显示和设置基本系统特性的实用工具。

编译大约需要耗费0.6 SBU的时间,磁盘大约需要占用132 MB的空间。

解压Coreutils源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf coreutils-8.25.tar.xz
  3. cd coreutils-8.25

配置Coreutils的编译选项:

  1. ./configure --prefix=/tools --enable-install-program=hostname

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –enable-install-program=hostname
    这个选项会编译和安装hostname程序 —— 这个程序默认是禁用的,但是Perl测试套件需要使用。

编译Coreutils:

  1. make

安装Coreutils:

  1. make install

最后,回退至$LFS/sources目录,删除Coreutils的源码目录:

  1. cd $LFS/sources
  2. rm -rfv coreutils-8.25

9. Diffutils-3.5

Diffutils能够显示两个文件之间或者两个目录之间的不同之处。

编译大约需要耗费0.2 SBU的时间,磁盘大约需要占用21.5 MB的空间。

解压Diffutils源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf diffutils-3.5.tar.xz
  3. cd diffutils-3.5

配置Diffutils的编译选项:

  1. ./configure --prefix=/tools

编译Diffutils:

  1. make

安装Diffutils:

  1. make install

最后,回退至$LFS/sources目录,删除Diffutils的源码目录:

  1. cd $LFS/sources
  2. rm -rfv diffutils-3.5

10. File-5.28

File包含用于确定一个或多个给定文件的文件类型的实用工具。

编译大约需要耗费0.1 SBU的时间,磁盘大约需要占用15 MB的空间。

解压File源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvzf file-5.28.tar.gz
  3. cd file-5.28

配置File的编译选项:

  1. ./configure --prefix=/tools

编译File:

  1. make

安装File:

  1. make install

最后,回退至$LFS/sources目录,删除File的源码目录:

  1. cd $LFS/sources
  2. rm -rfv file-5.28

11. Findutils-4.6.0

Findutils包含用于查找文件的程序。这些程序能够递归搜索一个目录树,并且还能创建、维护和搜索一个数据库(速度通常快于递归查找,但如果最近未更新数据库,那么搜索结果就不一定可靠)。

编译大约需要耗费0.3 SBU的时间,磁盘大约需要占用35 MB的空间。

解压Findutils源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvzf findutils-4.6.0.tar.gz
  3. cd findutils-4.6.0

配置Findutils的编译选项:

  1. ./configure --prefix=/tools

编译Findutils:

  1. make

安装Findutils:

  1. make install

最后,回退至$LFS/sources目录,删除Findutils的源码目录:

  1. cd $LFS/sources
  2. rm -rfv findutils-4.6.0

12. Gawk-4.1.3

Gawk包含用于操作文本文件的程序。

编译大约需要耗费0.2 SBU的时间,磁盘大约需要占用34 MB的空间。

解压Gawk源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf gawk-4.1.3.tar.xz
  3. cd gawk-4.1.3

配置Gawk的编译选项:

  1. ./configure --prefix=/tools

编译Gawk:

  1. make

安装Gawk:

  1. make install

最后,回退至$LFS/sources目录,删除Gawk的源码目录:

  1. cd $LFS/sources
  2. rm -rfv gawk-4.1.3

13. Gettext-0.19.8.1

Gettext包含用于国际化和本地化的实用工具。这些实用工具使得编译后的程序具有NLS(本地语言支持)功能,使得程序能够以用户本地语言输出各种信息。

编译大约需要耗费0.9 SBU的时间,磁盘大约需要占用164 MB的空间。

对于临时工具链来说,只需要编译和安装Gettext中的三个程序:msgfmt、msgmerge和xgettext。

解压Gettext源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf gettext-0.19.8.1.tar.xz
  3. cd gettext-0.19.8.1

配置Gettext的编译选项:

  1. cd gettext-tools
  2. EMACS="no" ./configure --prefix=/tools --disable-shared

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • EMACS=”no”
    这个选项会防止configure脚本确定Emacs Lisp文件的安装路径,因为测试套件可能会在某些主机上挂起。

  • –disable-shared
    此时不需要安装任何共享的Gettext程序库,因此就不需要编译它们。

安装Gettext:

  1. make -C gnulib-lib
  2. make -C intl pluralx.c
  3. make -C src msgfmt
  4. make -C src msgmerge
  5. make -C src xgettext

安装msgfmt、msgmerge和xgettext程序:

  1. cp -v src/{msgfmt,msgmerge,xgettext} /tools/bin

最后,回退至$LFS/sources目录,删除Gettext的源码目录:

  1. cd $LFS/sources
  2. rm -rfv gettext-0.19.8.1

14. Grep-2.25

Grep包含用于文件全文搜索的工具。

编译大约需要耗费0.2 SBU的时间,磁盘大约需要占用18 MB的空间。

解压Grep源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf grep-2.25.tar.xz
  3. cd grep-2.25

配置Grep的编译选项:

  1. ./configure --prefix=/tools

编译Grep:

  1. make

安装Grep:

  1. make install

最后,回退至$LFS/sources目录,删除Grep的源码目录:

  1. cd $LFS/sources
  2. rm -rfv grep-2.25

15. Gzip-1.8

Gzip包含用于压缩和解压文件的程序。

编译大约需要耗费0.1 SBU的时间,磁盘大约需要占用8.9 MB的空间。

解压Gzip源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf gzip-1.8.tar.xz
  3. cd gzip-1.8

配置Gzip的编译选项:

  1. ./configure --prefix=/tools

编译Gzip:

  1. make

安装Gzip:

  1. make install

最后,回退至$LFS/sources目录,删除Gzip的源码目录:

  1. cd $LFS/sources
  2. rm -rfv gzip-1.8

16. M4-1.4.17

M4包含一个宏处理器。

编译大约需要耗费0.2 SBU的时间,磁盘大约需要占用18 MB的空间。

解压M4源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf m4-1.4.17.tar.xz
  3. cd m4-1.4.17

配置M4的编译选项:

  1. ./configure --prefix=/tools

编译M4:

  1. make

安装M4:

  1. make install

最后,回退至$LFS/sources目录,删除M4的源码目录:

  1. cd $LFS/sources
  2. rm -rfv m4-1.4.17

17. Make-4.2.1

Make包含用于编译源码包的程序。

编译大约需要耗费0.1 SBU的时间,磁盘大约需要占用12.5 MB的空间。

解压Make源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvjf make-4.2.1.tar.bz2
  3. cd make-4.2.1

配置Make的编译选项:

  1. ./configure --prefix=/tools --without-guile

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –without-guile
    这个选项确保Make-4.2.1不会链接Guile程序库,虽然宿主机系统可能包含这个程序库,但是在下一章的chroot环境之中不需要使用这个程序库。

编译Make:

  1. make

安装Make:

  1. make install

最后,回退至$LFS/sources目录,删除Make的源码目录:

  1. cd $LFS/sources
  2. rm -rfv make-4.2.1

18. Patch-2.7.5

Patch能够通过补丁(后缀名为patch的文件)修改或创建文件,通常补丁文件由diff程序生成。

编译大约需要耗费0.2 SBU的时间,磁盘大约需要占用10.4 MB的空间。

解压Patch源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf patch-2.7.5.tar.xz
  3. cd patch-2.7.5

配置Patch的编译选项:

  1. ./configure --prefix=/tools

编译Patch:

  1. make

安装Patch:

  1. make install

最后,回退至$LFS/sources目录,删除Patch的源码目录:

  1. cd $LFS/sources
  2. rm -rfv patch-2.7.5

19. Perl-5.24.0

Perl是Practical Extraction and Report Language的缩写,它是一种实用的提取和报告语言。

编译大约需要耗费1.3 SBU的时间,磁盘大约需要占用248 MB的空间。

解压Perl源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvjf perl-5.24.0.tar.bz2
  3. cd perl-5.24.0

配置Perl的编译选项:

  1. sh Configure -des -Dprefix=/tools -Dlibs=-lm

编译Perl:

  1. make

此时,只需要安装一部分的工具和程序库:

  1. cp -v perl cpan/podlators/scripts/pod2man /tools/bin
  2. mkdir -pv /tools/lib/perl5/5.24.0
  3. cp -Rv lib/* /tools/lib/perl5/5.24.0

最后,回退至$LFS/sources目录,删除Perl的源码目录:

  1. cd $LFS/sources
  2. rm -rfv perl-5.24.0

20. Sed-4.2.2

Sed是一种流编辑器。

编译大约需要耗费0.1 SBU的时间,磁盘大约需要占用10.0 MB的空间。

解压Sed源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xvjf sed-4.2.2.tar.bz2
  3. cd sed-4.2.2

配置Sed的编译选项:

  1. ./configure --prefix=/tools

编译Sed:

  1. make

安装Sed:

  1. make install

最后,回退至$LFS/sources目录,删除Sed的源码目录:

  1. cd $LFS/sources
  2. rm -rfv sed-4.2.2

21. Tar-1.29

Tar是一种用于归档的工具。

编译大约需要耗费0.3 SBU的时间,磁盘大约需要占用32 MB的空间。

解压Tar源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf tar-1.29.tar.xz
  3. cd tar-1.29

配置Tar的编译选项:

  1. ./configure --prefix=/tools

编译Tar:

  1. make

安装Tar:

  1. make install

最后,回退至$LFS/sources目录,删除Tar的源码目录:

  1. cd $LFS/sources
  2. rm -rfv tar-1.29

22. Texinfo-6.1

Texinfo包含用于读取、写入和转换info页面的程序。

编译大约需要耗费0.2 SBU的时间,磁盘大约需要占用99 MB的空间。

解压Texinfo源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf texinfo-6.1.tar.xz
  3. cd texinfo-6.1

配置Texinfo的编译选项:

  1. ./configure --prefix=/tools

编译Texinfo:

  1. make

安装Texinfo:

  1. make install

最后,回退至$LFS/sources目录,删除Texinfo的源码目录:

  1. cd $LFS/sources
  2. rm -rfv texinfo-6.1

23. Util-linux-2.28.1

Util-linux包含各种各样的实用程序。

编译大约需要耗费0.8 SBU的时间,磁盘大约需要占用114 MB的空间。

解压Util-linux源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf util-linux-2.28.1.tar.xz
  3. cd util-linux-2.28.1

配置Util-linux的编译选项:

  1. ./configure --prefix=/tools \
  2. --without-python \
  3. --disable-makeinstall-chown \
  4. --without-systemdsystemunitdir \
  5. PKG_CONFIG=""

运行configure脚本时,使用的编译配置选项的意义,如下所示:

  • –without-python
    如果宿主机系统安装了Python,那么这个选项便会禁用宿主机系统的Python。它能够避免编译安装不需要的绑定。

  • –disable-makeinstall-chown
    这个选项能够在安装期间禁用chown命令。当安装至/tools目录时,不需要使用这个命令,也不需要以root用户权限进行安装。

  • –without-systemdsystemunitdir
    在使用systemd的系统上,Util-linux会尝试将一个systemd专用的文件安装至/tools中的一个不存在的目录之中。这个选项能够避免不需要的动作。

  • PKG_CONFIG=””
    将这个环境变量设置为空值,能够防止添加不需要的宿主机系统的功能特性。注意,此处设置环境变量的方式和LFS其他部分放在命令前面的方式不同。此处,只是为了展示一下使用configure脚本配置时,设置环境变量的另一种方式。

编译Util-linux:

  1. make

安装Util-linux:

  1. make install

最后,回退至$LFS/sources目录,删除Util-linux的源码目录:

  1. cd $LFS/sources
  2. rm -rfv util-linux-2.28.1

24. Xz-5.2.2

Xz包含用于压缩和解压文件的程序,它提供了lzma和xz压缩格式。对于文本文件来说,xz的压缩率要高于gzip和bzip2。

编译大约需要耗费0.2 SBU的时间,磁盘大约需要占用16 MB的空间。

解压Xz源码包,然后进入源码目录:

  1. cd $LFS/sources
  2. tar -xf xz-5.2.2.tar.xz
  3. cd xz-5.2.2

配置Xz的编译选项:

  1. ./configure --prefix=/tools

编译Xz:

  1. make

安装Xz:

  1. make install

最后,回退至$LFS/sources目录,删除Xz的源码目录:

  1. cd $LFS/sources
  2. rm -rfv xz-5.2.2

五、精简二进制文件

本节会删除临时工具链中不必要的数据,这样便能节省LFS分区的磁盘空间。到目前为止,编译得到的可执行文件和程序库包含大约70 MB的调试符号,这些都是不必要的数据。以lfs用户权限,运行以下命令,删除这些调试符号:

  1. strip --strip-debug /tools/lib/*
  2. /usr/bin/strip --strip-unneeded /tools/{,s}bin/*

这些命令会跳过很多文件,提示不能识别它们的文件格式。这些文件大多数是脚本文件,而不是二进制文件。注意,不要对程序库使用--strip-unneeded选项,否则就会损坏静态链接的程序库,从而导致整个工具链都要重新编译。

删除各种文档可以节省更多的磁盘空间,以lfs用户权限,运行以下命令:

  1. rm -rf /tools/{,share}/{info,man,doc}

此时,LFS文件系统应当至少有3 GB的空闲空间可以用于编译和安装下一章的Glibc和Gcc。如果可以成功地编译和安装Glibc,那么就可以编译和安装其他所有的源码包。

六、修改所有权

注意,本系列教程剩余章节之中的命令都必须以root用户权限执行,不会再使用lfs用户权限执行任何命令。另外,还需要反复检查$LFS是否已在root环境之中设置。

此时,$LFS/tools目录是归属于lfs用户的,这个用户只存在于宿主机系统。如果$LFS/tools目录继续保持现状,那么其中的文件将归属于一个没有对应账号的用户ID。这是非常危险的!因为,稍后创建的用户账户可能会使用这个相同的用户ID,所以这个用户便会拥有$LFS/tools目录,以及其中全部文件的所有权。将这些文件暴露给普通用户,很有可能会导致危险的操作。

为了避免这个问题,可以在稍后创建/etc/passwd文件时,将lfs用户添加至新的LFS系统,小心地分配UID和GID,确保和宿主机系统上的保持一致。还有一种更好的方法,将$LFS/tools目录的所有者修改为root用户。以root用户权限,运行以下命令:

  1. chown -R root:root $LFS/tools

虽然在LFS系统编译和安装完成之后就可以删除$LFS/tools目录,但是也可以保留这个目录中的工具链,用于下一次编译相同版本的LFS系统。以root用户权限,运行以下命令::

  1. tar zcvf /root/Downloads/toolchain.tar.gz $LFS/tools

然后,将备份得到的toolchain.tar.gz文件(大约418 MB)拷贝至U盘,或者其他存储设备即可。

现在,临时的最小化Linux系统已经搭建完成了。下一章会通过chroot命令进入这个临时系统,然后进行一些必要的环境配置,最后编译和安装LFS系统的各个组件的源码包。