该软件包的详细信息位于 第 6.16.2 节 「Binutils 内容」。
版权 © 1999-2019 Gerard Beekmans
Copyright © 1999-2019, Gerard Beekmans
版权所有。
本手册根据 Creative Commons License 授权。
手册中的计算机指令可根据 MIT License 摘录。
Linux® 为 Linus Torvalds 注册商标。
自 1998 始,我逐渐从学习 Linux 到对其深入了解。仅仅是在我安装完我的第一个 Linux 发行版后,我便迅速地为 Linux 所蕴含的整套理念及其背后的哲学思想所着迷。
总是会有很多的方法来完成一个任务。这对 Linux 的发行版而言也是这样。这些年来,这么多的发行版层出不迭,有的还在,有的已经今非昔比,有的现在也只能在记忆中感怀。它们的行事风格大相径庭,以满足不同受众的需求。也正是因为如此之多不同的发行版想要到达的是同一个目标,我开始意识到自己可以不必在受限于别人做好的发行版。在开始探索 Linux 之前,我们只能忍受各种操作系统的问题,因为我们别无选择。无论你喜欢还是不喜欢,它就在那里。但 Linux 让你拥有了选择的可能。如果你有什么不喜欢的,你可以自由地,甚至倍受鼓舞地去改变它。
我尝试过很多发行版,但还是无法决定该用哪个。这些系统,从它们自身的角度来看,它们都是很棒的系统。这里也并不是在探讨对错问题,这只不过是个人习惯的问题。在各种可选项中,显然没有哪个单一的系统对我而言是完美的。于是我开始着手创建自己的 Linux 系统,以完全满足自己的喜好。
为了使系统真正地属于我,我毅然决定开始从源代码编译所有东西,而不是使用预编译好的二进制包。这个「完美」的 Linux 系统能够拥有不同系统的优点,而不会有它们与之俱来的不足。起初,这种想法让人想都不敢想,但我一直保持着可以构建出这样一个系统的信念。
在梳理了诸如循环依赖和编译错误之类的问题后,我最终构建出了一个订制的 Linux 系统。它运转自如,用起来与当时的那些 Linux 系统一样完美,但它是由我亲手打造的。能组装出这样一个系统让我很满意,而与之相比更棒的,恐怕唯有在软件的各个方面都亲历亲为了,不过这也可算是第二棒的事了。
当我将自己的目标和经验与 Linux 社区的其它成员分享时,引发了大家对这些想法的不断关注。很快人们就清楚地意识到,这样一个定制的 Linux 系统不仅仅可以满足用户的特定需求,还可为程序员和系统管理员提供一个理想的学习机会,以增强他们(原有)的 Linux 技能。出于这个被放大了的兴趣,Linux From Scratch 项目诞生了。
这本 Linux From Scratch 手册是该项目的重中之重。它为你提供了设计和构建自己的系统所需的背景知识和指令。虽然本手册给出了一个模板,一个最后能正常工作的系统,但你可以自由地改变指令以适应自身的需求,从某种程度上说,这是也本项目的一个重要部分。你仍然可以控制一切,我们只是在你起航之前助你一臂之力。
我衷心地希望你能享受构建属于自己的 Linux From Scratch 系统的过程,并对坐拥一个真正属于自己的系统所带来诸多好处能做到甘之如饴。
--
Gerard Beekmans
gerard AT linuxfromscratch D0T org
说到为什么要读这本书,我想,原因一定有很多。有些人可能会提出这样的疑问:「都已经有现成的 Linux 系统可以下载和安装了,你为什么还要多此一举的从从头构建一个呢?」
此项目存在的一个重要原因是帮助你了解 Linux 的内部是如何工作的。通过构建 LFS 系统,你可以更好的理解 Linux 的工作原理,以及程序是如何协同工作和相互依赖的。最棒的是,这个学习经历能给你提供自定义 Linux 系统以满足你自己独特需求的能力。
另一个重要的原因是,你对系统本身有更多的控制权,而无需去关心别人的 Linux 是如何实现的。在 LFS 下,你就是主宰,系统的各个方面都需要你亲力亲为。
LFS 可以让你创建极其精简的 Linux 系统。在安装那些常规的 Linux 系统时,你往往会迫不得已而安装那些你用不到的(甚至你都不知道它们是干什么用的)程序。这些程序也许会浪费你的硬件资源。或许你可能会说,现在计算机的资源那么丰富,稍微浪费一些又有什么关系呢。但是你依旧要考虑到可引导 CD、U 盘或者是一些嵌入式的环境,它们对资源高度敏感,这也恰恰是 LFS 所擅长的地方。
另一个优势便是,自定义的 Linux 系统有着更高的安全性。通过从源码构建一个完整的系统,你有权审核所有的代码以及所打入的安全补丁。也就没必要去在等别人编译修复该安全漏洞的二进制程序了。而且,除非你亲自检查了补丁文件并且做了完整的测试,否则你又怎么能确信,新的二进制程序确实是正确的编译且解决了问题呢?
从头构建一个基本可用的 Linux 系统是本书的目标,你即使不打算这么做,也还是能从这本书的内容中有所收获。
支撑你从头构建属于自己的 LFS 系统的原因实在是太多了。但说到最后,其中最重要的一个原因还是教学。随着你的 LFS 经验愈趋熟稔,这些信息和知识真正给你带来的力量,你也便了然于胸了。
LFS 当前主要支持 AMD/Intel 的 x86(32 位)和 x86_64(64 位)构架的 CPU。另外,本文档也涉及一些更改以允许 LFS 顺利地在 Power PC 和 ARM CPU 上运行。为了顺利构建 LFS,除了后面几页的内容外,你主要需要一个可以在当前 CPU 上正常运行的 Linux 系统,例如:早先版本的 LFS、Ubuntu、Red Hat/Fedora、SuSE 或者是在你的架构上可以运行的其它发行版。另外注意 32 位的发行版是可以在 64 位的 AMD/Intel 处理器上作为宿主机正常安装和运作的。
这里也说明一下关于 64 位系统的一些情况。与 32 位相比,大体上程序运行的速度虽稍微地快了那么一点点,但体积也稍微的大了那么一点点。以在 Core 2 Duo 处理器上运行的 LFS-6.5 系统为例,以下便是实测数据:
Architecture Build Time Build Size
32-bit 198.5 minutes 648 MB
64-bit 190.6 minutes 709 MB
正如你看到的,64 位程序仅仅比 32 位程序快了 4%,而体积大了 9%。由此可见,单纯的追逐 64 位系统其实并没有太大的必要。但是,假如你的电脑的内存超过了 4 GB 又或者说需要操作大于 4 GB 的数据,64 位系统的优势就比较明显了。
上述讨论只适用于对比相同硬件下的构建过程。现代的 64 位系统会比老的 64 位系统更快。LFS 作者建议,如果有机会,尽量在 64 位系统上进行构建。
假如按照本文的默认方式构建,那么你将得到一个「纯」64 位系统——这意味着你仅能够执行 64 位的程序。构建「multi-lib」并不是不可以,但是这意味着很多的程序都需要编译两次:一次为 32 位程序编译,一次为 64 位程序编译。不过,本文档并不涉及这部份的内容,因为这些内容会干扰用户学习如何构建一份最基本的 Linux 系统。你可以通过阅读 Cross Linux From Scratch 的相关内容获得有关该话题的更多帮助。
LFS 的结构尽可能的遵循 Linux 的标准。主要的标准有:
LSB 有四个独立的标准:核心(Core)、桌面(Desktop)、运行时语言(Runtime Languages)和成像(Imaging)。除了通用要求,还有架构特定要求。此外还有两个领域在试行:分别是 Gtk3 和图形(Graphics)。LFS 试图遵从前一节中所讨论的架构要求。
很多人不认同 LSB 的要求。定义它的主要目的是确保私有软件能够在兼容的系统上安装并正常运行。由于 LFS 是基于源代码的,用户对于需要什么软件包有完全的控制权,有很多人就选择不安装 LSB 规范要求的软件包。
完全可以创建一个能够通过 LSB 认证测试的完整 LFS 系统,但这需要很多 LFS 范围之外的额外软件包。在 BLFS 中有这些额外软件包的安装说明。
LSB 核心: |
Bash, Bc, Binutils, Coreutils, Diffutils, File, Findutils, Gawk, Grep, Gzip, M4, Man-DB, Ncurses, Procps, Psmisc, Sed, Shadow, Tar, Util-linux, Zlib |
LSB 桌面: |
无 |
LSB 运行时语言: |
Perl |
LSB 成像: |
无 |
LSB Gtk3 和 LSB 图形(试用): |
无 |
LSB 核心: |
At, Batch (At 的一部分), Cpio, Ed, Fcrontab, Initd-tools, Lsb_release, NSPR, NSS, PAM, Pax, Sendmail (或 Postfix 或 Exim), time |
LSB 桌面: |
Alsa, ATK, Cairo, Desktop-file-utils, Freetype, Fontconfig, Gdk-pixbuf, Glib2, GTK+2, Icon-naming-utils, Libjpeg-turbo, Libpng, Libtiff, Libxml2, MesaLib, Pango, Xdg-utils, Xorg |
LSB 运行时语言: |
Python, Libxml2, Libxslt |
LSB 成像: |
CUPS, Cups-filters, Ghostscript, SANE |
LSB Gtk3 和 LSB 图形(试用): |
GTK+3 |
正如前文所述,LFS 的目标是构建一个完整可用的基础级系统。这其中便包含了,所有地软件包需要复制自身,直至为用户提供出一个相对最小的基础,然后根据用户的选择,在这个基础上定制出一个更完善的系统。这便意味着 LFS 并不是最小可用系统的代名词。其中包含的几个重要软件包,也并非严格需求的。下面的列表介绍了本书中选择每个软件包的理由。
Acl
这个软件包中,包含管理访问控制列表(ACL)的工具,用于定义文件和目录更细粒度的自主访问权。
Attr
这个软件包中包含的程序,用于管理文件系统对象的扩展属性。
Autoconf
这个软件包中包含的程序,能根据开发者的模板自动生成配置源代码的 shell 脚本。通常需要在更新构建过程之后重新构建一个软件包。
Automake
这个软件包中,包含了通过模板生成 Make 文件的程序。通常需要在更新构建过程之后重新构建一个软件包。
Bash
这个软件包用于能满足 LSB 核心的需求,为系统提供 Bourne Shell 接口。选择它而非其它 shell 软件包的理由,是因为它的通用性以及在基本 shell 功能上的扩展能力。
Bc
这个软件包提供了一种任意精度的数值处理语言。在构建 Linux 内核时需要它。
Binutils
这个软件包中,包含了一个链接器、一个汇编器和其它处理对象文件的工具。编译 LFS 以及 BLFS 系统中的大部分软件包,包需要该软件包中的程序。
Bison
这个软件包中有 GNU 版的 yacc(Yet Another Compiler Compiler),用于构建一些其它的 LFS 程序。
Bzip2
这个软件包中,包含用来压缩和解压缩文件的程序。在解压缩很多 LFS 软件包的时候需要它。
Check
这个软件包中,包含一个用于测试其它程序的工具。仅安装在临时工具链中。
Coreutils
这个软件包,包含了一些查看和管理文件及目录的重要程序。在命令行里管理文件时和每个 LFS 软件包的安装过程中需要它。
DejaGNU
这个软件包中,包含一个测试其它程序的框架。仅安装在临时工具链中。
Diffutils
这个软件包中,包含了一些显示文件和目录差异的程序。这些程序可以用来创建补丁,也用于很多软件包的构建过程。
E2fsprogs
这个软件包中,包含了一些处理 ext2、ext3 和 ext4 文件系统的工具。这些是 Linux 上支持的最常用而且完全经过考验的文件系统。
Eudev
该软件包是一个设备管理器。根据设备的加入和移出系统,动态地控制着 /dev 目录中的条目。
Expat
这个软件包中,包含了一个相对小的 XML 解析库。Perl 模块 XML::Parser 需求该软件包。
Expect
这个软件包中,包含的了一个生成与其它程序交互的脚本对话框的程序。通常用来测试其它软件包。仅安装在临时工具链中。
File
这个软件包中,包含一个能判断给定文件的类型的工具。一些软件包需要用它来构建。
Findutils
这个软件包中,包含了一些在文件系统中查找文件的程序。在很多软件包构建脚本中会用到。
Flex
这个软件包中,包含了一个能生成识别文本模式程序的工具。是 lex(lexical analyzer)程序的 GNU 版本。构建很多 LFS 软件包需要用到。
Gawk
这个软件包中,包含了一些操作文本文件的程序。是 awk(Aho-Weinberg-Kernighan)的 GNU 版本。在很多软件包的构建脚本中会用到。
Gcc
这个软件包是 GNU 编译器工具集。它包括 C 和 C++ 的编译器以及其它构建 LFS 时用不到的软件包。
GDBM
这个软件包中,包含 GNU 数据库管理库。LFS 的另一个软件包 Man-DB 会用到。
Gettext
这个软件包中,包含了很多软件包国际化和本地化需要用到的工具和库。
Glibc
这个软件包中,包含了主要的 C 语言库。缺少它 Linux 的程序便无法运行了。
GMP
这个软件包中,包含了能提供任意精度数值运算的数学库。编译 Gcc 时会用到。
Gperf
这个软件包中,包含了一个能从一个键集生成完美哈希函数的程序。Eudev 会用到。
Grep
这个软件包中,包含了一些在文件中搜索的程序。大部分软件包的构建脚本会用到。
Groff
这个软件包中,包含了处理和格式化文本的程序。其中一个重要的功能是格式化 man 页面。
GRUB
这个软件包是 Grand Unified Boot Loader。是诸多可用的引导加载器中的一个,但是最灵活。
Gzip
这个软件包中,包含了一些压缩和解压缩文件的程序。解压很多 LFS 以及 BLFS 的软件包时需要用到。
Iana-etc
这个软件包中,包含提供了网络服务和协议的数据。启用合适的网络功能时会用到。
Inetutils
这个软件包中,包含基本网络管理的程序。
Intltool
这个软件包中,包含能从源文件中抽取可翻译字符串的工具。
IProute2
这个软件包中,包含了一些基本和高级的 IPv4 和 IPv6 网络的程序。由于它的 IPv6 功能,所以选择它来取代其它的常见网络工具包(net-tools)。
Kbd
这个软件包中,包含一些键盘映射文件,用于非 US 键盘的键盘工具以及一些控制台字体。
Kmod
这个软件包中,包含一些用于管理 Linux 内核模块的程序。
Less
这个软件包中,包含了一个很好的文本文件查看器,允许查看文件的时候向上或向下滚动。Man-DB 用它来查看 man 页面。
Libcap
这个软件包中,包含实现了可以用于 Linux 内核的,从用户空间到 POSIX 1003.1e 的接口。
Libelf
elfutils 项目为 ELF 文件和 DWARF 数据提供了库和工具。该软件包中的大部分实用程序都可以在别的软件包中使用,但是该库需求使用默认(且最有效的)配置构建 Linux 内核。
Libffi
这个软件包实现了一个可移植的、高级编程接口,以适应各种调用惯例。在编译时,一些程序可能还不知道给函数传递的是什么参数。例如,一个解释器可能会在运行时才被告知函数调用中所传递的参数的个数和类型。Libffi 可以应用于这些程序,充当从解释程序到被编译代码的桥梁。
Libpipeline
这个软件包中,包含一个以灵活和便捷的方式操作子进程流水线的库。Man-DB 软件包会用到。
Libtool
这个软件包中,包含了一些 GNU 通用库支持脚本。它降低了在一致、可移植的接口上使用共享库的复杂度。在其他 LFS 软件包的测试套件里需要它。
Linux 内核
这个软件包就是操作系统。即我们常说的「GNU/Linux」中的「Linux」。
M4
这个软件包中,包含一个普通的文本宏处理器,作为其它程序的构建工具使用。
Make
这个软件包中,包含了一个指导软件包构建的程序。LFS 中的几乎每个包都需要它。
Man-DB
这个软件包中,包含了一些查找和查看 man 页面的程序。由于其更好的国际化功能,用来代替 man 软件包。它提供了 man 程序。
Man-pages
这个软件包中,包含基本的 Linux man 页面的真正内容。
Meson
这个软件包提供了自动化构建软件的工具。Meson 的主要目标是最小化软件开发者,在其构建的系统上,花费的配置时间。
MPC
这个软件包中,包含复数运算的函数。Gcc 需要它。
MPFR
这个软件包中,包含多精度运算的函数。Gcc 需要它。
Ninja
这个软件包中,包含了一个专注于速度的小型构建系统。其输入文件由更高层次的构建系统生成,并且尽可能快速地运行构建过程。
Ncurses
这个软件包中,包含了一些处理字符界面的不依赖特定终端的库。通常用来为菜单系统提供光标控制。一些 LFS 的软件包会用到。
Openssl
这个软件包中,提供了与加密相关管理工具和库。提供的这些加密功能对于他软件包(包括 Linux 内核)而言非常有用。
Patch
这个软件包中,包含了一个通过 patch 文件来修改或新建文件的程序,patch 文件通常是由 diff 程序创建的。一些 LFS 软件包的构建过程会需要它。
Perl
这个软件包中,包含了一个运行时语言 PERL 的解析器。一些 LFS 软件包的安装和测试套件会需要它。
Pkg-config
这个软件包提供了一个返回已安装库或软件包的元数据的程序。
Procps-NG
这个软件包中,包含了一些监视进程的程序。这些程序对系统管理非常有用,也被用于 LFS 的启动脚本。
Psmisc
这个软件包中,包含了一些显示运行中进程信息的程序。这些程序对系统管理非常有用。
Python 3
这个软件包提供了一种解释型语言,该语言的设计理念强调代码的可读性。
Readline
这个软件包提供了一些命令行编辑和历史功能的库。Bash 会使用它。
Sed
这个软件包提供了不通过文本编辑器而直接编辑文本的功能。大部分 LFS 软件包的配置脚本需要它。
Shadow
这个软件包中,包含了一些以安全方式处理密码的程序。
Sysklogd
这个软件包中,包含了一些用于记录系统消息日志的程序。例如,那些内核或守护进程发生不寻常事件时发出的消息。
Sysvinit
这个软件包提供了 init 程序,Linux 系统中所有进程的父进程。
Tar
这个软件包提供了归档和提取 LFS 中的几乎所有软件包的能力。
Tcl
这个软件包中,包含了在很多 LFS 软件包测试套件中使用的工具命令语言。仅安装在临时工具链中。
Texinfo
这个软件包中,包含了一些读、写以及转换信息页面的程序。在很多 LFS 软件包的安装过程中会使用它。
Util-linux
该软件包中,包含了许多工具。其中有处理文件系统、控制台、分区和消息的工具。
Vim
这个软件包中,包含一个编辑器。由于 vi 编辑器的经典以及大量的强大功能而选择它。对很多用户来说一个编辑器是一个非常个人的选择,如果需要的话也可以选择其它编辑器。
XML::Parser
这个软件包是和 Expat 交互的 Perl 模块。
XZ Utils
这个软件包中,包含了一些压缩和解压缩文件的程序。通常它的压缩率最高,在解压 XZ 或者 LZMA 格式的软件包时非常有用。
Zlib
这个软件包中,包含了一些程序所使用的压缩和解压缩功能。
构建一个 LFS 系统并不是一个简单的任务。它要求对 Unix 系统管理有一定水平的了解,以便可以解决问题并正确地执行所列出的命令。特别是,至少你应该拥有使用命令行(shell),复制或移动文件和目录、列出目录和文件内容、切换当前目录的能力。同时也希望你拥有使用和安装 Linux 软件的基本知识。
因为 LFS 书中假设你至少有这些基本的技能,所以众多的 LFS 提供的论坛看起来在这方面也给你提供不了太多的帮助。你会发现对于这些基本知识的问题并不会得到解答,或者会简单的建议你去看一下 LFS 主要的提前阅读列表。
构建 LFS 系统之前,我们建议阅读以下内容:
构建软件 HOWTO http://www.tldp.org/HOWTO/Software-Building-HOWTO.html
这是一个在 Linux 上编译和安装「通用」Unix 软件包的综合指南。尽管成文已有一段时间,但其对于编译和安装软件中所需的基本技能,还算一个不错的总结。
初学者源码安装指南 http://moi.vonos.net/linux/beginners-installing-from-source/
该指南很好的总结了从源码构建软件时所需要的基础技能和技巧。
为了能更更容易理解,这里有一些全书使用的排版约定。本段,会包含一些来自 Linux From Scratch 中排版格式的示例。
./configure --prefix=/usr
这种形式的文本,设计出来就是为了让你照着输入的,除非周围文本中另有说明。部分的解释段落和会用它,以指出引用的那条命令。(译注:由于文字排版原因,其中的内容即便是注释,也不会翻译,下同。)
某些情况下,会在一行的行末添加反斜,将一个逻辑行的内容分写成两个或更多的物理行。
CC="gcc -B/usr/bin/" ../binutils-2.18/configure \ --prefix=/tools --disable-nls --disable-werror
注意,反斜杠的后面必须紧跟一个回车符。其它的空白字符,例如空格和 Tab 将导致错误的结果。
install-info: unknown option '--dir-file=/mnt/lfs/usr/info/dir'
这种形式的文本(等宽文本)用于显示屏幕输出,通常是所运行命令的输出结果。这种形式也用于文件名的显示,例如:
/etc/ld.so.conf
。
强调
这种形式的文本在本书中有多重目的。主要目的是强调重要的内容或项目。
http://www.linuxfromscratch.org/
这种格式用来表示 LFS 社区内部及外部网页的超链接。包括 HOWTO,下载地址和网站等。
cat > $LFS/etc/group << "EOF"
root:x:0:
bin:x:1:
......
EOF
这种格式在创建配置文件中会使用。第一个命令告诉系统新建一个文件 $LFS/etc/group
,然后后面的行中不论在输入了什么都会输入到文件中,直至遇到文件终结符(EOF)。因此,这整个部分会如看到的那样输入。
<需要替换的文本>
这种格式用来封装需要替换为合适内容的文本,或者复制粘贴操作。
[可选的文本]
这种格式用来封装可选项文本。
passwd(5)
这种格式用来表示特定的手册 (man) 页面。括号内的数字表示该手册的特定部分。例如 passwd 有两个手册页面。在 LFS
安装说明中,这两个手册页面会表示为 /usr/share/man/man1/passwd.1
以及 /usr/share/man/man5/passwd.5
。当书中使用 passwd(5)
时它特指 /usr/share/man/man5/passwd.5
。man passwd
会输出它找到匹配「passwd」的第一个手册页面,也就是 /usr/share/man/man1/passwd.1
。在这个例子中,你需要执行
man 5 passwd
才能阅读指定的手册页面。应该注意的是,大部分的手册页面在不同部分不会有重复的页面名字。因此,man <program
name>
通常就足够了。
本书分为以下几个部分。
第一部分解释了一些安装 LFS 时的重要注意事项。同时还提供了本书的基本信息。
第二部分描述了构建的一些准备工作,包括分区,下载软件包,编译一些临时工具。
第三部分引导用户开始 LFS 系统的构建——逐一的编译和安装所有的软件包,设置启动脚本,安装内核。生成的 Linux 系统是继续编译其它一系列软件的基础,通过那些软件来扩展系统,系统才能更好地满足我们的需求。在本书的最后,我们还给出了一个便于使用的引用列表,包括安装好的程序、库和一些重要文件。
构建 LFS 系统的软件是在不断的更新和改进的,也许在这本 LFS 书发布以后就会出现一些安全警告或者是修复了一些 bug。请在着手构建之前访问 http://www.linuxfromscratch.org/lfs/errata/9.0/ ,检查此版本 LFS 软件包的版本或说明,是否需要修改以解决安全漏洞或其它 bug。你应该关注本书的一切变化,且将其应用到 LFS 系统的构建中去。
如果 LFS 的中文手册翻译的有问题,或是你对 LFS 中文手册有自己的建议和意见。可以通过中文手册的 Github 库 LFS-BOOK https://github.com/LCTT/LFS-BOOK 找到并告诉我们。
LFS 系统需要在一个已经安装好的 Linux 发行版(比如 Debian、OpenMandriva、Fedora 或 OpenSUSE 等)中构建。这个已有的 Linux 系统(即宿主)作为构建新系统的起始点,提供了必要的程序,包括编译器、链接器和 shell。请在安装发行版的过程中选择「Development(开发)」选项以便使用这些开发工具。
你可以选择在电脑上安装一个独立的发行版,亦可以临时使用商业发行版的 LiveCD。
本手册的 第 2 章 描述了如何创建一个新的 Linux 本地分区和文件系统。这就是编译和安装全新的 LFS 系统的地方。第 3 章 介绍了构建 LFS 系统所需要下载的软件包和补丁,以及如何将它们保存到新的文件系统中。第 4 章 讨论了如何设置恰当的工作环境。还请务必仔细阅读 第 4 章,它会告诉您在继续 第 5 章 以及后面的工作之前,尤其需要注意的几件事请。
第 5 章 解释了为构建编译套件(工具链)需要安装的多个软件包,在 第 6 章 中将会使用这套工具构建正真的系统。其中有一些包需要解决循环依赖——比如你需要一个编译器来编译出一个编译器。
第 5 章 还展示了如何构建第一阶段的工具链,包括 Binutils 和 GCC(第一阶段的主要意义就是重新安装这两个核心软件包)。下一步将来构建 Glibc,即 C 语言库。Glibc 将使用第一阶段构建的工具链编译。然后,第二阶段的工具链编译就完成了。这次,工具链将会被动态链接到新建立的 Glibc。第 5 章 中剩余的其他包都将使用第二阶段的工具链编译。当这些过程完成之后,除了正在运行的内核外,LFS 的安装过程就不再已赖于宿主发行版了。
把新系统从宿主发行版中分离出来的工作看起来可能有点多余。第 5.2 节 「工具链技术说明」 给出了为什么要这么做的完整技术说明。
在 第 6 章 中,将构建完整的 LFS 系统。我们将会使用 chroot(切换成 root 用户的)命令来进入一个虚拟环境并启动一个新的 shell,且整个虚拟环境就将作为 LFS 分区的根目录。这个过程,和重起电脑并指示内核将 LFS 分区挂载为根分区十分相像。只是系统并没有真正重启,而是通过 chroot 实现的,而这样做的原因是想要实现一个正真可以启动的系统还有很多工作没有完成。使用「chrooting」最大的好处在于,构建 LFS 的同时可以继续使用宿主系统。在等待包编译的过程中,你可以继续正常的使用电脑。
在完成基本的安装过程之后,您还需要阅读 第 7 章 对系统做简单的配置,然后阅读 第 8 章 配置内核和引导器(bootloader)。第 9 章 包含了一些 LFS 后续使用的建议。完成本手册的所有步骤之后,就可以重启电脑,进入新的 LFS 系统了。
大致流程就是如此。每一步的细节都会在后续的章节中逐一阐明。在你开始 LFS 的旅途之后,混沌的过程便会渐渐明晰。
下面列举了上一版手册发布后的一些软件包更新。
更新:
Bc 2.1.3
Bison-3.4.1
Bzip2-1.0.8
Coreutils-8.31
E2fsprogs-1.45.3
Eudev-3.2.8
Expat-2.2.7
File-5.37
Gawk-5.0.1
GCC-9.2.0
Gettext-0.20.1
Glibc-2.30
GRUB-2.04
IPRoute2-5.2.0
Kbd-2.2.0
Less-551
LFS-Bootscripts-20190524
Libcap-2.27
Libelf-0.177 (from elfutils)
Linux-5.2.8
Man-DB-2.8.6.1
Man-pages-5.02
Meson-0.51.1
Openssl-1.1.1c
Perl-5.30.0
Python-3.7.4
Shadow-4.7
SysVinit-2.95
Tar-1.32
Texinfo-6.6
Tzdata-2019b
Util-Linux-2.34
Vim-8.1.1846
新增:
移除:
本册是 Linux From Scratch 手册的 9.0 版本,发布于 2019 年 9 月 1 日。如果距离这个时间已超过 6 个月,那么应该已经有更新和更好的版本了。你可以访问 http://www.linuxfromscratch.org/mirrors.html 的任意镜像站点来获取他们。
下面是本书上一次发布之后的更新列表。
更新日志条目:
2019-09-01
[bdubbs] - LFS-9.0 发布。
2019-08-14
2019-08-04
[bdubbs] - 通过将 include 文件添加到 glibc 头文件来解决 linux-5.2 导致的问题。
2019-08-03
2019-07-21
2019-07-14
[bdubbs] - 修复对于 binutils-2.32 gold linker 的测试。修复 #4498。
[bdubbs] - 更新至 tzdata-2019b。修复 #4492。
[bdubbs] - 更新至 python3-3.7.4。修复 #4496。
[bdubbs] - 更新至 meson-0.51.1。修复 #4497。
[bdubbs] - 更新至 iproute2-5.2.0。修复 #4495。
[bdubbs] - 更新至 grub-2.04。修复 #4494。
[bdubbs] - 更新至 linux-5.2.1。修复 #4493。
[bdubbs] - 更新至 bc-2.1.0。修复 #4436。
[bdubbs] - 更新至 bzip2-1.0.8。修复 #4499。
2019-06-29
2019-06-24
[renodr] - 修复了在版本目录中安装 Check 文档的问题。感谢 Ryan Marsaw 报告了这个问题。通过在 make 安装命令中删除 unrecognized/unused --docdir 并替换成 "docdir=" 来解决此问题。
2019-06-18
[renodr] - 更新至 linux-5.1.11。修复 SOCK PANIC 问题。修复 #4485。
2019-06-16
2019-06-08
[renodr] - 为第 6 章构建 Util-Linux 前删除软链接的操作添加说明,仅在 systemd 版本中可见。
2019-06-03
2019-05-24
[dj] - LFS bootscripts 的外观更改。
2019-05-19
2019-05-03
2019-04-23
[bdubbs] - 应用一个变更,让最新版本的 gcc 可以正确构建 Perl。
2019-04-20
2019-04-15
2019-03-27
[bdubbs] - 回退至 meson-0.49.2。
2019-03-26
2019-03-25
2019-03-13
[xry111] - 更新软件包的内容和简短描述。修复 #4443。
2019-03-12
2019-03-05
[bdubbs] - 更新至 linux-5.0。修复 #4437。
2019-03-01
2019-03-01
[bdubbs] - LFS-8.4 发布。
如果在构建 LFS 系统的过程中遇到任何的错误,或者是有任何的疑问,亦或者认为本手册存在任何的拼写错误,请先阅读常见问题列表(FAQ):http://www.linuxfromscratch.org/faq/。
linuxfromscratch.org
的服务器上部署了一些和 LFS
项目开发工作相关的邮件列表。其中包括主要开发列表和支持列表,以及一些其他相关话题的讨论列表。如果 FAQ
不能解决您的问题,那么您可以在此搜索邮件列表:http://www.linuxfromscratch.org/search.html。
关于这些列表的订阅、归档、和其他的信息,都可以访问 http://www.linuxfromscratch.org/mail.html 获取。
有些 LFS 的社区成员也会在 IRC 上为别人提供帮助。在使用这种方法之前,请保证问题在 LFS 的 FAQ
或者是邮件列表中没有提及。您可以通过 irc.freenode.net
查找 #LFS-support 加入讨论。
LFS 项目在世界范围内有许多镜像站点,方便大家访问我们的网站以及下载所需文件。请访问 LFS 站点 http://www.linuxfromscratch.org/mirrors.html 查看最新的镜像站点列表。
如果在使用本书的过程中有疑问或碰到问题,可以先去看下 FAQ 页面http://www.linuxfromscratch.org/faq/#generalfaq。那里已经解决了很多经常遇到的问题。如果你的问题在那里找不到答案,可以先尝试找出问题的原因。下面页面里的提示可以提供一些帮你定位问题的帮助:http://www.linuxfromscratch.org/hints/downloads/files/errors.txt。
如果在 FAQ 里找不到你遇到的问题,还可以在这个邮件列表里搜索一下:http://www.linuxfromscratch.org/search.html。
我们还有一个很棒的 LFS 社区,大家都很乐意通过邮件列表和 IRC 提供协助(参看本书的 第 1.4 节 「资源」)。不过,我们每天收到的支持问题中有很多其实可以通过查看 FAQ 和搜索邮件列表轻松解决。所以为了让我们能最大可能地提供更好的协助,希望你碰到问题能自己先研究一下。这样可以让我们有精力去关注更罕见问题。如果你自己搜索不到解决方式,请在你的帮助请求里收集所有相关信息(下面提到的)。
除了对遇到的问题的简短描述外,帮助请求里还需要包含的以下的关键信息:
所用手册的版本(当前版本为 9.0 )
用来构建 LFS 的宿主机器的 Linux 发行版以及版本
本手册 宿主系统需求 中脚本的所有输出信息
出现问题的软件包或本手册的章节
精确的错误信息或表现形式
注明你是否已经脱离了本书的内容
脱离本手册的内容并不意味着我们就一定不会帮你。毕竟,LFS 还是属于个人爱好。坦率地告知别人对已验证的流程作出的任何改动,有助于我们评估和找到你问题的可能原因。
如果在运行 configure
脚本时遇到问题,查看一下 config.log
文件。这个文件中可能会包含 configure
脚本运行时没有输出到屏幕上的错误信息。想寻求帮助的话请包含相关行。
屏幕上的显示输出和各个文件的内容都有助于定位编译发生问题的原因。configure 和 make 命令的输出信息都有用。然而通常并不需要在寻求帮助时将这些信息一股脑的贴出来,但是一定要包含足够的相关信息。下面的例子就是在执行 make 命令出错后应该贴出来的输出信息:
gcc -DALIASPATH=\"/mnt/lfs/usr/share/locale:.\"
-DLOCALEDIR=\"/mnt/lfs/usr/share/locale\"
-DLIBDIR=\"/mnt/lfs/usr/lib\"
-DINCLUDEDIR=\"/mnt/lfs/usr/include\" -DHAVE_CONFIG_H -I. -I.
-g -O2 -c getopt1.c
gcc -g -O2 -static -o make ar.o arscan.o commands.o dir.o
expand.o file.o function.o getopt.o implicit.o job.o main.o
misc.o read.o remake.o rule.o signame.o variable.o vpath.o
default.o remote-stub.o version.o opt1.o
-lutil job.o: In function `load_too_high':
/lfs/tmp/make-3.79.1/job.c:1565: undefined reference
to `getloadavg'
collect2: ld returned 1 exit status
make[2]: *** [make] Error 1
make[2]: Leaving directory `/lfs/tmp/make-3.79.1'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/lfs/tmp/make-3.79.1'
make: *** [all-recursive-am] Error 2
在此例子中,很多人可能仅仅就贴出最后的一部分:
make [2]: *** [make] Error 1
这样的信息并不能用于诊断问题,实际上它仅仅是告知别人出了问题,却没有指出到底是哪里出现问题。这也就是为什么要提供更加完整的信息。比如在上面的例子中,就告诉别人所执行的命令及相应的错误信息。
这个链接 http://catb.org/~esr/faqs/smart-questions.html 中的文章告知人们如何在互联网上寻求帮助,可以去看一下,且遵循其中的提问原则进行提问,可以提高获得帮助的概率(亦让别人对你伸出援手更加方便)。
你的主机系统应具有以下指定最低版本的软件。 对于大多数现代 Linux 发行版来说,这应该不是问题。 另请注意,不少发行版可能会将软件的头文件单独打包,并采用「<package-name>-devel」或「<package-name>-dev」的形式命名。如果你的发行版提供了这样的软件包,请务必安装。
所列软件包的早期版本可能也能用,但尚未经过测试。
Bash-3.2 (/bin/sh 应该是 bash 的符号链接或硬链接)
Binutils-2.25 (不建议使用 2.32 以后的版本,尚未经过测试)
Bison-2.7 (/usr/bin/yacc 应该是指向 bison 或执行 bison 的小型脚本的链接)
Bzip2-1.0.4
Coreutils-6.9
Diffutils-2.8.1
Findutils-4.2.31
Gawk-4.0.1 (/usr/bin/awk 应该是 gawk 的链接)
GCC-6.2 包括 C++ 编译器,g++ (9.2.0 以后的版本不建议使用,尚未经过测试)
Glibc-2.11 (2.30 以后的版本不建议使用,尚未经过测试)
Grep-2.5.1a
Gzip-1.3.12
Linux Kernel-3.2
之所以指定使用这一版本的内核,是因为这是第 6 章构建 glibc 的需求,开发人员如是建议。udev 也有这个需求。
如果宿主机内核早于 3.2,则需使用更新版本的内核将其替换。有两种方法可以解决这个问题。首先,查看你的 Linux 供应商是否提供 3.2 或更高版本内核的软件包。如果提供的话,你便能如愿安装了。如果你的供应商不提供合适的内核(的软件包),或者是你不想直接安装,你也可以自己编译内核。编译内核和配置引导的说明(假设宿主机使用 GRUB)位于 第 8 章。
M4-1.4.10
Make-4.0
Patch-2.5.4
Perl-5.8.8
Python-3.4
Sed-4.1.5
Tar-1.22
Texinfo-4.7
Xz-5.0.0
请注意,依据本手册的指导构建 LFS 系统需确保上述提到的符号链接无误。虽然指向其他软件(例如 dash,mawk 等)的符号链接可能也可以,但 LFS 开发团队尚未对其进行测试或支持,并且这样做可能导致对于个别软件包在执行命令或者应用某些补丁的时候出现偏差。
查看宿主机系统是否已具备所有软件包的适当版本和编译程序的能力,请运行以下命令:
cat > version-check.sh << "EOF"
#!/bin/bash
# Simple script to list version numbers of critical development tools
export LC_ALL=C
bash --version | head -n1 | cut -d" " -f2-4
MYSH=$(readlink -f /bin/sh)
echo "/bin/sh -> $MYSH"
echo $MYSH | grep -q bash || echo "ERROR: /bin/sh does not point to bash"
unset MYSH
echo -n "Binutils: "; ld --version | head -n1 | cut -d" " -f3-
bison --version | head -n1
if [ -h /usr/bin/yacc ]; then
echo "/usr/bin/yacc -> `readlink -f /usr/bin/yacc`";
elif [ -x /usr/bin/yacc ]; then
echo yacc is `/usr/bin/yacc --version | head -n1`
else
echo "yacc not found"
fi
bzip2 --version 2>&1 < /dev/null | head -n1 | cut -d" " -f1,6-
echo -n "Coreutils: "; chown --version | head -n1 | cut -d")" -f2
diff --version | head -n1
find --version | head -n1
gawk --version | head -n1
if [ -h /usr/bin/awk ]; then
echo "/usr/bin/awk -> `readlink -f /usr/bin/awk`";
elif [ -x /usr/bin/awk ]; then
echo awk is `/usr/bin/awk --version | head -n1`
else
echo "awk not found"
fi
gcc --version | head -n1
g++ --version | head -n1
ldd --version | head -n1 | cut -d" " -f2- # glibc version
grep --version | head -n1
gzip --version | head -n1
cat /proc/version
m4 --version | head -n1
make --version | head -n1
patch --version | head -n1
echo Perl `perl -V:version`
python3 --version
sed --version | head -n1
tar --version | head -n1
makeinfo --version | head -n1 # texinfo version
xz --version | head -n1
echo 'int main(){}' > dummy.c && g++ -o dummy dummy.c
if [ -x dummy ]
then echo "g++ compilation OK";
else echo "g++ compilation failed"; fi
rm -f dummy.c dummy
EOF
bash version-check.sh
LFS 的设计是建立在一个会话中的。这也就是说假定系统在处理的过程中不会关机。但是实际上这并不意味着 LFS 的构建需要一气呵成。但是如果 LFS 恢复时的状态和之前不同,则某些程序必须在此之后重新构建。
当在主机系统各种完成此章节的内容,重启时,需要注意一下的内容:
程序在 2.4 节以后都是以 root 用户完成的。所以需要重新将 LFS 的环境变量设置为 ROOT。
/mnt/lfs 分区必须挂载。
第 5 章涉及的所有指令都应该由 lfs 用户执行。所以在执行第 5 章的任务之前请先执行 su - lfs。
第 5.3 节 「通用编译指南」 中提及的程序是十分重要的。如果在安装任何软件包时存在问题,请确保所有之前已经解压的软件包已经删除。重新将它们解压后再完成此章节中的所有命令。
/mnt/lfs 分区必须挂载。
当进入 chroot 环境,LFS 环境变量必须设置为 root 用户。否则不得使用 LFS 变量。
必须挂载虚拟文件系统。这可以在进入 chroot 之前或者之后通过改变主机的虚拟终端,以 root 用户执行 第 6.2.2 节 「挂载和激活 /dev」 和 第 6.2.3 节 「挂载虚拟文件系统」 中的命令。
与绝大多数其它操作系统相同,安装 LFS 通常需要专门的分区。构建 LFS 系统比较推荐的方法是使用可用的空分区,或者如果条件允许,最好是在未分区的空间里新建分区。
最小化的系统需要大约 6 GB 的分区,这足以存储所有的源码包及满足编译的需求。但如果要将 LFS 作为主要的 Linux 系统,可能需要安装其它附加的软件,这将需要额外的空间。考虑到了日后所需的空间,一个 20 GB 的分区是比较合理的。LFS 系统本身并不会占用这么多的空间,但大分区将能提供充裕的临时储存空间,并为完成 LFS 以后添加附加功能留有余地。编译软件包可能需要较大的磁盘空间,但这些空间可以在软件包安装后回收。
由于编译过程中所需的内存(RAM)可能不足,用一个小型的磁盘分区作为 swap
空间是个不错的想法。内核会在此分区中储存较少使用的数据,从而为活动进程提供更多的内存。LFS 系统可以与宿主系统共用
swap
分区,这样就没有必要再新建一个了。
启动磁盘分区程序,通过如 cfdisk 或 fdisk 加上新分区所在的磁盘名——例如
/dev/sda
作为主要的磁盘。创建一个 Linux
本地分区,并按需创建 swap
分区。如果需要,可以参考
cfdisk(8)
或 fdisk(8)
。
对于有经验的用户,也可自行定制分区分案。新版 LFS 系统支持软件 RAID 阵列或 LVM 然而,这些方案需要用到 initramfs,这涉及到比较复杂的话题。并不建议首次尝试 LFS 的用户使用这样的分区方法。
请记住新分区的位置(例如,sda5
)。本书中将称其为 LFS
分区。还需要记住 swap
分区的位置。这些将会在/etc/fstab
文件中用到。
LFS 邮件列表中经常有人问到关于系统分区的建议。这一话题非常主观。大多数发行版默认情况下会使用整个磁盘,仅为交换分区保留一小部分空间,但由于种种原因,这并不适合 LFS。这样做会降低灵活性,使得多个发行版或几个 LFS 版本之间共享数据变得困难,也让备份更耗时,还会导致文件系统结构分配不合理而浪费磁盘空间。
为 LFS 根分区(不要与 /root
目录混淆)分配
10 GB 的空间是比较好的折中方案。这为构建 LFS 和大多数 BLFS
提供了足够的空间,也小到可以轻易创建多个分区用于实验。
大多数发行版会自动创建交换分区。一般来说,交换分区的推荐大小为物理内存的两倍左右,然而鲜少需要这样做。若是磁盘空间有限,可以设置为 2 GB的交换分区并查看磁盘的交换量。
发生内存交换其实并不好。通常,你只需要观察磁盘活动以及系统对命令的响应程度就能知道这个系统是否在进行交换。通常在使用非常不合理的命令时才会发生交换,如尝试编辑一个大小为 5 GB 的文件时。如果交换在你的系统上是常态,那最好的办法就是为你的系统添置更多的物理内存。
如果是在 GPT(GUID Partition Table,GUID 分区表)下分 boot 磁盘,那么必然会产生一个约 1 MB 左右的小分区,如果之前没有的话。这个分区可能还没有被格式化,但是它必须存在,GRUB 会在安装引导器的时候用到。如果使用 fdisk,该分区通常会被标记为「BIOS Boot」。而如果使用的是 gdisk,则分区代码应为 EF02 。
Grub Bios 分区必须位于 BIOS 用于引导系统的磁盘驱动器上。却不一定要和 LFS 的根分区位于同一个磁盘驱动器。一个系统里的磁盘可能会用到不同类型的分区表。该磁盘仅与 boot 磁盘的分区表类型有关。
在分配磁盘时,有些分区不是必须的,但却值得你考虑。以下列表并不全面,仅供参考。
/boot – 强烈推荐。此分区用于存储内核和其它启动信息。为了减少大容量磁盘启动时的潜在问题,尽量将该分区设为磁盘驱动器上第一个物理分区。100 MB 的空间就十分充裕了。
/home – 强烈推荐。home 目录可用于跨发行版或多个 LFS 版本之间共享用户自定义内容。应该将尽量多的磁盘都分配给 home 分区。
/usr – 独立的 /usr 分区常见于瘦客户端服务器或无盘工作站。LFS 通常不需要。5 GB 大小足以应付大部分安装。
/opt – 该目录常用于在 BLFS 中,安装多个像 Gnome 或 KDE 这样无需将文件嵌入 /usr 层次结构的大型软件包。如果使用的话,5 到 10 GB 的空间就足够了。
/tmp – 独立的 /tmp 分区是比较少见的,但这在配置瘦客户端时会有用。如果使用的话,很少超过几 GB。
/usr/src – 该分区非常用于存储 BLFS 源文件并在构建不同版本的 LFS 中共享。它也可用于构建 BLFS 软件包。30-50 GB 的分区可以提供足够的空间。
任何你需要在启动时自动挂载的单独分区都需要写入到 /etc/fstab
文件中。有关如何指定分区的细节将在 第 8.2 节 「创建 /etc/fstab 文件」
中讨论 。
现在已经有了一个空的分区,可以创建文件系统了。LFS 可以使用 Linux 内核能识别的任何文件系统,但最常用的类型是 ext3 和 ext4。当然,至于具体采用哪种文件系统,应该取决于所要存储的文件特点和分区的大小。例如:
适用于那些分区容量不是太大,更新也不频繁的情况,例如 /boot 分区。
是 ext2 的改进版本,其支持日志功能,能够帮助系统从非正常关机导致的异常中恢复。是常用作一般需求的文件系统。
是 ext 文件系统的最新版。提供了很多新的特性,包括纳秒级时间戳、创建和使用巨型文件(16TB)、以及速度的提升。
其它文件系统,包括 FAT32、NTFS、ReiserFS、JFS 和 XFS 等都在专门领域有其独到的作用。关于这些文件系统更多的信息可以参考http://en.wikipedia.org/wiki/Comparison_of_file_systems。
LFS 假设要将根分区(/)的文件系统的类型设置为 ext4。运行如下指令,为 LFS 分区创建 ext4
文件系统:
mkfs -v -t ext4 /dev/<xxx>
请替换 <xxx>
为实际的
/
分区设备名。
如果你已经有了现成的 swap
分区,不需要重新格式化。如果是新建的 swap
分区,需要用下面的命令初始化:
mkswap /dev/<yyy>
请替换 <yyy>
为实际的
swap
分区设备名。
在整本书里,会多次用到环境变量 LFS
。你应该确保这个变量在整个 LFS
构建过程中总是定义了的。它应该被设置为你将要构建的 LFS 系统的目录名——这里我们使用 /mnt/lfs
作为例子,但是选择哪一个目录是你的自由。如果你把 LFS
构建到一个单独的分区里,这个目录将成为那个分区的挂载点。选择一个本地目录并用以下命令设置该变量:
export LFS=/mnt/lfs
设置这个变量的好处在于有些命令可以直接写成类似 mkdir -v $LFS/tools这样。在处理这条命令时,会自动替换「$LFS」为「/mnt/lfs」(或任何这个变量所指的地方)。
不论何时,当你离开又重新进入这个工作环境时都不要忘了检查 LFS
是否设置(比如当你使用 su
切换到 root
或是另一个用户时)。使用如下命令检查
LFS
变量是否正确设置:
echo $LFS
请确保输出的是你构建 LFS 的那个目录的路径,如果你是按照例子设置的,那就是 /mnt/lfs
。如果你输出的路径不正确,请使用本页前一部分提供的命令把
$LFS
重新设置到正确的目录。
确保 LFS
变量一直为设置的一个方法是编辑你 home 目录下的
.bash_profile
文件和 /root/.bash_profile
,并输入上述 export 命令。另外,在
/etc/passwd
文件中,为所有需要的用户指定
LFS
变量的 shell 需要是 bash,以确保
/root/.bash_profile
文件作为登录过程的一部分,并入其中。
另一个考虑是登入宿主系统的方法。如果是通过图形显示管理器登入,那么就不会像寻常那样在虚拟终端启动时,采用用户的
.bash_profile
文件。在这种情况下,需要将
export 命令添加至 root 用户的 .bashrc
中。另外,有些发行版还有指令,在非交互式 bash 的调用中不让 .bashrc
指令运行。在非交互式使用的测试前请保证 export 命令已经添加。
至此,文件系统已经创建妥当,下一步就是访问这些分区了。为此,需要将这些建立的分区挂载到选定的挂载点。本书假定的挂载点为环境变量
LFS
指向的地址,如前文所述。
创建挂载点并用下面的命令挂载 LFS 文件系统:
mkdir -pv $LFS
mount -v -t ext4 /dev/<xxx>
$LFS
请用 LFS 分区替代 <xxx>
。
如果 LFS 使用了多个分区(如,/
和 /usr
),需要挂载:
mkdir -pv $LFS mount -v -t ext4 /dev/<xxx>
$LFS mkdir -v $LFS/usr mount -v -t ext4 /dev/<yyy>
$LFS/usr
请替换 <xxx>
和
<yyy>
为合适的分区名。
请确保在挂载新分区时没有使用过于严格的权限参数(如 nosuid
或
nodev
选项)。运行不带任何参数的 mount 命令来查看在挂载 LFS
分区时设置了什么参数。如果设置了 nosuid
和/或
nodev
参数,就需要重新挂载了。
上述说明假定你在 LFS 的过程中不会重启你的计算机。如果你关闭了你的系统,你将需要在每次重新开始构建过程时重新挂载 LFS 分区,或者修改你宿主系统的 /etc/fstab 文件,让每次重新启动后都自动挂载它。示例如下:
/dev/<xxx>
/mnt/lfs ext4 defaults 1 1
如果你还需使用其它可选分区,那就确保将它们也一并添加。
如果你要使用 swap
分区,请使用 swapon 命令确保它是可用的:
/sbin/swapon -v /dev/<zzz>
请替换 <zzz>
为
swap
分区名。
至此你已经建立了自己的工作空间,是时候下载软件包了。
本章列出了一张软件包的清单,你需要下载它们来构建一个基础的 Linux 系统。列出的软件版本号便是该软件经过确认可以正常工作的版本,也是成书时我们使用的版本。我们强烈建议不要使用更新的版本,因为某个版本的编译指令并不一定适用于更新版本。最新的软件包会包含许多问题,需要特别对应。这些对应方法会在本书的开发版本中解决并固定下来。
我们无法保证下载的地址是一直有效的。如果在本书发布后下载地址变了,大部分软件包可以用 Google (http://www.google.com/)解决。如果连搜索也失败了,那不妨试一试 http://www.linuxfromscratch.org/lfs/packages.html#packages 中提到的其他下载地址。
下载好的软件包和补丁需要保存在某处,这个某处最好是一个在整个构建过程中都能便捷地访问到的地方。另外还需要一个工作目录,用于解压并构建源码。$LFS/sources
可以同时作为软件包、补丁的存放所和工作目录。通过这个目录,所有需要的元素都将存储在 LFS
分区中,并且在整个构建过程中都可以访问。
在开始下载任务之前,先用 root
用户执行下面的命令,创建这个目录:
mkdir -v $LFS/sources
设置目录的写权限和粘滞模式。「粘滞模式」是指,即便多个用户对某个目录有写权限,但仅有文件的所有者,能在粘滞目录中删除该文件。运行以下命令将开启目录的写权限和粘滞模式:
chmod -v a+wt $LFS/sources
一个简单的一口气下载所有软件包和补丁的方法是使用 wget-list 作为 wget 的输入。例如:
wget --input-file=wget-list --continue --directory-prefix=$LFS/sources
LCTT 译注:由于这些文件都分布在国外的不同站点上,因此有些下载的会很慢。非常感谢中国科学技术大学镜像站提供的 LFS 软件包:http://mirrors.ustc.edu.cn/lfs/lfs-packages/9.0/。可以使用我们制作的 wget-list-ustchttps://github.com/LCTT/LFS-BOOK/blob/9.0-translating/wget-list-ustc 方便下载。
另外,从 LFS-7.0 开始,多了一个单独的文件 md5sums,可以在正式开始前校验所有的文件是否都正确。将这个文件复制到
$LFS/sources
目录中并执行:
pushd $LFS/sources md5sum -c md5sums popd
下载或使用别的方式获取如下软件包:
下载: http://download.savannah.gnu.org/releases/acl/acl-2.2.53.tar.gz
MD5 校验和: 007aabf1dbb550bcddde52a244cd1070
主页: https://savannah.nongnu.org/projects/attr
下载: http://download.savannah.gnu.org/releases/attr/attr-2.4.48.tar.gz
MD5 校验和: bc1e5cb5c96d99b24886f1f527d3bb3d
主页: http://www.gnu.org/software/autoconf/
下载: http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.xz
MD5 校验和: 50f97f4159805e374639a73e2636f22e
主页: http://www.gnu.org/software/automake/
下载: http://ftp.gnu.org/gnu/automake/automake-1.16.1.tar.xz
MD5 校验和: 53f38e7591fa57c3d2cee682be668e5b
主页: http://www.gnu.org/software/bash/
下载: http://ftp.gnu.org/gnu/bash/bash-5.0.tar.gz
MD5 校验和: 2b44b47b905be16f45709648f671820b
主页: https://github.com/gavinhoward/bc
下载: https://github.com/gavinhoward/bc/archive/2.1.3/bc-2.1.3.tar.gz
MD5 校验和: 2a882dc39c0fb8e36c12f590d54cc039
主页: http://www.gnu.org/software/binutils/
下载: http://ftp.gnu.org/gnu/binutils/binutils-2.32.tar.xz
MD5 校验和: 0d174cdaf85721c5723bf52355be41e6
主页: http://www.gnu.org/software/bison/
下载: http://ftp.gnu.org/gnu/bison/bison-3.4.1.tar.xz
MD5 校验和: 201286a573b12da109df96282fe4ff4a
下载: https://www.sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz
MD5 校验和: 67e051268d0c475ea773822f7500d0e5
主页: https://libcheck.github.io/check
下载: https://github.com/libcheck/check/releases/download/0.12.0/check-0.12.0.tar.gz
MD5 校验和: 31b17c6075820a434119592941186f70
主页: http://www.gnu.org/software/coreutils/
下载: http://ftp.gnu.org/gnu/coreutils/coreutils-8.31.tar.xz
MD5 校验和: 0009a224d8e288e8ec406ef0161f9293
主页: http://www.gnu.org/software/dejagnu/
下载: http://ftp.gnu.org/gnu/dejagnu/dejagnu-1.6.2.tar.gz
MD5 校验和: e1b07516533f351b3aba3423fafeffd6
主页: http://www.gnu.org/software/diffutils/
下载: http://ftp.gnu.org/gnu/diffutils/diffutils-3.7.tar.xz
MD5 校验和: 4824adc0e95dbbf11dfbdfaad6a1e461
主页: http://e2fsprogs.sourceforge.net/
下载: https://downloads.sourceforge.net/project/e2fsprogs/e2fsprogs/v1.45.3/e2fsprogs-1.45.3.tar.gz
MD5 校验和: 447a225c05f0a81121f6ddffbf55b06c
主页: https://sourceware.org/ftp/elfutils/
下载: https://sourceware.org/ftp/elfutils/0.177/elfutils-0.177.tar.bz2
MD5 校验和: 0b583722f911e1632544718d502aab87
下载: https://dev.gentoo.org/~blueness/eudev/eudev-3.2.8.tar.gz
MD5 校验和: ce166b3fdd910c2a4a840378f48fedaf
主页: https://libexpat.github.io/
下载: https://prdownloads.sourceforge.net/expat/expat-2.2.7.tar.xz
MD5 校验和: 3659bc0938db78815b5f5a9c24d732aa
主页: https://core.tcl.tk/expect/
下载: https://prdownloads.sourceforge.net/expect/expect5.45.4.tar.gz
MD5 校验和: 00fce8de158422f5ccd2666512329bd2
主页: https://www.darwinsys.com/file/
下载: ftp://ftp.astron.com/pub/file/file-5.37.tar.gz
MD5 校验和: 80c29aca745466c6c24d11f059329075
File (5.37) 可能已经不能从列出的地址下载了。该站点管理员在新版本发布后删除了旧版本。合适版本的替代下载地址为: http://www.linuxfromscratch.org/lfs/download.html#ftp。
主页: http://www.gnu.org/software/findutils/
下载: http://ftp.gnu.org/gnu/findutils/findutils-4.6.0.tar.gz
MD5 校验和: 9936aa8009438ce185bea2694a997fc1
主页: https://github.com/westes/flex
下载: https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz
MD5 校验和: 2882e3179748cc9f9c23ec593d6adc8d
主页: http://www.gnu.org/software/gawk/
下载: http://ftp.gnu.org/gnu/gawk/gawk-5.0.1.tar.xz
MD5 校验和: f9db3f6715207c6f13719713abc9c707
下载: http://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.xz
MD5 校验和: 3818ad8600447f05349098232c2ddc78
主页: http://www.gnu.org/software/gdbm/
下载: http://ftp.gnu.org/gnu/gdbm/gdbm-1.18.1.tar.gz
MD5 校验和: 988dc82182121c7570e0cb8b4fcd5415
主页: http://www.gnu.org/software/gettext/
下载: http://ftp.gnu.org/gnu/gettext/gettext-0.20.1.tar.xz
MD5 校验和: 9ed9e26ab613b668e0026222a9c23639
主页: http://www.gnu.org/software/libc/
下载: http://ftp.gnu.org/gnu/glibc/glibc-2.30.tar.xz
MD5 校验和: 2b1dbdf27b28620752956c061d62f60c
主页: http://www.gnu.org/software/gmp/
下载: http://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.xz
MD5 校验和: f58fa8001d60c4c77595fbbb62b63c1d
主页: http://www.gnu.org/software/gperf/
下载: http://ftp.gnu.org/gnu/gperf/gperf-3.1.tar.gz
MD5 校验和: 9e251c0a618ad0824b51117d5d9db87e
主页: http://www.gnu.org/software/grep/
下载: http://ftp.gnu.org/gnu/grep/grep-3.3.tar.xz
MD5 校验和: 05d0718a1b7cc706a4bdf8115363f1ed
主页: http://www.gnu.org/software/groff/
下载: http://ftp.gnu.org/gnu/groff/groff-1.22.4.tar.gz
MD5 校验和: 08fb04335e2f5e73f23ea4c3adbf0c5f
主页: http://www.gnu.org/software/grub/
下载: https://ftp.gnu.org/gnu/grub/grub-2.04.tar.xz
MD5 校验和: 5aaca6713b47ca2456d8324a58755ac7
主页: http://www.gnu.org/software/gzip/
下载: http://ftp.gnu.org/gnu/gzip/gzip-1.10.tar.xz
MD5 校验和: 691b1221694c3394f1c537df4eee39d3
主页: http://freecode.com/projects/iana-etc
下载: http://anduin.linuxfromscratch.org/LFS/iana-etc-2.30.tar.bz2
MD5 校验和: 3ba3afb1d1b261383d247f46cb135ee8
主页: http://www.gnu.org/software/inetutils/
下载: http://ftp.gnu.org/gnu/inetutils/inetutils-1.9.4.tar.xz
MD5 校验和: 87fef1fa3f603aef11c41dcc097af75e
主页: https://freedesktop.org/wiki/Software/intltool
下载: https://launchpad.net/intltool/trunk/0.51.0/+download/intltool-0.51.0.tar.gz
MD5 校验和: 12e517cac2b57a0121cda351570f1e63
主页: https://www.kernel.org/pub/linux/utils/net/iproute2/
下载: https://www.kernel.org/pub/linux/utils/net/iproute2/iproute2-5.2.0.tar.xz
MD5 校验和: 0cb2736e7bc2f56254a363d3d23703b7
主页: http://ftp.altlinux.org/pub/people/legion/kbd
下载: https://www.kernel.org/pub/linux/utils/kbd/kbd-2.2.0.tar.xz
MD5 校验和: d1d7ae0b5fb875dc082731e09cd0c8bc
下载: https://www.kernel.org/pub/linux/utils/kernel/kmod/kmod-26.tar.xz
MD5 校验和: 1129c243199bdd7db01b55a61aa19601
主页: http://www.greenwoodsoftware.com/less/
下载: http://www.greenwoodsoftware.com/less/less-551.tar.gz
MD5 校验和: 4ad4408b06d7a6626a055cb453f36819
下载: http://www.linuxfromscratch.org/lfs/downloads/9.0/lfs-bootscripts-20190524.tar.xz
MD5 校验和: c91b11e366649c9cec60c2552820fed5
主页: https://sites.google.com/site/fullycapable/
下载: https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2/libcap-2.27.tar.xz
MD5 校验和: 2e8f9fab32eb5ccb37969fe317fd17aa
主页: https://sourceware.org/libffi/
下载: ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz
MD5 校验和: 83b89587607e3eb65c70d361f13bab43
主页: http://libpipeline.nongnu.org/
下载: http://download.savannah.gnu.org/releases/libpipeline/libpipeline-1.5.1.tar.gz
MD5 校验和: 4c8fe6cd85422baafd6e060f896c61bc
主页: http://www.gnu.org/software/libtool/
下载: http://ftp.gnu.org/gnu/libtool/libtool-2.4.6.tar.xz
MD5 校验和: 1bfb9b923f2c1339b4d2ce1807064aa5
下载: https://www.kernel.org/pub/linux/kernel/v5.x/linux-5.2.8.tar.xz
MD5 校验和: 602dd0ecb8646e539fefb2beb6eb6fe0
Linux 内核更新相对比较频繁,大多数时候是因为发现了安全漏洞。当前最新的 5.2.x 内核版本应该都可以使用,除非在勘误页有其他说明。
对于网速较慢或带宽费用较高的用户如果希望更新 Linux 内核,可以将基线版本的软件包和补丁分开下载。针对次要版本中一连串的补丁程度的更新,这种操作方式也许能让你少费些时间,或者说功夫。
主页: http://www.gnu.org/software/m4/
下载: http://ftp.gnu.org/gnu/m4/m4-1.4.18.tar.xz
MD5 校验和: 730bb15d96fffe47e148d1e09235af82
主页: http://www.gnu.org/software/make/
下载: http://ftp.gnu.org/gnu/make/make-4.2.1.tar.gz
MD5 校验和: 7d0dcb6c474b258aab4d54098f2cf5a7
主页: https://www.nongnu.org/man-db/
下载: http://download.savannah.gnu.org/releases/man-db/man-db-2.8.6.1.tar.xz
MD5 校验和: 22e82fe1127f4ca95de7100168a927d1
主页: https://www.kernel.org/doc/man-pages/
下载: https://www.kernel.org/pub/linux/docs/man-pages/man-pages-5.02.tar.xz
MD5 校验和: 136e5e3380963571a079693d8ae38f52
下载: https://github.com/mesonbuild/meson/releases/download/0.51.1/meson-0.51.1.tar.gz
MD5 校验和: 48787e391ec5c052799a3dd491f73909
主页: http://www.multiprecision.org/
下载: https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz
MD5 校验和: 4125404e41e482ec68282a2e687f6c73
下载: http://www.mpfr.org/mpfr-4.0.2/mpfr-4.0.2.tar.xz
MD5 校验和: 320fbc4463d4c8cb1e566929d8adc4f8
下载: https://github.com/ninja-build/ninja/archive/v1.9.0/ninja-1.9.0.tar.gz
MD5 校验和: f340be768a76724b83e6daab69009902
主页: http://www.gnu.org/software/ncurses/
下载: http://ftp.gnu.org/gnu/ncurses/ncurses-6.1.tar.gz
MD5 校验和: 98c889aaf8d23910d2b92d65be2e737a
下载: https://www.openssl.org/source/openssl-1.1.1c.tar.gz
MD5 校验和: 15e21da6efe8aa0e0768ffd8cd37a5f6
主页: https://savannah.gnu.org/projects/patch/
下载: http://ftp.gnu.org/gnu/patch/patch-2.7.6.tar.xz
MD5 校验和: 78ad9937e4caadcba1526ef1853730d5
下载: https://www.cpan.org/src/5.0/perl-5.30.0.tar.xz
MD5 校验和: 037c35000550bdcb47552ad0f6d3064d
主页: https://www.freedesktop.org/wiki/Software/pkg-config
下载: https://pkg-config.freedesktop.org/releases/pkg-config-0.29.2.tar.gz
MD5 校验和: f6e931e319531b736fadc017f470e68a
主页: https://sourceforge.net/projects/procps-ng
下载: https://sourceforge.net/projects/procps-ng/files/Production/procps-ng-3.3.15.tar.xz
MD5 校验和: 2b0717a7cb474b3d6dfdeedfbad2eccc
主页: http://psmisc.sourceforge.net/
下载: https://sourceforge.net/projects/psmisc/files/psmisc/psmisc-23.2.tar.xz
MD5 校验和: 0524258861f00be1a02d27d39d8e5e62
下载: https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tar.xz
MD5 校验和: d33e4aae66097051c2eca45ee3604803
下载: https://docs.python.org/ftp/python/doc/3.7.4/python-3.7.4-docs-html.tar.bz2
MD5 校验和: c410337e954dbba2d04fe169c355a6a2
主页: https://tiswww.case.edu/php/chet/readline/rltop.html
下载: http://ftp.gnu.org/gnu/readline/readline-8.0.tar.gz
MD5 校验和: 7e6c1f16aee3244a69aba6e438295ca3
主页: http://www.gnu.org/software/sed/
下载: http://ftp.gnu.org/gnu/sed/sed-4.7.tar.xz
MD5 校验和: 777ddfd9d71dd06711fe91f0925e1573
下载: https://github.com/shadow-maint/shadow/releases/download/4.7/shadow-4.7.tar.xz
MD5 校验和: f7ce18c8dfd05f1a009266cb604d58b7
主页: http://www.infodrom.org/projects/sysklogd/
下载: http://www.infodrom.org/projects/sysklogd/download/sysklogd-1.5.1.tar.gz
MD5 校验和: c70599ab0d037fde724f7210c2c8d7f8
主页: https://savannah.nongnu.org/projects/sysvinit
下载: http://download.savannah.gnu.org/releases/sysvinit/sysvinit-2.95.tar.xz
MD5 校验和: 66f488c952c70ff4245774fc7e87e529
主页: http://www.gnu.org/software/tar/
下载: http://ftp.gnu.org/gnu/tar/tar-1.32.tar.xz
MD5 校验和: 83e38700a80a26e30b2df054e69956e5
主页: http://tcl.sourceforge.net/
下载: https://downloads.sourceforge.net/tcl/tcl8.6.9-src.tar.gz
MD5 校验和: aa0a121d95a0e7b73a036f26028538d4
主页: http://www.gnu.org/software/texinfo/
下载: http://ftp.gnu.org/gnu/texinfo/texinfo-6.6.tar.xz
MD5 校验和: 5231da3e6aa106cd0532b8609e5b3702
主页: https://www.iana.org/time-zones
下载: https://www.iana.org/time-zones/repository/releases/tzdata2019b.tar.gz
MD5 校验和: b26b5d7d844cb96c73ed2fb6d588daaf
下载: http://anduin.linuxfromscratch.org/LFS/udev-lfs-20171102.tar.xz
MD5 校验和: 27cd82f9a61422e186b9d6759ddf1634
主页: http://freecode.com/projects/util-linux
下载: https://www.kernel.org/pub/linux/utils/util-linux/v2.34/util-linux-2.34.tar.xz
MD5 校验和: a78cbeaed9c39094b96a48ba8f891d50
下载: https://github.com/vim/vim/archive/v8.1.1846/vim-8.1.1846.tar.gz
MD5 校验和: 4f129a05254d93c739fcede843df87df
主页: https://github.com/chorny/XML-Parser
下载: https://cpan.metacpan.org/authors/id/T/TO/TODDR/XML-Parser-2.44.tar.gz
MD5 校验和: af4813fe3952362451201ced6fbce379
下载: https://tukaani.org/xz/xz-5.2.4.tar.xz
MD5 校验和: 003e4d0b1b1899fc6e3000b24feddf7c
下载: https://zlib.net/zlib-1.2.11.tar.xz
MD5 校验和: 85adef240c5f370b308da8c938951a68
这些安装包的总计:约 391 MB
除了下载软件包外,还需要几个补丁。这些补丁修正了软件包中应该由维护者来解决的问题。补丁也会对软件包做一些小调整方便大家使用。构建 LFS 系统需要下面的补丁:
下载: http://www.linuxfromscratch.org/patches/lfs/9.0/bzip2-1.0.8-install_docs-1.patch
MD5 校验和: 6a5ac7e89b791aae556de0f745916f7f
下载: http://www.linuxfromscratch.org/patches/lfs/9.0/coreutils-8.31-i18n-1.patch
MD5 校验和: a9404fb575dfd5514f3c8f4120f9ca7d
下载: http://www.linuxfromscratch.org/patches/lfs/9.0/glibc-2.30-fhs-1.patch
MD5 校验和: 9a5997c3452909b1769918c759eff8a2
下载: http://www.linuxfromscratch.org/patches/lfs/9.0/kbd-2.2.0-backspace-1.patch
MD5 校验和: f75cca16a38da6caa7d52151f7136895
下载: http://www.linuxfromscratch.org/patches/lfs/9.0/sysvinit-2.95-consolidated-1.patch
MD5 校验和: 4900322141d493e74020c9cf437b2cdc
这些补丁文件总大小: 约 186.8 KB
除了以上所要求的补丁外,还有一些由 LFS 社区创建的可选补丁。这些可选补丁或是解决了一些小问题,或是打开某个默认关闭的功能。请查阅补丁数据库 http://www.linuxfromscratch.org/patches/downloads/,获取适合你系统需求的额外补丁。
在本章,我们还需要为构建临时系统做一些额外的准备工作。我们会在 $LFS
中新建一个用于安装临时工具的目录,增加一个非特权用户用于降低风险,并为该用户创建合适的构建环境。我们还会解释用于测量构建
LFS 软件包花费时间的单位,或称「SBUs」,并给出一些关于软件包测试套件的信息。
所有 第 5 章 中编译的软件都会安装到
$LFS/tools
中,以确保和 第 6 章
中编译的软件相互分离。这里编译的软件是临时工具,并不会成为最终的 LFS
系统中的一部分。将这些软件保存在单独的目录中,用完后方便弃置。这样做也可以防止这些程序在宿主机生成目录中突然停止工作(在
第 5 章 中很容易发生意外)。
以 root
用户运行以下的命令来创建需要的文件夹:
mkdir -v $LFS/tools
下一步是在宿主系统中创建 /tools
的符号链接。将其指向
LFS 分区中新建的目录。同样以 root
用户运行下面的命令:
ln -sv $LFS/tools /
上面的命令是正确的。ln
命令有一些语法变种,所以在报出你觉得可能是错误的信息之前检查一下 info coreutils ln 和
ln(1)
。
创建的符号链接使得编译的工具链总是指向 /tools
文件夹,也就是说编译器、汇编器以及链接器无论是在第五章中(我们仍然使用宿主机的一些工具的时)还是之后(当我们「chrooted」到
LFS 分区时)都可以工作。
当以 root
用户登录时,犯一个小错误可能会破坏或摧毁整个系统。因此,我们建议在本章中以非特权用户编译软件包。你当然可以使用你自己的用户名,但为了使其更容易建立一个干净的工作环境,创建一个名为
lfs
的新用户作为新组(同样命名为 lfs
)的成员,并在安装过程中使用这个用户。以 root
用户运行以下命令来添加新用户:
groupadd lfs useradd -s /bin/bash -g lfs -m -k /dev/null lfs
命令行选项释义:
-s
/bin/bash
将 bash 设置为
lfs
用户的默认 shell。
-g
lfs
这个选项将用户 lfs
添加到组
lfs
中。
-m
为 lfs
用户创建主目录。
-k
/dev/null
这个参数通过改变输入位置为特殊的空(null)设备,以防止可能从框架目录(默认是 /etc/skel
)复制文件。
lfs
这是创建的组和用户的实际名称。
要以 lfs
用户身份登录(相较于以 root
身份登录的情况下切换到 lfs
用户时,无需为 lfs
用户设置密码),需要给 lfs
用户一个密码:
passwd lfs
通过更改目录所有者为 lfs
,为用户 lfs
赋予了访问 $LFS/tools
目录的所有权限:
chown -v lfs $LFS/tools
如果你按照建议创建了单独的工作目录,给 lfs
用户赋予这个目录的所有权:
chown -v lfs $LFS/sources
下一步,以 lfs
用户身份登录。可以能通过一个虚拟控制台、显示控制器,或者下面的切换用户命令完成:
su - lfs
这个「-
」授意 su 启动登录 shell,而非 non-login
shell。关于这两种 shell 类型的区别,可以在 bash(1)
和 info
bash 中查看详细详情。
通过为 bash shell
创建两个开机启动的文件,设置合适的工作环境。当以 lfs
用户身份登录时,运行以下命令创建一个新的 .bash_profile
文件:
cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF
当以 lfs
用户身份登录时,初始 shell 通常是一个
login 的 shell,它先读取宿主机的
/etc/profile
文件(很可能包括一些设定和环境变量),然后是 .bash_profile
文件。.bash_profile
中的命令 exec env -i.../bin/bash 用一个除了
HOME
,TERM
和 PS1
变量外,其他环境完全为空的新 shell 代替运行中的
shell。这能确保不会有潜在的和意想不到的危险环境变量,从宿主机泄露到构建环境中。这样做主要是为了确保环境的干净。
新的 shell 实例是一个 non-login
的 shell,不会读取 /etc/profile
或者
.bash_profile
文件,而是读取
.bashrc
。现在,创建 .bashrc
文件:
cat > ~/.bashrc << "EOF"
set +h
umask 022
LFS=/mnt/lfs
LC_ALL=POSIX
LFS_TGT=$(uname -m)-lfs-linux-gnu
PATH=/tools/bin:/bin:/usr/bin
export LFS LC_ALL LFS_TGT PATH
EOF
set +h 命令关闭了
bash
的哈希功能。哈希通常是个好用的功能——bash 用一个哈希表来记录可执行文件的完整路径,以规避对
PATH
进行检索的时间和对同一个可执行文件的重复寻找。然而,新工具在安装后,应马上使用。通过关闭哈希功能,程序执行的时候就会一直搜索
PATH
。如此,新编译的工具一旦可用,shell 便能在马上在文件夹
$LFS/tools
中找到它们,而不会去记录存在于不同地方的旧版该程序。
设置用户文件新建时的掩码(umask)为 022,以确保新建的文件和目录只有其所有者可写,但任何人都可读可执行(假设系统调用的
open(2)
使用的是默认模式,新文件将使用 664
权限模式、文件夹为 755 模式)。
LFS
变量应设置成选定的挂载点。
LC_ALL
变量控制某些程序的本地化,使它们的消息遵循特定国家的惯例。设置 LC_ALL
为「POSIX」或「C」(两者是等价的),确保在 chroot
环境中一切能如期望的那样进行。
LFS_TGT
变量设置了一个虽非默认,但在构建交叉编译器、连接器和交叉编译临时工作链时,用得上到的兼容的机器说明。第 5.2 节 「工具链技术说明」中包含更多信息。
通过把 /tools/bin
放在标准 PATH
变量的前面,使得所有在 第 5 章 中安装的程序,一经安装 shell
便能马上使用。与之配合的关闭哈希功能,能在第五章环境中的程序在可用的情况下,限制使用宿主机中旧程序的风险。
最后,启用刚才创建的用户配置,为构建临时工具完全准备好环境:
source ~/.bash_profile
在着手之前,不少人想知道编译和安装一个软件包到底需要多长的时间。因为 Linux From Scratch 能够运行于众多的硬件上,所以具体的编译时间无法一概而论。举一个简单的例子:在最快的系统上,编译最大的软件包(Glibc)需要约 20 分钟,但在慢的系统上有可能需要 3 天!所以,这里使用标准构建单元(SBU)来代替具体的编译时间。
SBU 衡量方式如下。我们编译的第一个软件包是 第 5 章 的 Binutils。将这个软件包在编译时所需要的时间,作为标准构建单元(SBU)。其它软件的构建时间,都以其为标准进行比较。
举个例子,假如编译某个软件耗时 4.5 SBUs。这意味着如果这个系统,在编译 Binutils 需要 10 分钟,大概需要约 45 分钟来构建此软件包。很幸运,大部分的系统在生成 Binutils 时花的时间都要比这个系统要短。
一般来说,SBU 的结果并不完全准确,因为影响编译的因素太多,包括宿主机系统中 GCC 的版本也会影响到。所以更多的时候,这仅仅是提供一个编译和安装时间的预估。然而,在某些情况下,这个数字可能偏差约十几分钟。
对于大多数带有多个处理器(或内核)的现代操作系统而言,可以通过设置环境变量或者是告知 make 程序具体可用的处理器数目,通过「并行 make」来减少编译的时间。例如,对于 Core2Duo 可以通过以下参数实现两个处理器同时编译:
export MAKEFLAGS='-j 2'
或者直接这样来构建:
make -j2
以该方式使用多处理器时,SBU 值可能比书中的正常值还要大。某些情况下,make 过程仅仅是简单的就失败了。分析错误日志也十分困难:因为不同处理器之间的执行路线是交错的。如果你在构建过程中遇到了问题,为了正确分析错误信息,最好返回单处理器。
很多软件包都提供相应的测试套件。为新构建的软件包运行测试套件是非常好的习惯,因为这样做可以「保证」所有功能都已编译正确。经由一系列的测试,套件往往能够检查出软件包的功能是否都如开发人员预想的那样。然而,这并不能保证所测试的软件包万无一失。
有一些测试套件要相较而言更为重要。例如,核心工具链软件包——GCC, Binutils 和 Glibc——对于对于一个系统的正常运转起到至关重要的作用。要完成 GCC 和 Glibc 的测试套件可能要花费很长的时间,特别是对于硬件比较慢的设备来说,但还是强烈推荐完成它们!
经验表明,在 第 5 章 中运行测试套件并不是什么好主意。在该章节运行测试将不可避免地面临一个问题,宿主机或多或少会对测试产生影响,这经常导致一些令人摸不着头脑的错误信息。因为在 第 5 章 中构建的这些工具只是临时的,最终我们并不需要它们,所以我们并不推荐普通读者在 第 5 章 中运行测试套件。虽然为测试者和开发者提供了测试套件的说明,但是这依旧是可选项。
对 Binutils 和 GCC
执行测试套件时可能会使伪终端(PTYs)耗尽。造成大量的测试失败。造成问题的原因有很多,但最有可能的原因是宿主系统没能正确设置
devpts
文件系统。针对这个问题在 http://www.linuxfromscratch.org/lfs/faq.html#no-ptys
中有更详尽的讨论。
还有一些测试套件运行错误,是开发人员已知且视为不重要的。查看 http://www.linuxfromscratch.org/lfs/build-logs/9.0/ 中的日志,确认这些失败信息是否都是意料之中的。该网址中涉及的内容会贯穿全书所有的测试。
本章将向您展示如何构造一个最小的 Linux 系统。该系统将包含刚好足够构建 第 6 章 中最终 LFS 系统所需的工具,以及一个比最小环境具有更好用户便利性的工作环境。
构建这个最小系统有两个步骤。第一步,是构建一个与宿主系统无关的新工具链(编译器、汇编器、链接器、库和一些有用的工具)。第二步则是使用该工具链,去构建其它的基础工具。
本章中编译得到的文件将被安装在目录 $LFS/tools
中,以确保在下一章中安装的文件和宿主系统生成的目录相互分离。由于此处编译的软件包都是临时性的,因此,我们不愿意它们污染后面即将构成的
LFS 系统。
这一节解释总体构建方法之中的某些基本原理和技术细节。本节中的所有问题并无需马上消化。在进行实际构建的过程中,绝大部分的信息会变得愈加清晰。过程中随时都可以查阅本小节的内容。
纵览 第 5 章 的目标是生成一个临时的系统,这个系统中包含一个已知的较好工具集,并且工具集可以独立于宿主系统。通过使用 chroot,其余各章中的命令将被包含在此环境中,以保证目标 LFS 系统能够洁净且无故障地生成。该构建过程的设计就是为了使得新读者承担最少的风险,同时还能有最好的指导价值。
在继续前,请留心工作平台的名称,它时常被称为目标三元组。要确定目标三元组的名称有一个简单的法子,那就是运行许多软件包的源码附带的脚本
config.guess。解压
Binutils 的源码并运行脚本:./config.guess
并注意它的输出。举一个例子,Intel 的 32 位处理器输出会是 i686-pc-linux-gnu。而在一个 64 位系统上,则会是
x86_64-pc-linux-gnu。
也请留心平台的动态链接器的的名称,它时常被称为动态加载器(不要与 Binutils 中的标准链接器
ld 混为一谈)。动态链接器由
Glibc 提供,用于寻找和加载程序所需的共享库,为程序的运行做准备,以及运行程序。32 位的 Intel
机器,动态链接器的名称是 ld-linux.so.2
(64
位系统则是 ld-linux-x86-64.so.2
)。确定动态链接名的一个确定的方法,就是检查随机二进制文件,通过在宿主机运行:readelf
-l <name of binary> | grep interpreter
并注意它的输出。覆盖全平台的权威参考在 Glibc 源码树根目录的 shlib-versions
文件中。
下面是 第 5 章 构建方法的几个关键技术点:
通过改变 LFS_TGT
变量的目标系统三段式中的「vendor(供应商)」字段,稍微调整一下工作平台的名称,以保证第一遍构建
Binutils 和 GCC
时能够生成兼容的交叉链接器和交叉编译器。此处的交叉链接器和交叉编译器生成的二进制文件与当前的硬件兼容,而不是用于其它的硬件架构。
临时库经交叉编译获得。由于交叉编译原本就不应该依赖于宿主系统,因此,通过降低宿主系统的头文件或库进入新工具的可能性,该方法可去除目标系统中潜在的污染。交叉编译的方式,还可以在 64 位硬件平台上同时构建出 32 位和 64 位库。
谨慎地操作 GCC 源码告诉编译器,哪个是即将被采用的目标动态链接器。
Binutils 是首个安装的包,因为无论是执行 GCC 还是 Glibc 的 configure 时,都将围绕关汇编器和链接器实施多项特性测试,来判断哪些软件特性要启用或是禁用。其重要性可能更甚于你对它的第一印象。GCC 或 Glibc 的错误配置会导致工具链出现难以捉摸的问题,可能直到整个构建过程接近尾声时,这些问题才会显现出来。不过,通常情况下,在你进行大量的无用功之前,一次测试套件的失败便会将该错误高亮出来。
Binutils 将其汇编器和链接器安装在两处,/tools/bin
和 /tools/$LFS_TGT/bin
。有一处的工具是另一处的硬链接。链接器的一个重要方面是它的库搜索顺序。可以给
ld 传递参数
--verbose
获取详细信息。例如,ld --verbose | grep
SEARCH
将说明当前的搜索路径及其顺序。通过编译一个 dummy(假)程序并向链接器传递
--verbose
参数,可显示
ld
都链接了哪些文件。例如,gcc dummy.c
-Wl,--verbose 2>&1 | grep succeeded
将显示链接过程中成功打开的所有文件。
下一个安装的包是 GCC。下面便是运行 GCC 的 configure 的输出示例:
checking what assembler to use... /tools/i686-lfs-linux-gnu/bin/as
checking what linker to use... /tools/i686-lfs-linux-gnu/bin/ld
基于上述原因,这很重要。这还说明了 GCC 的配置脚本并不会搜索 PATH 目录来寻找使用什么工具。不过,在
gcc
自身的实际运行中,并不需要使用同样的搜索路径。运行:gcc
-print-prog-name=ld
可获知 gcc 使用是何种标准链接器。
在编译 dummy(假)程序时,向 gcc 传递命令行选项 -v
可获得详细信息。例如,gcc -v dummy.c
将显示预处理器、编译和汇编阶段的详细信息,包括 gcc 的 include 搜索路径及其顺序。
下一个安装的是经过净化的 Linux API 头文件。这些头文件可使得标准 C 库(Glibc)与 Linux 内核提供的特性进行交行交互。
下一个安装的软件包是 Glibc。构建 Glibc
时,最重要的几个注意点是编译器、二进制工具和内核头文件。编译器通常不成问题,因为 Glibc
将一直使用传递给它配置脚本的,有关 --host
参数的编译器。如,在我们的这个场景中,编译器就是
i686-lfs-linux-gnu-gcc。而二进制工具和内核头文件可能就要复杂一些了。因此,请不要冒险,并利用可用的配置开关来强制正确的选择。configure
运行完毕,目录 glibc-build
下的文件
config.make
包含有所有的重要细节。需要注意的是,CC="i686-lfs-gnu-gcc"
用来控制使用哪个二进制工具,-nostdinc
和 -isystem
标志用来控制编译器的
include 搜索路径。这些条目强调了 Glibc
包的一个重要方面,即其构建机制是非常自给自足的,通常并不依赖默工具链的默认设置。
在第二遍编译 Binutils 过程中,我们能够利用配置开关 --with-lib-path
来控制 ld 的库搜索路径。
第二遍编译 GCC 时,也需要修改其源代码,以告诉 GCC 使用新的动态链接器。如果不加修改,将导致 GCC
的自身程序中嵌入源自宿主系统目录 /lib
中的动态链接器,这违背了独立于宿主系统的目的。正是基于前面的这个出发点,核心工具链是自包含和自托管的。第 5 章 其它的软件包都将在 /tools
中的新 Glibc 的基础上进行构建。
在进入 第 6 章中的 chroot
环境前,将安装的首个主要的软件包是 Glibc,这是因为它天生具有前面提及的自给自足特点。一旦将 Glibc 安装到
/usr
中,我们将快速改变工具链的默认设置,然后继续构建目标
LFS 系统的其余部分。
编译软件包时,本指南假设你已知晓这几点:
有几个软件包,在编译之前需要打补丁来规避一些问题。有的补丁在本章和下一章中都需要,但有些只有其中一章需要。因此,如果某章看起来缺少某个补丁的下载说明时,不用担心。安装补丁的时候也许会遇到关于 offset 或者 fuzz 的警告信息。别担心这些警告,补丁还是会成功安装的。
在大部分软件包的编译过程中,屏幕上都可能出现几个警告。这都很正常,可以安全地忽略。这些警告正如它们描述的那样,是对使用过时的 C 或 C++ 语法的警告,而不是这些语法不可用。C 语言的标准经常改变,一些软件包仍然在使用旧的标准。这并不是一个问题,不过确实会弹出警告。
最后确认一次,是否正确设置了 LFS
环境变量:
echo $LFS
确认输出显示的是 LFS 分区挂载点的路径,在我们的例子中,也就是 /mnt/lfs
。
最后,有两个重要事项必须强调:
编译指南假定你已经正确地设置了 宿主系统要求 和符号链接:
shell 使用的是 bash
sh 是到 bash 的符号链接。
/usr/bin/awk 是到 gawk 的符号链接。
/usr/bin/yacc 是到 bison 的符号链接,或者是一个执行 bison 的小脚本。
Binutils 软件包包含一个链接器、一个汇编器、以及其它处理目标文件的工具。
返回前面章节重新阅读注意事项。了解标记为重要的注意事项能在后面帮你省去很多麻烦。
第一个编译的软件包是 Binutils 软件包,这点很重要,因为 Glibc 和 GCC 都会对可用的链接器和汇编器执行各种测试,以决定启用它们自身的哪些功能。
Binutils 手册建议,在源码目录之外一个专门的编译目录里面编译 Binutils:
mkdir -v build cd build
为了衡量在本书中其余部分所使用 SBU
值,我们要测量一下这个软件包从配置到包括第一次安装在内的编译时间。为了轻松的做到这点,会用类似
time { ./configure ...
&& ... && make install; }
的方式将命令包裹在 time 命令中。
第五章中,大致的构建 SBU 值和所需磁盘空间不包括测试套件数据。
现在准备编译 Binutils:
../configure --prefix=/tools \ --with-sysroot=$LFS \ --with-lib-path=/tools/lib \ --target=$LFS_TGT \ --disable-nls \ --disable-werror
配置选项的含义:
--prefix=/tools
告诉配置脚本将 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
这会防止来自宿主编译器的警告事件导致停止编译。
继续编译软件包:
make
现在编译完成了。通常现在我们会运行测试套件,但在这个初期阶段,测试套件框架(Tcl、Expect 和 DejaGNU)还没有就绪。在此进行测试的收效甚微,因为第一遍编译的程序很快会被第二遍的代替。
如果是在 x86_64 上构建,创建符号链接,以确保工具链的完整性:
case $(uname -m) in x86_64) mkdir -v /tools/lib && ln -sv lib /tools/lib64 ;; esac
安装软件包:
make install
该软件包的详细信息位于 第 6.16.2 节 「Binutils 内容」。
GCC 软件包包括 GNU 编译器集,其中有 C 和 C++ 的编译器。
现在 GCC 需要 GMP、MPFR 和 MPC 软件包。在你的主机发行版中可能并不包括这些软件包,它们将和 GCC 一起编译。将每个解压软件包到 GCC 的目录下,并重命名解压后得到的目录,以便 GCC 编译过程中能自动使用这些软件:
读者经常对本章节产生误解。过程与之前的章节(软件包构建说明)中提到的一样。首先从源目录中解压 gcc 的源码包,然后进入创建的目录中。接着才可以执行下面的指令。
tar -xf ../mpfr-4.0.2.tar.xz mv -v mpfr-4.0.2 mpfr tar -xf ../gmp-6.1.2.tar.xz mv -v gmp-6.1.2 gmp tar -xf ../mpc-1.1.0.tar.gz mv -v mpc-1.1.0 mpc
下面的指令将会修改 GCC 默认的动态链接器的位置,安装到 /tools
目录中的。并将 /usr/include
从 GCC 的 include 检索路径中移除。执行:
for file in gcc/config/{linux,i386/linux{,64}}.h do cp -uv $file{,.orig} sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \ -e 's@/usr@/tools@g' $file.orig > $file echo ' #undef STANDARD_STARTFILE_PREFIX_1 #undef STANDARD_STARTFILE_PREFIX_2 #define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/" #define STANDARD_STARTFILE_PREFIX_2 ""' >> $file touch $file.orig done
如果上面的内容看起来有些难以理解,那让我们慢慢消化吧。首先,我们复制文件 gcc/config/linux.h
,gcc/config/i386/linux.h
,和 gcc/config/i368/linux64.h
,给复制的文件加上「.orig」后缀。然后第一个
sed
表达式在每个「/lib/ld」,「/lib64/ld」或者「/lib32/ld」实例前面增加「/tools」,第二个
sed 表达式替换「/usr」的硬编码实例。然后,我们添加这改变默认 startfile
前缀到文件末尾的定义语句。注意「/tools/lib/」后面的「/」是必须的。最后,我们用 touch 更新复制文件的时间戳。当与
cp -u
一起使用时,可以防止命令被无意中运行两次造成对原始文件意外的更改。
最后,在 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 手册建议在源目录之外一个专门的编译目录中编译 GCC:
mkdir -v build cd build
准备编译 GCC:
../configure \ --target=$LFS_TGT \ --prefix=/tools \ --with-glibc-version=2.11 \ --with-sysroot=$LFS \ --with-newlib \ --without-headers \ --with-local-prefix=/tools \ --with-native-system-header-dir=/tools/include \ --disable-nls \ --disable-shared \ --disable-multilib \ --disable-decimal-float \ --disable-threads \ --disable-libatomic \ --disable-libgomp \ --disable-libquadmath \ --disable-libssp \ --disable-libvtv \ --disable-libstdcxx \ --enable-languages=c,c++
配置选项的含义:
--with-newlib
由于还没有可用的 C 库,这确保编译 libgcc 时定义了常数 inhibit_libc。这可以防止编译任何需要 libc 支持的代码。
--without-headers
在创建完整的交叉编译器时,GCC 要求标准头文件和目标系统兼容。对于我们的目的来说,不需要这些头文件。这个选项可以防止 GCC 查找它们。
--with-local-prefix=/tools
GCC 会查找本地已安装的 include 文件的系统位置。默认是 /usr/local
。把它设置为 /tools
能把主机位置中的 /usr/local
从 GCC 的搜索路径中排除。
--with-native-system-header-dir=/tools/include
GCC 默认会在 /usr/include
中查找系统头文件。和 sysroot 选项一起使用,会转换为 $LFS/usr/include
。在后面两个章节中头文件会被安装到
$LFS/tools/include
。这个选项确保 gcc 能正确找到它们。第二次编译 GCC 时,同样的选项可以保证不会去寻找主机系统的头文件。
--disable-shared
这个选项强制 GCC 静态链接到它的内部库。我们这样做是为了避免主机系统可能出现的问题。
--disable-decimal-float,
--disable-threads, --disable-libatomic,
--disable-libgomp, --disable-libquadmath,
--disable-libssp, --disable-libvtv,
--disable-libstdcxx
这些选项取消了对十进制浮点数扩展、线程化、libatomic、libgomp、libquadmath、libssp、libvtv、libcilkrts 和 C++ 标准库的支持。这些功能在编译交叉编译器的时候会导致编译失败,对于交叉编译临时 libc 来说也没有必要。
--disable-multilib
在 x86_64 机器上,LFS 还不支持 multilib 配置。这个选项对 x86 来说无害。
--enable-languages=c,c++
这个选项确保只编译 C 和 C++ 编译器。这些是现在唯一需要的语言。
运行命令编译 GCC:
make
现在编译完成了。在这里,通常会运行测试套件,但正如前面提到的,测试套件框架还没有准备好。在此进行测试的并没有太多好处,因为第一遍编译的程序很快会被取代。
安装软件包:
make install
该软件包的详细信息请参见: 第 6.21.2 节 「GCC 软件包内容」。
Linux API 头文件(在 linux-5.2.8.tar.xz 里)会将内核 API 导出给 Glibc 使用。
Linux 内核需要展示供系统 C 库(在 LFS 中是 Glibc)使用的应用程序编程接口(API)。这通过在 Linux 内核源代码 tar 包中包括一些 C 头文件来完成。
确认这里没有陈旧的文件且不依赖于之前的操作:
make mrproper
从源代码中提取用户可见的内核头文件。把他们保存在一个临时本地文件夹中然后复制到所需的位置,因为解压过程会移除目标文件夹中任何已有的文件。
make INSTALL_HDR_PATH=dest headers_install cp -rv dest/include/* /tools/include
该软件包的详细信息请参见: 第 6.7.2 节 「Linux API 头文件内容」。
Glibc 软件包包含了主要的 C 函数库。这个库提供了分配内存、搜索目录、打开关闭文件、读写文件、操作字符串、模式匹配、基础算法等基本程序。
Glibc 手册建议在源文件夹之外的一个专用文件夹中编译 Glibc:
mkdir -v build cd build
下一步,准备编译 Glibc:
../configure \ --prefix=/tools \ --host=$LFS_TGT \ --build=$(../scripts/config.guess) \ --enable-kernel=3.2 \ --with-headers=/tools/include
配置选项的含义:
--host=$LFS_TGT,
--build=$(../scripts/config.guess)
这些选项的组合效果是 Glibc 的构建系统配置它自己用 /tools
里面的交叉链接器和交叉编译器交叉编译自己。
--enable-kernel=3.2
这告诉 Glibc 编译能支持 3.2 以及之后的内核库。更早的内核版本不受支持。
--with-headers=/tools/include
告诉 Glibc 利用刚刚安装在 tools 文件夹中的头文件编译自身,此能够根据内核的具体特性提供更好的优化。
在这个过程中,可能会出现下面的警告:
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 软件包的一部分,主机发行版应该提供了。
有报告说用「parallel make」编译这个软件包的时候会失败。如果出现这种情况,用「-j1」选项重新运行 make 命令。
编译软件包:
make
安装软件包:
make install
到了这里,必须停下来确认新工具链的基本功能(编译和链接)都是像预期的那样正常工作。运行下面的命令进行全面的检查:
echo 'int main(){}' > dummy.c $LFS_TGT-gcc dummy.c readelf -l a.out | grep ': /tools'
如果一切工作正常的话,这里应该没有错误,最后一个命令的输出形式会是:
[Requesting program interpreter: /tools/lib64/ld-linux-x86-64.so.2]
注意 32 位机器上对应的解释器名字是 /tools/lib/ld-linux.so.2
。
如果输出不是像上面那样或者根本就没有输出,那么可能某些地方出错了。调查并回溯这些步骤,找出问题所在并改正它。在继续之前必须解决这个问题。
一旦一切都顺利,清理测试文件:
rm -v dummy.c a.out
在后面的编译 Binutils 章节时会再一次检查工具链是否正确编译。如果 Binutils 编译失败,说明之前安装 Binutils、GCC、或者 Glibc 时某些地方出现了错误。
该软件包的详细信息请参见: 第 6.9.3 节 「Glibc 软件包内容」。
Libstdc++ 是标准的 C++ 库。需要用它来编译 C++ 代码(GCC 的一部分是用 C++ 写的),但是在构建 gcc-第 1 遍 时,我们需要推迟它的安装进程,因为依赖的 glibc,还未部署在 /tools 目录中。
Libstdc++ 是标准的 C++ 库。g++ 编译器正确运行需要它。
Libstdc++ 是 GCC
源文件的一部分。你首先应该解压 GCC 的压缩包,然后进入 gcc-9.2.0
文件夹。
为 Libstdc++ 另外创建一个用于构建的文件夹并进入该文件夹:
mkdir -v build cd build
准备编译 Libstdc++:
../libstdc++-v3/configure \ --host=$LFS_TGT \ --prefix=/tools \ --disable-multilib \ --disable-nls \ --disable-libstdcxx-threads \ --disable-libstdcxx-pch \ --with-gxx-include-dir=/tools/$LFS_TGT/include/c++/9.2.0
配置选项的含义:
--host=...
指示使用我们刚才编译的交叉编译器,而不是 /usr/bin
中的。
--disable-libstdcxx-threads
由于我们还没有编译 C 线程库,C++ 的也还不能编译。
--disable-libstdcxx-pch
此选项防止安装预编译文件,此步骤并不需要。
--with-gxx-include-dir=/tools/$LFS_TGT/include/c++/9.2.0
这是 C++ 编译器搜索标准 include 文件的位置。在一般的编译中,这个信息自动从顶层文件夹中传入 Libstdc++ configure 选项。在我们的例子中,必须明确给出这信息。
编译 libstdc++:
make
安装库:
make install
该软件包的详细信息请参见: 第 6.21.2 节 「GCC 软件包内容」。
Binutils 软件包包含一个链接器、一个汇编器、以及其它处理目标文件的工具。
再次新建一个单独的编译文件夹:
mkdir -v build cd build
准备编译 Binutils:
CC=$LFS_TGT-gcc \ AR=$LFS_TGT-ar \ RANLIB=$LFS_TGT-ranlib \ ../configure \ --prefix=/tools \ --disable-nls \ --disable-werror \ --with-lib-path=/tools/lib \ --with-sysroot
新配置选项的含义:
CC=$LFS_TGT-gcc AR=$LFS_TGT-ar
RANLIB=$LFS_TGT-ranlib
因为这是真正的原生编译 Binutils,设置这些变量能确保编译系统使用交叉编译器和相关的工具,而不是宿主系统中已有的。
--with-lib-path=/tools/lib
这告诉配置脚本在编译 Binutils 的时候指定库搜索目录,此处将 /tools/lib
传递到链接器。
--with-sysroot
sysroot 功能使链接器可以找到包括在其命令行中的其它共享对象明确需要的共享对象。否则的话,在某些主机上一些软件包可能会编译不成功。
编译软件包:
make
安装软件包:
make install
现在,为下一章的「Re-adjusting」阶段准备链接器:
make -C ld clean make -C ld LIB_PATH=/usr/lib:/lib cp -v ld/ld-new /tools/bin
make 参数的含义:
-C ld
clean
告诉 make 程序移除所有 ld
子目录中编译过的文件。
-C ld
LIB_PATH=/usr/lib:/lib
这个选项重新编译 ld
子目录中的所有文件。在命令行中指定 Makefile 的 LIB_PATH
变量可以使我们能够重写临时工具的默认值并指向正确的最终路径。该变量的值指定链接器的默认库搜索路径。下一章中会用到这个准备。
该软件包的详细信息请参见: 第 6.16.2 节 「Binutils 内容」。
GCC 软件包包括 GNU 编译器集,其中有 C 和 C++ 的编译器。
我们第一次编译 GCC 的时候安装了一些内部系统头文件。其中的一个 limits.h
会反过来包括对应的系统头文件 limits.h
,在我们的例子中,是 /tools/include/limits.h
。但是,第一次编译 gcc 的时候
/tools/include/limits.h
并不存在,因此
GCC 安装的内部头文件只是部分的自包含文件,并不包括系统头文件的扩展功能。这足以编译临时 libc,但是这次编译 GCC
要求完整的内部头文件。使用和正常情况下 GCC 编译系统使用的相同的命令创建一个完整版本的内部头文件:
cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \ `dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include-fixed/limits.h
再一次更改 GCC 的默认动态链接器的位置,使用安装在 /tools
的那个。
for file in gcc/config/{linux,i386/linux{,64}}.h do cp -uv $file{,.orig} sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \ -e 's@/usr@/tools@g' $file.orig > $file echo ' #undef STANDARD_STARTFILE_PREFIX_1 #undef STANDARD_STARTFILE_PREFIX_2 #define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/" #define STANDARD_STARTFILE_PREFIX_2 ""' >> $file touch $file.orig done
如果是在 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 一样,它要求 GMP、MPFR 和 MPC 软件包。解压 tar 包并把它们重名为到所需的文件夹名称:
tar -xf ../mpfr-4.0.2.tar.xz mv -v mpfr-4.0.2 mpfr tar -xf ../gmp-6.1.2.tar.xz mv -v gmp-6.1.2 gmp tar -xf ../mpc-1.1.0.tar.gz mv -v mpc-1.1.0 mpc
再次创建独立的编译文件夹:
mkdir -v build cd build
在开始编译 GCC 之前,记住取消所有会覆盖默认优化选项的环境变量。
准备编译 GCC:
CC=$LFS_TGT-gcc \ CXX=$LFS_TGT-g++ \ AR=$LFS_TGT-ar \ RANLIB=$LFS_TGT-ranlib \ ../configure \ --prefix=/tools \ --with-local-prefix=/tools \ --with-native-system-header-dir=/tools/include \ --enable-languages=c,c++ \ --disable-libstdcxx-pch \ --disable-multilib \ --disable-bootstrap \ --disable-libgomp
新配置选项的含义:
--enable-languages=c,c++
这个选项确保编译了 C 和 C++ 编译器。
--disable-libstdcxx-pch
不为 libstdc++
编译预编译的头文件(PCH)。这会花费很多时间,却对我们没有用处。
--disable-bootstrap
对于原生编译的 GCC,默认是做一个「引导」构建。这不仅会编译 GCC 一次,而是会编译很多次。使用初次编译的程序去编译其自身第二次,然后同样地进行第三次。比较第二次和第三次迭代确保其能完美地复制自身。这也能预示编译是正确地。但是,LFS 的构建方法能够提供一个稳定的编译器,避免每次都需要重新引导。
编译软件包:
make
安装软件包:
make install
作为画龙点睛,这里创建一个符号链接。很多程序和脚本执行 cc 而不是 gcc 来保证程序的通用性,并且在所有的 Unix 类型的系统上都能用,而非仅局限于安装了 GCC 的 Unix 类型的系统。运行 cc 使得系统管理员不用考虑要安装那种 C 编译器:
ln -sv gcc /tools/bin/cc
到了这里,必须停下来确认新工具链的基本功能(编译和链接)都是像预期的那样正常工作。运行下面的命令进行全面的检查:
echo 'int main(){}' > dummy.c cc dummy.c readelf -l a.out | grep ': /tools'
如果一切工作正常的话,这里应该没有错误,最后一个命令的输出形式会是:
[Requesting program interpreter: /tools/lib64/ld-linux-x86-64.so.2]
注意 32 位机器的动态链接是 /tools/lib/ld-linux.so.2。
如果输出不是像上面那样或者根本就没有输出,那么可能某些地方出错了。调查并回溯这些步骤,找出问题所在并改正它。在继续之前必须解决这个问题。首先,使用
gcc 而不是
cc
再次进行全面的检查。如果能运行,就符号链接 /tools/bin/cc
就不见了。像上面介绍的那样新建符号链接。下一步,确认
PATH
是正确的。这能通过运行 echo $PATH 检验,验证
/tools/bin
在列表的前面。如果
PATH
是错误的,这意味着你可能不是以
lfs
用户的身份登录或者在前面 第 4.4 节 「设置环境」 中某些地方出现了错误。
一旦一切都顺利,清理测试文件:
rm -v dummy.c a.out
该软件包的详细信息请参见: 第 6.21.2 节 「GCC 软件包内容」。
Tcl 软件包包含工具命令语言(Tool Command Language)相关程序。
此软件包和后面两个包(Expect 和 DejaGNU)用来为 GCC 和 Binutils 还有其他的一些软件包的测试套件提供运行支持。仅仅为了测试目的而安装三个软件包,看上去有点奢侈,虽然因为大部分重要的工具都能正常工作而并不需要去做测试。尽管在本章中并没有执行测试套件(并不做要求),但是在 第 6 章 中都要求执行这些软件包自带的测试套件。
注意,这里的 Tcl 软件包用的是最小化安装的版本,仅仅是为了运行 LFS 测试。需要完整版的软件包,可参考 BLFS 的 Tcl 流程。
配置 Tcl 准备编译:
cd unix ./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不强求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Tcl 的话可以用下面的命令:
TZ=UTC make test
Tcl 测试套件在宿主机某些特定条件下会失败,原因很难推测。不过测试套件失败并不奇怪,也不是什么严重的错误。参数
TZ=UTC
设定了时区和相应的世界标准时间(UTC),但是只在测试套件运行期间才有效。这个可以保证时钟测试能正常运行。关于
TZ
环境变量的细节请参阅本手册 第 7 章。
安装软件包:
make install
让安装的库文件可写,这样之后可以删除调试符号。
chmod -v u+w /tools/lib/libtcl8.6.so
安装 Tcl 的头文件。后面的软件包 Expect 在编译的时候会用到。
make install-private-headers
现在创建几个必要的符号链接:
ln -sv tclsh8.6 /tools/bin/tclsh
Expect 软件包包含一个实现用脚本和其他交互式程序进行对话的程序。
首先,强制 Expect 的 configure 配置脚本使用 /bin/stty
替代宿主机系统里可能存在的 /usr/local/bin/stty
。这样可以保证我们的测试套件工具在工具链的最后一次构建能够正常。
cp -v configure{,.orig} sed 's:/usr/local/bin:/bin:' configure.orig > configure
现在配置 Expect 准备编译:
./configure --prefix=/tools \ --with-tcl=/tools/lib \ --with-tclinclude=/tools/include
配置脚本参数的含义:
--with-tcl=/tools/lib
这个选项可以保证 configure 配置脚本会从临时工具目录里找 Tcl 的安装位置,而不是在宿主机系统中寻找。
--with-tclinclude=/tools/include
这个选项会给 Expect 显式地指定 Tcl 内部头文件的位置。通过这个选项可以避免 configure 脚本不能自动发现 Tcl 头文件位置的情况。
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Expect 的话可以用下面的命令:
make test
请注意 Expect 测试套件已知在某些宿主机特定情况下有过失败的情况,我们还没有完全把握。不过,在这里测试套件运行失败并不奇怪,也不认为是关键问题。
安装软件包:
make SCRIPTS="" install
make 参数的含义:
SCRIPTS=""
这个变量可以避免安装额外的、没有需求的 Expect 脚本。
DejaGNU 软件包包含了测试其他程序的框架。
配置 DejaGNU 准备编译:
./configure --prefix=/tools
编译安装软件包:
make install
要测试编译结果,执行:
make check
M4 软件包包含一个宏处理器。
首先,对应 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=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 M4 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.14.2 节 「M4 软件包内容」。
Ncurses 软件包包含用于不依赖于特定终端的字符屏幕处理的库。
在安装之前,须要确保 gawk 在第一次配置时已经找到:
sed -i s/mawk// configure
配置 Ncurses 准备编译:
./configure --prefix=/tools \ --with-shared \ --without-debug \ --without-ada \ --enable-widec \ --enable-overwrite
配置脚本参数的含义:
--without-ada
这个选项会保证 Ncurse 不会编译对宿主机系统里可能存在的 Ada 编译器的支持,而这在我们 chroot 切换环境后就不再可用。
--enable-overwrite
这个选项会告诉 Ncurses 安装它的头文件到 /tools/include
目录,而不是 /tools/include/ncurses
目录,保证其他软件包可以正常找到 Ncurses 的头文件。
--enable-widec
这个选项会控制编译宽字符库(比如,libncursesw.so.6.1
)而不是默认的普通库(比如,libncurses.so.6.1
)。这些宽字符库在多字节和传统的 8 位环境下使用,而普通库只能用于 8
位环境。宽字符库和普通库的源代码是兼容的,但并不是二进制兼容。
编译软件包:
make
这个软件包有测试套件,但是只有在安装后才能执行。测试用例在 test/
目录里。查看该目录下的 README
文件了解更多细节。
安装软件包:
make install ln -s libncursesw.so /tools/lib/libncurses.so
关于这个软件包的详细资料请参见: 第 6.24.2 节 「Ncurses 软件包内容」。
Bash 软件包包含 Bourne-Again Shell。
配置 Bash 准备编译:
./configure --prefix=/tools --without-bash-malloc
配置脚本参数的含义:
--without-bash-malloc
这个选项会禁用 Bash 的内存分配功能(malloc
),这个功能已知会导致段错误。而禁用这个功能后,Bash
将使用 Glibc 的 malloc
函数,这样会更稳定。
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Bash 的话可以用下面的命令:
make tests
安装软件包:
make install
为使用 sh 终端的程序创建一个符号链接:
ln -sv bash /tools/bin/sh
关于这个软件包的详细资料请参见: 第 6.34.2 节 「Bash 软件包内容」。
Bison 软件包包含一个语法生成器。
配置 Bison 准备编译:
./configure --prefix=/tools
编译软件包:
make
为了测试结果,输入:
make check
安装软件包:
make install
该软件包的详细信息参见: 第 6.31.2 节 「Bison 软件包内容」。
Bzip2 软件包包含压缩和解压缩的程序。用 bzip2 压缩文本文件能获得比传统的 gzip 更好的压缩比。
Bzip2 软件包里没有 configure 配置脚本。用下面的命令编译和测试:
make
安装软件包:
make PREFIX=/tools install
关于这个软件包的详细资料请参见: 第 6.22.2 节 「Bzip2 软件包内容」。
Coreutils 软件包包含用于显示和设置基本系统特性的工具。
配置 Coreutils 准备编译:
./configure --prefix=/tools --enable-install-program=hostname
配置脚本参数的含义:
--enable-install-program=hostname
这个选项会允许构建和安装 hostname 程序——默认是不安装的,但是 Perl 测试套件需要它。
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Coreutils 的话可以用下面的命令:
make RUN_EXPENSIVE_TESTS=yes check
参数 RUN_EXPENSIVE_TESTS=yes
会告诉测试套件额外运行对某些系统开销相对大一些(主要是 CPU 运算能力和内存消耗)的测试用例,但是通常对 Linux
来说不是问题。
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.54.2 节 「Coreutils 软件包内容」。
Diffutils 软件包包含显示文件和目录差异的程序。
配置 Diffutils 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Diffutils 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.56.2 节 「Diffutils 软件包内容」。
File 软件包包括一个判断给定的某个或某些文件文件类型的工具。
配置 File 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 File 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.12.2 节 「File 软件包内容」。
Findutils 软件包包含查找文件的程序。这些程序提供递归搜索目录树、创建、管理以及搜索数据库(通常比递归式的 find 要快,但如果数据库最近没有更新的话结果不可靠)。
首先,对应 glibc-2.28 的需求做一些修复:
sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' gl/lib/*.c sed -i '/unistd/a #include <sys/sysmacros.h>' gl/lib/mountlist.c echo "#define _IO_IN_BACKUP 0x100" >> gl/lib/stdio-impl.h
配置 Findutils 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Findutils 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.58.2 节 「Findutils 软件包内容」。
Gawk 软件包包含用于操作文本文件的程序。
配置 Gawk 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Gawk 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.57.2 节 「Gawk 软件包内容」。
Gettext 软件包包含用于国际化和本土化的工具。这允许用 NLS(Native Language Support,本地语言支持) 编译程序,使得能以用户的本地语言输出信息。
对于我们这次用到的临时工具集,我们只需要安装 Gettext 软件包里的 3 个程序。
配置 Gettext 准备编译:
./configure --disable-shared
配置脚本参数的含义:
--disable-shared
这次我们不需要安装任何的 Gettext 动态库,所以不需要编译。
编译软件包:
make
由于环境受限,建议不要在此阶段运行测试套件。
安装 msgfmt、msgmerge 和 xgettext 程序:
cp -v gettext-tools/src/{msgfmt,msgmerge,xgettext} /tools/bin
关于这个软件包的详细资料请参见: 第 6.47.2 节 「Gettext 软件包内容」。
Grep 软件包包含用于在文件中搜索的程序。
配置 Grep 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Grep 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.33.2 节 「Grep 软件包内容」。
Gzip 软件包包含用于压缩和解压文件的程序。
配置 Gzip 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Gzip 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.62.2 节 「Gzip 软件包内容」。
Make 软件包包含一个用于编译软件的程序。
首先,解决一个 glibc-2.27 或更高版本带来的问题:
sed -i '211,217 d; 219,229 d; 232 d' glob/glob.c
配置 Make 准备编译:
./configure --prefix=/tools --without-guile
配置脚本参数的含义:
--without-guile
这个选项会保证 Make-4.2.1 不会链接宿主系统上可能存在的 Guile 库,而在下一章里通过 chroot 切换环境后便不再有效了。
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Make 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.66.2 节 「Make 软件包内容」。
Patch 软件包包含一个通过打「补丁」创建或修改文件的程序,补丁文件通常由 diff 程序生成。
配置 Patch 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Patch 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.67.2 节 「Patch 软件包内容」。
Perl 软件包包含实用信息抽取与报告语言。
配置 Perl 准备编译:
sh Configure -des -Dprefix=/tools -Dlibs=-lm -Uloclibpth -Ulocincpth
配置选项的含义:
-des
这是三个选项地的组合:-d 对所有选项使用默认值;-e 确保完成所有任务;-s 静默,不产生非必要输出。
-Uloclibpth amd
-Ulocincpth
这些条目中未定义的变量,会促使配置过程去寻找宿主系统中已经存在的本地安装的组件。
编译软件包:
make
虽然 Perl 软件包自带测试套件,最好还是等下一章中它被完整安装之后再运行。
这次我们只需要安装一小部分的应用和库。
cp -v perl cpan/podlators/scripts/pod2man /tools/bin mkdir -pv /tools/lib/perl5/5.30.0 cp -Rv lib/* /tools/lib/perl5/5.30.0
关于这个软件包的详细资料请参见: 第 6.40.2 节 「Perl 软件包内容」。
软件包 Python 3 包含了 Python 的开发环境。对于面向对象编程,书写脚本,构建大型程序的原型,或者开发整个应用程序而言,非常有用。
有两个名称以 “python” 开头的包文件。注意要解压的是 Python-3.7.4.tar.xz
(首字母大写的那个)。
这个软件包首先构建 Python 解释器,然后是一些标准的 Python 模块。构建模块的主要脚本是用 Python
编写的,并使用宿主机 /usr/include
和
/usr/lib
目录的硬编码路径。以此防止他们被使用,输入:
sed -i '/def add_multiarch_paths/a \ return' setup.py
译注:PDF 文档在复制上述命令时,需注意「\」到「return」之间要保留 2 个 Tab、或 8 个空格的缩进。
配置 Python 准备编译:
./configure --prefix=/tools --without-ensurepip
配置选项的含义:
--without-ensurepip
该选项用于禁用现阶段好不需要的 Python 软件包安装程序。
编译软件包:
make
编译完成。测试套件需求 TK 和 X Windows,此时无法运行。
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.51.2 节 「Python 3 软件包内容.」
Sed 软件包包含一个流编辑器。
配置 Sed 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Sed 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.28.2 节 「Sed 软件包内容」。
Tar 软件包包含一个归档程序。
配置 Tar 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Tar 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.69.2 节 「Tar 软件包内容」。
Texinfo 软件包包含用于读、写以及转换信息页的程序。
配置 Texinfo 准备编译:
./configure --prefix=/tools
作为配制过程的一部分,有一个测试会指出 TestXS_la-TestXS.lo 有一处错误。这与 LFS 没有关系,可以忽略。
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Texinfo 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.70.2 节 「Texinfo 软件包内容」。
Xz 软件包包含用于压缩和解压文件的程序。它提供 lzma 和更新的 xz 压缩格式功能。和传统的 gzip 或 bzip2 命令相比,用 xz 压缩文本文件能获得更好的压缩率。
配置 Xz 准备编译:
./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Xz 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.45.2 节 「Xz 软件包内容」。
本小节里的步骤是可选的,但如果你的 LFS 分区容量比较小,知道有些不必要的内容可以被删除也是挺好的。目前编译好的可执行文件和库大概会有 70MB 左右不需要的调试符号。可以通过下面的命令移除这些符号:
strip --strip-debug /tools/lib/* /usr/bin/strip --strip-unneeded /tools/{,s}bin/*
这两个命令会跳过一些文件,并提示不可识别的文件格式。大多数是脚本文件而不是二进制文件。同样还可以用宿主系统里的 strip 命令为 /tools 目录下的 strip 二进制文件清理无用内容。
注意不要对库文件使用 --strip-unneeded
选项。静态库会被损坏导致整个工具链将会需要全部重新编译。
更节省更多空间,还可以删除帮助文档:
rm -rf /tools/{,share}/{info,man,doc}
删除不需要的文件:
find /tools/{lib,libexec} -name \*.la -delete
这个时候,你应该在 $LFS
分区中为下个阶段编译安装 Glibc 和
GCC 预留至少 3GB 剩余空间。如果你可以编译安装 Glibc,那其他的就不会有问题了。
本书余下部分的命令都必须以 root
用户身份执行而不再是
lfs
用户。另外,再次确认下 $LFS
变量在 root
用户环境下也有定义。
当前,$LFS/tools
目录属于 lfs
用户,这是一个只存在于宿主系统上的帐号。如果继续保持 $LFS/tools
目录的现状,其中的文件将属于一个没有相关联帐号的用户
ID。这很危险,因为随后创建的用户有可能会分配到相同的用户 ID,从而变成 $LFS/tools
目录及其中所有文件的属主,以致留下恶意操作这些文件的可能。
为了解决这个问题,你可以在随后新的 lfs
系统里创建
/etc/passwd
文件时增加一个 lfs
用户,并注意给它分配和宿主系统里相同的用户和组 ID。不过更好的方式是,通过下面的命令将 $LFS/tools
目录的属主改为 root
用户:
chown -R root:root $LFS/tools
尽管 $LFS/tools
目录可以在 LFS
系统构建完成后删除,但仍然可以保留下来用于 构建额外的相同版本 LFS 系统。备份 $LFS/tools
目录到底有多少好处取决于你个人。
如果你想保留临时工具用来构建新的 LFS 系统,现在就要备份好。本书随后第六章中的指令将对当前的工具做些调整,导致在构建新系统时会失效。
在本章中,我们会进入构建环境开始认真地构建 LFS 系统了。我们将 chroot 到之前准备好的临时迷你 Linux 系统,做一些最后的准备工作,然后就开始安装软件包。
安装软件很简单。尽管很多时候安装指令能更短而且更具通用性,但我们还是选择为每个软件包都提供完整的指令,以减小引起错误的可能性。了解 Linux 系统如何工作的关键就是知道每个软件包的作用以及为什么你(或系统)需要它。
我们不建议在编译时使用优化。这虽然可以让程序运行得快那么一点点,但是却也有可能增加编译难度,以及在运行时出问题。如果在打开优化后编译失败,请试一下关闭优化编译看看。就算打开优化通过了编译,考虑到源代码和编译工具之间的复杂交互,仍然存在编译不正确的风险。还有需要注意
-march
和 -mtune
选项除了本书指定的值都未经测试。这有可能导致工具链软件包(Binutils、GCC 和
Glibc)发生问题。对比使用编译优化带来的好处与风险,这样做经常是得不偿失。第一次构建 LFS
系统还是推荐不要使用自定义优化。这样构建出来的系统一样会运行得很快,于此同时还很稳定。
本章里安装软件包的顺序需要严格遵守,这是为了保证不会有程序意外地依赖与 /tools
路径的硬链相关的目录。同样的理由,不要同时编译不同的软件包。并行地编译也许能节省一点时间(特别是在双 CPU
电脑上),但是它可能会导致程序里存在包含到 /tools
目录的硬链接,这样的话在这个目录移除后程序就不能正常工作了。
在安装指令之前,每个页面都提供了关于软件包的信息,包括其中所包含内容的精确描述,构建需求的大致时间,以及在过程中需求磁盘空间的大小。在安装指令之后,是一个该软件包即将安装的程序和库(及概要说明)的列表。
第六章里软件包的 SBU 数值和所需磁盘空间包含了可能存在的测试套件数据。
总的来说,LFS 的编辑们并不推荐构建和安装静态库。许多静态库的初衷已经赶不上现在的 Linux 系统了。而且将静态库链接到程序还有不好之处。假设库更新需要移除一个安全问题,所有使用该静态库的程序都需要重新链接到新的库。由于静态库并不会总那么明显,有哪些相关的程序(以及需要链接的程序)很可能都不知道。
第六章的程序,我们移除或禁止了大部分静态库的安装。通常通过在 configure 命令中使用
--disable-static
项,便可以做到。有些情况下,可能用到其他代替的办法。当然也有少数情况,特别是 glibc 和 gcc
使用的静态库在软件包的构建过程中是必不可少的。
更多关于库的讨论,请参考 BLFS 中 库:静态还是共享? 章节。
内核会挂载几个文件系统用于自己和用户空间程序交换信息。这些文件系统是虚拟的,并不占用实际磁盘空间,它们的内容会放在内存里。
开始先创建将用来挂载文件系统的目录:
mkdir -pv $LFS/{dev,proc,sys,run}
在内核引导系统的时候,它依赖于几个设备节点,特别是 console
和 null
两个设备。设备点必须创建在硬盘上以保证在 udevd 启动前是可用的,特别是在使用
init=/bin/bash
启动
Linux 时。运行以下命令创建设备节点:
mknod -m 600 $LFS/dev/console c 5 1 mknod -m 666 $LFS/dev/null c 1 3
通常激活 /dev
目录下设备的方式是在
/dev
目录挂载一个虚拟文件系统(比如
tmpfs
),然后允许在检测到设备或打开设备时在这个虚拟文件系统里动态创建设备节点。这个通常是在启动过程中由
udev 完成。由于我们的新系统还没有 udev,也没有被引导,有必要手动挂载和激活 /dev
这可以通过绑定挂载宿主机系统的 /dev
目录来实现。绑定挂载是一种特殊的挂载模式,它允许在另外的位置创建某个目录或挂载点的镜像。运行下面的命令来实现:
mount -v --bind /dev $LFS/dev
现在挂载剩下的虚拟内核文件系统:
mount -vt devpts devpts $LFS/dev/pts -o gid=5,mode=620 mount -vt proc proc $LFS/proc mount -vt sysfs sysfs $LFS/sys mount -vt tmpfs tmpfs $LFS/run
挂载选项 devpts 的含义:
gid=5
以确保所有由 devpts 创建的设备节点属于 GID 5 之下。这个 ID 日后将用于
tty
组。我们使用 GID
来代替组名,原因是宿主系统可能为 tty
组使用其他的 ID。
mode=0620
以确保所有 devpts 创建的设备节点的属性为 0620 (属主用户可读写,组成员可写)。与上一个选项同时使用,可以保证 devpts 所创建的设备节点能满足 grantpt() 函数的要求,这就意味着不需要 Glibc (默认不安装的)帮助程序 pt_chown 了。
在某些宿主机系统里,/dev/shm
是一个指向
/run/shm
的符号链接。这个 /run 下的 tmpfs
文件系统已经在之前挂载了,所以在这里只需要创建一个目录。
if [ -h $LFS/dev/shm ]; then mkdir -pv $LFS/$(readlink $LFS/dev/shm) fi
软件包管理经常被请求加入到 LFS 手册中。软件包管理器可以追踪安装的文件,方便软件包的移除和升级。不仅是二进制执行文件和库文件,包管理器还会处理配置文件的安装。在你想太多之前,答案是不——本节不讨论也不安利任何特定的软件包管理器,只是总结了一下关于软件包管理的常用技术和工作原理。对你而言最完美的软件包管理器可能就在这些技术之中,也可能由这些技术的其中几个组合而成。本节还简要的提了一些在升级软件包时可能遇到的问题。
为什么 LFS 或 BLFS 手册里不采用任何软件包管理器的一些原因:
使用软件包管理偏离了本手册的主要目标——教大家 Linux 系统是如何构建出来的。
存在很多软件包管理的解决方案,每一个都有自己的长处和缺点。很难选择一种适合所有人的方式。
关于软件包管理有很多资料,可以访问 Hints Project 看看是否可以解决你的需求。
软件包管理器可以在软件新版本发布后轻松升级。一般来说 LFS 和 BLFS 手册里的指令是可以用来升级版本。下面是一些在你准备升级软件包时需要注意的事情,特别是运行中系统需更加注意。
如果需要升级 Glibc 到新版本(比如,从 glibc-2.19 升级到 glibc-2.20),重新构建整个 LFS 会比较安全。虽然你 也许 能够按依赖关系重新编译所有的软件包,不过我们不建议这样做。
如果某个包含的动态库的软件包升级了,而且库名字有所改变,那么所有动态链接到这个库的软件包都需要重新链接新的库。(请注意软件包版本和库名字并不存在相关性)。举个例子,说软件包
foo-1.2.3 安装了一个叫 libfoo.so.1
的动态库。然后你将软件包升级到了新版本
foo-1.2.4 安装的动态库叫 libfoo.so.2
了。在这种情况下,所有动态链接到
libfoo.so.1
的软件包都需要重新编译链接到 libfoo.so.2
。注意在所有依赖软件包重新编译完成之前,请勿删除之前的库文件。
下面介绍一些常见的软件包管理技巧。在决定用哪种包管理方式之前,先研究一下各种不同的技术,特别是某些方案的不足之处。
是的,这也算一种软件包管理技术。有些人觉得不需要管理软件包,是因为他们非常熟悉软件包,知道每个包都安装了哪些文件。也有些用户不需要管理软件包,是因为他们会在某个软件包有更改后重建整个系统。
这是一种简单的软件包管理方式,不需要其他额外的软件来管理软件的安装。每一个软件包都被装到一个独立的目录里。例如,软件包
foo-1.1 安装到目录 /usr/pkg/foo-1.1
中并创建一个符号链接 /usr/pkg/foo
指向 /usr/pkg/foo-1.1
。在安装新版本 foo-1.2
的时候,它会被装到目录 /usr/pkg/foo-1.2
中,然后用指向新版本的软链替代之前的符号链接。
类似 PATH
、LD_LIBRARY_PATH
、MANPATH
、INFOPATH
和 CPPFLAGS
之类的环境变量变量需要包含
/usr/pkg/foo
目录。在管理大量软件包时,这种方式就不可行了。
这是前一种软件包管理技术的变种。每个软件包的安装方式都和之前的方式类似。但不是建立目录的符号链接,而是把每个文件都链接到
/usr
目录结构里。这样就不需要扩展环境变量了。通过自动创建这些可由用户自行创建的软链,许多软件包管理器就采用了这种方式进行管理的。其中比较流行的有
Stow、Epkg、Graft 和 Depot。
这种安装方式需要伪装,这样软件包会认为自己被装到了 /usr
目录下,而实际上它被装到了 /usr/pkg
目录下。在这种方式下,安装并不是一件琐碎的小事。例如,假如你准备安装一个软件包
libfoo-1.1。下面的指令可能不会正确地安装:
./configure --prefix=/usr/pkg/libfoo/1.1 make make install
安装本身倒是没有问题,但是可能一些依赖包不会像你期望的那样链接到 libfoo 库。如果你要编译一个链接 libfoo
的软件,你可能会注意到它实际上链接到的是 /usr/pkg/libfoo/1.1/lib/libfoo.so.1
而不是你所期望的 /usr/lib/libfoo.so.1
。正确的方式是使用
DESTDIR
策略来伪装软件包的安装过程。这种方式需要像下面这样操作:
./configure --prefix=/usr make make DESTDIR=/usr/pkg/libfoo/1.1 install
大多数软件包支持这种方式,但也有一些例外。对于不兼容的软件包,你可能需要自己手动安装,或许你会发现将这些有问题的包安装到
/opt
目录下会更简单些。
在这种方式里,在安装之前会创建一个时间戳文件。在安装之后,用一行简单的 find 命令加上合适的参数就可以生成在时间戳文件创建之后所安装的所有文件列表。有一个采用这种方式的包管理器叫做 install-log。
这种方式的优点是非常简单,但是它有两个缺陷。比如,在安装过程中,所安装文件采用的是其它时间戳而不是当前时间,那这些文件将不能被软件包管理器跟踪到。还有,这种方式只能在一次安装一个软件包的情况下使用。如果在不同的终端里同时安装两个不同的软件包,此时的安装日志就不可靠了。
在这种方式里,安装脚本所使用的命令都会被记录下来。有两种技术,一种是:
设定环境变量 LD_PRELOAD
指向一个在安装前预加载的库。在安装过程中,这个库会追踪软件包安装脚本里所包含的各种执行文件比如
cp、install、mv,以及追踪会修改文件系统的系统调用。要让这种方式有效的话,所有的执行文件需要动态链接到没有
suid 或 sgid
标志位的库。预加载这个库可能会引起安装过程中一些意外的副作用。因此,建议做一些测试以保证软件包管理器不会造成破坏并且记录了所有适当的文件。
第二种技术是使用 strace 命令,它会记录下安装脚本执行过程中所有的系统调用。
在这种方式里,像之前的符号链接软件包管理方式里所描述的那样,软件包被伪装安装到一个独立的目录树里。在安装完成后,会将已安装文件打包成一个软件包存档。然后这个存档会用来在本地机器或其他机器上安装软件包。
这种方式为商业发行版中的大多数包管理器所采用。例子有 RPM(顺带提一下,这也是 Linux 标准规范 中指定的包管理器),pkg-utils,Debian 的 apt,和 Gentoo 的 Portage 系统。如何在 LFS 系统里采用这种包管理方式的简单描述,请参看 http://www.linuxfromscratch.org/hints/downloads/files/fakeroot.txt.
创建带有依赖关系的软件包存档非常复杂,已经超出 LFS 手册范围了。
Slackware 使用一个基于 tar 的系统来创建软件包存档。这套系统不像那些更复杂的包管理器,有意地不处理包依赖关系。关于 Slackware 包管理器的详细信息,请参看 http://www.slackbook.org/html/package-management.html。
这种方式,是 LFS 特有的,由 Matthias Benkmann 所设计,可以在 Hints Project 中能找到。在这种方式里,每个软件包都由一个单独的用户安装到标准的位置。文件属于某个软件包可以通过检查用户 ID 轻松识别出来。关于这种方式的利弊比较复杂,就不再本节中赘述了。详细的信息请参看 http://www.linuxfromscratch.org/hints/downloads/files/more_control_and_pkg_man.txt。
LFS 系统的一个优点是没有那种需要依赖其在磁盘系统中的位置的文件。克隆一份 LFS
到和宿主机器相似配置的机器上,简单到只要对包含根目录的 LFS 分区(对于一个基本的 LFS 构建不压缩的话大概有
250MB)使用 tar
命令打包,然后通过网络传输或光盘拷贝到新机器上展开即可。在这之后,需要调整一些配置文件。需要更新的配置文件包括:
/etc/hosts
, /etc/fstab
, /etc/passwd
, /etc/group
, /etc/shadow
,
/etc/ld.so.conf
, /etc/sysconfig/rc.site
, /etc/sysconfig/network
, 和 /etc/sysconfig/ifconfig.eth0
。
根据系统硬件和原始内核配置文件的差异,可能还需要重新编译一下内核。
据报告,当这样的复制发生在两个相近却又不完全相同的架构时会发生问题。例如,Intel 系统的指令集就和 AMD 处理器的不同,还有一些较新版的处理器可能会有一些在较早版本中不能支持的指令。
最后,通过 第 8.4 节 「使用 GRUB 设置启动过程」 中介绍的方法来使新系统可以引导。
现在可以切换到 chroot 环境开始构建和安装最终的 LFS 系统了。以 root
用户运行下面的命令进入此环境,从现在开始,就只剩下准备的那些临时工具了:
chroot "$LFS" /tools/bin/env -i \ HOME=/root \ TERM="$TERM" \ PS1='(lfs chroot) \u:\w\$ ' \ PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin \ /tools/bin/bash --login +h
给 env 命令传递
-i
选项会清除这个 chroot
切换进去的环境中的所有变量。随后,只需重新设定 HOME
、TERM
、PS1
、和
PATH
变量。TERM=$TERM
将会把 TERM
设定成 chroot 外环境相同的值。许多程序需要这个变量才能正常工作,比如
vim 和
less。如果还需设定其他变量,如
CFLAGS
或 CXXFLAGS
,正好在这一起设置了。
在这之后,LFS
变量就不再需要了,因为后面所有工作都将被限定在 LFS
文件系统中。因为我们已经告诉 Bash 终端 $LFS
就是当前的根目录(/
)目录。
注意要将 /tools/bin
放在 PATH
变量的最后。意思是在每个软件的最后版本编译安装好后就不再使用临时工具了。这还需要让
shell 不要「记住」每个可执行文件的位置——这样的话,还要给 bash 加上 +h
选项来关闭其哈希功能。
注意一下 bash 的提示符是
I have no name!
这是正常的,因为这个时候 /etc/passwd
文件还没有被创建。
非常重要,从本章开始,后续章节中的命令都要在 chroot 环境下运行。如果因为某种原因(比如说重启)离开了这个环境,请保证要按照 第 6.2.2 节 「挂载和激活 /dev」 和 第 6.2.3 节 「挂载虚拟文件系统」 中所说的那样挂载虚拟内核文件系统,并在继续构建之前重新运行 chroot 进入环境。
现在准备创建 LFS 文件系统里的一些目录结构。使用下面的命令创建一个标准的目录树:
mkdir -pv /{bin,boot,etc/{opt,sysconfig},home,lib/firmware,mnt,opt} mkdir -pv /{media/{floppy,cdrom},sbin,srv,var} install -dv -m 0750 /root install -dv -m 1777 /tmp /var/tmp mkdir -pv /usr/{,local/}{bin,include,lib,sbin,src} mkdir -pv /usr/{,local/}share/{color,dict,doc,info,locale,man} mkdir -v /usr/{,local/}share/{misc,terminfo,zoneinfo} mkdir -v /usr/libexec mkdir -pv /usr/{,local/}share/man/man{1..8} case $(uname -m) in x86_64) mkdir -v /lib64 ;; esac mkdir -v /var/{log,mail,spool} ln -sv /run /var/run ln -sv /run/lock /var/lock mkdir -pv /var/{opt,cache,lib/{color,misc,locate},local}
一般目录默认会按 755 的权限创建,但是这并不适用于所有的目录。在上面的命令里,有两个改动——一个是
root
的 home 目录,另一个是存放临时文件的目录。
第一个模式改动能保证不是所有人都能进入 /root
目录——同样一般用户也需要为他/她的 home 目录设置这样的模式。第二个模式改动能保证所有用户对目录
/tmp
和 /var/tmp
都是可写的,但又不能移除其他用户的文件。后面的这个限制是由所谓的「粘滞位」实现的,即位掩码 1777 中的最高位(1)。
这个目录树是基于文件系统目录结构标准(FHS)(参看 https://refspecs.linuxfoundation.org/fhs.shtml)。FHS
标准还规定了要有 /usr/local/games
和
/usr/share/games
目录。我们只创建了我们需要的目录。然而,如果你更喜欢严格遵守 FHS 标准,创建这些目录也无妨。
有些程序里会使用写死的路径调用其它暂时还未安装的程序。为了满足这种类型程序的需要,我们将创建一些符号链接,在完成本章内容后这些软件会安装好,并替代之前的符号链接:
ln -sv /tools/bin/{bash,cat,chmod,dd,echo,ln,mkdir,pwd,rm,stty,touch} /bin ln -sv /tools/bin/{env,install,perl,printf} /usr/bin ln -sv /tools/lib/libgcc_s.so{,.1} /usr/lib ln -sv /tools/lib/libstdc++.{a,so{,.6}} /usr/lib ln -sv bash /bin/sh
每个链接的目的:
/bin/bash
许多 bash
脚本指定了 /bin/bash
。
/bin/cat
这个路径在 Glibc 的配置脚本里写死了。
/bin/dd
到 dd
的路径将写死在/usr/bin/libtool
实用工具中。
/bin/echo
这个是为了满足 Glibc 测试套件里的一个测试用例,它会检测 /bin/echo
。
/usr/bin/env
这个路径名硬编码到许多软件包的构建过程中。
/usr/bin/install
到 install
的路径将写死在
/usr/lib/bash/Makefile.inc
文件中。
/bin/ln
到 ln
的路径将写死在 /usr/lib/perl5/5.30.0/<target-triplet>/Config_heavy.pl
文件中。
/bin/pwd
某些 configure 脚本,特别是 Glibc 的,写死了这个路径。
/bin/rm
到 rm
的路径将写死在 /usr/lib/perl5/5.30.0/<target-triplet>/Config_heavy.pl
文件中。
/bin/stty
这个路径在 Expect 软件中写死了,所以在 Binutils 和 GCC 测试套件中会需要它。
/usr/bin/perl
许多 Perl 脚本写死了这个路径调用 perl 执行程序。
/usr/lib/libgcc_s.so{,.1}
Glibc 需要这个让 pthreads 库正常工作。
/usr/lib/libstdc++{,.6}
在 Glibc 的一些测试套件和对于 GMP 的 C++ 支持中会需要。
/bin/sh
许多 shell 脚本写死了位置 /bin/sh
。
由于历史原因,Linux 在文件 /etc/mtab
中维护一个已挂载文件系统的列表。而现代内核改为在内部维护这个列表,并通过 /proc
文件系统输出给用户。为了满足一些依赖 /etc/mtab
文件的应用程序,我们要创建下面的符号链接:
ln -sv /proc/self/mounts /etc/mtab
为了让 root
用户能正常登录,而且「root」的名字能被正常识别,必须在文件 /etc/passwd
和 /etc/group
中写入相应的内容。
运行下面的命令创建 /etc/passwd
文件:
cat > /etc/passwd << "EOF"
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/dev/null:/bin/false
daemon:x:6:6:Daemon User:/dev/null:/bin/false
messagebus:x:18:18:D-Bus Message Daemon User:/var/run/dbus:/bin/false
nobody:x:99:99:Unprivileged User:/dev/null:/bin/false
EOF
root
用户的实际密码(这里的「x」只是占位符)将在后面创建。
运行下面的命令创建 /etc/group
文件:
cat > /etc/group << "EOF"
root:x:0:
bin:x:1:daemon
sys:x:2:
kmem:x:3:
tape:x:4:
tty:x:5:
daemon:x:6:
floppy:x:7:
disk:x:8:
lp:x:9:
dialout:x:10:
audio:x:11:
video:x:12:
utmp:x:13:
usb:x:14:
cdrom:x:15:
adm:x:16:
messagebus:x:18:
input:x:24:
mail:x:34:
kvm:x:61:
wheel:x:97:
nogroup:x:99:
users:x:999:
EOF
这里创建的用户组没有参照任何标准——它们一部分是为了满足本章中配置 udev 的需要,还有一部分来自一些现存 Linux
发行版的通用设定。另外,某些测试套件也依赖特定用户或组。而 Linux 标准规范 (LSB,参见 http://www.linuxbase.org)只要求以组
ID(GID)为 0 创建用户组 root
以及以 GID 为
1 创建用户组 bin
。系统管理员可以自由分配其它所有用户组名字和 GID,因为优秀的程序不会依赖
GID 数字,而是使用组名。
为了移除「I have no name!」的提示符,可以打开一个新 shell。由于完整的 Glibc 已经在
第 5 章 里装好了,而且已经创建好了
/etc/passwd
和 /etc/group
文件,用户名和组名就可以正常解析了:
exec /tools/bin/bash --login +h
注意这里使用了 +h
参数。这样会告诉
bash
不要使用它内建的路径哈希功能。而不加这个参数的话,bash
将会记住曾经执行过程序的路径。为了在新编译安装好程序后就能马上使用,参数 +h
将在本章中一直使用。
程序 login、agetty 和 init(还有一些其它的)会使用一些日志文件来记录信息,比如谁在什么时候登录了系统。不过,在日志文件不存在的时候这些程序一般不会写入。下面初始化一下日志文件并加上合适的权限:
touch /var/log/{btmp,lastlog,faillog,wtmp} chgrp -v utmp /var/log/lastlog chmod -v 664 /var/log/lastlog chmod -v 600 /var/log/btmp
文件 /var/log/wtmp
会记录所有的登录和登出动作。文件
/var/log/lastlog
会记录每个用户的最后一次登录时间。文件 /var/log/faillog
会记录失败的登录尝试。文件 /var/log/btmp
会记录非法的登录尝试。
文件 /run/utmp
会记录当前已登录的用户。这个文件会在启动脚本中动态创建。
Linux API 头文件(在 linux-5.2.8.tar.xz 里)会将内核 API 导出给 Glibc 使用。
Linux 内核需要提供一个应用编程接口(API)供系统的 C 库(LFS 中的 Glibc)调用。这通过整理 Linux 内核源码包中的多个 C 头文件来完成。
确保在之前的动作里没有留下旧文件和依赖关系:
make mrproper
现在要从源代码里解压出用户需要的内核头文件。因为解压过程会删除目标目录下所有文件,所以我们会先输出到一个本地中间目录后再拷贝到需要的地方。而且里面还有一些隐藏文件是给内核开发人员用的,而 LFS 不需要,所以会将它们从中间目录里删除。
make INSTALL_HDR_PATH=dest headers_install find dest/include \( -name .install -o -name ..install.cmd \) -delete cp -rv dest/include/* /usr/include
Man-pages 软件包里包含了超过 2,200 份 man 手册页面。
运行下面的命令安装 Man-pages:
make install
Glibc 软件包包含了主要的 C 函数库。这个库提供了分配内存、搜索目录、打开关闭文件、读写文件、操作字符串、模式匹配、基础算法等基本程序。
Glibc 构建系统是独立的,并且会完美地安装,即使编译器指定的文件和链接器仍然指向 /tools
。在 Glibc 安装之前,不能调整指定的文件和链接器,因为
Glibc 的 autoconf 测试会给出错误结果并阻碍目标实现干净的构建。
有些 Glibc 程序会用到和 FHS 不兼容的 /var/db
目录来存储它们的运行时数据。打上如下的补丁让这些程序在 FHS
兼容的位置存储它们的运行时数据。
patch -Np1 -i ../glibc-2.30-fhs-1.patch
修复 linux-5.2 内核引入的问题:
sed -i '/asm.socket.h/a# include <linux/sockios.h>' \ sysdeps/unix/sysv/linux/bits/socket.h
顺应 LSB 规范创建一个符号链接。此外,x86_64 情况下,为了使动态加载器正确运作,再创建一个兼容性的符号链接:
case $(uname -m) in i?86) ln -sfv ld-linux.so.2 /lib/ld-lsb.so.3 ;; x86_64) ln -sfv ../lib/ld-linux-x86-64.so.2 /lib64 ln -sfv ../lib/ld-linux-x86-64.so.2 /lib64/ld-lsb-x86-64.so.3 ;; esac
Glibc 文档里建议在 Glibc 特定编译目录下编译:
mkdir -v build cd build
配置 Glibc 准备编译:
CC="gcc -ffile-prefix-map=/tools=/usr" \ ../configure --prefix=/usr \ --disable-werror \ --enable-kernel=3.2 \ --enable-stack-protector=strong \ --with-headers=/usr/include \ libc_cv_slibdir=/lib
选项和新参数的含义:
CC="gcc
-ffile-prefix-map=/tools=/usr"
让 GCC 在编译后记录 /tools 中文件的引用,使文件如同位于 /usr 一样。避免在调试符号中引入无效路径。
--disable-werror
选项像禁用了传递给 GCC 的 -Werror 选项。为了测试套件,此项必不可少。
--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
接着,安装可以使系统响应不同语言的地域设置。没有一个地域是必需的,但是如果缺少了其中的某些,可能会导致在未来的软件包测试套件中,跳过了重要的测试项目。
单独的语言环境可以用 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.30/localedata/SUPPORTED
文件里列出的所有语言环境(包括以上列出的所有语言环境以及其它更多),执行下面这个非常耗时的命令:
make localedata/install-locales
你需要的语言环境几乎不太可能没有列在 glibc-2.30/localedata/SUPPORTED
文件中,但如果真的没有可以使用 localedef 命令创建和安装。
当前,Glibc 在解决国际化域名时使用 libidn2。此为运行时依赖。如果需要此功能,可以参考 BLFS libidn2 页 相关的安装指令。
由于 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 ../../tzdata2019b.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 命令的含义:
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
在询问了几个关于位置的问题后,脚本会输出所在时区的名字(比如 America/Edmonton))。在
/usr/share/zoneinfo
文件中也有其它一些可用时区,比如 Canada/Eastern 或 EST5EDT,这些时区并没有被脚本列出来但也是可以使用的。
然后运行下面的命令创建 /etc/localtime
文件:
ln -sfv /usr/share/zoneinfo/<xxx>
/etc/localtime
将命令中的 <xxx>
替换成你所在实际时区的名字(比如 Canada/Eastern)。
默认情况下,动态库加载器(/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
可以在程序因为段错误终止的时候创建栈调用历史 |
|
生成消息条目 |
|
显示文件系统相关的系统配置变量的值 |
|
获取系统数据库的内容 |
|
字符集转换 |
|
创建 iconv 快速加载模块配置文件 |
|
配置动态链接器的运行时环境 |
|
报告某个程序或动态库所依赖的动态库 |
|
协助 ldd 处理某些目标文件 |
|
输出当前语言环境的大量信息 |
|
编译语言环境规格 |
|
根据输入的文本创建简单数据库 |
|
读取并解析内存跟踪文件,然后用方便人阅读的格式显示一个摘要 |
|
一个后台服务程序,提供最常用名字服务请求的缓存 |
|
通过 PC 分析生成的 dump 信息 |
|
列出运行中进程正在使用的动态共享目标 |
|
一个静态链接的 ln 程序 |
|
跟踪指定命令里的动态库函数调用 |
|
读取并显示共享目标分析数据 |
|
询问用户该系统的地理位置并给出相应的时区描述 |
|
跟踪程序执行过程并打印当前执行的函数 |
|
时区数据输出工具 |
|
时区数据编译工具 |
|
用于动态库执行的辅助程序 |
|
Glibc 内部的一个粗暴破解用来修复损坏程序(比如,一些 Motif 应用)。查看文件
|
|
段错误信号处理函数,catchsegv 会用到 |
|
一个异步名字查找库 |
|
主要的 C 库 |
|
密码学函数库 |
|
动态链接接口函数库 |
|
不包含函数的一个空库。以前是 g++ 的运行时库 |
|
数学运算函数库 |
|
链接这个库后会打开内存分配检查 |
|
Used by memusage 命令用它来协助收集应用程序里内存使用信息 |
|
网络服务函数库 |
|
名称服务切换函数库,包含了解析主机名、用户名、组名、别称、服务、协议等等的函数 |
|
可以预加载到 PC 配置文件中的可执行文件 |
|
POSIX 线程函数库 |
|
包含了创建、发送和解析互联网域名服务器封包的函数 |
|
包含了实现 POSIX.1b 实时扩展里规定的大部分接口的函数 |
|
包含了方便构建多线程程序调试工具的函数 |
|
包含各种 Unix 应用程序中用到的「标准」函数的代码 |
现在最后的 C 语言库已经装好了,是时候调整工具链,让新编译的程序链接到这些新的库上。
首先,备份 /tools
链接器,然后用我们在第五章调整过的链接器代替它。我们还会创建一个链接,链接到 /tools/$(uname -m)-pc-linux-gnu/bin
的副本:
mv -v /tools/bin/{ld,ld-old} mv -v /tools/$(uname -m)-pc-linux-gnu/bin/{ld,ld-old} mv -v /tools/bin/{ld-new,ld} ln -sv /tools/bin/ld /tools/$(uname -m)-pc-linux-gnu/bin/ld
接下来,修改 GCC 参数文件,让它指向新的动态连接器。只需删除所有「/tools」的实例,这样应该可以留下到达动态链接器的正确路径。还要调整参数文件,这样 GCC 就知道怎样找到正确的头文件和 Glibc 启动文件。一个 sed 命令就能完成这些:
gcc -dumpspecs | sed -e 's@/tools@@g' \ -e '/\*startfile_prefix_spec:/{n;s@.*@/usr/lib/ @}' \ -e '/\*cpp:/{n;s@$@ -isystem /usr/include@}' > \ `dirname $(gcc --print-libgcc-file-name)`/specs
直观地检查参数文件来确认预期的变化已确实完成是个好办法。
确保已调整的工具链的基本功能(编译和链接)都能如期进行是非常必要的。怎样做呢?执行下面这条命令:
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]
注意在 64 位系统中,虽然我们的动态链接位于 /lib
中,但却可以通过 /lib64 中的符号链接访问。
32 位系统的解释器应该是 /lib/ld-linux.so.2.
现在确保我们已经设置好了启动文件:
grep -o '/usr/lib.*/crt[1in].*succeeded' dummy.log
上一条命令的输出应该是:
/usr/lib/../lib/crt1.o succeeded
/usr/lib/../lib/crti.o succeeded
/usr/lib/../lib/crtn.o succeeded
确保链接器能找到正确的头文件:
grep -B1 '^ /usr/include' dummy.log
这条命令应该返回如下输出:
#include <...> search starts here:
/usr/include
接下来,确认新的链接器已经在使用正确的搜索路径:
grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
应该忽略指向带有 '-linux-gnu' 的路径,上条命令的输出应该是:
SEARCH_DIR("/usr/lib")
SEARCH_DIR("/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
如果显示的结果不一样或者根本没有显示,那就出了大问题。检查并回溯之前的步骤,找到出错的地方并改正。最有可能的原因是参数文件的调整出了问题。在进行下一步之前所有的问题都要解决。
确保一切正常之后,清除测试文件:
rm -v dummy.c a.out dummy.log
Zlib 软件包包括一些程序所使用的压缩和解压缩例程。
准备编译 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
File 软件包包括一个判断给定的某个或某些文件文件类型的工具。
准备编译 File:
./configure --prefix=/usr
编译软件包:
make
输入命令检查结果:
make check
安装软件包:
make install
Readline 软件包是提供命令行编辑和历史功能的库的集合。
重装 Readline 会使旧的库移动到 <libraryname>.old。通常来说这并不是什么问题,但一些情况下可能引起 ldconfig 链接错误。可以通过下面的两个 sed 命令避免这个问题:
sed -i '/MV.*old/d' Makefile.in sed -i '/{OLDSUFF}/c:' support/shlib-install
准备编译 Readline:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/readline-8.0
编译软件包:
make SHLIB_LIBS="-L/tools/lib -lncursesw"
make 选项的含义:
SHLIB_LIBS="-L/tools/lib
-lncursesw"
该选项强制 Readline 链接到 libncursesw
库。
该软件包没有测试套件。
安装软件包:
make SHLIB_LIBS="-L/tools/lib -lncursesw" install
现在移动动态库到更合适的位置并修正一些文件权限和符号链接:
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
M4 软件包包含一个宏处理器。
首先,对应 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
Bc 软件包包括一个任意精度数值处理的语言。
准备编译 Bc:
PREFIX=/usr CC=gcc CFLAGS="-std=c99" ./configure.sh -G -O3
配置选项的含义:
CC=gcc
CFLAGS="-std=c99"
这些参数指定要使用的编译器和 C 标准。
-O3
指定要使用的优化。
-G
省略测试套件的一部分,如果没有 GNU bc,这些套件将无法使用。
编译软件包:
make
测试 bc,运行:
make test
安装软件包:
make install
Binutils 软件包包含一个链接器、一个汇编器、以及其它处理目标文件的工具。
通过一个简单测试验证在 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 的文档建议在一个专用的编译目录中编译 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
配置参数的意义:
--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
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
PC 相关的 offset 测试和 debug_msg.sh 中的测试已知会在 LFS 环境中测试失败。
安装软件包:
make tooldir=/usr install
转换程序地址为文件名称和行号;给定一个地址和可执行文件的名称,它使用可执行文件中的调试信息来判断与该地址关联的源文件以及行号。 |
|
创建、更改以及抽取归档文件。 |
|
一个将 gcc 的输出汇编到目标文件的汇编器。 |
|
链接器用来过滤 C++ 和 Java 符号以及防止重载函数冲突。 |
|
DWARF 打包实用程序 |
|
更新 ELF 文件的 ELF 文件头。 |
|
显示调用关系图配置数据。 |
|
一个将多个目标文件和归档文件合并为单一文件,重定位数据及绑定符号引用的链接器。 |
|
一个阉割版的 ld,仅支持 elf 对象文件格式 |
|
到 ld 的硬链接。 |
|
列出指定目标文件中出现的符号。 |
|
转换某种类型的目标文件到另一种类型。 |
|
显示给定目标文件的信息,用选项可以控制显示特定信息;显示的信息对于使用编译工具的程序员非常有用。 |
|
生成归档文件内容的索引并保存到归档文件;索引列出了所有归档文件成员——可重定位的目标文件定义的符号。 |
|
显示 ELF 类型的二进制文件的信息。 |
|
列出所给目标文件各部分大小和总的大小。 |
|
对每个给定文件,输出不低于指定长度(默认是4)的可打印字符序列;对于目标文件,它默认只打印初始化和引导部分的字符串,而对于其它类型的文件扫描整个文件。 |
|
从目标文件中去除符号。 |
|
二进制文件描述库。 |
|
一个库用于处理操作码——「可读文本」版的处理器指令;用于构建类似 objdump 的工具。 |
GMP 软件包包含一些数学库。这里有对任意精度数值计算很有用的函数。
如果你是为 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
新配置选项的含义:
--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
MPFR 软件包包含多精度数学函数。
准备编译 MPFR:
./configure --prefix=/usr \ --disable-static \ --enable-thread-safe \ --docdir=/usr/share/doc/mpfr-4.0.2
编译软件包并生成 HTML 文档:
make make html
该章节 MPFR 的测试套件至关重要,任何情况下都不能跳过。
检查结果确认通过了所有的测试:
make check
安装软件包以及文档:
make install make install-html
MPC 软件包包含一个能以任意高精度进行复数数值计算和对结果进行正确四舍五入的库。
准备编译 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
Shadow 软件包包含以安全方式处理密码的程序。
如果你喜欢强制使用更强的密码,在编译 Shadow 之前可以根据
http://www.linuxfromscratch.org/blfs/view/9.0/postlfs/cracklib.html
安装 CrackLib。然后在下面的 configure 命令中增加
--with-libcrack
。
禁用对 groups 程序以及相应 man 手册的安装,Coreutils 已经提供了更棒的版本。同时也避免了安装已由 第 6.8 节 「Man-pages-5.02」 安装过的手册页:
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 -i -e 's@#ENCRYPT_METHOD DES@ENCRYPT_METHOD SHA512@' \ -e 's@/var/spool/mail@/var/mail@' etc/login.defs
如果你选择编译支持 Cracklib 的 Shadow,运行下面的命令:
sed -i 's@DICTPATH.*@DICTPATH\t/lib/cracklib/pw_dict@' etc/login.defs
做一个小改动,用 useradd 1000 生成第一个组号:
sed -i 's/1000/999/' etc/useradd
准备编译 Shadow:
./configure --sysconfdir=/etc --with-group-name-max-length=32
配置选项的含义:
--with-group-name-max-length=32
最长用户名为 32 个字符,使组名称也是如此。
编译软件包:
make
该软件包没有测试套件。
安装软件包:
make install
移动位置错误的程序到正确的位置:
mv -v /usr/bin/passwd /bin
该软件包包含增加、更改、以及删除用户和组的工具;设置和修改密码;执行其它特权级任务。软件包解压后的
doc/HOWTO
文件有关于 password shadowing 的完整解释。如果使用
Shadow 支持,记住需要验证密码(显示管理器、FTP 程序、pop3 守护进程等)的程序必须和 Shadow
兼容。也就是说,它们要能使用 Shadow 加密的密码。
运行下面的命令启用 shadow 密码:
pwconv
运行下面的命令启用 shadow 组密码:
grpconv
用于 useradd 工具的
Shadow 配置有一些需要解释的注意事项。首先,useradd
工具的默认操作是创建用户以及和用户名相同的组。默认情况下,用户 ID(UID) 和组 ID(GID) 的数字从 1000
开始。这意味着如果你不传递参数给 useradd,系统中的每个用户都会属于一个不同的组。如果不需要这样的结果,你需要传递参数
-g
到 useradd。默认参数保存在
/etc/default/useradd
文件中。你需要修改该文件中的两个参数来实现你的特定需求。
/etc/default/useradd
参数解释
GROUP=1000
该参数设定 /etc/group 文件中使用的起始组序号。你可以把它更改为任何你需要的数字。注意
useradd
永远不会重用 UID 或
GID。如果该参数指定的数字已经被使用了,将会使用它之后的下一个可用数字。另外注意如果你系统中没有序号为
1000 的组,第一次使用 useradd 而没有参数
-g
的话,你会在终端中看到一个提示信息:useradd:
unknown GID 1000
。你可以忽视这个信息,它会使用组号 1000。
CREATE_MAIL_SPOOL=yes
这个参数会为 useradd
新添加的用户创建邮箱文件。useradd 会使组
mail
拥有该文件的所有权,并赋予组
0660 的权限。如果你希望 useradd
不创建这些邮箱文件,你可以运行下面的命令:
sed -i 's/yes/no/' /etc/default/useradd
运行下面的命令为用户 root 设置密码:
passwd root
用来更改强制性密码更新的最大天数 |
|
用来更改用户的全名以及其它信息 |
|
用来以批处理模式更新组密码 |
|
用来以批处理模式更新用户密码 |
|
用来更改用户登录时默认使用的 shell |
|
检查并强制执行当前密码过期策略 |
|
用来检查登录失败的日志文件,设置锁定用户的最大失败次数,或者重置失败次数 |
|
用来给组增加、删除成员以及管理员 |
|
用指定的名称创建组 |
|
用指定的名称删除组 |
|
允许用户管理他/她自己的组成员列表而不需要超级用户权限。 |
|
用于更改指定组的名称或 GID |
|
验证组文件 |
|
从普通组文件创建或升级为 shadow 组文件 |
|
从 |
|
报告所有用户或指定用户的最近一次登录 |
|
用于系统让用户登录进来 |
|
用于强制限制登录时间和端口的守护进程 |
|
用于设置用户命名空间的 gid 映射 |
|
用于在一次登录会话中更改当前 GID |
|
用于设置用户命名空间的 uid 映射 |
|
用于批量创建或更新用户账户 |
|
显示一个账户不可用的信息;它用于来作为不可登录的账户的默认 shell |
|
用来更改用户或组账户的密码 |
|
验证密码文件 |
|
从普通密码文件创建或升级 shadow 密码文件 |
|
从 |
|
当用户的 GID 被设置为指定组的 GID 时执行一个特定命令 |
|
用替换的用户和组 ID 运行 Shell |
|
用指定的名称新建用户或更新新用户的默认信息 |
|
删除指定的用户账户 |
|
用于更改指定用户的登录名称、UID、shell、初始组、home 目录,等 |
|
编辑 |
|
编辑 |
GCC 软件包包括 GNU 编译器集,其中有 C 和 C++ 的编译器。
如果是在 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 的文档建议在源代码目录之外一个专用的编译目录中编译 GCC:
mkdir -v build cd build
准备编译 GCC:
SED=sed \ ../configure --prefix=/usr \ --enable-languages=c,c++ \ --disable-multilib \ --disable-bootstrap \ --with-system-zlib
注意,对于其它的编程语言,现在还有一些前提条件没有准备好。可以查看 BLFS Book 了解如何编译 GCC 支持的所有语言的指令。
新配置选项的含义:
SED=sed
设置环境变量防止访问到硬编码的 /tools/bin/sed 路径。
--with-system-zlib
这个选项告诉 GCC 链接系统安装的 Zlib 库,而不是它内部自带的库。
编译软件包:
make
本章节中 GCC 的测试套件至关重要,任何情况下都不能跳过。
GCC 测试套件中一个测试集的会耗尽堆栈空间,因此运行测试之前要增加栈大小:
ulimit -s 32768
以非特权用户测试编译结果,不要因为出现错误就停下来:
chown -Rv nobody . su nobody -s /bin/bash -c "PATH=$PATH make -k check"
要查看测试套件结果的概要,运行:
../contrib/test_summary
如果仅查看摘要,则使用管道 grep -A7
Summ
选项控制输出将输出。
结果可以和 http://www.linuxfromscratch.org/lfs/build-logs/9.0/ 以及 https://gcc.gnu.org/ml/gcc-testresults/ 上的相比较。
已知与 get_time 相关的六个测试失败。 这些显然与 en_HK 语言环境有关。
两个 experimental/net 的测试 lookup.cc 和 reverse.cc 已知会在 LFS chroot 环境失败因为它们需要 /etc /hosts 和 iana-etc。
两个测试 pr57193.c 和 pr90178.c 已知会失败。
一些意料之外的错误总是难以避免。GCC 开发者通常会意识到这些问题,但还没有解决。除非测试结果和上面 URL 中的相差很大,不然就可以安全继续。
安装软件包并删除不需要的目录:
make install rm -rf /usr/lib/gcc/$(gcc -dumpmachine)/9.2.0/include-fixed/bits/
GCC 的构建目录现在属于 nobody
安装头文件(及其内容)目录的所有权不正确。将所有权更改为 root
用户和组:
chown -v -R root:root \ /usr/lib/gcc/*linux-gnu/9.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)/9.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/9.2.0/../../../../lib/crt1.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/crti.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/crtn.o succeeded
取决于你机器的架构,上面的结果可能有稍微不同,差异通常是 /usr/lib/gcc
后目录的名称。这里重要的一点是 gcc 能在 /usr/lib
目录下找到所有的三个 crt*.o
文件。
确保链接器能找到正确的头文件:
grep -B4 '^ /usr/include' dummy.log
这条命令应该返回如下输出:
#include <...> search starts here:
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include
/usr/local/include
/usr/lib/gcc/x86_64-pc-linux-gnu/9.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
如果显示的结果不一样或者根本没有显示,那就出了大问题。检查并回溯之前的步骤,找到出错的地方并改正。最有可能的原因是参数文件的调整出了问题。在进行下一步之前所有的问题都要解决。
确保一切正常之后,清除测试文件:
rm -v dummy.c a.out dummy.log
最后,移动位置放错的文件:
mkdir -pv /usr/share/gdb/auto-load/usr/lib mv -v /usr/lib/*gdb.py /usr/share/gdb/auto-load/usr/lib
C++ 编译器 |
|
C 编译器 |
|
C 预处理器;编译器用来扩展源文件中 #include、#define 以及类似语句 |
|
C++ 编译器 |
|
C 编译器 |
|
增加插件到命令行的 ar 的封装。这个程序只用于添加 "链接时间优化",在使用默认编译选项时不起作用 |
|
增加插件到命令行的 nm 的封装。这个程序只用于添加 "链接时间优化",在使用默认编译选项时不起作用 |
|
增加插件到命令行的 ranlib 的封装。这个程序只用于添加 "链接时间优化",在使用默认编译选项时不起作用 |
|
一个覆盖测试工具;用于分析程序以决定在哪里进行优化有最大的效果 |
|
离线 gcda 和 gcno 配置文件 dump 工具 |
|
离线 gcda 配置文件处理工具 |
|
Address Sanitizer(地址消毒剂)运行时库。 |
|
GCC 原子内置运行时库 |
|
C 预处理库 |
|
包含用于 gcc 的运行时支持 |
|
当指示 GCC 启用分析时该库会被链接到程序中 |
|
用于 C/C++、Fortran 语言的多平台共享内存并行编程的 OpenMP API 的 GNU 实现 |
|
泄漏消毒(Leak Sanitizer)剂运行时库 |
|
GCC 的链接时间优化(LTO)插件,允许 GCC 跨编译单元进行优化 |
|
GCC 四精度数学库 API |
|
包含支持 GCC 堆栈溢出保护功能的例程 |
|
标准 C++ 库 |
|
ISO/IEC TS 18822:2015 文件系统库 |
|
为 C++ 编程语言提供支持例程 |
|
Thread Sanitizer(数据速率检测工具) 运行时库 |
|
未定义行为消毒剂(Undefined Behavior Sanitizer)运行时库 |
Bzip2 软件包包含压缩和解压缩的程序。用 bzip2 压缩文本文件能获得比传统的 gzip 更好的压缩比。
使用能为这个软件包安装帮助文档的补丁:
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
make 参数的含义:
-f
Makefile-libbz2_so
这会使用不同的 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
解压 bzip 压缩的文件 |
|
解压到标准输出 |
|
对 bzip 压缩的文件运行 cmp 命令 |
|
对 bzip 压缩的文件运行 diff 命令 |
|
对 bzip 压缩的文件运行 egrep 命令 |
|
对 bzip 压缩的文件运行 fgrep 命令 |
|
对 bzip 压缩的文件运行 grep 命令 |
|
使用哈夫曼编码的 Burrows-Wheeler 块排序文本压缩算法压缩文件;压缩率比传统的用「Lempel-Ziv」算法的压缩器要好,比如 gzip。 |
|
尝试从损坏的 bzip 压缩文件中恢复数据 |
|
对 bzip 压缩的文件运行 less 命令 |
|
对 bzip 压缩的文件运行 more 命令 |
|
用 Burrows-Wheeler 算法实现的无损的块排序数据压缩库 |
pkg-config 软件包包含一个在配置和 make 文件运行时把 include 路径和库路径传递给编译工具的工具。
准备编译 Pkg-config:
./configure --prefix=/usr \ --with-internal-glib \ --disable-host-tool \ --docdir=/usr/share/doc/pkg-config-0.29.2
新配置选项的含义:
--with-internal-glib
这会让 pkg-config 使用它自己内部版本的 Glib,因为在 LFS 中没有可用的外部版本。
--disable-host-tool
此选项取消创建到 pkg-config 程序的不必要的硬链接。
编译软件包:
make
用以下命令检查结果:
make check
安装软件包:
make install
Ncurses 软件包包含用于不依赖于特定终端的字符屏幕处理的库。
不要安装静态库,它不受配置控制:
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
新配置选项的含义:
--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.1 cp -v -R doc/* /usr/share/doc/ncurses-6.1
上面的指令并不会创建非宽字符 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
转换 termcap 描述为 terminfo 描述 |
|
如果可以的话清空屏幕 |
|
比较或输出 terminfo 描述 |
|
转换 terminfo 描述为 termcap 描述 |
|
P为 ncurses 提供配置信息 |
|
重新初始化终端为默认设置 |
|
清空终端并设置制表符长度 |
|
将 terminfo 文件从源文件格式转换到二进制格式的 terminfo 条目描述编译器需要 ncurses 例程 [terminfo 文件包含特定终端的功能信息] |
|
列出所有可用的终端类型,给出每个主名称和描述 |
|
可以在 shell 中使用终端特定的功能值;也可用来重置或初始化终端或者报告它的完整名称 |
|
可以用来初始化终端 |
|
到 |
|
包含在一个终端屏幕以多种复杂方式显示文本的函数;使用这些功能的一个好的例子是内核 make menuconfig 时的菜单显示 |
|
包含实现表单的函数 |
|
包含实现菜单的函数 |
|
包含实现面板的函数 |
attr 软件包包含管理文件系统对象的扩展属性的工具。
准备编译 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
Acl 软件包包含管理访问控制列表的工具,访问控制列表用于定义文件和目录更细粒度的自定义访问权限。
准备编译 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
Libcap 软件包实现了可用在 Linux 内核上的对 POSIX 1003.1e 功能的用户空间接口。这些功能将所有强大 root 权限划分为不同的权限组合。
避免安装静态库:
sed -i '/install.*STALIBNAME/d' libcap/Makefile
编译软件包
make
这个软件包没有测试套件。
安装软件包:
make RAISE_SETFCAP=no lib=lib prefix=/usr install chmod -v 755 /usr/lib/libcap.so.2.27
make 选项的含义:
RAISE_SETFCAP=no
这个选项跳过尝试对自身使用 setcap。这可以避免内核或文件系统不支持扩展功能时出现安装错误。
lib=lib
此参数用于在 x86_64 位系统中,将库文件安装到 $prefix/lib
而不是 $prefix/lib64
。此参数不影响 x86 系统。
需要移动共享库到 /lib
,因此需要重建
/usr/lib
中的 .so
文件:
mv -v /usr/lib/libcap.so.* /lib ln -sfv ../../lib/$(readlink /usr/lib/libcap.so) /usr/lib/libcap.so
Sed 软件包包含一个流编辑器。
首先修复 LFS 环境中的问题,然后移除一个失败的测试:
sed -i 's/usr/tools/' build-aux/help2man sed -i 's/testsuite.panic-tests.sh//' Makefile.in
准备编译 Sed:
./configure --prefix=/usr --bindir=/bin
编译软件包并生成 HTML 文档:
make make html
输入命令查看结果:
make check
安装软件包和它的文档:
make install install -d -m755 /usr/share/doc/sed-4.7 install -m644 doc/sed.html /usr/share/doc/sed-4.7
Psmisc 软件包包含用于显示运行中进程信息的程序。
准备编译 Psmisc:
./configure --prefix=/usr
编译软件包:
make
该软件包没有测试套件。
安装软件包:
make install
最后,将程序 killall 和 fuser 移动到 FHS 指定的位置:
mv -v /usr/bin/fuser /bin mv -v /usr/bin/killall /bin
Iana-Etc 软件包为网络服务和协议提供数据。
下面的命令将 IANA 提供的原始数据转换为 /etc/protocols
和 /etc/services
数据文件的正确格式:
make
该软件包没有测试套件。
安装软件包:
make install
Bison 软件包包含一个语法生成器。
首先,解决当前版本的构建问题:
sed -i '6855 s/mv/cp/' Makefile.in
准备编译 Bison:
./configure --prefix=/usr --docdir=/usr/share/doc/bison-3.4.1
编译该软件包,但是需要避免当前版本中存在的争用情况:
make -j1
考虑到 bison 和 flex 的检查有循环依赖。如有需要,在下一节安装 flex 之后,可以使用 make check 命令重新编译并检查 bison 软件包。
安装软件包:
make install
Flex 软件包包括一个用于生成识别文本模式的程序的工具。
首先,修复一个 glibc-2.26 引入的问题:
sed -i "/math.h/a #include <malloc.h>" src/flexdef.h
构建过程假设能使用程序 help2man 的 --help 选项来创建 man 手册。但这显然是不存在的,所以我们使用环境变量来跳过这步。现在,准备编译 Flex:
HELP2MAN=/tools/bin/true \ ./configure --prefix=/usr --docdir=/usr/share/doc/flex-2.6.4
编译软件包:
make
用以下命令测试结果(大约 0.5 SBU):
make check
安装软件包:
make install
一些程序还不知道 flex
并尝试运行它的预处理器 lex。为了支持这些程序,创建以
lex
仿真模式运行 flex
的符号链接 lex:
ln -sv flex /usr/bin/lex
Grep 软件包包含用于在文件中搜索的程序。
准备编译 Grep :
./configure --prefix=/usr --bindir=/bin
编译软件包:
make
用以下命令测试结果:
make -k check
安装软件包:
make install
Bash 软件包包含 Bourne-Again Shell。
准备编译 Bash:
./configure --prefix=/usr \ --docdir=/usr/share/doc/bash-5.0 \ --without-bash-malloc \ --with-installed-readline
新配置选项的含义:
--with-installed-readline
该选项告诉 Bash 使用系统中已经安装的 readline
库而不是使用自带的 readline 版本。
编译软件包:
make
如果不需要运行测试套件的话跳转到「安装软件包」。
准备测试,确保 nobody
用户可以写源文件树:
chown -Rv nobody .
现在,以 nobody
用户身份运行测试:
su nobody -s /bin/bash -c "PATH=$PATH HOME=/home make tests"
安装软件包并将主要的可执行文件移动至 /bin
:
make install mv -vf /usr/bin/bash /bin
运行新编译的 bash 程序(替换正在运行的那个):
exec /bin/bash --login +h
参数使 bash 进程成为一个可交互的登录 shell 并停用散列使得新程序可用的时候就能发现。
Libtool 软件包包含 GNU 通用库支持脚本。它用一致的、可移植的接口封装复杂的共享库。
准备编译 Libtool:
./configure --prefix=/usr
编译软件包:
make
用以下命令测试结果:
make check
在具有多个内核的系统上,libtool 的测试时间可以显著削减。为此,请在上面那行命令中添加 TESTSUITEFLAGS=-j<N>。例如,使用 -j4 或将减少 60% 的测试时间。
在 LFS 构建环境中已知有五个测试由于循环依赖会失败,但如果安装完 automake 之后重新检查,所有测试就都能通过。
安装软件包:
make install
GDBM 软件包包含 GNU 数据库管理器。是使用扩展散列,工作方法和标准 UNIX dbm 类似的数据库函数库。该库提供存储键/数据对、通过键搜索和检索数据、以及删除键和数据的原语。
准备编译 GDBM:
./configure --prefix=/usr \ --disable-static \ --enable-libgdbm-compat
配置选项的含义:
--enable-libgdbm-compat
该选项启用编译 libgdbm 兼容性库,因为一些 LFS 之外的软件包可能需要它提供的旧的 DBM 例程。
编译软件包:
make
用以下命令测试结果:
make check
安装软件包:
make install
Gperf 为键集合生成完美的哈希函数。
准备编译 Gperf:
./configure --prefix=/usr --docdir=/usr/share/doc/gperf-3.1
编译软件包:
make
该测试已知在运行多任务同时测试(即 -j 选项大于 1)时会失败。用以下命令测试结果:
make -j1 check
安装软件包:
make install
Expat 软件包包含一个用于解析 XML 的面向流的 C 库。
首先修复一个 LFS 环境中,回归测试的问题:
sed -i 's|usr/bin/env |bin/|' run.sh.in
准备编译 Expat:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/expat-2.2.7
编译软件包:
make
用以下命令测试结果:
make check
安装软件包:
make install
如果需要的话,安装帮助文档:
install -v -m644 doc/*.{html,png,css} /usr/share/doc/expat-2.2.7
Inetutils 软件包包含基本的网络程序。
准备编译 Inetutils:
./configure --prefix=/usr \ --localstatedir=/var \ --disable-logger \ --disable-whois \ --disable-rcp \ --disable-rexec \ --disable-rlogin \ --disable-rsh \ --disable-servers
配置选项的含义:
--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
Perl 软件包包含实用信息抽取与报告语言。
首先创建其中一个 Perl 配置文件和可选测试套件引用的基本 /etc/hosts
文件:
echo "127.0.0.1 localhost $(hostname)" > /etc/hosts
该版本的 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 \ -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 -k test
因为使用了最新的 gdbm 从而会导致一项测试失败。
安装软件包并清理:
make install unset BUILD_ZLIB BUILD_BZIP2
Module::CoreList 的命令行前端 |
|
用命令行与综合 Perl 归档网络(Comprehensive Perl Archive Network,CPAN)交互 |
|
从 Unicode 字符映射或 Tcl 编码文件为 Encode 模块编译 Perl 扩展 |
|
猜测一个或多个文件的编码类型 |
|
转换 |
|
转换 |
|
用于检查安装的 Perl 模块的 shell 脚本,并且能从一个安装的模块中创建 tar 包 |
|
在特定输入输出格式之间转换数据 |
|
可用于配置 |
|
将 C,sed,awk 以及 sh 一些最好的特性结合到一个单一的强大语言 |
|
到 perl 的硬链接 |
|
用于生成关于 Perl、或者一起发布的模块的 bug 报告,并用邮件通知 |
|
用嵌入到 Perl 安装目录或 Perl 脚本中的 pod 格式显示文档 |
|
Perl 安装验证程序;能用于验证 Perl 和它的库是否正确安装 |
|
用于生成发送到 Perl 开发者的感谢邮件 |
|
Perl 版本的字符编码转换程序 iconv |
|
用于将 Perl4 |
|
将文件从 pod 格式转换为 HTML 格式 |
|
将 pod 格式数据转换为格式化的 *roff 输入 |
|
将 pod 数据转换为格式化的 ASCII 文本 |
|
从文件中嵌入的 pod 文档显示使用信息 |
|
检查 pod 格式的文档文件语法 |
|
显示 pod 文档选中的章节 |
|
运行对 Test::Harness 模块测试的命令行工具 |
|
用 Perl 写的类似 tar 的程序 |
|
用于比较提取的文档和未提取的 Perl 程序 |
|
用于对 tar 归档文件中的内容进行模式匹配的 Perl 程序 |
|
打印或检查 SHA 校验码 |
|
用于 Perl 中的强制冗长警告诊断 |
|
转换 Perl XS 代码为 C 代码 |
|
显示 Zip 文件内部结构的详细信息 |
XML::Parser 模块是到 James Clark 的 XML 解析器的 Perl Expat 接口。
准备编译 XML::Parser:
perl Makefile.PL
编译软件包:
make
用以下命令测试结果:
make test
安装软件包:
make install
Intltool 是一个用于从源文件中抽取可翻译字符串的国际化工具。
首先修复 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
Autoconf 软件包包含用于生成自动配置源代码的 shell 脚本的程序。
准备编译 Autoconf:
首先,修复一个 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
生成能为多种类 Unix 系统自动配置软件源码包的 shell 脚本;它生成的配置脚本是独立的——运行脚本不需要 autoconf 程序 |
|
一个能生成给配置脚本使用的 C #define 语句模板文件的工具 |
|
M4 宏处理器的封装 |
|
对 autoconf 和 automake 模板文件作了更改后能以正确顺序自动运行 autoconf,autoheader,aclocal,automake,gettextize,以及 libtoolize 以节省时间 |
|
为软件包帮助生成 |
|
更改 |
|
帮助为软件包写 |
软件包包含了生成可与 Autoconf 一同使用的 Makefile 的程序。
准备编译 Automake:
./configure --prefix=/usr --docdir=/usr/share/doc/automake-1.16.1
编译软件包:
make
因为各个测试之间存在内部延时,故建议就算是在单核处理器的设备上,也使用 -j4 编译选项加速测试过程。用以下命令测试结果:
make -j4 check
已知 LFS 环境中 subobj.sh 测试会失败。
安装软件包:
make install
Xz 软件包包含用于压缩和解压文件的程序。它提供 lzma 和更新的 xz 压缩格式功能。和传统的 gzip 或 bzip2 命令相比,用 xz 压缩文本文件能获得更好的压缩率。
准备编译 Xz:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/xz-5.2.4
编译软件包:
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
解压标准输出 |
|
对 LZMA 压缩文件运行 cmp 命令 |
|
对 LZMA 压缩文件运行 diff 命令 |
|
对 LZMA 压缩文件运行 egrep 命令 |
|
对 LZMA 压缩文件运行 fgrep 命令 |
|
对 LZMA 压缩文件运行 grep 命令 |
|
对 LZMA 压缩文件运行 less 命令 |
|
用 LZMA 格式压缩或解压文件 |
|
用于 LZMA 压缩文件的轻便解码器 |
|
显示存储在 LZMA 压缩文件头部的信息 |
|
对 LZMA 压缩文件运行 more 命令 |
|
用 LZMA 格式解压文件 |
|
用 XZ 格式解压文件 |
|
用 XZ 格式压缩或解压文件 |
|
解压到标准输出 |
|
对 XZ 压缩文件运行 cmp 命令 |
|
用于 XZ 压缩文件的轻便解码器 |
|
对 XZ 压缩文件运行 diff 命令 |
|
对 XZ 压缩文件运行 egrep 命令 |
|
对 XZ 压缩文件运行 fgrep 命令 |
|
对 XZ 压缩文件运行 grep 命令 |
|
对 XZ 压缩文件运行 less 命令 |
|
对 XZ 压缩文件运行 more 命令 |
|
用 Lempel-Ziv-Markov 链算法实现无损块排序数据压缩的库 |
Kmod 软件包包含用于加载内核模块的库和工具
准备编译 Kmod:
./configure --prefix=/usr \ --bindir=/bin \ --sysconfdir=/etc \ --with-rootlibdir=/lib \ --with-xz \ --with-zlib
配置选项的含义:
--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
Gettext 软件包包含用于国际化和本土化的工具。这允许用 NLS(Native Language Support,本地语言支持) 编译程序,使得能以用户的本地语言输出信息。
准备编译 Gettext:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/gettext-0.20.1
编译软件包:
make
用以下命令测试结果(需要较长一段时间,大概 3 SBUs):
make check
安装软件包:
make install chmod -v 0755 /usr/lib/preloadable_libintl.so
复制标准 Gettext 基础文件到源码包 |
|
shell 格式字符串的替代环境变量 |
|
通过查看信息目录中的转换将原来语言信息转换为用户语言 |
|
主要作为 gettext 的一个 shell 函数库 |
|
复制所有标准 Gettext 文件到指定软件包的顶层目录以开始国际化 |
|
根据属性过滤翻译目录的信息并操作属性 |
|
连接和合并给定 |
|
比较两个 |
|
查找给定 |
|
转换翻译目录到不同的字符编码 |
|
创建一个英语翻译目录 |
|
对翻译目录的所有翻译运行命令 |
|
对翻译目录的所有翻译应用过滤器 |
|
从翻译目录生成一个二进制信息目录 |
|
从翻译目录中抽取所有符合指定模式或属于特定源文件的信息 |
|
创建一个新的 |
|
合并两个原译到一个单独的文件 |
|
反向编译一个二进制信息目录为原译文本 |
|
统一重复翻译为一个翻译目录 |
|
显示语法形式取决于多种母语翻译的文本信息的本地语言翻译 |
|
对 Serbian 文本从 Cyrillic 重新编码为 Latin 脚本 |
|
从指定源文件中抽取可翻译信息行用于生成第一个翻译模板 |
|
定义 autosprintf 类,该类使 C 格式化输出在 C++程序中能和 <string> 字符串以及 <iostream> 流一起使用 |
|
包括多种 Gettext 程序使用的常用例程的私有库;并不用于一般用途 |
|
用来写处理 |
|
包括多种 Gettext 程序使用的常用例程的私有库;并不用于一般用途 |
|
LD_PRELOAD 帮助 |
Libelf 是处理 ELF(可执行与可链接格式)文件的库。
Libelf 是软件包 elfutils-0.177 种的一部分。使用 elfutils-0.177.tar.bz2 作为源码包。
编译 Libelf 前的准备:
./configure --prefix=/usr
编译软件包:
make
用以下命令测试结果:
make check
仅安装 Libelf:
make -C libelf install install -vm644 config/libelf.pc /usr/lib/pkgconfig
Libffi 库为各种调用规范提供了一个可移植的,高级编程接口。允许程序员在运行时,通过调用接口描述调用任意指定函数。
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
OpenSSL 软件包包含管理工具和加密相关的库。其中提供的这些加密功能对于其他软件包,比方说 OpenSSH,email 应用和网页浏览器(访问 HTTPS 站点)来说,十分有用。
首先,解决上游发现的问题:
sed -i '/\} data/s/ =.*$/;\n memset(\&data, 0, sizeof(data));/' \ crypto/rand/rand_lib.c
编译 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.1c cp -vfr doc/* /usr/share/doc/openssl-1.1.1c
软件包 Python 3 包含了 Python 的开发环境。对于面向对象编程,书写脚本,构建大型程序的原型,或者开发整个应用程序而言,非常有用。
编译 Python 前的准备:
./configure --prefix=/usr \ --enable-shared \ --with-system-expat \ --with-system-ffi \ --with-ensurepip=yes
配置选项的含义:
--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.7 /usr/bin/pip3
安装命令的含义:
修复库的权限问题,同其他库保持一致。
如果需要,安装预格式化好的文档:
install -v -dm755 /usr/share/doc/python-3.7.4/html tar --strip-components=1 \ --no-same-owner \ --no-same-permissions \ -C /usr/share/doc/python-3.7.4/html \ -xvf ../python-3.7.4-docs-html.tar.bz2
文档安装命令的含义:
--no-same-owner
and
--no-same-permissions
确保安装文件的归属和权限是正确的。没有这个选项的话,运行 tar 时会以上游创建者的身份安装软件包内的文件。
Ninja 是一个专注于速度的小型构件系统。
在运行时,ninja 通常会并行最大数量的进程。默认值是系统的核心数乘以二。有些时候会导致 CPU 过热,或者内存容量不足。如果是命令行运行,通过传递 -jN 参数可以限制并行的进程数,但是有些软件包虽然潜入了 ninja 的执行却不会传递 -j 参数。
通过使用下方 可选 过程,让用户能够通过环境变量 NINJAJOBS 来限制并行进程的数量。例如, 设定:
export NINJAJOBS=4
将限制 ninja 最多仅四个进程并行。
如果需要,运行以下命令以添加使用环境变量 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
Meson 是一个开源代码构建系统,不仅速度非常快,而且更重要的是对用户极其友好。
通过下列命令编译 Meson:
python3 setup.py build
这个软件包还没有测试套件。
安装软件包:
python3 setup.py install --root=dest cp -rv dest/* /
安装参数的含义:
--root=dest
默认情况下执行命令 python3 setup.py install 会将所有文件(比如 man 手册)安装到 Python Eggs。指定根路径则 setup.py会将这些文件安装到标准层次目录。然后只需要复制层次目录,文件即处于标准位置。
Coreutils 软件包包含用于显示和设置基本系统特性的工具。
POSIX 要求 Coreutils 中的程序即使在多字节语言环境也能正确识别字符边界。下面的补丁修复这个不兼容性以及其它一些和国际化相关的错误。
patch -Np1 -i ../coreutils-8.31-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
配置选项的含义:
此命令更新已有的配置文件,以使其和 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
LFS-Bootscripts 包中的一些脚本依赖于 head、nice、 sleep 和 touch。由于 /usr
在启动的早期和晚期阶段可能不可用,这些二进制文件需要被存在放在根分区(/bin 目录)以保证 FHS 合规性:
mv -v /usr/bin/{head,nice,sleep,touch} /bin
根据 base32(RFC 4648)规范编码和解码数据 |
|
根据 base64(RFC 4648)规范编码和解码数据 |
|
打印或查看 BLAKE2 (512-bit) 校验和 |
|
从文件名称中抽取路径和后缀 |
|
使用各种算法对数据进行编码或解码 |
|
将文件连接到标准输出 |
|
改变文件和目录的安全上下文 |
|
更改文件和目录的组所有者 |
|
更改每个文件为指定模式的权限;模式可以是要实现更改的符号表示 或者表示新权限的十进制数字 |
|
更改文件和目录的用户和/或组所有者 |
|
使用指定目录作为 |
|
输出指定文件的循环冗余检验(CRC)校验码和字节数目 |
|
比较两个排序后的文件,以三列输出不用的行和相同的行 |
|
复制文件 |
|
将指定文件分割为几个新的文件,根据指定的模式或者行数分割并 输出每个新文件的字节数 |
|
根据指定的域或位置,选择输出行的组成 |
|
根据指定格式显示当前时间,或设置系统日期 |
|
用给定的块大小和数目复制文件,同时可以进行一些转换 |
|
报告所有挂载的文件系统中可用(和已用)磁盘空间,或只是 含有指定文件的文件系统 |
|
列出指定目的的内容(和 ls 命令相同) |
|
输出设置 |
|
从一个文件名称中抽取非目录后缀 |
|
报告当前目录使用的磁盘空间,根据指定的每个文件夹(包括子文件夹) 或每个指定的文件 |
|
显示给定的字符串 |
|
在更改后的环境中运行命令 |
|
把 tab 键转换为空格 |
|
计算表达式 |
|
输出所有指定整数的质数因子 |
|
什么都不做;总是以指示失败的状态码退出 |
|
重新格式化给定文件中的段落 |
|
折叠指定文件的行 |
|
报告一个用户的组成员 |
|
输出指定文件的前十行(或指定数目行数) |
|
报告主机的数字标识符(以十六进制) |
|
报告当前用户或指定用户的有效 用户 ID、组 ID 以及组成员 |
|
复制文件的同时设置权限模式,如果可以的话包括用户和组 |
|
从两个单独的文件中连接有相同域的行 |
|
用指定的名称创建到一个文件的硬链接 |
|
在文件之间建立硬链接或软(符号)链接 |
|
报告当前用户的登录名 |
|
列出给定目录的内容 |
|
报告或检查消息摘要 5(MD5) 校验码 |
|
用指定的名称新建目录 |
|
用指定的名称在 UNIX 中创建先进先出(FIFO) 的「命名管道」 |
|
用指定的名称创建设备结点;设备结点是一个特殊字符文件、特殊块文件或先进先出 |
|
以安全方式新建临时文件;在脚本中使用 |
|
移动或重命名文件或目录 |
|
以更改后的调度优先级运行程序 |
|
标记指定文件的行号 |
|
以不能被挂起方式运行命令,输出重定向到一个日志文件 |
|
输出进程可用的处理单元数目 |
|
转换数字为人可读字符串或者相反 |
|
以十进制或其他格式转储文件 |
|
合并指定文件,用 tab 字符分隔,以行并列方式连续合并 |
|
检查文件名是否可用 |
|
轻量级的 finger 客户端;报告指定用户的信息 |
|
对文件进行分页分行用于打印 |
|
输出环境 |
|
根据指定格式打印指定参数,类似于 C 语言的 printf 函数 |
|
用文中的每个关键字,根据文件的内容中建立重排索引 |
|
报告当前工作目录的名称 |
|
报告指定符号链接的值 |
|
打印解释后路径 |
|
删除文件或目录 |
|
如果目录为空则删除 |
|
以指定安全上下文运行命令 |
|
用指定的范围和增长步长输出一序列数字 |
|
打印或检查 160-bit 安全哈希算法1(SHA1) 校验码 |
|
打印或检查 224-bit 安全哈希算法校验码 |
|
打印或检查 256-bit 安全哈希算法校验码 |
|
打印或检查 384-bit 安全哈希算法校验码 |
|
打印或检查 512-bit 安全哈希算法校验码 |
|
用复杂形式多次重写指定文件,使得难以恢复其中的数据 |
|
打乱文本行 |
|
暂停指定时间 |
|
排序给定文件的行 |
|
根据大小或行数分割文件为多个块 |
|
显示文件或文件系统状态 |
|
用改变后的缓冲操作在标准流上运行命令 |
|
设置或报告终端行设置 |
|
打印指定文件的校验码和块数目 |
|
清空文件系统缓存;强制更改块到磁盘并更新超级块 |
|
反向输出给定文件 |
|
输出每个给定文件的最后十行(或给定数目的行) |
|
从标准输入读入并写出到标准输出和指定文件 |
|
比较值并检查文件类型 |
|
有限时间内运行命令 |
|
更改文件时间戳,设置指定文件的访问和修改时间为当前时间; 如果文件不存在则创建空文件 |
|
从标准输入转换、压缩并删除指定字符 |
|
不做任何事情,总是成功;总是以表示成功的状态码退出 |
|
压缩或扩展文件到特定大小 |
|
进行拓扑排序;根据指定文件的部分排序写出完全有序列表 |
|
报告链接到标准输入的终端文件名称 |
|
报告系统信息 |
|
转换空格为 tab 键 |
|
忽略所有除非出现连续相同的行 |
|
移除指定文件 |
|
报告当前登录的用户名 |
|
和 ls -l 相同 |
|
报告给定文件的行数、单词数和字节数,以及给定多个文件时总的行数 |
|
报告谁登录了 |
|
报告和当前有效用户 ID 关联的用户名 |
|
重复输出「y」或指定的字符串直到被杀死 |
|
stdbuf 使用的库 |
Check 是 C 语言的单元测试框架。
配置 Check 准备编译:
./configure --prefix=/usr
构建软件包:
make
现在编译完成了。运行 Check 的测试套件,输入以下命令:
make check
注意,Check 的测试套件可能会占用挺长(上至 4 SBU)的时间。
安装软件包,并修复脚本:
make docdir=/usr/share/doc/check-0.12.0 install sed -i '1 s/tools/usr/' /usr/bin/checkmk
Diffutils 软件包包含显示文件和目录差异的程序。
准备编译 Diffutils:
./configure --prefix=/usr
编译软件包:
make
用以下命令测试结果:
make check
安装软件包:
make install
Gawk 软件包包含用于操作文本文件的程序。
首先,确保哪些不需要的文件没有被安装:
sed -i 's/extras//' Makefile.in
准备编译 Gawk:
./configure --prefix=/usr
编译软件包:
make
用以下命令测试结果:
make check
安装软件包:
make install
如果需要的话,安装帮助文档:
mkdir -v /usr/share/doc/gawk-5.0.1 cp -v doc/{awkforai.txt,*.{eps,pdf,jpg}} /usr/share/doc/gawk-5.0.1
Findutils 软件包包含查找文件的程序。这些程序提供递归搜索目录树、创建、管理以及搜索数据库(通常比递归式的 find 要快,但如果数据库最近没有更新的话结果不可靠)。
首先,抑制测试中可能在某些机器中出现的无限循环:
sed -i 's/test-lock..EXEEXT.//' tests/Makefile.in
然后,对应 glibc-2.28 或更高版本 glibc 的需求做一些修复:
sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' gl/lib/*.c sed -i '/unistd/a #include <sys/sysmacros.h>' gl/lib/mountlist.c echo "#define _IO_IN_BACKUP 0x100" >> gl/lib/stdio-impl.h
准备编译 Findutils:
./configure --prefix=/usr --localstatedir=/var/lib/locate
配置选项的含义:
--localstatedir
该选项改变 locate 数据库的位置为 FHS
兼容的 /var/lib/locate
。
编译软件包:
make
用以下命令测试结果:
make check
安装软件包:
make install
LFS-Bootscripts 包中的部分脚本依赖于 find。由于 /usr
在启动的早期阶段可能不可用,该程序需要被放置在根分区。updatedb
脚本也需要修改,以明确的路径更正:
mv -v /usr/bin/find /bin sed -i 's|find:=${BINDIR}|find:=/bin|' /usr/bin/updatedb
Groff 软件包包含用于处理和格式化文本的程序。
Groff 希望环境变量 PAGE
包含默认的页面大小,对于美国的用户,为 PAGE=letter
,对于其它地方,PAGE=A4
更合适。尽管在编译的时候配置了默认页面大小,后面通过 echo「A4」或「letter」到 /etc/papersize
文件仍然可以修改。
准备编译 Groff:
PAGE=<paper_size>
./configure --prefix=/usr
该软件不支持并行构建。编译软件包:
make -j1
该软件包没有测试套具。
安装软件包:
make install