news 2026/10/3 14:07:37

老RPM包在KeyarchOS上的适配实战:编译修复与spec重写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
老RPM包在KeyarchOS上的适配实战:编译修复与spec重写

上个月在整理内部服务器清单时,发现一台新入库的KeyarchOS机器上有个任务始终没跑起来——cron里每天凌晨都要执行一次calendar输出当日日程到审计日志,但日志里连续几天都是command not found。查了一圈,根子是包管理器里根本没有calendar,而运维拿来的安装包还是calendar-1.28-1.20140613cvs这种2014年风格的RPM。说白了,这就是一个典型的国产操作系统适配老软件包的问题:二进制包装不上、源码得重新编译、spec要改,还得保证行为不变。

把整个适配过程走完之后,我最大的感受是:这种老包的适配,难度不在代码本身,而在"破案"——顺着版本号、依赖关系和构建日志一步一步往回倒,搞清楚十年前这个包是怎么生产出来的,然后在今天的系统上把这个过程重演一遍。这篇文章就把我这次在KeyarchOS上适配calendar-1.28-1.20140613cvs的完整链路写出来,包括怎么分析RPM、怎么修编译错误、怎么重写spec、怎么验证功能。如果你也在国内Linux发行版上碰到类似的老RPM包,这篇的排查思路可以直接复用。

1. 为什么要在KeyarchOS上折腾一个十年前的老日历包

1.1 calendar这个工具到底是干嘛的

先说清楚对象。这里的calendar不是图形界面的日历应用,也不是安卓上的日历App,而是一个源自BSD的命令行日程工具。它的工作方式非常朴素:系统在/usr/share/calendar目录下放一批日期定义文件,比如calendar.usholiday、calendar.christian,用户也可以在自己的目录下放一个~/.calendar文件,里面按行写"月份/日期 + 日程描述"。运行calendar命令时,它会读取今天和接下来几天的日程,然后打到标准输出。

这个工具的典型用法是配cron。比如我这边实际部署的场景:

0 7 * * * /usr/bin/calendar -d | mail -s "Today Schedule" ops@internal

每天早上7点把当天日程发到邮箱,服务器端不需要任何图形环境,几十KB的二进制就够了。对自动化运维和内部审计来说,这种极简工具比安装一个完整的日历套件实用得多——没有数据库、没有后台服务、不占端口,输出是纯文本,方便进日志系统。

1.2 谁还会在2025年用到这么老的包

很多人第一反应是"2014年的包还有人用?"。但现实就是,企业内部遗留系统的粘性远比想象中大。我在这次适配前梳理了一下使用场景:

  • 审计留痕:某些内部流程要求每天记录排班、维护窗口等日程,calendar配合cron输出纯文本日志,格式固定,便于grep。
  • 脚本依赖:个别老脚本直接调用了/usr/bin/calendar处理日期判断,比如"判断今天是否为季度末"这类逻辑,依赖calendar的日期库。
  • 迁移合规:原来的CentOS环境跑得好好的,现在服务器换成了KeyarchOS,应用层不能变,否则下游对接方要跟着改。

所以这个适配的目标很明确:不是把代码现代化,而是让一个在旧系统上正常工作、新系统上装不上的RPM包,经过重新构建后能在KeyarchOS上以同样的方式安装、运行、输出。行为必须和旧环境一致。

1.3 适配完成后的交付物是什么

我在接这个任务时给自己列了一个交付清单,不只是"能跑了"这么简单:

  • 一个可在KeyarchOS上安装的RPM包:calendar-1.28-1.20140613cvs.keyarchos.x86_64.rpm
  • 一套可复用的spec文件:以后如果KeyarchOS升级内核或工具链,可以用rpmbuild -ba重新产出包。
  • 一份功能验证记录:证明日历输出、日期计算、cron集成这些核心行为和旧环境一致。

这个清单帮我框定了工作量,也避免了"装上了但没测透"的情况。后面所有步骤都是在为这三项交付服务。

2. 动手之前:把RPM包的"底细"摸透

2.1 从版本号读出关键线索

拿到calendar-1.28-1.20140613cvs这个包名,第一件事不是急着装,而是拆解它的版本号命名规则。这里有个容易忽略的信息量:

  • 1.28是上游版本号,对应calendar工具自身的版本。
  • 1.20140613cvs是RPM的release号,其中20140613是构建日期,cvs表示这份代码是从CVS版本控制系统的某个快照拉出来的。

