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>
This commit is contained in:
2026-09-05 21:24:54 -04:00
parent e668221b4e
commit a98b1e1c76

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 "$key=$val" >> "$CONFIG_FILE"
fi
}
set_config CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT "\"$GO_ROOT\""
if [ "$EXTERNAL" = true ]; then
set_config CONFIG_GOLANG_BUILD_BOOTSTRAP n
info "Bootstrap chain disabled; host Go is built with the installed toolchain."
else
echo "[INFO] Adding CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT..."
echo "CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT=\"${LATEST_PATH}\"" >> "$CONFIG_FILE"
set_config CONFIG_GOLANG_BUILD_BOOTSTRAP y
info "Bootstrap chain kept (only the initial step is skipped)."
info "Pass --external to skip it entirely."
fi
# --- Verify result ---
echo
echo "[SUCCESS] Go path updated in .config:"
grep 'CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT' "$CONFIG_FILE"
if [ "$DRY_RUN" = true ]; then
echo
info "Dry run complete; .config was not modified."
exit 0
fi
if [ "$RUN_DEFCONFIG" = true ]; then
info "Running make defconfig..."
make defconfig >/dev/null
fi
echo
echo "========================================"
echo " Go path update complete!"
echo "OK Go configuration updated."
echo "========================================"
echo
echo "👉 If you wish to override manually, edit .config and change:"
echo " CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT=\"/usr/lib/go-X.XX/\""
grep -E '^(CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT|CONFIG_GOLANG_BUILD_BOOTSTRAP)|^# CONFIG_GOLANG_BUILD_BOOTSTRAP' "$CONFIG_FILE" | sed 's/^/ /'
echo