LFS 系统主体部分

主体部分包含大部分软件包的构建,总计 67 个软件包,仅余 systemd 和 System V 相关的个别软件包在分支部分说明。LFS 构建顺序保证被依赖的软件包先构建,再构建需要依赖的软件包,所以很难划分章节,大致会在 binutil、glibc、gcc、bash,以及 PERL 和 Python 分别停一下做一个整理,但并不等同于所有 binutil 小节构建的所有软件包都是 binutil 依赖的软件包,其中有一部分是 gcc 的依赖。

虽然 LFS 手册的构建顺序并不是不可以更改,但考虑到变更顺序可能会导致难以预见的问题,为避免不必要的麻烦,笔者没有也不推荐变更构建顺序。目前的构建顺序至少能够保证每个软件包在构建前所依赖的软件包已被构建。如果发生问题也可以在 Google 和百度上查到类似的说明,极大的降低了风险和难度。大致来说,主体部分的三个重点软件包分别为 binutil、glibc、gcc 这三位老朋友。前半部分主要围绕它们展开构建,在构建完它们之后,后续的部分并没有特别的重点,主要还是为了完成 LSB 规范而构建的软件包。

4.1.1 用于测试 Tcl、Expect、DejaGNU 及其他无依赖软件包

安装 Tcl、Expect 和 DejaGNU 这三个软件包的主要目的是为了满足第 4 章中的测试需求,对于 Glibc、Binutil 和 GCC 的测试非常重要,不建议跳过,但如果明确没有在第四章中运行测试套件需求的话,也可以不用构建。

man-pages 和 iana-etc 和所有的软件包都没有依赖关系,并且构建十分简单,顺带一起构建。

1. 安装 Man-pages

软件包包含 2,200 多个 man 手册页面。帮助使用者了解命令,查询参数。

  • 大致构建用时:少于 0.1 SBU

  • 所需磁盘空间: 28 MB

解压并进入软件包:

tar -xf man-pages-5.08.tar.xz

cd man-pages-5.08

运行下面的命令安装 Man-pages:

make install

退出并清理软件包:

cd ..
rm -rf man-pages-5.08

2. 安装 Tcl

工具命令语言 (tool command language),一种可靠的通用脚本语言。 此软件包和后面两个包(Expect 和 DejaGNU)用来为 GCC 和 Binutils 还有其他的一些软件包的测试套件提供运行支持。

注意,这里的 Tcl 软件包用的是最小化安装的版本,仅仅是为了运行 LFS 测试。需要完整版的软件包,可参考 BLFS 的 Tcl 流程。

  • 大致构建用时: 4.0 SBU
  • 所需磁盘空间: 83 MB

解压并进入软件包:

tar -xf tcl8.6.10-src.tar.gz
cd	tcl8.6.10

解压文档:

tar -xf ../tcl8.6.10-html.tar.gz --strip-components=1

配置 TCL 准备编译:

SRCDIR=$(pwd)
cd unix
./configure			\
--prefix=/usr           	\
--mandir=/usr/share/man 	\
$([ "$(uname -m)" = x86_64 ] && echo --enable-64bit)

构建软件包:

make
sed -e "s|$SRCDIR/unix|/usr/lib|" 	\
     -e "s|$SRCDIR|/usr/include|"  	\
     -i tclConfig.sh
sed -e "s|$SRCDIR/unix/pkgs/tdbc1.1.1|/usr/lib/tdbc1.1.1|" 	\
     -e "s|$SRCDIR/pkgs/tdbc1.1.1/generic|/usr/include|"    	\
     -e "s|$SRCDIR/pkgs/tdbc1.1.1/library|/usr/lib/tcl8.6|" 	\
     -e "s|$SRCDIR/pkgs/tdbc1.1.1|/usr/include|"            	\
     -i pkgs/tdbc1.1.1/tdbcConfig.sh
sed -e "s|$SRCDIR/unix/pkgs/itcl4.2.0|/usr/lib/itcl4.2.0|" 	\
     -e "s|$SRCDIR/pkgs/itcl4.2.0/generic|/usr/include|"    	\
     -e "s|$SRCDIR/pkgs/itcl4.2.0|/usr/include|"            	\
     -i pkgs/itcl4.2.0/itclConfig.sh
unset SRCDIR

测试编译结果:

make test

安装软件包:

make install

让安装的库文件可写,这样之后可以删除调试符号。

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

安装 TCL 的头文件。后面的软件包 Expect 在编译的时候会用到。

make install-private-headers

现在创建几个必要的软链接:

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

退出并清理软件包:

cd ../..
rm -rf tcl8.6.10

3. 安装 Expect

Expect 是类 Unix 系统中用来进行自动化控制和测试的软件工具,由 Tcl 脚本语言编写。

  • 大致构建用时: 0.2 SBU
  • 所需磁盘空间: 3.9 MB 解压并进入软件包:
tar -xf expect5.45.4.tar.gz
cd       expect5.45.4

配置 Expect 准备编译:

./configure			\
    --prefix=/usr		\
    --with-tcl=/usr/lib     	\
    --enable-shared         	\
    --mandir=/usr/share/man 	\
    --with-tclinclude=/usr/include

配置选项的含义:

表 4-1 Expect 配置选项
参数描述
--with-tcl=/tools/lib保证 configure 配置脚本会从临时工具目录里找 tcl,而不是在宿主机系统中寻找
--with-tclinclude=/tools/include指定 tcl 内部头文件的位置。通过这个选项可以避免 configure 脚本不能自动发现 tcl 头文件位置的情况

构建软件包:

make

测试编译结果:

make test

安装软件包:

make install
ln -svf expect5.45.4/libexpect5.45.4.so /usr/lib

退出并清理软件包:

cd ..
rm -rf expect5.45.4

4. 安装 DejaGNU

DejaGNU 是一个著名的测试框架,由 Expect 编写。

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 4.6 MB 解压并进入软件包:
tar -xf dejagnu-1.6.2.tar.gz
cd       dejagnu-1.6.2

配置 DejaGNU 准备编译:

./configure --prefix=/usr
makeinfo --html --no-split -o doc/dejagnu.html doc/dejagnu.texi
makeinfo --plaintext -o doc/dejagnu.txt  doc/dejagnu.texi

构建安装软件包:

make install
install -v -dm755  /usr/share/doc/dejagnu-1.6.2
install -v -m644   doc/dejagnu.{html,txt} /usr/share/doc/dejagnu-1.6.2

测试编译安装结果:

make check

退出并清理软件包:

cd ..
rm -rf dejagnu-1.6.2
  1. 安装 iana-etc iana-etc 中主要是/etc/protocols 和/etc/services 两个配置文件,提供了网络服务和协议的数据。
  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 4.7 MB 解压并进入软件包:
tar -xf iana-etc-20200821.tar.gz
cd 	iana-etc-20200821

只需要将文件复制到 etc 目录下即可:

cp services protocols /etc

退出并清理软件包:

cd ..
rm -rf iana-etc-20200821

4.1.2 构建并配置 glibc

老朋友了,在第三章构建交叉编译工具链的时候已经构建过了,不过此次由于基本工具和临时工具已经就位,构建的时候便不用依赖宿主的软件包了。

1. 安装 glibc

  • 大致构建用时: 20 SBU
  • 所需磁盘空间: 2.4 GB 解压并进入软件包:
tar -xf glibc-2.32.tar.xz
cd 	glibc-2.32

glibc 构建系统是独立的,并且会完美地安装,即使编译器指定的文件和链接器仍然指向/tools。在 glibc 安装之前,不能调整指定的文件和链接器,因为 glibc 的 autoconf 测试会给出错误结果并阻碍目标实现干净的构建。 有些 glibc 程序会用到和 FHS 不兼容的/var/db 目录来存储它们的运行时数据。打上如下的补丁可以让这些程序在 FHS 兼容的位置存储它们的运行时数据。

patch -Np1 -i ../glibc-2.32-fhs-1.patch

glibc 文档里建议在 Glibc 特定编译目录下编译:

mkdir -v build
cd	build

配置 glibc 准备编译:

../configure                       	\
    --prefix=/usr			\
    --disable-werror \
    --enable-kernel=3.2			\
    --enable-stack-protector=strong       \
    --with-headers=/usr/include            \
    libc_cv_slibdir=/lib

配置选项的含义如表 4-2 所示。

表 4-2 glibc 配置选项
配置选项含义
--disable-werror禁用了传递给 GCC 的-werror 选项。为了测试套件,此项必不可少
--enable-kernel=3.2告诉系统 glibc 可能在 3.2 版本的内核上使用,避免在旧内核上调用新内核的功能
--enable-stack-protector=strong通过添加额外代码检查缓冲区溢出(例如,堆栈粉碎攻击)来增强系统安全性
--with-headers=/usr/include告诉构建系统在哪里可以找到内核 API 的头文件。默认情况下,会在 /tools/include 中查找头文件
libc_cv_slibdir=/lib为所有系统设置正确的库。我们不希推荐用 lib64

编译软件包:

make

注意:运行 glibc 的测试套件是很关键的。在任何情况下都不要跳过这个测试。

通常会有一些测试不能通过。若是下面列出的这些失败,通常就可以安心地无视了。

case $(uname -m) in
  i?86)   ln -sfnv $PWD/elf/ld-linux.so.2        /lib ;;
  x86_64) ln -sfnv $PWD/elf/ld-linux-x86-64.so.2 /lib ;;
esac

在 chroot 环境的这个阶段需要上面的符号链接以运行测试。在接下来的安装中,这些链接将会被覆盖。

make check

你可能会看到一些失败的测试项。glibc 的测试套件对宿主系统有一定的依赖。下面是一些 LFS 版本中最常见的问题。

  • misc/tst-ttyname:已知会在 LFS 的 chroot 环境中失败。
  • inet/tst-idna_name_classify:已知会在 LFS 的 chroot 环境中失败。
  • posix/tst-getaddrinfo4 和 posix/tst-getaddrinfo5:可能在某些架构上失败。
  • nss/tst-nss-files-hosts-multi:由于未知的原因,测试可能失败。
  • rt/tst-cputimer{1,2,3}:测试取决于宿主机的系统内核。众所周知,内核版本为 4.14.91–4.14.96、4.19.13–4.19.18 和 4.20.0–4.20.5 会导致这些测试失败。

如果你系统的 CPU 不是相对较新的 Intel 或 AMD 处理器,数学运算测试有时候会失败。虽然这只是无关紧要的消息,在安装 glibc 时会报告找不到/etc/ld.so.conf 文件。下面的方式可以避免这个警告:

touch /etc/ld.so.conf

运行以下命令,修复生成的 Makefile 以跳过一个没有必要的完整性检查,该检查在部分 LFS 环境中会失败:

sed '/test-installation/s@$(PERL)@echo not running@' -i ../Makefile

安装软件包:

make install

为 nscd 安装配置文件并创建运行时目录:

cp -v ../nscd/nscd.conf /etc/nscd.conf
mkdir -pv /var/cache/nscd

为 nscd 安装系统支持文件:

install -v -Dm644 ../nscd/nscd.tmpfiles /usr/lib/tmpfiles.d/nscd.conf
install -v -Dm644 ../nscd/nscd.service /lib/systemd/system/nscd.service

接着,安装可以使系统响应不同语言的地域设置。没有一个地域是必需的,但是如果缺少了其中的某些地域,可能会导致在未来的软件包测试套件中,跳过了重要的测试项目。 单独的语言环境可以用 localedef 程序安装。例如,下面第一个 localedef 命令将 /usr/share/i18n/locales/cs_CZ 字符表定义组合在一起,并将结果附加到 /usr/share/i18n/charmaps/UTF-8.gz 文件末尾。下面的命令将安装能完美覆盖测试所需语言环境的最小集合:

