Compare commits

..

6 Commits

Author SHA1 Message Date
2abd8aba00 Rewrite the README for the current script set
The README had been patched incrementally across several changes and no longer
matched the repository: sections about adding a device and the package list
format had ended up nested under Quick start, and the layout it described
predated the per-device packages/ directory.

Restructure it around what someone actually does: layout, requirements, a
five-step quick start, then per-script reference. Document the parts that are
easy to get wrong and were learned the hard way — the buildbot flags and why
stripping them is safe, the Go toolchain options, the WSL PATH problem that
breaks every Go package, and the git clean -xdff hazard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 00:28:54 -04:00
a98b1e1c76 Make update-go-path.sh able to skip the Go bootstrap chain
Setting CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT on its own only skips the
initial bootstrap step; OpenWrt still builds the rest of the chain from
source because CONFIG_GOLANG_BUILD_BOOTSTRAP defaults to y. Add --external,
which clears that symbol so the installed toolchain builds host Go directly.

Fix the "no Go installed" detection, which never fired. An unmatched glob is
left as a literal, so the array held the pattern itself and the script wrote
CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT="/usr/lib/go-go-*/" and reported
success. nullglob alone is not enough either, since it only drops words that
contain a wildcard and the literal candidate paths survive regardless, so
each candidate is now checked for existence.

Also pick the Go version by asking bin/go rather than parsing the directory
name, warn when the installed Go is older than the bootstrap version the
tree expects, replace every existing form of a symbol instead of appending
a duplicate, and run make defconfig afterwards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:24:54 -04:00
e668221b4e Keep one extras list per device instead of per version
The package lists were stored as config_<version>/packages_<device>_<version>.txt,
but the version dimension was pure duplication: the Linksys MX8500 extras were
byte-identical across 24.10.4, 25.12.0, 25.12.2 and 25.12.4, and the Cudy lists
only ever changed with the device, never with the release.

Your packages now live in packages/<device-id>.txt, one file per device and
independent of the OpenWrt version. The built-in part is fetched per release
anyway, so upgrading needs no file changes at all.

The combined list is a build artefact and moves to .generated/, which is
gitignored, along with the buildinfo saved by --save-buildinfo --offline.

This removes work rather than adding it: because the generated file is now
entirely machine-owned, the sentinel comments, the in-section replacement state
machine and the "line containing base-files and libc is the old generated one"
migration heuristic are all gone. Hand-written content is isolated in the extras
file, generated content in .generated/.

Also fix version detection when a script is run from the helper directory. The
helper checkout is its own git repository inside the OpenWrt worktree, so git
commands there described the helper repo -- a checkout of OpenWrt 25.12.5 was
reported as SNAPSHOT because the helper repo is on main. Version queries are now
anchored to the OpenWrt tree.

The config_* archives are deleted; git history keeps them. That includes
config_24.10.4/apply-dahdi-patches.sh, which only applied to 24.10.4.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:18:29 -04:00
0255d2f890 Generate the built-in package list from the official index
The ##built-in section of each package list was copied by hand from the
Firmware Selector for every new release. It is now generated from the
target's profiles.json (default_packages + device_packages, plus the two
packages the Selector adds on top), which reproduces the hand-written list
byte for byte, order included.

New lib-openwrt-upstream.sh holds the shared upstream access: URL
construction, cached fetches, version detection, device search over
.overview.json and profiles.json parsing. It needs python3, not jq.

New gen-package-list.sh writes config_<ver>/packages_<dev>_<ver>.txt,
replacing only the generated block so hand-written extras and the ##module
section survive; --stdout and --apply skip the file entirely.

download-config.sh:
- Look the device up in the official index instead of a hardcoded table of
  three devices, so any supported device can be selected by name.
- Build the firmware URLs from profiles.json images[]. The old code pasted
  the target into the filename with its '/' intact, so the URL was always
  wrong and the check always warned.
- Stage the download in a temp file and validate it before touching
  .config, and keep a backup. A 404 used to truncate .config to nothing.
- Fix version detection: `git describe --tags --abbrev=0` returns a tag
  whenever any tag is reachable, so the entire fallback chain below it was
  unreachable and a branch tip silently produced the wrong release URL.
- Build only the selected device instead of all 35 in the target, and turn
  off the buildbot flags (ALL_KMODS, ALL_NONSHARED, SDK, IB,
  MAKE_TOOLCHAIN, COLLECT_KERNEL_DEBUG, AUTOREMOVE). Verified not to change
  the firmware: the set of packages built into the image is identical
  either way (193 packages), while the module packages to build drop from
  1248 to 0. --keep-buildbot-flags and --all-profiles restore the old
  behaviour.
