本教程分为二个部分:编译教程与问题解决 和 特殊设备问题解决。编译教程与问题解决:基础知识、准备工作、开始编译、错误与解决、FAQ。特殊设备问题解决:序号 五 至 后。(OpenWrt 的一些功能实现会写在教程的zui后)请注意,文章部分 patch 代码块为了显示效果会写为 diff 代码块,我会在代码快前添加“(patch 代码)”来标志。本文仅在酷安、图拉丁Club、恩山论坛发布,请以上述平台中zui新文章为准,谢绝一切转载。基础知识 / Basic本节名词解释:• SDK —— 英文 Software Development Kit 的缩写,翻译:“软件开发套件”。• ImageBuilder —— 翻译:映像构建器。• Github —— 国际著名的开源代码托管平台,国内可能无法直接访问。对于教程中任何出现中文方括号(【】)的内容,请将其(包括方括号)替换为方括号内文本表示的“实际内容”。如:请将 【Linux内核版本】 替换为 5.10.4。源码、SDK、ImageBuilder / Source, SDK and ImageBuilder编译 OpenWrt 更好使用来自官方的源码。即位于 Github 仓库 或 第三方镜像源 的源码。对于不同的需求,有下面三种选择:• 如果你想定制 OpenWrt,更好的方法是直接从 OpenWrt 放在 Github 上的源码开始修改和编译。• 如果你想定制/编译 opkg 的软件包(*.ipk)或定制 OpenWrt,更好的方法是直接从 OpenWrt 官方和各大镜像(如清华Tuna镜像源)上的 SDK 开始修改和编译。• 如果你想简单打包 OpenWrt(给 OpenWrt 封装第三方 opkg),更好的方法是直接从 OpenWrt 官方和各大镜像(如清华 Tuna 镜像源)上的 ImageBuilder 开始修改和编译。复杂度和可定制度从上往下递减。总的来说,OpenWrt 的源码、SDK、ImageBuilder 是面向不同人、不同需求所准备的。编译 OpenWrt 的源码也可以生成 SDK 和 ImageBuilder(需要手动选择)。友情链接:• 清华大学 Tuna 镜像源• 中国科技大学镜像源Patch 文件(补丁文件) / Patch Files位置:补丁文件通常位于源码目录 target/linux/【架构】/patches-【Linux 内核版本】/ 目录下,这个目录的名字不是固定的(请勿直接复制粘贴)。名称:000-XXXXXX.patch,开头的数字(如:000)表示这个补丁文件的序号,也对补丁文件的应用顺序做了定义。短横线(“-”)后的英文字符串为对这个补丁文件的说明。例如:322-mt7621-fix-cpu-clk-add-clkdev.patch需要注意:补丁文件开头的数字不能与现有补丁文件开头的数字重复!补丁文件的说明可以自定义。语法 / Grammar头 / Head“头”用来表示需要修补的文件,以 +++ 和 — 两行开头,另一个“头”的开始结束,用来表示旧文件和新文件。例如:(patch 代码)— a/arch/mips/ralink/mt7621.c+++ b/arch/mips/ralink/mt7621.c“头”后面的块均用来修补“头”中指定的文件。块 / Block“块”用来表示需要修补的内容,以 @@ 开头,另一个“块”的开头或另一个“头”的开头结束。例如:(patch 代码)@@ -105,11 +110,94 @@ static struct rt2880_pmx_group mt7621_pi“块”的开头第一行可以称为“‘块’的起始行”,“块”的起始行中,两个“@@”(不是两个“@”)中间的内容指定这一块的内容应该修补原文件的哪里、多少行,修改的内容是什么。在这个示例中,这一块表示:从原文件的第 105 行开始,删除 11 行,从第 110 行开始,添加 94 行。逗号前的数字前面的符号用于表示增加行(+)还是删除行(-),不写符号则表示仅引用(用于“定位”)。要修改的内容从“块”的起始行已经开始了,第二个“@@”后面写的内容即为要改为的内容的第一行。增加行的内容的语法取决于被修补文件的类型,例如对于修补上述“头”中指定的需要修补的文件而言(mt7621.c),添加行的内容的语法为 C 语言的语法(因为“.c”结尾的文件为 C 语言源文件)。OpenWrt 的固件分类 / OpenWrt Image CategoryOpenWrt 将其固件分为四类:• Kernel —— 具有zui少文件系统的 Linux 内核。• Kernel1 —— 单独的 Linux 内核映像。• rootfs0 —— 单独的根文件系统映像。• Systupgrade —— 系统更新映像,含有固件所有完整的功能。zui常使用的为 Kernel 和 Sysupgrade 映像。Kernel 即为 initramfs 映像,具有基本的功能,一般用于双分区设备的初始化安装。Sysupgrade 即为完整的全量映像,具有全部的软件包和完整的功能,为路由器的zui终系统。Kernel1 和 rootfs0 不常使用,不在本文探讨。OpenWrt 的源码分类 / OpenWrt Source Category目前 OpenWrt 的源码主要有:• OpenWrt 官方源码• X-Wrt 源码• ImmortalWrt• LEDE 源码(“L大”源码)X-Wrt 在 OpenWrt 的基础上,做了部分易用性优化。很多非官方源码已停止更新/维护,推荐使用 OpenWrt 官方源代码,维护人数多,安全性更好,代码、使用的 Linux 内核更新,同时交流社区zui多,易于问题解决。本文使用 OpenWrt 官方源码。需要注意的是,固件的“特有”软件包由固件编译者添加,并非某个源码特有。准备工作 / Preparing Works本节名词解释:• Linux —— 类 Unix 的开源操作系统(常用于通称基于其的系统)。• Windows —— 由微软开发的闭源操作系统的名称。• Ubuntu —— 一个基于 Linux 系统的名称。• Fedora —— 一个基于 Linux 系统的名称。• WSL —— 英文 Windows Subsystem for Linux 的缩写,翻译:“适用于 Windows 的 Linux 子系统”。• VMware —— 集成了虚拟机环境的软件的名称。• 宿主机 —— 指运行虚拟机/模拟环境等不存在于现实的环境的设备。本人的编译环境:CPU:Intel Core i5-10400GPU:NVIDIA GeForce GTX650 (Patched for UEFI)RAM:DDR4 16GB 2733MHz硬盘(系统):SSD 120GB网络:中国联通 300Mbps GPON
主系统:Windows 11 22H2 PREVIEW子系统:Ubuntu 20.04 LTS子系统环境:> 环境变量(PATH):/home/levi/openwrt/staging_dir/host/bin:/home/levi/openwrt/staging_dir/toolchain-mipsel_24kc_gcc-11.2.0_musl/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin编译目标:OpenWrt 22.03 (git branch)> MediaTek Ralink MIPS> MT7621 based boards> Xiaomi Mi Router 3G安装依赖 / Install Dependencies官方和我均推荐使用 Ubuntu 系统进行编译,不论是虚拟机(WSL/VMware/其他)还是实体机。我尝试使用 Fedora 35(WSL)进行编译的时候尽管已经安装好各种依赖,但总有一些玄学问题。zui后编译成功是在 Ubuntu 20.04 LTS(WSL)系统上。Ubuntu 推荐执行这个命令(两条都要执行):(来自 OpenWrt 官网)sudo apt updatesudo apt install build-essential ccache ecj fastjar file g++ gawk \gettext git java-propose-classpath libelf-dev libncurses5-dev \libncursesw5-dev libssl-dev python python2.7-dev python3 unzip wget \python3-distutils python3-setuptools python3-dev rsync subversion \swig time xsltproc zlib1g-dev编译前准备 / Preparing for Compiling• 用户账户:确认当前用户不是“root”用户,如果是,则新建并切换为普通用户(建议在安装系统时就创建,后期创建的普通用户可能会有权限问题),随后设置默认用户为普通用户。• 环境变量:对于 WSL,官方推荐使用下面的环境变量替代系统原有环境变量(因为 WSL 子系统的 $PATH 会包含部分 Windows 目录,而这些目录带有空格,可能导致编译失败):PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin并且推荐在这个环境变量的基础上添加 OpenWrt 编译工具链的变量(OpenWrt 编译工具链在编译之前不存在,但是开始编译后会自动生成,因此可以在编译前添加入系统环境变量):PATH=~/【OpenWrt源码目录】/staging_dir/host/bin:~/【OpenWrt 源码目录】/staging_dir/toolchain-mipsel_24kc_gcc-【GCC 版本】_musl/bin:$PATH• 网络环境:确保可以正常访问 OpenWrt 源码的 Github 仓库。确保可以正常访问部分软件包源码的下载仓库/地址。• 源码:首先下载 OpenWrt 的源码(克隆仓库到本地):git clone 查看链接执行命令修改目录权限,防止权限问题造成编译失败:chmod -R 777 OpenWrtchmod -R +x OpenWrt进入源码目录,首先要做的事情是切换分支到你想要的 OpenWrt 版本(分支),如果没有你想要的版本,请从别处下载你想要的版本的 OpenWrt 源码,如果你想使用zui新的代码,可以不切换分支,直接使用 master 分支的代码(但这样做可能导致你的固件包含许多 BUG)。例如:强制切换到 OpenWrt 22.03 版本的分支。git checkout –force openwrt-22.03切换分支完毕后,我们需要添加 OpenWrt 的“feeds”。修改 feeds.conf.default 文件,添加你想要的 feeds 源后,保存。使用命令更新并安装 feeds 源:./script/feeds update -a # 执行源码目录内 feeds 脚本的 update 命令./script/feeds install -a # 执行源码目录内 feeds 脚本的 install 命令开始编译 / Start Compiling配置 OpenWrt 的特性、功能、软件包等 / Config OpenWrt首先要确定你编译的固件需要用在什么架构的什么芯片的什么型号的设备上。比如我的是:固件用在 MediaTek MIPS 架构的 MT7621 的芯片的 小米路由 3G 设备。使用命令:make menuconfig随后你的终端会变成一个使用键盘操作配置的 GUI 界面,如果终端界面大小过小则无法显示 GUI 界面并进行配置!根据每一个选项的字面意思(不懂英文的自行翻译)来进行配置。大多数的功能和配置都会影响固件的大小从而影响路由器硬盘空间的占用,因此如果添加过多的功能和软件包则会造成固件大小和实际安装后的大小过大!对于部分双分区的设备而言,由于其固件可能主要使用第二个分区作为系统分区,第一个分区大小较小,因此无法安装较大的 initramfs 映像,从而无法正常启动 initramfs 映像(这个的影响会写在后面)。下载源码 / Download Souce根据你上一步的配置,下载编译需要的源码(包括软件包),这步操作将减少编译时因下载错误造成的编译失败并且节省编译时间。使用命令:make download -j【CPU线程数】 V=s 【请根据自己电脑的 CPU 来设置,例如本人的这条命令为:make download -j12 V=s由于网络环境影响,有时会下载失败或卡住,这时可以通过“Ctrl + C”(^C)快捷键终止操作,执行下面的命令 删除下载临时文件 后再次执行上面的命令继续下载。rm -rf tmp/小技巧:有时候无法通过 “curl” 工具下载特定文件或者自动下载的文件地址不正确/失效导致一直下载失败,这时可以直接利用搜索引擎搜索需要下载的文件名下载到电脑后放到位于 OpenWrt 源码目录内 dl/ 目录下,并且删除下载临时文件后继续执行下载命令直到完全正常(无报错)。推荐多执行几次,确保所需软件包的源码下载正确、完整。编译 / Compiling官方推荐使用单线程编译(make -j1 V=s),这样会让错误显示在zui底部(不同线程执行命令的顺序不同,因此错误信息返回的时间也不同,有可能导致错误信息被其他线程的正确信息推到终端后面)开始前的建议:检查位于 OpenWrt 源码目录内 dl/ 目录下下载的源文件(文件大小、哈希值等),通常文件大小小于 1M 的源码文件是下载错误的,建议重新下载。(例如:php 8.1.3 为 11.7M,Linux 显示为 12M)我个人推荐:使用全线程编译,当遇到错误终止后再次多线程编译(可能解决玄学问题,但我没遇到),同一问题没有消失查看错误:有明显报错信息的,根据报错信息解决。没有明显报错信息的,换单线程编译查看错误(make -j1 V=sc)。目前我遇到的错误和解决方案:• 修改修补文件后,内核无法编译成功。原因:修补文件“块”内容的语法错误。报错后待编译程序自动中止后修正修补文件的语法错误,随后可继续多线程编译。• 同一个文件,但两个软件包均提供了。报错自动中止后重新配置 OpenWrt,冲突的软件包二选一(OpenWrt 自带的软件包优先保留(为了兼容性),但也可选择替换,一般不会出现问题)。使用命令进行编译:make -j12 V=s根据我的电脑配置,zui后编译成功实际花费时间(取决于功能和软件包的数量):34 分钟。因为使用的是 WSL,存在性能损耗,因此实际编译速度应该更快一点。错误与解决(编译时)/ Error Solutions (Compiling)这里仅说明我遇到的问题,我没遇到的也不知道,也不能乱说。内核编译错误 / Error of Kernel Compiling这种错误通常是 Linux 内核文件在编译时发生错误,这时需要检查是否修改过内核源码文件和修补文件。如果修改过,则进行检查,检查修改过的文件语法、文件名是否有问题,更改的内容的语法是否有问题(比如是否缺少符号,“头”指定的新的行数是否与“块”中写入的相等)。例如:原来修补文件的“头”是:@@ -10,11 +12,15 @@ static struct example意思为从第十行删去11行,随后从第十二行添加15行。如果增加/删除了修补文件“块”中的任何一行,则上方代码中加粗的数字应当对应增加/减少行数。软件包编译错误 / Error of Package Compiling这种错误通常是:• 选中的软件包中的部分文件相互冲突。• 玄学问题。对于第 1 种问题,我们需要查看错误日志,找到对应提示的文件。例如提示(大概意思):“软件包:XXX 无法创建文件 ABC,因为 YYY 也提供了这个文件”。这个时候需要重新配置 OpenWrt,在冲突的软件包二选一。对于第 2 种问题,可能是下载的软件包源码文件有问题或莫名其妙出现这种情况我在用 Fedora 35 编译的时候会有莫名其妙的问题,但使用 Ubuntu 则一次性编译成功,所以请尽量使用 Ubuntu 系统进行编译。更好的方法是再次执行编译,大多时候这种问题就会消失,但如果还不消失,则需要执行下面的命令(每个命令我会用“#”符号在末尾注释)。rm -rf tmp/ # 清空make download的缓存(临时文件)rm -rf dl/【提示编译失败的软件包的名字】<TAB> # 删除无法编译的软件包源码,<TAB>表示按键盘的“TAB键”make download -j【CPU线程数】V=s # 重新下载缺少的软件包源码make -j1 V=sc # “单线程”编译,显示全部错误日志随后观察是否仍有错误,如果问题消失则可以按需求使用“Ctrl + C”终止后再次多线程编译。欢迎补充相关的编译问题。FAQ / Question and Answer• 软件包Q:为什么我没有添加某个软件包、库,在编译的时候自动开始下载编译?A:部分软件包编译时依赖于其他软件包、库和一些编程语言,这种情况使用提前下载源码文件的命令是无法下载的。如 AdGuardHome 软件包。其编译时依赖于 php、node.js、golang 等语言,因此编译过程中会自动下载这些语言的源码并编译。• 刷机Q:我定制好了固件,接下来怎么刷?A:对于路由器刷机,我们一般推荐使用下面的方案:刷入 Breed 固件,通过 Breed 固件刷入系统。Breed 固件可以保证路由器不论怎么刷机都不会刷坏。但如果在刷入 Breed 固件的过程操作失败,那么只能通过串口刷机救砖,如果厂家没有在路由器上提供串口端口,则只能通过编程器直刷闪存。因此风险与收益均较高。进入 Breed 固件,在控制台页面选择 Sysupgrade 固件刷入,重启。对于特殊设备,刷入方式不同,请自行查找确认设备为普通设备。本文末仅有少量特殊设备的刷入方案。其他 / OthersOpenWrt 的一些功能实现 / Feature realization(欢迎大家补充)• 更改默认的 IP 地址:OpenWrt 默认的 IP 地址是 192.168.1.1,但对于多个子网或多台路由器的网络来说这可能不是很好。使用命令:uci set network.lan.ipaddr=’【你要修改为的 IP 地址】’ && uci commit network然后重新启动路由器即可。OpenWrt 问题交流 / Communication首先请自行使用搜索引擎如 Google、Bing 等对你的问题进行搜索并查找解决方案,若搜索引擎没有解决方案或提供的解决方案不适用/无效,再去询问别人。关于询问别人:如何询问?在哪询问?除非明确知道对方是中国人,否则请使用英语交流(不会英语或英语不好的人请使用翻译软件,比你手打、自己编要好得多)。应尽可能简要的表述你的问题,如果可能还应告知对方你尝试了哪些解决方案(但没有用)。zui重要的一点:请有礼貌。别人没有责任和义务去帮助你同时别人也不是神仙,解决问题主要靠自己,并且非必要不要告诉别人你是哪个国家的,不然你或多或少都代表了一个国家的人,尽管你没有资格。这个问题其实很好理解,在别人不了解某个国家的人是怎样的,你就很容易给别人树立某个国家的人的形象。对于第三方软件包的问题,首先去它的讨论社区或 Issues,如 Github Issue,开发者提供的 IRC 频道或讨论组。如果没有讨论区域,再去 OpenWrt 的讨论社区询问,询问时应当告知别人你的问题是第三方软件包的问题。对于 OpenWrt 的问题,可以去 OpenWrt 提供的官方 IRC 频道页面 查找适合你的问题的频道。对于重大问题(如代码漏洞,BUG),可以在 OpenWrt 的 Github 源码仓库提交 Issue。(一般你也不知道是 BUG 还是你的问题,所以请直接去 IRC 频道询问)。特殊设备问题解决 / Special Device Problem Solutions小米路由 3G(R3G) / Xiaomi Mi Router 3G小米路由 3G 为双分区设备,因此安装步骤略微复杂。以提前刷入 Breed 固件为基础。进入 Breed 固件控制台页面,选择 OpenWrt 官方编译的 initramfs 固件,刷入。重启后,在 OpenWrt 的 LuCI Web 控制台页面,选择“系统 > 备份/升级”,随后在“刷写新的固件”选项中,选择 Sysupgrade 映像刷入。重启后直接进入 Breed 控制台页面,添加环境变量 xiaomi.r3g.bootfw 值为 2 (设置启动的分区为第二分区)重启即可。首先在 Breed 中刷入的固件被刷入第一分区,第一分区大小较小,无法容纳较大容量的固件,因此需要将第一分区刷入较小的 initramfs 固件(官方编译),由于自己定制的 OpenWrt 固件或多或少都会添加较多的软件包,因此大小较大,所以刷入后无法正常启动。进入 LuCI Web 控制台刷入的固件会被直接刷入第二分区,因此设置环境变量启动第二分区后重启即可。
以上教程由“WiFi之家网”整理收藏!
评论