#!/bin/bash # 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 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 -euo pipefail umask 022 CONFIG_FILE=".config" EXTERNAL=false GO_ROOT="" RUN_DEFCONFIG=true DRY_RUN=false info() { echo "[INFO] $*"; } warn() { echo "[WARN] $*"; } err() { echo "[ERROR] $*" >&2; } usage() { sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//;s/^#$//'; } 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 echo "========================================" echo " Go Toolchain Setup for OpenWrt" 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 # --- 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 ." 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 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 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 "OK Go configuration updated." echo "========================================" grep -E '^(CONFIG_GOLANG_EXTERNAL_BOOTSTRAP_ROOT|CONFIG_GOLANG_BUILD_BOOTSTRAP)|^# CONFIG_GOLANG_BUILD_BOOTSTRAP' "$CONFIG_FILE" | sed 's/^/ /' echo