mkdir -pv /usr/lib/locale
localedef -i POSIX -f UTF-8 C.UTF-8 2> /dev/null || true
localedef -i cs_CZ -f UTF-8 cs_CZ.UTF-8
localedef -i de_DE -f ISO-8859-1 de_DE
localedef -i de_DE@euro -f ISO-8859-15 de_DE@euro
localedef -i de_DE -f UTF-8 de_DE.UTF-8
localedef -i el_GR -f ISO-8859-7 el_GR
localedef -i en_GB -f UTF-8 en_GB.UTF-8
localedef -i en_HK -f ISO-8859-1 en_HK
localedef -i en_PH -f ISO-8859-1 en_PH
localedef -i en_US -f ISO-8859-1 en_US
localedef -i en_US -f UTF-8 en_US.UTF-8
localedef -i es_MX -f ISO-8859-1 es_MX
localedef -i fa_IR -f UTF-8 fa_IR
localedef -i fr_FR -f ISO-8859-1 fr_FR
localedef -i fr_FR@euro -f ISO-8859-15 fr_FR@euro
localedef -i fr_FR -f UTF-8 fr_FR.UTF-8
localedef -i it_IT -f ISO-8859-1 it_IT
localedef -i it_IT -f UTF-8 it_IT.UTF-8
localedef -i ja_JP -f EUC-JP ja_JP
localedef -i ja_JP -f SHIFT_JIS ja_JP.SIJS 2> /dev/null || true
localedef -i ja_JP -f UTF-8 ja_JP.UTF-8
localedef -i ru_RU -f KOI8-R ru_RU.KOI8-R
localedef -i ru_RU -f UTF-8 ru_RU.UTF-8
localedef -i tr_TR -f UTF-8 tr_TR.UTF-8
localedef -i zh_CN -f GB18030 zh_CN.GB18030
localedef -i zh_HK -f BIG5-HKSCS zh_HK.BIG5-HKSCS

或者,也可以一次性安装在 glibc-2.29/localedata/SUPPORTED 文件里列出的所有语言环境(包括以上列出的所有语言环境以及其他更多),执行下面这个非常耗时的命令:

make localedata/install-locales

你需要的语言环境几乎不太可能没有列在 glibc-2.29/localedata/SUPPORTED 文件中,但如果真的没有,可以使用 localedef 命令创建和安装。

当前,glibc 在解决国际化域名时使用 libidn2。此为运行时依赖。如果需要此功能,可以参考 BLFS libidn2 页相关的安装指令。

2. 配置 glibc

由于 glibc 的默认状态在网络环境下工作得并不好,所以需要创建/etc/nsswitch.conf 文件。 创建新的/etc/nsswitch.conf 通过以下命令:

cat > /etc/nsswitch.conf << "EOF"
# Begin /etc/nsswitch.conf

passwd: files
group: files
shadow: files

hosts: files dns
networks: files

protocols: files
services: files
ethers: files
rpc: files

# End /etc/nsswitch.conf
EOF

通过以下命令安装并启动时区数据:

tar -xf ../../tzdata2018i.tar.gz

ZONEINFO=/usr/share/zoneinfo
mkdir -pv $ZONEINFO/{posix,right}

for tz in etcetera southamerica northamerica europe africa antarctica  \
          asia australasia backward pacificnew systemv; do
    zic -L /dev/null   -d $ZONEINFO       ${tz}
    zic -L /dev/null   -d $ZONEINFO/posix ${tz}
    zic -L leapseconds -d $ZONEINFO/right ${tz}
done

cp -v zone.tab zone1970.tab iso3166.tab $ZONEINFO
zic -d $ZONEINFO -p America/New_York
unset ZONEINFO

zic 命令的含义如表 4-3 所示。

表 4-3 zic 命令参数含义
参数含义
zic -L /dev/null ...创建没有时间补偿的 posix 时区数据。一般将它们同时放在 zoneinfo 目录和 zoneinfo/posix 目录。需要将 POSIX 时区数据放到 zoneinfo 目录下,否则很多测试套件会报错。在嵌入式平台,如果存储空间紧张而且你也不准备更新时区,也可以不用 posix 目录,从而节省 1.9MB 空间,但是一些应用程序或测试套件也许会出错
zic -L leapseconds ...创建包含时间补偿的 right 时区数据。在嵌入式平台,空间比较紧张而且你也不打算更新时区或者不需要准确时间,你可以忽略 right 目录,从而节省 1.9MB 空间
zic ... -p ...创建 posixrules 文件。我们使用纽约是因为 POSIX 要求夏令时规则与 US 标准一致

运行以下脚本,确定本地时区:

tzselect

在询问了几个关于位置的问题后,脚本会输出所在时区的名字(比如 Asia/Shanghai)。在 /usr/share/zoneinfo 文件中也有其他一些可用时区,比如 Canada/Eastern 或 EST5EDT,这些时区并没有被脚本列出来,但也是可以使用的。 然后运行下面的命令创建/etc/localtime 文件:

ln -sfv /usr/share/zoneinfo/<xxx> /etc/localtime

将命令中的替换成你所在实际时区的名字(比如 Asia/Shanghai)。 接下来,配置动态库加载器。默认情况下,动态库加载器(/lib/ld-linux.so.2)会搜索目录/lib 和 /usr/lib 查找程序运行时所需的动态库文件。如果库文件不在/lib 和/usr/lib 目录下,需要把它所在目录写到/etc/ld.so.conf 文件里,保证动态库加载器能找到这些库。通常会用/usr/local/lib 和/opt/lib 这两个目录包含额外的动态库,把这两个目录加到动态库加载器的搜索路径中。 运行下面的命令创建一个新文件/etc/ld.so.conf:

cat > /etc/ld.so.conf << "EOF"
# Begin /etc/ld.so.conf
/usr/local/lib
/opt/lib

EOF

如果需要的话,动态库加载器也可以查找目录并包含里面配置文件的内容。通常在这个包含目录下的文件只有一行字指向库目录。运行下面的命令增加这个功能:

cat >> /etc/ld.so.conf << "EOF"
# Add an include directory
include /etc/ld.so.conf.d/*.conf

EOF
mkdir -pv /etc/ld.so.conf.d

退出并清理软件包:

cd ../..
rm -rf glibc-2.32

4.1.3 构建 binutil 和 binutil 依赖的软件包及其他

整理一下依赖关系,主要安装的依赖软件包是:File、Flex、zlib,其他的依赖在第三章已经构建了,而测试的依赖在 4.1.1 用于测试 Tcl、Expect、DejaGNU 及其他无依赖软件包已经构建。 File 依赖 Bzip2 和 xz,Flex 依赖 M4,所以各自先构建了所依赖的软件包。 zstd 是 gcc 的依赖,需要依赖 xz,可能自身比较小就顺手和 xz 一起构建了(推测)。 bc 理论只要在内核编译前编译即可,本身的依赖 bash, binutils, coreutils, gcc, glibc、grep、make 又在第三章已经构建,所以放在何时构建都没什么关系,排在 binutil 之前应该是字母排序的原因。

1. 安装 zlib:系统底层压缩算法支持

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 4.4 MB 解压并进入软件包:
tar -xf zlib-1.2.11.tar.xz
cd 	zlib-1.2.11

准备编译 Zlib:

./configure --prefix=/usr

编译软件包:

make

输入命令查看结果:

make check

安装软件包:

make install

共享库需要移动到/lib,因此需要重建.so 里面的/usr/lib 文件:

mv -v /usr/lib/libz.so.* /lib
ln -sfv ../../lib/$(readlink /usr/lib/libz.so) /usr/lib/libz.so

退出并清理软件包:

cd ..
rm -rf zlib-1.2.11

2. 安装 Bzip2:无损压缩软件

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 7.7 MB 解压并进入软件包:
tar -xf bzip2-1.0.8.tar.gz
cd 	bzip2-1.0.8

使用能为这个软件包安装帮助文档的补丁:

patch -Np1 -i ../bzip2-1.0.8-install_docs-1.patch

下面的命令确保安装的符号链接是相对链接:

sed -i 's@\(ln -s -f \)$(PREFIX)/bin/@\1@' Makefile

确认 man 页面安装到了正确的位置:

sed -i "s@(PREFIX)/man@(PREFIX)/share/man@g" Makefile

准备编译 Bzip:

make -f Makefile-libbz2_so
make clean

这会使用不同的 Makefile 文件编译 Bzip2,在这里是 Makefile-libbz2_so,它会创建动态 libbz2.so 库,并把它链接到 Bzip2 工具。 编译并测试软件包:

make

安装程序:

make PREFIX=/usr install

安装使用动态链接库的 bzip2 二进制文件到 /bin 目录,创建一些必须的符号链接并清理:

cp -v bzip2-shared /bin/bzip2
cp -av libbz2.so* /lib
ln -sv ../../lib/libbz2.so.1.0 /usr/lib/libbz2.so
rm -v /usr/bin/{bunzip2,bzcat,bzip2}
ln -sv bzip2 /bin/bunzip2
ln -sv bzip2 /bin/bzcat

退出并清理软件包:

cd ..
rm -rf bzip2-1.0.8

3. 安装 xz:通用数据压缩软件

  • 大致构建用时: 0.2 SBU
  • 所需磁盘空间: 15MB 解压并进入软件包:
tar -xf xz-5.2.5.tar.xz
cd xz-5.2.5

准备编译 xz:

./configure		\
    --prefix=/usr	\
    --disable-static	\
    --docdir=/usr/share/doc/xz-5.2.5

编译软件包:

make

用以下命令测试结果:

make check

安装软件包并确保所需的文件都在正确目录中:

make install
mv -v   /usr/bin/{lzma,unlzma,lzcat,xz,unxz,xzcat} /bin
mv -v /usr/lib/liblzma.so.* /lib
ln -svf ../../lib/$(readlink /usr/lib/liblzma.so) /usr/lib/liblzma.so

退出并清理软件包:

cd ..
rm -rf xz-5.2.5

4. 安装 zstd

一种实时压缩算法,提供较高的压缩比,具有很宽的压缩/速度权衡范围,同时具有非常快速的解码器。

  • 大致构建用时: 0.7 SBU
  • 所需磁盘空间: 16MB 解压并进入软件包:
tar -xf zstd-1.4.5.tar.gz
cd 	zstd-1.4.5

构建软件包:

make

用以下命令测试结果:

make check

本软件包不包含测试套件。 安装软件包:

make prefix=/usr install

删除静态库,并将共享库移动到/lib。然后,重新创建指向/usr/lib 目录的.so 文件的符号:

rm -v /usr/lib/libzstd.a
mv -v /usr/lib/libzstd.so.* /lib
ln -sfv ../../lib/$(readlink /usr/lib/libzstd.so) /usr/lib/libzstd.so

退出并清理软件包:

cd ..
rm -rf zstd-1.4.5.tar.gz
  1. 安装 File:用于判断给定文件类型的工具
  • 大致构建用时: 0.1 SBU
  • 所需磁盘空间: 14 MB 解压并进入软件包:
tar -xf file-5.39.tar.gz
cd 	file-5.39

准备编译 File:

./configure --prefix=/usr

编译软件包:

make

输入命令检查结果:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf file-5.39
  1. 安装 Readline:为交互式程序提供行编辑和历史记录功能
  • 大致构建用时: 0.1 SBU
  • 所需磁盘空间: 15 MB 解压并进入软件包:
tar -xf readline-8.0.tar.gz
cd 	readline-8.0

重装 Readline 会使旧的库移动到 .old。通常来说这并不是什么问题,但一些情况下可能引起 ldconfig 链接错误。可以通过下面的两个 sed 命令避免这个问题:

sed -i '/MV.*old/d' Makefile.in
sed -i '/{OLDSUFF}/c:' support/shlib-install

准备编译 Readline:

./configure		\
    --prefix=/usr    	\
    --disable-static 	\
    --with-curses    	\
    --docdir=/usr/share/doc/readline-8.0

选项--with-curses 告诉 Readline 可以在 curses 库中查找 termcap 库函数,而非 termcap 库。以此保证生成正确的 readline.pc 文件。 编译软件包:

make SHLIB_LIBS="-lncursesw"

强制将 Readline 链接到 libncursesw 库。 该软件包没有测试套件,直接安装软件包:

make SHLIB_LIBS="-lncursesw" install

现在移动动态库到更合适它的位置/lib 中去,并修正一些文件权限和符号链接:

mv -v /usr/lib/lib{readline,history}.so.* /lib
chmod -v u+w /lib/lib{readline,history}.so.*
ln -sfv ../../lib/$(readlink /usr/lib/libreadline.so) /usr/lib/libreadline.so
ln -sfv ../../lib/$(readlink /usr/lib/libhistory.so ) /usr/lib/libhistory.so

如果需要的话,安装帮助文档:

install -v -m644 doc/*.{ps,pdf,html,dvi} /usr/share/doc/readline-8.0

退出并清理软件包:

cd ..
rm -rf readline-8.0

7. 安装 M4:宏处理器

  • 大致构建用时: 0.4 SBU
  • 所需磁盘空间: 31 MB 解压并进入软件包:
tar -xf m4-1.4.18.tar.xz
cd 	m4-1.4.18

对应 glibc-2.28 的需求做一些修复:

sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' lib/*.c
echo "#define _IO_IN_BACKUP 0x100" >> lib/stdio-impl.h

准备编译 M4:

./configure --prefix=/usr

编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf m4-1.4.18

8. 安装 bc:任意精度计算器语言

  • 大致构建用时: 0.1 SBU
  • 所需磁盘空间: 4.1 MB 解压并进入软件包:
tar -xf bc-3.1.5.tar.xz
cd 	bc-3.1.5

准备编译 bc:

PREFIX=/usr CC=gcc CFLAGS="-std=c99" ./configure.sh -G -O3

配置选项的含义:

表 4-1 bc 配置选项
参数描述
CC=gcc CFLAGS="-std=c99"这些参数指定要使用的编译器和 C 标准
-O3指定要使用的优化
-G省略测试套件的一部分,如果没有 GNU bc,这些套件将无法使用

编译软件包:

make

测试 bc,运行:

make test

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf bc-3.1.5

9. 安装 Flex:词法分析器

  • 大致构建用时: 0.4 SBU
  • 所需磁盘空间: 35 MB 解压并进入软件包:
tar -xf flex-2.6.4.tar.gz
cd 	flex-2.6.4

准备编译 Flex:

./configure --prefix=/usr --docdir=/usr/share/doc/flex-2.6.4

编译软件包:

make

用以下命令测试结果(大约 0.5 SBU):

make check

安装软件包:

make install

对于一些程序来说,Flex 可能比较陌生,但如果提到 Flex 的先辈 lex 大家可能或多或少有些了解。为了支持这些程序,为 Flex 在 bin 目录下创建符号链接 lex:

ln -sv flex /usr/bin/lex

退出并清理软件包:

cd ..
rm -rf flex-2.6.4
  1. 安装 binutils:二进制工具的集合
  • 大致构建用时: 6.5 SBU
  • 所需磁盘空间: 4.8 GB 解压并进入软件包:
tar -xf binutils-2.35.tar.xz
cd 	binutils-2.35

通过一个简单测试验证在 chroot 环境下 PTY 工作正常:

expect -c "spawn ls"

这个命令应该输出以下内容:

spawn ls

假如输出包括下面的信息,那么表示没有为 PTY 操作设置好环境。在运行 Binutils 和 GCC 的测试套件之前需要解决这个问题:

The system has no more ptys.
Ask your system administrator to create more.

删除一项测试,以保证不会影响到测试的完成:

sed -i '/@\tincremental_copy/d' gold/testsuite/Makefile.in

按照 binutils 的文档建议,创建一个专门用于编译目录:

mkdir -v build
cd       	build

接下来,配置 binutils 准备编译:

../configure --prefix=/usr       \
             --enable-gold       \
             --enable-ld=default \
             --enable-plugins    \
             --enable-shared     \
             --disable-werror    \
             --enable-64-bit-bfd \
             --with-system-zlib

配置选项的含义如表 4-4 所示。

表 4-2 Binutils 配置选项
参数含义
--enable-gold构建 gold 链接器并将其安装为 ld.gold(紧挨着默认链接器)
--enable-ld=default构建原来的 bdf 链接器并将其安装为 ld(默认链接器)和 ld.bfd
--enable-plugins为链接器启用插件支持
--enable-64-bit-bfd启用 64 位支持(针对字宽较窄的主机)。64 位系统可能没什么必要,但也不会有什么坏处
--with-system-zlib使用安装的 zlib 库替代自带的版本构建

编译软件包:

make tooldir=/usr

一般来说,tooldir (最终存放可执行文件的目录) 设置为 $(exec_prefix)/$(target_alias)。例如,x86_64 机器会把它扩展为/usr/x86_64-unknown-linux-gnu。因为这是个自定制的系统,并不需要 /usr 中的特定目标目录。如果系统用于交叉编译(例如,在 Intel 机器上编译能生成在 PowerPC 机器上运行的代码的软件包)会使用$(exec_prefix)/$(target_alias)。

本节 binutils 测试套件至关重要,任何情况下都不能跳过。

查看测试结果:

make -k check

已知测试中的 debug_msg.sh 会失败,可以忽略。 安装软件包:

make tooldir=/usr install

退出并清理软件包:

cd ../..
rm -rf binutils-2.35

4.1.4 构建 gcc 和 gcc 依赖的软件包

最后一次安装 gcc,首先要装得自然还是 GMP、MPFR、MPC,区别于前两次只是将软件包解压并复制到 gcc 软件包目录中,这次是直接构建。这三个软件包的依赖已经全部在第 3 章交叉编译完了。只需要按照自身的依赖顺序构建即可。

gcc 的测试需要用到 Shadow,所以需要先行构建 Shadow 的依赖 acl、attr 以及 libcap,acl、attr 和 libcap 三者之间也存在依赖关系,所以要以 attr、acl、libcap 的顺序构建。

按理说 GCC 的构建,除了这三个软件包,在第 3 章中没有交叉编译的就只有 zstd 了,而在 Binutils 安装前,已经伴随着一众压缩相关的软件包,完成了安装任务。不过,GCC 的测试套件需要 Shadow,由此也就必须安装 Shadow 的依赖的软件包了。

4.1.4 构建 gcc 和 gcc 依赖的软件包

最后一次安装 gcc,首先要装得自然还是 GMP、MPFR、MPC,区别于前两次只是将软件包解压并复制到 gcc 软件包目录中,这次是直接构建。这三个软件包的依赖已经全部在第 3 章交叉编译完了。只需要按照自身的依赖顺序构建即可。

gcc 的测试需要用到 Shadow,所以需要先行构建 Shadow 的依赖 acl、attr 以及 libcap,acl、attr 和 libcap 三者之间也存在依赖关系,所以要以 attr、acl、libcap 的顺序构建。

按理说 GCC 的构建,除了这三个软件包,在第 3 章中没有交叉编译的就只有 zstd 了,而在 Binutils 安装前,已经伴随着一众压缩相关的软件包,完成了安装任务。不过,GCC 的测试套件需要 Shadow,由此也就必须安装 Shadow 的依赖的软件包了。

1. 安装 GMP:GNU 多重精度运算库

大致构建用时: 1.1 SBU 所需磁盘空间: 52 MB 解压并进入软件包: tar -xf gmp-6.2.0.tar.xz cd gmp-6.2.0 如果为 32 位的 x86 系统编译,但是 CPU 可以运行 64 位代码,并且环境中有指定的 CFLAGS,那么配置脚本会尝试配置为 64 位从而导致失败。用以下方式执行配置命令来避免这个问题: ABI=32 ./configure ... GMP 的默认设定会为主机的处理器优化库。如果你不需要完美符合主机 CPU 的库,可以通过下方命令创建通用库,这样的话契合度会差一些:

cp -v configfsf.guess config.guess
cp -v configfsf.sub   config.sub

准备编译 GMP:

./configure --prefix=/usr    \
            --enable-cxx     \
            --disable-static \
            --docdir=/usr/share/doc/gmp-6.1.2

配置选项的含义如表 4-6 所示。

表 4-3 GMP 配置选项
配置选项含义
--enable-cxx这个参数启用 C++ 支持
--docdir=/usr/share/doc/gmp-6.1.2这个变量指定保存文档的正确位置

编译软件包并生成 HTML 文档:

make
make html

注意:该章节 GMP 的测试套件至关重要,任何情况下都不能跳过 查看结果:

make check 2>&1 | tee gmp-check-log

GMP 中的代码对于其构建的处理器进行了高度优化。有时检测处理器的代码会误认系统的功能,并在测试中报错,或在其他应用使用 GMP 库的时候显示消息「Illegal instruction(非法指令)」。在这种情况下,GMP 需要重新配置选项--build=x86_64-unknown-linux-gnu 并重新构建。 确认测试套件中所有的 190 个测试都通过了,并通过输入下面的命令检查结果:

awk '/# PASS:/{total+=$3} ; END{print total}' gmp-check-log

安装软件包和文档:

make install
make install-html

退出并清理软件包:

cd ..
rm -rf gmp-6.2.0

2. 安装 MPFR:多精度浮点可靠运算库

  • 大致构建用时: 0.9 SBU
  • 所需磁盘空间: 38 MB

解压并进入软件包:

tar -xf mpfr-4.1.0.tar.xz
cd 	mpfr-4.1.0

准备编译 MPFR:

./configure --prefix=/usr        \
            --disable-static     \
            --enable-thread-safe \
            --docdir=/usr/share/doc/mpfr-4.1.0

编译软件包并生成 HTML 文档:

make
make html

该章节 MPFR 的测试套件至关重要,任何情况下都不能跳过。 检查结果确认通过了所有的测试:

make check

安装软件包以及文档:

make install
make install-html

退出并清理软件包:

cd ..
rm -rf mpfr-4.1.0
  1. 安装 MPC:高精度复数运算库
  • 大致构建用时: 0.3 SBU
  • 所需磁盘空间: 22 MB

解压并进入软件包:

tar -xf mpc-1.1.0.tar.gz
cd mpc-1.1.0

准备编译 MPC:

./configure --prefix=/usr    \
            --disable-static \
            --docdir=/usr/share/doc/mpc-1.1.0

编译软件包并生成 HTML 文档:

make
make html

用以下命令检查结果:

make check

安装软件包及其帮助文档:

make install
make install-html

退出并清理软件包:

cd ..
rm -rf mpc-1.1.0

4. 安装 attr:用于管理文件系统扩展属性的实用程序

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 4.2 MB 解压并进入软件包:
tar -xf attr-2.4.48.tar.gz
cd 	attr-2.4.48

准备编译 Attr:

./configure --prefix=/usr     \
            --bindir=/bin     \
            --disable-static  \
            --sysconfdir=/etc \
            --docdir=/usr/share/doc/attr-2.4.48

编译软件包:

make

测试需要在支持扩展属性的文件系统上运行,例如 ext2、ext3 或者 ext4。如果同时运行多个测试则会导致测试失败(-j 选项大于 1)。输入命令检查结果:

make check

安装软件包:

make install

需要移动共享库到/lib,因此需要重建.so 中的/usr/lib 文件:

mv -v /usr/lib/libattr.so.* /lib
ln -sfv ../../lib/$(readlink /usr/lib/libattr.so) /usr/lib/libattr.so

退出并清理软件包:

cd ..
rm -rf attr-2.4.48
  1. 安装 acl:访问控制列表 大致构建用时:少于 0.1 SBU 所需磁盘空间: 6.4 MB 解压并进入软件包:
tar -xf acl-2.2.53.tar.gz
cd acl-2.2.53

准备编译 Acl:

./configure --prefix=/usr         \
            --bindir=/bin         \
            --disable-static      \
            --libexecdir=/usr/lib \
            --docdir=/usr/share/doc/acl-2.2.53

编译软件包:

make

在用 Acl 库构建 Coreutils 后,Acl 测试才能在支持访问控制的文件系统上运行。如果需要的话,可以在本章后面构建 Coreutils 之后回到这个软件包,并运行 make check 进行测试。 安装软件包:

make install

需要移动共享库到/lib,因此需要重建/usr/lib 中的.so 文件:

mv -v /usr/lib/libacl.so.* /lib
ln -sfv ../../lib/$(readlink /usr/lib/libacl.so) /usr/lib/libacl.so

退出并清理软件包:

cd ..
rm -rf acl-2.2.53

6. 安装 libcap

用于兼容 POSIX 1003.1e 的库。

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 11 MB 解压并进入软件包:
tar -xf libcap-2.42.tar.xz
cd libcap-2.42

防止安装静态库:

sed -i '/install -m.*STACAPLIBNAME/d' libcap/Makefile

编译软件包:

make lib=lib

用以下命令测试结果:

make test

安装软件包:

make lib=lib PKGCONFIGDIR=/usr/lib/pkgconfig install
chmod -v 755 /lib/libcap.so.2.42
mv -v /lib/libpsx.a /usr/lib
rm -v /lib/libcap.so
ln -sfv ../../lib/libcap.so.2 /usr/lib/libcap.so

退出并清理软件包:

cd ..
rm -rf libcap-2.42

7. 安装 Shadow:安全的密码管理程序

  • 大致构建用时: 0.2 SBU
  • 所需磁盘空间: 45 MB 解压并进入软件包:
tar -xf shadow-4.8.1.tar.xz
cd 	shadow-4.8.1

由于 Coreutils 已经提供了更好的版本,因此禁用对 groups 程序以及相应 man 手册的安装。同时避免安装第 4.2.2 节安装过的手册页:

sed -i 's/groups$(EXEEXT) //' src/Makefile.in
find man -name Makefile.in -exec sed -i 's/groups\.1 / /'   {} \;
find man -name Makefile.in -exec sed -i 's/getspnam\.3 / /' {} \;
find man -name Makefile.in -exec sed -i 's/passwd\.5 / /'   {} \;

比起默认的 crypt 方法,用更安全的 SHA-512 方法加密密码,它允许密码长度超过 8 个字符。也需要把 Shadow 默认使用的用户邮箱由陈旧的/var/spool/mail 位置改为正在使用的/var/mail 位置:

sed -e 's:#ENCRYPT_METHOD DES:ENCRYPT_METHOD SHA512:' \
    -e 's:/var/spool/mail:/var/mail:'                 \
    -i etc/login.defs

做一个小改动,用 useradd 1000 生成第一个组号:

sed -i 's/1000/999/' etc/useradd

准备编译 Shadow:

touch /usr/bin/passwd
./configure --sysconfdir=/etc \
            --with-group-name-max-length=32

在上面的代码中,通过--with-group-name-max-length=32 将用户名和组名的长度限制在 32 个字符以下。 编译软件包:

make

安装软件包:

make install

8. 配置 Shadow

该软件包包含:① 增加、更改以及删除用户和组的工具;② 设置和修改密码;③ 执行其他特权级任务。软件包解压后的 doc/HOWTO 文件有关于 password shadowing 的完整解释。如果使用 Shadow 支持,记住需要验证密码(显示管理器、FTP 程序、pop3 守护进程等)的程序必须和 Shadow 兼容。也就是说,它们要能使用 Shadow 加密的密码。 运行下面的命令启用 Shadow 密码:

pwconv

运行下面的命令启用 shadow 组密码:

grpconv

设置 root 密码:

passwd root

退出并清理软件包:

cd ..
rm -rf shadow-4.8.1

9. 安装 gcc

  • 大致构建用时: 102 SBU (包含测试)
  • 所需磁盘空间: 4.6 GB 解压并进入软件包:
tar -xf gcc-10.2.0.tar.xz
cd gcc-10.2.0

如果是在 x86_64 上实施构建,更改 64 位库的默认目录名为 lib:

case $(uname -m) in
  x86_64)
    sed -e '/m64=/s/lib64/lib/' \
        -i.orig gcc/config/i386/t-linux64
  ;;
esac

按照 gcc 的文档建议,创建一个专门用于编译目录:

mkdir -v build
cd       build

准备编译 GCC:

../configure --prefix=/usr            \
             LD=ld                    \
             --enable-languages=c,c++ \
             --disable-multilib       \
             --disable-bootstrap      \
             --with-system-zlib

注意,对于其他的编程语言,现在还有一些前提条件没有准备好。可以查看 BLFS 手册了解如何编译 gcc 支持的所有语言的指令。

配置选项的含义,如表 4-7 所示。

表 4-4 GCC 配置选项
配置选项含义
LD=ld指定使用第 4 章中安装的 ld,而非交叉编译中构建的 ld
--with-system-zlib告诉 GCC 链接系统安装的 Zlib 库,而不是它内部自带的库

编译软件包:

make

注意:gcc 的测试套件至关重要,任何情况下都不能跳过。 gcc 测试套件中一个测试集的会耗尽堆空间,因此运行测试之前要增加堆大小:

ulimit -s 32768

以非特权用户测试编译结果,不要因为出现错误就停下来:

chown -Rv tester .
su tester -c "PATH=$PATH make -k check"

要查看测试套件结果的概要,运行以下命令:

../contrib/test_summary

如果仅查看摘要,则使用管道 grep -A7 Summ 过滤输出内容:

../contrib/test_summary | grep -A7 Summ

以上的输出结果可以和 http://www.linuxfromscratch.org/lfs/build-logs/10.0/以及 http://gcc.gnu.org/ml/gcc-testresults/上的数据相比较,查看输出的差异。

已知与 get_time 相关的 6 个测试失败。这些显然与 en_HK 语言环境有关。

两个 experimental/net 的测试 lookup.cc 和 reverse.cc 已知会在 LFS chroot 环境失败,因为它们需要/etc /hosts 和 iana-etc。

两个测试 pr57193.c 和 pr90178.c 已知会失败。

一些意料之外的错误总是难以避免。gcc 开发者通常会意识到这些问题,但还没有解决。除非测试结果和上面 URL 中的相差很大,不然就可以安全继续。

在某些内核配置和 AMD 处理器组合的时候,肯能会在 gcc.target/i386/mpx 测试(旨在测试最新英特尔处理器上的 MPX 选项)中出现 1100 个失败。AMD 处理器的话可以安心的忽略这些。

安装软件包并删除不需要的目录:

make install
rm -rf /usr/lib/gcc/$(gcc -dumpmachine)/10.2.0/include-fixed/bits/

GCC 的构建目录现在属于 nobody,安装头文件(及其内容)目录的所有权不正确,因此需要将所有权更改为 root 用户和组:

chown -v -R root:root \
    /usr/lib/gcc/*linux-gnu/10.2.0/include{,-fixed}

因为「历史」原因而需要创建 FHS 的软链接。

ln -sv ../usr/bin/cpp /lib

很多软件包用命令 cc 调用 C 编译器。为了满足这些软件包,创建一个符号链接:

ln -sv gcc /usr/bin/cc

增加一个兼容符号链接,启用编译程序时进行链接时间优化(Link Time Optimization,LTO):

install -v -dm755 /usr/lib/bfd-plugins
ln -sfv ../../libexec/gcc/$(gcc -dumpmachine)/8.2.0/liblto_plugin.so \
        /usr/lib/bfd-plugins/

现在最终的工具链已经准备就绪了,再一次确认编译和链接都能正常工作,这很重要。我们通过做和前面章节做过的相同的完整性检查做到这点:

echo 'int main(){}' > dummy.c
cc dummy.c -v -Wl,--verbose &> dummy.log
readelf -l a.out | grep ': /lib'

如果没有任何错误,上条命令的输出(不同的平台上的动态链接器可能名字不同)应该如下:

[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

现在确保已经设置好了启动文件:

grep -o '/usr/lib.*/crt[1in].*succeeded' dummy.log

上一条命令的输出应该是:

/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib/crt1.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib/crti.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib/crtn.o succeeded

上面的结果可能有稍微不同,这取决于你使用的机器的架构,差异通常在于/usr/lib/gcc 后目录的名称。注意,gcc 能在/usr/lib 目录下找到所有的 3 个 crt*.o 文件。 确保链接器能找到正确的头文件:

grep -B4 '^ /usr/include' dummy.log

这条命令应该返回如下输出:

#include <...> search starts here:
 /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include
 /usr/local/include
 /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include-fixed
 /usr/include

同时,注意目标系统三段式后面的目录名称可能和上面的不同,这取决于你的架构。 接下来确认新的链接器已经在使用正确的搜索路径:

grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'

应该忽略指向带有'-linux-gnu'的路径,上条命令的输出应该是:

SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib64")
SEARCH_DIR("/usr/local/lib64")
SEARCH_DIR("/lib64")
SEARCH_DIR("/usr/lib64")
SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib")
SEARCH_DIR("/usr/local/lib")
SEARCH_DIR("/lib")
SEARCH_DIR("/usr/lib");

32 位的系统可能有一些不同的目录。例如,下面是一台 i686 机器的输出:

SEARCH_DIR("/usr/i686-pc-linux-gnu/lib32")
SEARCH_DIR("/usr/local/lib32")
SEARCH_DIR("/lib32")
SEARCH_DIR("/usr/lib32")
SEARCH_DIR("/usr/i686-pc-linux-gnu/lib")
SEARCH_DIR("/usr/local/lib")
SEARCH_DIR("/lib")
SEARCH_DIR("/usr/lib");

然后要确定我们使用的是正确的 libc:

grep "/lib.*/libc.so.6 " dummy.log

上条命令的输出应该为:

attempt to open /lib/libc.so.6 succeeded

最后,确保 GCC 使用的是正确的动态链接器:

grep found dummy.log

上条命令的结果应该是(不同的平台上链接器名字可以不同):

found ld-linux-x86-64.so.2 at /lib/ld-linux-x86-64.so.2

如果显示的结果不一样或者根本没有显示,那就出了大问题。检查并回溯之前的步骤,找到出错的地方并改正。最有可能的原因是参数文件的调整出了问题。在进行下一步之前所有的问题都要解决。 移动放错位置的参数文件:

mkdir -pv /usr/share/gdb/auto-load/usr/lib
mv -v /usr/lib/*gdb.py /usr/share/gdb/auto-load/usr/lib

退出并清理软件包:

cd ../..
rm -rf gcc-10.2.0

4.1.5 bash

1. 安装 pkg-config:编译器选择

  • 大致构建用时: 0.3 SBU
  • 所需磁盘空间: 30 MB 解压并进入软件包:
tar -xf pkg-config-0.29.2.tar.gz
cd 	pkg-config-0.29.2

准备编译 Pkg-config:

./configure --prefix=/usr              \
            --with-internal-glib       \
            --disable-host-tool        \
            --docdir=/usr/share/doc/pkg-config-0.29.2

配置选项的含义如表 4-8 所示。

表 4-5 pkg-config 配置选项
配置选项含义
--with-internal-glib让 pkg-config 使用它自己内部版本的 Glib,因为在 LFS 中没有可用的外部版本
--disable-host-tool此选项取消创建到 pkg-config 程序的不必要的硬链接

编译软件包:

make

用以下命令检查结果:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf pkg-config-0.29.2

2. 安装 Ncurses:字符终端处理库

  • 大致构建用时: 0.4 SBU
  • 所需磁盘空间: 33 MB 解压并进入软件包:
tar -xf ncurses-6.2.tar.gz
cd 	ncurses-6.2

不要安装静态库,它不受配置控制:

sed -i '/LIBTOOL_INSTALL/d' c++/Makefile.in

准备编译 Ncurses:

./configure --prefix=/usr           \
            --mandir=/usr/share/man \
            --with-shared           \
            --without-debug         \
            --without-normal        \
            --enable-pc-files       \
            --enable-widec

配置选项的含义,如表 4-9 所示。

配置选项含义
--enable-widec编译宽字符库(例如 libncursesw.so.6.1)来代替普通字符库(例如 libncurses.so.6.1)。宽字符库可用于多字节和传统 8 位本地字符,而常规的库只能用于 8 位本地字符。宽字符库和常规的库是源文件兼容的,而不是二进制文件兼容的
--enable-pc-files为 pkg-config 生成和安装.pc 文件
--without-normal用于禁用构建和安装大多数的静态库

编译软件包:

make

该软件包有个测试套件,但只能在安装完软件包后运行。测试程序在 test/目录中。查看该目录中的 README 文件获取更详细信息。 安装软件包:

make install

移动共享库到期望的/lib 文件夹:

mv -v /usr/lib/libncursesw.so.6* /lib

由于库已经被移走了,符号链接指向了一个不存在的文件。重建符号链接:

ln -sfv ../../lib/$(readlink /usr/lib/libncursesw.so) /usr/lib/libncursesw.so

很多应用程序仍然希望编辑器能找到非宽字符的 Ncurses 库,比如通过符号链接和链接器脚本欺骗应用程序链接到宽字符库:

for lib in ncurses form panel menu ; do
    rm -vf                    /usr/lib/lib${lib}.so
    echo "INPUT(-l${lib}w)" > /usr/lib/lib${lib}.so
    ln -sfv ${lib}w.pc        /usr/lib/pkgconfig/${lib}.pc
done

确保在编译时会查找-lcurses 的旧应用程序仍然可以编译:

rm -vf                     /usr/lib/libcursesw.so
echo "INPUT(-lncursesw)" > /usr/lib/libcursesw.so
ln -sfv libncurses.so      /usr/lib/libcurses.so

如果需要的话,安装 Ncurses 的帮助文档:

mkdir -v       /usr/share/doc/ncurses-6.2
cp -v -R doc/* /usr/share/doc/ncurses-6.2

上面的指令并不会创建非宽字符 Ncurses 库,因为没有从源文件中编译安装的软件包会在运行时再链接它们。然而,已知的仅有二进制应用程序并能链接到非等宽字符的库,需要第 5 版的支持。如果你由于一些仅有二进制的应用程序或要和 LSB 兼容而必须要有这样的库,用下面的命令重新编译软件包:

make distclean
./configure --prefix=/usr    \
            --with-shared    \
            --without-normal \
            --without-debug  \
            --without-cxx-binding \
            --with-abi-version=5
make sources libs
cp -av lib/lib*.so.5* /usr/lib

退出并清理软件包:

cd ..
rm -rf ncurses-6.2

3. 安装 Sed:流编辑器

  • 大致构建用时: 0.3 SBU
  • 所需磁盘空间: 32 MB 解压并进入软件包:
tar -xf sed-4.8
cd 	sed-4.8

准备编译 Sed:

./configure --prefix=/usr --bindir=/bin

编译软件包并生成 HTML 文档:

make
make html

用 tester 用户运行测试:

chown -Rv tester .
su tester -c "PATH=$PATH make check"v

安装软件包和它的文档:

make install
install -d -m755           /usr/share/doc/sed-4.8
install -m644 doc/sed.html /usr/share/doc/sed-4.8

退出并清理软件包:

cd ..
rm -rf sed-4.8

4. 安装 Psmisc:用于列出运行进程的信息

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 4.8 MB 解压并进入软件包:
tar -xf psmisc-23.3.tar.xz
cd 	psmisc-23.3

准备编译 Psmisc:

./configure --prefix=/usr

编译软件包:

make

该软件包没有测试套件。 安装软件包:

make install

将程序 killall 和 fuser 移动到 FHS 指定的位置:

mv -v /usr/bin/fuser   /bin
mv -v /usr/bin/killall /bin

退出并清理软件包:

cd ..
rm -rf psmisc-23.3

5. 安装 Gettext:国际化和本地化(i18n)系统

  • 大致构建用时: 3.2 SBU
  • 所需磁盘空间: 240MB 解压并进入软件包:
tar -xf gettext-0.21.tar.xz
cd gettext-0.21

准备编译 Gettext:

./configure --prefix=/usr    \
            --disable-static \
            --docdir=/usr/share/doc/gettext-0.21

编译软件包:

make

用以下命令测试结果(需要较长一段时间,大概 3 SBUs):

make check

安装软件包:

make install
chmod -v 0755 /usr/lib/preloadable_libintl.so

退出并清理软件包:

cd ..
rm -rf gettext-0.20.1

6. 安装 Bison:语法生成器

  • 大致构建用时: 6.7 SBU
  • 所需磁盘空间: 54 MB 解压并进入软件包:
tar -xf bison-3.7.1.tar.xz
cd bison-3.7.1

准备编译 Bison:

./configure --prefix=/usr --docdir=/usr/share/doc/bison-3.3.2

编译该软件包:

make

考虑到 Bison 和 Flex 的检查有循环依赖。如有需要,在下一节安装 Flex 之后,可以使用 make check 命令重新编译并检查 Bison 软件包。 测试可能会花费(大约 5.5 SBU):

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf bison-3.7.1

7. 安装 Grep:搜索行的纯文本数据集

  • 大致构建用时: 0.9 SBU
  • 所需磁盘空间: 37 MB 解压并进入软件包:
tar -xf grep-3.4.tar.xz
cd grep-3.4

准备编译 Grep :

./configure --prefix=/usr --bindir=/bin

编译软件包:

make

用以下命令测试结果:

make -k check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf grep-3.4

8. 安装 Bash:最常用的 shell

  • 大致构建用时: 2.4 SBU
  • 所需磁盘空间: 48 MB 解压并进入软件包:
tar -xf bash-5.0.tar.gz
cd bash-5.0

首先合并上游的修复:

patch -Np1 -i ../bash-5.0-upstream_fixes-1.patch

准备编译 Bash:

./configure --prefix=/usr                    \
            --docdir=/usr/share/doc/bash-5.0 \
            --without-bash-malloc            \
            --with-installed-readline

其中的新选项--with-installed-readline 用于告知 Bash 使用系统中已经安装的 readline 库,而不是使用自带的 readline 版本。 编译软件包:

make

如果不需要运行测试套件的话跳转到“安装软件包”。 准备测试,确保 tester 用户可以写源文件树:

chown -Rv tester .

现在,以 nobody 用户身份运行测试:

su tester << EOF
PATH=$PATH make tests < $(tty)
EOF

安装软件包并将主要的可执行文件移动至/bin:

make install
mv -vf /usr/bin/bash /bin

运行新编译的 Bash(替换正在运行的那个):

exec /bin/bash --login +h

参数使 bash 进程成为一个可交互的登录 shell 并停用散列使得新程序可用的时候就能发现。 退出并清理软件包:

cd ..
rm -rf bash-5.0

4.1.5 PERL 和 Python

1. 安装 Libtool:创建可移植编译库的工具

  • 大致构建用时: 1.5 SBU
  • 所需磁盘空间: 43 MB 解压并进入软件包:
tar -xf libtool-2.4.6.tar.xz
cd libtool-2.4.6

准备编译 Libtool:

./configure --prefix=/usr

编译软件包:

make

用以下命令测试结果:

make check

在具有多个内核的系统上,libtool 的测试时间可以显著削减。为此,请在上面那行命令中添加 TESTSUITEFLAGS=-j。例如,使用-j4 可能减少 60% 的测试时间。 在 LFS 构建环境中会有 5 个测试由于循环依赖会失败,但如果安装完 automake 之后重新检查,所有测试就都能通过。 安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf libtool-2.4.6

2. 安装 GDBM:数据库功能库

  • 大致构建用时: 0.1 SBU
  • 所需磁盘空间: 11 MB 解压并进入软件包:
tar -xf gdbm-1.18.1.tar.gz
cd gdbm-1.18.1

首先,修复 gcc 10 带来的问题:

sed -r -i '/^char.*parseopt_program_(doc|args)/d' src/parseopt.c

准备编译 GDBM:

./configure --prefix=/usr    \
            --disable-static \
            --enable-libgdbm-compat

新选项--enable-libgdbm-compat 用于启用 libgdbm 兼容性库,因为一些 LFS 之外的软件包可能需要它提供的旧的 DBM 例程。 编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf gdbm-1.18.1

3. 安装 Gperf:完美哈希函数生成器

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 6.4 MB 解压并进入软件包:
tar -xf gperf-3.1.tar.gz
cd gperf-3.1

准备编译 Gperf:

./configure --prefix=/usr --docdir=/usr/share/doc/gperf-3.1

编译软件包:

make

该测试已知在运行多任务同时测试(即-j 选项大于 1)时会失败。用以下命令测试结果:

make -j1 check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf gperf-3.1

4. 安装 Expat:面向流的 XML 1.0 解析器库

  • 大致构建用时: 0.1 SBU
  • 所需磁盘空间: 14 MB 解压并进入软件包:
tar -xf expat-2.2.9.tar.bz2
cd expat-2.2.9

准备编译 Expat:

./configure --prefix=/usr    \
            --disable-static \
            --docdir=/usr/share/doc/expat-2.2.9

编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

如果需要的话,安装帮助文档:

install -v -m644 doc/*.{html,png,css} /usr/share/doc/expat-2.2.9

退出并清理软件包:

cd ..
rm -rf expat-2.2.9

5. 安装 Inetutils:GNU 网络实用程序

  • 大致构建用时: 0.3 SBU
  • 所需磁盘空间: 29 MB 解压并进入软件包:
tar -xf inetutils-1.9.4.tar.xz
cd inetutils-1.9.4

准备编译 Inetutils:

./configure --prefix=/usr        \
            --localstatedir=/var \
            --disable-logger     \
            --disable-whois      \
            --disable-rcp        \
            --disable-rexec      \
            --disable-rlogin     \
            --disable-rsh        \
            --disable-servers

配置选项的含义,如表 4-11 所示。

表 4-7 Inetutils 配置选项
配置选项含义
--disable-logger防止 Inetutils 安装 logger 程序,脚本使用该程序传递消息到系统日志守护进程。因为 Util-linux 安装了一个更新版本,因此不能安装这个
--disable-whois禁用编译过时的 Inetutils whois 客户端。BLFS 指南中有更好的 whois 客户端说明
--disable-r*为了安全,使编译过时的程序不能被使用。提供该功能的程序在手册 BLFS 中的 openssh 会有所提及
--disable-servers禁用安装作为 Inetutils 软件包一部分的多种网络服务程序。这些服务程序被认为不适用于基础的 LFS 系统。其中的一些本来就不安全,或者说仅在可信网络中才被认为安全。注意这些服务程序有更好的可用替代品

编译软件包:

make

用以下命令测试结果:

make check

测试 libls.sh 可能会在初始的 chroot 环境中失败,但是在 LFS 系统构建完成后重新运行就会通过了。测试 ping-localhost.sh 会因为宿主系统不支持 IPv6 而失败。 安装软件包:

make install

移动一些程序使得/usr 在不可访问时仍保持可用:

mv -v /usr/bin/{hostname,ping,ping6,traceroute} /bin
mv -v /usr/bin/ifconfig /sbin

退出并清理软件包:

cd ..
rm -rf inetutils-1.9.4

6. 安装 PERL:实用的提取和报告语言

  • 大致构建用时: 11 SBU
  • 所需磁盘空间: 222 MB 解压并进入软件包:
tar -xf perl-5.32.0.tar.xz
cd perl-5.32.0

该版本的 Perl 会编译 Compress::Raw::Zlib 和 Compress::Raw::BZip2 模块。Perl 默认会使用内部的源码用于构建。用以下的命令使 Perl 使用系统中已安装的库:

export BUILD_ZLIB=False
export BUILD_BZIP2=0

为了能完全控制 Perl 的设置,你可以在下面的命令中移除【-des】选项并手动设置编译该软件包的方式。相应的,用下面的命令来使用 Perl 自动检测到的默认值:

sh Configure -des                                         \
             -Dprefix=/usr                                \
             -Dvendorprefix=/usr                          \
             -Dprivlib=/usr/lib/perl5/5.32/core_perl      \
             -Darchlib=/usr/lib/perl5/5.32/core_perl      \
             -Dsitelib=/usr/lib/perl5/5.32/site_perl      \
             -Dsitearch=/usr/lib/perl5/5.32/site_perl     \
             -Dvendorlib=/usr/lib/perl5/5.32/vendor_perl  \
             -Dvendorarch=/usr/lib/perl5/5.32/vendor_perl \
             -Dman1dir=/usr/share/man/man1                \
             -Dman3dir=/usr/share/man/man3                \
             -Dpager="/usr/bin/less -isR"                 \
             -Duseshrplib                                 \
             -Dusethreads

配置选项的含义:

配置选项含义
-Dvendorprefix=/usr这能确保 perl 知道,该如何告知软件包应该将它们的 perl 模块安装在哪里
-Dpager="/usr/bin/less -isR"这能确保使用的是 less 而非 more
-Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3由于 Groff 还没有安装,Configure 会认为我们不希望为 Perl 安装 man 手册。用这些参数更改这个判断
-Duseshrplib构建某些 perl 模块需要的共享 libperl
-Dusethreads构建支持线程的 perl

编译软件包:

make

用以下命令测试结果(大概 11 SBU):

make test

安装软件包并清理:

make install
unset BUILD_ZLIB BUILD_BZIP2

退出并清理软件包:

cd ..
rm -rf perl-5.32.0

7. 安装 XML::Parser:解析 XML 文件的 Perl 模块

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 2.4 MB 解压并进入软件包:
tar -xf XML-Parser-2.46.tar.gz
cd XML-Parser-2.46

准备编译 XML::Parser:

perl Makefile.PL

编译软件包:

make

用以下命令测试结果:

make test

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf XML-Parser-2.46

8. 安装 Intltool:集中翻译工具

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 1.5 MB

解压并进入软件包:

tar -xf intltool-0.51.0.tar.gz
cd intltool-0.51.0

修复 perl-5.22 和其后版本导致的警告:

sed -i 's:\\\${:\\\$\\{:' intltool-update.in

准备编译 Intltool:

./configure --prefix=/usr

编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install
install -v -Dm644 doc/I18N-HOWTO /usr/share/doc/intltool-0.51.0/I18N-HOWTO

退出并清理软件包:

cd ..
rm -rf intltool-0.51.0

9. 安装 Autoconf:用于生成自动配置脚本的程序

  • 大致构建用时:少于 0.1 SBU(包含测试大于 3.5 SBU)
  • 所需磁盘空间: 79 MB

解压并进入软件包:

tar -xf autoconf-2.69.tar.xz
cd autoconf-2.69

修复一个 Perl 5.28 引入的问题。

sed '361 s/{/\\{/' -i bin/autoscan.in

准备编译 Autoconf:

./configure --prefix=/usr

编译软件包:

make

由于 bash-5 和 libtool-2.4.3 的原因,测试套件当前无法使用。如果要强行测试,输入:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf autoconf-2.69

10. 安装 Automake:生成 make 文件的程序

大致构建用时:少于 0.1 SBU(包含测试大于 9.6SBU)
所需磁盘空间: 108 MB

解压并进入软件包:

tar -xf automake-1.16.2.tar.xz
cd automake-1.16.2

解决可能导致测试失败的问题:

sed -i "s/''/etags/" t/tags-lisp-space.sh

准备编译 Automake:

./configure --prefix=/usr --docdir=/usr/share/doc/automake-1.16.2

编译软件包:

make

因为各个测试之间存在内部延时,故建议就算是在单核处理器的设备上,也使用-j4 编译选项加速测试过程。用以下命令测试结果:

make -j4 check

已知 LFS 环境中 subobj.sh 测试会失败。 安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf automake-1.16.2

11. 安装 kmod:加载内核模块的库和工具

  • 大致构建用时: 0.1 SBU
  • 所需磁盘空间: 13 MB

解压并进入软件包:

tar -xf kmod-27.tar.xz
cd kmod-27

准备编译 kmod:

./configure --prefix=/usr          \
            --bindir=/bin          \
            --sysconfdir=/etc      \
            --with-rootlibdir=/lib \
            --with-xz              \
            --with-zlib

配置选项的含义:

表 4-9 kmod 配置选项
配置项目描述
--with-xz, --with-zlib使 Kmod 能处理压缩的内核模块。
--with-rootlibdir=/lib确保和不同库相关的文件放置到正确的目录。

编译软件包:

make

这个软件包没有附带可在 LFS chroot 环境中的运行测试套件。你至少需要 git 程序并进行一些测试以保证不会在 git 仓库外运行。 安装软件包并创建符号链接使兼容 Module-Init-Tools(之前处理 Linux 内核模块的软件包):

make install

for target in depmod insmod lsmod modinfo modprobe rmmod; do
  ln -sfv ../bin/kmod /sbin/$target
done

ln -sfv kmod /bin/lsmod

退出并清理软件包:

cd ..
rm -rf kmod-27

12. 安装 libelf:处理 ELF 文件的库

  • 大致构建用时: 1.3 SBU
  • 所需磁盘空间: 105 MB 解压并进入软件包:
tar -xf elfutils-0.180.tar.bz2
cd elfutils-0.180

libelf 是软件包 elfutils-0.180 中的一部分。使用 elfutils-0.180.tar.bz2 作为源码包。 编译 libelf 前的准备:

./configure --prefix=/usr --disable-debuginfod --libdir=/lib

编译软件包:

make

用以下命令测试结果:

make check

仅安装 libelf:

make -C libelf install
install -vm644 config/libelf.pc /usr/lib/pkgconfig
rm /lib/libelf.a

退出并清理软件包:

cd ..
rm -rf elfutils-0.180

13. 安装 libffi:外部功能接口库

  • 大致构建用时: 2.0 SBU
  • 所需磁盘空间: 10 MB 解压并进入软件包:
tar -xf libffi-3.3.tar.gz
cd libffi-3.3

Libffi 与 GMP 相似,构建时会根据使用的处理器优化。如果需要构建的是另一个系统,设定 CFLAGS 和 CXXFLAGS 为你的架构指定成通用构建。如果不这样做,所有指向 Libffi 的链接将触发非法操作错误。 修改 Makefile 将头文件安装到标准的 /usr/include 目录,而非 /usr/lib/libffi-3.2.1/include。

sed -e '/^includesdir/ s/$(libdir).*$/$(includedir)/' \
    -i include/Makefile.in

sed -e '/^includedir/ s/=.*$/=@includedir@/' \
    -e 's/^Cflags: -I${includedir}/Cflags:/' \
    -i libffi.pc.in

编译 Libffi 前的准备:

./configure --prefix=/usr --disable-static --with-gcc-arch=native

配置选项的含义:

配置选项含义
--with-gcc-arch=native用于确保 GCC 为当前系统进行优化 。如果没有指定则会进行猜测,这样生成的代码在某些系统上是不正确的。如果生成的代码想要从本机系统复制到功能较弱的系统,则应该以功能较弱的系统作为参数。有关替代系统类型的详细信息,请参阅 gcc 手册中的 x86 选项

编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf libffi-3.3

14. 安装 OpenSSL:用于实施安全通讯的软件包

  • 大致构建用时: 2.1 SBU
  • 所需磁盘空间: 150MB 解压并进入软件包:
tar -xf openssl-1.1.1g.tar.gz
cd openssl-1.1.1g

编译 OpenSSL 前的准备:

./config --prefix=/usr         \
         --openssldir=/etc/ssl \
         --libdir=lib          \
         shared                \
         zlib-dynamic

编译软件包:

make

查看测试结果,输入:

make test

安装软件包:

sed -i '/INSTALL_LIBS/s/libcrypto.a libssl.a//' Makefile
make MANSUFFIX=ssl install

如果有需要,安装文档:

mv -v /usr/share/doc/openssl /usr/share/doc/openssl-1.1.1g
cp -vfr doc/* /usr/share/doc/openssl-1.1.1g

退出并清理软件包:

cd ..
rm -rf openssl-1.1.1g

15. 安装 Python:广泛使用的高级编程语言

  • 大致构建用时: 1.3 SBU
  • 所需磁盘空间: 248 MB 解压并进入软件包:
tar -xf Python-3.8.5.tar.xz
cd Python-3.8.5

编译 Python 前的准备:

./configure --prefix=/usr       \
            --enable-shared     \
            --with-system-expat \
            --with-system-ffi   \
            --with-ensurepip=yes

配置选项的含义如表 4-14 所示。

表 4-11 Python 配置选项
选项用途
--with-system-expat用于启用 Expat 系统版本的链接。
--with-system-ffi用于启用 libffi 系统版本的链接。
--with-ensurepip=yes用于启用 pip 和 setuptools 打包程序的构建。

编译软件包:

make

测试套件需要 TK 和 X Windows 会话,直至 BLFS 中重新安装 Python 3 之前都执行不了。 安装软件包:

make install
chmod -v 755 /usr/lib/libpython3.7m.so
chmod -v 755 /usr/lib/libpython3.so
ln -sfv pip3.8 /usr/bin/pip3

安装后的 chmod 等指令是用于修复库的权限问题,同其他库保持一致。

如果需要,安装预格式化好的文档:

install -v -dm755 /usr/share/doc/python-3.8.5/html

tar --strip-components=1  \
    --no-same-owner       \
    --no-same-permissions \
    -C /usr/share/doc/python-3.8.5/html \
    -xvf ../python-3.8.5-docs-html.tar.bz2

--no-same-owner 和 --no-same-permissions 用于确保安装文件的归属和权限是正确的。没有的话,运行 tar 时会以上游创建者的身份安装软件包内的文件。 退出并清理软件包:

cd ..
rm -rf Python-3.8.5

4.1.6 最后亿点点

1. 安装 Ninja:专注于速度的小型构件系统

  • 大致构建用时: 0.2 SBU
  • 所需磁盘空间: 78 MB 解压并进入软件包:
tar -xf ninja-1.10.0.tar.gz
cd ninja-1.10.0

在运行时,ninja 通常会并行最大数量的进程。默认值是系统的核心数乘以二。有些时候会导致 CPU 过热,或者内存容量不足。如果是命令行运行,通过传递 -jN 参数可以限制并行的进程数,但是有些软件包虽然潜入了 ninja 的执行却不会传递 -j 参数。 通过使用下方可选过程,让用户能够通过环境变量 NINJAJOBS 来限制并行进程的数量。例如, 设定:

export NINJAJOBS=4

将限制 Ninja 最多仅 4 个进程并行。 如果需要,运行以下命令以添加使用环境变量 NINJAJOBS 的功能:

sed -i '/int Guess/a \
  int   j = 0;\
  char* jobs = getenv( "NINJAJOBS" );\
  if ( jobs != NULL ) j = atoi( jobs );\
  if ( j > 0 ) return j;\
' src/ninja.cc

构建 Ninja:

python3 configure.py --bootstrap

--bootstrap 迫使 Ninja 重新构建自身以适应当前系统。 查看测试结果,输入:

./ninja ninja_test
./ninja_test --gtest_filter=-SubprocessTest.SetWithLots

安装软件包:

install -vm755 ninja /usr/bin/
install -vDm644 misc/bash-completion /usr/share/bash-completion/completions/ninja
install -vDm644 misc/zsh-completion  /usr/share/zsh/site-functions/_ninja

退出并清理软件包:

cd ..
rm -rf ninja-1.10.0
  1. 安装 Meson:软件自动化构建系统
  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 34 MB 解压并进入软件包:
tar -xf meson-0.55.1.tar.gz
cd meson-0.55.1

通过下列命令编译 Meson:

python3 setup.py build

这个软件包还没有测试套件。 安装软件包:

python3 setup.py install --root=dest
cp -rv dest/* /

默认情况下执行命令 python3 setup.py install 会将所有文件(比如 man 手册)安装到 Python Eggs。指定根路径后 setup.py 则会将这些文件安装到标准层次目录。然后只需要复制层次目录,文件即处于标准位置。 退出并清理软件包:

cd ..
rm -rf meson-0.55.1

3. 安装 Coreutils:GNU 核心工具组

  • 大致构建用时: 2.88 SBU
  • 所需磁盘空间: 158 MB 解压并进入软件包:
tar -xf coreutils-8.32.tar.xz
cd coreutils-8.32

POSIX 要求 Coreutils 中的程序即使在多字节语言环境也能正确识别字符边界。下面的补丁修复这个不兼容性以及其他一些和国际化相关的错误。

patch -Np1 -i ../coreutils-8.32-i18n-1.patch

之前在这个补丁中发现了很多错误。当向 Coreutils 维护者报告新错误的时候,请先检查没有该补丁是否可以重现该错误。 抑制测试在某些机器上可能出现的无线循环:

sed -i '/test.lock/s/^/#/' gnulib-tests/gnulib.mk

现在准备编译 Coreutils:

autoreconf -fiv
FORCE_UNSAFE_CONFIGURE=1 ./configure \
            --prefix=/usr            \
            --enable-no-install-program=kill,uptime

配置选项的含义:

表 4-12 Coreutils 配置选项
选项用途
autoreconf更新已有的配置文件,以使其和 automake 最新生成的一致。
FORCE_UNSAFE_CONFIGURE=1允许以 root 用户权限编译软件包。
--enable-no-install-program=kill,uptime的目的是防止 Coreutils 安装其他软件包后面会安装的二进制包。

编译软件包:

make

如果不运行测试套件的话跳到「安装软件包」 现在可以运行测试套件了。首先,运行需要以 root 用户运行的测试:

make NON_ROOT_USERNAME=nobody check-root

我们会以 nobody 用户运行剩下的测试。但是,一些测试要求用户属于多个组。为了不跳过这些测试,我们会添加一个临时的组并添加用户 nobody 作为它的成员:

echo "dummy:x:1000:nobody" >> /etc/group

修复一些权限问题以便非 root 用户可以编译和运行测试:

chown -Rv nobody .

现在运行测试。确保 su 环境中的 PATH 环境变量包含了 /tools/bin。

su nobody -s /bin/bash \
          -c "PATH=$PATH make RUN_EXPENSIVE_TESTS=yes check"

已知测试程序 test-getlogin 在部分构建的系统环境(如 chroot 环境)中会失败,章节结束后运行便会通过。已知测试程序 tty.sh 也会失败。

移除临时组:

sed -i '/dummy/d' /etc/group

安装软件包:

make install

移动程序到 FHS 指定的位置:

mv -v /usr/bin/{cat,chgrp,chmod,chown,cp,date,dd,df,echo} /bin
mv -v /usr/bin/{false,ln,ls,mkdir,mknod,mv,pwd,rm} /bin
mv -v /usr/bin/{rmdir,stty,sync,true,uname} /bin
mv -v /usr/bin/chroot /usr/sbin
mv -v /usr/share/man/man1/chroot.1 /usr/share/man/man8/chroot.8
sed -i s/\"1\"/\"8\"/1 /usr/share/man/man8/chroot.8

mv -v /usr/bin/{head,nice,sleep,touch} /bin

退出并清理软件包:

cd ..
rm -rf coreutils-8.32

4. 安装 check:单元测试框架

  • 大致构建用时: 0.1 SBU(包含测试大于 3.0 SBU)
  • 所需磁盘空间: 12 MB 解压并进入软件包:
tar -xf check-0.15.2.tar.gz
cd check-0.15.2

配置 Check 准备编译:

./configure --prefix=/usr

构建软件包:

make

现在编译完成了。运行 Check 的测试套件,输入以下命令:

make check

注意,Check 的测试套件可能会占用挺长(上至 4 SBU)的时间。 安装软件包,并修复脚本:

make docdir=/usr/share/doc/check-0.15.2 install
sed -i '1 s/tools/usr/' /usr/bin/checkmk

退出并清理软件包:

cd ..
rm -rf check-0.15.2

5. 安装 Diffutils:一组用于显示文本文件之间的差异实用程序

  • 大致构建用时: 0.4 SBU
  • 所需磁盘空间: 33 MB 解压并进入软件包:
tar -xf diffutils-3.7.tar.xz
cd diffutils-3.7

准备编译 Diffutils:

./configure --prefix=/usr

编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf diffutils-3.7

6. 安装 gawk:一种优良的文本处理工具

  • 大致构建用时: 0.5 SBU
  • 所需磁盘空间: 43 MB 解压并进入软件包:
tar -xf gawk-5.1.0.tar.xz
cd gawk-5.1.0

确保哪些不需要的文件没有被安装:

sed -i 's/extras//' Makefile.in

准备编译 Gawk:

./configure --prefix=/usr

编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

如果需要的话,安装帮助文档:

mkdir -v /usr/share/doc/gawk-5.1.0
cp    -v doc/{awkforai.txt,*.{eps,pdf,jpg}} /usr/share/doc/ gawk-5.1.0

退出并清理软件包:

cd ..
rm -rf gawk-5.1.0

7. 安装 gindutils:GNU 操作系统的基本目录搜索实用程序

  • 大致构建用时: 0.8 SBU
  • 所需磁盘空间: 52 MB 解压并进入软件包:
tar -xf findutils-4.7.0.tar.gz
cd findutils-4.7.0

准备编译 Findutils:

./configure --prefix=/usr --localstatedir=/var/lib/locate

配置选项的含义: --localstatedir 用于改变 locate 数据库的位置为 FHS 兼容的 /var/lib/locate。 编译软件包:

make

用以下命令测试结果:

chown -Rv tester .
su tester -c "PATH=$PATH make check"

安装软件包:

make install

一些 BLFS 上的软件包希望 find 命令在/bin,因此确保位置正确:

mv -v /usr/bin/find /bin
sed -i 's|find:=${BINDIR}|find:=/bin|' /usr/bin/updatedb

退出并清理软件包:

cd ..
rm -rf findutils-4.7.0

8. 安装 Groff:文档格式化系统

  • 大致构建用时: 0.5 SBU
  • 所需磁盘空间: 96 MB 解压并进入软件包:
tar -xf groff-1.22.4.tar.gz
cd groff-1.22.4

Groff 希望环境变量 PAGE 包含默认的页面大小,对于美国的用户,为 PAGE=letter,对于其他地方,PAGE=A4 更合适。尽管在编译的时候配置了默认页面大小,后面通过 echo「A4」或「letter」到 /etc/papersize 文件仍然可以修改。 准备编译 Groff:

PAGE=A4 ./configure --prefix=/usr

该软件不支持并行构建。编译软件包:

make -j1

该软件包没有测试套具。 安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf groff-1.22.4

9. 安装 GRUB:GNU 项目的启动引导程序

  • 大致构建用时: 0.8 SBU
  • 所需磁盘空间: 154 MB 解压并进入软件包:
tar -xf grub-2.04.tar.xz
cd grub-2.04

准备编译 GRUB:

./configure --prefix=/usr          \
            --sbindir=/sbin        \
            --sysconfdir=/etc      \
            --disable-efiemu       \
            --disable-werror

新配置选项的含义:

表 4-13 GRUB 配置选项
选项含义
--disable-werror允许忽视有更新 Flex 版本提示的警告以完成构建。
--disable-efiemu这些选项通过停用 LFS 不需要的功能和测试程序最小化构建。

编译软件包:

make

该软件包没有测试套件。 安装软件包:

make install
mv -v /etc/bash_completion.d/grub /usr/share/bash-completion/completions

会在 5.2.2 节使用 GRUB 设置启动过程中介绍 GRUB 的用法,让你通过 GRUB 启动 LFS 系统 退出并清理软件包:

cd ..
rm -rf grub-2.04

10. 安装 less:显示分页器

  • 大致构建用时:少于 0.1 SBU
  • 所需磁盘空间: 4.1 MB 解压并进入软件包:
tar -xf less-551.tar.gz
cd less-551

准备编译 Less:

./configure --prefix=/usr --sysconfdir=/etc

配置选项的含义: --sysconfdir=/etc 用于告知软件包创建的程序在 /etc 中查找配置文件。 编译软件包:

make

该软件包没有测试套件。 安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf less-551

11. 安装 gzip:GNU 文件压缩程序

  • 大致构建用时: 0.1 SBU
  • 所需磁盘空间: 20 MB 解压并进入软件包:
tar -xf gzip-1.10.tar.xz
cd gzip-1.10

准备编译 Gzip:

./configure --prefix=/usr

编译软件包:

make

用以下命令测试结果:

make check

已知有两个测试在 LFS 环境中会失败:help-version 和 zmore。 安装软件包:

make install

移动一些需要放在根文件系统的程序:

mv -v /usr/bin/gzip /bin

退出并清理软件包:

cd ..
rm -rf gzip-1.10

12. 安装 IPRoute2:用于控制和监视 Linux 内核中网络的各个方面

  • 大致构建用时: 0.2 SBU
  • 所需磁盘空间: 14 MB 解压并进入软件包:
tar -xf iproute2-5.8.0.tar.xz
cd iproute2-5.8.0

此软件包中包含的 arpd 因为依赖于 Berkeley DB,但是此软件并没有包含于 LFS 中,所以将不会进行编译。但是 arpd 的目录依旧会被安装。运行以下的命令来阻止这一动作。如果需要 arpd 的二进制文件,请查看 BLFS Book 的网页 http://www.linuxfromscratch.org/blfs/view/8.4/server/databases.html#db 以了解编译 Berkeley DB 都需要哪些指令。

sed -i /ARPD/d Makefile
rm -fv man/man8/arpd.8

此外,还需要禁用两个模块,它依赖于 http://www.linuxfromscratch.org/blfs/view/10.0/postlfs/iptables.html.

sed -i 's/.m_ipt.o//' tc/Makefile

编译软件包:

make

此软件包不包含可用的测试套件。 安装软件包:

make DOCDIR=/usr/share/doc/iproute2-5.8.0 install

退出并清理软件包:

cd ..
rm -rf iproute2-5.8.0

13. 安装 Kbd:Linux 控制台字体和键表的实用程序

  • 大致构建用时: 0.2 SBU
  • 所需磁盘空间: 32 MB 解压并进入软件包:
tar -xf kbd-2.3.0.tar.xz
cd kbd-2.3.0

在 Kbd 软件包中退格键(Backspace)和删除键(Delete)的行为和键映射并不一致。下面的补丁修复了 i386 键映射中的这个问题:

patch -Np1 -i ../kbd-2.3.0-backspace-1.patch

打补丁后,退格键生成编码为 127 的字符,删除键会生成一个著名的转义序列。 移除冗余的 resizecons 程序(它要求功能不全的 svglib 提供视频模式文件——用于正常使用 setfont 设置控制台字体大小)以及帮助手册。

sed -i 's/\(RESIZECONS_PROGS=\)yes/\1no/g' configure
sed -i 's/resizecons.8 //' docs/man/man8/Makefile.in

准备编译 Kbd:

./configure --prefix=/usr --disable-vlock

--disable-vlock 用于防止编译 vlock 工具,因为它要求 chroot 环境中不可用的 PAM 库。 编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

由于通常使用的 CP1251 键映射假设使用 ISO-8859-5 编码,Kbd 软件包不能为某些语言(例如,白俄罗斯)提供可用的键映射。使用这样的语言需要单独下载能工作的键映射。 如果需要的话,安装帮助文档:

mkdir -v       /usr/share/doc/kbd-2.3.0
cp -R -v docs/doc/* /usr/share/doc/kbd-2.3.0

退出并清理软件包:

cd ..
rm -rf kbd-2.3.0

14. 安装 libpipeline:管理管道的库

  • 大致构建用时: 0.2 SBU
  • 所需磁盘空间: 9.3 MB 解压并进入软件包:
tar -xf libpipeline-1.5.3.tar.gz
cd libpipeline-1.5.3

准备编译 Libpipeline:

./configure --prefix=/usr

编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf libpipeline-1.5.3

15. 安装 make:生成可执行文件和其他非源码程序

  • 大致构建用时: 0.6 SBU
  • 所需磁盘空间: 13 MB 解压并进入软件包:
tar -xf make-4.3.tar.bz2
cd make-4.3

准备编译 make:

./configure --prefix=/usr

编译软件包:

make

测试套件需要知道支持 perl 文件的位置。我们使用一个环境变量来达成这个目的。用以下命令测试结果:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf make-4.3

16. 安装 patch:更新文本文件

  • 大致构建用时: 0.2 SBU
  • 所需磁盘空间: 12 MB 解压并进入软件包:
tar -xf patch-2.7.6.tar.xz
cd patch-2.7.6

准备编译 Patch:

./configure --prefix=/usr

编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

退出并清理软件包:

cd ..
rm -rf patch-2.7.6

17. 安装 man-db:参考手册的数据库和浏览器

  • 大致构建用时: 0.5 SBU
  • 所需磁盘空间: 40 MB 解压并进入软件包:
tar -xf man-db-2.9.3.tar.xz
cd man-db-2.9.3

准备编译 Man-DB: System

sed -i '/find/s@/usr@@' init/systemd/man-db.service.in

./configure --prefix=/usr                        \
            --docdir=/usr/share/doc/man-db-2.9.3 \
            --sysconfdir=/etc                    \
            --disable-setuid                     \
            --enable-cache-owner=bin             \
            --with-browser=/usr/bin/lynx         \
            --with-vgrind=/usr/bin/vgrind        \
            --with-grap=/usr/bin/grap

sysv

./configure --prefix=/usr                        \
            --docdir=/usr/share/doc/man-db-2.9.3 \
            --sysconfdir=/etc                    \
            --disable-setuid                     \
            --enable-cache-owner=bin             \
            --with-browser=/usr/bin/lynx         \
            --with-vgrind=/usr/bin/vgrind        \
            --with-grap=/usr/bin/grap            \
            --with-systemdtmpfilesdir=           \
            --with-systemdsystemunitdir=

配置选项的含义如表 4-17 所示。

选项含义

sed -i '/find/s@/usr@@' init/systemd/man-db.service.in | 将硬编码路径更改为安装在 /bin 中的 find 实用程序 --disable-setuid | 为用户 man 禁止 man --enable-cache-owner=bin | 用于将系统范围内的 cache 文件的拥有者设置为 bin 用户 --with-... | 这些参数用于设置一些默认程序。lynx 是一个基于文本的网络浏览器(查看 BLFS 获取安装指令),vgrind 将程序源码转换为 Groff 输入,grap 在 Groof 文档排版图中非常有用。查看手册页通常并不需要 vgrind 和 grap 程序。它们并不是 LFS 或 BLFS 的一部分,但是如果需要的话你自己应该能够在完成 LFS 之后安装它们。

编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

LFS 中的非英语手册页

ls /usr/share/man/
表 4-15 语言编码对应表
语言 (缩写)编码语言 (缩写)编码
Danish (da)ISO-8859-1Croatian (hr)ISO-8859-2
German (de)ISO-8859-1Hungarian (hu)ISO-8859-2
English (en)ISO-8859-1Japanese (ja)EUC-JP
Spanish (es)ISO-8859-1Korean (ko)EUC-KR
Estonian (et)ISO-8859-1Lithuanian (lt)ISO-8859-13
Finnish (fi)ISO-8859-1Latvian (lv)ISO-8859-13
French (fr)ISO-8859-1Macedonian (mk)ISO-8859-5
Irish (ga)ISO-8859-1Polish (pl)ISO-8859-2
Galician (gl)ISO-8859-1Romanian (ro)ISO-8859-2
Indonesian (id)ISO-8859-1Russian (ru)KOI8-R
Icelandic (is)ISO-8859-1Slovak (sk)ISO-8859-2
Italian (it)ISO-8859-1Slovenian (sl)ISO-8859-2
Norwegian Bokmal (nb)ISO-8859-1Serbian Latin (sr@latin)ISO-8859-2
Dutch (nl)ISO-8859-1Serbian (sr)ISO-8859-5
Norwegian Nynorsk (nn)ISO-8859-1Turkish (tr)ISO-8859-9
Norwegian (no)ISO-8859-1Ukrainian (uk)KOI8-U
Portuguese (pt)ISO-8859-1Vietnamese (vi)TCVN5712-1
Swedish (sv)ISO-8859-1Simplified Chinese (zh_CN)GBK
Belarusian (be)CP1251Simplified Chinese, Singapore (zh_SG)GBK
Bulgarian (bg)CP1251Traditional Chinese, Hong Kong (zh_HK)BIG5HKSCS
Czech (cs)ISO-8859-2Traditional Chinese (zh_TW)BIG5
Greek (el)ISO-8859-7  

退出并清理软件包:

cd ..
rm -rf man-db-2.9.3
  1. 安装 tar:tar 文件的创建和管理功能
  • 大致构建用时: 2.0 SBU
  • 所需磁盘空间: 39 MB 解压并进入软件包:
tar -xf tar-1.32.tar.xz
cd tar-1.32

准备编译 Tar:

FORCE_UNSAFE_CONFIGURE=1  \
./configure --prefix=/usr \
            --bindir=/bin

FORCE_UNSAFE_CONFIGURE=1 强制以 root 用户运行 mknod 的测试。通常认为以 root 用户运行该测试是危险的,但由于是在部分构建的系统上运行,这样并没有问题。 编译软件包:

make

用以下命令测试结果(大概 3 SBU):

make check

安装软件包:

make install
make -C doc install-html docdir=/usr/share/doc/tar-1.31

退出并清理软件包:

cd ..
rm -rf tar-1.32

19. 安装 texinfo:排版语法

  • 大致构建用时: 0.8 SBU
  • 所需磁盘空间: 104 MB 解压并进入软件包:
tar -xf texinfo-6.7.tar.xz
cd texinfo-6.7

准备编译 Texinfo:

./configure --prefix=/usr --disable-static

配置选项的含义: 顶级的配置脚本会告诉你 --disable-static 是一个未能识别的选项,但是 XSParagraph 的配置脚本能够识别它,并能用其来禁用安装静态 XSParagraph.a 至 /usr/lib/texinfo 的操作。 编译软件包:

make

用以下命令测试结果:

make check

安装软件包:

make install

可选地安装 TeX 中的组件:

make TEXMF=/usr/share/texmf install-tex

此处的 makefile 变量 TEXMF 记录了作为 TeX 树的根位置,以待日后安装 TeX 软件包时候使用。 该信息文档系统使用一个纯文本文件来存放菜单条目清单。文件保存在 /usr/share/info/dir。不幸的是,由于不同软件包 Makefile 的偶然问题,有时候会和系统中安装的信息页不同步。如果需要重建 /usr/share/info/dir 文件,下面的可选命令能完成任务:

pushd /usr/share/info
rm -v dir
for f in *
  do install-info $f dir 2>/dev/null
done
popd

退出并清理软件包:

cd ..
rm -rf texinfo-6.7

20. 安装 Vim:文本编辑器

  • 大致构建用时: 2.2 SBU
  • 所需磁盘空间: 201 MB 如果你钟情于其他的编辑器,比如 Emacs、Joe,或 Nano。请参考 http://www.linuxfromscratch.org/blfs/view/8.4/postlfs/editors.html 中的安装指导。 解压并进入软件包:
tar -xf vim-8.2.1361.tar.gz
cd vim-8.2.1361

把配置文件 vimrc 从默认位置移动到 /etc:

echo '#define SYS_VIMRC_FILE "/etc/vimrc"' >> src/feature.h

让 Vim 做好编译准备:

./configure --prefix=/usr

编译安装包:

make

为测试做准备,确保 tester 用户拥有源码目录的写权限:

chown -Rv tester .

现在用用户 nobody 执行测试:

su nobody -s /bin/bash -c "LANG=en_US.UTF-8 make -j1 test" &> vim-test.log

这个测试套件会输出一堆二进制数据到屏幕上。这会导致当前设置下的终端出现问题。把输出重定向到一个日志文件就可以解决这个问题。测试成功的话就会输出【ALL DONE】]。 安装软件包:

make install

许多用户习惯于使用 vi 而不是 vim。为了当人们在习惯性的输入 vi 时能执行 vim,需要给二进制文件和 man 页建立符号连接:

ln -sv vim /usr/bin/vi
for L in  /usr/share/man/{,*/}man1/vim.1; do
    ln -sv vim.1 $(dirname $L)/vi.1
