做GIS数据处理的朋友,多半都撞见过E00这个老格式。E00是ESRI Arc/Info早期用来做数据交换的文本格式,虽然如今Shapefile、FileGDB满地都是,但在国土、测绘、地矿这类历史数据堆积如山的行业里,E00文件依然一抓一大把。而e00compr就是专门对付这种E00格式压缩和解压的小工具,版本号里的1.0.1-6,前半截是上游版本,后半截的6是打包发布号。最近我接了个任务,要把e00compr-1.0.1-6在KeyarchOS(浪潮信息的企业级服务器操作系统)上跑通并做成RPM包交付,整个过程踩了不少坑,也理顺了不少思路。这篇就把适配过程完整拆开讲,给后面接手国产操作系统软件适配、老GIS工具移植的兄弟们做个参考。
1. 这次适配到底在解决什么问题
1.1 E00格式是什么,为什么还有人用它
E00是Arc/Info Workstation时代的标准交换格式,本质上是ASCII文本,文件开头通常是一串“EXP”标记。一个完整的地理数据图层——点、线、面、注记、属性表——都会被打包成一个或一组E00文件。因为它是纯文本,所以结构很直观,但也因此体积大、解析慢,一个几十MB的矢量数据导出成E00后,膨胀到两三百MB都很正常。
当年为了在软盘和早期网络上传递这种数据,社区里就出现了e00compr这样的工具。它做的事情很纯粹:把E00文本压缩成体积小得多的二进制存储,需要时再反向解压回E00文本。压缩效果很可观,常见的E00数据压缩后往往能缩到原来的十分之一左右。虽然今天大家习惯用gzip、zip,或者直接转成GeoJSON,但老系统、老项目沉淀下来的存量数据,有不少就是以压缩E00形式存在原始介质里的。
这类工具冷门,但并没死。只要还有人维护旧系统、迁移老库、解析历史测绘档案,e00compr这类工具就依然有存在价值。
1.2 谁会在KeyarchOS上用e00compr
KeyarchOS是浪潮信息面向数据中心和企业级客户推出的Linux服务器操作系统,主打自主可控和生态兼容,常见于政企机房、政务云、国资单位。这些年国产操作系统替代需求特别多,很多单位原本跑在CentOS 7、Ubuntu上的GIS相关应用,都被要求逐步迁移到KeyarchOS这类国产平台上。
但迁移会遇到一个很现实的问题:系统换了,原来那些“小工具”不一定有现成的包。尤其是e00compr这种年代久远、作者早已不维护的老软件,基本不可能进入KeyarchOS自带的软件源。你只能自己想办法:要么找源码手编,要么找SRPM重新打包。而运维侧又希望所有软件能被统一管理、可审计、可回滚,这种情况下,把e00compr做成规范的RPM包,而不是靠make install散装安装,就成了更专业的交付方式。
所以这篇博文适合的读者,主要就是两类人:一类是政企环境里的运维工程师和系统管理员,需要在国产OS上快速打包交付存量工具;另一类是GIS开发或数据处理工程师,需要在新平台跑通老格式的数据处理链路。
2. 动手之前先把地基打好:架构、系统与依赖分析
2.1 确认KeyarchOS系统基线与内核环境
拿到任务,我建议不要急着下载源码开编。先花五分钟搞清楚目标环境,能省下后面半天折腾的时间。
第一步是确认操作系统版本和CPU架构。登录目标机器或拉一个同版本容器,执行以下命令:
cat /etc/os-release uname -m cat /proc/version这里要看的核心信息有这么几点:
- 系统大版本:决定你该用何种包管理器命令(dnf还是yum),也决定了RPM包内嵌的dist标签(比如el8、el9、openeuler等)。
- CPU架构:x86_64和aarch64是当前KeyarchOS最常见的两种架构。编译工具链和RPM包架构都会跟着这个走。
- 内核与glibc版本:对于e00compr这种纯C语言老工具,glibc的影响相对小,但如果遇到老代码里的某些系统调用或头文件差异,内核版本和glibc版本就是排查时的重要参照。
我有一次在一台aarch64的机器上直接拿x86_64的RPM包去装,结果当然是架构不匹配,rpm安装直接报“wrong architecture”。这属于低级错误,但确实容易出现,尤其是当你手里既有x86机器又有ARM机器的时候。
2.2 拆解e00compr-1.0.1-6的依赖关系
e00compr虽然是老工具,但它对系统库是有依赖的。如果手上已经有别人打好的旧RPM包,可以直接用rpm命令查看依赖:
rpm -qpR e00compr-1.0.1-6.x86_64.rpm通常你会看到类似这样的输出:
libc.so.6()(64bit) libz.so.1()(64bit) rtld(GNU_HASH)这说明它运行时依赖系统的glibc和zlib。如果你是从源码开始构建,那么编译期还需要对应的头文件和开发库,也就是:
dnf install -y gcc make glibc-devel zlib-devel这里的逻辑其实很简单:运行期需要的是libz.so.1,编译期则需要zlib.h和libz.so(用于链接)。很多新人会漏掉zlib-devel,结果在编源码时报出“zlib.h: No such file or directory”,一脸懵。
顺便解释一下RPM包概念里的“BuildRequires”和“Requires”区别。BuildRequires是指构建这个包时需要的组件,只影响编译过程;Requires是这个包安装后运行时需要的依赖,会影响安装时的自动依赖检查。对于e00compr这种简单程序,两者都不复杂,但分离清楚是写好SPEC文件的前提。
2.3 为什么我舍弃源码安装、坚持改造RPM包
可能有人会说,这个工具这么小,非得打包吗?我make install或者把编译好的二进制直接拷到/usr/local/bin不就行了?
单机自己用确实可以。但一旦面对的是多台服务器、多个环境,或者要给客户交付,源码安装的弊端就很明显了:
- 文件散落四处,卸载时很难清干净,容易残留二进制或库文件。
- 不同机器编译参数不一致,二进制行为可能有细微差别。
- 无法自动处理依赖关系,装完了才发现缺libz,又得手动补。
- 不可审计,不符合政企环境常见的软件资产管理和合规要求。
而做成RPM包,好处是实打实的:统一的安装路径、自动依赖解析、支持升级回滚和卸载清理,还能直接塞进内部的yum源/仓库里批量分发。所以即使e00compr代码量很小,把它工程化地打包,依然是更稳妥、更专业的做法。
3. 核心实操:从源码编译到RPM包生成
3.1 准备工具链、目录结构与源码
打包前,先把构建环境准备好。以KeyarchOS为例,建议使用dnf安装以下工具:
dnf install -y gcc make rpm-build rpmdevtools zlib-devel rpmlint其中rpmdevtools会带出rpmdev-setuptree命令,一条命令创建标准的RPM构建目录:
rpmdev-setuptree执行后你的用户目录下会生成这样的结构:
~/rpmbuild/ ├── BUILD ├── RPMS ├── SOURCES ├── SPECS └── SRPMS这里分别用于存放解压后的源码、构建出的二进制RPM、原始源码压缩包、SPEC文件以及源码RPM。
接下来获取e00compr源码。常见的做法是从软件官网、GitHub归档或第三方源码站下载对应版本的tar.gz压缩包,假设文件名为e00compr-1.0.1.tar.gz。把它放到~/rpmbuild/SOURCES/目录下。
在解压之前,我建议先看一眼包内的README和Makefile,确认主程序叫什么、用什么方式编译。老软件经常不按常理出牌,有些用autotools的一套configure脚本,有些就一个Makefile。提前摸清结构,后面会顺很多。
3.2 手工编译e00compr并修复常见编译错误
虽然最终目标是打包,但我还是强烈建议先手工编一遍。原因很简单:直接进rpmbuild阶段,一旦编译出错,日志会被层层包装,排查起来反而费劲。手工编译能让问题第一时间暴露在眼前。
在源码目录下,如果是自带Makefile的,直接执行:
make如果遇到cc: command not found,说明系统虽然装了gcc,但没有cc符号链接,或者压根没装gcc。解决办法是统一用:
make CC=gcc如果Makefile写死了别的变量,可以先make clean再重新编译,避免旧对象文件干扰。
编译过程中最常见的错误是缺头文件。比如:
zlib.h: No such file or directory这个很好解决:
dnf install -y zlib-devel还有一个典型错误是链接阶段报“undefined reference tocompress2'”之类的符号缺失。这多半是Makefile里的LIBS或LDFLAGS没有加-lz`,解决办法是在Makefile中找到LIBS变量,追加:
LIBS = -lz改完再重新make。如果遇到比较老的源码用到了匿名结构体、旧式函数声明等C语言写法,在高版本GCC下可能报warning甚至error,这种情况就需要适度修改源码。不过e00compr的代码量不大,结构也比较简单,基本只要依赖齐了,手工编译这一步都能顺利通过。
手工编译通过后,别忘了执行一下生成的二进制,确认基础功能正常,比如:
./e00con -h3.3 编写SPEC文件:几个关键字段不能错
手工编译通过只是第一步,真正的重头戏是写SPEC文件。SPEC文件就是RPM包的“菜谱”,rpmbuild会按照它的指令来构建、安装、组织文件。
下面是一个适配e00compr的SPEC示例,字段可以根据实际环境微调:
Name: e00compr Version: 1.0.1 Release: 6%{?dist} Summary: Compress and expand ESRI E00 files License: MIT URL: http://example.com/e00compr Source0: %{name}-%{version}.tar.gz BuildRequires: gcc, make, glibc-devel, zlib-devel Requires: glibc, zlib %description e00compr is a small utility set for compressing and restoring ESRI Arc/Info E00 interchange files. %prep %setup -q %build make CC=gcc # 如果Makefile里LIBS没有-lz,可以在这里做sed补丁 # sed -i 's/^LIBS.*/LIBS = -lz/' Makefile %install install -D -m 0755 e00con %{buildroot}%{_bindir}/e00con # 如果还有man文档,一并安装 # install -D -m 0644 e00con.1 %{buildroot}%{_mandir}/man1/e00con.1 %files %{_bindir}/e00con # %{_mandir}/man1/e00con.1* %changelog * Fri Jan 01 2024 Your Name <you@example.com> - 1.0.1-6 - Rebuild for KeyarchOS几个关键点我单独拎出来说:
Name、Version、Release三个字段共同决定RPM包的完整版本号,最终的包名就是e00compr-1.0.1-6.el9.x86_64.rpm这种格式。其中%{?dist}会自动带上发行版标识。%prep段落里的%setup -q会自动解压Source0并进入源码目录,不用你手动去解压。%build段是编译命令,我在这里用make CC=gcc来规避cc符号缺失的问题。如果Makefile还需要别的调整,可以用sed命令在这里做补丁式修改,这比直接改源码工程更规范。%install段是“安装”到临时目录,重要的事情说三遍:这里不是装到系统的真实根目录,而是装到%{buildroot},rpmbuild最终会把这个临时目录里的内容打包。所以路径必须写对,否则会导致RPM包里文件丢失。%files段必须列出所有要被打进RPM的文件和目录,如果漏了某个文件,rpmbuild会在最后阶段报错:“Installed (but unpackaged) file(s) found”。
另外%changelog不是必有的,但对于企业级交付,我建议写上,一来方便回溯这次构建的目的,二来出现问题时能确认是哪一次构建产生的包。
3.4 rpmbuild构建与产物核查
SPEC文件写好后,执行构建命令:
rpmbuild -ba ~/rpmbuild/SPECS/e00compr.spec-ba表示同时构建二进制RPM和源码RPM(SRPM)。执行完成后,你会得到两个产物:
~/rpmbuild/RPMS/x86_64/e00compr-1.0.1-6.el9.x86_64.rpm ~/rpmbuild/SRPMS/e00compr-1.0.1-6.el9.src.rpm其中SRPM是这次构建的“完整快照”,包含源码、SPEC文件和配置修改,是审计和复现的重要凭证。企业交付时,SRPM的重要性不亚于二进制RPM。
紧接着做两件事核查:
rpmlint ~/rpmbuild/RPMS/x86_64/e00compr-1.0.1-6.el9.x86_64.rpm rpm -qpl ~/rpmbuild/RPMS/x86_64/e00compr-1.0.1-6.el9.x86_64.rpm第一条命令用来检查RPM包的规范性问题,比如文件权限错误、缺少依赖、描述过于简单等。第二条命令用来查看包里到底装了哪些文件。这两步都通过,包才算基本合格。
rpmlint输出中如果有warning,问题不大,但要确认不是关键错误;如果有error,那必须回到SPEC文件里修改。
4. 安装部署与真实数据验证
4.1 在干净环境安装RPM包并验证依赖
打包成功后,我强烈建议不要直接在生产环境装,先找一台干净的KeyarchOS环境(或容器)验证安装。干净环境能暴露出依赖声明是否完整:如果SPEC里漏写了某个运行时依赖,在机器上就会直接报依赖缺失。
安装命令:
dnf install ./e00compr-1.0.1-6.el9.x86_64.rpm这里用dnf而不是rpm -ivh,是为了让dnf自动去仓库里补充依赖项。如果一切顺利,fnf会先把zlib、glibc等依赖装好,再安装e00compr。
装完立刻验证命令是否在PATH里可用:
which e00con e00con -h4.2 用真实E00文件测试压缩/解压流程
这一步是适配工作里最有价值的环节,能真实检验程序行为是否符合预期。我通常会准备一个小型E00文件,比如一个包含几十个多边形要素的test.e00,然后执行压缩和解压的往返测试。
假设主程序命令是e00con,典型的压缩解压操作是这样的:
# 压缩 e00con -c test.e00 test.e00.e00c # 解压还原 e00con -e test.e00.e00c test_recovered.e00压缩完成后,可以对比压缩文件的体积,确认压缩率正常。解压还原以后,再用md5sum对比原文件和还原文件的内容:
md5sum test.e00 test_recovered.e00如果两者哈希值一致,说明往返无损;如果不一致,就要检查是不是文件头、换行符、编码等细节处理不当。
这里有一个缩略版验证流程,适合写进交付文档里:
![缩略版验证思路]
不过这里不画图,用文字描述一遍:压缩前记录原始文件hash,压缩文件记录一下体积和解压后的hash,三个值对得上,然后记录压缩和解压耗时。
我在实测中发现,E00文件由于是ASCII文本,压缩下来的体积降幅非常明显,但耗时通常也很短,因为整个文件都是顺序读写和压缩算法的组合。真正需要警惕的反而是大文件的异常中断,或者源文件本身就不是纯文本E00而混入了二进制干扰。
4.3 跨架构、跨系统的兼容性抽查
e00compr是C语言编写的老工具,源代码层面的可移植性其实不错。但既然KeyarchOS可能跑在x86_64和aarch64两种常见架构上,我还是建议打包时两个架构都各编一次、各测一次。
我实测下来,x86_64和aarch64都是小端序体系,只要代码里没有写出依赖CPU字长假设的逻辑(比如把int直接写进结构体并落盘),压缩出来的二进制格式是一致的。压缩解压往返测试在两种架构上的结果也一致。
另外一个抽查点是不同系统版本上的兼容性。比如有的KeyarchOS基于openEuler演进,有的更接近CentOS系,这两者的动态库版本号可能有细微差别。如果你在spec里写死了某个绝对路径或特定库版本,可能在另一个衍生版上就装不起来。这也是我为什么建议尽量让RPM只依赖系统最基础的libc和zlib,因为这两个库在所有KeyarchOS版本上都有,且基本保证向后兼容。
5. 避坑手册:适配与打包中的高频问题
5.1 编译期与链接期的典型错误
我把这段时间踩过的编译和链接问题整理成一个速查表,方便快速对照:
| 错误信息 | 可能原因 | 解决办法 |
|---|---|---|
cc: command not found | 系统没有安装GCC套件,或没有创建cc符号链接 | dnf install -y gcc,或者在make时指定CC=gcc |
zlib.h: No such file or directory | 缺少zlib开发包 | dnf install -y zlib-devel |
undefined reference to 'compress2' | 链接时忘记加-lz | 修改Makefile,在LIBS或LDFLAGS中加入-lz |
make: *** No rule to make target | 源码包下载不完整或目录名与SPEC不匹配 | 确认tar.gz已放入SOURCES目录且版本号在SPEC中一致 |
Installed (but unpackaged) file(s) found | %install安装到了buildroot,但%files段漏声明 | 在%files段补充所有需要发布路径 |
这些坑看似基础,但在一遍遍排查时非常消磨时间。我的经验是,遇到报错第一时间先看完整日志,不要只看最后几行;很多错误上下文中已经明确指出了缺什么、在哪个文件第几行。
5.2 运行期动态库与数据一致性问题
RPM装完之后,程序能不能立刻跑到,取决于动态库依赖是否满足。如果你执行:
ldd /usr/bin/e00con输出里有not found字样,那就说明某个共享库缺失。大多数情况下是zlib或libc版本不完整。解决思路是:
dnf install -y zlib ldconfig不过对于KeyarchOS这类系统,zlib和glibc几乎是系统基础组件,很少会出现缺库情况。更常见的问题反而是数据一致性:如果你拿着这个包去处理从Windows上拷贝出来的E00文件,文件末尾的换行符可能是CRLF风格,压缩还原后也可能保持原样;而有些老工具在解析时会对换行符做规整化处理,导致还原后的文件与原文件哈希不一致。
我的建议是:交付给客户前,一定要用一套具有代表性的真实数据做往返测试(压缩再解压),然后对原始文件和还原文件做hash比对或字节级diff,确认数据无损。如果客户对hash一致性有硬性要求,那就必须在项目文档里明确说明该工具的数据处理行为。
5.3 打包与交付的验收要点
到了交付环节,我一般会按下面这份清单逐项打钩:
- [ ] 二进制RPM包能在最小化安装的KeyarchOS上通过dnf安装成功;
- [ ] 安装后命令可通过
which找到,帮助信息可正常输出; - [ ] 用标准E00样例文件完成压缩、解压往返测试,结果一致;
- [ ] 在x86_64和aarch64架构上各完成一次构建和安装测试;
- [ ] rpmlint无error级别问题,warning已逐项评估;
- [ ] SRPM包已留存,源码文件与SPEC可完整复现构建过程;
- [ ] 交付文档中写明包名、版本、依赖、验证结果与已知限制。
这份清单本质上就是“适配工作做完整了”的证明。很多适配任务之所以交付后还被挑毛病,不是因为程序功能不行,而是过程文件、验证记录不完整。
6. 写在最后的一点体会
把e00compr-1.0.1-6在KeyarchOS上适配完并打包成RPM,整个过程梳理下来,我觉得最花时间的其实不是敲代码,而是想把每一步都做得可复现、可验证。这个老工具本身很小,但做适配工作的框架是可以复用的:确认环境基线、拆解依赖、手工编译定位问题、编写SPEC、构建RPM、干净环境安装验证、真实数据做往返测试,每一步都不能省。
如果你也遇到类似的老工具适配需求,我个人的建议是,从一开始就把SRPM保留好,把验证脚本和结果记录都归档到项目的交付目录里。这样不管是未来系统版本升级,还是客户突然要求复现,你都能很快拿出证据和方案,而不是靠记忆去补历史账。这个习惯,比任何打包技巧都值钱。