这种"release里带日期和cvs"的命名风格,在2014年前后的Linux发行版里非常常见,尤其是Fedora边源和EPEL里一些缺乏正式发版节奏的小工具。它暗示了一件事:这个包的构建方式很可能是"从CVS checkout代码 -> 打补丁 -> 用rpmbuild打包"。也就是说,原包不一定是纯tar包,而可能是版本库里某个时间点的快照外加若干补丁。

搞清楚这个命名规则后,我开始判断这个包属于哪个上游家族。语法上"月份/日期"的行格式、默认读取/usr/share/calendar目录下的calendar.*文件、支持-t参数指定日期、-A参数指定天数——这些都是BSD calendar的经典行为,所以它的上游源码基本可以确定是BSD系或者基于BSD移植的版本。这一点在后面的源码编译环节很关键,因为BSD代码对于glibc和GCC标准库的依赖习惯和GNU项目不太一样。

2.2 用rpm命令做一次"尸体解剖"

老规矩,先把RPM包的元数据、文件清单、依赖关系全拆出来看一眼。我习惯用一条命令分别拿三组信息:

rpm -qip calendar-1.28-1.20140613cvs.x86_64.rpm rpm -qlp calendar-1.28-1.20140613cvs.x86_64.rpm rpm -qpR calendar-1.28-1.20140613cvs.x86_64.rpm

执行后关键信息如下:

项目解析结果
Namecalendar
Version1.28
Release1.20140613cvs
Architecturex86_64
依赖/bin/sh,libc.so.6(GLIBC_2.14),rtld(GNU_HASH)
文件列表/usr/bin/calendar、/usr/share/calendar/calendar.usholiday、/usr/share/man/man1/calendar.1.gz等

这里有两个点值得注意。

第一,它对glibc的依赖是GLIBC_2.14,这个要求很古老。理论上2025年的KeyarchOS上glibc版本至少在2.28以上,这种版本符号要求是向下兼容的,所以直接安装时一般不会卡在glibc版本上。

第二,它只依赖/bin/sh,没有任何额外的动态库依赖。这得益于calendar本来就是独立的小工具,编译时只链接了libc。这意味着如果这个二进制能被加载运行,基本不会遇到"缺库"的经典问题。

2.3 实际安装时的报错才是真正的缺口

理论上依赖很干净,那为什么装不上?直接在KeyarchOS上试一次:

rpm -ivh calendar-1.28-1.20140613cvs.x86_64.rpm

报错内容大概是:

error: Failed dependencies: /bin/sh is needed by calendar-1.28-1.20140613cvs.x86_64

看到这个报错我愣了一下。KeyarchOS上/bin/sh明明存在,而且指向的是bash。查了一圈,问题出在/bin在KeyarchOS上的默认文件系统布局:新版系统里/bin是usr/bin的符号链接,而老版RHEL/CentOS的rpm会在事务中单独校验/bin/sh这个文件路径是否存在,符号链接场景下某些rpm版本会判定依赖不满足。

这个坑非常典型。它不是"缺依赖",而是"打包时间太早,用的依赖写法太死"。解决办法不是强行--nodeps装进去——一旦强制装,后续rpm -e、rpm -V校验都会出问题。正确路线是重新构建一个适配KeyarchOS的RPM,在spec里把/bin/sh的依赖改成/bin/sh或直接写成bash,同时让构建环境生成新的依赖标记。

这一步也确认了我的判断:这个包必须走"源码重新构建"路线,而不是"二进制迁移"路线。

3. 依赖断点与源码构建环境准备

3.1 先把构建工具链补齐全

KeyarchOS本身装了gcc、make这些基础工具,但rpmbuild不一定在默认安装里。我这边缺失的是rpm-build包。补齐的命令:

dnf install -y rpm-build dnf groupinstall -y "Development Tools"

补完后验证一下rpmbuild可用:

rpmbuild --showrc | grep -E "topdir|buildarch"

拿到源码包之后,rpmbuild才真正开始起作用。这里提醒一句:如果在内网离线环境做适配,最好提前准备一份rpm-build的离线仓库,或者把构建机和配有网环境的机器放在同一个网络区域,否则临时装工具会非常痛苦。

3.2 源码树里藏着第一个编译断点

