嵌入式实战:WSL交叉编译arm64 deb包并dpkg部署到开发板

发布时间:2026/11/4 15:26:52
嵌入式实战:WSL交叉编译arm64 deb包并dpkg部署到开发板 做嵌入式开发的人一定不陌生这个场景开发机是 Windows手里的板子是 arm64 架构跑着 Debian 系 Linux。程序写完、交叉编译通过之后最省事的交付方式就是把可执行文件、配置、服务脚本打成一个 deb 包拷到板子上执行一条sudo dpkg -i全部就位。最近我正好给一块 RK3568 的板子做程序交付完整走了一遍这条链路Windows 上用 WSL 做编译环境装 aarch64 交叉编译工具链把产物打成 arm64 的 deb 包最后到板子上验证安装。整个过程串起来看网上的资料很散要么只讲交叉编译要么只讲 deb 格式很少有人把“从 Windows 出发到板端 dpkg -i”这条全链路一次讲清楚。这篇就来填这个空顺便把踩过的坑都交代了。1. 链路设计为什么是 WSL 交叉编译 deb1.1 为什么非要在 WSL 里编译先说结论交叉编译这种事情尽量别在 Windows 原生环境里做也别为了这个专门开一个完整的虚拟机。Windows 原生生态里虽然也有 aarch64 交叉编译器但大部分开源项目的构建脚本、依赖解析、make 逻辑都是按 Linux 环境写的在 Windows 下折腾 CMake、pkg-config、各种 .so 依赖会让你怀疑人生。完整的 VM比如 VMware 装一个 Ubuntu倒是能跑但资源占用大、启动慢文件共享和命令行体验也笨重。WSL 是 Windows 上直接跑 Linux 发行版的那层工具WSL2 带有真正的 Linux 内核文件系统、系统调用、包管理器都是真 Ubuntu 的体验但启动一个终端只要几秒钟和 Windows 共享文件也方便。做嵌入式交叉编译WSL 基本是性价比最高的选择Windows 负责日常编辑、浏览器查资料、管理文件WSL 负责编译、打包两边用同一个工作目录VSCode 装了 WSL 插件之后直接 remote 进去改代码体验非常顺。我实测下来WSL 里跑gcc-aarch64-linux-gnu和真机 Ubuntu 没有任何行为差异唯一要注意的是 WSL 默认发行版比如 Ubuntu 22.04的 glibc 版本比较新后面打包和上板时会牵扯到兼容性问题这个我在第 3 章专门讲。1.2 为什么交付格式选 deb板子上的系统如果是 Debian/Ubuntu 衍生装软件有两种常见思路一种是直接丢一个可执行文件加一堆依赖库另一种是打成 deb 包用 dpkg 管理。前者看起来简单但你要手工处理动态库路径、权限、开机自启、卸载残留交付次数多了迟早出乱子。deb 包本质上是一个“安装脚本 文件清单”的打包体它帮你解决了三件事一是文件该放哪里由包内路径决定不会乱二是依赖关系写在 control 文件里缺了什么dpkg -i会直接告诉你三是卸载时dpkg -r能把文件清干净postinst 脚本还能帮你做 ldconfig、注册 systemd 服务这类收尾动作。对嵌入式设备来说dpkg -i一条命令就能装好一个带服务、带配置的完整应用运维成本低很多。1.3 整条链路先心里过一遍我实际走通的流程是这么几步Windows 上装 WSL安装 Ubuntu 发行版。WSL 里安装gcc-aarch64-linux-gnu交叉编译工具链、CMake、dpkg 相关工具。编译出一个 arm64 架构的可执行文件用file、readelf、qemu-user验证产物正确。按照 Debian 包规范组织目录结构写DEBIAN/control用dpkg-deb --build打成.deb包。把包通过scp、adb push等方式传到板子上执行sudo dpkg -i。验证安装结果检查可执行文件是否在 PATH 里、服务是否起来、卸载能否清干净。每一步都有不少细节下面从环境准备开始逐个讲。2. 环境准备WSL 和 arm64 交叉工具链到手2.1 五分钟装好 WSL装 WSL 的官方姿势很简单Windows 10 版本 2004 以上或者 Windows 11用管理员身份打开 PowerShell 执行wsl --install这个命令默认会装 WSL2 和一个 Ubuntu 发行版。如果你想指定发行版可以加-d参数wsl --install -d Ubuntu-22.04装完以后重启系统会提示你设置 Linux 用户名和密码。这里有两个实测经验装完以后先执行wsl --update把 WSL 内核更新到最新版很多莫名其妙的问题都是内核版本太老导致的。执行wsl -l -v检查发行版状态确认 VERSION 那一列是 2。如果显示 1说明还在用 WSL1兼容性不如 WSL2执行wsl --set-version 发行版名 2升级。如果你遇到发行版下载特别慢或者一直卡在某个百分比可以试试加--web-download参数让安装器走 Web 方式而不是 Microsoft Store 通道wsl --install -d Ubuntu-22.04 --web-download我前阵子给同事配环境时就碰到了这个情况默认走 Store 下载非常慢换成 Web 下载后速度立刻正常了。如果连 Web 下载也不顺还可以去微软官网手动下载 Linux 发行版的 .appx 离线包安装这个我在第 6 章的排查表里会展开说。另外一个容易踩的细节是 WSL 默认会把虚拟磁盘放在 C 盘C 盘空间紧张的话可以在装好 Ubuntu 后用wsl --export导出、wsl --import导入到其他盘也可以直接用新版 WSL 的wsl --manage Ubuntu-22.04 --move D:\WSL移动位置。嵌入式项目里交叉编译工具链、SDK、根文件系统动不动好几个 GB提前把 WSL 挪到大盘能省很多事。2.2 安装 aarch64 交叉编译工具链进入 WSL 后先把系统源和基础工具更新一下sudo apt update sudo apt upgrade -y sudo apt install -y build-essential cmake dpkg-dev然后安装 arm64 交叉编译工具链sudo apt install -y gcc-aarch64-linux-gnu g-aarch64-linux-gnu装完以后编译器名字是aarch64-linux-gnu-gcc注意不是裸的gcc。验证一下aarch64-linux-gnu-gcc --version file /usr/bin/aarch64-linux-gnu-gccUbuntu 的 arm64 交叉工具链是基于目标架构的 glibc 库来链接的。也就是说它默认会把你的程序链接到/usr/aarch64-linux-gnu下的 libc、libstdc。这个库的版本和 WSL 发行版本身Ubuntu 22.04 对应 glibc 2.35是匹配的这一点在后面非常关键——如果板子上的系统比 Ubuntu 22.04 老glibc 版本过低程序跑起来会报GLIBC_2.34 not found解决办法我放在第 3 章。有的开发板 SDK 会自带交叉编译工具链比如一些 RK3568 的 Android/Linux SDK 资料包里就有自己的 aarch64 工具链。对于纯用户态程序用 Debian 官方工具链完全够如果你需要编译内核模块、链接 SDK 私有库或者必须匹配板子 SDK 的特定 sysroot那就优先用厂商提供的工具链。原则很简单工具链的 glibc 版本必须和板端系统的 glibc 版本兼容或者使用静态链接绕开这个问题。如果你要链接一些外部依赖库还得装对应的交叉版开发包比如sudo apt install -y libssl-dev:arm64 libsqlite3-dev:arm64Debian 的 apt 支持用:arm64指定架构安装交叉版库。不过这里有个坑直接apt install xxx:arm64有时候会因为源里缺少某些架构的包而失败需要先启用 arm64 架构sudo dpkg --add-architecture arm64 sudo apt update这一套操作下来编译环境就齐活了。2.3 强烈建议顺手装个 qemu-user交叉编译最大的痛点是不能在本机直接跑产物验证。qemu-user可以解决一部分问题它用用户态模拟的方式在 x86 的 Ubuntu 上直接执行 arm64 的可执行文件不需要完整模拟整台机器。安装sudo apt install -y qemu-user然后就可以直接跑 arm64 程序wget https://example.com/hello-arm64 # 举例实际用你自己编译的产物 qemu-aarch64 ./hello注意qemu-user的模拟不是百分之百完美的涉及某些硬件相关的 ioctl、内核模块交互的程序可能跑不起来但验证一个纯用户态的 CLI 程序绝对够用。对我来说它能让我在打 deb 包之前就发现“程序一启动就段错误”“动态库找不到”这类低级问题省得把包传到板子上才发现往返一趟板子麻烦得很。3. 交叉编译实战写出并验证 arm64 产物3.1 先拿一个最小的 C 程序练手写一个没有依赖的 hello world验证整条工具链通不通。在 WSL 里创建hello.c#include stdio.h int main(void) { printf(Hello arm64, from deb!\n); return 0; }编译aarch64-linux-gnu-gcc -o hello hello.c然后立刻用file查看产物格式file hello正常输出是hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, not stripped看到ARM aarch64就说明工具链工作正常。再用qemu-user跑一下qemu-aarch64 ./hello能打印出Hello arm64, from deb!说明这个程序在你的开发机上已经验证过了。到这一步最简单的交叉编译链路已经通了。3.2 CMake 交叉编译工具链文件实际项目基本都用 CMake不可能手动敲编译命令。CMake 交叉编译的核心就是写一个 toolchain 文件告诉它目标平台是什么、编译器是谁、去哪里找依赖库。我常用的toolchain-arm64.cmake长这样set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g) set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)解释一下这几个关键行CMAKE_SYSTEM_NAME Linux告诉 CMake 目标系统是 Linux它就不会用宿主机的平台探测结果。CMAKE_SYSTEM_PROCESSOR aarch64让 CMake 的CMAKE_SYSTEM_PROCESSOR变量变成 aarch64很多项目会拿它判断架构。CMAKE_FIND_ROOT_PATH指向交叉工具的 sysroot 路径/usr/aarch64-linux-gnu这样find_library、find_path会在交叉环境的目录里找而不是找到宿主机的 x86 库。三个_MODE_*设置的作用是查找程序时还是用宿主机工具比如find_program要找到宿主机上的代码生成器但查找库和头文件时只允许在 sysroot 里找避免混入 x86 的 .so。使用方式cmake -DCMAKE_TOOLCHAIN_FILE../toolchain-arm64.cmake -DCMAKE_BUILD_TYPERelease .. make -j$(nproc)这里有个经验-j$(nproc)在 WSL 里经常能被识别成满核并行如果编译时内存不够可以改成-j4这类保守值。我在 WSL 里遇到过交叉编译 OOM尤其是编译 Qt 这类大项目时把并行度降下来是立竿见影的解决办法。3.3 产物自检三板斧打完包之前一定先做三个检查别急着上传板子第一file看架构。如果输出变成x86-64说明编译时没用交叉工具链或者 CMake 没有正确加载 toolchain 文件这一版包打了也白打。第二readelf -d看动态依赖readelf -d hello重点看NEEDED列表和interpreter。如果是动态链接interpreter应该是/lib/ld-linux-aarch64.so.1如果出现 x86 的/lib64/ld-linux-x86-64.so.2那就是链接到了错误环境的库。NEEDED列表里出现的 .so 必须保证在板子上存在且版本够新。第三qemu-aarch64直接跑一下做冒烟测试。这一步能过滤掉 80% 的低级问题。我习惯跑一轮程序的--version、--help这类命令确认至少能正常启动。上面说的是动态链接的通用做法。如果你不想赌板子的 glibc 版本对简单程序可以静态编译aarch64-linux-gnu-gcc -static -o hello hello.c静态链接的产物体积会变大不少但好处是彻底不依赖板子的动态库环境。代价是 glibc 的静态链接在某些场景下会有 DNS、NSS 方面的问题所以我的建议是简单命令行工具静态编译省心涉及网络、复杂依赖的程序还是动态链接然后用下面第 6 章的方法处理 glibc 版本兼容。4. 打 deb 包目录布局、control 字段和权限细节4.1 deb 包的本质与目录结构deb 包内部其实是 ar 格式的归档包含debian-binary、control.tar.*和data.tar.*三个成员。但你不需要手工拼这些dpkg-deb --build会处理。我们只需要准备一个 staging 目录里面放好“安装到目标系统根目录后的完整目录树”再加上一个特殊的DEBIAN目录存放控制信息。示例hello_1.0.0_arm64/ ├── DEBIAN/ │ ├── control │ ├── postinst │ └── prerm └── usr/ ├── bin/ │ └── hello └── share/ └── doc/ └── hello/ └── changelog.gz规则很简单usr/bin/hello就是安装后文件系统里/usr/bin/hello的位置。依赖库、配置文件、systemd service 文件都照这个逻辑放。比如hello_1.0.0_arm64/ ├── DEBIAN/ │ └── control ├── etc/ │ └── hello/ │ └── config.ini ├── lib/ │ └── systemd/ │ └── system/ │ └── hello.service └── usr/ └── bin/ └── hello4.2 control 文件字段逐个说DEBIAN/control是包的“身份证”我最简版本是这样Package: hello Version: 1.0.0 Section: utils Priority: optional Architecture: arm64 Depends: libc6 ( 2.31) Maintainer: Your Name youexample.com Description: A sample arm64 package built on WSL This is a demonstration deb package created for embedded devices.字段含义Package包名全小写安装后dpkg -l显示的就是它。Version版本号必须包含数字推荐主版本.次版本.修订号三段式。Section和Priority分类信息填utils、optional这类常规值即可不填也能装。Architecture这里必须写arm64不能是 amd64。如果写错板子上会直接报架构不匹配。Depends运行时依赖。libc6 ( 2.31)表示要求 libc6 版本至少 2.31如果板端版本不够dpkg -i会拒绝安装并提示缺依赖。这里的版本号一定要参考你在第 3 章readelf看到的实际符号版本。Maintainer和Description前者是维护者邮箱后者第一行是短描述后面空一格缩进可以写长描述。如果程序有开机自启需求写一个postinst脚本安装后自动注册 systemd 服务#!/bin/sh set -e if [ -d /run/systemd/system ]; then systemctl daemon-reload systemctl enable hello.service systemctl start hello.service || true fi exit 0对应的prerm脚本做卸载前清理#!/bin/sh set -e if [ -d /run/systemd/system ]; then systemctl stop hello.service || true systemctl disable hello.service || true fi exit 0注意脚本第一行必须是#!/bin/sh而且要有可执行权限否则 dpkg 执行后置脚本时会失败。4.3 权限、属主和 md5sums打包时最容易出错的地方是权限我踩过几次可执行文件必须有755或755以上权限否则装到板子上根本没有执行位。交叉编译出来的文件默认就有执行权限但如果你从 Windows 侧拷贝文件进了 WSL可能把权限弄丢装之前务必ls -l检查。postinst、prerm等维护脚本必须可执行。包内文件的属主在理想情况下应该是 root:root。如果你在 WSL 里用当前用户比如me直接dpkg-deb --build装到板子上文件属主可能变成 uid 1000 对应的用户这在某些服务场景下会造成诡异问题。解决属主问题最简单的办法是用--root-owner-group参数强制所有文件属主归 rootdpkg-deb --build --root-owner-group hello_1.0.0_arm64dpkg-deb --build本身不需要 root 权限但加上这个参数后包内所有文件的属主都会变成 root这是最省心的做法。传统做法是用fakeroot创建伪 root 环境再打包但--root-owner-group已经足够满足绝大多数嵌入式场景。md5sums文件记录包内所有文件的校验值dpkg -V验证包完整性时会用到。dpkg-deb --build不会自动帮你生成但缺了它包也能正常安装。如果你在意完整性校验可以在打包前手动生成cd hello_1.0.0_arm64 find . -path ./DEBIAN -prune -o -type f -print | sed s|^\./|| | sort | xargs md5sum DEBIAN/md5sums4.4 dpkg-deb 打包与自检打包命令dpkg-deb --build --root-owner-group hello_1.0.0_arm64成功后会生成hello_1.0.0_arm64.deb。立刻自检两个信息dpkg-deb --info hello_1.0.0_arm64.deb dpkg-deb -c hello_1.0.0_arm64.deb第一个命令打印包的 control 信息看Architecture: arm64是否写对第二个命令列出包内文件清单确认路径结构符合预期。还可以顺手解包到临时目录看看内容mkdir /tmp/check cd /tmp/check dpkg-deb -x /path/to/hello_1.0.0_arm64.deb . find . -type f -exec file {} \;确认里面的可执行文件确实是ARM aarch64格式。到这一步包基本就合格了。5. 上板安装传包、dpkg -i 与验证5.1 包怎么传到板子包打好以后要送进板子传包方式取决于你的板子环境。我整理了一张常用的对比表方式前置条件优点缺点scp板子上有 SSH 服务如 dropbear、openssh-server命令行直接传最推荐板子需要联网配置和 SSH 账号adb push板子支持 adb 调试USB 直连无需网络一些不带 adb 的 Linux 板不支持U盘/读卡器板子有 USB 口或可插 SD 卡不依赖网络和协议来回插拔效率低HTTP 内网下载板子上有 wget/curl适合批量刷机需要在 PC 上临时起 HTTP 服务我平时用得最多的是 scp。很多嵌入式 Linux 板默认带 dropbear 这个轻量 SSH 服务端配合两个命令就能传scp hello_1.0.0_arm64.deb root板子IP:/root/ ssh root板子IP dpkg -i /root/hello_1.0.0_arm64.deb如果板子连不上局域网那adb push更靠谱adb push hello_1.0.0_arm64.deb /data/local/tmp/ adb shell dpkg -i /data/local/tmp/hello_1.0.0_arm64.deb5.2 板端安装与运行验证上板第一件事不是直接 dpkg -i而是先确认板子的架构dpkg --print-architecture正常输出arm64。如果板子系统是 32 位的 ARM输出会是armhf那你打的 arm64 包根本装不进去得重新用arm-linux-gnueabihf工具链编译打armhf包。架构对了就安装sudo dpkg -i hello_1.0.0_arm64.deb正常输出类似Selecting previously unselected package hello. (Reading database ... 50233 files and directories currently installed.) Preparing to unpack hello_1.0.0_arm64.deb ... Unpacking hello (1.0.0) ... Setting up hello (1.0.0) ...然后用dpkg -s查看状态确认Status: install ok installeddpkg -s hello运行一下程序which hello hellowhich能找到说明/usr/bin下的符号链接或文件已经就位。如果程序有 postinst 注册了 systemd 服务再检查一下服务状态systemctl status hello.service5.3 卸载与升级卸载顺便也验证一下sudo dpkg -r helloprerm脚本会先停掉服务然后 dpkg 把dpkg -S登记过的文件删掉。卸载后which hello应该找不到了配置文件如果放在/etc/hello下面默认不会删需要dpkg --purge hello才会连配置文件一起清。升级则是直接装新版包sudo dpkg -i hello_1.1.0_arm64.debdpkg 会保留现有配置文件只覆盖程序文件这个行为对嵌入式设备的远程升级很重要。6. 常见问题排查速查表这一节是我一路踩坑攒下来的按环节整理成速查表遇到问题可以直接对号入座。6.1 WSL 环境问题现象原因处理办法wsl --install提示需要启用虚拟化功能BIOS/固件里虚拟化被关闭进 BIOS 开启 Intel VT-x/AMD SVM再执行wsl --update发行版下载极慢或中途失败默认仓库通道网络不稳定加--web-download或手动下载 .appx 离线包安装wsl -l -v显示版本为 1WSL1 未升级到 WSL2wsl --set-version 发行版 2WSL 启动报Error code: WSL/...一长串WSL 内核组件异常wsl --shutdown后重启终端还不行就netsh winsock reset后重启 Windowsapt 源连不上网络或源镜像问题换成国内可用镜像源再apt update关于离线包安装方法是去微软官方页面下载对应发行版的.appx或.msixbundle文件双击安装后再执行wsl --install -d 发行版名做初始化这在批量部署开发机时特别有用不需要每台机器都走一遍在线安装。6.2 交叉编译问题现象原因处理办法file显示 x86-64编译器用错了还是本机 gcc确认 CMake 加载了 toolchain 文件命令行直接编译时用aarch64-linux-gnu-gcc运行报GLIBC_2.34 not found工具链 glibc 比板端新静态链接或用与板端 glibc 匹配的老版本工具链或先在板端升级系统 libc慎用链接时找不到 .so需要交叉版依赖库dpkg --add-architecture arm64后apt install xxx:arm64pkg-config找到 x86 库pkg-config 环境没切换安装pkg-config-aarch64-linux-gnu用aarch64-linux-gnu-pkg-config编译大项目 OOM并行度过高make -j4或降低内存占用WSL 可调整.wslconfig内存限制glibc 版本不匹配这个问题要单独强调。WSL 里 Ubuntu 22.04 的工具链链接的 glibc 是 2.35如果板子系统是 Debian 11glibc 2.31或更老动态链接的程序装上去一跑就报错。最稳妥的办法是交付前先用readelf -d看 NEEDED再用strings或者objdump -T确认实际引用的 GLIBC 符号版本上限和板端ldd --version对比。对比不过就用静态链接或者准备一个和板端系统版本一致的 Docker 容器来做编译环境。6.3 dpkg 安装问题现象原因处理办法package architecture (amd64) does not match system (arm64)control 里 Architecture 写错重新打包含Architecture: arm64的包dependency is not satisfiable: libc6 ( 2.31)板端 libc6 版本不够更新板端系统或降低 control 里的版本要求到实际值package is in a very bad inconsistent state之前安装中断sudo dpkg --remove --force-remove-reinstreq 包名清理后重装unable to open file ... permission denied包内文件属主或权限异常用--root-owner-group重新打包检查可执行位postinst 脚本报错导致安装失败脚本缺#!/bin/sh或没有执行权限给脚本加执行权限重打包6.4 打包与权限问题打包相关的坑主要集中在三处一是dpkg-deb --build前忘了加--root-owner-group导致文件属主不对二是从 Windows 手动拖文件进 WSL 时Linux 权限标记丢失可执行文件变成 644三是目录权限过松导致opt下文件没有正确权限服务起不来。我的习惯是所有文件先在一个固定的 staging 目录里整理好全部用chmod/chown设好权限再统一打包装进包。不要在包目录里零散地拷贝文件那样权限很难管理。7. 把流程固化成脚本链路走通以后我强烈建议把它写成一个build.sh脚本一键完成“编译-打包-校验”否则每次手动敲一堆命令迟早会敲错。我项目里的脚本骨架大概是#!/bin/bash set -e BUILD_DIRbuild-arm64 PKG_DIRhello_1.0.0_arm64 rm -rf $BUILD_DIR $PKG_DIR mkdir -p $BUILD_DIR # 1. 交叉编译 cmake -S . -B $BUILD_DIR \ -DCMAKE_TOOLCHAIN_FILE../toolchain-arm64.cmake \ -DCMAKE_BUILD_TYPERelease cmake --build $BUILD_DIR -j4 # 2. 校验架构 file $BUILD_DIR/hello | grep -q ARM aarch64 || { echo 架构错误; exit 1; } # 3. 组织 deb 目录 mkdir -p $PKG_DIR/usr/bin $PKG_DIR/DEBIAN cp $BUILD_DIR/hello $PKG_DIR/usr/bin/ chmod 755 $PKG_DIR/usr/bin/hello cp packaging/control $PKG_DIR/DEBIAN/control # 4. 打包 dpkg-deb --build --root-owner-group $PKG_DIR # 5. 自检 dpkg-deb --info ${PKG_DIR}.deb脚本里每一步都做了校验尤其是第 2 步用file检查架构能在打包前就拦住错误产物。之后的交付就只剩两件事./build.sh生成 debscp上传然后在板子上sudo dpkg -i。我个人的实际体会是这套流程里最值得花时间调试的不是dpkg-deb本身而是 glibc 版本兼容——很多人第一版包传到板子上装是能装上一运行就报GLIBC_2.34 not found然后才开始手忙脚乱地查。先搞清楚板端的 libc 版本再决定用动态链接还是静态链接能省掉一整轮“打包-传包-安装-翻车”的循环。如果后续还想更正式可以引入dpkg-buildpackage做源码包管理或者用 GitHub Actions 的 ubuntu runner 配同样的交叉工具链把整个流程变成 CI这样每次提交代码自动出一份 arm64 deb。对你自己的小项目来说一个可靠的build.sh加上一条dpkg -i已经相当够用了。