编译 OpenWrt 官方固件

编译 OpenWrt 官方固件

编译 OpenWrt 官方固件
Sakisaki Lv1

示例环境

本文默认你能访问所有互联网环境。如果遇到任何问题,请搜索相关项目文档以及Google。

  • OpenWrt版本: 23.05.5
  • 目标设备: TP-Link TL-XDR6088
  • 编译系统: ArchLinux
  • 磁盘: 至少 20G
  • 内存: 推荐 16G 以上
  • CPU: 越快越好

环境搭建

准备工作

首先,在 https://github.com/openwrt/openwrt 中找到你需要构建的版本,如 v23.05.5。然后替换以下命令中的 <tag> 为你需要的版本号。

1
$export OPENWRT_VER=<tag>

然后在 https://openwrt.org/toh/start 中搜索你的设备型号,点击搜索结果后的 Edit 进入详细页面。找到 Dataentry 中的 TargetSubtarget 替换到下面的命令中。

1
2
$export OPENWRT_TARGET=<Target>
$export OPENWRT_SUBTARGET=<Subtarget>

确保你设置的变量都正确无误,后面会频繁用到。

拉取源码

1
2
$git clone -b $OPENWRT_VER --single-branch https://www.github.com/openwrt/openwrt
$cd openwrt

或者拉取整个仓库

1
2
3
$git clone https://github.com/openwrt/openwrt
$cd openwrt
$git checkout $OPENWRT_VER

之后的所有操作都在 openwrt 目录下进行。

Docker 环境搭建

如果你不希望在本机上编译固件,那么可以选择使用 Docker 环境。否则请跳过本节,从本机环境搭建开始。
关于 Docker 和 docker-compose 的一切内容,请参考官方文档

安装 Docker

1
2
3
4
5
6
7
8
# 安装
$sudo pacman -S docker

# 启动 docker
$sudo systemctl enable --now docker

# 将当前用户加入到 docker 组
$sudo usermod -aG docker $USER

Dockerfile

官方提供用来编译固件的镜像不太好用,所以我们来自己构建一个。在 openwrt 目录下创建一个 Dockerfile 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
FROM ubuntu:24.10

