自编译简介与固件定制

当你受够了闪存空间不够、插件依赖冲突、官方固件里总有几个用不上的组件时,就会自然地走向同一条路:自己编译固件。自编译听起来门槛很高,其实 OpenWrt 的构建系统已经把流程标准化了,跟着步骤走,绝大多数人都能成功。本篇讲清楚自编译是什么、需要什么条件、具体怎么做,以及什么时候不该折腾。

自编译解决了什么

先说清楚自编译和直接刷官方固件、再 opkg 装插件之间的区别。在路由器上用 opkg 安装软件,有三个绕不开的限制:闪存空间总量封顶、内核模块必须与固件内核版本精确匹配、重启后配置和软件依赖 overlay 分区。而自编译是在电脑上把所有需要的软件包和固件镜像一起构建出来,内核与模块天然同源、空间利用最优、软件版本完全由你掌控。

三个典型的适用场景。第一,小闪存设备,比如 8MB 的老路由器,官方固件装完 luci 就所剩无几,自编译可以把用不到的语言包、调试组件全部剔除,省出空间放真正需要的插件。第二,快照版固件用户,厌倦了天天和内核版本不匹配作斗争。第三,有定制需求的人,比如要调整内核配置、打一张自己的补丁、把默认主题换成 Argon。关于自编译的价值判断,可以把它理解成 服务器环境定制 的嵌入式版本。

对比维度 官方固件加插件 自编译固件
空间利用 叠加安装较浪费 按需打包最节省
内核兼容性 可能版本对不上 天然完全匹配
定制程度 低,只能选已有插件 高,可调内核配置
上手难度 零门槛 需要一些构建知识
维护成本 低,随源更新 每次更新需重新构建

准备构建环境

OpenWrt 的构建要求是一台 Linux 电脑(或虚拟机),Windows 用户需要装 WSL 或者开一台 Linux 虚拟机。系统发行版随意,Ubuntu 或 Debian 都行,关键是要装齐编译工具链。官方 README 列出的依赖包括 gcc、make、binutils、bzip2、flex、gawk、perl、python3、rsync、unzip 等,一行命令就能装上。

有一个新手常犯的错:用 root 用户运行构建。官方文档明确要求以普通用户身份执行,路径里还不能有空格,否则会有各种莫名其妙的失败。

准备就绪后,克隆源码并选择稳定版本:

git clone https://git.openwrt.org/openwrt/openwrt.git
cd openwrt
git checkout v23.05.3

# 更新并安装 feeds(软件源订阅)
./scripts/feeds update -a
./scripts/feeds install -a

feeds 是 OpenWrt 的附加软件订阅机制,luci 插件、社区应用都通过 feeds 引入。feeds.conf.default 里可以追加自己的软件源地址(src-git 或 src-link 格式),这么做之后,menuconfig 里就能选中源里的包,这在编译第三方插件时非常关键。

新手的第一次安装至少预留三到四个小时的完整时间,其中大头花在下载源码和构建交叉编译工具链上。工具链只需要构建一次,之后无论你怎么改配置重编,都可以直接复用,第二次编译固件往往只要十几分钟。所以如果可能,给构建环境准备充裕的磁盘空间(建议三十 GB 以上)和稳定的网络,一次性把首轮编译跑通,之后的迭代成本会低得多。切记全程用普通用户身份执行 make,root 用户构建出的产物权限错乱,刷进路由器可能出现莫名故障。

配置与编译

make menuconfig

这是整个流程的核心。menuconfig 会打开一个文本界面菜单,第一级是 Target System(目标平台)和 Subtarget(子平台),必须和你路由器的硬件一致;然后是 Target Profile,对应具体设备型号;再往下是各类软件包选择,按 M 键编译为独立模块(装在固件外)、按 Y 键直接打包进固件(推荐空间紧张时使用)、N 键不编译。

经验不足时,有三条红线要守住。其一,选定平台后不要乱动内核配置(kernel_menuconfig),除非你明确知道自己在改什么。其二,第一次最好导入官方对应设备的 config.buildinfo 作为基础,再在其上增删软件包,这样能保证固件功能完整。其三,改完务必保存退出,确认生成了 .config 文件。

然后是编译:

make -j$(nproc)

