Hard Build System

来自osdev
跳到导航 跳到搜索
Everything should be made as simple as possible, but not simpler. (译者注:这句话是爱因斯坦的名言,其实本身的含义也仁者见仁智者见智。可以理解为:凡事尽可能简洁,但不能太过简单;也可以理解为:任何事情都应该做到最简单,而不是相对简单。总的来说应该是提醒读者要简化问题,但是不能太过个人理想化。)

自制操作系统的构建系统需要执行一项复杂的任务,并且往往是高度可定制的。 选择好的抽象来管理这种复杂性是至关重要的。 源代码树有很多方面的配置,比如目标处理器体系结构、使用了哪些优化和警告、代码安装在哪里、使用了哪个编译器等等。 没有万能的解决方案。

陷入提供简单构建系统的陷阱是很诱人的。 用户只需运行具有管理员权限的脚本,就可以在系统范围内进行更改,生成源代码,安装程序,并在另一端获得操作系统映像。 不费吹灰之力,就等着。 这种方法太简单了,最终也会太复杂。

示例

交叉构建具有用户空间和端口的功能齐全的类Unix操作系统,需要的步骤和决策如下:

  • 下载源代码 (工具链源、核心操作系统、端口)。
    • 决策:从哪些镜像下载。
    • 决策:源代码安装位置。
  • 按照文档中所述安装所需的程序。
    • 决策:从何处获取程序(包管理(deb、rpm等),还是从源代码构建,…)。
    • 决策:使用哪个版本的程序。
    • 决策: 在哪里安装程序。
  • 为此操作系统生成并安装自定义程序。
    • 决策:使用哪种编译器和编译器选项。
    • 决策: 在哪里安装程序。
  • 准备一个包含标准库头的System Root(制作Hosted GCC Cross Compiler所需)。
    • 决策:system root所在位置。
    • 决策: 使用什么处理器架构。
  • 构建并安装交叉工具链:
    • 决策:使用哪些编译器和编译器选项来编译交叉编译器。
    • 决策: 在哪里安装交叉编译器。
  • 构建核心操作系统和端口,并安装到系统根目录中。
    • 决策:使用哪种交叉编译器以及用于编译操作系统和端口的编译器选项。
    • 决策: 这是否是调试版本,以及是否使用了编译器保护和安全措施。
    • 决定:要建造哪些端口。
  • 根据sysroot内容创建可引导映像。
    • 决策: 使用什么引导方法 (cdrom,硬盘,软盘)。
    • 决策:包括什么和不包括什么(如果进行最小构建)。
    • 决策:initrd需要哪些驱动。
  • 在虚拟机中运行操作系统。
    • 决策:使用哪个虚拟机。
    • 决策:处理器功能、模拟硬件、可用内存大小。
    • 决策: 是否启用硬件加速。
  • 在真正的硬件上安装操作系统。
    • 决策:使用哪个Bootloader。
    • 决策: 使用什么硬盘和分区。
    • 决策:使用什么文件系统。

很明显,还有更多的决定要做,用户会做出不同的决定。 您不应该强制用户接受太多这些决定。 安装软件和进行系统范围的更改是用户的责任,而不是构建系统的责任。 相反,您应该记录需要做的事情,并让用户去做。

硬构建系统

硬构建系统只是构建操作系统。 用户通过遵循文档或可能使用方便的脚本手动准备构建环境。 它相信用户能够安装先决条件的程序和库。 它信任用户下载必要的源代码。

由用户控制,用户决定要做什么,然后手动操作或调用方便的脚本(如果适用)。 这与简单的构建系统形成对比,在该系统中,脚本处于控制之中,并且可能会咨询用户。 我们的目标不是让事情变得困难,而是接受用户必须做出的特定决定,并让设计不再过于简化需求。

帮助用户

帮助用户做的事情:

  • 记录过程。
  • 链接主要源代码镜像。
  • 记录常见Linux发行版的先决条件程序和列表包名称。
  • 记录如何创建交叉编译器(可能为通用系统提供预构建的工具链)。
  • 有一种简单的方法来创建system root并用标准库头填充它。
  • 如果未指定,默认为合理的优化设置。

记录主要的做事方式,并为那些有愿望的人提供第二条捷径。

交叉编译很困难

交叉编译本质上比本机编译具有更高的复杂性。 例如,其实存在两个工具链:一个用于制作本地程序,另一个用于交叉编译。 存在对System Root的使用。 你必须特别准备汇编。

解决办法是接受这个事实。 交叉编译自制操作系统是构建您的操作系统的次要途径,它只能这么简单。 构建操作系统的主要途径是在自制的操作系统上构建自身。 您可能还没有做到这一点,但要将构建系统设计为主要在自制操作系统上运行。

默认情况下,让构建系统始终为当前系统编译。 当您主要在自制操作系统下进行开发时,这是自然而然的选择。 通过设置 CCHOST 或将 -- host选项传递给您的构建系统,让用户明确请求交叉编译代码。 当您的操作系统目标是支持多种处理器时,交叉编译时还处在不清楚默认目标平台的情况下。 如果您在ARM系统上编译您的MIPS和x86-64操作系统,该怎么办? 通过以本地操作系统为目标来解决问题 (即使使用Linux编译器编译操作系统是不明智的),从而迫使用户指定预期的交叉目标。