- Assert the device symbol survived make defconfig, so a tree that does not
  match the release fails loudly instead of silently building the default.

add-openwrt-packages.sh:
- Rewrite existing CONFIG_PACKAGE_ lines in place. Repeated runs used to
  append duplicates and grow .config every time.
- Support removal via a -pkg prefix or a ##remove section.
- Fix the parser dropping a lone '#' or an unknown ##header into the
  package list, where it was reported as a missing package named '#'.
- Read .config once into a map instead of grepping it per package, and
  compare package names exactly rather than as regexes.
- After make defconfig, report any package whose final state differs from
  what was asked for, which is the only way to see a removal that a
  dependency pulled back in.

Delete add-openwrt-packages_old.sh; nothing referenced it and it was a
strict subset of the current script.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:14:36 -04:00
351e7c41f3 Require the OpenWrt root in add-external-repos.sh
Every path in the script is relative to the current directory, so running
it from anywhere else cloned the packages into that directory instead.
Now that the helper checkout lives at openwrt/helper/, running it there
would populate helper/package/ rather than the real package/.

Use the same guard the other helper scripts already have.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 20:31:32 -04:00
de7f3f12fd Rewrite the prepare scripts for current OpenWrt and more distros
prepare-openwrt-env.sh
- Dispatch on the package manager instead of guessing from the distro
  version: apt, dnf/yum, pacman, zypper and apk are all supported.
- Probe every package against the running release and skip the ones that
  no longer exist. Previously a single dropped package (on Ubuntu 26.04
  arm64, gcc-multilib-s390x-linux-gnu) failed the whole apt command and
  set -e aborted the script, so nothing was installed at all.
- Align the package lists with the official build system guide, adding
  libelf-dev, python3-dev, bc, xsltproc, zstd, u-boot-tools and others.
- Make Go opt-in via --with-go rather than forced on arm64.
- Add -y/--yes, -n/--dry-run and -h/--help; allow running as root.

prepare-openwrt.sh
- Split read-only discovery from mutation. Versions are resolved over
  git ls-remote before anything is moved, so aborting at the prompt
  leaves the helper checkout untouched.
- Resume an interrupted first run instead of failing forever: a root
  with .git but no source tree was previously unrecoverable without a
  manual rm -rf. Also restore the worktree when HEAD already points at
  the target ref, where checkout is a no-op.
- Detect the relocated layout by content rather than directory name, so
  renaming the root no longer triggers a second, nested relocation.
- Replace the staged in-place relocation with a resumable content move,
  removing the window that could strand the checkout in a hidden dir.
- Filter release candidates before sorting; sort -V orders v25.12.0-rc1
  after v25.12.0, so the newest stable tag could be an rc.
- Follow upstream: snapshots use main (master as fallback) and the dead
  -SNAPSHOT tag lookup is gone.
- Use a partial clone (--filter=blob:none) by default.
- Guard version switches on a dirty tree and back up .config first.
- Add /helper/ to .git/info/exclude and warn that git clean -xdff still
  deletes it, since -x bypasses ignore rules by design.
- Leave a symlink at the old checkout path so shells and editors whose
  working directory still points there keep working.
- Add --stable/--branch/--snapshot plus -y, -n, --force, --full-clone,
  --allow-rc, --root, --skip-feeds, --skip-defconfig, --repo-url and
  --no-compat-link.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 20:31:26 -04:00
32 changed files with 2918 additions and 50521 deletions

3
.gitignore vendored Normal file
View 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
View File

@@ -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.
## Whats 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 cant 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 youre 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).

View File

@@ -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"

View File

@@ -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
# Verify file exists
if [ ! -f "$PKG_FILE" ]; then
echo "❌ Error: File not found: $PKG_FILE"
[ -t 0 ] || ASSUME_YES=true
if [ -z "$PKG_FILE" ]; then
err "No package list given."
usage >&2
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 [ "$PKG_FILE" != "-" ] && [ ! -f "$PKG_FILE" ]; then
err "Package list not found: $PKG_FILE"
exit 1
fi
# Ensure .config exists
if [ ! -f ".config" ]; then
echo "⚙️ Generating initial .config..."
make defconfig
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
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"
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"
# Run defconfig to sync dependencies
echo
echo "⚙️ Running make defconfig..."
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
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
# 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

View File

@@ -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

View File

@@ -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

View File

@@ -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=""

View File