首次编译包含工具链构建,大约需要一两个小时,取决于电脑性能和网络速度,之后的增量编译会快得多。编译产物在 bin/ 目录下,按平台分目录存放 .img 固件文件和 .ipk 软件包。找到自己型号对应的固件包,通过 LuCI 的固件升级页面上传刷入即可。

自编译固件完整流程

编译失败是常态,不要气馁。最常见的失败原因是网络拉不到某个源码包(重试或换镜像)、依赖工具版本不对(检查环境)、以及磁盘空间不足(构建过程要占用十几个 GB)。养成一个习惯:编译成功后把 .config 文件备份一份,下次重建只需在此基础上改动,能省下大量时间。这一习惯和 配置备份 里强调的备份优先原则是一脉相承的。

关于版本选择多说两句。git checkout 时建议选带 v 前缀的正式发布标签(如 v23.05.3),而不是 openwrt-23.05 这类持续集成分支,两者差别在于发布标签对应该版本软件源的完整镜像,编译出的固件和官方包完全兼容;用了集成分支,内核版本每天都在变,刷上去之后照样会遇到依赖难题,等于绕了一圈又回到原点。下载源码慢的话可以改用 GitHub 镜像仓库克隆,再通过 git remote 切回官方源。

固件定制进阶:包的选取策略

menuconfig 里的软件包浩如烟海,新手容易犯两个极端的错误:要么什么都不选,编译出的固件和官方基础版没区别;要么什么都想塞,结果固件体积超出闪存容量上限,编译出来的镜像根本刷不进去。比较稳妥的选取顺序是这样的。

先保基础:LuCI 界面(选 luci 主程序加 luci-i18n-base-zh-cn 汉化包)、无线驱动与固件包、dnsmasq 全功能版、防火墙组件,这些是路由器能正常工作的地基。再按需加应用:家庭场景下优先级最高的是 upnp、ddns、adblock 这类小而专的工具,每个只占几十 KB。最后才考虑大家伙:统计采集、文件共享、容器等占用内存较多的服务,每加一个都要掂量一下路由器的 CPU 和内存吃不吃得消。

判断哪些包值得打进固件还有一条实用标准:凡是你每次重刷固件都会第一时间 opkg install 的包,就值得直接编译进镜像,省去重复安装的麻烦;临时试用性质的应用则编译成独立 ipk(menuconfig 里按 M 键),需要时再装、不想要时易删,保持固件本体的干净。闪存吃紧的设备还可以反向操作,把官方默认包含但你从不使用的组件在 menuconfig 里去掉,腾出的空间能多塞好几个实用插件。

需要提醒的是,无论用哪种路线,第一次刷入自编译固件前务必确认手上有官方固件和救砖方案的兜底。编译环境成熟之后,再做系统性的裁剪尝试会更稳妥。

定制固件空间分配

两条捷径

觉得完整编译太重,还有两个轻量选择。

第一个是 Image Builder(镜像生成器)。它不编译任何代码,而是把官方预编译的 ipk 包按你的选择组装成固件镜像,几分钟就能产出定制固件,非常适合只想调整软件包组合、不改任何代码的需求。用法是下载对应版本的 Image Builder 压缩包,解压后用类似下面的命令:

make info
make image PROFILE=generic PACKAGES="luci luci-app-upnp luci-app-ddns -dnsmasq"

PACKAGES 参数里加减号表示从默认集合里剔除。这个工具能覆盖八成的日常定制需求。再进阶一步,Image Builder 还支持自定义 files 目录,把你的 /etc/config 配置文件原样放进去,生成的固件刷完就是你调好的状态,连初始化配置的步骤都全省了。

第二个是 firmware-selector 网页工具。打开官方固件选择器,选好设备和版本,勾勾选选需要的软件包,它直接生成可下载的定制固件链接,全程图形化、零命令行,是给新手最友好的方案。想深入理解其中原理的,可以对照 网络与系统入门 补充背景知识。

三种定制路线对比;完整编译最灵活最慢;Image Builder 组装 快捷方便;固件选择器网页点点即可

延伸阅读

  • 《依赖冲突的处理》:自编译是解决依赖问题的终极手段,这篇讲了配套的排障思路。
  • 《LuCI 应用与常用插件》:编译时该选哪些插件,先看这份清单再动 menuconfig。
  • 《性能优化:CPU 与无线调优》:固件定制之后,性能调优是把硬件潜力发挥出来的下一步。