找到了配套的SRPM包(calendar-1.28-1.20140613cvs.src.rpm),在KeyarchOS上解包:

rpm -ivh calendar-1.28-1.20140613cvs.src.rpm

解包后源码在~/rpmbuild/SOURCES/calendar-1.28目录下,spec文件在~/rpmbuild/SPECS/calendar.spec。

然后执行普通的构建流程:

rpmbuild -ba ~/rpmbuild/SPECS/calendar.spec

第一次构建不出意外地挂了。报错关键行是:

calendar.c: In function 'getdate': calendar.c:123:15: error: implicit declaration of function 'stpcpy'

这个错误在2025年看到其实一点不意外。老代码写于2014年甚至更早,当时GCC对函数隐式声明只给警告,不给错误。但GCC的默认标准在十几年间升级了好几轮,从-std=gnu89到默认-std=gnu11乃至更高,把隐式声明从警告抬高成了错误。

这里值得展开说一下原理。stpcpy这个函数在POSIX 2008标准里才被正式纳入,旧代码默认认为"调用一个未声明的函数也能编译过"。新版glibc里stpcpy的声明是有条件暴露的,通常需要_GNU_SOURCE或_POSIX_C_SOURCE >= 200809L才会在头文件里出现。老代码没有设置这些宏,于是编译器在string.h里看不到stpcpy的声明,就报隐式声明错误。

修复方案很朴素,在源码里加一个宏定义,或者编译时加-D_GNU_SOURCE。我选择了修改Makefile,在CFLAGS里追加:

CFLAGS += -D_GNU_SOURCE -O2 -g

这一处改完,stpcpy的声明问题消失,但紧接着又冒出来第二个编译断点。

3.3 链接阶段的隐藏断点:bsd兼容函数

第二个报错出在链接阶段:

/tmp/ccXXXX.o: undefined reference to 'strlcpy' collect2: error: ld returned 1 exit status

strlcpy同样是BSD系的常用函数,但glibc直到比较晚的版本才在string.h里暴露声明,而且链接时它属于libbsd的范畴,不像stpcpy那样直接从glibc的库里就能找到符号。

这个断点的本质是:calendar的上游代码默认自己运行在"类BSD"环境里,它直接使用了BSD的字符串函数。而KeyarchOS是标准的glibc/Linux环境,没有默认提供这些函数的实现。

我查了一下系统里有没有libbsd:

dnf search libbsd dnf install -y libbsd-devel

KeyarchOS的仓库里是有libbsd-devel的,装上之后在Makefile里把-lbsd加到LDFLAGS:

LDFLAGS += -lbsd

这个做法在Linux发行版上构建BSD系工具时非常常见:不改源码里的函数调用,只用libbsd提供的兼容实现,保持上游代码尽量少动。

3.4 环境细节:locale和时区对calendar输出的影响

编译一过,就以为万事大吉是大忌。calendar这种工具,输出内容完全依赖日期和本地化规则,而构建机和运行机的locale、timezone配置稍有不同,输出就会跟着变。比如calendar内部判断节假日时,可能会读取/usr/share/calendar下带locale后缀的文件(calendar.zh_CN之类),如果构建时没有安装对应的locale数据,运行时就会静默降级成空白日程。

我这边虽然不需要中文节假日,但为了行为一致,还是在spec的%post脚本里显式设置了软链:

ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

以及在%build阶段强制指定LANG=en_US.UTF-8、LC_TIME=C,保证构建期间生成的man page和默认日历文件不因为locale不同而内容漂移。这种细节如果不在构建时锁死,后面功能回归阶段会浪费大量时间。

4. 重写spec文件与rpmbuild全过程

4.1 先理清原spec的结构

把原spec打开看了一遍,整体框架没问题,毕竟是2014年从官方渠道流出来的包,打包规范是标准的。核心段落有:

  • Source0: calendar-1.28.tar.gz
  • Patch0: calendar-1.28-cvs.patch
  • %build里执行make
  • %install里执行make install DESTDIR=%{buildroot}

我保留了Source0和Patch0,因为这些是上游代码的正确基线。改动点集中在以下三处。

4.2 三个必须改的地方:Release、依赖写法、构建参数