@@ -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=""

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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."
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=""
WITH_PACKAGES=false
SINGLE_PROFILE=true
STRIP_BUILDBOT=true
SAVE_BUILDINFO=false
OFFLINE=false
OUTPUT=""
RUN_DEFCONFIG=true
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
}
# --- Detect Git version ---
VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
VERSION="${VERSION#v}"
IS_LATEST=false
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.*"
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"
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 " 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 "========================================"
# --- Run make defconfig ---
echo "[INFO] Running make defconfig..."
if [ "$DRY_RUN" != true ]; then
echo
if ! confirm "Proceed?" yes; then
info "Aborted by user."
exit 0
fi
fi
# --- 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
echo "[INFO] defconfig done."
# --- Check firmware existence ---
if curl --head --silent --fail "$FIRMWARE_URL" >/dev/null; then
echo "[INFO] Firmware exists: $FIRMWARE_URL"
else
echo "[WARN] Firmware not found or URL invalid."
# 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
# --- 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"
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
View 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
View 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
}

View 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

View 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

View 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

View File

@@ -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"
# --- 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 [[ "$current" == "unknown" ]]; then
return 1
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
if command -v dpkg >/dev/null 2>&1; then
dpkg --compare-versions "$current" ge "$minimum"
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
# ---------------------------------------------------------------------------
# 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=""
case "$PKG_MGR" in
apt-get)
REFRESH_CMD="$SUDO apt-get update"
INSTALL_CMD="$SUDO apt-get install -y --no-install-recommends"
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"
# 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"
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
if [[ "$DISTRO" == "ubuntu" ]] && version_ge "$VERSION_ID" "26.04"; then
IS_UBUNTU_26_OR_NEWER=true
if [ "$WITH_GO" = true ]; then
OPTIONAL_PACKAGES="$OPTIONAL_PACKAGES golang golang-go"
fi
;;
if [[ "$DISTRO" == "debian" ]] && version_ge "$VERSION_ID" "13"; then
IS_DEBIAN_13_OR_NEWER=true
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
;;
# --- 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"
pacman)
REFRESH_CMD="$SUDO pacman -Sy"
INSTALL_CMD="$SUDO pacman -S --needed --noconfirm"
# --- Determine specific package set ---
if [[ "$ARCH" == "x86_64" || "$ARCH" == "amd64" ]]; then
echo "[INFO] Preparing environment for x86_64."
REQUIRED_PACKAGES="base-devel bash bzip2 git libelf libxslt ncurses openssl \
rsync swig time unzip util-linux wget which zlib python python-setuptools"
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"
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
echo "[INFO] Detected older Ubuntu/Debian (with python3-distutils)."
EXTRA_PACKAGES="gcc-multilib g++-multilib libncurses-dev python3-distutils python3-setuptools"
dropped="$dropped $pkg"
fi
done
INSTALL_PACKAGES="$COMMON_PACKAGES $EXTRA_PACKAGES"
FILTER_KEPT="${kept# }"
FILTER_DROPPED="${dropped# }"
}
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"
echo
echo "[INFO] Refreshing package metadata..."
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] $REFRESH_CMD"
else
EXTRA_PACKAGES="libncurses-dev python3-distutils python3-setuptools"
$REFRESH_CMD
fi
INSTALL_PACKAGES="$COMMON_PACKAGES $EXTRA_PACKAGES $MULTILIB_PACKAGES golang"
echo "[INFO] Checking package availability for this release..."
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
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 "========================================"
if [ -n "$REQUIRED_MISSING" ]; then
echo
echo "[WARN] Required packages not offered by this release (build may fail):"
echo " $REQUIRED_MISSING"
fi
echo "[INFO] Updating APT repositories..."
sudo apt update -y
echo "[INFO] Installing packages..."
sudo apt install -y $INSTALL_PACKAGES
# --- Post installation notice ---
echo
echo "========================================"
echo "✅ OpenWRT build environment setup complete!"
echo "========================================"
if [ -n "$OPTIONAL_MISSING" ]; then
echo
echo "[INFO] Optional packages unavailable here, skipped:"
echo " $OPTIONAL_MISSING"
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 [ "$DRY_RUN" = true ]; then
echo
echo "[DRY-RUN] $INSTALL_CMD $INSTALL_PACKAGES"
exit 0
fi
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

File diff suppressed because it is too large Load Diff

View File

@@ -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 "[INFO] Adding CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT..."
echo "CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT=\"${LATEST_PATH}\"" >> "$CONFIG_FILE"
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
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 ---
if [ "$DRY_RUN" = true ]; then
echo
echo "[SUCCESS] Go path updated in .config:"
grep 'CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT' "$CONFIG_FILE"
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