RUN apt-get update && apt-get install -y \
vim sed git build-essential clang flex bison g++ gawk \
gcc-multilib g++-multilib gettext git libncurses5-dev libssl-dev \
python3-setuptools rsync swig unzip zlib1g-dev file wget \
apt clean && rm -rf /var/lib/apt/lists/*

WORKDIR /openwrt

CMD ["/bin/bash"]

编译镜像并启动

1
2
3
4
5
6
7
$docker build -t openwrt.

$docker run --rm -it openwrt \
-v "$(pwd):/openwrt"
-e OPENWRT_VER="$OPENWRT_VER"
-e OPENWRT_TARGET="$OPENWRT_TARGET"
-e OPENWRT_SUBTARGET="$OPENWRT_SUBTARGET"

此时你已经进入容器了,执行 ./setup.sh 拉取源码,并执行 apt-get update && apt-get install vim 安装编辑器。之后的步骤参考下面的本机环境搭建。

本机环境搭建

本文示例环境为 Archlinux,所以默认你拥有 Linux 系统。
如果你是 Windows 用户,参考这个 文档安装 WSL,并按照此文档进行额外设置。
如果你是 MacOs 用户,请参考此处部署构建环境。

安装依赖

Build system setup 中找到自己系统对应的发行版依赖设置,按照文档中的说明安装即可。

这里以 Arch/Manjaro/EndeavourOS 为例,如果你安装了 yayparu 可以直接从 AUR 中安装 openwrt-devel 包:

1
2
3
$yay -S openwrt-devel
# 或
$paru -S openwrt-devel

或者手动安装所有依赖

1
2
3
4
5
6
7
8
9
# 必选依赖项
$pacman -Syu
$pacman -S --needed base-devel autoconf automake bash binutils bison \
bzip2 fakeroot file findutils flex gawk gcc gettext git grep groff \
gzip libelf libtool libxslt m4 make ncurses openssl patch pkgconf \
python python-distutils-extra rsync sed texinfo time unzip util-linux wget which zlib

# 某些软件包需要安装如下依赖
$pacman -S --needed asciidoc help2man intltool perl-extutils-makemaker swig

添加并更新软件包

使用 feeds.conf.default

定义软件包的文件是feeds.conf.default。通常你还需要添加一些额外的软件包,比如kenzok8。找到你希望添加的仓库,然后执行

1
2
$echo 'src-git kenzo https://github.com/kenzok8/openwrt-packages feeds.conf.default
src-git small https://github.com/kenzok8/small feeds.conf.default' >> feeds.conf.default

手动拉取

你也可以使用 git clone 命令手动拉取软件包到 package 目录下。

1
2
$git clone https://github.com/kenzok8/openwrt-packages package
$git clone https://github.com/kenzok8/small package

更新软件包

在每次对软件包进行修改后,都需要对其进行更新。

1
2
$./scripts/feeds update -a
$./scripts/feeds install -a

可能的 Golang 版本问题

在选择部分软件包后,编译时可能会导致 Go 版本冲突,提示类似如下:

1
go: .... requires go >= 1.22 (running go 1.21.5; GOTOOLCHAIN=local)

此时需要在更新软件包后手动拉取符合条件的 Go 版本:

1
2
$rm -rf feeds/packages/lang/golang
$git clone https://github.com/sbwml/packages_lang_golang -b 22.x feeds/packages/lang/golang

修改源码

vermagic

自己编译的固件 vermagic 很可能和官方的不一致,此时 OpenWrt 无法使用 opkg install
安装任何包。所以我们需要手动获取 vermagic 值。

1
$curl -s https://downloads.openwrt.org/releases/${OPENWRT_VER#v}/targets/$OPENWRT_TARGET/$OPENWRT_SUBTARGET/openwrt-${OPENWRT_VER#v}-$OPENWRT_TARGET-$OPENWRT_SUBTARGET.manifest | grep kernel | awk '{print $3}' | awk -F- '{print $3}' > vermagic

手动获取 vermagic 值的方式见此处

修改 vermagic 获取方式

我们还需要修改源码中 vermagic 的获取方式,让他设置为我们刚刚获取到的 vermagic 值。

1
2
3
4
5
6
7
# 编辑配置文件
$vim include/kernel-defaults.mk +121

# 找到这一行,然后注释掉!
grep '=[ym]' $(LINUX_DIR)/.config.set | LC_ALL=C sort | $(MKHASH) md5 > $(LINUX_DIR)/.vermagic
# 再在下一行添加如下内容
cp $(TOPDIR)/vermagic $(LINUX_DIR)/.vermagic

修改后的代码如下所示

1
2
# grep '=[ym]' $(LINUX_DIR)/.config.set | LC_ALL=C sort | $(MKHASH) md5 > $(LINUX_DIR)/.vermagic
cp $(TOPDIR)/vermagic $(LINUX_DIR)/.vermagic

修改时区

1
2
3
4
5
6
7
# 编辑配置文件
$vim package/base-files/files/bin/config_generate +315

# 修改时区为 CST-8
set system.@system[-1].timezone='CST-8'
# 再在下一行添加如下内容
set system.@system[-1].zonename='Asia/Shanghai'

修改后的代码如下所示

1
2
3
4
5
6
set system.@system[-1].hostname='OpenWrt'
set system.@system[-1].timezone='CST-8'
set system.@system[-1].zonename='Asia/Shanghai
set system.@system[-1].ttylogin='0'
set system.@system[-1].log_size='64'
set system.@system[-1].urandom_seed='0'

修改IP

1
2
3
4
5
6
# 设置一个自己希望的IP地址
$export IPADDR=192.168.0.1
$sed -i 's/ipad=\${ipaddr:-"192.168.1.1"}/ipad=\${ipaddr:-"'$IPADDR'"}/' package/base-files/files/bin/config_generate

# 也可以使用编辑器
$vim package/base-files/files/bin/config_generate +165

配置和编译

如果你不确定一个模块的作用,那么就不要取消任何默认选中的模块

OpenWrt 的编译配置比较复杂,需要你了解你当前设备所需要的软件包,不过你也可以直接使用官方配置并在此基础上进行修改。

1
2
# 拉取官方配置
$wget https://downloads.openwrt.org/releases/${OPENWRT_VER#v}/targets/$OPENWRT_TARGET/$OPENWRT_SUBTARGET/config.buildinfo -O .config

在拉取了官方配置后,此时你就拥有了和官方一致的配置文件,此时直接编译就能得到和官方一致的固件。
但是官方配置中,默认情况下会编译当前架构下所有设备的固件,在大多数情况下我们只需要编译我们拥有的设备的固件。此外,你可能还需要添加一些额外的软件包。

使用菜单进行配置

执行以下命令打开配置菜单。

1
make menuconfig

菜单中的操作如下

按键 功能
↑|↓ 切换模块
Y 选中模块
N 取消选中模块
M 选中模块但是不编译进内核,选中的模块会单独编译
/ 搜索模块
←|→ 切换下方子菜单
Enter 选择子菜单
ESC``ESC 退出
? 帮助

模块的选中状态如下,模块前面的括号可能是 [ ]{ }< >

1
2
3
[ ] 被排除的模块
[*] 选中的模块
[M] 选中但不编译进内核

注意 { } 的模块只能选择是否被编译进内核,不能取消。

选择具体设备

之前说过,OpenWrt 官方的默认配置会编译当前架构下的所有设备的固件,因此我们需要在 Target Profile 中选择自己的设备型号。

LuCI

LuCI是一个OpenWrt的Web框架,大部分常用工具都有更便捷的LuCI版本,让我们通过Web网页就能完成大部分设置。
如果你需要使用LuCI的包,那么我还推荐你选择这些模块:

1
2
3
4
5
6
7
8
LuCI
├─ Collections
│ ├─ luci # 核心包
│ └─ luci-ssl # SSL支持
└─ Modules
├─ Translations
│ └─ Chinese Simplified (zh_Hans) (NEW) # 中文语言包
└─ luci-comppat # 兼容性包

之后就可以在 ApplicationsThemes 中选择喜欢的主题和应用。

其他常用包

以下包是我在 TL-XDR6088 上常用的软件包,可以按需选择。注意官方配置中有些模块是不被编译进内核的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Base system
├─ block-mount # 挂载块设备
├─ dnsmasq-full # 将默认的dnsmasq设置为{M}
├─ resolveip # 解析域名
├─ swconfig # 端口配置
└─ zram-swap # swap分区
Administration
└─ btop # 系统监控
Kernel modules
├─ Filesystems
│ ├─ kmod-fs-exfat
│ ├─ kmod-fs-ext4
│ └─ kmod-fs-vfat
├─ Network Extensions
│ └─ kmod-nft-tproxy # 透明代理
├─ Network Support
│ ├─ kmod-tun
│ └─ kmod-tcp-bbr
└─ USB Support
└─ kmof-usb-storage # 支持USB存储设备
Network
├─ File Transfer
│ ├─ curl
│ └─ wget
├─ Routing and Redirection
│ ├─ ss # 连接检查
├─ SSH
│ └─ openssh-sftp-server # SSH服务
├─ ds-lite # IPv6隧道
└─ tcpdump # 抓包
Utilities
├─ Compression
│ ├─ gzip
│ └─ unzip
├─ Disc
│ ├─ blkid # 磁盘信息
│ ├─ fdisk # 分区管理
│ └─ lsblk # 磁盘信息
├─ Editors
│ └─ vim
├─ Filesystems
│ ├─ dosfstools
│ ├─ e2fsprogs
│ └─ exfat-mkfs
└─ Shells
└─ bash

编译

如果你在编译中遇到了错误,那么你最好使用 make clean 来清理编译环境,然后重新编译。
因为遇到错误时,很可能会出现一些编译到一半的文件,在下次编译时就会认为这些文件已经被编译过了。

在编译之前,我们最好先下载所有依赖的源码,这能提升编译的成功率。

1
2
3
4
5
6
7
8
# 进行单核下载
$make download

# 使用 5 个核心进行下载
$make download -j5

# 使用全部核心下载
$make download -j$(nproc)

在下载完成后,我们就可以开始编译了。和下载类似,编译也支持多核编译。

1
$make -j$(nproc) V=s

如果你不希望编译时占用过多的资源,那么可以参考如下命令让其只使用4和核心和空闲资源进行编译(注意,这可能会让构建时间变得非常长)。

1
2
# 将编译输出到 build.log,并且输出包含 error 的日志到屏幕。
$ionice -c 3 nice -n 20 make -j 4 V=s 2>&1 | tee build.log

推荐优先使用多核编译,但是如果在编译过程中遇到问题,并且不能通过日志来确定错误原因,那么最简单的方式是使用单核编译(注意,这可能会让构建时间长达6小时以上)。

1
$make V=s

在编译完成后,在 bin/targets/<target>/<subtarget> 下就能找到编译好的固件。

参考

OpenWrt 编译说明
OpenWrt Snapshots 编译 xray-core & xray-plugin 1.8.8 提示需要 golang 1.22.0 版本的解决方法
使用 OpenWrt 23.05.5 官网源码编译固件
Docker OpenWrt Image Generation
Quick image building guide
Build system essentials
Build system usage

评论