第一处是Release号。为了和原包区分,避免在同一个系统里出现两个release号相同的RPM,我把Release改成了1.20140613cvs.keyarchos%{?dist}。这样产出的包是calendar-1.28-1.20140613cvs.keyarchos.x86_64.rpm,一眼就能看出是适配版本,也符合KeyarchOS对第三方适配包的命名习惯。

第二处是依赖写法。原spec里写死Requires: /bin/sh,在KeyarchOS上引发了本篇第2节说的路径依赖坑。我把这里的依赖改成:

Requires: /bin/sh Requires: bash

虽然KeyarchOS的/bin/sh实际就是指向bash的符号链接,但显式加上bash依赖更稳妥,而且rpm做依赖解析时不会再纠结/bin/sh的路径类型。

第三处是构建参数。把我在3.2节和3.3节验证过的-D_GNU_SOURCE和-lbsd写进spec的%build段,确保后续任何人在这个环境里执行rpmbuild -ba,都能稳定复现成功结果:

make %{?_smp_mflags} CFLAGS="-D_GNU_SOURCE -O2 -g" LDFLAGS="-lbsd"

4.3 完整的rpmbuild执行过程

spec改完后执行:

rpmbuild -ba ~/rpmbuild/SPECS/calendar.spec

这一步会把整个流程重跑一遍:%prep解压源码、打补丁,%build编译,%install安装到临时root,%files列表校验,最后生成二进制RPM和SRPM。

这里有个经验:rpmbuild的输出信息里,如果%files阶段出现"File not found"错误,通常不是文件真的不存在,而是%install阶段的安装路径没对齐。我这次就遇到了/usr/share/man/man1/calendar.1.gz找不到的报错,原因是源码里的Makefile安装man page时用的目录是/usr/man而不是/usr/share/man。修复方式是在spec的%install段加一句:

mkdir -p %{buildroot}%{_mandir}/man1 install -m 0644 calendar.1 %{buildroot}%{_mandir}/man1/calendar.1

这种"源码自带的安装路径旧"的问题,在2014年的包中不少见,处理思路就是不要在%files里硬凑,而是统一在%install阶段把文件放回标准的FHS路径上。

4.4 安装新包并做一次完整性校验

构建完拿到的产物:

~/rpmbuild/RPMS/x86_64/calendar-1.28-1.20140613cvs.keyarchos.x86_64.rpm

安装并校验:

rpm -Uvh calendar-1.28-1.20140613cvs.keyarchos.x86_64.rpm rpm -V calendar

rpm -V输出为空时,说明文件属性和校验和与打包时一致,安装是干净的。接着确认核心文件确实落在预期位置:

ls -l /usr/bin/calendar /usr/share/calendar/calendar.usholiday

到这里,RPM包本身已经能在KeyarchOS上正常安装了。

5. 回归验证与想清楚老日历在现代场景的位置

5.1 功能回归用例:不能只验证"能跑"

一个命令行工具适配完成后,至少要跑一遍核心功能。calendar的价值在于日期计算和日程输出,我设计了下面这组回归用例:

用例命令预期结果
基础输出calendar输出今日及未来若干天的日程
指定日期calendar -t 2025/12/25输出2025年12月25日的日程
扩展天数calendar -A 7输出未来7天的日程
用户自定义在~/.calendar写一行自定义日程后运行用户日程出现在输出中
系统日历数据cat /usr/share/calendar/calendar.usholiday节假日定义文件完整
man page`man calendarhead -5`

这组用例全部通过,基本可以确认适配没有破坏原有行为。特别推荐大家把"用户自定义日程"这个用例放在最后跑,因为只有它真正测到了用户态读写路径,比单纯看系统节假日文件要深一层。

5.2 同一个"日历"关键词下的另一个世界:fossify calendar

搭这套命令行日历的过程中,我顺便留意到现在搜索"calendar"热词时,经常看到fossify calendar这个项目。它是安卓端的一个开源日历应用,强调的是无广告、离线可用、隐私友好,支持事件提醒、农历视图、ICS导入导出这些功能。

我把两者放到一起看,不是为了分高下,而是想说明一个事:同样是"日历",服务端命令行工具和个人移动端应用是两条完全不同的产品线。calendar-1.28-1.20140613cvs解决的是"无人值守环境里如何自动生成、投递日程文本"的问题,它的竞品是cron、邮件和脚本;fossify calendar解决的是"个人如何在手机上管理事件、获取提醒"的问题,它的竞品是Google Calendar和系统自带日历。

