Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c776e2379 | |||
| af91d55799 | |||
| 503fd4036e | |||
| 4625e6e182 | |||
| e5e693a35b | |||
| ca0de3ed66 | |||
| 69f9855b95 | |||
| 788cf4433a | |||
| a408db2cc5 | |||
| 82813ea218 | |||
| 40c631166a | |||
| 76015e9cb5 | |||
| 12be6f6244 | |||
| 603192df10 | |||
| 41832801d8 | |||
| 143b49bfa4 | |||
| f4e3483f9d | |||
| 9ce3fe51e1 | |||
| a643e74a70 | |||
| 592094be82 | |||
| 7ab6aefbb1 | |||
| 05da3a4d57 | |||
| 09f1e7d567 | |||
| 24e2f7188d | |||
| 3c860fc7e6 | |||
| 502113f2a6 | |||
| 5deda07401 | |||
| c03f61acfb | |||
| 3ffeb904bb | |||
| 60e539a694 | |||
| 6e4c2f37c5 | |||
| bf0d9d2db9 | |||
| 99091363c7 | |||
| ffee41eabc | |||
| c55f0c6f83 | |||
| 073955ce4a |
@@ -1,43 +1,188 @@
|
||||
# ImmortalWrt Image Builder
|
||||
|
||||
这是一个用于快速构建 ImmortalWrt 固件的自动化项目,基于 ImmortalWrt 官方 Image Builder 生成 x86_64 平台固件。它适合用于定制个人或家庭网络环境中的 OpenWrt/ImmortalWrt 系统.
|
||||
这是一个用于快速构建 ImmortalWrt 固件的自动化项目,基于 ImmortalWrt 官方 Image Builder 生成自定义固件。
|
||||
|
||||
**目前只支持 x86-64 固件构建**
|
||||
当前仓库提供多个构建脚本:
|
||||
|
||||
## 使用说明
|
||||
- `x86-64.sh`:构建 x86/64 通用固件
|
||||
- `r2s.sh`:构建 FriendlyARM NanoPi R2S 固件
|
||||
- `hiwifi-b70.sh`:构建 极路由 B70 固件
|
||||
- `newifi3-d2.sh`:构建 新三路由 固件
|
||||
|
||||
执行构建:
|
||||
脚本会自动安装构建依赖、下载对应版本的 Image Builder、复制 `files/` 目录中的自定义文件,并执行固件构建。
|
||||
|
||||
固件的默认登录地址是 `192.168.1.1`. 登录账号: `root`. 密码: `无`
|
||||
|
||||
## 环境要求
|
||||
|
||||
建议在 Debian/Ubuntu 或 WSL Ubuntu 环境中运行。
|
||||
|
||||
最好找一个独立的环境执行代码.
|
||||
|
||||
官方说最好找一个非 root 用户执行代码. 怕是 root 权限太大, 怕出现误删文件的情况.
|
||||
|
||||
## 使用方法
|
||||
|
||||
克隆本仓库:
|
||||
|
||||
```bash
|
||||
bash main.sh
|
||||
git clone https://gitea.9001003.xyz/William/immortalwrt_image_builder.git
|
||||
cd immortalwrt_image_builder
|
||||
```
|
||||
|
||||
构建完成后,固件文件会生成在 `output/` 目录中。
|
||||
### 构建 x86-64 固件
|
||||
|
||||
```bash
|
||||
bash x86-64.sh
|
||||
```
|
||||
|
||||
## 其他问题
|
||||
- 如何修改构建固件的版本?
|
||||
构建完成后,固件文件会生成到:
|
||||
|
||||
在 `main.sh` 中修改 `version` 变量:
|
||||
```text
|
||||
x86-64_output/
|
||||
```
|
||||
|
||||
```bash
|
||||
version="25.12.0"
|
||||
```
|
||||
### 构建 NanoPi R2S 固件
|
||||
|
||||
例如要构建其他版本,就把版本号改成 ImmortalWrt 官方下载站中存在的版本:
|
||||
```bash
|
||||
bash r2s.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
version="24.10.2"
|
||||
```
|
||||
构建完成后,固件文件会生成到:
|
||||
|
||||
保存后重新执行:
|
||||
```text
|
||||
r2s_output/
|
||||
```
|
||||
|
||||
```bash
|
||||
bash main.sh
|
||||
```
|
||||
### 构建 新三路由 wifi3-d2 固件
|
||||
|
||||
脚本会根据该版本号下载对应的 x86-64 Image Builder:
|
||||
```bash
|
||||
bash newifi3_d2.sh
|
||||
```
|
||||
|
||||
```text
|
||||
https://downloads.immortalwrt.org/releases/<version>/targets/x86/64/
|
||||
```
|
||||
构建完成后,固件文件会生成到:
|
||||
|
||||
```text
|
||||
newifi3_output/
|
||||
```
|
||||
|
||||
### 构建 极路由 B70 固件
|
||||
|
||||
```bash
|
||||
bash hiwifi-b70.sh
|
||||
```
|
||||
|
||||
构建完成后,固件文件会生成到:
|
||||
|
||||
```text
|
||||
hiwifi_b70_output/
|
||||
```
|
||||
|
||||
如果输出目录已经存在,脚本会先将旧目录备份为带时间戳的 `.bak` 目录,再移动新的构建结果。
|
||||
|
||||
## 默认配置
|
||||
|
||||
### x86-64
|
||||
|
||||
- ImmortalWrt 版本:`25.12.1`
|
||||
- Target:`x86/64`
|
||||
- Profile:`generic`
|
||||
- 根分区大小:`600 MB`
|
||||
- 输出目录:`output/`
|
||||
|
||||
### NanoPi R2S
|
||||
|
||||
- ImmortalWrt 版本:`25.12.1`
|
||||
- Target:`rockchip/armv8`
|
||||
- Profile:`friendlyarm_nanopi-r2s`
|
||||
- 根分区大小:`600 MB`
|
||||
- 输出目录:`r2s_output/`
|
||||
|
||||
### 新三路由(Newifi3 D2)
|
||||
|
||||
- ImmortalWrt 版本:`25.12.1`
|
||||
- Target:`ramips/mt7621`
|
||||
- Profile:`d-team_newifi-d2`
|
||||
- 输出目录:`newifi3_output/`
|
||||
> mt7621(newifi3-d2)构建已移除 rootfs_partsize 参数,使用 ImageBuilder 默认分区策略。
|
||||
|
||||
### 极路由 B70
|
||||
|
||||
- ImmortalWrt 版本:`25.12.1`
|
||||
- Target:`ramips/mt7621`
|
||||
- Profile:`hiwifi_hc5962`
|
||||
- 输出目录:`newifi3_output/`
|
||||
|
||||
## 自定义固件
|
||||
|
||||
### ImmortalWrt 版本
|
||||
|
||||
脚本在运行时会询问用户 ImmortalWrt 版本. 不知道用什么版本的, 直接默认版本就好.
|
||||
|
||||
版本查询 https://github.com/immortalwrt/immortalwrt/tags
|
||||
|
||||
### 修改预装软件包
|
||||
|
||||
编辑对应脚本中 `build()` 函数里的 `PKG_LIST` 数组。
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
PKG_LIST=(
|
||||
openssh-sftp-server
|
||||
vim-full
|
||||
curl
|
||||
luci-i18n-firewall-zh-cn
|
||||
)
|
||||
```
|
||||
|
||||
如果要从固件中移除某个包,可以在包名前加 `-`,例如:
|
||||
|
||||
```bash
|
||||
PKG_LIST=(
|
||||
-dropbear # 排除 dropbear
|
||||
openssh-sftp-server
|
||||
luci-i18n-firewall-zh-cn
|
||||
)
|
||||
```
|
||||
|
||||
### 添加自定义文件
|
||||
|
||||
仓库中的 `files/` 目录会被复制到固件根文件系统。
|
||||
|
||||
例如:
|
||||
|
||||
```text
|
||||
files/etc/uci-defaults/99-sshd-root-login
|
||||
```
|
||||
|
||||
构建后会出现在固件中的:
|
||||
|
||||
```text
|
||||
/etc/uci-defaults/99-sshd-root-login
|
||||
```
|
||||
|
||||
这个机制适合放置默认配置、初始化脚本、SSH 配置、LuCI 配置等文件。
|
||||
|
||||
## 关于开发
|
||||
|
||||
如果需要增加新设备固件. 你可以用以下`提示词`和`材料`喂给 AI
|
||||
|
||||
#### 材料:
|
||||
|
||||
- 代码模板: 找项目其中一个构建脚本作为模板, 比如: x86-64.sh
|
||||
- ImmortalWrt image builder 下载地址: 其中下载地址可以在[https://downloads.immortalwrt.org/](https://downloads.immortalwrt.org/)找. 名字以x86-64为例,叫这个`immortalwrt-imagebuilder-25.12.1-x86-64.Linux-x86_64.tar.zst`
|
||||
- 设备系统架构: 如果设备已安装了`openwrt`系统, 可以通过命令`cat /etc/os-release | grep ARCH`查找设备系统架构信息.
|
||||
|
||||
#### AI 提示词:
|
||||
|
||||
```
|
||||
image builder的下载地址: xxxx
|
||||
|
||||
目标设备型号: xxx
|
||||
|
||||
设备系统架构是: xxx
|
||||
|
||||
我想构建xxx设备固件, 请你按这个代码模板编写代码.
|
||||
|
||||
```
|
||||
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 仅在首次启动时执行(/etc/uci-defaults机制)
|
||||
|
||||
# 获取当前 OpenWrt/ImmortalWrt 的主版本号
|
||||
get_openwrt_version() {
|
||||
local version=""
|
||||
if [ -f /etc/openwrt_release ]; then
|
||||
# 提取形如 25.12 或 23.05 的前两位数字版本
|
||||
version=$(grep "DISTRIB_RELEASE" /etc/openwrt_release | cut -d"'" -f2 | cut -d'.' -f1,2)
|
||||
fi
|
||||
echo "$version"
|
||||
}
|
||||
|
||||
# 比较版本号是否大于或等于 25.12
|
||||
# 返回 0 表示 >= 25.12,返回 1 表示较旧版本
|
||||
is_version_25_12_or_newer() {
|
||||
local current_ver=$(get_openwrt_version)
|
||||
|
||||
# 如果没获取到版本号,保守起见当做旧版本处理
|
||||
[ -z "$current_ver" ] && return 1
|
||||
|
||||
# 使用 awk 进行浮点数版本比较
|
||||
awk -v cur="$current_ver" 'BEGIN { if (cur >= 25.12) exit 0; else exit 1 }'
|
||||
}
|
||||
|
||||
# 修改 LAN 口 IP 地址
|
||||
update_lan_ip(){
|
||||
local NEW_LAN_IP="$1"
|
||||
|
||||
# 检查 network 配置是否存在
|
||||
if [ -f /etc/config/network ]; then
|
||||
|
||||
# 检查 lan 接口是否存在
|
||||
if uci get network.lan >/dev/null 2>&1; then
|
||||
|
||||
# 设置为静态 IP 协议
|
||||
uci set network.lan.proto='static'
|
||||
|
||||
# 根据版本号执行不同的设置逻辑
|
||||
if is_version_25_12_or_newer; then
|
||||
# 25.12+ 版本:合并为 CIDR 格式,并清除可能残留的旧 netmask
|
||||
uci set network.lan.ipaddr="${NEW_LAN_IP}/24"
|
||||
uci del network.lan.netmask >/dev/null 2>&1
|
||||
else
|
||||
# 25.12 以下旧版本:拆分为 ipaddr 和 netmask
|
||||
uci set network.lan.ipaddr="${NEW_LAN_IP}"
|
||||
uci set network.lan.netmask="255.255.255.0"
|
||||
fi
|
||||
|
||||
# 保存配置
|
||||
uci commit network
|
||||
fi
|
||||
fi
|
||||
|
||||
# 重启网络服务
|
||||
if [ -x /etc/init.d/network ]; then
|
||||
/etc/init.d/network restart
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# update_lan_ip "192.168.50.100" 修改lan口ip
|
||||
|
||||
exit 0
|
||||
Regular → Executable
+5
@@ -20,8 +20,13 @@ if [ -f "$SSHD_CONF" ]; then
|
||||
else
|
||||
echo "PasswordAuthentication yes" >> "$SSHD_CONF"
|
||||
fi
|
||||
|
||||
# 创建root用户ssh公钥目录和文件
|
||||
mkdir -p /root/.ssh
|
||||
touch /root/.ssh/authorized_keys
|
||||
fi
|
||||
|
||||
|
||||
# 重启 OpenSSH 服务(如果已安装)
|
||||
if [ -x /etc/init.d/sshd ]; then
|
||||
/etc/init.d/sshd enable
|
||||
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
install_dependencies() {
|
||||
sudo apt install -y build-essential libncurses-dev zlib1g-dev gawk git \
|
||||
gettext libssl-dev xsltproc rsync wget unzip python3 qemu-utils mkisofs zstd
|
||||
}
|
||||
|
||||
download_imagebuilder() {
|
||||
local version="$1"
|
||||
local target="ramips/mt7621"
|
||||
|
||||
local file="immortalwrt-imagebuilder-$version-ramips-mt7621.Linux-x86_64.tar.zst"
|
||||
local url="https://downloads.immortalwrt.org/releases/$version/targets/ramips/mt7621/$file"
|
||||
|
||||
local folder="immortalwrt-imagebuilder-$version-ramips-mt7621.Linux-x86_64"
|
||||
local marker="$folder/.ready"
|
||||
|
||||
if [ -f "$marker" ]; then
|
||||
echo "$(pwd)/$folder"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -d "$folder" ] && [ ! -f "$marker" ]; then
|
||||
rm -rf "$folder"
|
||||
fi
|
||||
|
||||
wget -O "$file" "$url" >&2
|
||||
tar -I zstd -xvf "$file" >&2
|
||||
|
||||
touch "$marker"
|
||||
echo "$(pwd)/$folder"
|
||||
}
|
||||
|
||||
build() {
|
||||
# =============================================================
|
||||
# 设备 Profile(HiWiFi HC5962 / 极路由B70)
|
||||
# =============================================================
|
||||
PROFILE="hiwifi_hc5962"
|
||||
|
||||
# =============================================================
|
||||
# 自定义文件目录(映射到固件 rootfs)
|
||||
# =============================================================
|
||||
FILES_DIR="files"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
BUILDER_DIR="$1"
|
||||
|
||||
# =============================================================
|
||||
# 固件输出目录
|
||||
# =============================================================
|
||||
OUTPUT_DIR="$BUILDER_DIR/hiwifi_b70_output"
|
||||
PROJECT_OUTPUT_DIR="$SCRIPT_DIR/hiwifi_b70_output"
|
||||
|
||||
# =============================================================
|
||||
# 固件名称标识
|
||||
# =============================================================
|
||||
EXTRA_IMAGE_NAME="hc5962"
|
||||
|
||||
# =============================================================
|
||||
# 插件包列表(按需增删)
|
||||
# =============================================================
|
||||
PKG_LIST=(
|
||||
vim
|
||||
nano-full
|
||||
openssh-sftp-server
|
||||
luci-i18n-vlmcsd-zh-cn
|
||||
luci-i18n-argon-config-zh-cn
|
||||
luci-i18n-arpbind-zh-cn
|
||||
luci-i18n-package-manager-zh-cn
|
||||
|
||||
# 代理插件
|
||||
luci-i18n-homeproxy-zh-cn
|
||||
sing-box
|
||||
)
|
||||
|
||||
PACKAGES=$(printf "%s " "${PKG_LIST[@]}")
|
||||
|
||||
# =============================================================
|
||||
# 颜色输出
|
||||
# =============================================================
|
||||
GREEN="\033[0;32m"
|
||||
YELLOW="\033[1;33m"
|
||||
RED="\033[0;31m"
|
||||
NC="\033[0m"
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} ImmortalWrt Image Builder 开始构建${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e " Device : HiWiFi HC5962 (B70)"
|
||||
echo -e " Target : ramips/mt7621"
|
||||
echo -e " Profile : ${PROFILE}"
|
||||
echo -e " Output : ${OUTPUT_DIR}"
|
||||
echo ""
|
||||
|
||||
cd "$BUILDER_DIR"
|
||||
|
||||
mkdir -p "$FILES_DIR"
|
||||
|
||||
# =============================================================
|
||||
# 复制自定义 files 目录(如果存在)
|
||||
# =============================================================
|
||||
if [ -d "${SCRIPT_DIR}/files" ]; then
|
||||
cp -rf "${SCRIPT_DIR}/files/." "$BUILDER_DIR/files/"
|
||||
fi
|
||||
|
||||
# =============================================================
|
||||
# 校验 ImageBuilder 环境
|
||||
# =============================================================
|
||||
if [ ! -f "Makefile" ]; then
|
||||
echo -e "${RED}未找到 Makefile,请在 ImageBuilder 目录运行${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# =============================================================
|
||||
# 开始构建固件
|
||||
# =============================================================
|
||||
make image \
|
||||
PROFILE="$PROFILE" \
|
||||
PACKAGES="$PACKAGES" \
|
||||
FILES="$FILES_DIR" \
|
||||
BIN_DIR="$OUTPUT_DIR" \
|
||||
EXTRA_IMAGE_NAME="$EXTRA_IMAGE_NAME"
|
||||
|
||||
# =============================================================
|
||||
# 输出目录整理
|
||||
# =============================================================
|
||||
if [ "$OUTPUT_DIR" != "$PROJECT_OUTPUT_DIR" ]; then
|
||||
if [ -e "$PROJECT_OUTPUT_DIR" ]; then
|
||||
BACKUP="${PROJECT_OUTPUT_DIR}.bak.$(date +%Y%m%d%H%M%S)"
|
||||
mv "$PROJECT_OUTPUT_DIR" "$BACKUP"
|
||||
fi
|
||||
|
||||
mv "$OUTPUT_DIR" "$PROJECT_OUTPUT_DIR"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}构建完成${NC}"
|
||||
echo -e "固件输出: ${PROJECT_OUTPUT_DIR}"
|
||||
}
|
||||
|
||||
# =============================================================
|
||||
# 交互输入函数
|
||||
# 参数1: 提示信息
|
||||
# 参数2: 默认值
|
||||
# 返回: 用户输入值,直接回车返回默认值
|
||||
# =============================================================
|
||||
ask_input() {
|
||||
local prompt="$1"
|
||||
local default="$2"
|
||||
|
||||
read -p "${prompt} [ 默认: ${default} ]: " value
|
||||
|
||||
if [ -z "$value" ]; then
|
||||
echo "$default"
|
||||
else
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
project_dir="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$project_dir"
|
||||
|
||||
# 要构建的 ImmortalWrt 版本, 可以在 https://github.com/immortalwrt/immortalwrt 查询版本号
|
||||
default_version="25.12.1"
|
||||
version=$(ask_input "请输入 ImmortalWrt 版本" "$default_version")
|
||||
|
||||
install_dependencies
|
||||
|
||||
builder_dir="$(download_imagebuilder "$version")"
|
||||
|
||||
build "$builder_dir"
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
install_dependencies() {
|
||||
sudo apt install -y build-essential libncurses-dev zlib1g-dev gawk git \
|
||||
gettext libssl-dev xsltproc rsync wget unzip python3 qemu-utils mkisofs zstd
|
||||
}
|
||||
|
||||
download_imagebuilder() {
|
||||
local version="$1"
|
||||
local target="ramips/mt7621"
|
||||
|
||||
local file="immortalwrt-imagebuilder-$version-ramips-mt7621.Linux-x86_64.tar.zst"
|
||||
local url="https://downloads.immortalwrt.org/releases/$version/targets/ramips/mt7621/$file"
|
||||
|
||||
local folder="immortalwrt-imagebuilder-$version-ramips-mt7621.Linux-x86_64"
|
||||
local marker="$folder/.ready"
|
||||
|
||||
if [ -f "$marker" ]; then
|
||||
echo "$(pwd)/$folder"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -d "$folder" ] && [ ! -f "$marker" ]; then
|
||||
rm -rf "$folder"
|
||||
fi
|
||||
|
||||
wget -O "$file" "$url" >&2
|
||||
tar -I zstd -xvf "$file" >&2
|
||||
|
||||
touch "$marker"
|
||||
echo "$(pwd)/$folder"
|
||||
}
|
||||
|
||||
# =============================================================
|
||||
# 交互输入函数
|
||||
# 参数1: 提示信息
|
||||
# 参数2: 默认值
|
||||
# 返回: 用户输入值,直接回车返回默认值
|
||||
# =============================================================
|
||||
ask_input() {
|
||||
local prompt="$1"
|
||||
local default="$2"
|
||||
|
||||
read -p "${prompt} [ 默认: ${default} ]: " value
|
||||
|
||||
if [ -z "$value" ]; then
|
||||
echo "$default"
|
||||
else
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
build() {
|
||||
# =============================================================
|
||||
# 设备 Profile(必须与 ImageBuilder 支持的设备一致)
|
||||
# =============================================================
|
||||
PROFILE="d-team_newifi-d2"
|
||||
|
||||
# =============================================================
|
||||
# 自定义文件目录(映射到固件 rootfs)
|
||||
# =============================================================
|
||||
FILES_DIR="files"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
BUILDER_DIR="$1"
|
||||
|
||||
# =============================================================
|
||||
# 固件输出目录
|
||||
# =============================================================
|
||||
OUTPUT_DIR="$BUILDER_DIR/newifi3_output"
|
||||
PROJECT_OUTPUT_DIR="$SCRIPT_DIR/newifi3_output"
|
||||
|
||||
# =============================================================
|
||||
# 固件名称标识
|
||||
# =============================================================
|
||||
EXTRA_IMAGE_NAME="newifi3"
|
||||
|
||||
# =============================================================
|
||||
# 插件包列表(按需增删)
|
||||
# =============================================================
|
||||
PKG_LIST=(
|
||||
vim
|
||||
nano-full
|
||||
openssh-sftp-server
|
||||
luci-i18n-vlmcsd-zh-cn
|
||||
luci-i18n-argon-config-zh-cn
|
||||
luci-i18n-arpbind-zh-cn
|
||||
luci-i18n-package-manager-zh-cn
|
||||
|
||||
# 代理插件
|
||||
luci-i18n-homeproxy-zh-cn
|
||||
sing-box
|
||||
)
|
||||
|
||||
PACKAGES=$(printf "%s " "${PKG_LIST[@]}")
|
||||
|
||||
# =============================================================
|
||||
# 颜色输出
|
||||
# =============================================================
|
||||
GREEN="\033[0;32m"
|
||||
YELLOW="\033[1;33m"
|
||||
RED="\033[0;31m"
|
||||
NC="\033[0m"
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} ImmortalWrt Image Builder 开始构建${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e " Device : Newifi D2"
|
||||
echo -e " Target : ramips/mt7621"
|
||||
echo -e " Profile : ${PROFILE}"
|
||||
echo -e " Output : ${OUTPUT_DIR}"
|
||||
echo ""
|
||||
|
||||
cd "$BUILDER_DIR"
|
||||
|
||||
mkdir -p "$FILES_DIR"
|
||||
|
||||
# =============================================================
|
||||
# 复制自定义 files 目录(如果存在)
|
||||
# =============================================================
|
||||
if [ -d "${SCRIPT_DIR}/files" ]; then
|
||||
cp -rf "${SCRIPT_DIR}/files/." "$BUILDER_DIR/files/"
|
||||
fi
|
||||
|
||||
# =============================================================
|
||||
# 校验 ImageBuilder 环境
|
||||
# =============================================================
|
||||
if [ ! -f "Makefile" ]; then
|
||||
echo -e "${RED}未找到 Makefile,请在 ImageBuilder 目录运行${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# =============================================================
|
||||
# 开始构建固件
|
||||
# =============================================================
|
||||
make image \
|
||||
PROFILE="$PROFILE" \
|
||||
PACKAGES="$PACKAGES" \
|
||||
FILES="$FILES_DIR" \
|
||||
BIN_DIR="$OUTPUT_DIR" \
|
||||
EXTRA_IMAGE_NAME="$EXTRA_IMAGE_NAME"
|
||||
|
||||
# =============================================================
|
||||
# 输出目录整理
|
||||
# =============================================================
|
||||
if [ "$OUTPUT_DIR" != "$PROJECT_OUTPUT_DIR" ]; then
|
||||
if [ -e "$PROJECT_OUTPUT_DIR" ]; then
|
||||
BACKUP="${PROJECT_OUTPUT_DIR}.bak.$(date +%Y%m%d%H%M%S)"
|
||||
mv "$PROJECT_OUTPUT_DIR" "$BACKUP"
|
||||
fi
|
||||
|
||||
mv "$OUTPUT_DIR" "$PROJECT_OUTPUT_DIR"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}构建完成${NC}"
|
||||
echo -e "固件输出: ${PROJECT_OUTPUT_DIR}"
|
||||
}
|
||||
|
||||
|
||||
project_dir="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$project_dir"
|
||||
|
||||
# 要构建的 ImmortalWrt 版本, 可以在 https://github.com/immortalwrt/immortalwrt 查询版本号
|
||||
default_version="25.12.1"
|
||||
version=$(ask_input "请输入 ImmortalWrt 版本" "$default_version")
|
||||
|
||||
# 安装依赖
|
||||
install_dependencies
|
||||
|
||||
# 下载并解压 Image Builder,返回解压后的目录路径
|
||||
builder_dir="$(download_imagebuilder "$version")"
|
||||
|
||||
# 构建固件
|
||||
build "$builder_dir"
|
||||
@@ -0,0 +1,226 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# 安装 Image Builder 构建所需依赖
|
||||
install_dependencies() {
|
||||
sudo apt install -y build-essential libncurses-dev zlib1g-dev gawk git \
|
||||
gettext libssl-dev xsltproc rsync wget unzip python3 qemu-utils mkisofs zstd
|
||||
}
|
||||
|
||||
# 下载并解压 FriendlyARM NanoPi R2S 的 Image Builder
|
||||
# 参数 1: ImmortalWrt 版本号,例如 25.12.1
|
||||
# 输出: 解压后的 Image Builder 目录绝对路径
|
||||
download_imagebuilder() {
|
||||
local version="$1"
|
||||
local target="rockchip-armv8"
|
||||
local file="immortalwrt-imagebuilder-$version-$target.Linux-x86_64.tar.zst"
|
||||
local url="https://downloads.immortalwrt.org/releases/$version/targets/rockchip/armv8/$file"
|
||||
local folder="immortalwrt-imagebuilder-$version-$target.Linux-x86_64"
|
||||
local marker="$folder/.ready"
|
||||
|
||||
if [ -f "$marker" ]; then
|
||||
echo "$(pwd)/$folder"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 如果目录存在但不完整,先清理
|
||||
if [ -d "$folder" ] && [ ! -f "$marker" ]; then
|
||||
echo "[WARN] found incomplete folder, cleaning..." >&2
|
||||
rm -rf "$folder"
|
||||
fi
|
||||
|
||||
# 下载并覆盖同名压缩包
|
||||
wget -O "$file" "$url" >&2
|
||||
|
||||
# 解压
|
||||
tar -I zstd -xvf "$file" >&2
|
||||
|
||||
touch "$marker"
|
||||
|
||||
echo "$(pwd)/$folder"
|
||||
}
|
||||
|
||||
|
||||
build(){
|
||||
# =============================================================
|
||||
# 设备 Profile
|
||||
# 可在 Image Builder 目录运行 `make info` 查看所有可用 profile
|
||||
# =============================================================
|
||||
PROFILE="friendlyarm_nanopi-r2s"
|
||||
|
||||
# =============================================================
|
||||
# 自定义文件目录,映射到固件根文件系统
|
||||
# =============================================================
|
||||
FILES_DIR="files"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
BUILDER_DIR="${1}"
|
||||
|
||||
# =============================================================
|
||||
# 固件输出目录
|
||||
# =============================================================
|
||||
OUTPUT_DIR="$BUILDER_DIR/r2s_output"
|
||||
PROJECT_OUTPUT_DIR="$SCRIPT_DIR/r2s_output"
|
||||
|
||||
# =============================================================
|
||||
# 固件文件名附加标识,可留空
|
||||
# =============================================================
|
||||
EXTRA_IMAGE_NAME="r2s"
|
||||
|
||||
# =============================================================
|
||||
# 根分区大小,单位 MB;留空则使用 imagebuilder 默认值
|
||||
# =============================================================
|
||||
ROOTFS_PARTSIZE="${2:-}"
|
||||
|
||||
# =============================================================
|
||||
# 插件包列表
|
||||
# - 前面加 - 表示从固件中移除该包,例如 -dnsmasq
|
||||
# - 每行一个,方便增删注释
|
||||
# =============================================================
|
||||
PKG_LIST=(
|
||||
openssh-sftp-server
|
||||
vim-full
|
||||
nano-plus
|
||||
bash
|
||||
shadow-chsh
|
||||
curl
|
||||
luci-i18n-argon-config-zh-cn
|
||||
luci-i18n-package-manager-zh-cn
|
||||
luci-i18n-firewall-zh-cn
|
||||
luci-i18n-arpbind-zh-cn
|
||||
|
||||
# 现代终端工具
|
||||
ripgrep
|
||||
diffutils
|
||||
less
|
||||
|
||||
# 分区扩容
|
||||
luci-i18n-diskman-zh-cn
|
||||
cfdisk
|
||||
resize2fs
|
||||
e2fsprogs
|
||||
losetup
|
||||
|
||||
# 代理插件
|
||||
luci-i18n-daed-zh-cn
|
||||
luci-i18n-homeproxy-zh-cn
|
||||
sing-box
|
||||
)
|
||||
|
||||
PACKAGES=$(printf "%s " "${PKG_LIST[@]}")
|
||||
|
||||
GREEN="\033[0;32m"
|
||||
YELLOW="\033[1;33m"
|
||||
RED="\033[0;31m"
|
||||
NC="\033[0m"
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} OpenWrt Image Builder 开始构建${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e " Device : ${YELLOW}FriendlyARM NanoPi R2S${NC}"
|
||||
echo -e " Target : ${YELLOW}rockchip/armv8${NC}"
|
||||
echo -e " Profile : ${YELLOW}${PROFILE}${NC}"
|
||||
echo -e " Files 目录 : ${YELLOW}${FILES_DIR}${NC}"
|
||||
echo -e " 输出目录 : ${YELLOW}${OUTPUT_DIR}${NC}"
|
||||
echo -e " 项目输出目录: ${YELLOW}${PROJECT_OUTPUT_DIR}${NC}"
|
||||
echo -e " 镜像标识 : ${YELLOW}${EXTRA_IMAGE_NAME}${NC}"
|
||||
echo ""
|
||||
|
||||
cd "$BUILDER_DIR"
|
||||
|
||||
mkdir -p "$FILES_DIR"
|
||||
# 复制 files 目录下的自定义文件到 Image Builder 的 files 目录
|
||||
if [ -d "${SCRIPT_DIR}/files" ]; then
|
||||
echo -e "${YELLOW}[提示] 复制自定义 files/ 目录到 Image Builder${NC}"
|
||||
cp ${SCRIPT_DIR}/files/* $BUILDER_DIR/files/ -rf
|
||||
else
|
||||
echo -e "${YELLOW}[提示] 未找到自定义 files/ 目录,跳过复制${NC}"
|
||||
fi
|
||||
|
||||
if [ ! -f "Makefile" ]; then
|
||||
echo -e "${RED}[错误] 未找到 Makefile,请在 imagebuilder 根目录下运行本脚本!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
make image \
|
||||
PROFILE="$PROFILE" \
|
||||
PACKAGES="$PACKAGES" \
|
||||
FILES="$FILES_DIR" \
|
||||
BIN_DIR="$OUTPUT_DIR" \
|
||||
EXTRA_IMAGE_NAME="$EXTRA_IMAGE_NAME" \
|
||||
${ROOTFS_PARTSIZE:+ROOTFS_PARTSIZE="$ROOTFS_PARTSIZE"}
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
if [ "$OUTPUT_DIR" != "$PROJECT_OUTPUT_DIR" ]; then
|
||||
if [ ! -d "$OUTPUT_DIR" ]; then
|
||||
echo -e "${RED} 未找到构建输出目录: ${OUTPUT_DIR}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -e "$PROJECT_OUTPUT_DIR" ]; then
|
||||
BACKUP_OUTPUT_DIR="${PROJECT_OUTPUT_DIR}.bak.$(date +%Y%m%d%H%M%S)"
|
||||
if ! mv "$PROJECT_OUTPUT_DIR" "$BACKUP_OUTPUT_DIR"; then
|
||||
echo -e "${RED} 备份已有输出目录失败: ${PROJECT_OUTPUT_DIR}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${YELLOW} 已有输出目录,已备份到: ${BACKUP_OUTPUT_DIR}${NC}"
|
||||
fi
|
||||
|
||||
if ! mv "$OUTPUT_DIR" "$PROJECT_OUTPUT_DIR"; then
|
||||
echo -e "${RED} 移动构建输出目录失败: ${OUTPUT_DIR} -> ${PROJECT_OUTPUT_DIR}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} 构建成功!${NC}"
|
||||
echo -e "${GREEN} 固件路径: ${PROJECT_OUTPUT_DIR}${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
else
|
||||
echo ""
|
||||
echo -e "${RED}========================================${NC}"
|
||||
echo -e "${RED} 构建失败,请检查上方错误日志${NC}"
|
||||
echo -e "${RED}========================================${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================
|
||||
# 交互输入函数
|
||||
# 参数1: 提示信息
|
||||
# 参数2: 默认值
|
||||
# 返回: 用户输入值,直接回车返回默认值
|
||||
# =============================================================
|
||||
ask_input() {
|
||||
local prompt="$1"
|
||||
local default="$2"
|
||||
|
||||
read -p "${prompt} [ 默认: ${default} ]: " value
|
||||
|
||||
if [ -z "$value" ]; then
|
||||
echo "$default"
|
||||
else
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
# 项目根目录,保证从任意位置执行 r2s.sh 都能找到其他资源
|
||||
project_dir="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$project_dir"
|
||||
|
||||
# 要构建的 ImmortalWrt 版本, 可以在 https://github.com/immortalwrt/immortalwrt 查询版本号
|
||||
default_version="25.12.1"
|
||||
version=$(ask_input "请输入 ImmortalWrt 版本" "$default_version")
|
||||
|
||||
# 根分区大小(单位: MB)
|
||||
default_rootfs_partsize="600"
|
||||
rootfs_partsize=$(ask_input "请输入根分区大小(MB)" "$default_rootfs_partsize")
|
||||
|
||||
install_dependencies
|
||||
|
||||
builder_dir="$(download_imagebuilder "$version")"
|
||||
|
||||
build $builder_dir $rootfs_partsize
|
||||
+35
-6
@@ -3,12 +3,12 @@ set -e
|
||||
|
||||
# 安装 Image Builder 构建所需依赖
|
||||
install_dependencies() {
|
||||
apt install -y build-essential libncurses-dev zlib1g-dev gawk git \
|
||||
sudo apt install -y build-essential libncurses-dev zlib1g-dev gawk git \
|
||||
gettext libssl-dev xsltproc rsync wget unzip python3 qemu-utils mkisofs zstd
|
||||
}
|
||||
|
||||
# 下载并解压 x86-64 的 Image Builder
|
||||
# 参数 1: ImmortalWrt 版本号,例如 25.12.0
|
||||
# 参数 1: ImmortalWrt 版本号,例如 25.12.1
|
||||
# 输出: 解压的后 Image Builder 目录绝对路径
|
||||
download_imagebuilder() {
|
||||
local version="$1"
|
||||
@@ -58,8 +58,8 @@ build(){
|
||||
# ────────────────────────────────────────────
|
||||
# 固件输出目录
|
||||
# ────────────────────────────────────────────
|
||||
OUTPUT_DIR="$BUILDER_DIR/output"
|
||||
PROJECT_OUTPUT_DIR="$SCRIPT_DIR/output"
|
||||
OUTPUT_DIR="$BUILDER_DIR/x86-64_output"
|
||||
PROJECT_OUTPUT_DIR="$SCRIPT_DIR/x86-64_output"
|
||||
|
||||
# ────────────────────────────────────────────
|
||||
# 固件文件名附加标识(可留空)
|
||||
@@ -84,6 +84,7 @@ build(){
|
||||
nano-plus
|
||||
bash
|
||||
shadow-chsh
|
||||
curl
|
||||
luci-i18n-vlmcsd-zh-cn
|
||||
luci-i18n-argon-config-zh-cn
|
||||
luci-i18n-package-manager-zh-cn
|
||||
@@ -135,7 +136,12 @@ build(){
|
||||
cd "$BUILDER_DIR"
|
||||
|
||||
# 复制 files 目录下的自定义文件到 Image Builder 的 files 目录
|
||||
if [ -d "${SCRIPT_DIR}/files" ]; then
|
||||
echo -e "${YELLOW}[提示] 复制自定义 files/ 目录到 Image Builder${NC}"
|
||||
cp ${SCRIPT_DIR}/files/* $BUILDER_DIR/files/ -rf
|
||||
else
|
||||
echo -e "${YELLOW}[提示] 未找到自定义 files/ 目录,跳过复制${NC}"
|
||||
fi
|
||||
|
||||
# 修改.config文件, 不构建以下格式固件
|
||||
sed -i \
|
||||
@@ -208,13 +214,36 @@ build(){
|
||||
|
||||
}
|
||||
|
||||
# =============================================================
|
||||
# 交互输入函数
|
||||
# 参数1: 提示信息
|
||||
# 参数2: 默认值
|
||||
# 返回: 用户输入值,直接回车返回默认值
|
||||
# =============================================================
|
||||
ask_input() {
|
||||
local prompt="$1"
|
||||
local default="$2"
|
||||
|
||||
read -p "${prompt} [ 默认: ${default} ]: " value
|
||||
|
||||
if [ -z "$value" ]; then
|
||||
echo "$default"
|
||||
else
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
# 项目根目录,用来保证从任意位置执行 main.sh 都能找到其他脚本
|
||||
project_dir="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$project_dir"
|
||||
|
||||
# 要构建的 ImmortalWrt 版本, 可以在 https://github.com/immortalwrt/immortalwrt 查询版本号
|
||||
version="25.12.0"
|
||||
default_version="25.12.1"
|
||||
version=$(ask_input "请输入 ImmortalWrt 版本" "$default_version")
|
||||
|
||||
# 根分区大小(单位: MB)
|
||||
default_rootfs_partsize="600"
|
||||
rootfs_partsize=$(ask_input "请输入根分区大小(MB)" "$default_rootfs_partsize")
|
||||
|
||||
# 安装依赖
|
||||
install_dependencies
|
||||
@@ -223,4 +252,4 @@ install_dependencies
|
||||
builder_dir="$(download_imagebuilder "$version")"
|
||||
|
||||
# 构建固件
|
||||
build $builder_dir 500 # 500 表示根分区大小为 500MB
|
||||
build $builder_dir $rootfs_partsize
|
||||
Reference in New Issue
Block a user