ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

SerenityOS 端口系统完全指南:package.sh、构建流水线与移植实战

SerenityOS 端口系统完全指南:package.sh、构建流水线与移植实战 SerenityOS 端口系统完全指南package.sh、构建流水线与移植实战【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity本篇技术指南以 SerenityOS 仓库 Ports/README.md 为骨架结合 .port_include.sh 等核心实现源码系统讲解 SerenityOS 的软件移植Porting体系如何安装与批量构建第三方软件、package.sh脚本的每个子命令与执行流水线以及如何为某个软件编写自己的端口脚本并贡献回上游。读完本文你将掌握在 SerenityOS 构建环境中安装、卸载、调试第三方软件以及将任意开源软件移植到 SerenityOS 的完整方法论。一、什么是 SerenityOS 的 Ports 系统SerenityOS 是一个从零构建的类 Unix 操作系统其用户态没有继承任何现成的二进制生态。为了让 Bash、Vim、DOOM、SDL2 等经典开源软件能够在这套全新系统上运行Serenity 提供了ports端口机制把第三方软件源码下载下来打上针对 SerenityOS 的适配补丁再用 Serenity 自己的交叉编译工具链编译安装最终落盘到系统镜像的 root 文件系统里。这套机制的全部入口都位于仓库的 Ports 目录每个子目录对应一个可移植软件如 Ports/bash、Ports/ncurses每个子目录内必须有一个package.shBash 脚本声明软件名称、版本、依赖、下载地址与编译选项可选的patches/目录存放针对 SerenityOS 的补丁文件目录根的 .port_include.sh 是整套脚本框架的引擎所有端口的魔法都发生在这里AvailablePorts.md 以表格形式列出当前仓库中全部可用端口及其版本。使用前提执行端口脚本前你必须已经成功构建过 SerenityOS 本体并处于 Serenity 的构建环境中交叉编译工具链、Build/arch/Root根文件系统均已就绪。端口脚本安装的目标位置是Build/架构/Root即根文件系统暂存目录而不是宿主机的/usr。二、可用端口清单与外部端口2.1 官方端口清单完整的端口目录维护在 AvailablePorts.md每个条目包含端口目录名、软件全名、版本号与上游官网。以其中几项为例端口目录软件名版本bashGNU Bash5.3ncursesncurses6.6doomDOOM613f870gnGN Meta Build System2023.07.12gitGit2.55.0该清单涵盖了从核心工具链gcc、binutils、cmake、ninja、编程语言运行时python3、ruby、php、zig、图形库SDL2全家桶、cairo、pango到经典游戏doom、quake、nethack、openttd的广泛生态。2.2 外部端口SERENITY_PORT_DIRS官方端口以外的第三方移植例如你自己维护的本地端口仓库可能依赖来自其他位置的额外软件包。此时可以用环境变量SERENITY_PORT_DIRS指向一个本地端口目录支持多个路径用冒号分隔export SERENITY_PORT_DIRS/path/to/port/dir/:/other/path/从源码实现看该变量在 .port_include.sh 的installdepends函数中被消费当某个依赖在官方 Ports 目录中找不到时脚本会按冒号拆分SERENITY_PORT_DIRS逐个目录查找名为依赖名的子目录并执行其./package.sh --auto全部找不到则报错Dependency $depend could not be found.并退出。这是外部端口机制的核心工作方式。三、使用端口脚本安装与批量构建每个端口目录中的package.sh是唯一入口。它通过 shebang#!/usr/bin/env -S bash ../.port_include.sh被框架接管——脚本会先 source 端口自身的变量声明再转入框架的分发逻辑。3.1 基本用法安装单个端口进入该端口目录并直接运行./package.sh安装全部端口在 Ports 根目录运行./build_all.sh若想先清理旧构建产物把clean作为第一个参数传入重装所有已安装端口运行 build_installed.sh。当 SerenityOS 的 LibC 等基础库发生变更时通常需要这样做来让所有已装软件跟随重编译同样可传clean先清理旧文件。不带任何参数运行./package.sh等价于依次执行installdepends、fetch、patch、configure、build、install这也是常规安装推荐的方式。对应源码中parse_arguments为空参数时调用do_all的逻辑见 .port_include.sh。3.2 安装数据库 installed.db已安装端口被记录在Build/架构/Root/usr/Ports/installed.db——一个纯文本数据库。以ncurses为例其内容形如manual ncurses 6.6 auto readline 8.3 dependency ncurses readline手动安装的端口标记为manual作为依赖自动安装的标记为auto对应--auto选项带依赖的端口会额外写入一行dependency port dep...。这个文件可以随时删除而且在清空构建目录如执行./package.sh clean或清理Build/时必须编辑或删除它否则端口依赖可能不会按预期重新安装。从源码看addtodb在写入前会先通过package_install_state检查该端口是否已记录已存在则跳过installdepends也会据此跳过已安装的依赖。3.3 各子命令详解package.sh支持以下子命令均可直接作为第一个参数传入fetch默认行为下载、校验并解压端口在files变量中声明的所有文件。框架会区分简单下载URL#SHA256与 Git 仓库gitURL#REVISION两种格式分别处理简单下载用curl --fail -L拉取后用sha256sum比对校验和不匹配会删除重试一次仍失败则报错退出见 .port_include.sh 的fetch_simple。patch应用端口patches/*.patch目录下的全部补丁。补丁应用成功后会在workdir中创建.xxx.patch_applied标记文件确保同一补丁只应用一次若workdir本身是 Git 仓库如 Git 格式的 files则改用git am应用并在全部应用后打上patchedtagpatch_internal函数。configure默认运行端口的configscript通常即configure脚本并传入configopts。注意--host${SERENITY_ARCH}-serenity总是会被附加在有SERENITY_SOURCE_DIR的托管构建环境下如果这不符合需求需要覆盖configure函数。若useconfigure为false则跳过该步骤并打印提示。build默认在workdir中执行make并附加makeopts默认-j$(nproc)即并行编译。执行前会先通过ensure_build做健全性检查——验证${DESTDIR}/usr/lib/libc.so是否存在不存在则提示Serenity 尚未构建/安装或安装位置异常并退出。install默认执行make install并附加installopts同时总是传递DESTDIR${SERENITY_INSTALL_ROOT}即Build/${SERENITY_ARCH}/Root。安装完成后框架还会依次执行install_main_launcher与install_main_icon若端口声明了launcher_name、launcher_category、launcher_command会在${DESTDIR}/res/apps/下生成.af应用配置启动器配置 中 DOOM 端口即声明了launcher_categoryGames若同时提供了icon_file则用 ImageMagick 提取 16x16 与 32x32 图标并通过objcopy --add-section serenity_icon_s/serenity_icon_m写入可执行文件。shell先完成依赖安装、fetch、patch然后带着构建环境进入workdir打开一个交互式 Bash shell方便手动调试编译问题。installdepends按depends列表安装所有依赖端口含通过SERENITY_PORT_DIRS查找外部端口未安装的依赖以--auto方式安装。clean默认删除workdir中所有.out等构建产物实际实现是清空PORT_BUILD_DIR。clean_dist删除所有根据files列表下载下来的归档或 Git 裸仓库副本。clean_allclean与clean_dist的组合。uninstall若端口带plist文件记录安装文件清单则据此从 Serenity 构建目录中删除对应文件并从installed.db移除manual记录。注意只有标记为manual的端口才能卸载且必须存在plist文件否则报错见 .port_include.sh 的uninstall函数。dev开发模式用于引导式地修改端口补丁进入一个以本地另一个Git 仓库为干净基线的 Git 工作副本中该基线即打好补丁、可直接构建的版本这个远程仓库可以被 push、pull与普通 Git 远程无异退出 dev shell 后所有补丁会被自动更新git format-patch重新生成并提示是否重新生成补丁 ReadMedo_generate_patch_readme该模式还支持通过半自动化的引导流程帮助把旧补丁迁移到新版本额外支持--no-depends选项跳过依赖的拉取与构建注意dev 模式下对workdir的一切改动都是临时的退出时会被git clean还原脚本注册了 EXIT trap。--auto与不带参数等价执行完整流水线但会把端口标记为自动安装用于依赖安装场景。此外框架还支持showproperty打印变量值、generate_patch_readme、interactive等隐藏子命令。批量脚本的额外参数build_all.sh 除clean外还接受verbose输出完整日志而非重定向到 /dev/null与failfast任一端口失败立即退出该脚本会智能跳过已处理过的端口及其依赖避免重复构建。四、编写端口脚本package.sh 的变量体系package.sh是一个简单的 Bash 脚本通过 shebang 被 .port_include.sh 框架 source 执行。补丁及其他文件是可选的。一个最小端口脚本长这样#!/usr/bin/env -S bash ../.port_include.sh portfoo version1.2.3 useconfiguretrue files( https://example.com/foo-${version}.tar.gz#9acd50f9a2af37e471f761c3fe7b8dea5617e51dac802fe6c177b74abf0abb5a ) depends( bar baz )框架会先加载该脚本. $因此这些变量在框架函数中全部可见。以下变量具有特殊功能port端口的包名通常与脚本所在目录同名也是构建目录Build/arch/Ports/port与数据库记录的关键字。version端口版本号会写入installed.db并通常通过变量插值参与files中的文件名如bash-${version}.tar.gz。useconfigure布尔开关默认false。为true时configure步骤会依次运行pre_configure、configure、post_configure为false时直接跳过并打印该端口不使用 configure 脚本。参考 Ports/doom/package.sh——DOOM 端口未设置该变量仅通过makeopts/installopts指定--directorydoomgeneric/ --fileMakefile.sdl直接 make。configscriptconfigure步骤默认执行的脚本名默认值为configure。框架在调用前会chmod x确保可执行。configopts传给configscript的选项数组。如前所述--host${SERENITY_ARCH}-serenity总会附加。以 Ports/bash/package.sh 为例configopts(--disable-nls --without-bash-malloc CFLAGS-stdc17 CFLAGS_FOR_BUILD-stdc17)makeopts传给make的选项默认-j$(nproc)。DOOM 端口用它在默认build函数中切换 Makefile 与子目录makeopts( --directorydoomgeneric/ --fileMakefile.sdl )installopts传给make install的选项DESTDIR${SERENITY_INSTALL_ROOT}总会附加。depends其他 SerenityOS 端口的依赖数组在installdepends步骤安装depends( gettext ncurses )例如 Bash 端口声明依赖readlinePorts/bash/package.shDOOM 端口依赖SDL2与SDL2_mixerPorts/doom/package.sh。files端口所需外部文件数组支持两种格式简单下载URL#HASH其中URL是使用curl下载的地址HASH是用于校验的 SHA256 哈希files( https://example.com/foo-${version}.tar.xz#9acd50f9a2af37e471f761c3fe7b8dea5617e51dac802fe6c177b74abf0abb5a )若文件是压缩 tar 归档.tar.gz/.tar.bz2/.tar.xz/.tar.lz/.tar.zst/.tgz等、gzip 文件或 zip 文件会被自动解压.exe/.htm及其他未知类型则直接复制进工作目录。Git 仓库gitURL#REVISIONREVISION可以是任何git fetch接受的修订标识commit hash、tag、分支files( githttps://gn.googlesource.com/gn#fae280eabe5d31accc53100137459ece19a7a295 )对应 Ports/gn/package.sh 中 GN 端口的真实写法。实现上框架会先建立裸仓库作为备份git init --baregit fetch --tags再用git worktree add检出到构建目录并处理子模块submodule update --init --recursive若目标修订变化会执行clean -ffdx、reset --hard并重新 checkout。workdir用于执行命令与清理的工作目录通常是解压后源码目录名默认值为$port-$version。DOOM 端口因上游 tar 包内目录名特殊显式设置了workdirdoomgeneric-${version}。prefix端口目录的位置目前仅用于package.db文件默认是$SERENITY_SOURCE_DIR/Ports。官方贡献的端口不应覆盖它。patchlevel传给patch的-p/--strip参数值参见man patch默认1。icon_file用于端口启动器图标的文件假定同时包含 16x16 与 32x32 两个图层。Bash 端口中以#icon_fileFIXME注释形式预留。use_fresh_config_sub布尔开关默认false。为true时会在打补丁阶段用上游最新的config.sub替换config_sub_paths指向的文件。SerenityOS 是全新的平台三元组老版本config.sub不认识${arch}-pc-serenity因此许多 autoconf 项目都需要这个开关。参考 Ports/bash/package.shBash 同时开启了use_fresh_config_subtrue与use_fresh_config_guesstrue并把路径指向support/config.sub与support/config.guessBash 源码内的非标准位置。config_sub_pathsautoconf 使用的config.sub文件路径相对$workdir默认(config.sub)。对应的还有config_guess_paths与use_fresh_config_guess。框架的get_new_config_sub会先检查当前config.sub是否已包含serenity没有则从上游 config.git 下载新版替换.port_include.sh。其他可选变量launcher_name/launcher_category/launcher_command/launcher_workdir/launcher_run_in_terminal用于生成桌面启动器.af文件。DOOM 端口示例launcher_nameDoom、launcher_categoryGames、launcher_command/usr/local/bin/doom。若launcher_command含空格框架会自动在/usr/local/libexec/下生成一个包装脚本。五、覆盖函数与工具函数安装过程的各个步骤被拆分为独立的 Bash 函数端口脚本可以像这样覆盖默认实现build() { run mybuildtool --foo --bar }5.1 可覆盖的函数函数名与前面介绍的子命令一一对应含义不言自明pre_fetch/post_fetchpre_patchpre_configure/configure/post_configurebuildpre_install/install/post_installclean/clean_dist/clean_all框架通过func_defined检查端口脚本是否定义了这些函数未定义则回退到默认实现.port_include.sh 中大量func_defined xxx || xxx() { ... }即是此机制。这意味着端口作者可以只写自己需要定制的步骤。真实案例Ports/gn/package.sh 同时覆盖了configurerun python3 build/gen.py --platformserenity --allow-warnings、buildrun ninja -C out与install复制out/gn与out/gn_unittests到/usr/local/binPorts/ncurses/package.sh 覆盖了pre_configure导出CPPFLAGS-P、install检测宿主机tic 6.1以便生成 terminfo 数据并把TIC_PATH传给 make和post_install为宽字符库与非宽字符库建立大量兼容符号链接。5.2 内置工具函数以下工具函数不可覆盖供端口脚本在自定义函数中调用run记录命令日志后在端口的workdir中执行echo $后(cd $workdir $)。run_nocd记录命令日志后在当前工作目录即Ports/$port执行。run_replace_in_file用 Perl 正则表达式替换文件内容run_replace_in_file s/define FOO 1/undef FOO/ config.h真实案例见 Ports/bash/package.sh 的build()先用run_replace_in_file关闭 Bash 的GETCWD_BROKEN与CAN_REDEFINE_GETENV宏再执行make。5.3 默认流水线的源码级视角do_all对应的完整流水线在框架中由多个do_*函数串联.port_include.shdo_installdepends打印Installing dependencies of $port...并安装依赖do_fetchbuildstep fetch——每个步骤的输出都会被sed加上[port/步骤名]前缀并着色失败时以红色高亮Error in step port/步骤名 (statusN)同时保留退出码让批量脚本判断成败do_patchpre_patchpatch_internaldo_configure先ensure_build检查 libc.so再按需刷新config.sub/config.guess最后pre_configureconfigurepost_configuredo_buildensure_buildbuilddo_installpre_installinstallinstall_main_launcherinstall_main_iconpost_install最后addtodb写入installed.db。这个分层设计保证了单端口安装与批量构建build_all.sh 逐个目录执行./package.sh共享同一套逻辑。六、补丁机制与补丁 ReadMe适配 SerenityOS 的核心手段是补丁。以 Ports/ncurses/patches 为例0001-Teach-configure-about-serenity.patch教会 autoconf 的 configure 脚本识别 serenity 平台0002-Disable-mixed-case-directory-names-when-building-on-.patch在 macOS 上构建时禁用大小写混合的 terminfo 目录名因为 macOS 文件系统大小写不敏感tic只会生成十六进制目录名而 configure 默认仍开启混合大小写特性导致查找x/xterm这类条目失败。每个端口目录下的 patches/ReadMe.md 汇总了补丁的主题与背景说明——它由generate_patch_readme子命令自动生成从补丁的 git 提交信息中提取 Subject 与正文跳过Co-Authored-By行也可以手动更新。补丁应用规则来自patch_internal只在workdir尚未打过某补丁时应用通过.补丁名_applied标记文件判断普通 tar 源码用patch -p$patchlevel应用Git 检出源则用git am --keep-cr --keep-non-patch应用完成后为 Git 工作树打上patchedtag作为 dev 模式比较补丁变化的基线。七、如何贡献新端口为 SerenityOS 贡献端口有两种途径新增端口把软件成功编译通过补齐必要的补丁与package.sh脚本参考上文变量与函数体系尽量复用框架默认实现更新既有端口升级版本、让之前不可用的功能可用等同时务必同步更新对应的补丁。一个实用的工作流是使用dev模式进入被 Git 管理的端口工作副本直接修改源码、跑./package.sh build验证退出后框架会用git format-patch自动把改动导出为patches/*.patch并提示生成 ReadMe。对于大版本升级这种补丁大面积失效的场景dev模式的半自动引导流程也能显著降低迁移成本。若依赖来自第三方记得把本地端口目录通过SERENITY_PORT_DIRS暴露给框架。八、小结从使用到移植的完整路径SerenityOS 的端口系统是一套约定优于配置的 Bash 框架package.sh声明元数据.port_include.sh 提供流水线与默认行为installed.db跟踪安装状态patches/承载平台适配。日常使用只需三步——构建好 SerenityOS、cd进端口目录、运行./package.sh想要贡献新软件则按 AvailablePorts.md 的清单约定补充脚本与补丁即可。深入阅读 .port_include.sh 源码还能发现诸如 ccache 加速、SERENITY_STRIPPED_ENV环境剥离、showproperty调试等更多细节是理解整套构建体系的最佳入口。【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表