SerenityOS 移植 GNU make 4.4.1:两个关键补丁的源码级解析
2026/9/13 2:52:20 网站建设 项目流程

SerenityOS 移植 GNU make 4.4.1:两个关键补丁的源码级解析

【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity

SerenityOS 通过Ports目录下的移植脚本与补丁,将 GNU make 4.4.1 完整地带入了这套独立实现的操作系统。本文以 Ports/make/patches/ReadMe.md 为骨架,深入解析 make 移植过程中必须解决的两个核心问题——归档文件扫描所需的ar.h头文件,以及缺失confstr时的默认 PATH 硬编码,并结合移植脚本与 LibC 实现给出可验证的源码依据。

移植 make 到 SerenityOS 的整体背景

SerenityOS 的第三方软件移植体系位于仓库根目录的 Ports 目录。每个移植项都是一个独立的子目录,其中至少包含一个package.sh脚本,用于描述软件的名称、版本、依赖、下载来源(files数组,支持mirror://镜像源与 SHA256 校验)、配置选项(configopts)、编译选项(makeopts)等元信息;可选地,还会附带一个patches/目录,存放针对该软件源码的定制补丁。

GNU make 的移植项位于 Ports/make,其package.sh定义了以下关键信息:

#!/usr/bin/env -S bash ../.port_include.sh port='make' version='4.4.1' useconfigure='true' use_fresh_config_sub='true' config_sub_paths=("build-aux/config.sub") files=( "mirror://gnu/make/make-${version}.tar.gz#dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3" ) configopts=("--without-guile" "CFLAGS=-std=c17")

解读这些字段:

  • useconfigure='true':启用 autoconf 流程,即依次执行pre_configureconfigure(默认运行./configure);
  • use_fresh_config_sub='true'config_sub_paths=("build-aux/config.sub"):在打补丁阶段用更新版本的config.sub替换 make 自带的旧版脚本,使其能识别$SERENITY_ARCH-serenity这样的宿主三元组(--host=${SERENITY_ARCH}-serenity总会作为配置参数传入,详见 Ports/README.md 中的configopts说明);
  • files:从gnu镜像源拉取make-4.4.1.tar.gz,并用给定的 SHA256 摘要做完整性校验;
  • configopts:禁用 Guile 扩展支持,并以 C17 标准编译。

仓库的 Ports/README.md 还说明,默认安装一个移植项(不传任何参数)会按installdepends → fetch → patch → configure → build → install的顺序执行;其中patch步骤会将patches/*.patch依次应用到源码树,并在成功后于workdir下写入.foo_applied标记文件,确保同一补丁只应用一次。

补丁一:让ar_scan在 SerenityOS 上包含ar.h

补丁原文

--- a/src/arscan.c +++ b/src/arscan.c @@ -331,7 +331,7 @@ ar_scan (const char *archive, ar_member_func_t function, const void *varg) #endif #ifndef WINDOWS32 -# if !defined (__ANDROID__) && !defined (__BEOS__) && !defined(MK_OS_ZOS) +# if !defined (__ANDROID__) && !defined (__BEOS__) && !defined(MK_OS_ZOS) && !defined(__serenity__) # include <ar.h> # else /* These platforms don't have <ar.h> but have archives in the same format

提交者为 Andreas Kling(kling@serenityos.org),提交时间为 2020-12-15。

修改了什么

补丁修改的是 GNU make 的src/arscan.c文件中ar_scan()函数所在的条件编译区域。原逻辑是:只要不是 Windows,且平台不是 Android、BeOS、z/OS,就#include <ar.h>。补丁在条件中追加了&& !defined(__serenity__),把 SerenityOS 排除在“无 ar.h 平台”名单之外,使 SerenityOS 构建时同样能包含标准的<ar.h>

ar.h是 POSIX 归档(ar 格式)文件头的标准定义所在,其中包含ar_hdr结构体以及ARMAGARFMAG等宏。GNU make 的ar_scan()依赖这些定义来遍历静态库(.a文件)中的成员表,从而支持对归档内成员的依赖追踪与时间戳比较。若 SerenityOS 被误归入“无 ar.h”分支,make 将退化为使用兼容格式的内部定义,可能与 SerenityOS 的归档格式产生偏差。

SerenityOS 侧的依据

  • 该补丁依赖编译器在构建时定义__serenity__宏;从仓库的移植框架与构建配置看,这是 SerenityOS 工具链为所有目标平台代码注入的通用宏(例如--host=${SERENITY_ARCH}-serenity三元组贯穿移植构建流程,见 Ports/README.md)。
  • 静态库归档是 make 处理 C/C++ 链接场景的基础输入格式,ar_scan属于 make 源码(上游src/arscan.c)的核心归档扫描例程,本补丁即针对该函数所在文件。

补丁二:缺失confstr时硬编码默认 PATH

补丁原文

--- a/src/job.c +++ b/src/job.c @@ -2430,6 +2430,7 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv) /* execvp() will use a default PATH if none is set; emulate that. */ if (p == NULL) { +#ifndef __serenity__ size_t l = confstr (_CS_PATH, NULL, 0); if (l) { @@ -2437,6 +2438,9 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv) confstr (_CS_PATH, dp, l); p = dp; } +#else + p = strdup("/bin:/usr/bin"); +#endif } cmd = (char *)find_in_given_path (argv[0], p, NULL, 0);

提交者为 Cameron Youell(cameronyouell@gmail.com),提交时间为 2023-03-27。

修改了什么

补丁作用于 make 的src/job.cchild_execute_job()函数。该函数在通过execvp()之类的调用启动子进程前,会先构造查找可执行文件的 PATH 列表:若环境变量中未显式设置 PATH(p == NULL),上游代码会调用 POSIX 的confstr(_CS_PATH, ...)获取系统默认路径;但在 SerenityOS 上confstr尚未实现,因此补丁以#ifndef __serenity__保留原逻辑,并在#else分支直接strdup("/bin:/usr/bin")作为默认查找路径。

这条补丁的语义等价于execvp()在 PATH 未设置时的行为——GNU make 上游注释也明确写着"execvp() will use a default PATH if none is set; emulate that"。SerenityOS 将系统命令统一安装于/bin/usr/bin(与移植安装目标Build/<arch>/Root/usr一致,见 Ports/README.md 对安装根目录的描述),因此硬编码这两个目录在语义上与系统布局相符。

关联源码佐证

  • 补丁所在的child_execute_job()是 make 作业(job)执行的核心路径,负责子进程的 fork/exec 与执行环境构造;
  • 补丁紧接着调用find_in_given_path(argv[0], p, NULL, 0),即用该 PATH 列表解析要执行的命令的绝对路径——硬编码的/bin:/usr/bin直接影响 make 在 SerenityOS 上能否正确找到sh、编译器等工具。

两个补丁的协作逻辑与移植方法论

把两份补丁放在一起看,可以还原出一条清晰的移植思路:让 GNU make 在 SerenityOS 上“表现得像在原生 POSIX 系统上一样”

  • 补丁一解决的是编译期头文件可用性:SerenityOS 提供<ar.h>,make 应当使用标准定义而非平台私有回退;
  • 补丁二解决的是运行期库函数缺失confstr在 SerenityOS LibC 中尚不可用,就用与系统目录布局等价的常量替代。

从源码结构看,这两处修改都严格限定在条件编译块内(#ifndef __serenity__/#if ... && !defined(__serenity__)),不触碰上游在其他平台上的行为,符合 SerenityOS 移植补丁“最小侵入、按平台隔离”的通行做法,也便于后续 make 升级版本时以同样的模式迁移(Ports/README.md 的dev模式即提供了补丁迁移辅助流程)。

如何验证与应用

若要在 SerenityOS 构建环境中安装 make 移植项,可在仓库根目录执行:

cd Ports/make ./package.sh

默认会完成依赖安装、源码下载与 SHA256 校验、补丁应用、configure、编译与安装全流程;若需在重新移植前清理旧构建产物,可先执行./package.sh clean。补丁应用是否成功,可通过workdir(默认make-4.4.1)下生成的.foo_applied标记文件确认(见 Ports/README.md)。

验证补丁生效的方式有两类:

  1. 编译期验证:检查src/arscan.c在预处理后确实包含<ar.h>(例如用-E预处理输出中检索ar.h),并确认job.c__serenity__分支进入strdup("/bin:/usr/bin")路径;
  2. 运行期验证:在 SerenityOS 系统内运行make,在一个未显式设置 PATH 的环境中执行含外部命令的规则,确认子进程可被正确找到并启动;同时用make处理含静态库依赖的目标,确认归档扫描正常。

小结

GNU make 是 SerenityOS 移植体系中典型的"纯 POSIX 工具"案例:主体代码无需改动,仅需针对平台差异打上少量条件编译补丁。Ports/make/patches/ReadMe.md 记录的两个补丁,分别从"头文件缺失判定"与"系统调用缺失回退"两个角度,展示了 SerenityOS 移植工作的常见模式——理解上游代码的假设、用最小改动注入平台分支、并以系统自身的目录与头文件约定作为回退依据。对于想要为 SerenityOS 贡献新移植项或维护既有移植项的开发者,这两个补丁是很好的入门范例。

【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询