理解这个边界很有实际意义。很多人在服务器上调不出图形日历就认为"日历适配没用",其实是要区分使用场景。如果哪天KeyarchOS上需要跑安卓容器、做移动端日历应用的交叉编译,那fossify calendar的开源代码反而是更好的研究对象——它那套用Kotlin写的日程存储、事件提醒模块,可以迁移到桌面端复用。

5.3 这次适配沉淀下来的通用方法

把整个流程回头看一遍,适配老RPM包的关键点可以收敛成六步排查法:

  1. 先拆版本号:1.28-1.20140613cvs里的上游版本、release、日期、版本系统代号都要拆出来,判断它的上游代码来自哪里、有多老。
  2. 再拆二进制包:rpm -qip、rpm -qlp、rpm -qpR三连,把依赖关系、文件清单、架构信息全部拿到手。
  3. 直接安装做验证:用一次真实的rpm -ivh试错,拿到具体的依赖报错,判断是路径问题、glibc问题还是缺包。
  4. 从SRPM重构建:能拿到源码包就优先走源码重打包路径,不要用--nodeps硬装二进制包。
  5. 逐个击破编译错误:隐式声明、缺库、安装路径这类问题,每一个都要搞清楚背后的标准差异,再用补丁或spec参数修复。
  6. 回归验证与打包命名:验证功能完整后,一定要做release重命名、依赖改写、完整性校验,确保成品符合新系统的安装规范和命名预期。

这次适配calendar-1.28-1.20140613cvs,本身不是一个高深项目,但恰恰是这样"不起眼"的老包,最能考验一个工程师对RPM生态、编译工具链演进和系统兼容性的理解深度。我在操作过程中一个比较深的体会是:不要急着改代码,先把"这个包十年前是怎么构建出来的"这个问题回答清楚,后面每一步都能少走弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 14:05:27

流处理性能优化实战:从背压到数据倾斜的端到端调优

1. 流处理系统性能优化到底在优化什么做流处理这件事,最怕的不是任务跑不起来,而是任务跑起来了,你却不知道它还能跑多快。很多团队在大数据平台初建时,用 Flink 或 Spark Streaming 跑几个 demo 都挺顺畅,数据量一上来…

作者头像 李华
网站建设 2026/10/3 14:04:39

openclaw与cline集成实战:从WSL环境部署到协议打通

我是在一次构建失败触发到 openclaw 的任务队列、而 IDE 里的 cline 并没有任何感知的那一刻,才决定把这两个工具真正集成到一起的。在此之前,openclaw 和 cline 在我的机器上完全是两条平行线:一个负责跨任务编排,一个负责在编辑…

作者头像 李华
网站建设 2026/10/3 14:03:38

eBPF内核可观测性实战:从TCP重传到生产排障

去年我们线上发生了一次诡异的高频超时:数据库连接偶发建立失败,丢包率不到0.1%,但每次抖动都精准砸在连接建立那几十毫秒上。我用 netstat 、 ss 、 strace 排查了大半天,数据都有,但没人能告诉我“这个重传到底…

作者头像 李华
网站建设 2026/10/3 14:02:52

Python实现图片贝叶斯分类器:从特征提取到决策边界

简介:本资源是面向模式识别课程学习者与机器学习入门者的Python贝叶斯图像分类完整项目,对应课程大作业场景,帮助读者理解贝叶斯定理在图像分类中的落地方式。项目同时提供控制台与GUI两种交互形式,用户可输入图片路径或通过文件浏…

作者头像 李华
网站建设 2026/10/3 14:02:47

SpringSecurity整合JWT实现前后端分离Token认证完整指南

前后端分离做久了,大家大概率都会遇到同一个问题:登录状态到底怎么维持?Cookie Session 方案不是不能用,但跨域、集群、App 端适配、单点登录扩展,每一个都够你折腾半天。所以我现在做新项目,基本直接上 S…

作者头像 李华
网站建设 2026/10/3 14:00:50

基于Hadoop的协同过滤视频推荐系统:从环境搭建到Top-N生成

简介:面向大数据与推荐系统学习者的基于Hadoop的协同过滤视频推荐系统完整项目包,解决海量视频场景下传统推荐性能瓶颈问题。资源共383个文件、12.1MB,涵盖58个Java源码(系统核心算法与MapReduce实现)、88个JavaScript…

作者头像 李华