mirror of https://github.com/golang/go.git
[release-branch.go1.21] cmd/go: find GOROOT using os.Executable when installed to GOROOT/bin/GOOS_GOARCH
When running make.bash in a cross-compiled configuration
(for example, GOARCH different from GOHOSTARCH), cmd/go
is installed to GOROOT/bin/GOOS_GOARCH instead of GOROOT/bin.
That means that we need to look for GOROOT in both ../.. and ../../..,
not just the former.
Fixes #62144.
Updates #62119.
Updates #18678.
Change-Id: I283c6a10c46df573ff44da826f870417359226a7
Reviewed-on: https://go-review.googlesource.com/c/go/+/521015
Reviewed-by: Michael Matloob <matloob@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
(cherry picked from commit 9e9556d328)
Reviewed-on: https://go-review.googlesource.com/c/go/+/521695
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
parent
2d07bb86f0
commit
6385a6fb18
|
|
@ -490,25 +490,43 @@ func findGOROOT(env string) string {
|
||||||
// depend on the executable's location.
|
// depend on the executable's location.
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canonical returns a directory path that represents
|
||||||
|
// the same directory as dir,
|
||||||
|
// preferring the spelling in def if the two are the same.
|
||||||
|
canonical := func(dir string) string {
|
||||||
|
if isSameDir(def, dir) {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
exe, err := os.Executable()
|
exe, err := os.Executable()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
exe, err = filepath.Abs(exe)
|
exe, err = filepath.Abs(exe)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
// cmd/go may be installed in GOROOT/bin or GOROOT/bin/GOOS_GOARCH,
|
||||||
|
// depending on whether it was cross-compiled with a different
|
||||||
|
// GOHOSTOS (see https://go.dev/issue/62119). Try both.
|
||||||
if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
|
if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
|
||||||
// If def (runtime.GOROOT()) and dir are the same
|
return canonical(dir)
|
||||||
// directory, prefer the spelling used in def.
|
|
||||||
if isSameDir(def, dir) {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
return dir
|
|
||||||
}
|
}
|
||||||
|
if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
|
||||||
|
return canonical(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depending on what was passed on the command line, it is possible
|
||||||
|
// that os.Executable is a symlink (like /usr/local/bin/go) referring
|
||||||
|
// to a binary installed in a real GOROOT elsewhere
|
||||||
|
// (like /usr/lib/go/bin/go).
|
||||||
|
// Try to find that GOROOT by resolving the symlinks.
|
||||||
exe, err = filepath.EvalSymlinks(exe)
|
exe, err = filepath.EvalSymlinks(exe)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
|
if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
|
||||||
if isSameDir(def, dir) {
|
return canonical(dir)
|
||||||
return def
|
}
|
||||||
}
|
if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
|
||||||
return dir
|
return canonical(dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
[compiler:gccgo] skip
|
[compiler:gccgo] skip
|
||||||
|
[short] skip 'builds and links another cmd/go'
|
||||||
|
|
||||||
mkdir $WORK/new/bin
|
mkdir $WORK/new/bin
|
||||||
|
|
||||||
|
|
@ -9,15 +10,18 @@ mkdir $WORK/new/bin
|
||||||
# new cmd/go is built.
|
# new cmd/go is built.
|
||||||
env GOROOT_FINAL=
|
env GOROOT_FINAL=
|
||||||
|
|
||||||
|
# $GOROOT/bin/go is whatever the user has already installed
|
||||||
|
# (using make.bash or similar). We can't make assumptions about what
|
||||||
|
# options it may have been built with, such as -trimpath or GOROOT_FINAL.
|
||||||
|
# Instead, we build a fresh copy of the binary with known settings.
|
||||||
go build -o $WORK/new/bin/go$GOEXE cmd/go &
|
go build -o $WORK/new/bin/go$GOEXE cmd/go &
|
||||||
go build -o $WORK/bin/check$GOEXE check.go &
|
go build -trimpath -o $WORK/bin/check$GOEXE check.go &
|
||||||
wait
|
wait
|
||||||
|
|
||||||
env TESTGOROOT=$GOROOT
|
env TESTGOROOT=$GOROOT
|
||||||
env GOROOT=
|
env GOROOT=
|
||||||
|
|
||||||
# Relocated Executable
|
# Relocated Executable
|
||||||
# cp $TESTGOROOT/bin/go$GOEXE $WORK/new/bin/go$GOEXE
|
|
||||||
exec $WORK/bin/check$GOEXE $WORK/new/bin/go$GOEXE $TESTGOROOT
|
exec $WORK/bin/check$GOEXE $WORK/new/bin/go$GOEXE $TESTGOROOT
|
||||||
|
|
||||||
# Relocated Tree:
|
# Relocated Tree:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
# Regression test for https://go.dev/issue/62119:
|
||||||
|
# A 'go' command cross-compiled with a different GOHOSTOS
|
||||||
|
# should be able to locate its GOROOT using os.Executable.
|
||||||
|
#
|
||||||
|
# (This also tests a 'go' command built with -trimpath
|
||||||
|
# that is not cross-compiled, since we need to build that
|
||||||
|
# configuration for the test anyway.)
|
||||||
|
|
||||||
|
[short] skip 'builds and links another cmd/go'
|
||||||
|
|
||||||
|
mkdir $WORK/new/bin
|
||||||
|
mkdir $WORK/new/bin/${GOOS}_${GOARCH}
|
||||||
|
|
||||||
|
# In this test, we are specifically checking the logic for deriving
|
||||||
|
# the value of GOROOT from os.Executable when runtime.GOROOT is
|
||||||
|
# trimmed away.
|
||||||
|
# GOROOT_FINAL changes the default behavior of runtime.GOROOT,
|
||||||
|
# so we explicitly clear it to remove it as a confounding variable.
|
||||||
|
env GOROOT_FINAL=
|
||||||
|
|
||||||
|
# $GOROOT/bin/go is whatever the user has already installed
|
||||||
|
# (using make.bash or similar). We can't make assumptions about what
|
||||||
|
# options it may have been built with, such as -trimpath or GOROOT_FINAL.
|
||||||
|
# Instead, we build a fresh copy of the binary with known settings.
|
||||||
|
go build -trimpath -o $WORK/new/bin/go$GOEXE cmd/go &
|
||||||
|
go build -trimpath -o $WORK/bin/check$GOEXE check.go &
|
||||||
|
wait
|
||||||
|
|
||||||
|
env TESTGOROOT=$GOROOT
|
||||||
|
env GOROOT=
|
||||||
|
|
||||||
|
# Relocated Executable
|
||||||
|
# Since we built with -trimpath and the binary isn't installed in a
|
||||||
|
# normal-looking GOROOT, this command should fail.
|
||||||
|
|
||||||
|
! exec $WORK/new/bin/go$GOEXE env GOROOT
|
||||||
|
stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT is not set$'
|
||||||
|
|
||||||
|
# Cross-compiled binaries in cmd are installed to a ${GOOS}_${GOARCH} subdirectory,
|
||||||
|
# so we also want to try a copy there.
|
||||||
|
# (Note that the script engine's 'exec' engine already works around
|
||||||
|
# https://go.dev/issue/22315, so we don't have to do that explicitly in the
|
||||||
|
# 'check' program we use later.)
|
||||||
|
cp $WORK/new/bin/go$GOEXE $WORK/new/bin/${GOOS}_${GOARCH}/go$GOEXE
|
||||||
|
! exec $WORK/new/bin/${GOOS}_${GOARCH}/go$GOEXE env GOROOT
|
||||||
|
stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT is not set$'
|
||||||
|
|
||||||
|
# Relocated Tree:
|
||||||
|
# If the binary is sitting in a bin dir next to ../pkg/tool, that counts as a GOROOT,
|
||||||
|
# so it should find the new tree.
|
||||||
|
mkdir $WORK/new/pkg/tool
|
||||||
|
exec $WORK/bin/check$GOEXE $WORK/new/bin/go$GOEXE $WORK/new
|
||||||
|
exec $WORK/bin/check$GOEXE $WORK/new/bin/${GOOS}_${GOARCH}/go$GOEXE $WORK/new
|
||||||
|
|
||||||
|
-- check.go --
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
exe := os.Args[1]
|
||||||
|
want := os.Args[2]
|
||||||
|
cmd := exec.Command(exe, "env", "GOROOT")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s env GOROOT: %v, %s\n", exe, err, out)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
goroot, err := filepath.EvalSymlinks(strings.TrimSpace(string(out)))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
want, err = filepath.EvalSymlinks(want)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if !strings.EqualFold(goroot, want) {
|
||||||
|
fmt.Fprintf(os.Stderr, "go env GOROOT:\nhave %s\nwant %s\n", goroot, want)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "go env GOROOT: %s\n", goroot)
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue