Compare commits
6 Commits
04f311e577
...
2abd8aba00
| Author | SHA1 | Date | |
|---|---|---|---|
| 2abd8aba00 | |||
| a98b1e1c76 | |||
| e668221b4e | |||
| 0255d2f890 | |||
| 351e7c41f3 | |||
| de7f3f12fd |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Generated artefacts: full package lists and saved buildinfo.
|
||||
# Regenerate with gen-package-list.sh / download-config.sh --save-buildinfo
|
||||
.generated/
|
||||
336
README.md
336
README.md
@@ -1,133 +1,245 @@
|
||||
# OpenWrt Build Helper Scripts
|
||||
|
||||
## Overview
|
||||
This repository contains shell scripts that streamline preparing, configuring, and building OpenWrt from source on Debian/Ubuntu systems (x86_64 and ARM64). It also includes convenience utilities to manage package selections, fetch device config.buildinfo, apply a DAHDI driver patch, and add external LuCI packages.
|
||||
Shell scripts that automate building OpenWrt from source: installing the host
|
||||
dependencies, fetching the source tree, importing a device's official
|
||||
configuration, and keeping a per-device package list.
|
||||
|
||||
## What’s included
|
||||
- prepare-openwrt-env.sh
|
||||
- Installs required build dependencies via apt on Ubuntu 22.04/24.04/26.04 and Debian 12/13+.
|
||||
- Handles x86_64 and ARM64 differences (Python/distutils, multilib notes, Go on ARM64).
|
||||
- prepare-openwrt.sh
|
||||
- Clones or updates the OpenWrt source repo in the top-level `openwrt/` root.
|
||||
- Lets you select Stable, Beta, or Snapshot versions interactively.
|
||||
- Uses the script location as the source of truth, so running it from another current working directory is supported.
|
||||
- On first run from a helper checkout, relocates that checkout to `openwrt/helper/`.
|
||||
- Updates feeds and runs make defconfig.
|
||||
- add-external-repos.sh
|
||||
- Clones or updates additional package repositories into package/.
|
||||
- Currently includes luci-app-netspeedtest and luci-app-easytier (adjust REPOS as needed).
|
||||
- download-config.sh
|
||||
- Downloads config.buildinfo for a selected device based on your checked-out OpenWrt version (stable or snapshot).
|
||||
- Writes .config and runs make defconfig.
|
||||
- Prints firmware URL and a Firmware Selector link for reference.
|
||||
- add-openwrt-packages.sh
|
||||
- Enables packages listed in a text file by appending CONFIG_PACKAGE_<name>=y to .config (without removing existing settings).
|
||||
- Verifies presence in package/ or feeds/ and reports missing ones, then runs make defconfig.
|
||||
- update-go-path.sh
|
||||
- Detects the latest Go installation under /usr/lib/go-* and sets CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT in .config.
|
||||
- apply-dahdi-patches.sh
|
||||
- Writes a DAHDI driver patch into package/feeds/telephony/dahdi-linux/patches/300-fix-dahdi-max-attempts.patch.
|
||||
- Fixes MAX macro naming collisions in multiple DAHDI modules for OpenWrt 24.10.4 builds.
|
||||
The design goal is that **a new OpenWrt release requires no file changes** —
|
||||
everything version specific is fetched from upstream, and only your own package
|
||||
choices are stored in this repository.
|
||||
|
||||
## System requirements
|
||||
- OS: Ubuntu 22.04/24.04/26.04 or Debian 12/13+ (others may work with adjustments)
|
||||
- Architectures: x86_64/amd64 and aarch64/arm64
|
||||
- Tools: git, wget, curl, bash
|
||||
- Internet access for feeds and package downloads
|
||||
- Optional: Go (required by some OpenWrt packages; auto-configured on ARM64 by prepare-openwrt-env.sh; configurable via update-go-path.sh)
|
||||
## Layout
|
||||
|
||||
`prepare-openwrt.sh` normalizes the checkout on first run:
|
||||
|
||||
```
|
||||
before <parent>/openwrt-build-helper/ this repository
|
||||
first run <parent>/openwrt/ the OpenWrt source root
|
||||
<parent>/openwrt/helper/ this repository, moved in
|
||||
<parent>/openwrt-build-helper -> openwrt/helper (symlink)
|
||||
later runs the same <parent>/openwrt/ is updated in place
|
||||
```
|
||||
|
||||
The symlink means shells, editors and agent sessions still sitting in the old
|
||||
path keep working. Disable it with `--no-compat-link`.
|
||||
|
||||
Inside `helper/`:
|
||||
|
||||
| Path | In git | Purpose |
|
||||
|---|---|---|
|
||||
| `packages/<device-id>.txt` | yes | your packages for one device, version independent |
|
||||
| `.generated/` | no | combined package lists and saved buildinfo (build artefacts) |
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux with one of: apt (Debian/Ubuntu/Mint), dnf/yum (Fedora/RHEL/Rocky/Alma),
|
||||
pacman (Arch/Manjaro), zypper (openSUSE), apk (Alpine)
|
||||
- x86_64/amd64 or aarch64/arm64
|
||||
- git, curl, wget, bash, python3 (used to read the upstream JSON indexes; jq is
|
||||
not required)
|
||||
- Internet access, and roughly 30 GB of disk for a full build
|
||||
|
||||
## Quick start
|
||||
1) Prepare the host machine
|
||||
- Run: ./prepare-openwrt-env.sh
|
||||
- This installs compilers, headers, Python tooling, and other build prerequisites.
|
||||
|
||||
2) Get OpenWrt sources
|
||||
- Run: ./prepare-openwrt.sh
|
||||
- Choose Stable, Beta, or Snapshot.
|
||||
- The script:
|
||||
- Reuses or creates the top-level OpenWrt root at `./openwrt`.
|
||||
- Moves this helper checkout to `./openwrt/helper` on first run.
|
||||
- Updates/install feeds and runs make defconfig.
|
||||
```bash
|
||||
# 1. host dependencies
|
||||
./prepare-openwrt-env.sh # -n to preview, --with-go for Go
|
||||
|
||||
3) Add optional external packages
|
||||
- From the OpenWrt root:
|
||||
- ./helper/add-external-repos.sh
|
||||
- This pulls extra LuCI packages into package/.
|
||||
# 2. OpenWrt source (relocates this checkout to openwrt/helper on first run)
|
||||
./prepare-openwrt.sh # or --stable 25.12.5 -y
|
||||
|
||||
4) Import a device config.buildinfo (optional but convenient)
|
||||
- From the OpenWrt root:
|
||||
- ./helper/download-config.sh
|
||||
- Pick a device, and the script writes .config and runs make defconfig.
|
||||
# from here on, run everything from the OpenWrt root
|
||||
cd ../openwrt # or: cd openwrt
|
||||
|
||||
5) Enable additional packages from a list (optional)
|
||||
- Create a file packages.txt containing package names (whitespace or newlines).
|
||||
- From the OpenWrt root:
|
||||
- ./helper/add-openwrt-packages.sh packages.txt
|
||||
# 3. the device's official configuration
|
||||
./helper/download-config.sh --device linksys_mx8500 -y
|
||||
|
||||
6) Configure Go bootstrap path (if needed)
|
||||
- From the OpenWrt root:
|
||||
- ./helper/update-go-path.sh
|
||||
- This sets CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT to the latest /usr/lib/go-X.XX/ found.
|
||||
- You can override it manually in .config if necessary.
|
||||
# 4. built-in packages + your own, applied to .config
|
||||
./helper/gen-package-list.sh --device linksys_mx8500 --apply -y
|
||||
|
||||
7) Apply DAHDI patch (only if you build telephony/dahdi-linux)
|
||||
- Ensure telephony feed is installed:
|
||||
- ./scripts/feeds update telephony
|
||||
- ./scripts/feeds install -a
|
||||
- Then run:
|
||||
- ./helper/config_24.10.4/apply-dahdi-patches.sh
|
||||
- Build the package:
|
||||
- make package/feeds/telephony/dahdi-linux/{clean,prepare} V=s
|
||||
- make package/feeds/telephony/dahdi-linux/compile V=s
|
||||
# 5. build
|
||||
make -j$(nproc) download world
|
||||
```
|
||||
|
||||
8) Build OpenWrt
|
||||
- Common commands:
|
||||
- make menuconfig
|
||||
- make -j$(nproc) download world
|
||||
- Artifacts will be in bin/ after the build completes.
|
||||
Firmware lands in `bin/targets/<target>/<subtarget>/`.
|
||||
|
||||
## Script usage notes and tips
|
||||
- Run locations:
|
||||
- `prepare-openwrt.sh`: resolves paths from the script location, not your current shell directory.
|
||||
- `prepare-openwrt-env.sh`: can be run before the first normalization from the fresh helper checkout.
|
||||
- All other helper scripts: run them from inside the OpenWrt source root as `./helper/...` unless otherwise indicated.
|
||||
- Feeds:
|
||||
- If a package can’t be found, ensure feeds are updated/installed:
|
||||
- ./scripts/feeds update -a && ./scripts/feeds install -a
|
||||
- Go toolchain:
|
||||
- On x86_64, Go is optional unless your selection pulls packages needing Go (e.g., firewall4 in some versions). If needed:
|
||||
- sudo apt install golang -y
|
||||
- Then use update-go-path.sh to set .config automatically.
|
||||
- ARM64 multilib:
|
||||
- The script installs cross multilib packages for better compatibility, but not all targets need them. If apt errors on specific multilib packages, remove or adjust as appropriate for your environment.
|
||||
Run `./helper/download-config.sh` without `--device` to search for a device by
|
||||
name. Add `--with-packages` to fold step 4 into step 3.
|
||||
|
||||
### Adding a device
|
||||
|
||||
```bash
|
||||
./helper/gen-package-list.sh --device <id> --init-extras # creates packages/<id>.txt
|
||||
$EDITOR helper/packages/<id>.txt # list what you want
|
||||
./helper/gen-package-list.sh --device <id> --apply
|
||||
```
|
||||
|
||||
### Upgrading to a new OpenWrt release
|
||||
|
||||
Nothing to edit — the built-in list is re-fetched and `packages/<id>.txt` is
|
||||
reused as is:
|
||||
|
||||
```bash
|
||||
./helper/prepare-openwrt.sh --stable -y
|
||||
cd ../openwrt
|
||||
./helper/download-config.sh --device <id> -y
|
||||
./helper/gen-package-list.sh --device <id> --apply -y
|
||||
make -j$(nproc) download world
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
- **prepare-openwrt-env.sh** — installs the build dependencies from the official
|
||||
build system guide. Dispatches on the package manager rather than guessing from
|
||||
the distro version, and probes every package against the running release so a
|
||||
renamed or dropped package cannot abort the install.
|
||||
`-y`, `-n/--dry-run`, `--with-go`.
|
||||
|
||||
- **prepare-openwrt.sh** — clones or updates the OpenWrt source in `openwrt/`.
|
||||
Resolves the version over `git ls-remote` and prints a plan before touching
|
||||
anything, so aborting at the prompt leaves the checkout untouched. Uses a
|
||||
partial clone (`--filter=blob:none`) by default and resumes an interrupted
|
||||
first run automatically.
|
||||
`--stable [<ver>]`, `--branch <name>`, `--snapshot`, `-y`, `-n`, `-f/--force`,
|
||||
`--full-clone`, `--allow-rc`, `--root <dir>`, `--skip-feeds`,
|
||||
`--skip-defconfig`, `--repo-url <url>`, `--no-compat-link`.
|
||||
|
||||
- **download-config.sh** — installs a device's official `config.buildinfo` as
|
||||
`.config`. Looks the device up in the official index, so any supported device
|
||||
can be selected by name. Downloads to a temp file and validates it before
|
||||
touching `.config`, keeping a `.config.download.bak`. By default builds only
|
||||
the selected device and strips the buildbot flags (see below).
|
||||
`--device <id>`, `--target <t>`, `--version <ver>`, `--snapshot`,
|
||||
`--with-packages`, `--all-profiles`, `--keep-buildbot-flags`,
|
||||
`--save-buildinfo`, `--offline`, `--output <file>`, `-y`, `-n`.
|
||||
|
||||
- **gen-package-list.sh** — builds the complete package list: the built-in part
|
||||
from the target's `profiles.json` (`default_packages` + `device_packages` plus
|
||||
the two packages the Firmware Selector adds), combined with your
|
||||
`packages/<device-id>.txt`.
|
||||
`--device <id>`, `--apply`, `--init-extras`, `--stdout`, `--full-stdout`,
|
||||
`--out <path>`, `--no-extras`, `-y`, `-n`.
|
||||
|
||||
- **add-openwrt-packages.sh** — applies a package list to `.config`. Rewrites
|
||||
existing `CONFIG_PACKAGE_` lines in place, so repeated runs do not grow the
|
||||
file. After `make defconfig` it reports any package whose final state differs
|
||||
from what you asked for.
|
||||
`-y`, `-n`, `--no-defconfig`, `--no-backup`, `--strict`, and `-` to read stdin.
|
||||
|
||||
- **update-go-path.sh** — points the build at an installed Go toolchain.
|
||||
`--external` clears `CONFIG_GOLANG_BUILD_BOOTSTRAP` so the installed toolchain
|
||||
builds host Go directly instead of OpenWrt building the whole bootstrap chain
|
||||
from source.
|
||||
`--external`, `--go-root <dir>`, `-n`.
|
||||
|
||||
- **add-external-repos.sh** — clones extra package repositories into `package/`.
|
||||
Edit the `REPOS` array to change the list.
|
||||
|
||||
## Package list format
|
||||
|
||||
Used by both `packages/<device-id>.txt` and the generated lists:
|
||||
|
||||
```
|
||||
curl yq luci-app-ttyd packages, any number per line, set to =y
|
||||
-luci-app-wol a leading '-' removes a package (=n)
|
||||
##module packages after this are set to =m
|
||||
##remove packages after this are set to =n
|
||||
##built-in back to =y (the default)
|
||||
pkg # note text after a single '#' is a comment
|
||||
```
|
||||
|
||||
## Notes and gotchas
|
||||
|
||||
### Buildbot flags
|
||||
The official `config.buildinfo` is the buildbot's own configuration: it enables
|
||||
every model in the target and sets `ALL_KMODS`, `ALL_NONSHARED`, `SDK`, `IB`,
|
||||
`MAKE_TOOLCHAIN`, `COLLECT_KERNEL_DEBUG` and `AUTOREMOVE`. For a Linksys MX8500
|
||||
that is 1248 extra module packages plus the SDK, ImageBuilder and a toolchain
|
||||
tarball on every build.
|
||||
|
||||
`download-config.sh` turns these off by default. This was verified not to change
|
||||
the firmware: the set of packages built into the image (`=y`) is identical either
|
||||
way — 193 packages before and after. The trade-off is that `bin/` no longer
|
||||
contains prebuilt packages for modules you did not select, so you cannot later
|
||||
install an arbitrary kmod from your own build output.
|
||||
`--keep-buildbot-flags` and `--all-profiles` restore upstream behaviour.
|
||||
|
||||
### Go packages
|
||||
Packages such as tailscale, adguardhome, easytier and cloudflared need a host Go.
|
||||
By default OpenWrt builds the entire bootstrap chain from source, which works but
|
||||
is slow. To skip it, install a Go matching the version the tree expects and run:
|
||||
|
||||
```bash
|
||||
sudo apt install golang-1.24-go
|
||||
./helper/update-go-path.sh --external
|
||||
```
|
||||
|
||||
### WSL: Windows PATH breaks Go package builds
|
||||
On WSL, `PATH` includes Windows directories containing spaces and parentheses
|
||||
(`/mnt/c/Program Files (x86)/...`). OpenWrt's Go build recipe interpolates `PATH`
|
||||
unquoted into a shell command, so every Go package fails with:
|
||||
|
||||
```
|
||||
bash: -c: line 1: syntax error near unexpected token `('
|
||||
```
|
||||
|
||||
Either build with a sanitized PATH:
|
||||
|
||||
```bash
|
||||
CLEAN_PATH=$(echo "$PATH" | tr ':' '\n' | grep -v '^/mnt/' | paste -sd:)
|
||||
env PATH="$CLEAN_PATH" make -j$(nproc) download world
|
||||
```
|
||||
|
||||
or disable the interop PATH permanently in `/etc/wsl.conf` and restart WSL
|
||||
(`wsl --shutdown`):
|
||||
|
||||
```ini
|
||||
[interop]
|
||||
appendWindowsPath = false
|
||||
```
|
||||
|
||||
### Hazard: `git clean -xdff`
|
||||
`helper/` lives inside the OpenWrt Git worktree but is not tracked by it.
|
||||
`prepare-openwrt.sh` adds `/helper/` to `openwrt/.git/info/exclude`, which keeps
|
||||
`git status` clean and stops `git add -A` from staging it — but that does **not**
|
||||
protect it from `git clean -xdff`: `-x` deliberately includes ignored files and
|
||||
`-ff` removes Git's refusal to recurse into a nested repository. Use instead:
|
||||
|
||||
```bash
|
||||
git clean -xdf -e /helper
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- Telephony feed not found when applying DAHDI patch:
|
||||
- Run feeds update/install for telephony as shown above.
|
||||
- Version detection in download-config.sh:
|
||||
- If you’re on a branch like openwrt-24.10, the script will try to find the latest v24.10.x tag automatically. Otherwise it prompts for a version (e.g., 24.10.4 or SNAPSHOT).
|
||||
- Missing packages in add-openwrt-packages.sh:
|
||||
- Ensure the corresponding feed is enabled in feeds.conf.default and run feeds update/install.
|
||||
- Permission errors:
|
||||
- Scripts use umask 022 and do not require root except when installing apt packages.
|
||||
- Clean up and rebuild a package:
|
||||
- make package/<pkg>/{clean,compile} V=s
|
||||
- Full rebuild:
|
||||
- make clean; make -j$(nproc) download world
|
||||
|
||||
## Directory behavior
|
||||
- If you start from a freshly cloned helper checkout at `<parent>/<origin_folder>`, `prepare-openwrt.sh` will:
|
||||
- Reuse or create `<parent>/openwrt/` as the OpenWrt source root.
|
||||
- Move the entire helper checkout, including its `.git`, to `<parent>/openwrt/helper/`.
|
||||
- Initialize or update the OpenWrt source tree directly in `<parent>/openwrt/`.
|
||||
- If you later rerun `openwrt/helper/prepare-openwrt.sh`, it reuses the parent `openwrt/` directory and does not move directories again.
|
||||
- If the script is already located in a real OpenWrt source root, it treats that directory as the source root and does not relocate it just because the directory name is `openwrt`.
|
||||
- **Version detection in download-config.sh** — it uses, in order: an explicit
|
||||
`--version`/`--snapshot`; the exact tag at HEAD; the branch (`main`/`master` →
|
||||
SNAPSHOT, `openwrt-XX.YY` → the newest matching tag merged into HEAD);
|
||||
otherwise it asks. If HEAD is ahead of the tag it warns that the downloaded
|
||||
config describes the release rather than your tree. Version queries are
|
||||
anchored to the OpenWrt tree, not to whichever repository the script sits in.
|
||||
- **The device symbol did not survive make defconfig** — your tree does not match
|
||||
the release you downloaded a config for. The script stops and points at the
|
||||
`.config.download.bak` it kept.
|
||||
- **A package is reported missing** — names are verified against
|
||||
`tmp/.config-package.in` and `tmp/.packageinfo`. Enable the relevant feed in
|
||||
`feeds.conf.default`, then `./scripts/feeds update -a && ./scripts/feeds install -a`.
|
||||
- **A removed package (`-pkg`) comes back after make defconfig** — something else
|
||||
depends on it. Reported explicitly as "wanted =n, got =y".
|
||||
- **A package is in .config but not in the manifest** — it is probably there under
|
||||
a provider name (`libgcc` → `libgcc1`, `nftables` → `nftables-json`).
|
||||
- **Rebuild one package** — `make package/<pkg>/{clean,compile} V=s`
|
||||
- **Full rebuild** — `make clean; make -j$(nproc) download world`
|
||||
|
||||
## Security and safety
|
||||
- Scripts use set -e to stop on errors.
|
||||
- They modify .config and write under package/ and feeds/ inside your OpenWrt tree.
|
||||
- Backup your .config if you need a known baseline.
|
||||
## Safety
|
||||
|
||||
- Scripts use `set -euo pipefail` and `umask 022`, and need root only to install
|
||||
host packages.
|
||||
- `.config` is backed up before it is replaced or before a version switch.
|
||||
- `prepare-openwrt.sh` refuses to switch versions on a tree with modified tracked
|
||||
files unless you pass `--force`.
|
||||
- Scripts write under `package/` and `feeds/` inside your OpenWrt tree.
|
||||
|
||||
## License and authorship
|
||||
- Scripts authored by Zhe Yuan with help from ChatGPT.
|
||||
|
||||
- Scripts authored by Zhe Yuan.
|
||||
- License: MIT (unless you choose a different license; update this line accordingly).
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
set -e # Exit immediately if any command fails
|
||||
umask 022 # Set default file permissions (files=644, dirs=755)
|
||||
|
||||
# --- Check if inside OpenWRT source directory ---
|
||||
# Every path below is relative to the current directory, so running this from
|
||||
# the helper checkout would clone into helper/package/ instead of the real
|
||||
# OpenWRT package/ directory.
|
||||
if [ ! -f "feeds.conf.default" ] || [ ! -d "package" ]; then
|
||||
echo "[ERROR] Must run inside OpenWRT root directory."
|
||||
echo "[ERROR] From the OpenWrt root, run it as: ./helper/add-external-repos.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Define repositories in the format: "git_url branch local_dir"
|
||||
REPOS=(
|
||||
"https://github.com/muink/luci-app-netspeedtest.git master package/luci-app-netspeedtest"
|
||||
|
||||
@@ -1,162 +1,340 @@
|
||||
#!/bin/bash
|
||||
# Add specified OpenWRT packages (from a text file) to .config without removing existing ones
|
||||
# Apply a package list to the OpenWrt .config.
|
||||
#
|
||||
# The list is a plain text file of package names, whitespace separated, any
|
||||
# number per line. Section headers switch how the following packages are
|
||||
# applied, and a leading '-' on a name removes that package:
|
||||
#
|
||||
# ##built-in the packages after this are set to =y (default)
|
||||
# ##module ... are set to =m
|
||||
# ##remove ... are set to =n
|
||||
# -luci-app-ttyd set to =n regardless of the current section
|
||||
# pkg # comment text after a single # is ignored
|
||||
#
|
||||
# Existing CONFIG_PACKAGE_ lines are rewritten in place rather than appended,
|
||||
# so running this repeatedly does not grow .config.
|
||||
#
|
||||
# Usage: ./add-openwrt-packages.sh [options] <package_list.txt | ->
|
||||
#
|
||||
# -y, --yes do not ask for confirmation
|
||||
# -n, --dry-run show the resulting diff, change nothing
|
||||
# --no-defconfig do not run make defconfig afterwards
|
||||
# --no-backup do not keep a .config.bak
|
||||
# --strict exit non-zero if any listed package was not found
|
||||
# -h, --help show this help
|
||||
#
|
||||
# Run it from the OpenWrt root: ./helper/add-openwrt-packages.sh <list>
|
||||
#
|
||||
# Author: Zhe Yuan
|
||||
# Usage:
|
||||
# ./add-openwrt-packages.sh packages.txt
|
||||
# Description:
|
||||
# - Reads package names from a text file (supports both newline or space separation)
|
||||
# - Supports ##built-in and ##module sections to control build mode (=y or =m)
|
||||
# - Packages default to built-in (=y) if no section header is specified
|
||||
# - Adds missing CONFIG_PACKAGE_xxx entries to .config
|
||||
# - Keeps existing settings intact
|
||||
# - Reports missing packages
|
||||
# - Runs make defconfig at the end to sync dependencies
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
umask 022
|
||||
|
||||
# Check argument
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "❌ Usage: $0 <package_list.txt>"
|
||||
PKG_FILE=""
|
||||
ASSUME_YES=false
|
||||
DRY_RUN=false
|
||||
RUN_DEFCONFIG=true
|
||||
KEEP_BACKUP=true
|
||||
STRICT=false
|
||||
|
||||
info() { echo "[INFO] $*"; }
|
||||
warn() { echo "[WARN] $*"; }
|
||||
err() { echo "[ERROR] $*" >&2; }
|
||||
|
||||
usage() { sed -n '2,29p' "$0" | sed 's/^# \{0,1\}//;s/^#$//'; }
|
||||
|
||||
confirm() {
|
||||
local question="$1" default="$2" reply=""
|
||||
[ "$ASSUME_YES" = true ] && return 0
|
||||
if [ "$default" = "yes" ]; then
|
||||
read -r -p "$question [Y/n]: " reply || reply=""
|
||||
case "${reply:-}" in [nN] | [nN][oO]) return 1 ;; *) return 0 ;; esac
|
||||
else
|
||||
read -r -p "$question [y/N]: " reply || reply=""
|
||||
case "${reply:-}" in [yY] | [yY][eE][sS]) return 0 ;; *) return 1 ;; esac
|
||||
fi
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-y|--yes) ASSUME_YES=true ;;
|
||||
-n|--dry-run) DRY_RUN=true ;;
|
||||
--no-defconfig) RUN_DEFCONFIG=false ;;
|
||||
--no-backup) KEEP_BACKUP=false ;;
|
||||
--strict) STRICT=true ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
-) PKG_FILE="-" ;;
|
||||
-*) err "Unknown option: $1"; echo "Run '$0 --help' for usage." >&2; exit 1 ;;
|
||||
*)
|
||||
if [ -n "$PKG_FILE" ]; then
|
||||
err "Only one package list can be given."
|
||||
exit 1
|
||||
fi
|
||||
PKG_FILE="$1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ -t 0 ] || ASSUME_YES=true
|
||||
|
||||
if [ -z "$PKG_FILE" ]; then
|
||||
err "No package list given."
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PKG_FILE="$1"
|
||||
|
||||
# Verify file exists
|
||||
if [ ! -f "$PKG_FILE" ]; then
|
||||
echo "❌ Error: File not found: $PKG_FILE"
|
||||
if [ "$PKG_FILE" != "-" ] && [ ! -f "$PKG_FILE" ]; then
|
||||
err "Package list not found: $PKG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify we are in OpenWRT source root
|
||||
if [ ! -d "package" ] || [ ! -f "Makefile" ]; then
|
||||
echo "❌ Error: Please run this script in the OpenWRT source root directory."
|
||||
if [ ! -d package ] || [ ! -f Makefile ] || [ ! -f feeds.conf.default ]; then
|
||||
err "Must run inside the OpenWrt root directory."
|
||||
err "From the OpenWrt root, run it as: ./helper/$(basename "$0")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure .config exists
|
||||
if [ ! -f ".config" ]; then
|
||||
echo "⚙️ Generating initial .config..."
|
||||
make defconfig
|
||||
if [ ! -f .config ]; then
|
||||
info "No .config yet; running make defconfig first..."
|
||||
make defconfig >/dev/null
|
||||
fi
|
||||
|
||||
PACKAGE_INDEX="tmp/.config-package.in"
|
||||
PACKAGE_INFO="tmp/.packageinfo"
|
||||
|
||||
# Ensure package metadata exists for reliable package name lookups.
|
||||
if [ ! -f "$PACKAGE_INDEX" ] && [ ! -f "$PACKAGE_INFO" ]; then
|
||||
echo "⚙️ Generating package metadata..."
|
||||
if [ ! -f "$PACKAGE_INDEX" ] || [ ! -f "$PACKAGE_INFO" ]; then
|
||||
info "Generating package metadata..."
|
||||
make defconfig >/dev/null
|
||||
fi
|
||||
|
||||
is_already_builtin() {
|
||||
local pkg="$1"
|
||||
|
||||
awk -v package_y="CONFIG_PACKAGE_${pkg}=y" \
|
||||
-v default_y="CONFIG_DEFAULT_${pkg}=y" \
|
||||
'$0 == package_y || $0 == default_y { found=1; exit }
|
||||
END { exit !found }' .config
|
||||
# --- Parse the list ------------------------------------------------------
|
||||
# Section headers are handled BEFORE stripping comments: the old
|
||||
# `sub(/#[^#].*/,"")` left a bare '#' behind, which was then reported as a
|
||||
# missing package named '#'.
|
||||
parse_list() {
|
||||
awk '
|
||||
BEGIN { mode = "y" }
|
||||
{
|
||||
line = $0
|
||||
if (line ~ /^[ \t]*##/) {
|
||||
if (line ~ /^[ \t]*##built-in/) { mode = "y"; next }
|
||||
if (line ~ /^[ \t]*##module/) { mode = "m"; next }
|
||||
if (line ~ /^[ \t]*##remove/) { mode = "n"; next }
|
||||
print "[WARN] unknown section header, ignored: " line > "/dev/stderr"
|
||||
next
|
||||
}
|
||||
sub(/#.*$/, "", line)
|
||||
n = split(line, t, /[ \t]+/)
|
||||
for (i = 1; i <= n; i++) {
|
||||
tok = t[i]; m = mode
|
||||
if (tok == "") continue
|
||||
if (substr(tok, 1, 1) == "-") { tok = substr(tok, 2); m = "n" }
|
||||
if (tok == "") continue
|
||||
print tok "\t" m
|
||||
}
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
is_module() {
|
||||
local pkg="$1"
|
||||
if [ "$PKG_FILE" = "-" ]; then
|
||||
ENTRIES="$(parse_list)"
|
||||
PKG_LABEL="(stdin)"
|
||||
else
|
||||
ENTRIES="$(parse_list < "$PKG_FILE")"
|
||||
PKG_LABEL="$PKG_FILE"
|
||||
fi
|
||||
|
||||
grep -qx "CONFIG_PACKAGE_${pkg}=m" .config
|
||||
}
|
||||
if [ -z "$ENTRIES" ]; then
|
||||
err "No packages found in $PKG_LABEL."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Read the current state once -----------------------------------------
|
||||
declare -A CUR=()
|
||||
declare -A DEF=()
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
CONFIG_PACKAGE_*=*) CUR["${line%%=*}"]="${line#*=}" ;;
|
||||
"# CONFIG_PACKAGE_"*" is not set")
|
||||
s="${line#\# }"
|
||||
CUR["${s% is not set}"]="n"
|
||||
;;
|
||||
CONFIG_DEFAULT_*=y) DEF["${line%%=*}"]="y" ;;
|
||||
esac
|
||||
done < .config
|
||||
|
||||
# --- Verify and classify --------------------------------------------------
|
||||
package_exists() {
|
||||
local pkg="$1"
|
||||
|
||||
if [ -f "$PACKAGE_INDEX" ] &&
|
||||
awk -v key="PACKAGE_${pkg}" '$1 == "config" && $2 == key { found=1; exit }
|
||||
awk -v key="PACKAGE_$pkg" '$1 == "config" && $2 == key { found = 1; exit }
|
||||
END { exit !found }' "$PACKAGE_INDEX"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -f "$PACKAGE_INFO" ] && grep -Fqx "Package: $pkg" "$PACKAGE_INFO"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "📦 Reading packages from $PKG_FILE ..."
|
||||
DESIRED="$(mktemp "${TMPDIR:-/tmp}/desired.XXXXXX")"
|
||||
NEWCFG="$(mktemp ./.config.new.XXXXXX)"
|
||||
trap 'rm -f "$DESIRED" "$NEWCFG"' EXIT
|
||||
|
||||
# Parse the file into "package:mode" pairs, respecting ##built-in / ##module sections.
|
||||
# Default mode is "y" (built-in). Inline comments (#xxx but not ##) are stripped.
|
||||
ENTRIES=$(awk '
|
||||
BEGIN { mode = "y" }
|
||||
/^##built-in/ { mode = "y"; next }
|
||||
/^##module/ { mode = "m"; next }
|
||||
{
|
||||
sub(/#[^#].*/, "") # strip inline comments (single #), preserve ## headers
|
||||
for (i = 1; i <= NF; i++) print $i ":" mode
|
||||
}
|
||||
' "$PKG_FILE")
|
||||
declare -a MISSING=() SKIPPED=() PLANNED=() INVALID=()
|
||||
|
||||
ADDED=()
|
||||
UPGRADED=()
|
||||
MISSING=()
|
||||
while IFS=$'\t' read -r pkg mode; do
|
||||
[ -z "$pkg" ] && continue
|
||||
|
||||
for entry in $ENTRIES; do
|
||||
pkg="${entry%:*}"
|
||||
mode="${entry##*:}"
|
||||
CONFIG_NAME="CONFIG_PACKAGE_${pkg}"
|
||||
|
||||
if [ "$mode" = "y" ]; then
|
||||
# --- built-in mode ---
|
||||
if is_already_builtin "$pkg"; then
|
||||
echo "✅ Already built-in: $pkg"
|
||||
if ! [[ "$pkg" =~ ^[A-Za-z0-9._+-]+$ ]]; then
|
||||
INVALID+=("$pkg")
|
||||
continue
|
||||
fi
|
||||
|
||||
if is_module "$pkg"; then
|
||||
sed -i "s/^CONFIG_PACKAGE_${pkg}=m$/CONFIG_PACKAGE_${pkg}=y/" .config
|
||||
UPGRADED+=("$pkg")
|
||||
echo "⬆️ Module → built-in: $pkg"
|
||||
continue
|
||||
fi
|
||||
else
|
||||
# --- module mode ---
|
||||
if is_module "$pkg"; then
|
||||
echo "✅ Already module: $pkg"
|
||||
sym="CONFIG_PACKAGE_$pkg"
|
||||
cur="${CUR[$sym]:-}"
|
||||
|
||||
# Already provided by the target defaults: nothing to write, no churn.
|
||||
if [ "$mode" = "y" ] && [ -n "${DEF[CONFIG_DEFAULT_$pkg]:-}" ] && [ "$cur" != "m" ]; then
|
||||
SKIPPED+=("$pkg (target default)")
|
||||
continue
|
||||
fi
|
||||
|
||||
if is_already_builtin "$pkg"; then
|
||||
echo "✅ Already built-in (keeping): $pkg"
|
||||
if [ "$cur" = "$mode" ]; then
|
||||
SKIPPED+=("$pkg (already =$mode)")
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if package exists in OpenWrt package metadata.
|
||||
if ! package_exists "$pkg"; then
|
||||
echo "⚠️ Package not found: $pkg"
|
||||
# A removal of something that was never enabled needs no check.
|
||||
if [ "$mode" != "n" ] && ! package_exists "$pkg"; then
|
||||
MISSING+=("$pkg")
|
||||
continue
|
||||
fi
|
||||
|
||||
# Append package config
|
||||
echo "${CONFIG_NAME}=${mode}" >> .config
|
||||
ADDED+=("$pkg (=${mode})")
|
||||
echo "➕ Added: $pkg (=${mode})"
|
||||
done
|
||||
printf '%s\t%s\n' "$sym" "$mode" >> "$DESIRED"
|
||||
PLANNED+=("$pkg: ${cur:-unset} -> $mode")
|
||||
done <<< "$ENTRIES"
|
||||
|
||||
# Run defconfig to sync dependencies
|
||||
echo
|
||||
echo "⚙️ Running make defconfig..."
|
||||
make defconfig >/dev/null
|
||||
|
||||
echo
|
||||
echo "✅ Done!"
|
||||
echo "📋 Packages newly added: ${#ADDED[@]}"
|
||||
[ ${#ADDED[@]} -gt 0 ] && echo " → ${ADDED[*]}"
|
||||
echo "📋 Packages upgraded to built-in: ${#UPGRADED[@]}"
|
||||
[ ${#UPGRADED[@]} -gt 0 ] && echo " → ${UPGRADED[*]}"
|
||||
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||
echo "⚠️ Packages not found (check feeds or spelling):"
|
||||
for m in "${MISSING[@]}"; do
|
||||
echo " - $m"
|
||||
done
|
||||
if [ ${#INVALID[@]} -gt 0 ]; then
|
||||
warn "Ignored ${#INVALID[@]} entries with invalid package names:"
|
||||
printf ' %s\n' "${INVALID[@]}"
|
||||
fi
|
||||
|
||||
if [ ! -s "$DESIRED" ]; then
|
||||
info "Nothing to change; .config already matches $PKG_LABEL."
|
||||
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||
warn "Packages not found (check feeds or spelling):"
|
||||
printf ' %s\n' "${MISSING[@]}"
|
||||
warn "Try: ./scripts/feeds update -a && ./scripts/feeds install -a"
|
||||
[ "$STRICT" = true ] && exit 1
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Merge into .config ---------------------------------------------------
|
||||
# Existing symbols are rewritten where they already are, so the file neither
|
||||
# grows nor reorders; duplicates left behind by older versions collapse.
|
||||
awk -F'\t' '
|
||||
FNR == NR { want[$1] = $2; order[++nk] = $1; next }
|
||||
{
|
||||
key = ""
|
||||
if ($0 ~ /^CONFIG_PACKAGE_[^=]+=/) { key = $0; sub(/=.*$/, "", key) }
|
||||
else if ($0 ~ /^# CONFIG_PACKAGE_[^ ]+ is not set$/) {
|
||||
# Extract by string, not by field: FS is a tab here, so $2 is empty
|
||||
# on .config lines and the symbol would never be matched.
|
||||
key = $0; sub(/^# /, "", key); sub(/ is not set$/, "", key)
|
||||
}
|
||||
if (key != "" && (key in want)) {
|
||||
if (!(key in done)) {
|
||||
done[key] = 1
|
||||
if (want[key] == "n") print "# " key " is not set"
|
||||
else print key "=" want[key]
|
||||
}
|
||||
next
|
||||
}
|
||||
print
|
||||
}
|
||||
END {
|
||||
for (i = 1; i <= nk; i++) {
|
||||
k = order[i]
|
||||
if (k in done) continue
|
||||
if (want[k] == "n") print "# " k " is not set"
|
||||
else print k "=" want[k]
|
||||
}
|
||||
}
|
||||
' "$DESIRED" .config > "$NEWCFG"
|
||||
|
||||
echo
|
||||
echo "========================================"
|
||||
echo " Planned .config changes"
|
||||
echo "========================================"
|
||||
printf ' %s\n' "${PLANNED[@]}"
|
||||
echo " ---"
|
||||
echo " change: ${#PLANNED[@]} unchanged: ${#SKIPPED[@]} not found: ${#MISSING[@]}"
|
||||
echo "========================================"
|
||||
|
||||
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||
warn "Packages not found (check feeds or spelling):"
|
||||
printf ' %s\n' "${MISSING[@]}"
|
||||
warn "Try: ./scripts/feeds update -a && ./scripts/feeds install -a"
|
||||
fi
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo
|
||||
diff -u .config "$NEWCFG" || true
|
||||
echo
|
||||
info "Dry run complete; .config was not modified."
|
||||
[ "$STRICT" = true ] && [ ${#MISSING[@]} -gt 0 ] && exit 1
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo
|
||||
if ! confirm "Apply these changes to .config?" yes; then
|
||||
info "Aborted by user."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ "$KEEP_BACKUP" = true ] && cp -a .config .config.bak
|
||||
mv -f "$NEWCFG" .config
|
||||
chmod 644 .config
|
||||
trap 'rm -f "$DESIRED"' EXIT
|
||||
|
||||
if [ "$RUN_DEFCONFIG" = true ]; then
|
||||
info "Running make defconfig..."
|
||||
make defconfig >/dev/null
|
||||
|
||||
# defconfig silently reverts a removal whose package something else selects,
|
||||
# and can demote =y to =m. Without this check that stays invisible.
|
||||
declare -a REVERTED=()
|
||||
declare -A FINAL=()
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
CONFIG_PACKAGE_*=*) FINAL["${line%%=*}"]="${line#*=}" ;;
|
||||
"# CONFIG_PACKAGE_"*" is not set")
|
||||
s="${line#\# }"
|
||||
FINAL["${s% is not set}"]="n"
|
||||
;;
|
||||
esac
|
||||
done < .config
|
||||
|
||||
while IFS=$'\t' read -r sym mode; do
|
||||
got="${FINAL[$sym]:-unset}"
|
||||
[ "$got" = "$mode" ] || REVERTED+=("${sym#CONFIG_PACKAGE_}: wanted =$mode, got =$got")
|
||||
done < "$DESIRED"
|
||||
|
||||
if [ ${#REVERTED[@]} -gt 0 ]; then
|
||||
warn "make defconfig changed ${#REVERTED[@]} of the requested states:"
|
||||
printf ' %s\n' "${REVERTED[@]}"
|
||||
warn "Usually a dependency pulled the package back in."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "========================================"
|
||||
echo "OK Applied ${#PLANNED[@]} change(s) from $PKG_LABEL"
|
||||
[ "$KEEP_BACKUP" = true ] && echo " Backup: .config.bak"
|
||||
echo "========================================"
|
||||
|
||||
if [ "$STRICT" = true ] && [ ${#MISSING[@]} -gt 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Add specified OpenWRT packages (from a text file) to .config without removing existing ones
|
||||
# Author: Zhe Yuan
|
||||
# Usage:
|
||||
# ./add-openwrt-packages.sh packages.txt
|
||||
# Description:
|
||||
# - Reads package names from a text file (supports both newline or space separation)
|
||||
# - Adds missing CONFIG_PACKAGE_xxx=y entries to .config
|
||||
# - Keeps existing settings intact
|
||||
# - Reports missing packages
|
||||
# - Runs make defconfig at the end to sync dependencies
|
||||
|
||||
set -e
|
||||
umask 022
|
||||
|
||||
# Check argument
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "❌ Usage: $0 <package_list.txt>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PKG_FILE="$1"
|
||||
|
||||
# Verify file exists
|
||||
if [ ! -f "$PKG_FILE" ]; then
|
||||
echo "❌ Error: File not found: $PKG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify we are in OpenWRT source root
|
||||
if [ ! -d "package" ] || [ ! -f "Makefile" ]; then
|
||||
echo "❌ Error: Please run this script in the OpenWRT source root directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure .config exists
|
||||
if [ ! -f ".config" ]; then
|
||||
echo "⚙️ Generating initial .config..."
|
||||
make defconfig
|
||||
fi
|
||||
|
||||
PACKAGE_INDEX="tmp/.config-package.in"
|
||||
PACKAGE_INFO="tmp/.packageinfo"
|
||||
|
||||
# Ensure package metadata exists for reliable package name lookups.
|
||||
if [ ! -f "$PACKAGE_INDEX" ] && [ ! -f "$PACKAGE_INFO" ]; then
|
||||
echo "⚙️ Generating package metadata..."
|
||||
make defconfig >/dev/null
|
||||
fi
|
||||
|
||||
is_already_builtin() {
|
||||
local pkg="$1"
|
||||
|
||||
awk -v package_y="CONFIG_PACKAGE_${pkg}=y" \
|
||||
-v default_y="CONFIG_DEFAULT_${pkg}=y" \
|
||||
'$0 == package_y || $0 == default_y { found=1; exit }
|
||||
END { exit !found }' .config
|
||||
}
|
||||
|
||||
is_module() {
|
||||
local pkg="$1"
|
||||
|
||||
grep -qx "CONFIG_PACKAGE_${pkg}=m" .config
|
||||
}
|
||||
|
||||
package_exists() {
|
||||
local pkg="$1"
|
||||
|
||||
if [ -f "$PACKAGE_INDEX" ] &&
|
||||
awk -v key="PACKAGE_${pkg}" '$1 == "config" && $2 == key { found=1; exit }
|
||||
END { exit !found }' "$PACKAGE_INDEX"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -f "$PACKAGE_INFO" ] && grep -Fqx "Package: $pkg" "$PACKAGE_INFO"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "📦 Reading packages from $PKG_FILE ..."
|
||||
|
||||
# Read package tokens from file, while allowing inline comments.
|
||||
PACKAGES=$(awk '{ sub(/#.*/, ""); for (i = 1; i <= NF; i++) print $i }' "$PKG_FILE")
|
||||
|
||||
ADDED=()
|
||||
MISSING=()
|
||||
|
||||
for pkg in $PACKAGES; do
|
||||
CONFIG_NAME="CONFIG_PACKAGE_${pkg}"
|
||||
|
||||
# Skip if already built-in
|
||||
if is_already_builtin "$pkg"; then
|
||||
echo "✅ Already built-in: $pkg"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Upgrade module to built-in
|
||||
if is_module "$pkg"; then
|
||||
sed -i "s/^CONFIG_PACKAGE_${pkg}=m$/CONFIG_PACKAGE_${pkg}=y/" .config
|
||||
ADDED+=("$pkg")
|
||||
echo "⬆️ Module → built-in: $pkg"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if package exists in OpenWrt package metadata.
|
||||
if ! package_exists "$pkg"; then
|
||||
echo "⚠️ Package not found: $pkg"
|
||||
MISSING+=("$pkg")
|
||||
continue
|
||||
fi
|
||||
|
||||
# Append package config
|
||||
echo "${CONFIG_NAME}=y" >> .config
|
||||
ADDED+=("$pkg")
|
||||
echo "➕ Added: $pkg"
|
||||
done
|
||||
|
||||
# Run defconfig to sync dependencies
|
||||
echo
|
||||
echo "⚙️ Running make defconfig..."
|
||||
make defconfig >/dev/null
|
||||
|
||||
echo
|
||||
echo "✅ Done!"
|
||||
echo "📋 Packages newly added: ${#ADDED[@]}"
|
||||
[ ${#ADDED[@]} -gt 0 ] && echo " → ${ADDED[*]}"
|
||||
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||
echo "⚠️ Packages not found (check feeds or spelling):"
|
||||
for m in "${MISSING[@]}"; do
|
||||
echo " - $m"
|
||||
done
|
||||
fi
|
||||
@@ -1,148 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Apply DAHDI driver patch for OpenWRT 24.10.4
|
||||
# Fixes MAX macro naming issues in dahdi-linux modules
|
||||
# Author: ChatGPT (for Zhe Yuan)
|
||||
|
||||
set -e
|
||||
umask 022
|
||||
|
||||
PATCH_DIR="package/feeds/telephony/dahdi-linux/patches"
|
||||
PATCH_FILE="$PATCH_DIR/300-fix-dahdi-max-attempts.patch"
|
||||
|
||||
echo "========================================"
|
||||
echo " DAHDI Linux Patch Applier for OpenWRT"
|
||||
echo "========================================"
|
||||
|
||||
# --- Check if inside OpenWRT source directory ---
|
||||
if [ ! -f "feeds.conf.default" ] || [ ! -d "package" ]; then
|
||||
echo "[ERROR] This script must be run inside the OpenWRT root directory."
|
||||
echo "Please 'cd' to your OpenWRT source root and run again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Check if dahdi patch directory exists ---
|
||||
if [ ! -d "$PATCH_DIR" ]; then
|
||||
echo "[ERROR] Patch directory not found:"
|
||||
echo " $PATCH_DIR"
|
||||
echo
|
||||
echo "Please make sure telephony feed is installed:"
|
||||
echo " ./scripts/feeds update telephony"
|
||||
echo " ./scripts/feeds install -a"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Create patch file ---
|
||||
echo "[INFO] Writing DAHDI patch to: $PATCH_FILE"
|
||||
cat > "$PATCH_FILE" <<'EOF'
|
||||
--- a/drivers/dahdi/wctdm24xxp/base.c
|
||||
+++ b/drivers/dahdi/wctdm24xxp/base.c
|
||||
@@ -1516,16 +1516,16 @@ static int wait_access(struct wctdm *wc, struct wctdm_module *const mod)
|
||||
{
|
||||
unsigned char data = 0;
|
||||
int count = 0;
|
||||
|
||||
- #define MAX 10 /* attempts */
|
||||
+ #define MAX_ATTEMPTS 10 /* attempts */
|
||||
|
||||
/* Wait for indirect access */
|
||||
- while (count++ < MAX) {
|
||||
+ while (count++ < MAX_ATTEMPTS) {
|
||||
data = wctdm_getreg(wc, mod, I_STATUS);
|
||||
if (!data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
- if (count > (MAX-1)) {
|
||||
+ if (count > (MAX_ATTEMPTS-1)) {
|
||||
dev_notice(&wc->vb.pdev->dev,
|
||||
" ##### Loop error (%02x) #####\n", data);
|
||||
}
|
||||
|
||||
--- a/drivers/dahdi/opvxa1200/base.c
|
||||
+++ b/drivers/dahdi/opvxa1200/base.c
|
||||
@@ -868,12 +868,12 @@ static int wait_access(struct wctdm *wc, int card)
|
||||
long origjiffies;
|
||||
int count = 0;
|
||||
|
||||
- #define MAX 6000 /* attempts */
|
||||
+ #define MAX_ATTEMPTS 6000 /* attempts */
|
||||
|
||||
|
||||
origjiffies = jiffies;
|
||||
/* Wait for indirect access */
|
||||
- while (count++ < MAX)
|
||||
+ while (count++ < MAX_ATTEMPTS)
|
||||
{
|
||||
data = __wctdm_getreg(wc, card, I_STATUS);
|
||||
|
||||
@@ -882,7 +882,7 @@ static int wait_access(struct wctdm *wc, int card)
|
||||
|
||||
}
|
||||
|
||||
- if(count > (MAX-1)) printk(KERN_NOTICE " ##### Loop error (%02x) #####\n", data);
|
||||
+ if(count > (MAX_ATTEMPTS-1)) printk(KERN_NOTICE " ##### Loop error (%02x) #####\n", data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
--- a/drivers/dahdi/wcaxx-base.c
|
||||
+++ b/drivers/dahdi/wcaxx-base.c
|
||||
@@ -1066,16 +1066,16 @@ static int wait_access(struct wcaxx *wc, struct wcaxx_module *const mod)
|
||||
unsigned char data = 0;
|
||||
int count = 0;
|
||||
|
||||
- #define MAX 10 /* attempts */
|
||||
+ #define MAX_ATTEMPTS 10 /* attempts */
|
||||
|
||||
/* Wait for indirect access */
|
||||
- while (count++ < MAX) {
|
||||
+ while (count++ < MAX_ATTEMPTS) {
|
||||
data = wcaxx_getreg(wc, mod, I_STATUS);
|
||||
if (!data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
- if (count > (MAX-1)) {
|
||||
+ if (count > (MAX_ATTEMPTS-1)) {
|
||||
dev_notice(&wc->xb.pdev->dev,
|
||||
" ##### Loop error (%02x) #####\n", data);
|
||||
}
|
||||
|
||||
--- a/drivers/dahdi/wctdm.c
|
||||
+++ b/drivers/dahdi/wctdm.c
|
||||
@@ -623,11 +623,11 @@ static int wait_access(struct wctdm *wc, int card)
|
||||
unsigned char data = 0;
|
||||
int count = 0;
|
||||
|
||||
- #define MAX 6000 /* attempts */
|
||||
+ #define MAX_ATTEMPTS 6000 /* attempts */
|
||||
|
||||
|
||||
/* Wait for indirect access */
|
||||
- while (count++ < MAX)
|
||||
+ while (count++ < MAX_ATTEMPTS)
|
||||
{
|
||||
data = __wctdm_getreg(wc, card, I_STATUS);
|
||||
|
||||
@@ -636,7 +636,7 @@ static int wait_access(struct wctdm *wc, int card)
|
||||
|
||||
}
|
||||
|
||||
- if(count > (MAX-1)) printk(KERN_NOTICE " ##### Loop error (%02x) #####\n", data);
|
||||
+ if(count > (MAX_ATTEMPTS-1)) printk(KERN_NOTICE " ##### Loop error (%02x) #####\n", data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "[SUCCESS] Patch written successfully!"
|
||||
echo
|
||||
echo "========================================"
|
||||
echo "✅ DAHDI patch applied successfully!"
|
||||
echo " File: $PATCH_FILE"
|
||||
echo "========================================"
|
||||
echo
|
||||
echo "👉 Next step:"
|
||||
echo " make package/feeds/telephony/dahdi-linux/{clean,prepare} V=s"
|
||||
echo " make package/feeds/telephony/dahdi-linux/compile V=s"
|
||||
echo
|
||||
@@ -1,193 +0,0 @@
|
||||
CONFIG_TARGET_mediatek=y
|
||||
CONFIG_TARGET_mediatek_filogic=y
|
||||
CONFIG_TARGET_mediatek_filogic_DEVICE_cudy_tr3000-v1=y
|
||||
CONFIG_ALL_KMODS=y
|
||||
CONFIG_ALL_NONSHARED=y
|
||||
CONFIG_DEVEL=y
|
||||
CONFIG_AUTOREMOVE=y
|
||||
CONFIG_BUILDBOT=y
|
||||
CONFIG_COLLECT_KERNEL_DEBUG=y
|
||||
CONFIG_EASYTIER_INCLUDE_WEBCONSOLE=y
|
||||
CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT="/usr/lib/go-1.22/"
|
||||
CONFIG_IB=y
|
||||
CONFIG_IMAGEOPT=y
|
||||
CONFIG_JSON_CYCLONEDX_SBOM=y
|
||||
CONFIG_KERNEL_BUILD_DOMAIN="buildhost"
|
||||
CONFIG_KERNEL_BUILD_USER="builder"
|
||||
# CONFIG_KERNEL_KALLSYMS is not set
|
||||
CONFIG_LIBCURL_COOKIES=y
|
||||
CONFIG_LIBCURL_FILE=y
|
||||
CONFIG_LIBCURL_FTP=y
|
||||
CONFIG_LIBCURL_HTTP=y
|
||||
CONFIG_LIBCURL_MBEDTLS=y
|
||||
CONFIG_LIBCURL_NGHTTP2=y
|
||||
CONFIG_LIBCURL_NO_SMB="!"
|
||||
CONFIG_LIBCURL_PROXY=y
|
||||
CONFIG_LIBCURL_UNIX_SOCKETS=y
|
||||
CONFIG_MAKE_TOOLCHAIN=y
|
||||
CONFIG_OPENSSL_ENGINE=y
|
||||
CONFIG_OPENSSL_WITH_ASM=y
|
||||
CONFIG_OPENSSL_WITH_CHACHA_POLY1305=y
|
||||
CONFIG_OPENSSL_WITH_CMS=y
|
||||
CONFIG_OPENSSL_WITH_DEPRECATED=y
|
||||
CONFIG_OPENSSL_WITH_ERROR_MESSAGES=y
|
||||
CONFIG_OPENSSL_WITH_IDEA=y
|
||||
CONFIG_OPENSSL_WITH_MDC2=y
|
||||
CONFIG_OPENSSL_WITH_PSK=y
|
||||
CONFIG_OPENSSL_WITH_SEED=y
|
||||
CONFIG_OPENSSL_WITH_SRP=y
|
||||
CONFIG_OPENSSL_WITH_TLS13=y
|
||||
CONFIG_OPENSSL_WITH_WHIRLPOOL=y
|
||||
CONFIG_PACKAGE_adguardhome=y
|
||||
CONFIG_PACKAGE_ca-certificates=y
|
||||
CONFIG_PACKAGE_cgi-io=y
|
||||
CONFIG_PACKAGE_curl=y
|
||||
CONFIG_PACKAGE_e2fsprogs=m
|
||||
CONFIG_PACKAGE_easytier=y
|
||||
CONFIG_PACKAGE_etherwake=y
|
||||
CONFIG_PACKAGE_f2fsck=m
|
||||
CONFIG_PACKAGE_fdisk=m
|
||||
CONFIG_PACKAGE_ip-full=y
|
||||
CONFIG_PACKAGE_ip6tables-nft=y
|
||||
CONFIG_PACKAGE_iperf3=y
|
||||
CONFIG_PACKAGE_ipset=y
|
||||
CONFIG_PACKAGE_iptables-mod-conntrack-extra=y
|
||||
CONFIG_PACKAGE_iptables-mod-ipopt=y
|
||||
CONFIG_PACKAGE_iptables-nft=y
|
||||
CONFIG_PACKAGE_kmod-inet-diag=y
|
||||
CONFIG_PACKAGE_kmod-ip6tables=y
|
||||
CONFIG_PACKAGE_kmod-ipt-conntrack=y
|
||||
CONFIG_PACKAGE_kmod-ipt-conntrack-extra=y
|
||||
CONFIG_PACKAGE_kmod-ipt-core=y
|
||||
CONFIG_PACKAGE_kmod-ipt-ipopt=y
|
||||
CONFIG_PACKAGE_kmod-ipt-ipset=y
|
||||
CONFIG_PACKAGE_kmod-nf-conncount=y
|
||||
CONFIG_PACKAGE_kmod-nf-ipt=y
|
||||
CONFIG_PACKAGE_kmod-nf-ipt6=y
|
||||
CONFIG_PACKAGE_kmod-nf-socket=y
|
||||
CONFIG_PACKAGE_kmod-nf-tproxy=y
|
||||
CONFIG_PACKAGE_kmod-nft-compat=y
|
||||
CONFIG_PACKAGE_kmod-nft-socket=y
|
||||
CONFIG_PACKAGE_kmod-nft-tproxy=y
|
||||
CONFIG_PACKAGE_kmod-tun=y
|
||||
CONFIG_PACKAGE_libatomic=y
|
||||
CONFIG_PACKAGE_libblkid=m
|
||||
CONFIG_PACKAGE_libbpf=y
|
||||
CONFIG_PACKAGE_libbz2=y
|
||||
CONFIG_PACKAGE_libcap=y
|
||||
CONFIG_PACKAGE_libcap-ng=y
|
||||
CONFIG_PACKAGE_libcomerr=m
|
||||
CONFIG_PACKAGE_libcurl=y
|
||||
CONFIG_PACKAGE_libe2p=m
|
||||
CONFIG_PACKAGE_libelf=y
|
||||
CONFIG_PACKAGE_libext2fs=m
|
||||
CONFIG_PACKAGE_libf2fs=m
|
||||
CONFIG_PACKAGE_libfdisk=m
|
||||
CONFIG_PACKAGE_libiperf3=y
|
||||
CONFIG_PACKAGE_libipset=y
|
||||
CONFIG_PACKAGE_libiptext=y
|
||||
CONFIG_PACKAGE_libiptext-nft=y
|
||||
CONFIG_PACKAGE_libiptext6=y
|
||||
CONFIG_PACKAGE_liblua=y
|
||||
CONFIG_PACKAGE_liblucihttp=y
|
||||
CONFIG_PACKAGE_liblucihttp-lua=y
|
||||
CONFIG_PACKAGE_liblucihttp-ucode=y
|
||||
CONFIG_PACKAGE_libminiupnpc=y
|
||||
CONFIG_PACKAGE_libnatpmp=y
|
||||
CONFIG_PACKAGE_libncurses=m
|
||||
CONFIG_PACKAGE_libnghttp2=y
|
||||
CONFIG_PACKAGE_libopenssl=y
|
||||
CONFIG_PACKAGE_libpython3=y
|
||||
CONFIG_PACKAGE_librespeed-go=y
|
||||
CONFIG_PACKAGE_librt=y
|
||||
CONFIG_PACKAGE_libsmartcols=m
|
||||
CONFIG_PACKAGE_libss=m
|
||||
CONFIG_PACKAGE_libstdcpp=y
|
||||
CONFIG_PACKAGE_libubus-lua=y
|
||||
CONFIG_PACKAGE_libuuid=y
|
||||
CONFIG_PACKAGE_libuv=y
|
||||
CONFIG_PACKAGE_libwebsockets-full=y
|
||||
CONFIG_PACKAGE_libxtables=y
|
||||
CONFIG_PACKAGE_lua=y
|
||||
CONFIG_PACKAGE_luci=y
|
||||
CONFIG_PACKAGE_luci-app-easytier=y
|
||||
CONFIG_PACKAGE_luci-app-filemanager=y
|
||||
CONFIG_PACKAGE_luci-app-firewall=y
|
||||
CONFIG_PACKAGE_luci-app-mwan3=y
|
||||
CONFIG_PACKAGE_luci-app-netspeedtest=y
|
||||
CONFIG_PACKAGE_luci-app-package-manager=y
|
||||
CONFIG_PACKAGE_luci-app-ttyd=y
|
||||
CONFIG_PACKAGE_luci-app-upnp=y
|
||||
CONFIG_PACKAGE_luci-app-wol=y
|
||||
CONFIG_PACKAGE_luci-base=y
|
||||
CONFIG_PACKAGE_luci-compat=y
|
||||
CONFIG_PACKAGE_luci-lib-base=y
|
||||
CONFIG_PACKAGE_luci-lib-ip=y
|
||||
CONFIG_PACKAGE_luci-lib-jsonc=y
|
||||
CONFIG_PACKAGE_luci-lib-nixio=y
|
||||
CONFIG_PACKAGE_luci-light=y
|
||||
CONFIG_PACKAGE_luci-lua-runtime=y
|
||||
CONFIG_PACKAGE_luci-mod-admin-full=y
|
||||
CONFIG_PACKAGE_luci-mod-network=y
|
||||
CONFIG_PACKAGE_luci-mod-status=y
|
||||
CONFIG_PACKAGE_luci-mod-system=y
|
||||
CONFIG_PACKAGE_luci-proto-ipv6=y
|
||||
CONFIG_PACKAGE_luci-proto-ppp=y
|
||||
CONFIG_PACKAGE_luci-ssl=y
|
||||
CONFIG_PACKAGE_luci-theme-bootstrap=y
|
||||
CONFIG_PACKAGE_miniupnpd-nftables=y
|
||||
CONFIG_PACKAGE_mkf2fs=m
|
||||
CONFIG_PACKAGE_mt7986-wo-firmware=m
|
||||
CONFIG_PACKAGE_mt7988-2p5g-phy-firmware=m
|
||||
CONFIG_PACKAGE_mt7988-wo-firmware=m
|
||||
CONFIG_PACKAGE_mwan3=y
|
||||
CONFIG_PACKAGE_partx-utils=m
|
||||
CONFIG_PACKAGE_px5g-mbedtls=y
|
||||
CONFIG_PACKAGE_python3-base=y
|
||||
CONFIG_PACKAGE_python3-codecs=y
|
||||
CONFIG_PACKAGE_python3-email=y
|
||||
CONFIG_PACKAGE_python3-light=y
|
||||
CONFIG_PACKAGE_python3-logging=y
|
||||
CONFIG_PACKAGE_python3-openssl=y
|
||||
CONFIG_PACKAGE_python3-pkg-resources=y
|
||||
CONFIG_PACKAGE_python3-speedtest-cli=y
|
||||
CONFIG_PACKAGE_python3-urllib=y
|
||||
CONFIG_PACKAGE_python3-xml=y
|
||||
CONFIG_PACKAGE_rpcd=y
|
||||
CONFIG_PACKAGE_rpcd-mod-file=y
|
||||
CONFIG_PACKAGE_rpcd-mod-iwinfo=y
|
||||
CONFIG_PACKAGE_rpcd-mod-luci=y
|
||||
CONFIG_PACKAGE_rpcd-mod-rrdns=y
|
||||
CONFIG_PACKAGE_rpcd-mod-ucode=y
|
||||
CONFIG_PACKAGE_tailscale=y
|
||||
CONFIG_PACKAGE_terminfo=m
|
||||
CONFIG_PACKAGE_ttyd=y
|
||||
CONFIG_PACKAGE_ucode-mod-html=y
|
||||
CONFIG_PACKAGE_ucode-mod-lua=y
|
||||
CONFIG_PACKAGE_ucode-mod-math=y
|
||||
CONFIG_PACKAGE_uhttpd=y
|
||||
CONFIG_PACKAGE_uhttpd-mod-ubus=y
|
||||
CONFIG_PACKAGE_unzip=y
|
||||
CONFIG_PACKAGE_wpad=y
|
||||
# CONFIG_PACKAGE_wpad-basic-mbedtls is not set
|
||||
CONFIG_PACKAGE_xtables-nft=y
|
||||
CONFIG_PACKAGE_yq=y
|
||||
CONFIG_PACKAGE_zerotier=y
|
||||
CONFIG_PACKAGE_zlib=y
|
||||
CONFIG_PACKAGE_zyxel-bootconfig=m
|
||||
CONFIG_REPRODUCIBLE_DEBUG_INFO=y
|
||||
CONFIG_SDK=y
|
||||
CONFIG_SDK_LLVM_BPF=y
|
||||
CONFIG_VERSIONOPT=y
|
||||
CONFIG_VERSION_BUG_URL=""
|
||||
CONFIG_VERSION_CODE=""
|
||||
CONFIG_VERSION_DIST="OpenWrt"
|
||||
CONFIG_VERSION_FILENAMES=y
|
||||
CONFIG_VERSION_HOME_URL=""
|
||||
CONFIG_VERSION_HWREV=""
|
||||
CONFIG_VERSION_MANUFACTURER=""
|
||||
CONFIG_VERSION_MANUFACTURER_URL=""
|
||||
CONFIG_VERSION_NUMBER=""
|
||||
CONFIG_VERSION_PRODUCT=""
|
||||
CONFIG_VERSION_REPO="https://downloads.openwrt.org/releases/24.10.4"
|
||||
CONFIG_VERSION_SUPPORT_URL=""
|
||||
@@ -1,196 +0,0 @@
|
||||
CONFIG_TARGET_qualcommax=y
|
||||
CONFIG_TARGET_qualcommax_ipq807x=y
|
||||
CONFIG_TARGET_qualcommax_ipq807x_DEVICE_linksys_mx8500=y
|
||||
CONFIG_ALL_KMODS=y
|
||||
CONFIG_ALL_NONSHARED=y
|
||||
CONFIG_DEVEL=y
|
||||
CONFIG_AUTOREMOVE=y
|
||||
CONFIG_BUILDBOT=y
|
||||
CONFIG_COLLECT_KERNEL_DEBUG=y
|
||||
CONFIG_EASYTIER_INCLUDE_WEBCONSOLE=y
|
||||
CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT="/usr/lib/go-1.22/"
|
||||
CONFIG_IB=y
|
||||
CONFIG_IMAGEOPT=y
|
||||
CONFIG_JSON_CYCLONEDX_SBOM=y
|
||||
CONFIG_KERNEL_BUILD_DOMAIN="buildhost"
|
||||
CONFIG_KERNEL_BUILD_USER="builder"
|
||||
# CONFIG_KERNEL_KALLSYMS is not set
|
||||
CONFIG_LIBCURL_COOKIES=y
|
||||
CONFIG_LIBCURL_FILE=y
|
||||
CONFIG_LIBCURL_FTP=y
|
||||
CONFIG_LIBCURL_HTTP=y
|
||||
CONFIG_LIBCURL_MBEDTLS=y
|
||||
CONFIG_LIBCURL_NGHTTP2=y
|
||||
CONFIG_LIBCURL_NO_SMB="!"
|
||||
CONFIG_LIBCURL_PROXY=y
|
||||
CONFIG_LIBCURL_UNIX_SOCKETS=y
|
||||
CONFIG_MAKE_TOOLCHAIN=y
|
||||
CONFIG_OPENSSL_ENGINE=y
|
||||
CONFIG_OPENSSL_WITH_ASM=y
|
||||
CONFIG_OPENSSL_WITH_CHACHA_POLY1305=y
|
||||
CONFIG_OPENSSL_WITH_CMS=y
|
||||
CONFIG_OPENSSL_WITH_DEPRECATED=y
|
||||
CONFIG_OPENSSL_WITH_ERROR_MESSAGES=y
|
||||
CONFIG_OPENSSL_WITH_IDEA=y
|
||||
CONFIG_OPENSSL_WITH_MDC2=y
|
||||
CONFIG_OPENSSL_WITH_PSK=y
|
||||
CONFIG_OPENSSL_WITH_SEED=y
|
||||
CONFIG_OPENSSL_WITH_SRP=y
|
||||
CONFIG_OPENSSL_WITH_TLS13=y
|
||||
CONFIG_OPENSSL_WITH_WHIRLPOOL=y
|
||||
CONFIG_PACKAGE_adguardhome=y
|
||||
CONFIG_PACKAGE_ath10k-board-qca9887=m
|
||||
CONFIG_PACKAGE_ath10k-board-qca9984=m
|
||||
CONFIG_PACKAGE_ath10k-firmware-qca9887-ct=m
|
||||
CONFIG_PACKAGE_ath10k-firmware-qca9984-ct=m
|
||||
CONFIG_PACKAGE_ca-certificates=y
|
||||
CONFIG_PACKAGE_cgi-io=y
|
||||
CONFIG_PACKAGE_cloudflared=y
|
||||
CONFIG_PACKAGE_curl=y
|
||||
CONFIG_PACKAGE_easytier=y
|
||||
CONFIG_PACKAGE_etherwake=y
|
||||
CONFIG_PACKAGE_f2fs-tools=m
|
||||
CONFIG_PACKAGE_f2fsck=m
|
||||
CONFIG_PACKAGE_ip-full=y
|
||||
CONFIG_PACKAGE_ip-tiny=y
|
||||
CONFIG_PACKAGE_ip6tables-nft=y
|
||||
CONFIG_PACKAGE_iperf3=y
|
||||
CONFIG_PACKAGE_ipset=y
|
||||
CONFIG_PACKAGE_iptables-mod-conntrack-extra=y
|
||||
CONFIG_PACKAGE_iptables-mod-ipopt=y
|
||||
CONFIG_PACKAGE_iptables-nft=y
|
||||
CONFIG_PACKAGE_kmod-crypto-lib-chacha20=y
|
||||
CONFIG_PACKAGE_kmod-crypto-lib-chacha20poly1305=y
|
||||
CONFIG_PACKAGE_kmod-crypto-lib-curve25519=y
|
||||
CONFIG_PACKAGE_kmod-crypto-lib-poly1305=y
|
||||
CONFIG_PACKAGE_kmod-inet-diag=y
|
||||
CONFIG_PACKAGE_kmod-ip6tables=y
|
||||
CONFIG_PACKAGE_kmod-ipt-conntrack=y
|
||||
CONFIG_PACKAGE_kmod-ipt-conntrack-extra=y
|
||||
CONFIG_PACKAGE_kmod-ipt-core=y
|
||||
CONFIG_PACKAGE_kmod-ipt-ipopt=y
|
||||
CONFIG_PACKAGE_kmod-ipt-ipset=y
|
||||
CONFIG_PACKAGE_kmod-mtd-rw=y
|
||||
CONFIG_PACKAGE_kmod-nf-conncount=y
|
||||
CONFIG_PACKAGE_kmod-nf-ipt=y
|
||||
CONFIG_PACKAGE_kmod-nf-ipt6=y
|
||||
CONFIG_PACKAGE_kmod-nf-socket=y
|
||||
CONFIG_PACKAGE_kmod-nf-tproxy=y
|
||||
CONFIG_PACKAGE_kmod-nft-compat=y
|
||||
CONFIG_PACKAGE_kmod-nft-socket=y
|
||||
CONFIG_PACKAGE_kmod-nft-tproxy=y
|
||||
CONFIG_PACKAGE_kmod-tun=y
|
||||
CONFIG_PACKAGE_kmod-udptunnel4=y
|
||||
CONFIG_PACKAGE_kmod-udptunnel6=y
|
||||
CONFIG_PACKAGE_kmod-wireguard=y
|
||||
CONFIG_PACKAGE_libatomic=y
|
||||
CONFIG_PACKAGE_libbpf=y
|
||||
CONFIG_PACKAGE_libbz2=y
|
||||
CONFIG_PACKAGE_libcap=y
|
||||
CONFIG_PACKAGE_libcap-ng=y
|
||||
CONFIG_PACKAGE_libcurl=y
|
||||
CONFIG_PACKAGE_libelf=y
|
||||
CONFIG_PACKAGE_libf2fs=m
|
||||
CONFIG_PACKAGE_libiperf3=y
|
||||
CONFIG_PACKAGE_libipset=y
|
||||
CONFIG_PACKAGE_libiptext=y
|
||||
CONFIG_PACKAGE_libiptext-nft=y
|
||||
CONFIG_PACKAGE_libiptext6=y
|
||||
CONFIG_PACKAGE_liblua=y
|
||||
CONFIG_PACKAGE_liblucihttp=y
|
||||
CONFIG_PACKAGE_liblucihttp-lua=y
|
||||
CONFIG_PACKAGE_liblucihttp-ucode=y
|
||||
CONFIG_PACKAGE_libminiupnpc=y
|
||||
CONFIG_PACKAGE_libnatpmp=y
|
||||
CONFIG_PACKAGE_libnghttp2=y
|
||||
CONFIG_PACKAGE_libopenssl=y
|
||||
CONFIG_PACKAGE_libpython3=y
|
||||
CONFIG_PACKAGE_libqrencode=y
|
||||
CONFIG_PACKAGE_librespeed-go=y
|
||||
CONFIG_PACKAGE_libstdcpp=y
|
||||
CONFIG_PACKAGE_libubus-lua=y
|
||||
CONFIG_PACKAGE_libuv=y
|
||||
CONFIG_PACKAGE_libwebsockets-full=y
|
||||
CONFIG_PACKAGE_libxtables=y
|
||||
CONFIG_PACKAGE_lua=y
|
||||
CONFIG_PACKAGE_luci=y
|
||||
CONFIG_PACKAGE_luci-app-cloudflared=y
|
||||
CONFIG_PACKAGE_luci-app-easytier=y
|
||||
CONFIG_PACKAGE_luci-app-filemanager=y
|
||||
CONFIG_PACKAGE_luci-app-firewall=y
|
||||
CONFIG_PACKAGE_luci-app-mwan3=y
|
||||
CONFIG_PACKAGE_luci-app-netspeedtest=y
|
||||
CONFIG_PACKAGE_luci-app-openvpn=y
|
||||
CONFIG_PACKAGE_luci-app-package-manager=y
|
||||
CONFIG_PACKAGE_luci-app-ttyd=y
|
||||
CONFIG_PACKAGE_luci-app-upnp=y
|
||||
CONFIG_PACKAGE_luci-app-wol=y
|
||||
CONFIG_PACKAGE_luci-base=y
|
||||
CONFIG_PACKAGE_luci-compat=y
|
||||
CONFIG_PACKAGE_luci-lib-base=y
|
||||
CONFIG_PACKAGE_luci-lib-ip=y
|
||||
CONFIG_PACKAGE_luci-lib-jsonc=y
|
||||
CONFIG_PACKAGE_luci-lib-nixio=y
|
||||
CONFIG_PACKAGE_luci-lib-uqr=y
|
||||
CONFIG_PACKAGE_luci-light=y
|
||||
CONFIG_PACKAGE_luci-lua-runtime=y
|
||||
CONFIG_PACKAGE_luci-mod-admin-full=y
|
||||
CONFIG_PACKAGE_luci-mod-network=y
|
||||
CONFIG_PACKAGE_luci-mod-status=y
|
||||
CONFIG_PACKAGE_luci-mod-system=y
|
||||
CONFIG_PACKAGE_luci-proto-ipv6=y
|
||||
CONFIG_PACKAGE_luci-proto-ppp=y
|
||||
CONFIG_PACKAGE_luci-proto-wireguard=y
|
||||
CONFIG_PACKAGE_luci-ssl=y
|
||||
CONFIG_PACKAGE_luci-theme-bootstrap=y
|
||||
CONFIG_PACKAGE_miniupnpd-nftables=y
|
||||
CONFIG_PACKAGE_mkf2fs=m
|
||||
CONFIG_PACKAGE_mwan3=y
|
||||
CONFIG_PACKAGE_px5g-mbedtls=y
|
||||
CONFIG_PACKAGE_python3-base=y
|
||||
CONFIG_PACKAGE_python3-codecs=y
|
||||
CONFIG_PACKAGE_python3-email=y
|
||||
CONFIG_PACKAGE_python3-light=y
|
||||
CONFIG_PACKAGE_python3-logging=y
|
||||
CONFIG_PACKAGE_python3-openssl=y
|
||||
CONFIG_PACKAGE_python3-pkg-resources=y
|
||||
CONFIG_PACKAGE_python3-speedtest-cli=y
|
||||
CONFIG_PACKAGE_python3-urllib=y
|
||||
CONFIG_PACKAGE_python3-xml=y
|
||||
CONFIG_PACKAGE_qrencode=y
|
||||
CONFIG_PACKAGE_resolveip=y
|
||||
CONFIG_PACKAGE_rpcd=y
|
||||
CONFIG_PACKAGE_rpcd-mod-file=y
|
||||
CONFIG_PACKAGE_rpcd-mod-iwinfo=y
|
||||
CONFIG_PACKAGE_rpcd-mod-luci=y
|
||||
CONFIG_PACKAGE_rpcd-mod-rrdns=y
|
||||
CONFIG_PACKAGE_rpcd-mod-ucode=y
|
||||
CONFIG_PACKAGE_tailscale=y
|
||||
CONFIG_PACKAGE_ttyd=y
|
||||
CONFIG_PACKAGE_ucode-mod-html=y
|
||||
CONFIG_PACKAGE_ucode-mod-lua=y
|
||||
CONFIG_PACKAGE_ucode-mod-math=y
|
||||
CONFIG_PACKAGE_uhttpd=y
|
||||
CONFIG_PACKAGE_uhttpd-mod-ubus=y
|
||||
CONFIG_PACKAGE_unzip=y
|
||||
CONFIG_PACKAGE_wireguard-tools=y
|
||||
CONFIG_PACKAGE_xtables-nft=y
|
||||
CONFIG_PACKAGE_yq=y
|
||||
CONFIG_PACKAGE_zerotier=y
|
||||
CONFIG_PACKAGE_zlib=y
|
||||
CONFIG_REPRODUCIBLE_DEBUG_INFO=y
|
||||
CONFIG_SDK=y
|
||||
CONFIG_SDK_LLVM_BPF=y
|
||||
CONFIG_VERSIONOPT=y
|
||||
CONFIG_VERSION_BUG_URL=""
|
||||
CONFIG_VERSION_CODE=""
|
||||
CONFIG_VERSION_DIST="OpenWrt"
|
||||
CONFIG_VERSION_FILENAMES=y
|
||||
CONFIG_VERSION_HOME_URL=""
|
||||
CONFIG_VERSION_HWREV=""
|
||||
CONFIG_VERSION_MANUFACTURER=""
|
||||
CONFIG_VERSION_MANUFACTURER_URL=""
|
||||
CONFIG_VERSION_NUMBER=""
|
||||
CONFIG_VERSION_PRODUCT=""
|
||||
CONFIG_VERSION_REPO="https://downloads.openwrt.org/releases/24.10.4"
|
||||
CONFIG_VERSION_SUPPORT_URL=""
|
||||
@@ -1,3 +0,0 @@
|
||||
base-files ca-bundle dnsmasq dropbear firewall4 fitblk fstools kmod-crypto-hw-safexcel kmod-gpio-button-hotplug kmod-leds-gpio kmod-nft-offload kmod-phy-aquantia libc libgcc libustream-mbedtls logd mtd netifd nftables odhcp6c odhcpd-ipv6only opkg ppp ppp-mod-pppoe procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd wpad-basic-mbedtls kmod-usb3 kmod-mt7915e kmod-mt7981-firmware mt7981-wo-firmware luci
|
||||
curl yq unzip ip-full kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft luci-app-openvpn luci-proto-wireguard luci-app-cloudflared qrencode tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
@@ -1,3 +0,0 @@
|
||||
base-files ca-bundle dnsmasq dropbear firewall4 fitblk fstools kmod-crypto-hw-safexcel kmod-gpio-button-hotplug kmod-leds-gpio kmod-nft-offload kmod-phy-aquantia libc libgcc libustream-mbedtls logd mtd netifd nftables odhcp6c odhcpd-ipv6only opkg ppp ppp-mod-pppoe procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd wpad kmod-usb3 kmod-mt7915e kmod-mt7981-firmware mt7981-wo-firmware luci
|
||||
curl yq unzip ip-full kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
@@ -1,3 +0,0 @@
|
||||
ath11k-firmware-ipq8074 base-files ca-bundle dnsmasq dropbear e2fsprogs firewall4 fstools kmod-ath11k-ahb kmod-fs-ext4 kmod-gpio-button-hotplug kmod-leds-gpio kmod-nft-offload kmod-phy-aquantia kmod-qca-nss-dp kmod-usb-dwc3 kmod-usb-dwc3-qcom kmod-usb3 libc libgcc libustream-mbedtls logd losetup mtd netifd nftables odhcp6c odhcpd-ipv6only opkg ppp ppp-mod-pppoe procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd wpad-basic-mbedtls kmod-leds-pca963x ipq-wifi-linksys_mx8500 kmod-ath11k-pci ath11k-firmware-qcn9074 kmod-bluetooth luci
|
||||
curl yq unzip ip-full kmod-mtd-rw kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft luci-app-openvpn luci-proto-wireguard luci-app-cloudflared qrencode tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
apk-mbedtls base-files ca-bundle dnsmasq dropbear firewall4 fitblk fstools kmod-crypto-hw-safexcel kmod-gpio-button-hotplug kmod-leds-gpio kmod-nft-offload libc libgcc libustream-mbedtls logd mtd netifd nftables odhcp6c odhcpd-ipv6only ppp ppp-mod-pppoe procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd wpad-basic-mbedtls kmod-usb3 kmod-mt7915e kmod-mt7981-firmware mt7981-wo-firmware luci
|
||||
curl yq unzip ip-full kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
@@ -1,3 +0,0 @@
|
||||
apk-mbedtls ath11k-firmware-ipq8074 base-files ca-bundle dnsmasq dropbear e2fsprogs firewall4 fstools kmod-ath11k-ahb kmod-fs-ext4 kmod-gpio-button-hotplug kmod-leds-gpio kmod-nft-offload kmod-phy-aquantia kmod-qca-nss-dp kmod-usb-dwc3 kmod-usb-dwc3-qcom kmod-usb3 libc libgcc libustream-mbedtls logd losetup mtd netifd nftables odhcp6c odhcpd-ipv6only ppp ppp-mod-pppoe procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd wpad-basic-mbedtls kmod-leds-pca963x ipq-wifi-linksys_mx8500 kmod-ath11k-pci ath11k-firmware-qcn9074 kmod-hci-uart luci
|
||||
curl yq unzip ip-full kmod-mtd-rw kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft luci-app-openvpn luci-proto-wireguard luci-app-cloudflared qrencode tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
##built-in
|
||||
|
||||
apk-mbedtls base-files ca-bundle dnsmasq dropbear firewall4 fitblk fstools kmod-crypto-hw-safexcel kmod-gpio-button-hotplug kmod-leds-gpio kmod-nft-offload libc libgcc libustream-mbedtls logd mtd netifd nftables odhcp6c odhcpd-ipv6only ppp ppp-mod-pppoe procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd wpad kmod-usb3 kmod-mt7915e kmod-mt7981-firmware mt7981-wo-firmware luci luci-app-attendedsysupgrade
|
||||
|
||||
curl yq unzip ip-full kmod-mtd-rw kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft
|
||||
|
||||
luci-app-openvpn luci-proto-wireguard luci-app-cloudflared qrencode tailscale zerotier adguardhome
|
||||
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
|
||||
##module
|
||||
@@ -1,9 +0,0 @@
|
||||
##built-in
|
||||
|
||||
apk-mbedtls base-files ca-bundle dnsmasq dropbear firewall4 fitblk fstools kmod-crypto-hw-safexcel kmod-gpio-button-hotplug kmod-leds-gpio kmod-nft-offload libc libgcc libustream-mbedtls logd mtd netifd nftables odhcp6c odhcpd-ipv6only ppp ppp-mod-pppoe procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd wpad kmod-usb3 kmod-mt7915e kmod-mt7981-firmware mt7981-wo-firmware luci luci-app-attendedsysupgrade
|
||||
iptables-nft ip6tables-nft curl yq unzip ip-full kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun
|
||||
|
||||
luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
|
||||
##module
|
||||
@@ -1,8 +0,0 @@
|
||||
##built-in
|
||||
|
||||
apk-mbedtls ath11k-firmware-ipq8074 base-files ca-bundle dnsmasq dropbear e2fsprogs firewall4 fstools kmod-ath11k-ahb kmod-fs-ext4 kmod-gpio-button-hotplug kmod-leds-gpio kmod-nft-offload kmod-phy-aquantia kmod-qca-nss-dp kmod-usb-dwc3 kmod-usb-dwc3-qcom kmod-usb3 libc libgcc libustream-mbedtls logd losetup mtd netifd nftables odhcp6c odhcpd-ipv6only ppp ppp-mod-pppoe procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd wpad-basic-mbedtls kmod-leds-pca963x ipq-wifi-linksys_mx8500 kmod-ath11k-pci ath11k-firmware-qcn9074 kmod-hci-uart luci luci-app-attendedsysupgrade
|
||||
curl yq unzip ip-full kmod-mtd-rw kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft
|
||||
|
||||
luci-app-openvpn luci-proto-wireguard luci-app-cloudflared qrencode tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
##module
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
##built-in
|
||||
|
||||
apk-mbedtls ath11k-firmware-ipq8074 base-files ca-bundle dnsmasq dropbear e2fsprogs firewall4 fstools kmod-ath11k-ahb kmod-fs-ext4 kmod-gpio-button-hotplug kmod-leds-gpio kmod-nft-offload kmod-phy-aquantia kmod-qca-nss-dp kmod-usb-dwc3 kmod-usb-dwc3-qcom kmod-usb3 libc libgcc libustream-mbedtls logd losetup mtd netifd nftables odhcp6c odhcpd-ipv6only ppp ppp-mod-pppoe procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd wpad-basic-mbedtls kmod-leds-pca963x ipq-wifi-linksys_mx8500 kmod-ath11k-pci ath11k-firmware-qcn9074 kmod-hci-uart luci luci-app-attendedsysupgrade
|
||||
curl yq unzip ip-full kmod-mtd-rw kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft
|
||||
|
||||
luci-app-openvpn luci-proto-wireguard luci-app-cloudflared qrencode tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
##module
|
||||
@@ -1,96 +1,277 @@
|
||||
#!/bin/bash
|
||||
# Download OpenWRT config.buildinfo, generate .config and run defconfig
|
||||
# Fetch the official config.buildinfo for a device and install it as .config.
|
||||
#
|
||||
# The device is looked up in the official index, so any of the ~1900 supported
|
||||
# devices can be selected by name; nothing is hardcoded.
|
||||
#
|
||||
# Two things are changed in the downloaded config by default, because it is
|
||||
# the buildbot's configuration rather than a personal one:
|
||||
# - only the selected device is built, instead of every model in the target
|
||||
# - the buildbot flags (ALL_KMODS, ALL_NONSHARED, SDK, IB, MAKE_TOOLCHAIN,
|
||||
# COLLECT_KERNEL_DEBUG, AUTOREMOVE, ...) are turned off
|
||||
# Neither changes the contents of the firmware image; they only stop the build
|
||||
# from also producing every optional module, the SDK and the ImageBuilder.
|
||||
#
|
||||
# Usage: ./download-config.sh [options]
|
||||
#
|
||||
# --device <id> profile id, e.g. linksys_mx8500
|
||||
# --target <t> disambiguate an id present in several targets
|
||||
# --version <ver> override version detection (e.g. 25.12.5)
|
||||
# --snapshot shorthand for --version SNAPSHOT
|
||||
# --with-packages also refresh the device's package list file
|
||||
# --all-profiles keep every device in the target (upstream default)
|
||||
# --keep-buildbot-flags keep ALL_KMODS / SDK / IB / ... as upstream ships
|
||||
# --save-buildinfo keep a copy in .generated/<id>_<ver>.buildinfo
|
||||
# --offline use a previously saved buildinfo, no network
|
||||
# --output <file> write here instead of ./.config (for testing)
|
||||
# --refresh ignore the cached JSON indexes
|
||||
# --skip-defconfig do not run make defconfig afterwards
|
||||
# -y, --yes do not ask for confirmation
|
||||
# -n, --dry-run show the plan, change nothing
|
||||
# -h, --help show this help
|
||||
#
|
||||
# Run it from the OpenWrt root: ./helper/download-config.sh
|
||||
#
|
||||
# Author: Zhe Yuan
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
umask 022
|
||||
|
||||
# --- Check if inside OpenWRT source directory ---
|
||||
if [ ! -f "feeds.conf.default" ] || [ ! -d "package" ]; then
|
||||
echo "[ERROR] Must run inside OpenWRT root directory."
|
||||
exit 1
|
||||
fi
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
LIB_SCRIPT_DIR="$SCRIPT_DIR"
|
||||
# shellcheck source=lib-openwrt-upstream.sh
|
||||
source "$SCRIPT_DIR/lib-openwrt-upstream.sh"
|
||||
|
||||
# --- Detect Git version ---
|
||||
VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||
VERSION="${VERSION#v}"
|
||||
IS_LATEST=false
|
||||
DEVICE_ID=""
|
||||
DEVICE_TARGET=""
|
||||
VERSION_OVERRIDE=""
|
||||
WITH_PACKAGES=false
|
||||
SINGLE_PROFILE=true
|
||||
STRIP_BUILDBOT=true
|
||||
SAVE_BUILDINFO=false
|
||||
OFFLINE=false
|
||||
OUTPUT=""
|
||||
RUN_DEFCONFIG=true
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
if [[ "$BRANCH" =~ openwrt-([0-9]+\.[0-9]+) ]]; then
|
||||
PARTIAL_VERSION="${BASH_REMATCH[1]}"
|
||||
# Find latest patch tag
|
||||
LATEST_TAG=$(git tag -l "v${PARTIAL_VERSION}.*" | sort -V | tail -n1)
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
echo "[ERROR] Cannot find latest tag for $PARTIAL_VERSION.*"
|
||||
usage() { sed -n '2,37p' "$0" | sed 's/^# \{0,1\}//;s/^#$//'; }
|
||||
|
||||
need_value() {
|
||||
if [ "$2" -lt 2 ] || [ -z "${3:-}" ]; then
|
||||
err "$1 requires a value."
|
||||
exit 1
|
||||
fi
|
||||
VERSION="${LATEST_TAG#v}"
|
||||
IS_LATEST=true
|
||||
elif [[ "$BRANCH" == *SNAPSHOT* ]]; then
|
||||
VERSION="SNAPSHOT"
|
||||
else
|
||||
read -rp "Cannot detect version, input manually (e.g., 24.10.4 or SNAPSHOT): " VERSION
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
echo "[INFO] OpenWRT version: $VERSION"
|
||||
|
||||
# --- Devices list ---
|
||||
# Format: Name Target FirmwareDevice
|
||||
DEVICES=(
|
||||
"Linksys_MX8500 qualcommax/ipq807x linksys_mx8500"
|
||||
"Cudy_TR3000_128MB mediatek/filogic cudy_tr3000-v1"
|
||||
"Cudy_TR3000_256MB mediatek/filogic cudy_tr3000-256mb-v1"
|
||||
)
|
||||
|
||||
echo "Available devices:"
|
||||
for i in "${!DEVICES[@]}"; do
|
||||
NAME=$(echo "${DEVICES[$i]}" | awk '{print $1}')
|
||||
echo " $i) $NAME"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--device) need_value "$1" $# "${2:-}"; DEVICE_ID="$2"; shift ;;
|
||||
--target) need_value "$1" $# "${2:-}"; DEVICE_TARGET="$2"; shift ;;
|
||||
--version) need_value "$1" $# "${2:-}"; VERSION_OVERRIDE="$2"; shift ;;
|
||||
--snapshot) VERSION_OVERRIDE="SNAPSHOT" ;;
|
||||
--with-packages) WITH_PACKAGES=true ;;
|
||||
--all-profiles) SINGLE_PROFILE=false ;;
|
||||
--keep-buildbot-flags) STRIP_BUILDBOT=false ;;
|
||||
--save-buildinfo) SAVE_BUILDINFO=true ;;
|
||||
--offline) OFFLINE=true ;;
|
||||
--output) need_value "$1" $# "${2:-}"; OUTPUT="$2"; shift ;;
|
||||
--refresh) REFRESH=true ;;
|
||||
--skip-defconfig) RUN_DEFCONFIG=false ;;
|
||||
-y|--yes) ASSUME_YES=true ;;
|
||||
-n|--dry-run) DRY_RUN=true ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) err "Unknown option: $1"; echo "Run '$0 --help' for usage." >&2; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
read -rp "Select device number: " DEV_INDEX
|
||||
if [[ -z "${DEVICES[$DEV_INDEX]}" ]]; then
|
||||
echo "[ERROR] Invalid selection."
|
||||
[ -t 0 ] || ASSUME_YES=true
|
||||
|
||||
require_python3
|
||||
|
||||
if [ -n "$OUTPUT" ]; then
|
||||
DEST="$OUTPUT"
|
||||
RUN_DEFCONFIG=false
|
||||
else
|
||||
require_openwrt_root
|
||||
DEST=".config"
|
||||
fi
|
||||
DEST_DIR="$(dirname -- "$DEST")"
|
||||
|
||||
VER="$(detect_version)"
|
||||
|
||||
if [ "$OFFLINE" != true ] && ! version_exists "$VER"; then
|
||||
err "Version '$VER' is not published at $BASE_URL."
|
||||
err " $(url_overview "$VER")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Extract device info ---
|
||||
read -r DEV_NAME DEV_TARGET DEV_FW <<<"${DEVICES[$DEV_INDEX]}"
|
||||
echo "[INFO] Selected device: $DEV_NAME"
|
||||
select_device "$VER"
|
||||
|
||||
# --- Construct URLs ---
|
||||
if [[ "$VERSION" == "SNAPSHOT" ]]; then
|
||||
CONFIG_URL="https://downloads.openwrt.org/snapshots/targets/$DEV_TARGET/config.buildinfo"
|
||||
FIRMWARE_URL="https://downloads.openwrt.org/snapshots/targets/$DEV_TARGET/openwrt-SNAPSHOT-$DEV_TARGET-$DEV_FW-squashfs-sysupgrade.bin"
|
||||
else
|
||||
CONFIG_URL="https://downloads.openwrt.org/releases/$VERSION/targets/$DEV_TARGET/config.buildinfo"
|
||||
FIRMWARE_URL="https://downloads.openwrt.org/releases/$VERSION/targets/$DEV_TARGET/openwrt-$VERSION-$DEV_TARGET-$DEV_FW-squashfs-sysupgrade.bin"
|
||||
SAVED_BUILDINFO="$SCRIPT_DIR/.generated/${DEV_ID}_${VER}.buildinfo"
|
||||
|
||||
SYSUPGRADE_URL=""
|
||||
FACTORY_URL=""
|
||||
PKG_COUNT="?"
|
||||
if [ "$OFFLINE" != true ]; then
|
||||
PROFILES_JSON="$(fetch_profiles "$VER" "$DEV_TARGET")"
|
||||
PKG_COUNT="$(profile_packages "$PROFILES_JSON" "$DEV_ID" | grep -c . || true)"
|
||||
while IFS= read -r img; do
|
||||
case "$img" in
|
||||
*squashfs-sysupgrade*) SYSUPGRADE_URL="$(url_image "$VER" "$DEV_TARGET" "$img")" ;;
|
||||
*squashfs-factory*) FACTORY_URL="$(url_image "$VER" "$DEV_TARGET" "$img")" ;;
|
||||
esac
|
||||
done < <(profile_images "$PROFILES_JSON" "$DEV_ID")
|
||||
fi
|
||||
|
||||
echo "[INFO] Config URL: $CONFIG_URL"
|
||||
echo "[INFO] Firmware URL: $FIRMWARE_URL"
|
||||
CONFIG_URL="$(url_buildinfo "$VER" "$DEV_TARGET")"
|
||||
|
||||
# --- Download config.buildinfo ---
|
||||
echo "[INFO] Downloading config.buildinfo..."
|
||||
wget -q --show-progress -O ".config" "$CONFIG_URL"
|
||||
echo "[INFO] Saved as .config"
|
||||
|
||||
# --- Run make defconfig ---
|
||||
echo "[INFO] Running make defconfig..."
|
||||
make defconfig >/dev/null
|
||||
echo "[INFO] defconfig done."
|
||||
|
||||
# --- Check firmware existence ---
|
||||
if curl --head --silent --fail "$FIRMWARE_URL" >/dev/null; then
|
||||
echo "[INFO] Firmware exists: $FIRMWARE_URL"
|
||||
echo
|
||||
echo "========================================"
|
||||
echo " Planned actions"
|
||||
echo "========================================"
|
||||
echo " Device : $DEV_ID ($DEV_LABEL)"
|
||||
echo " Target : $DEV_TARGET"
|
||||
echo " Version : $VER"
|
||||
if [ "$OFFLINE" = true ]; then
|
||||
echo " Source : $SAVED_BUILDINFO (offline)"
|
||||
else
|
||||
echo "[WARN] Firmware not found or URL invalid."
|
||||
echo " Source : $CONFIG_URL"
|
||||
fi
|
||||
echo " Destination : $DEST$([ -f "$DEST" ] && echo ' (backed up first)')"
|
||||
echo " Device scope : $([ "$SINGLE_PROFILE" = true ] && echo 'this device only' || echo 'all devices in target')"
|
||||
echo " Buildbot flags: $([ "$STRIP_BUILDBOT" = true ] && echo 'stripped' || echo 'kept')"
|
||||
echo " Built-in pkgs : $PKG_COUNT (per profiles.json)"
|
||||
[ -n "$SYSUPGRADE_URL" ] && echo " Sysupgrade : $SYSUPGRADE_URL"
|
||||
echo " Post : $([ "$RUN_DEFCONFIG" = true ] && echo 'make defconfig' || echo 'skipped')"
|
||||
echo "========================================"
|
||||
|
||||
if [ "$DRY_RUN" != true ]; then
|
||||
echo
|
||||
if ! confirm "Proceed?" yes; then
|
||||
info "Aborted by user."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Print Firmware Selector URL ---
|
||||
TARGET_ESC=$(echo $DEV_TARGET | sed 's|/|%2F|g')
|
||||
FS_URL="https://firmware-selector.openwrt.org/?version=$VERSION&target=$TARGET_ESC&id=$DEV_FW"
|
||||
echo "[INFO] Check Installed Packages at: $FS_URL"
|
||||
# --- Download into a temp file next to the destination -------------------
|
||||
# Same directory keeps the final mv atomic; `curl -f` plus this staging is
|
||||
# what stops a 404 from truncating an existing .config.
|
||||
TMP="$(mktemp "$DEST_DIR/.config.dl.XXXXXX")"
|
||||
trap 'rm -f "$TMP" "$TMP.pp"' EXIT
|
||||
|
||||
if [ "$OFFLINE" = true ]; then
|
||||
if [ ! -f "$SAVED_BUILDINFO" ]; then
|
||||
err "No saved buildinfo at $SAVED_BUILDINFO"
|
||||
err "Run once without --offline (add --save-buildinfo) to create it."
|
||||
exit 1
|
||||
fi
|
||||
info "Using the saved buildinfo (offline)."
|
||||
cp "$SAVED_BUILDINFO" "$TMP"
|
||||
else
|
||||
info "Downloading $CONFIG_URL"
|
||||
if ! fetch_url "$CONFIG_URL" "$TMP"; then
|
||||
err "Download failed; $DEST was not touched."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Validate before letting it near .config ------------------------------
|
||||
if [ ! -s "$TMP" ] || [ "$(wc -l < "$TMP")" -lt 5 ]; then
|
||||
err "Downloaded config looks empty or truncated; $DEST was not touched."
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -qx "CONFIG_TARGET_${DEV_TSYM}=y" "$TMP"; then
|
||||
err "Downloaded config is not for target $DEV_TARGET; $DEST was not touched."
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "_DEVICE_${DEV_ID}=y" "$TMP"; then
|
||||
err "Downloaded config does not contain device $DEV_ID; $DEST was not touched."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Post-process ---------------------------------------------------------
|
||||
# One awk pass: a chain of `sed -i` would rewrite the file repeatedly and the
|
||||
# symbol names contain '-' and '+', which are awkward in sed replacements.
|
||||
awk -v tsym="$DEV_TSYM" -v prof="$DEV_ID" \
|
||||
-v single="$SINGLE_PROFILE" -v strip="$STRIP_BUILDBOT" '
|
||||
# Matches both CONFIG_TARGET_DEVICE_<t>_DEVICE_<id>=y and
|
||||
# CONFIG_TARGET_DEVICE_PACKAGES_<t>_DEVICE_<id>="". It cannot match the
|
||||
# single-profile symbol appended below, which has no DEVICE_ prefix.
|
||||
single == "true" && /^CONFIG_TARGET_DEVICE_/ { next }
|
||||
single == "true" && /^CONFIG_TARGET_MULTI_PROFILE=y$/ { print "# CONFIG_TARGET_MULTI_PROFILE is not set"; next }
|
||||
single == "true" && /^CONFIG_TARGET_ALL_PROFILES=y$/ { print "# CONFIG_TARGET_ALL_PROFILES is not set"; next }
|
||||
single == "true" && /^CONFIG_TARGET_PER_DEVICE_ROOTFS=y$/ { print "# CONFIG_TARGET_PER_DEVICE_ROOTFS is not set"; next }
|
||||
strip == "true" && $0 ~ /^CONFIG_(ALL_KMODS|ALL_NONSHARED|BUILDBOT|SDK|SDK_LLVM_BPF|IB|MAKE_TOOLCHAIN|COLLECT_KERNEL_DEBUG|JSON_CYCLONEDX_SBOM|AUTOREMOVE)=y$/ {
|
||||
sym = $0; sub(/=y$/, "", sym); print "# " sym " is not set"; next
|
||||
}
|
||||
{ print }
|
||||
END {
|
||||
# CONFIG_TARGET_PROFILE is deliberately not written: Kconfig derives it.
|
||||
if (single == "true") print "CONFIG_TARGET_" tsym "_DEVICE_" prof "=y"
|
||||
}
|
||||
' "$TMP" > "$TMP.pp"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo
|
||||
info "Resulting config (first differences against the download):"
|
||||
diff -u "$TMP" "$TMP.pp" | head -40 || true
|
||||
echo
|
||||
info "Dry run complete; $DEST was not touched."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$SAVE_BUILDINFO" = true ]; then
|
||||
mkdir -p "$(dirname "$SAVED_BUILDINFO")"
|
||||
cp "$TMP" "$SAVED_BUILDINFO"
|
||||
info "Saved the raw buildinfo to $SAVED_BUILDINFO"
|
||||
fi
|
||||
|
||||
if [ -f "$DEST" ]; then
|
||||
cp -a "$DEST" "$DEST.download.bak"
|
||||
info "Previous config kept at $DEST.download.bak"
|
||||
fi
|
||||
mv -f "$TMP.pp" "$DEST"
|
||||
chmod 644 "$DEST"
|
||||
info "Wrote $DEST"
|
||||
|
||||
if [ "$RUN_DEFCONFIG" = true ]; then
|
||||
info "Running make defconfig..."
|
||||
make defconfig >/dev/null
|
||||
|
||||
# A config whose symbols do not exist in this tree is silently dropped by
|
||||
# Kconfig, leaving a config that looks fine and builds the wrong thing.
|
||||
if [ "$SINGLE_PROFILE" = true ]; then
|
||||
if ! grep -qx "CONFIG_TARGET_${DEV_TSYM}_DEVICE_${DEV_ID}=y" .config ||
|
||||
! grep -qx "CONFIG_TARGET_PROFILE=\"DEVICE_${DEV_ID}\"" .config; then
|
||||
err "The device symbol did not survive make defconfig."
|
||||
err "Your tree ($(git describe --tags 2>/dev/null || echo unknown)) probably"
|
||||
err "does not match release $VER. Restore with: cp $DEST.download.bak $DEST"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "========================================"
|
||||
echo "OK Configuration installed."
|
||||
echo " Device: $DEV_ID ($DEV_LABEL)"
|
||||
echo " Target: $DEV_TARGET"
|
||||
echo " Version: $VER"
|
||||
echo "========================================"
|
||||
[ -n "$SYSUPGRADE_URL" ] && echo " Official sysupgrade: $SYSUPGRADE_URL"
|
||||
[ -n "$FACTORY_URL" ] && echo " Official factory: $FACTORY_URL"
|
||||
echo " Firmware Selector: $(selector_url "$VER" "$DEV_TARGET" "$DEV_ID")"
|
||||
echo
|
||||
|
||||
if [ "$WITH_PACKAGES" = true ]; then
|
||||
GEN_ARGS=(--device "$DEV_ID" --target "$DEV_TARGET" --version "$VER")
|
||||
[ "$ASSUME_YES" = true ] && GEN_ARGS+=(-y)
|
||||
"$SCRIPT_DIR/gen-package-list.sh" "${GEN_ARGS[@]}"
|
||||
else
|
||||
echo "Next steps:"
|
||||
echo " ./helper/gen-package-list.sh --device $DEV_ID --apply"
|
||||
echo " (or --out/-n first if you want to review the list before applying)"
|
||||
echo " make menuconfig"
|
||||
echo " make -j\$(nproc) download world"
|
||||
echo
|
||||
fi
|
||||
|
||||
236
gen-package-list.sh
Executable file
236
gen-package-list.sh
Executable file
@@ -0,0 +1,236 @@
|
||||
#!/bin/bash
|
||||
# Build a device's complete package list.
|
||||
#
|
||||
# The built-in part comes from the target's profiles.json (default_packages +
|
||||
# device_packages, plus the packages the Firmware Selector adds on top), so it
|
||||
# never has to be copied by hand for a new release. Your own packages live in
|
||||
# packages/<device-id>.txt and are version independent.
|
||||
#
|
||||
# packages/<device-id>.txt your packages, kept in git, edited by hand
|
||||
# .generated/ the combined list, not kept in git
|
||||
#
|
||||
# Usage: ./gen-package-list.sh [options]
|
||||
#
|
||||
# --device <id> profile id, e.g. linksys_mx8500
|
||||
# --target <t> disambiguate an id present in several targets
|
||||
# --version <ver> override version detection (e.g. 25.12.5)
|
||||
# --snapshot shorthand for --version SNAPSHOT
|
||||
# --extras-file <f> use this instead of packages/<device-id>.txt
|
||||
# --no-extras-file built-in packages only
|
||||
# --init-extras create packages/<device-id>.txt if it is missing
|
||||
# --extras "<pkgs>" override the Firmware Selector additions
|
||||
# --no-extras omit the Firmware Selector additions
|
||||
# --wrap <cols> wrap the built-in list (default 0 = one line)
|
||||
# --out <path> write here instead of .generated/
|
||||
# --stdout print the built-in list only, one per line
|
||||
# --full-stdout print the complete list and exit
|
||||
# --apply apply the complete list to .config
|
||||
# --refresh ignore the cached JSON indexes
|
||||
# -y, --yes do not ask for confirmation
|
||||
# -n, --dry-run show what would happen, write nothing
|
||||
# -h, --help show this help
|
||||
#
|
||||
# Author: Zhe Yuan
|
||||
|
||||
set -euo pipefail
|
||||
umask 022
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
LIB_SCRIPT_DIR="$SCRIPT_DIR"
|
||||
# shellcheck source=lib-openwrt-upstream.sh
|
||||
source "$SCRIPT_DIR/lib-openwrt-upstream.sh"
|
||||
|
||||
DEVICE_ID=""
|
||||
DEVICE_TARGET=""
|
||||
VERSION_OVERRIDE=""
|
||||
EXTRAS="$SELECTOR_EXTRAS"
|
||||
EXTRAS_FILE=""
|
||||
USE_EXTRAS_FILE=true
|
||||
INIT_EXTRAS=false
|
||||
WRAP=0
|
||||
OUT_FILE=""
|
||||
MODE="file" # file | stdout | full-stdout | apply
|
||||
|
||||
usage() { sed -n '2,34p' "$0" | sed 's/^# \{0,1\}//;s/^#$//'; }
|
||||
|
||||
need_value() {
|
||||
if [ "$2" -lt 2 ] || [ -z "${3:-}" ]; then
|
||||
err "$1 requires a value."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--device) need_value "$1" $# "${2:-}"; DEVICE_ID="$2"; shift ;;
|
||||
--target) need_value "$1" $# "${2:-}"; DEVICE_TARGET="$2"; shift ;;
|
||||
--version) need_value "$1" $# "${2:-}"; VERSION_OVERRIDE="$2"; shift ;;
|
||||
--snapshot) VERSION_OVERRIDE="SNAPSHOT" ;;
|
||||
--extras-file) need_value "$1" $# "${2:-}"; EXTRAS_FILE="$2"; shift ;;
|
||||
--no-extras-file) USE_EXTRAS_FILE=false ;;
|
||||
--init-extras) INIT_EXTRAS=true ;;
|
||||
--extras) need_value "$1" $# "${2:-}"; EXTRAS="$2"; shift ;;
|
||||
--no-extras) EXTRAS="" ;;
|
||||
--wrap) need_value "$1" $# "${2:-}"; WRAP="$2"; shift ;;
|
||||
--out) need_value "$1" $# "${2:-}"; OUT_FILE="$2"; shift ;;
|
||||
--stdout) MODE="stdout" ;;
|
||||
--full-stdout) MODE="full-stdout" ;;
|
||||
--apply) MODE="apply" ;;
|
||||
--refresh) REFRESH=true ;;
|
||||
-y|--yes) ASSUME_YES=true ;;
|
||||
-n|--dry-run) DRY_RUN=true ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) err "Unknown option: $1"; echo "Run '$0 --help' for usage." >&2; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ -t 0 ] || ASSUME_YES=true
|
||||
|
||||
if ! [[ "$WRAP" =~ ^[0-9]+$ ]]; then
|
||||
err "--wrap needs a non-negative number."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
require_python3
|
||||
|
||||
VER="$(detect_version)"
|
||||
select_device "$VER"
|
||||
|
||||
PROFILES_JSON="$(fetch_profiles "$VER" "$DEV_TARGET")"
|
||||
PKGS="$(profile_packages "$PROFILES_JSON" "$DEV_ID")"
|
||||
|
||||
# Selector additions go last and never duplicate what the target provides.
|
||||
for extra in $EXTRAS; do
|
||||
if ! printf '%s\n' "$PKGS" | grep -qxF "$extra"; then
|
||||
PKGS="$PKGS"$'\n'"$extra"
|
||||
fi
|
||||
done
|
||||
|
||||
BUILTIN_COUNT="$(printf '%s\n' "$PKGS" | grep -c . || true)"
|
||||
|
||||
if [ "$MODE" = "stdout" ]; then
|
||||
printf '%s\n' "$PKGS"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Extras file ----------------------------------------------------------
|
||||
if [ "$USE_EXTRAS_FILE" = true ] && [ -z "$EXTRAS_FILE" ]; then
|
||||
EXTRAS_FILE="$SCRIPT_DIR/packages/$DEV_ID.txt"
|
||||
fi
|
||||
|
||||
init_extras_file() {
|
||||
mkdir -p "$(dirname "$EXTRAS_FILE")"
|
||||
cat > "$EXTRAS_FILE" <<EOF
|
||||
# Extra packages for $DEV_ID, on top of the built-in list that
|
||||
# gen-package-list.sh pulls from the official profiles.json.
|
||||
#
|
||||
# Whitespace separated, any number per line. '#' starts a comment.
|
||||
# '-pkg' removes a package; '##module' / '##remove' switch modes.
|
||||
|
||||
EOF
|
||||
info "Created $EXTRAS_FILE"
|
||||
}
|
||||
|
||||
EXTRAS_COUNT=0
|
||||
if [ "$USE_EXTRAS_FILE" = true ]; then
|
||||
if [ ! -f "$EXTRAS_FILE" ]; then
|
||||
if [ "$INIT_EXTRAS" = true ] && [ "$DRY_RUN" != true ]; then
|
||||
init_extras_file
|
||||
else
|
||||
warn "No extras file at $EXTRAS_FILE"
|
||||
warn "Only the built-in packages will be included (--init-extras creates one)."
|
||||
EXTRAS_FILE=""
|
||||
fi
|
||||
fi
|
||||
if [ -n "$EXTRAS_FILE" ] && [ -f "$EXTRAS_FILE" ]; then
|
||||
EXTRAS_COUNT="$(grep -v '^[[:space:]]*#' "$EXTRAS_FILE" | tr -s ' \t\n' '\n' |
|
||||
grep -c . || true)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Render the complete list ---------------------------------------------
|
||||
# The generated file is entirely machine-owned, so it is simply rewritten;
|
||||
# everything hand-written lives in the extras file instead.
|
||||
render_full() {
|
||||
echo "##built-in"
|
||||
echo "# Generated from profiles.json for $DEV_ID ($DEV_TARGET) at $VER."
|
||||
echo "# Do not edit: regenerate with gen-package-list.sh."
|
||||
if [ "$WRAP" -gt 0 ]; then
|
||||
printf '%s\n' "$PKGS" | tr '\n' ' ' | fold -s -w "$WRAP" | sed 's/[[:space:]]*$//'
|
||||
else
|
||||
printf '%s\n' "$PKGS" | tr '\n' ' ' | sed 's/[[:space:]]*$//'
|
||||
echo
|
||||
fi
|
||||
|
||||
if [ -n "$EXTRAS_FILE" ] && [ -f "$EXTRAS_FILE" ]; then
|
||||
echo
|
||||
echo "# --- extras from ${EXTRAS_FILE#"$SCRIPT_DIR"/} ---"
|
||||
# Appended verbatim: section headers are positional, so a ##module in
|
||||
# the extras file keeps working after the built-in block.
|
||||
cat "$EXTRAS_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$MODE" = "full-stdout" ]; then
|
||||
render_full
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$MODE" = "apply" ]; then
|
||||
info "Applying $BUILTIN_COUNT built-in + $EXTRAS_COUNT extra packages to .config"
|
||||
APPLY_ARGS=()
|
||||
[ "$ASSUME_YES" = true ] && APPLY_ARGS+=(-y)
|
||||
[ "$DRY_RUN" = true ] && APPLY_ARGS+=(-n)
|
||||
render_full | "$SCRIPT_DIR/add-openwrt-packages.sh" "${APPLY_ARGS[@]}" -
|
||||
exit $?
|
||||
fi
|
||||
|
||||
# --- File mode ------------------------------------------------------------
|
||||
if [ -z "$OUT_FILE" ]; then
|
||||
OUT_FILE="$SCRIPT_DIR/.generated/packages_${DEV_ID}_${VER}.txt"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "========================================"
|
||||
echo " Planned actions"
|
||||
echo "========================================"
|
||||
echo " Device : $DEV_ID ($DEV_LABEL)"
|
||||
echo " Target : $DEV_TARGET"
|
||||
echo " Version : $VER"
|
||||
echo " Built-in : $BUILTIN_COUNT packages (from profiles.json)"
|
||||
echo " Extras : $EXTRAS_COUNT packages${EXTRAS_FILE:+ from ${EXTRAS_FILE#"$SCRIPT_DIR"/}}"
|
||||
echo " Output : $OUT_FILE"
|
||||
echo "========================================"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo
|
||||
info "Complete list that would be written:"
|
||||
render_full | sed 's/^/ /'
|
||||
echo
|
||||
info "Dry run complete; nothing was written."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo
|
||||
if ! confirm "Write $OUT_FILE?" yes; then
|
||||
info "Aborted by user."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname -- "$OUT_FILE")"
|
||||
render_full > "$OUT_FILE"
|
||||
chmod 644 "$OUT_FILE"
|
||||
|
||||
echo
|
||||
echo "========================================"
|
||||
echo "OK Package list written."
|
||||
echo " File: $OUT_FILE"
|
||||
echo " Packages: $BUILTIN_COUNT built-in + $EXTRAS_COUNT extra"
|
||||
echo "========================================"
|
||||
echo
|
||||
echo "Next steps:"
|
||||
[ -n "$EXTRAS_FILE" ] && echo " Edit ${EXTRAS_FILE#"$SCRIPT_DIR"/} to change your own packages"
|
||||
echo " cd $(dirname "$SCRIPT_DIR")"
|
||||
echo " ./helper/add-openwrt-packages.sh helper/${OUT_FILE#"$SCRIPT_DIR"/}"
|
||||
echo
|
||||
451
lib-openwrt-upstream.sh
Normal file
451
lib-openwrt-upstream.sh
Normal file
@@ -0,0 +1,451 @@
|
||||
#!/bin/bash
|
||||
# Shared helpers for talking to downloads.openwrt.org.
|
||||
#
|
||||
# Sourced by download-config.sh and gen-package-list.sh. Not executable on its
|
||||
# own. Callers are expected to set `set -euo pipefail`; this file does not.
|
||||
#
|
||||
# Author: Zhe Yuan
|
||||
|
||||
[ -n "${_OWRT_LIB_LOADED:-}" ] && return 0
|
||||
_OWRT_LIB_LOADED=1
|
||||
|
||||
BASE_URL="${BASE_URL:-https://downloads.openwrt.org}"
|
||||
CACHE_DIR="${CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/openwrt-build-helper}"
|
||||
CACHE_TTL="${CACHE_TTL:-86400}"
|
||||
|
||||
# What the Firmware Selector adds on top of the target's own package set.
|
||||
# This is a UI convention, not an API, so keep it overridable.
|
||||
SELECTOR_EXTRAS="${SELECTOR_EXTRAS:-luci luci-app-attendedsysupgrade}"
|
||||
|
||||
# Devices offered when an interactive search is left empty. Only a convenience
|
||||
# shortcut -- the target still comes from .overview.json, never hardcoded.
|
||||
FAVOURITE_DEVICES="${FAVOURITE_DEVICES:-linksys_mx8500 cudy_tr3000-v1 cudy_tr3000-256mb-v1}"
|
||||
|
||||
REFRESH="${REFRESH:-false}"
|
||||
DRY_RUN="${DRY_RUN:-false}"
|
||||
ASSUME_YES="${ASSUME_YES:-false}"
|
||||
|
||||
# --- Output helpers -------------------------------------------------------
|
||||
# All logging goes to stderr so that functions returning a value on stdout
|
||||
# (detect_version, profile_packages, ...) can be captured safely.
|
||||
info() { echo "[INFO] $*" >&2; }
|
||||
warn() { echo "[WARN] $*" >&2; }
|
||||
err() { echo "[ERROR] $*" >&2; }
|
||||
|
||||
# Wrap every mutating command so --dry-run prints instead of running.
|
||||
run() {
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
printf '[DRY-RUN]'
|
||||
printf ' %q' "$@"
|
||||
echo
|
||||
return 0
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
confirm() {
|
||||
# confirm "<question>" <default: yes|no>
|
||||
local question="$1" default="$2" reply=""
|
||||
|
||||
[ "$ASSUME_YES" = true ] && return 0
|
||||
|
||||
if [ "$default" = "yes" ]; then
|
||||
read -r -p "$question [Y/n]: " reply || reply=""
|
||||
case "${reply:-}" in
|
||||
[nN] | [nN][oO]) return 1 ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
else
|
||||
read -r -p "$question [y/N]: " reply || reply=""
|
||||
case "${reply:-}" in
|
||||
[yY] | [yY][eE][sS]) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
require_python3() {
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
err "python3 is required to parse the upstream JSON indexes."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# The helper checkout is itself a git repository living inside the OpenWrt
|
||||
# worktree, so plain git commands run from here describe the HELPER repo.
|
||||
# Every version query must be anchored to the OpenWrt tree instead.
|
||||
find_openwrt_root() {
|
||||
local d
|
||||
for d in "$PWD" "$(dirname -- "${LIB_SCRIPT_DIR:-$PWD}")"; do
|
||||
[ -n "$d" ] || continue
|
||||
if [ -f "$d/feeds.conf.default" ] && [ -f "$d/Makefile" ] && [ -d "$d/scripts" ]; then
|
||||
( cd -- "$d" && pwd -P )
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
require_openwrt_root() {
|
||||
if [ ! -f feeds.conf.default ] || [ ! -f Makefile ] || [ ! -d scripts ]; then
|
||||
err "Must run inside the OpenWrt root directory."
|
||||
err "From the OpenWrt root, run it as: ./helper/$(basename "$0")"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# --- URL construction -----------------------------------------------------
|
||||
# Every upstream URL is built from this one function.
|
||||
release_path() {
|
||||
local ver="$1"
|
||||
if [ "$ver" = "SNAPSHOT" ]; then
|
||||
echo "snapshots"
|
||||
else
|
||||
echo "releases/$ver"
|
||||
fi
|
||||
}
|
||||
|
||||
url_overview() { echo "$BASE_URL/$(release_path "$1")/.overview.json"; }
|
||||
url_profiles() { echo "$BASE_URL/$(release_path "$1")/targets/$2/profiles.json"; }
|
||||
url_buildinfo() { echo "$BASE_URL/$(release_path "$1")/targets/$2/config.buildinfo"; }
|
||||
url_image() { echo "$BASE_URL/$(release_path "$1")/targets/$2/$3"; }
|
||||
|
||||
selector_url() {
|
||||
local ver="$1" target="$2" id="$3"
|
||||
echo "https://firmware-selector.openwrt.org/?version=$ver&target=${target//\//%2F}&id=$id"
|
||||
}
|
||||
|
||||
# --- Fetching -------------------------------------------------------------
|
||||
# `curl -f` is the key flag: a 404 exits non-zero and never writes a body, so
|
||||
# a failed download can never truncate the destination.
|
||||
fetch_url() {
|
||||
local url="$1" dest="$2"
|
||||
|
||||
if ! curl -fsSL --retry 3 --retry-delay 2 --max-time 60 -o "$dest.part" "$url"; then
|
||||
rm -f "$dest.part"
|
||||
return 1
|
||||
fi
|
||||
mv -f "$dest.part" "$dest"
|
||||
}
|
||||
|
||||
fetch_cached() {
|
||||
local url="$1" cache="$2" age=0
|
||||
|
||||
mkdir -p "$(dirname "$cache")"
|
||||
|
||||
if [ "$REFRESH" != true ] && [ -s "$cache" ]; then
|
||||
age=$(( $(date +%s) - $(stat -c %Y "$cache" 2>/dev/null || echo 0) ))
|
||||
if [ "$age" -lt "$CACHE_TTL" ]; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if fetch_url "$url" "$cache"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# A stale cache beats no data when the network is down.
|
||||
if [ -s "$cache" ]; then
|
||||
warn "Could not refresh $url; using the cached copy."
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
version_exists() {
|
||||
curl -fsI --max-time 30 "$(url_overview "$1")" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# --- Version detection ----------------------------------------------------
|
||||
# Replaces `git describe --tags --abbrev=0`, which returns a tag whenever ANY
|
||||
# tag is reachable -- so the old script's whole fallback chain was dead code
|
||||
# and a branch tip past a tag silently produced the wrong release URL.
|
||||
detect_version() {
|
||||
local ver="" branch="" tag="" drift=0
|
||||
|
||||
if [ -n "${VERSION_OVERRIDE:-}" ]; then
|
||||
ver="${VERSION_OVERRIDE#v}"
|
||||
info "Version from the command line: $ver"
|
||||
echo "$ver"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local root=""
|
||||
if ! root="$(find_openwrt_root)"; then
|
||||
err "Cannot locate the OpenWrt source tree; pass --version <ver> explicitly."
|
||||
exit 1
|
||||
fi
|
||||
if ! git -C "$root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
err "'$root' is not a Git repository and no --version was given."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if tag="$(git -C "$root" describe --tags --exact-match HEAD 2>/dev/null)"; then
|
||||
ver="${tag#v}"
|
||||
info "HEAD is exactly at tag $tag; using version $ver."
|
||||
echo "$ver"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if branch="$(git -C "$root" symbolic-ref --quiet --short HEAD 2>/dev/null)"; then
|
||||
case "$branch" in
|
||||
main | master)
|
||||
info "On branch '$branch'; using SNAPSHOT."
|
||||
echo "SNAPSHOT"
|
||||
return 0
|
||||
;;
|
||||
openwrt-* | [0-9]*)
|
||||
local series="${branch#openwrt-}"
|
||||
# --merged HEAD keeps tags from unrelated branches out of the picture.
|
||||
tag="$(git -C "$root" tag --merged HEAD -l "v${series}.*" 2>/dev/null |
|
||||
grep -v -- '-rc' | sort -V | tail -n 1)"
|
||||
if [ -n "$tag" ]; then
|
||||
drift="$(git -C "$root" rev-list --count "$tag..HEAD" 2>/dev/null || echo 0)"
|
||||
if [ "$drift" -gt 0 ]; then
|
||||
warn "HEAD is $drift commit(s) past $tag."
|
||||
warn "The downloaded config describes the release, not your tree."
|
||||
fi
|
||||
ver="${tag#v}"
|
||||
info "Branch '$branch' -> newest merged tag $tag; using version $ver."
|
||||
echo "$ver"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if tag="$(git -C "$root" describe --tags --long 2>/dev/null)"; then
|
||||
tag="${tag%-*-g*}"
|
||||
drift="$(git -C "$root" rev-list --count "$tag..HEAD" 2>/dev/null || echo 0)"
|
||||
warn "HEAD is not at a tag; nearest is $tag ($drift commit(s) behind HEAD)."
|
||||
if [ "$ASSUME_YES" != true ] &&
|
||||
! confirm "Use version ${tag#v}?" no; then
|
||||
err "Aborted; pass --version explicitly."
|
||||
exit 1
|
||||
fi
|
||||
ver="${tag#v}"
|
||||
info "Using version $ver."
|
||||
echo "$ver"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$ASSUME_YES" = true ] || [ ! -t 0 ]; then
|
||||
err "Cannot determine the OpenWrt version; pass --version <ver> or --snapshot."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -r -p "Enter version (e.g. 25.12.5 or SNAPSHOT): " ver || ver=""
|
||||
if [ -z "$ver" ]; then
|
||||
err "No version given."
|
||||
exit 1
|
||||
fi
|
||||
echo "${ver#v}"
|
||||
}
|
||||
|
||||
# --- Device index ---------------------------------------------------------
|
||||
fetch_overview() {
|
||||
local ver="$1"
|
||||
# Separate statement on purpose: `local a=$1 b=$a` expands every word before
|
||||
# assigning, so $a would be unbound under `set -u`.
|
||||
local cache="$CACHE_DIR/overview-$ver.json"
|
||||
|
||||
# Snapshots move daily, so never serve them from a day-old cache.
|
||||
[ "$ver" = "SNAPSHOT" ] && REFRESH=true
|
||||
|
||||
if ! fetch_cached "$(url_overview "$ver")" "$cache"; then
|
||||
err "Cannot fetch the device index for version '$ver'."
|
||||
err " $(url_overview "$ver")"
|
||||
exit 1
|
||||
fi
|
||||
echo "$cache"
|
||||
}
|
||||
|
||||
# Emits TSV: id <tab> target <tab> label
|
||||
_overview_py() {
|
||||
python3 - "$@" <<'PY'
|
||||
import json, sys, difflib
|
||||
|
||||
path, mode, arg = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
data = json.load(open(path, encoding="utf-8"))
|
||||
profiles = data.get("profiles") or []
|
||||
|
||||
|
||||
def label(p):
|
||||
parts = []
|
||||
for t in p.get("titles") or []:
|
||||
if not isinstance(t, dict):
|
||||
continue
|
||||
# Upstream entries come in three shapes: {title}, {vendor,model},
|
||||
# and {vendor,model,variant}.
|
||||
if t.get("title"):
|
||||
parts.append(t["title"])
|
||||
else:
|
||||
parts.append(" ".join(
|
||||
str(x) for x in (t.get("vendor"), t.get("model"), t.get("variant")) if x
|
||||
))
|
||||
parts = [p for p in parts if p]
|
||||
return " / ".join(parts) or p.get("id", "")
|
||||
|
||||
|
||||
def emit(rows):
|
||||
for p in rows:
|
||||
print("%s\t%s\t%s" % (p.get("id", ""), p.get("target", ""), label(p)))
|
||||
|
||||
|
||||
if mode == "id":
|
||||
rows = [p for p in profiles if p.get("id") == arg]
|
||||
if not rows:
|
||||
names = [p.get("id", "") for p in profiles]
|
||||
close = difflib.get_close_matches(arg, names, n=5, cutoff=0.5)
|
||||
sys.stderr.write("no device with id '%s'\n" % arg)
|
||||
if close:
|
||||
sys.stderr.write("closest matches: %s\n" % ", ".join(close))
|
||||
sys.exit(2)
|
||||
emit(rows)
|
||||
elif mode == "search":
|
||||
terms = [t.lower() for t in arg.split() if t]
|
||||
rows = []
|
||||
for p in profiles:
|
||||
hay = (p.get("id", "") + " " + label(p)).lower()
|
||||
if all(t in hay for t in terms):
|
||||
rows.append(p)
|
||||
rows.sort(key=lambda p: p.get("id", ""))
|
||||
emit(rows)
|
||||
elif mode == "list":
|
||||
wanted = arg.split()
|
||||
by_id = {p.get("id"): p for p in profiles}
|
||||
emit([by_id[i] for i in wanted if i in by_id])
|
||||
PY
|
||||
}
|
||||
|
||||
# Sets DEV_ID, DEV_TARGET, DEV_TSYM, DEV_LABEL
|
||||
select_device() {
|
||||
local ver="$1" json="" rows="" n=0 choice="" tries=0 query=""
|
||||
|
||||
json="$(fetch_overview "$ver")"
|
||||
|
||||
if [ -n "${DEVICE_ID:-}" ]; then
|
||||
if ! rows="$(_overview_py "$json" id "$DEVICE_ID" 2>&1)"; then
|
||||
err "$rows"
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "${DEVICE_TARGET:-}" ]; then
|
||||
rows="$(printf '%s\n' "$rows" | awk -F'\t' -v t="$DEVICE_TARGET" '$2 == t')"
|
||||
fi
|
||||
n="$(printf '%s\n' "$rows" | grep -c . || true)"
|
||||
if [ "$n" -eq 0 ]; then
|
||||
err "Device '$DEVICE_ID' exists but not for target '${DEVICE_TARGET:-}'."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$n" -gt 1 ]; then
|
||||
err "Device id '$DEVICE_ID' is present in more than one target:"
|
||||
printf '%s\n' "$rows" | awk -F'\t' '{print " " $2 " (" $3 ")"}' >&2
|
||||
err "Disambiguate with --target <target>."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [ "$ASSUME_YES" = true ] || [ ! -t 0 ]; then
|
||||
err "No device selected. Pass --device <id> for non-interactive use."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while [ "$tries" -lt 3 ]; do
|
||||
tries=$((tries + 1))
|
||||
read -r -p "Search device (vendor, model or id; empty for favourites): " query || query=""
|
||||
|
||||
if [ -z "${query:-}" ]; then
|
||||
rows="$(_overview_py "$json" list "$FAVOURITE_DEVICES")"
|
||||
else
|
||||
rows="$(_overview_py "$json" search "$query")"
|
||||
fi
|
||||
|
||||
n="$(printf '%s\n' "$rows" | grep -c . || true)"
|
||||
if [ "$n" -eq 0 ]; then
|
||||
warn "No device matched '$query'."
|
||||
continue
|
||||
fi
|
||||
if [ "$n" -gt 25 ]; then
|
||||
warn "$n devices matched; please narrow the search."
|
||||
continue
|
||||
fi
|
||||
|
||||
echo
|
||||
printf '%s\n' "$rows" | awk -F'\t' '{printf " %2d) %-32s %-22s %s\n", NR, $1, $2, $3}'
|
||||
echo
|
||||
read -r -p "Select device number [1-$n]: " choice || choice=""
|
||||
|
||||
# 1-based plus an explicit numeric test: an unset/non-numeric index in
|
||||
# an array subscript is an arithmetic context that silently yields 0.
|
||||
if [[ "${choice:-}" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$n" ]; then
|
||||
rows="$(printf '%s\n' "$rows" | sed -n "${choice}p")"
|
||||
break
|
||||
fi
|
||||
warn "Invalid selection."
|
||||
rows=""
|
||||
done
|
||||
|
||||
if [ -z "$rows" ]; then
|
||||
err "No device selected."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
IFS=$'\t' read -r DEV_ID DEV_TARGET DEV_LABEL <<<"$rows"
|
||||
DEV_TSYM="${DEV_TARGET//\//_}"
|
||||
|
||||
if [ -z "$DEV_ID" ] || [ -z "$DEV_TARGET" ]; then
|
||||
err "Could not resolve the device selection."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# --- profiles.json --------------------------------------------------------
|
||||
fetch_profiles() {
|
||||
local ver="$1" target="$2"
|
||||
local cache="$CACHE_DIR/profiles-$ver-${target//\//_}.json"
|
||||
|
||||
[ "$ver" = "SNAPSHOT" ] && REFRESH=true
|
||||
|
||||
if ! fetch_cached "$(url_profiles "$ver" "$target")" "$cache"; then
|
||||
err "Cannot fetch profiles.json for $target at version $ver."
|
||||
err " $(url_profiles "$ver" "$target")"
|
||||
exit 1
|
||||
fi
|
||||
echo "$cache"
|
||||
}
|
||||
|
||||
# default_packages + device_packages, order preserved, deduplicated.
|
||||
profile_packages() {
|
||||
python3 - "$1" "$2" <<'PY'
|
||||
import json, sys
|
||||
|
||||
data = json.load(open(sys.argv[1], encoding="utf-8"))
|
||||
pid = sys.argv[2]
|
||||
prof = (data.get("profiles") or {}).get(pid)
|
||||
if prof is None:
|
||||
sys.stderr.write("profile '%s' is not in this target's profiles.json\n" % pid)
|
||||
sys.exit(2)
|
||||
|
||||
seen, out = set(), []
|
||||
for p in list(data.get("default_packages") or []) + list(prof.get("device_packages") or []):
|
||||
if p not in seen:
|
||||
seen.add(p)
|
||||
out.append(p)
|
||||
print("\n".join(out))
|
||||
PY
|
||||
}
|
||||
|
||||
# Image filenames straight from profiles.json -- no string building, which is
|
||||
# what made the old firmware URL wrong (the target still contained a '/').
|
||||
profile_images() {
|
||||
python3 - "$1" "$2" <<'PY'
|
||||
import json, sys
|
||||
|
||||
data = json.load(open(sys.argv[1], encoding="utf-8"))
|
||||
prof = (data.get("profiles") or {}).get(sys.argv[2]) or {}
|
||||
for img in prof.get("images") or []:
|
||||
name = img.get("name")
|
||||
if name:
|
||||
print(name)
|
||||
PY
|
||||
}
|
||||
9
packages/cudy_tr3000-256mb-v1.txt
Normal file
9
packages/cudy_tr3000-256mb-v1.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# Extra packages for cudy_tr3000-256mb-v1, on top of the built-in list that
|
||||
# gen-package-list.sh pulls from the official profiles.json.
|
||||
#
|
||||
# Whitespace separated, any number per line. '#' starts a comment.
|
||||
# '-pkg' removes a package; '##module' / '##remove' switch modes.
|
||||
|
||||
curl yq unzip ip-full kmod-mtd-rw kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft
|
||||
luci-app-openvpn luci-proto-wireguard luci-app-cloudflared qrencode tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
9
packages/cudy_tr3000-v1.txt
Normal file
9
packages/cudy_tr3000-v1.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# Extra packages for cudy_tr3000-v1, on top of the built-in list that
|
||||
# gen-package-list.sh pulls from the official profiles.json.
|
||||
#
|
||||
# Whitespace separated, any number per line. '#' starts a comment.
|
||||
# '-pkg' removes a package; '##module' / '##remove' switch modes.
|
||||
|
||||
iptables-nft ip6tables-nft curl yq unzip ip-full kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun
|
||||
luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
9
packages/linksys_mx8500.txt
Normal file
9
packages/linksys_mx8500.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# Extra packages for linksys_mx8500, on top of the built-in list that
|
||||
# gen-package-list.sh pulls from the official profiles.json.
|
||||
#
|
||||
# Whitespace separated, any number per line. '#' starts a comment.
|
||||
# '-pkg' removes a package; '##module' / '##remove' switch modes.
|
||||
|
||||
curl yq unzip ip-full kmod-mtd-rw kmod-inet-diag kmod-nft-socket kmod-nft-tproxy kmod-tun luci-app-upnp luci-app-ttyd luci-app-filemanager luci-app-wol luci-app-mwan3 iptables-nft ip6tables-nft
|
||||
luci-app-openvpn luci-proto-wireguard luci-app-cloudflared qrencode tailscale zerotier adguardhome
|
||||
easytier luci-app-netspeedtest luci-app-easytier
|
||||
@@ -1,154 +1,370 @@
|
||||
#!/bin/bash
|
||||
# Automatically prepare OpenWRT build dependencies for Debian/Ubuntu
|
||||
# Supports both x86_64 and arm64
|
||||
# Detects Ubuntu 22.04, 24.04, 26.04, Debian 12/13 and newer
|
||||
# Prepare the OpenWrt build dependencies.
|
||||
#
|
||||
# Supported distributions:
|
||||
# - Debian / Ubuntu / Linux Mint / Raspberry Pi OS (apt)
|
||||
# - Fedora / RHEL / CentOS / Rocky / AlmaLinux (dnf, yum)
|
||||
# - Arch / Manjaro / EndeavourOS (pacman)
|
||||
# - openSUSE / SLES (zypper)
|
||||
# - Alpine (apk)
|
||||
#
|
||||
# Package lists follow https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem
|
||||
# Packages that do not exist on the running release are skipped instead of
|
||||
# aborting the whole installation.
|
||||
#
|
||||
# Usage: ./prepare-openwrt-env.sh [-y] [-n] [--with-go]
|
||||
# -y, --yes do not ask for confirmation
|
||||
# -n, --dry-run only print what would be installed
|
||||
# --with-go also install the Go toolchain
|
||||
#
|
||||
# Author: Zhe Yuan
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
umask 022
|
||||
|
||||
ASSUME_YES=false
|
||||
DRY_RUN=false
|
||||
WITH_GO=false
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-y|--yes) ASSUME_YES=true ;;
|
||||
-n|--dry-run) DRY_RUN=true ;;
|
||||
--with-go) WITH_GO=true ;;
|
||||
-h|--help)
|
||||
sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "[ERROR] Unknown option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
echo "========================================"
|
||||
echo " OpenWRT Build Environment Preparation"
|
||||
echo " OpenWrt Build Environment Preparation"
|
||||
echo "========================================"
|
||||
|
||||
# --- Detect architecture ---
|
||||
ARCH=$(uname -m)
|
||||
echo "[INFO] Detected architecture: $ARCH"
|
||||
ARCH="$(uname -m)"
|
||||
echo "[INFO] Architecture: $ARCH"
|
||||
|
||||
# --- Detect OS version ---
|
||||
if [ -f /etc/os-release ]; then
|
||||
# --- Detect distribution ---
|
||||
DISTRO="unknown"
|
||||
DISTRO_LIKE=""
|
||||
VERSION_ID="unknown"
|
||||
PRETTY_NAME="unknown"
|
||||
|
||||
if [ -r /etc/os-release ]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/os-release
|
||||
DISTRO="$ID"
|
||||
DISTRO="${ID:-unknown}"
|
||||
DISTRO_LIKE="${ID_LIKE:-}"
|
||||
VERSION_ID="${VERSION_ID:-unknown}"
|
||||
PRETTY_NAME="${PRETTY_NAME:-$DISTRO $VERSION_ID}"
|
||||
else
|
||||
echo "[ERROR] Cannot detect system version (no /etc/os-release)"
|
||||
echo "[ERROR] Cannot detect the distribution (no /etc/os-release)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[INFO] Detected distribution: $DISTRO $VERSION_ID"
|
||||
echo "[INFO] Distribution: $PRETTY_NAME (id=$DISTRO)"
|
||||
|
||||
# --- Version helpers ---
|
||||
version_ge() {
|
||||
local current="${1:-unknown}"
|
||||
local minimum="$2"
|
||||
|
||||
if [[ "$current" == "unknown" ]]; then
|
||||
return 1
|
||||
# --- Detect package manager ---
|
||||
PKG_MGR=""
|
||||
for candidate in apt-get dnf yum pacman zypper apk; do
|
||||
if command -v "$candidate" >/dev/null 2>&1; then
|
||||
PKG_MGR="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if command -v dpkg >/dev/null 2>&1; then
|
||||
dpkg --compare-versions "$current" ge "$minimum"
|
||||
if [ -z "$PKG_MGR" ]; then
|
||||
echo "[ERROR] No supported package manager found." >&2
|
||||
echo "[ERROR] Supported: apt-get, dnf, yum, pacman, zypper, apk." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[INFO] Package manager: $PKG_MGR"
|
||||
|
||||
# --- Privilege escalation ---
|
||||
SUDO=""
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
SUDO="sudo"
|
||||
else
|
||||
[[ "$(printf '%s\n%s\n' "$minimum" "$current" | sort -V | head -n1)" == "$minimum" ]]
|
||||
echo "[ERROR] Not running as root and 'sudo' is not available." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
is_x86_64() {
|
||||
[ "$ARCH" = "x86_64" ] || [ "$ARCH" = "amd64" ]
|
||||
}
|
||||
|
||||
IS_UBUNTU_24_OR_NEWER=false
|
||||
IS_UBUNTU_26_OR_NEWER=false
|
||||
IS_DEBIAN_13_OR_NEWER=false
|
||||
is_arm64() {
|
||||
[ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]
|
||||
}
|
||||
|
||||
if [[ "$DISTRO" == "ubuntu" ]] && version_ge "$VERSION_ID" "24.04"; then
|
||||
IS_UBUNTU_24_OR_NEWER=true
|
||||
fi
|
||||
# ---------------------------------------------------------------------------
|
||||
# Package lists
|
||||
#
|
||||
# REQUIRED_PACKAGES installation fails if one of them cannot be installed
|
||||
# OPTIONAL_PACKAGES nice to have; silently skipped when unavailable
|
||||
# ---------------------------------------------------------------------------
|
||||
REQUIRED_PACKAGES=""
|
||||
OPTIONAL_PACKAGES=""
|
||||
REFRESH_CMD=""
|
||||
INSTALL_CMD=""
|
||||
|
||||
if [[ "$DISTRO" == "ubuntu" ]] && version_ge "$VERSION_ID" "26.04"; then
|
||||
IS_UBUNTU_26_OR_NEWER=true
|
||||
fi
|
||||
case "$PKG_MGR" in
|
||||
apt-get)
|
||||
REFRESH_CMD="$SUDO apt-get update"
|
||||
INSTALL_CMD="$SUDO apt-get install -y --no-install-recommends"
|
||||
|
||||
if [[ "$DISTRO" == "debian" ]] && version_ge "$VERSION_ID" "13"; then
|
||||
IS_DEBIAN_13_OR_NEWER=true
|
||||
fi
|
||||
REQUIRED_PACKAGES="build-essential clang flex bison g++ gawk gcc gettext git \
|
||||
file wget unzip rsync swig time python3 python3-dev python3-setuptools \
|
||||
libncurses-dev libssl-dev zlib1g-dev libelf-dev make patch diffutils"
|
||||
|
||||
# --- Base packages (common to all) ---
|
||||
COMMON_PACKAGES="build-essential clang flex bison g++ gawk \
|
||||
gettext git libssl-dev rsync swig unzip zlib1g-dev file wget bzip2"
|
||||
# Extras from the OpenWrt 25.xx / Debian Trixie list. Names come and go
|
||||
# between releases, so everything here is filtered before installing.
|
||||
OPTIONAL_PACKAGES="bc binutils-gold ccache ecj fastjar help2man libbsd-dev \
|
||||
liblzma-dev meson mold mtd-utils ninja-build pbzip2 pigz pkg-config \
|
||||
subversion texinfo u-boot-tools xsltproc xxd zstd bzip2 quilt asciidoc \
|
||||
device-tree-compiler java-propose-classpath python3-distutils \
|
||||
python3-distutils-extra libncurses5-dev"
|
||||
|
||||
# --- Determine specific package set ---
|
||||
if [[ "$ARCH" == "x86_64" || "$ARCH" == "amd64" ]]; then
|
||||
echo "[INFO] Preparing environment for x86_64."
|
||||
|
||||
if [[ "$IS_UBUNTU_26_OR_NEWER" == "true" ]]; then
|
||||
echo "[INFO] Detected Ubuntu 26.04+ (no python3-distutils; OpenWrt docs use libncurses-dev)."
|
||||
EXTRA_PACKAGES="gcc-multilib g++-multilib glibc-source libncurses-dev python3-setuptools"
|
||||
elif [[ "$IS_UBUNTU_24_OR_NEWER" == "true" ]]; then
|
||||
echo "[INFO] Detected Ubuntu 24.04+ (Python 3.12+, no python3-distutils)."
|
||||
EXTRA_PACKAGES="gcc-multilib g++-multilib libncurses5-dev python3-setuptools"
|
||||
elif [[ "$IS_DEBIAN_13_OR_NEWER" == "true" ]]; then
|
||||
echo "[INFO] Detected Debian 13+ (no python3-distutils)."
|
||||
EXTRA_PACKAGES="gcc-multilib g++-multilib libncurses-dev python3-setuptools"
|
||||
else
|
||||
echo "[INFO] Detected older Ubuntu/Debian (with python3-distutils)."
|
||||
EXTRA_PACKAGES="gcc-multilib g++-multilib libncurses-dev python3-distutils python3-setuptools"
|
||||
if is_x86_64; then
|
||||
OPTIONAL_PACKAGES="$OPTIONAL_PACKAGES gcc-multilib g++-multilib glibc-source"
|
||||
elif is_arm64; then
|
||||
# arm64 has no native multilib; the cross variants are optional and the
|
||||
# available set differs a lot between releases (e.g. the s390x variants
|
||||
# were dropped in Ubuntu 26.04).
|
||||
OPTIONAL_PACKAGES="$OPTIONAL_PACKAGES \
|
||||
gcc-multilib-i686-linux-gnu g++-multilib-i686-linux-gnu \
|
||||
gcc-multilib-x86-64-linux-gnu g++-multilib-x86-64-linux-gnu \
|
||||
gcc-multilib-x86-64-linux-gnux32 g++-multilib-x86-64-linux-gnux32 \
|
||||
gcc-multilib-s390x-linux-gnu g++-multilib-s390x-linux-gnu \
|
||||
libc6-dev-i386-cross libc6-dev-i386-amd64-cross libc6-dev-i386-x32-cross \
|
||||
glibc-source"
|
||||
fi
|
||||
|
||||
INSTALL_PACKAGES="$COMMON_PACKAGES $EXTRA_PACKAGES"
|
||||
|
||||
elif [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then
|
||||
echo "[INFO] Preparing environment for ARM64."
|
||||
|
||||
# Note: arm64 has no native multilib; install cross multilib packages
|
||||
MULTILIB_PACKAGES="
|
||||
gcc-multilib-i686-linux-gnu gcc-multilib-s390x-linux-gnu \
|
||||
gcc-multilib-x86-64-linux-gnu gcc-multilib-x86-64-linux-gnux32 \
|
||||
g++-multilib-i686-linux-gnu g++-multilib-s390x-linux-gnu \
|
||||
g++-multilib-x86-64-linux-gnu g++-multilib-x86-64-linux-gnux32 \
|
||||
libc6-dev-i386-amd64-cross libc6-dev-i386-cross libc6-dev-i386-x32-cross"
|
||||
|
||||
if [[ "$IS_UBUNTU_26_OR_NEWER" == "true" ]]; then
|
||||
EXTRA_PACKAGES="glibc-source libncurses-dev python3-setuptools"
|
||||
elif [[ "$IS_UBUNTU_24_OR_NEWER" == "true" ]]; then
|
||||
EXTRA_PACKAGES="libncurses5-dev python3-setuptools"
|
||||
elif [[ "$IS_DEBIAN_13_OR_NEWER" == "true" ]]; then
|
||||
EXTRA_PACKAGES="libncurses-dev python3-setuptools"
|
||||
else
|
||||
EXTRA_PACKAGES="libncurses-dev python3-distutils python3-setuptools"
|
||||
if [ "$WITH_GO" = true ]; then
|
||||
OPTIONAL_PACKAGES="$OPTIONAL_PACKAGES golang golang-go"
|
||||
fi
|
||||
;;
|
||||
|
||||
INSTALL_PACKAGES="$COMMON_PACKAGES $EXTRA_PACKAGES $MULTILIB_PACKAGES golang"
|
||||
dnf|yum)
|
||||
REFRESH_CMD="$SUDO $PKG_MGR makecache"
|
||||
INSTALL_CMD="$SUDO $PKG_MGR install -y --setopt=install_weak_deps=False"
|
||||
|
||||
REQUIRED_PACKAGES="gcc gcc-c++ make git-core patch diffutils file wget \
|
||||
unzip rsync tar which bzip2 ncurses-devel openssl-devel zlib-devel \
|
||||
python3 python3-devel python3-setuptools"
|
||||
|
||||
OPTIONAL_PACKAGES="bash-completion elfutils-libelf-devel zlib-static \
|
||||
glibc-static swig quilt ccache perl-base perl-Data-Dumper \
|
||||
perl-File-Compare perl-File-Copy perl-FindBin perl-IPC-Cmd perl-JSON-PP \
|
||||
perl-lib perl-Thread-Queue perl-Time-Piece libxslt bc texinfo \
|
||||
dtc zstd pkgconf-pkg-config"
|
||||
|
||||
if [ "$WITH_GO" = true ]; then
|
||||
OPTIONAL_PACKAGES="$OPTIONAL_PACKAGES golang"
|
||||
fi
|
||||
;;
|
||||
|
||||
pacman)
|
||||
REFRESH_CMD="$SUDO pacman -Sy"
|
||||
INSTALL_CMD="$SUDO pacman -S --needed --noconfirm"
|
||||
|
||||
REQUIRED_PACKAGES="base-devel bash bzip2 git libelf libxslt ncurses openssl \
|
||||
rsync swig time unzip util-linux wget which zlib python python-setuptools"
|
||||
|
||||
OPTIONAL_PACKAGES="ccache quilt pkgconf diffutils patch tar make gawk \
|
||||
subversion asciidoc dtc zstd"
|
||||
|
||||
if [ "$WITH_GO" = true ]; then
|
||||
OPTIONAL_PACKAGES="$OPTIONAL_PACKAGES go"
|
||||
fi
|
||||
;;
|
||||
|
||||
zypper)
|
||||
REFRESH_CMD="$SUDO zypper --non-interactive refresh"
|
||||
INSTALL_CMD="$SUDO zypper --non-interactive install --no-recommends"
|
||||
|
||||
REQUIRED_PACKAGES="bash bc binutils bzip2 flex gawk gcc gcc-c++ \
|
||||
gettext-tools git-core make ncurses-devel patch rsync unzip util-linux \
|
||||
wget zlib-devel libopenssl-devel python3-devel python3-setuptools"
|
||||
|
||||
OPTIONAL_PACKAGES="asciidoc fastjar intltool libxslt-tools mercurial \
|
||||
perl-ExtUtils-MakeMaker sdcc quilt ccache libelf-devel swig zstd dtc"
|
||||
|
||||
if [ "$WITH_GO" = true ]; then
|
||||
OPTIONAL_PACKAGES="$OPTIONAL_PACKAGES go"
|
||||
fi
|
||||
;;
|
||||
|
||||
apk)
|
||||
REFRESH_CMD="$SUDO apk update"
|
||||
INSTALL_CMD="$SUDO apk add"
|
||||
|
||||
REQUIRED_PACKAGES="argp-standalone bash bc binutils bzip2 coreutils \
|
||||
diffutils elfutils-dev findutils flex g++ gawk gcc gettext git grep gzip \
|
||||
linux-headers make ncurses-dev openssl-dev patch perl python3-dev rsync \
|
||||
tar unzip util-linux wget zlib-dev"
|
||||
|
||||
OPTIONAL_PACKAGES="asciidoc cdrkit intltool libxslt musl-fts-dev \
|
||||
musl-libintl musl-obstack-dev swig quilt ccache zstd"
|
||||
|
||||
if [ "$WITH_GO" = true ]; then
|
||||
OPTIONAL_PACKAGES="$OPTIONAL_PACKAGES go"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Availability filtering
|
||||
# ---------------------------------------------------------------------------
|
||||
pkg_available() {
|
||||
local pkg="$1"
|
||||
|
||||
case "$PKG_MGR" in
|
||||
apt-get)
|
||||
# apt-cache policy prints no "Candidate:" line for purely virtual names
|
||||
# and "(none)" when no installable version exists in the repositories.
|
||||
local policy=""
|
||||
policy="$(apt-cache policy "$pkg" 2>/dev/null || true)"
|
||||
case "$policy" in
|
||||
*"Candidate: (none)"*) return 1 ;;
|
||||
*"Candidate:"*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
;;
|
||||
dnf) dnf -q info "$pkg" >/dev/null 2>&1 ;;
|
||||
yum) yum -q info "$pkg" >/dev/null 2>&1 ;;
|
||||
pacman) pacman -Si "$pkg" >/dev/null 2>&1 || pacman -Sg "$pkg" >/dev/null 2>&1 ;;
|
||||
zypper) zypper --non-interactive --quiet search --match-exact "$pkg" >/dev/null 2>&1 ;;
|
||||
apk) [ -n "$(apk search -x "$pkg" 2>/dev/null)" ] ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
filter_available() {
|
||||
local pkg
|
||||
local kept=""
|
||||
local dropped=""
|
||||
|
||||
for pkg in $1; do
|
||||
if pkg_available "$pkg"; then
|
||||
kept="$kept $pkg"
|
||||
else
|
||||
dropped="$dropped $pkg"
|
||||
fi
|
||||
done
|
||||
|
||||
FILTER_KEPT="${kept# }"
|
||||
FILTER_DROPPED="${dropped# }"
|
||||
}
|
||||
|
||||
echo
|
||||
echo "[INFO] Refreshing package metadata..."
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] $REFRESH_CMD"
|
||||
else
|
||||
echo "[WARN] Unknown architecture: $ARCH"
|
||||
echo "[WARN] Attempting to use x86_64 package list."
|
||||
if [[ "$IS_UBUNTU_26_OR_NEWER" == "true" ]]; then
|
||||
INSTALL_PACKAGES="$COMMON_PACKAGES gcc-multilib g++-multilib glibc-source libncurses-dev python3-setuptools"
|
||||
else
|
||||
INSTALL_PACKAGES="$COMMON_PACKAGES gcc-multilib g++-multilib libncurses-dev python3-setuptools"
|
||||
fi
|
||||
$REFRESH_CMD
|
||||
fi
|
||||
|
||||
echo "[INFO] Checking package availability for this release..."
|
||||
|
||||
filter_available "$REQUIRED_PACKAGES"
|
||||
REQUIRED_AVAILABLE="$FILTER_KEPT"
|
||||
REQUIRED_MISSING="$FILTER_DROPPED"
|
||||
|
||||
filter_available "$OPTIONAL_PACKAGES"
|
||||
OPTIONAL_AVAILABLE="$FILTER_KEPT"
|
||||
OPTIONAL_MISSING="$FILTER_DROPPED"
|
||||
|
||||
INSTALL_PACKAGES="$REQUIRED_AVAILABLE $OPTIONAL_AVAILABLE"
|
||||
INSTALL_PACKAGES="$(echo "$INSTALL_PACKAGES" | tr -s '[:space:]' ' ' | sed 's/^ //;s/ $//')"
|
||||
|
||||
if [ -z "$INSTALL_PACKAGES" ]; then
|
||||
echo "[ERROR] No installable package found. Are the repositories configured?" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Display package list ---
|
||||
echo
|
||||
echo "========================================"
|
||||
echo "📦 Packages to install:"
|
||||
echo "$INSTALL_PACKAGES"
|
||||
echo "$INSTALL_PACKAGES" | tr ' ' '\n' | sed 's/^/ /'
|
||||
echo "========================================"
|
||||
echo
|
||||
|
||||
echo "[INFO] Updating APT repositories..."
|
||||
sudo apt update -y
|
||||
if [ -n "$REQUIRED_MISSING" ]; then
|
||||
echo
|
||||
echo "[WARN] Required packages not offered by this release (build may fail):"
|
||||
echo " $REQUIRED_MISSING"
|
||||
fi
|
||||
|
||||
echo "[INFO] Installing packages..."
|
||||
sudo apt install -y $INSTALL_PACKAGES
|
||||
if [ -n "$OPTIONAL_MISSING" ]; then
|
||||
echo
|
||||
echo "[INFO] Optional packages unavailable here, skipped:"
|
||||
echo " $OPTIONAL_MISSING"
|
||||
fi
|
||||
|
||||
# --- Post installation notice ---
|
||||
echo
|
||||
echo "========================================"
|
||||
echo "✅ OpenWRT build environment setup complete!"
|
||||
echo "========================================"
|
||||
echo
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo
|
||||
echo "[DRY-RUN] $INSTALL_CMD $INSTALL_PACKAGES"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$ARCH" == "x86_64" || "$ARCH" == "amd64" ]]; then
|
||||
echo "👉 [NOTE] Go language is optional on x86_64."
|
||||
echo " If you plan to compile packages requiring Go (e.g. firewall4, netifd), install it manually:"
|
||||
echo " sudo apt install golang -y"
|
||||
else
|
||||
echo "👉 [INFO] Go language installed for ARM64 cross compilation."
|
||||
if [ "$ASSUME_YES" != true ]; then
|
||||
echo
|
||||
read -r -p "Proceed with installation? [Y/n]: " reply
|
||||
case "$reply" in
|
||||
[nN]|[nN][oO]) echo "[INFO] Aborted by user."; exit 0 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "⚙️ After running 'make menuconfig', set Go toolchain path if required:"
|
||||
echo " Example: /usr/lib/go-1.18"
|
||||
echo "[INFO] Installing packages..."
|
||||
|
||||
FAILED_PACKAGES=""
|
||||
if ! $INSTALL_CMD $INSTALL_PACKAGES; then
|
||||
echo
|
||||
echo "[WARN] Bulk installation failed. Retrying package by package..."
|
||||
for pkg in $INSTALL_PACKAGES; do
|
||||
if ! $INSTALL_CMD "$pkg" >/dev/null 2>&1; then
|
||||
FAILED_PACKAGES="$FAILED_PACKAGES $pkg"
|
||||
echo " [FAIL] $pkg"
|
||||
fi
|
||||
done
|
||||
FAILED_PACKAGES="${FAILED_PACKAGES# }"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "========================================"
|
||||
if [ -n "$FAILED_PACKAGES" ]; then
|
||||
echo "⚠️ Setup finished with errors."
|
||||
echo " Failed packages: $FAILED_PACKAGES"
|
||||
else
|
||||
echo "✅ OpenWrt build environment setup complete!"
|
||||
fi
|
||||
echo "========================================"
|
||||
echo
|
||||
|
||||
if [ "$WITH_GO" != true ]; then
|
||||
echo "👉 [NOTE] The Go toolchain is only needed for Go based packages"
|
||||
echo " (e.g. xray-core, v2ray). Install it with:"
|
||||
echo " ./prepare-openwrt-env.sh --with-go"
|
||||
echo
|
||||
fi
|
||||
|
||||
if is_arm64; then
|
||||
echo "👉 [NOTE] arm64 has no native multilib. The cross-multilib packages are"
|
||||
echo " optional and only needed for x86 targets; missing ones were skipped."
|
||||
echo
|
||||
fi
|
||||
|
||||
echo "👉 Next step:"
|
||||
echo " ./prepare-openwrt.sh # to clone and set up OpenWRT source"
|
||||
echo " ./prepare-openwrt.sh # clone and set up the OpenWrt source"
|
||||
echo
|
||||
|
||||
1211
prepare-openwrt.sh
1211
prepare-openwrt.sh
File diff suppressed because it is too large
Load Diff
@@ -1,74 +1,172 @@
|
||||
#!/bin/bash
|
||||
# Automatically detect Go installation and update .config for OpenWRT
|
||||
# Point the OpenWrt build at an installed Go toolchain.
|
||||
#
|
||||
# Go based packages (tailscale, adguardhome, easytier, cloudflared, ...) need a
|
||||
# host Go. By default OpenWrt builds the whole bootstrap chain from source; an
|
||||
# already installed Go can replace part or all of that:
|
||||
#
|
||||
# --external GOLANG_BUILD_BOOTSTRAP=n, host Go is built with the
|
||||
# installed toolchain directly (skips the entire chain,
|
||||
# requires a Go new enough for the target version)
|
||||
# default keep GOLANG_BUILD_BOOTSTRAP=y and only set the root, which
|
||||
# skips the initial bootstrap step
|
||||
#
|
||||
# Usage: ./update-go-path.sh [options]
|
||||
#
|
||||
# --external skip the bootstrap chain entirely (recommended)
|
||||
# --go-root <dir> use this Go instead of auto-detecting
|
||||
# --skip-defconfig do not run make defconfig afterwards
|
||||
# -n, --dry-run show what would change, write nothing
|
||||
# -h, --help show this help
|
||||
#
|
||||
# Run it from the OpenWrt root: ./helper/update-go-path.sh
|
||||
#
|
||||
# Author: Zhe Yuan
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
umask 022
|
||||
|
||||
CONFIG_FILE=".config"
|
||||
EXTERNAL=false
|
||||
GO_ROOT=""
|
||||
RUN_DEFCONFIG=true
|
||||
DRY_RUN=false
|
||||
|
||||
echo "========================================"
|
||||
echo " Go Path Auto Updater for OpenWRT"
|
||||
echo "========================================"
|
||||
info() { echo "[INFO] $*"; }
|
||||
warn() { echo "[WARN] $*"; }
|
||||
err() { echo "[ERROR] $*" >&2; }
|
||||
|
||||
# --- Check for .config file ---
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo "[ERROR] No .config file found in current directory!"
|
||||
echo "Please run this script inside your OpenWRT build root."
|
||||
exit 1
|
||||
fi
|
||||
usage() { sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//;s/^#$//'; }
|
||||
|
||||
# --- Detect available Go installations ---
|
||||
GO_DIRS=(/usr/lib/go-*/)
|
||||
if [ ${#GO_DIRS[@]} -eq 0 ]; then
|
||||
echo "[ERROR] No Go installation found under /usr/lib/go-*"
|
||||
echo "Please install Go using: sudo apt install golang -y"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Extract version numbers ---
|
||||
GO_VERSIONS=()
|
||||
for dir in "${GO_DIRS[@]}"; do
|
||||
ver=$(basename "$dir" | sed -E 's/^go-([0-9]+\.[0-9]+).*$/\1/')
|
||||
GO_VERSIONS+=("$ver")
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--external) EXTERNAL=true ;;
|
||||
--go-root)
|
||||
if [ $# -lt 2 ] || [ -z "${2:-}" ]; then err "--go-root requires a directory."; exit 1; fi
|
||||
GO_ROOT="$2"; shift ;;
|
||||
--skip-defconfig) RUN_DEFCONFIG=false ;;
|
||||
-n|--dry-run) DRY_RUN=true ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) err "Unknown option: $1"; echo "Run '$0 --help' for usage." >&2; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# --- Sort versions and pick the latest ---
|
||||
IFS=$'\n' sorted=($(sort -V <<<"${GO_VERSIONS[*]}"))
|
||||
LATEST_VER="${sorted[-1]}"
|
||||
LATEST_PATH="/usr/lib/go-${LATEST_VER}/"
|
||||
echo "========================================"
|
||||
echo " Go Toolchain Setup for OpenWrt"
|
||||
echo "========================================"
|
||||
|
||||
echo "[INFO] Found Go versions: ${GO_VERSIONS[*]}"
|
||||
echo "[INFO] Using latest Go version: $LATEST_VER"
|
||||
echo "[INFO] Path: $LATEST_PATH"
|
||||
|
||||
# --- Check if multiple versions exist ---
|
||||
if [ ${#GO_VERSIONS[@]} -gt 1 ]; then
|
||||
echo "[WARN] Multiple Go versions detected!"
|
||||
echo "If you prefer to use an older version, modify manually:"
|
||||
echo " CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT=\"/usr/lib/go-X.XX/\""
|
||||
echo
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
err "No .config in the current directory."
|
||||
err "From the OpenWrt root, run it as: ./helper/$(basename "$0")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Update .config ---
|
||||
if grep -q '^CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT=' "$CONFIG_FILE"; then
|
||||
echo "[INFO] Updating existing CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT..."
|
||||
sed -i "s|^CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT=.*|CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT=\"${LATEST_PATH}\"|" "$CONFIG_FILE"
|
||||
# --- Locate a Go installation --------------------------------------------
|
||||
if [ -z "$GO_ROOT" ]; then
|
||||
# nullglob only drops words that actually contain a wildcard; a literal path
|
||||
# such as /usr/local/go/ survives even when it does not exist. So collect
|
||||
# candidates and keep only the directories that are really there.
|
||||
shopt -s nullglob
|
||||
CANDIDATES=(/usr/lib/go-*/ /usr/local/go/ /usr/lib/golang/)
|
||||
shopt -u nullglob
|
||||
|
||||
GO_DIRS=()
|
||||
for dir in "${CANDIDATES[@]}"; do
|
||||
[ -d "$dir" ] && GO_DIRS+=("$dir")
|
||||
done
|
||||
|
||||
if [ ${#GO_DIRS[@]} -eq 0 ]; then
|
||||
err "No Go installation found under /usr/lib/go-*, /usr/local/go or /usr/lib/golang."
|
||||
err "Install one with: sudo apt install golang-1.24-go"
|
||||
err "or keep the default, where OpenWrt builds its own bootstrap chain."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BEST_VER=""
|
||||
for dir in "${GO_DIRS[@]}"; do
|
||||
[ -x "${dir}bin/go" ] || continue
|
||||
ver="$("${dir}bin/go" version 2>/dev/null | awk '{print $3}' | sed 's/^go//')"
|
||||
[ -n "$ver" ] || continue
|
||||
if [ -z "$BEST_VER" ] ||
|
||||
[ "$(printf '%s\n%s\n' "$BEST_VER" "$ver" | sort -V | tail -n 1)" = "$ver" ]; then
|
||||
BEST_VER="$ver"
|
||||
GO_ROOT="$dir"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$GO_ROOT" ]; then
|
||||
err "Found Go directories but none contained a working bin/go."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ${#GO_DIRS[@]} -gt 1 ]; then
|
||||
info "Go installations found: ${GO_DIRS[*]}"
|
||||
info "Using the newest; override with --go-root <dir>."
|
||||
fi
|
||||
fi
|
||||
|
||||
GO_ROOT="${GO_ROOT%/}/"
|
||||
if [ ! -x "${GO_ROOT}bin/go" ]; then
|
||||
err "No usable Go compiler at ${GO_ROOT}bin/go"
|
||||
exit 1
|
||||
fi
|
||||
GO_VER="$("${GO_ROOT}bin/go" version | awk '{print $3}' | sed 's/^go//')"
|
||||
|
||||
info "Go toolchain: $GO_ROOT (version $GO_VER)"
|
||||
|
||||
# --- Warn if it is older than what the tree wants -------------------------
|
||||
BOOTSTRAP_MK="feeds/packages/lang/golang/golang-bootstrap/Makefile"
|
||||
if [ -f "$BOOTSTRAP_MK" ]; then
|
||||
NEED="$(awk -F':=' '/^GO_VERSION_MAJOR_MINOR/ {print $2; exit}' "$BOOTSTRAP_MK" | tr -d ' ')"
|
||||
if [ -n "$NEED" ] &&
|
||||
[ "$(printf '%s\n%s\n' "$NEED" "$GO_VER" | sort -V | head -n 1)" != "$NEED" ]; then
|
||||
warn "Installed Go $GO_VER is older than the bootstrap version $NEED this tree expects."
|
||||
warn "The build may fail; consider installing golang-$NEED-go."
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Apply ----------------------------------------------------------------
|
||||
set_config() {
|
||||
local key="$1" val="$2"
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] $key=$val"
|
||||
return
|
||||
fi
|
||||
# Drop every existing form of the symbol, then append once.
|
||||
sed -i "/^${key}=/d;/^# ${key} is not set$/d" "$CONFIG_FILE"
|
||||
if [ "$val" = "n" ]; then
|
||||
echo "# $key is not set" >> "$CONFIG_FILE"
|
||||
else
|
||||
echo "$key=$val" >> "$CONFIG_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
set_config CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT "\"$GO_ROOT\""
|
||||
|
||||
if [ "$EXTERNAL" = true ]; then
|
||||
set_config CONFIG_GOLANG_BUILD_BOOTSTRAP n
|
||||
info "Bootstrap chain disabled; host Go is built with the installed toolchain."
|
||||
else
|
||||
echo "[INFO] Adding CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT..."
|
||||
echo "CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT=\"${LATEST_PATH}\"" >> "$CONFIG_FILE"
|
||||
set_config CONFIG_GOLANG_BUILD_BOOTSTRAP y
|
||||
info "Bootstrap chain kept (only the initial step is skipped)."
|
||||
info "Pass --external to skip it entirely."
|
||||
fi
|
||||
|
||||
# --- Verify result ---
|
||||
echo
|
||||
echo "[SUCCESS] Go path updated in .config:"
|
||||
grep 'CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT' "$CONFIG_FILE"
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo
|
||||
info "Dry run complete; .config was not modified."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$RUN_DEFCONFIG" = true ]; then
|
||||
info "Running make defconfig..."
|
||||
make defconfig >/dev/null
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "========================================"
|
||||
echo "✅ Go path update complete!"
|
||||
echo "OK Go configuration updated."
|
||||
echo "========================================"
|
||||
echo
|
||||
echo "👉 If you wish to override manually, edit .config and change:"
|
||||
echo " CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT=\"/usr/lib/go-X.XX/\""
|
||||
grep -E '^(CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT|CONFIG_GOLANG_BUILD_BOOTSTRAP)|^# CONFIG_GOLANG_BUILD_BOOTSTRAP' "$CONFIG_FILE" | sed 's/^/ /'
|
||||
echo
|
||||
|
||||
Reference in New Issue
Block a user