diff --git a/README.md b/README.md index 7006053..4406594 100644 --- a/README.md +++ b/README.md @@ -62,42 +62,17 @@ Additional options can be specified when installing Go: -h, --help Display this message. ### A Note on Compiling Go 1.5+ -Go 1.5+ removed the C compilers from the toolchain and [replaced][compiler_note] them with one written in Go. Obviously, this creates a bootstrapping problem if you don't already have a working Go install. In order to compile Go 1.5+, make sure Go 1.4 is installed first. If Go 1.4 won't install try a later version (e.g. go1.5), just make sure you have the `-B` option after the version number. +Go 1.5+ removed the C compilers from the toolchain and replaced them with one written in Go. GVM handles this bootstrapping problem automatically. When you install a version of Go from source that requires a bootstrap compiler, GVM will: -``` -gvm install go1.4 -B -gvm use go1.4 -export GOROOT_BOOTSTRAP=$GOROOT -gvm install go1.7 -``` - -### A Note on ARMv6 and ARMv7 architectures (32 bit) -Binary versions for ARMv6 architecture are available [starting from Go 1.6](https://go.dev/dl/#go1.6). So, it is necessary to bootstrap with an existing binary version, then it will be possible compiling other versions. For instance, to bootstrap a setup, version `1.21.0` may be used: - -``` -gvm install go1.21.0 -B -gvm use go1.21.0 -``` +1. Search your installed versions for a compatible bootstrap Go. +2. If none is found, it will automatically download and install the required bootstrap version as a binary. +3. Use that binary to compile your target version. -And then, compile any other version: +This allows you to install any version of Go (including `master`) on a clean system without any pre-existing Go installation. -``` -gvm install go1.20.7 -``` - -#### To install Go 1.20+ -Go 1.20+ requires go1.17.3+. Use the below: +### A Note on ARMv6 and ARMv7 architectures (32 bit) +Binary versions for ARMv6 architecture are available [starting from Go 1.6](https://go.dev/dl/#go1.6). GVM will automatically pull a compatible binary to bootstrap source installations on ARM platforms as well. -``` -gvm install go1.4 -B -gvm use go1.4 -export GOROOT_BOOTSTRAP=$GOROOT -gvm install go1.17.13 -gvm use go1.17.13 -export GOROOT_BOOTSTRAP=$GOROOT -gvm install go1.20 -gvm use go1.20 -``` [compiler_note]: https://docs.google.com/document/d/1OaatvGhEAq7VseQ9kkavxKNAfepWy2yhPUBs96FGV28/edit diff --git a/scripts/function/gvm_bootstrap b/scripts/function/gvm_bootstrap new file mode 100644 index 0000000..fe85a28 --- /dev/null +++ b/scripts/function/gvm_bootstrap @@ -0,0 +1,154 @@ +#!/usr/bin/env bash + +__gvm_get_required_bootstrap_version() { + local version=$1 + if [[ "$version" == "master" ]]; then + echo "1.22.6" + return 0 + fi + + local semver=$(extract_version "$version") + + # Check for non-numeric versions (like tip, etc) + if [[ ! "$semver" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + # Default to latest bootstrap for unknown versions + echo "1.22.6" + return 0 + fi + + # Go 1.24+ needs Go 1.22.6+ + compare_version "$semver" "1.24" + if [[ $? -ne 2 ]]; then + echo "1.22.6" + return 0 + fi + + # Go 1.22+ needs Go 1.20+ + compare_version "$semver" "1.22" + if [[ $? -ne 2 ]]; then + echo "1.20.14" + return 0 + fi + + # Go 1.20+ needs Go 1.17.13+ + compare_version "$semver" "1.20" + if [[ $? -ne 2 ]]; then + echo "1.17.13" + return 0 + fi + + # Go 1.5+ needs Go 1.4+ + compare_version "$semver" "1.5" + if [[ $? -ne 2 ]]; then + echo "1.4.3" + return 0 + fi + + echo "" + return 1 +} + +__gvm_find_best_installed_bootstrap() { + local min_version=$1 + local best_version="" + local best_semver="" + + if [[ ! -d "$GVM_ROOT/gos" ]]; then + return 1 + fi + + for v_dir in "$GVM_ROOT/gos"/*; do + [[ -d "$v_dir" ]] || continue + [[ -x "$v_dir/bin/go" ]] || continue + local v_name=$(basename "$v_dir") + local v_semver=$(extract_version "$v_name") + + # Only consider numeric versions + if [[ ! "$v_semver" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + continue + fi + + compare_version "$v_semver" "$min_version" + if [[ $? -ne 2 ]]; then # v_semver >= min_version + if [[ -z "$best_version" ]]; then + best_version=$v_name + best_semver=$v_semver + else + compare_version "$v_semver" "$best_semver" + if [[ $? -eq 1 ]]; then # v_semver > best_semver + best_version=$v_name + best_semver=$v_semver + fi + fi + fi + done + + if [[ -n "$best_version" ]]; then + echo "$best_version" + return 0 + fi + return 1 +} + +__gvm_is_bootstrap_compatible() { + local target=$1 + local bootstrap_path=$2 + + local req_version=$(__gvm_get_required_bootstrap_version "$target") + if [[ -z "$req_version" ]]; then + return 0 # No bootstrap needed + fi + + if [[ -z "$bootstrap_path" ]]; then + return 1 # No bootstrap path provided + fi + + if [[ ! -x "$bootstrap_path/bin/go" ]]; then + return 1 # Bootstrap path doesn't have a working go + fi + + # Try to get the version of the bootstrap Go + local bootstrap_version_out=$("$bootstrap_path/bin/go" version 2>/dev/null) + if [[ $? -ne 0 ]]; then + return 1 + fi + + # Extract version from "go version go1.22.6 linux/amd64" + local bootstrap_version=$(echo "$bootstrap_version_out" | awk '{print $3}') + local bootstrap_semver=$(extract_version "$bootstrap_version") + + compare_version "$bootstrap_semver" "$req_version" + if [[ $? -ne 2 ]]; then + return 0 # Compatible (>=) + fi + + return 1 # Incompatible (<) +} + +__gvm_ensure_bootstrap() { + local target_version=$1 + local req_version=$(__gvm_get_required_bootstrap_version "$target_version") + + if [[ -z "$req_version" ]]; then + return 0 + fi + + local bootstrap_version=$(__gvm_find_best_installed_bootstrap "$req_version") + + if [[ -n "$bootstrap_version" ]]; then + export GOROOT_BOOTSTRAP="$GVM_ROOT/gos/$bootstrap_version" + display_message "Using $bootstrap_version as bootstrap" + return 0 + fi + + display_message "No suitable bootstrap Go version found. Installing go$req_version binary..." + + # Use the absolute path to gvm to ensure we use the right one + "$GVM_ROOT/bin/gvm" install "go$req_version" --binary || { + display_error "Failed to install bootstrap Go version go$req_version" + return 1 + } + + export GOROOT_BOOTSTRAP="$GVM_ROOT/gos/go$req_version" + return 0 +} diff --git a/scripts/install b/scripts/install index a039c27..fdd5153 100755 --- a/scripts/install +++ b/scripts/install @@ -90,7 +90,13 @@ compile_go() { MAKE_SCRIPT=make.bash ;; esac - [ -z "$GOROOT_BOOTSTRAP" ] && export GOROOT_BOOTSTRAP=$(go env GOROOT) + + [ -z "$GOROOT_BOOTSTRAP" ] && [ "$(command -v go)" ] && export GOROOT_BOOTSTRAP=$(go env GOROOT) + + if ! __gvm_is_bootstrap_compatible "$GO_NAME" "$GOROOT_BOOTSTRAP"; then + __gvm_ensure_bootstrap "$GO_NAME" || display_fatal "Failed to ensure bootstrap Go version" + fi + unset GOARCH && unset GOOS && unset GOPATH && unset GOBIN && unset GOROOT && export GOBIN=$GO_INSTALL_ROOT/bin && export PATH=$GOBIN:$PATH && @@ -196,8 +202,8 @@ download_binary() { GVM_ARCH="s390x" fi - SEMVER=$(extract_version $VERSION) - compare_version $SEMVER "1.4.3" + SEMVER=$(extract_version "$VERSION") + compare_version "$SEMVER" "1.4.3" if [ $? -eq 2 ]; then GO_BINARY_FILE=${VERSION}.${GVM_OS}-${GVM_ARCH}${GVM_OS_VERSION}.tar.gz else