done

默认情况下,Vim 的说明文档被安装在/usr/share/vim 里。下面的这个符号链接使得可以通过/usr/share/doc/vim-8.1 访问该文档,让它的位置与其他软件包的文档位置保持一致:

ln -sv ../vim/vim81/doc /usr/share/doc/vim-8.2.1361

如果要把一个 X Window 系统安装在 LFS 系统上,可能得在安装完 X 系统后再重新编译 Vim。Vim 带有一个 GUI 版本,这个版本需要安装 X 和一些额外的库。想了解更多信息,请参考 Vim 文档和 BLFS http://www.linuxfromscratch.org/blfs/view/8.4/postlfs/vim.html 中 Vim 安装指导页。

21. 配置 Vim

默认情况下,vim 使用的是不兼容 vi 的模式。这对于使用其他编辑器的用户这个问题可能十分新颖。下面列出的设置中,【nocompatible】尤为突出排在第一位,由此提醒着那些想换成【compatible】模式的用户。这个设置尤为重要,因为它会影响其他的设置,而且必须在这个设置之后进行覆盖设置。 让我们先创建一个默认的 vim 配置文件:

cat > /etc/vimrc << "EOF"
" Begin /etc/vimrc

" Ensure defaults are set before customizing settings, not after
source $VIMRUNTIME/defaults.vim
let skip_defaults_vim=1