分离工具链

当您在其自身下开发操作系统时,您可以使用系统编译器来编译您的操作系统。 系统编译器只是安装在系统目录中的程序。 交叉编译时,这种情况很自然: 交叉编译器只是一个安装在某个地方的程序。

将交叉编译器存储在用户选择的目录中,例如 $HOME/opt/x86_64-myos,并将其 bin 放在 PATH 中。 不要把它放在操作系统的源代码目录中,它与此无关。

工具链构建的编译器应该是操作系统上的本机编译器,由交叉编译器交叉编译,如果您做到这一点的话。

做就是学习

用户通过手动执行以下文档中的操作来学习。 这些信息在操作系统开发中至关重要,在操作系统开发中,开发人员必须是专家才能成功。 能够制作交叉编译器是将编译器移植到操作系统的绝佳做法。 当编译过程失败时,理解它是有帮助的。

好用户脚本

你可能会发现你总是做出特定的决策 (即使有些人可能会做出其他决策)。 在这种情况下,你可以简单地编写个人脚本,让事情变得简单。 您可以共享这些脚本,其他人可以根据需要调整它们或直接使用它们。

配置是个人的

不需要贡献者编辑属于构建系统的文件(如果已编辑,将显示为版本控制更改的文件)来定制琐碎的决策。 传递到生成系统的环境变量和选项是存在于通用生成系统之外的个人配置的示例。

例如,您可能会想在项目的Makefile中对交叉编译器目录的路径进行硬编码。 不要这样做,而是假设它已经在PATH路径中。

轻松构建系统

一个简单的构建系统负责尽可能多的工作,避免用户的任何困难。 当然,这种程度是存在的。 最终容易构建的系统是当用户盲目地将URL通过管道传输到管理Shell时:

download-to-stdout http://example.com/myos.sh | sudo sh

它将尝试做尽可能多的工作来可靠地了解本地操作系统。 它将找出需要安装的软件,检测发行版并安装正确的软件包,如果需要,下载并编译源代码,如果需要,可能会进行全局更改,通常会尝试选择合理的默认值。 脚本将非常复杂,因为外面的系统环境非常不同。 用户只能希望它能正常工作,而不会过多地破坏本地系统。

交互式构建系统

交互式构建系统是盲目轻松构建系统的更上一层楼。 它询问正在进行的问题,例如源代码应该安装在哪里。 优秀的构建系统可能会问很多问题,并提出合理的违规判断。 这样的脚本往往比盲文更复杂。

另一种方法是用户下载脚本并简单地在顶部编辑变量。 这至少避免了交互的复杂性。

简单的贡献谬误

如果构建项目所需的工作量较小,则更容易吸引贡献者。 最好是完全不做任何努力,也不做任何决定。 但请想一想:这些贡献者值得吗? 如果他们无法手动构建跨工具链,安装必要的程序,下载源代码以及所有内容 - 他们能为操作系统做出任何不平凡的贡献吗?

如果您真的希望让事情变得简单,而不是支持它们通常使用的许多截然不同的系统,您应该成为能自托管的系统。 然后,您可以简单地为他们提供您的操作系统的映像,其中包含已经完美设置的开发工具和源代码。 他们可以在本地安装,也可以在虚拟机中运行。

也许目标仅仅是让他们可以轻松地试用您的操作系统? 在这种情况下,您只需要偶尔发布或设置夜间构建服务。 从现有的源代码建立映像永远比运行现有的映像更难。

无错误恢复

不幸的是,在编译操作系统时,事情往往会出错。 如果发生这种情况,不成熟的脚本可能会出现严重故障。 可靠的脚本会检测到这一点,然后就会合理的失败退出。 例如,用户必须完全重新开始,重复构建交叉编译器的相同工作,而这可能只是因为您用尽了链接内核的硬盘空间。

一个更好的交互式脚本可能只是询问用户是否想再试一次或跳过某个特定步骤,而代价是脚本更复杂。 需要将其与用户仅在shell中手动输入命令进行比较。

可选来源脚本

一些解决方案会创建一个小脚本,用户可以在当前shell中获取它(相当于#include的shell)。 这些脚本通常设置几个环境变量,并将内容添加到PATH。 硬构建系统只需让用户将目录永久添加到例如在 ~/.profilePATH中。

信任问题

当您必须理解以root身份运行的复杂shell脚本时,很难信任一个容易构建的系统。 更容易验证自己输入的命令,以非特权用户身份编译,读取makefile并在调用脚本之前构建脚本。 更容易确保easy build系统不会重新开始重命名/usr中的文件和其他可怕的事情。

另见

文章

外部链接

注:虽然Peter Miller论文的目的是合理的,但第5.4节中概述的依赖项配置被认为低于Makefile中概述的依赖项配置。