ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Windows上制作arm64 deb包:从工具选择到避坑实操

Windows上制作arm64 deb包:从工具选择到避坑实操 1. 先搞清楚在 Windows 上打 arm64 的 deb到底算什么任务1.1 三种不同类型的“打包”需求很多人一看到在 Windows 上打出 arm64 的 deb 包这句话第一反应就是你是不是闲得慌我当时也是这么想的。事情的起因不复杂——要给一台 arm64 架构的 Ubuntu 设备准备离线安装包但手边只有一台 Windows 开发机目标机器又不能联网。更麻烦的是我手里已经有一堆编好的 arm64 二进制文件就是缺一个能把它们装上去的安装包格式。如果你也遇到类似需求我建议你先把任务拆清楚。在 Windows 上跟 deb 打交道实际上有三种完全不同的场景交叉编译场景源码需要用 aarch64 交叉编译工具链编译成 ELF 可执行文件再把编译产物包进 deb。这个场景的重头戏是编译器不是 deb 本身。搬运重打包场景你已经拿到了 arm64 的二进制文件或者第三方 .deb 包只是想改个版本号、加个配置文件、合并几个包或者把散落文件重新整理成一个新 deb。这个场景的重头戏才是打包本身。修改已有 deb从现成的 deb 包里提取内容改 control 信息改依赖再加回去。这个场景本质上也是重打包。我后来才发现绝大多数在 Windows 上打 deb的需求其实是第二种和第三种。这很关键如果你的目标只是把已有的 arm64 文件装进 deb 外壳那打包过程本身跟架构没有关系你不是在编译你只是在组装归档文件。思路一旦转到组装归档这个方向Windows 上能做的事情就比想象中多得多。1.2 deb 包到底是什么拆开看一眼就明白在绕远路之前先把 deb 的物理结构看明白。deb 包不是一个压缩之后的文件夹它最外层是一个 Unix 下常见的 ar 归档文件。你可以用 7-Zip 打开一个任意 deb 包会看到里头通常有三个成员debian-binary一个纯文本文件内容就是2.0表示 deb 格式版本。control.tar.xz也可能是 .zst 或 .gz放的是包元数据包括 control、md5sums、conffiles、postinst、prerm 这些脚本。data.tar.xz同样可能是 .zst 或 .gz这个才是真正的内容物也就是安装后要释放到系统里的所有文件。control.tar.* 和 data.tar.* 都是 tar 压缩包tar 又是 Unix 世界早就定好的归档格式。所以只要你手头有能读 tar 的工具就能从 Windows 侧把这玩意儿拆开看个底朝天。我当时第一次用 7-Zip 打开一个 deb发现里面居然不是一层套一层的文件夹而是三个平级成员时脑子才转过弯来——deb 说白了就是壳 元数据 文件三件套。理解了这一层后面很多想错的地方就都能解释通了。1.3 为什么会需要这种需求适用场景有哪些连续说几个真实场景你看看自己是不是也在其中内网部署目标设备是 arm64 的飞腾、鲲鹏或者树莓派系统是 Ubuntu、Debian 或麒麟的 ARM 版但生产网隔离了外网所有软件都得做成安装包人工拷进去。给特定设备定制软件包公司自己编译的内部工具只在某几台 ARM 设备上跑不想每个设备手动复制文件、配 systemd 服务想用apt install一样的方式部署。第三方 deb 改包下载到一个 x86 的 deb内容其实是脚本/数据想改成 arm64 可用或者拿到 deb 但里面捆绑了不需要的文件想精简重打。开发机与目标机不一致开发机是 Windows客户现场是 ARM Linux交付物要求是.deb文件。这些场景有一个共同特点你可能根本没有一台现成的 ARM Linux 机器来就地打包。所以在 Windows 上打 deb 不是没事找事而是很多工程交付环节里实打实的刚需。2. 想错的地方一以为开门就要先装 Linux 环境2.1 我一开始的第一步就走错了我最初的计划特别蠢装虚拟机。还想着反正要装 Ubuntu顺便模拟一下 arm64 环境结果虚拟机快照快把磁盘占满了系统还没配完。后来冷静下来想了五分钟发现这个方案重得离谱——我明明只是要把文件塞进一个 tar、再塞进一个 ar跟运行 Linux这件事没有半毛钱关系。我当然知道 WSL 是个好选择。但要说清楚的是用 WSL 只是为了借用dpkg-deb这个工具并不是为了跑整套系统、配环境、装依赖。如果你工作中经常要处理 deb装一个 WSL 里的 Ubuntu 发行版作为打包工具箱就够了平时根本不进图形界面就敲几条命令。这跟装一个完整虚拟机是两个操作量级。2.2 deb 的壳与瓤决定了打包不一定需要 Linux回到 deb 的结构。最外层的 ar 归档是一种极简单的格式全局文件头是!arch\n之后每个文件就是一个 60 字节的文件头 原始内容。tar 格式呢Windows 上能处理 tar 的工具更是一抓一大把7-Zip、WinRAR、libarchive 都能。而 deb 包里的内容物不管是 control 文本还是实际安装文件本质上都是普通文件。你看这件事从头到尾都没有必须调用 Linux 内核的环节。你需要的只是一个能解压/压缩 tar 的工具一个能正确设置 Unix 文件权限、所有者、符号链接的打包工具一个能生成 ar 归档的工具。这三个需求在 Windows 生态里都有解。我之前之所以认为必须先有 Linux纯粹是把deb只能在 Linux 上操作当成了常识。这里也顺便说一句如果你手里只有 Windows没有 WSL不是不能干活只是有些细节比如权限、符号链接、换行符处理起来会特别痛苦。所以我的建议是不必装 VM但 WSL 值得有。2.3 我实际采用的工具组合经过实践我在 Windows 上打 arm64 deb 用到的工具是这套工具用途说明7-Zip拆包/查看 deb、tar读 ar、tar 都方便修改时建议解到临时目录bsdtarlibarchive创建/处理 tar 包能指定 uid/gid、权限、符号链接Windows 版可用WSLUbuntu执行dpkg-deb --build最后一步封包顺便解决换行和权限问题PowerShell计算 md5、整理文件、生成脚本用于自动化批处理file / readelf验证二进制架构Windows 上可以用 LLVM 工具链里的版本我试过纯手工从 Windows 侧把所有内容组装成 ar 归档能做但没必要。因为dpkg-deb这条命令本身就干得很漂亮它会自动生成正确的 control.tar 和 data.tar自动处理 root 所有者自动校验目录结构。老老实实把工作拆成Windows 整理文件 WSL 一次性封包两段效率最高也不容易出现玄学问题。3. 想错的地方二以为打包完成等于安装可用3.1 Architecture 字段决定这台机器认不认第二个我严重低估的地方是Architecture字段。我一开始想反正我打的是 arm64 的包控制文件里写Architecture: arm64不就行了结果当时我差点写成了aarch64。这里要跟新手说明白在 Debian/Ubuntu 的 dpkg 世界里ARM 64 位的架构名通常就叫arm64。aarch64是很多编译工具链叫法比如 GCC 的aarch64-linux-gnu-但它们不是同一个字段值。control 里写错dpkg 在安装时会直接报 wrong architecture。还有一个更隐蔽的误区不要把Architecture当成这是一台什么机器的说明而要当成这个包里的二进制跑在什么指令集上的声明。如果你包里全部是 Python 脚本、HTML 页面、配置文件没有任何编译产物那架构可以写all表示架构无关。但只要里面有一个 ELF 可执行文件或者 .so 动态库就必须老老实实写arm64。这个判断不能靠猜我用file命令检查那个二进制的输出是ELF 64-bit LSB pie executable, ARM aarch64那 control 里就写 arm64。3.2 arm64 不是万金油依赖才是真门槛包架构对了只能保证 dpkg 愿意把文件放进去不能保证软件能跑起来。arm64 机器上的软件能正常运行靠的是目标机器上已经装了对应的动态库这就是Depends字段的职责。我当初给自己打一个小工具控制文件里很自信地只写了Depends: libc6 ( 2.31)结果拿到目标机器上一运行报错说缺少 libstdc。一查我的程序链接了 C 标准库而我根本没在 Depends 里声明。这在 x86 台式机上可能不明显因为开发机往往装了一堆东西但在最小化安装的 ARM 设备上缺个 libstdc 是常有的事。所以检查依赖这件事必须看二进制的真实需求。在 Windows 上可以用 LLVM 的llvm-readelf或者 WSL 里的readelf -d查看DT_NEEDED列表看它到底需要哪些 .so再用目标系统里的apt-cache depends或dpkg -s去核对这些库由哪个包提供。千万别拍脑袋。我见过一个 arm64 的 ROS 相关软件包因为漏声明了python3相关依赖装上去完全起不来最后排查到凌晨。3.3 文件装到哪里比打进没打进更重要第三个想错是文件路径。在 Windows 上整理文件时我特别容易沿用 Windows 的目录习惯把内容放在C:\myapp\bin\...这种思维里。但 deb 安装时data.tar 里路径就是最终系统里的绝对路径比如./usr/bin/myapp、./etc/myapp/config.ini、./lib/systemd/system/myapp.service。路径放错deb 一样能装上但命令找不到、服务起不来等于白装。具体来说我常用的目录安排是这样的可执行文件放usr/bin/或opt/应用名/bin/库文件放usr/lib/应用名/或opt/应用名/lib/systemd 服务文件放lib/systemd/system/配置文件放etc/应用名/日志目录、数据目录用var/lib/应用名/。另外如果包里有etc/下的配置文件记得在DEBIAN/conffiles里把这些文件列出来。这个文件的作用是告诉 dpkg这些配置文件升级时不要无脑覆盖要保留用户的修改。没写 conffiles 的后果就是你升级包的时候用户辛苦配好的参数被静默覆盖了这个坑特别隐蔽我当时也没注意后来被测试同事投诉才发现。4. 想错的地方三以为 Windows 文件装进 tar 就能原样在 Linux 用4.1 三个隐藏杀手大小写、权限、符号链接这是我第三个、也是最疼的一个教训。Windows 里处理好的文件直接塞进 deb装上之后经常出现各种看起来不该发生的问题。第一个杀手是文件名大小写。NTFS 文件系统默认大小写不敏感你在 Windows 里建一个Config.json再建一个config.json系统并不觉得这是两个文件。但 Linux 的 ext4 区分大小写两个文件能共存。如果你打包的目录里恰好有两个仅大小写不同的文件在 Windows 上复制/归档时很容易互相覆盖而且你还发现不了。解决思路是在 Windows 上整理完文件后专门跑一遍脚本检查有没有大小写冲突的文件名。第二个杀手是权限。你从 Windows 资源管理器复制出来、再拖进 tar 的文件它的 Unix 权限往往是当前 Windows 用户的默认值或者干脆被压成了 644。对于普通配置文件 644 没问题但如果是可执行文件、脚本或者像postinst这类安装脚本权限不对会直接出大事。特别是 deb 里的postinst脚本它没有可执行权限时dpkg 装包时会跑脚本失败甚至回滚整个事务。第三个杀手是符号链接。Windows 上创建符号链接需要管理员权限或者开发者模式平时根本没人去开。但 deb 包里大量使用符号链接比如usr/lib下有些 .so 会做成指向libfoo.so.1.2的软链接。如果你在 Windows 下把这些符号链接复制成了普通文件那装出来的系统库里就会出现一个几百字节的假文件或一个复制出来的完整动态库程序一运行就找不到入口符号。这比权限问题更难排查因为表面上看文件都在也不报文件缺失能报错也只会是undefined symbol这种不直接指向文件问题的信息。4.2 在 Windows 上打出有 Unix 味道的 tar搞明白坑在哪事情就好办了。我后来总结了一套固定操作专门用来让 Windows 下产生的内容尽量接近Unix 原产。关键点在于创建 data.tar 的时候不能用 Windows 的复制粘贴 普通压缩思路要用 bsdtarlibarchive 的命令行工具来打并且显式指定一些 Unix 特有的属性。我在 7-Zip 之外还装了一个 bsdtar因为它能识别并保留符号链接。打 tar 时加上这些参数效果会好很多bsdtar --uid 0 --gid 0 --mode755 -cf data.tar -C stage ./usr ./etc ./lib--uid 0 --gid 0是让打包进去的文件所有者强制设为 root避免出现安装出来的文件属于某个 Windows 用户名的情况--mode755是给目录和二进制一个合理的默认权限。当然不同文件需要的权限不一样我会先处理好目录里每个文件的权限再用这个命令打底最后对特定文件单独调整。如果你跟我一样有 WSL更省心的做法是在 WSL 里执行dpkg-deb --build --root-owner-group这个--root-owner-group参数会自动把包内所有文件所有者处理成 root不用自己手动chown。Windows 侧我只负责把文件路径和内容组织对权限、属主这些Unix 味道全交给工具环节解决。4.3 md5sums 和换行符全在细节里再说两个容易翻车的细节校验文件和换行符。deb 包里通常有一个DEBIAN/md5sums记录了 data 部分每个文件的 MD5。dpkg 安装时可以用它来校验包是否完好。如果你在 Windows 下用记事本或者 PowerShell 重定向生成了 md5sums很可能出现两个问题一是换行符是 CRLF二是文件路径分隔符是反斜杠。dpkg 对这两样东西都很敏感轻则警告重则报 md5sums mismatch。我的解决办法是要么在 PowerShell 里把生成的文本统一把\r\n替换成\n要么干脆最后在 WSL 里重新生成一遍cd stage find usr etc lib -type f -exec md5sum {} \; DEBIAN/md5sums这个方法最干净因为md5sum生成的格式天生就是 dpkg 认的那一套。此外DEBIAN/control文件也必须是 LF 换行。Windows 下用 PowerShell 写文本文件默认会带 CRLF如果直接拿去dpkg-deb --build有些版本会报错有些则能打出来但结果不稳定。所以我的流程是control 内容先在 Windows 里写好草稿但最终文件一定在 WSL 里用printf或者cat生成。这是我踩过 CRLF 的坑之后养成的习惯建议你直接照做别试。5. 实操全景一条完整的 Windows→arm64 deb 流水线5.1 目录树与 control 文件的写法说了这么多直接上一条可以照抄的流水线。假设我要打的软件叫myapp目标架构是 arm64目标系统是 Ubuntu 22.04 ARM 版。在 Windows 上先建立一个临时目录stage/里面按 Linux 根目录结构排布stage/ ├── DEBIAN/ │ └── control ├── etc/ │ └── myapp/ │ └── config.ini ├── lib/ │ └── systemd/system/ │ └── myapp.service ├── usr/ │ └── bin/ │ └── myapp └── var/ └── lib/ └── myapp/DEBIAN/control文件的标准写法是Package: myapp Version: 1.0.0 Architecture: arm64 Maintainer: Your Name youexample.com Depends: libc6 ( 2.35), libstdc6 ( 12) Section: utils Priority: optional Description: My application for ARM64 devices This package installs myapp on ARM64 Ubuntu systems. The first line after Description is a short summary. Following lines are the long description, indented with spaces.这里有几个细节Version里不能用连字符加数字之外的内容太随性建议遵循 Debian 版本规范Description第一行是摘要后续行是详细描述每一行开头要加一个空格Depends的版本符号前后有空格这是 dpkg 的语法别写错。5.2 用 PowerShell 脚本一键整理再在 WSL 里封包我整理文件时优先用脚本而不是鼠标点来点去因为重复操作容易出岔子。下面这个 PowerShell 脚本片段负责把编译好的 arm64 二进制和配置放进 stage 目录并生成 md5sums$ErrorActionPreference Stop $stage ./stage # 创建目录结构 New-Item -ItemType Directory -Force $stage/DEBIAN | Out-Null New-Item -ItemType Directory -Force $stage/usr/bin | Out-Null New-Item -ItemType Directory -Force $stage/etc/myapp | Out-Null New-Item -ItemType Directory -Force $stage/lib/systemd/system | Out-Null New-Item -ItemType Directory -Force $stage/var/lib/myapp | Out-Null # 复制文件 Copy-Item ./build-arm64/myapp $stage/usr/bin/myapp -Force Copy-Item ./config/config.ini $stage/etc/myapp/config.ini -Force Copy-Item ./deploy/myapp.service $stage/lib/systemd/system/myapp.service -Force # 给可执行文件设置只读等基础属性真正的 Unix 权限由 WSL 里的封包命令处理 Set-ItemProperty $stage/usr/bin/myapp -Name IsReadOnly -Value $false # 生成 md5sums注意: 这里只是 Windows 侧参考 # 最后到 WSL 里我会用 find md5sum 再生成一遍 $files Get-ChildItem -Path $stage -Recurse -File | Where-Object { $_.FullName -notmatch \\DEBIAN\\ } foreach ($f in $files) { $rel $f.FullName.Substring((Resolve-Path $stage).Path.Length 1) -replace \\, / $hash (Get-FileHash -Algorithm MD5 $f.FullName).Hash.ToLower() ${hash} ${rel} } | Set-Content -Path $stage/DEBIAN/md5sums -Encoding ascii跑完之后在 Windows 命令行里进入 WSL执行最终封包命令cd /mnt/c/work/myapp # 先把 Windows 生成的 control 和 md5sums 转成 LF 换行 sed -i s/\r$// stage/DEBIAN/control stage/DEBIAN/md5sums # 重新生成一次 md5sums保证格式 100% 正确 find stage -type f ! -path stage/DEBIAN/* -exec md5sum {} \; | \ sed s# stage/# # stage/DEBIAN/md5sums # 构建 deb 包--root-owner-group 自动设置 root 属主 dpkg-deb --build --root-owner-group stage myapp_1.0.0_arm64.debsed -i s/\r$//这条命令专门清掉 Windows 换行符效果立竿见影。--root-owner-group是 dpkg 1.19 之后引入的参数我用它批量解决属主问题。最终产物就是myapp_1.0.0_arm64.deb这个文件名符合 Debian 的命名惯例包名_版本号_架构.deb。5.3 验证三步走file、readelf、dpkg-deb打出包来别急着交货先做三分钟自查。第一步确认二进制本身的架构。在 WSL 里运行file stage/usr/bin/myapp输出应该是类似ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked。如果输出是x86-64那说明你拿错二进制了这时候打出来的 arm64 包就是个自欺欺人的空壳。第二步确认 deb 包里的元信息。运行dpkg-deb -I myapp_1.0.0_arm64.deb dpkg-deb -c myapp_1.0.0_arm64.deb-I打印控制信息-c打印文件列表。重点看Architecture: arm64、Depends是否完整、文件路径是否有./usr/bin/myapp这种规范的相对根路径。第三步如果目标机器不在手边可以用 qemu 在 Windows 或 WSL 里做一次轻量验证用qemu-aarch64用户态模拟运行那个二进制跑个--version或者--help至少能暴露缺动态库这种低级问题。模拟器不能完全替代真机但在这条流水线里已经够用了。到这里一个能交给客户/设备的 deb 包就出来了。整个过程里Windows 负责文件组织和脚本自动化WSL 只干封包和验证这几件需要 Unix 语义的事。6. 常见问题与排查技巧实录6.1 我踩过的坑速查表把我在这个流程里遇到的典型问题整理成一张表按症状、原因、解法排列遇到类似情况直接翻症状原因检查与解决安装时报 wrong architecturecontrol 里Architecture写错比如写成 aarch64、x64打开 deb 的 control 确认字段值若包内全为数据脚本可改all安装后运行提示 No such file or directory动态库缺失或二进制引用的解释器路径不对用readelf -l看interpreter和DT_NEEDED把缺失库打进包或用 apt 安装dpkg-deb --build 报告 control 格式错误control 文件是 CRLF 换行在 WSL 里sed -i s/\r$// stage/DEBIAN/control再重打包安装后脚本执行失败、事务回滚postinst/preinst没有可执行权限或者 sha-bang 不对、脚本含 CRLF确认脚本 shebang 是#!/bin/sh在 WSL 设置chmod 755并检查换行解压出的 .so 文件异常大或运行报 undefined symbolWindows 复制符号链接时把软链接变成了实体文件用 bsdtar 保留符号链接打 tar或在 WSL 里重新创建ln -sdata.tar 里的文件属主是某个奇怪用户而非 rootWindows 侧写文件带入当前用户 uid/gid打 deb 时用--root-owner-group或 tar 时--uid 0 --gid 0目标系统 dpkg 版本太老解压失败data.tar 用了 zstd 压缩旧系统不支持把压缩格式改为 xzdpkg-deb -Zxz --build升级软件后配置文件被覆盖了没写DEBIAN/conffiles或没列出/etc下文件在DEBIAN/conffiles里每行写一个绝对路径安装顺利但命令找不到文件路径放在usr/local/bin但 PATH 没包含或包结构路径不对用dpkg-deb -c查看实际路径对照标准 FHS 目录6.2 多做一步在 Windows 上做 ARM 模拟验证最后分享一个提升交付质量的小技巧。如果你的目标 ARM 机器不在手边交付之前强烈建议在 Windows 上装一个 qemu-aarch64 的用户态模拟器。WSL 里也可以直接apt install qemu-user。它的作用不是完整模拟整台机器而是能直接执行 arm64 的 ELF 二进制让你在 Windows 环境下就能跑一下待交付程序的--version或者基本自检。模拟器跑不了完整系统但能提前暴露好几类问题二进制架构是不是真的 arm64、动态库搜不到时的报错信息、程序启动时会不会因为某个路径写死而崩溃。有次我就是靠它发现打进去的二进制居然还在调用一个绝对路径/opt/intel/...那明显是编译时链接了 x86 机器上的库目录。这种问题如果直接发到客户现场来回一次就是好几天。我个人的经验是qemu-user 这个工具在 Windows 上打 arm64 deb 流程里的价值被很多人忽略了。它不需要你额外准备硬件几秒钟就能跑一次冒烟验证是性价比最高的一道质检工序。6.3 三个必要的心理准备说点工具之外的事。如果你打算长期跟这种跨架构打包需求打交道有几句实在话想提前跟你讲第一别把目标机器是 arm64和包架构是 arm64画等号。打包只是包装真正的兼容性由二进制本身和目标系统的库版本决定。你在 Windows 上能把包打得再漂亮二进制如果是坏的装上也是坏的。第二源文件从哪里来决定了整个包的命运。我后来养成的习惯是先在目标机器或者相同架构的容器里把软件跑通再把那一整套文件原样搬进 Windows 打包。顺序反过来的话你永远不知道是打包的问题还是软件本身的问题。第三保留一份完整的可复现记录。把 Windows 侧的 PowerShell 脚本、WSL 封包命令、control 模板放到 Git 仓库里。下次要打 1.0.1 版本时你只需要改 Version 字段重新跑一遍。这一点在我后续维护好几个 ARM 设备安装包时帮了大忙因为时间一久当初是怎么打出这个包的你真的会忘得干干净净。写在最后折腾完这一整圈我最大的收获是三个字别想当然。deb 看起来是个带安装逻辑的安装包拆到底无非是 ar tar 文本元数据。反过来想一旦你能在 Windows 上把这三样组装明白很多看似神秘的 Linux 软件工程问题其实也就那么回事。如果你也准备在 Windows 上给 ARM 设备打 deb照着上面这条流水线走Windows 整理文件、WSL 封包、qemu 验证三步下来基本能避开我当初走的弯路。最后再提醒一句先拿个 hello world 级别的程序完整跑一遍流程再碰真实业务包这是最稳妥的练手顺序。
返回列表