set nocompatible
set backspace=2
set mouse=
syntax on
if (&term == "xterm") || (&term == "putty")
  set background=dark
endif

" End /etc/vimrc
EOF

设置 set nocompatible 让 vim 比 vi 兼容模式更有用。删掉「no」以保留旧的 vi 特性。设置 set backspace=2 让退格跨越换行、自动缩进和插入的开始。syntax on 参数使 vim 能高亮显示语法。设置 set mouse 让你能在 chroot 和远程连接的时候用鼠标粘帖文本。

最后,带有 set background=dark 的 if 语句矫正了 vim 对于某些终端模拟器的背景颜色的估算。这让某些写在黑色背景上的程序的高亮色能有更好的调色方案。

用下面的命令可以获得其他选项的文档:

vim -c ':options'

默认情况下,Vim 只安装了英文的拼写检查文件。要想安装你想要的语言的拼写检查文件,请从 ftp://ftp.vim.org/pub/vim/runtime/spell/ 下载你所用语言的 *.spl 文件,可下可不下的 *.sug 文件和文字编码。并把它们保存到 /usr/share/vim/vim81/spell/。

要使用这些文件,需要设置 /etc/vimrc 里的某些项,例如:

set spelllang=en,ru
set spell

想要了解更多信息,请阅读上方 URL 里对应 README 文件。 退出并清理软件包:

cd ..
rm -rf vim-8.2.1361