#!/usr/bin/env bash # Build Fenrir with a suitable Go toolchain without changing the system Go. set -euo pipefail projectDir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) fail() { echo "Error: $*" >&2 exit 1 } usage() { cat <<'EOF' Usage: build-fenrir.sh [-o PATH | --output PATH] [-- GO_BUILD_FLAGS...] Build Fenrir, downloading a suitable Go toolchain when needed. -o, --output PATH Write the binary to PATH (default: checkout/fenrir). Relative paths are resolved from the caller's directory. Missing parent directories are created. -h, --help Show this help without checking build dependencies. -- Pass remaining flags to go build, e.g. -- -trimpath. Go environment variables such as GOFLAGS, GOCACHE, GOMODCACHE, GOTMPDIR, and CGO_CFLAGS/CGO_LDFLAGS are inherited. This script only builds the binary; package installation and resource staging belong in the packaging recipe. EOF } outputPath="$projectDir/fenrir" buildFlags=() while (( $# )); do case $1 in -o|--output) (( $# >= 2 )) || fail "$1 requires a path." [[ -n $2 && $2 != -* ]] || fail "$1 requires a path (use ./ for a path starting with '-')." outputPath=$2 shift 2 ;; -h|--help) usage exit 0 ;; --) shift buildFlags=("$@") break ;; *) fail "Unknown argument: $1. See --help." ;; esac done if [[ $outputPath != /* ]]; then outputPath="$PWD/$outputPath" fi [[ ! -d $outputPath ]] || fail "Output path is a directory; specify the binary filename." cd -- "$projectDir" [[ $(uname -s) == Linux ]] || fail "Fenrir requires Linux." command -v pkg-config >/dev/null || fail "Install pkg-config and the native development libraries listed in README.md." missingLibraries=() for library in libevdev x11 vterm alsa; do if ! pkg-config --exists "$library"; then missingLibraries+=("$library") fi done if (( ${#missingLibraries[@]} )); then fail "Missing development libraries (pkg-config names): ${missingLibraries[*]}. See README.md." fi requiredVersion=$(awk '$1 == "go" { print $2; exit }' go.mod) [[ $requiredVersion =~ ^1\.[0-9]+\.[0-9]+$ ]] || fail "Cannot read the required Go version from go.mod." # Go 1.21+ downloads and verifies the toolchain selected by go.mod itself. # Probe the bundled version so a user's GOTOOLCHAIN setting cannot affect this check. unset GOROOT goCommand="" if command -v go >/dev/null; then installedVersion=$(GOTOOLCHAIN=local go version 2>/dev/null || true) if [[ $installedVersion =~ go1\.([0-9]+) ]] && (( 10#${BASH_REMATCH[1]} >= 21 )); then goCommand=$(command -v go) fi fi if [[ -z $goCommand ]]; then case $(uname -m) in x86_64) archiveArch=amd64 ;; aarch64|arm64) archiveArch=arm64 ;; armv6l|armv7l) archiveArch=armv6l ;; i386|i486|i586|i686) archiveArch=386 ;; ppc64le|riscv64|s390x|loong64) archiveArch=$(uname -m) ;; loongarch64) archiveArch=loong64 ;; *) fail "No Go bootstrap archive configured for $(uname -m). Install Go 1.21 or newer and rerun this script." ;; esac toolchainDir="${XDG_CACHE_HOME:-$HOME/.cache}/fenrir/toolchains/go${requiredVersion}.linux-${archiveArch}" goCommand="$toolchainDir/go/bin/go" if [[ ! -x $goCommand ]]; then command -v curl >/dev/null || fail "Install curl to download Go, or install Go 1.21 or newer." command -v tar >/dev/null || fail "Install tar to unpack Go." mkdir -p -- "$(dirname -- "$toolchainDir")" downloadDir=$(mktemp -d "${toolchainDir}.download.XXXXXX") trap 'rm -rf -- "$downloadDir"' EXIT echo "Downloading Go $requiredVersion for Linux/$archiveArch from go.dev..." curl --fail --location --show-error --proto '=https' --proto-redir '=https' \ --output "$downloadDir/go.tar.gz" \ "https://go.dev/dl/go${requiredVersion}.linux-${archiveArch}.tar.gz" tar -xzf "$downloadDir/go.tar.gz" -C "$downloadDir" [[ -x $downloadDir/go/bin/go ]] || fail "The downloaded archive does not contain Go." # Another build may have finished the same download while ours ran. if [[ ! -x $goCommand ]]; then mkdir -p -- "$toolchainDir" mv -T -- "$downloadDir/go" "$toolchainDir/go" || [[ -x $goCommand ]] fi fi fi # Apply automatic selection only to this build, including when the caller has # disabled it globally. export GOTOOLCHAIN=auto echo "Using $("$goCommand" version)" mkdir -p -- "$(dirname -- "$outputPath")" "$goCommand" build "${buildFlags[@]}" -o "$outputPath" ./cmd/fenrir echo "Fenrir has been built: $outputPath"