Skip to content

Commit 6385a6f

Browse files
Bryan C. Millsgopherbot
Bryan C. Mills
authored andcommitted
[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 9e9556d) Reviewed-on: https://go-review.googlesource.com/c/go/+/521695 Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
1 parent 2d07bb8 commit 6385a6f

File tree

3 files changed

+125
-12
lines changed

3 files changed

+125
-12
lines changed

src/cmd/go/internal/cfg/cfg.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -490,25 +490,43 @@ func findGOROOT(env string) string {
490490
// depend on the executable's location.
491491
return def
492492
}
493+
494+
// canonical returns a directory path that represents
495+
// the same directory as dir,
496+
// preferring the spelling in def if the two are the same.
497+
canonical := func(dir string) string {
498+
if isSameDir(def, dir) {
499+
return def
500+
}
501+
return dir
502+
}
503+
493504
exe, err := os.Executable()
494505
if err == nil {
495506
exe, err = filepath.Abs(exe)
496507
if err == nil {
508+
// cmd/go may be installed in GOROOT/bin or GOROOT/bin/GOOS_GOARCH,
509+
// depending on whether it was cross-compiled with a different
510+
// GOHOSTOS (see https://go.dev/issue/62119). Try both.
497511
if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
498-
// If def (runtime.GOROOT()) and dir are the same
499-
// directory, prefer the spelling used in def.
500-
if isSameDir(def, dir) {
501-
return def
502-
}
503-
return dir
512+
return canonical(dir)
513+
}
514+
if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
515+
return canonical(dir)
504516
}
517+
518+
// Depending on what was passed on the command line, it is possible
519+
// that os.Executable is a symlink (like /usr/local/bin/go) referring
520+
// to a binary installed in a real GOROOT elsewhere
521+
// (like /usr/lib/go/bin/go).
522+
// Try to find that GOROOT by resolving the symlinks.
505523
exe, err = filepath.EvalSymlinks(exe)
506524
if err == nil {
507525
if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
508-
if isSameDir(def, dir) {
509-
return def
510-
}
511-
return dir
526+
return canonical(dir)
527+
}
528+
if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
529+
return canonical(dir)
512530
}
513531
}
514532
}

src/cmd/go/testdata/script/goroot_executable.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[compiler:gccgo] skip
2+
[short] skip 'builds and links another cmd/go'
23

34
mkdir $WORK/new/bin
45

@@ -9,15 +10,18 @@ mkdir $WORK/new/bin
910
# new cmd/go is built.
1011
env GOROOT_FINAL=
1112

13+
# $GOROOT/bin/go is whatever the user has already installed
14+
# (using make.bash or similar). We can't make assumptions about what
15+
# options it may have been built with, such as -trimpath or GOROOT_FINAL.
16+
# Instead, we build a fresh copy of the binary with known settings.
1217
go build -o $WORK/new/bin/go$GOEXE cmd/go &
13-
go build -o $WORK/bin/check$GOEXE check.go &
18+
go build -trimpath -o $WORK/bin/check$GOEXE check.go &
1419
wait
1520

1621
env TESTGOROOT=$GOROOT
1722
env GOROOT=
1823

1924
# Relocated Executable
20-
# cp $TESTGOROOT/bin/go$GOEXE $WORK/new/bin/go$GOEXE
2125
exec $WORK/bin/check$GOEXE $WORK/new/bin/go$GOEXE $TESTGOROOT
2226

2327
# Relocated Tree:
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Regression test for https://go.dev/issue/62119:
2+
# A 'go' command cross-compiled with a different GOHOSTOS
3+
# should be able to locate its GOROOT using os.Executable.
4+
#
5+
# (This also tests a 'go' command built with -trimpath
6+
# that is not cross-compiled, since we need to build that
7+
# configuration for the test anyway.)
8+
9+
[short] skip 'builds and links another cmd/go'
10+
11+
mkdir $WORK/new/bin
12+
mkdir $WORK/new/bin/${GOOS}_${GOARCH}
13+
14+
# In this test, we are specifically checking the logic for deriving
15+
# the value of GOROOT from os.Executable when runtime.GOROOT is
16+
# trimmed away.
17+
# GOROOT_FINAL changes the default behavior of runtime.GOROOT,
18+
# so we explicitly clear it to remove it as a confounding variable.
19+
env GOROOT_FINAL=
20+
21+
# $GOROOT/bin/go is whatever the user has already installed
22+
# (using make.bash or similar). We can't make assumptions about what
23+
# options it may have been built with, such as -trimpath or GOROOT_FINAL.
24+
# Instead, we build a fresh copy of the binary with known settings.
25+
go build -trimpath -o $WORK/new/bin/go$GOEXE cmd/go &
26+
go build -trimpath -o $WORK/bin/check$GOEXE check.go &
27+
wait
28+
29+
env TESTGOROOT=$GOROOT
30+
env GOROOT=
31+
32+
# Relocated Executable
33+
# Since we built with -trimpath and the binary isn't installed in a
34+
# normal-looking GOROOT, this command should fail.
35+
36+
! exec $WORK/new/bin/go$GOEXE env GOROOT
37+
stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT is not set$'
38+
39+
# Cross-compiled binaries in cmd are installed to a ${GOOS}_${GOARCH} subdirectory,
40+
# so we also want to try a copy there.
41+
# (Note that the script engine's 'exec' engine already works around
42+
# https://go.dev/issue/22315, so we don't have to do that explicitly in the
43+
# 'check' program we use later.)
44+
cp $WORK/new/bin/go$GOEXE $WORK/new/bin/${GOOS}_${GOARCH}/go$GOEXE
45+
! exec $WORK/new/bin/${GOOS}_${GOARCH}/go$GOEXE env GOROOT
46+
stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT is not set$'
47+
48+
# Relocated Tree:
49+
# If the binary is sitting in a bin dir next to ../pkg/tool, that counts as a GOROOT,
50+
# so it should find the new tree.
51+
mkdir $WORK/new/pkg/tool
52+
exec $WORK/bin/check$GOEXE $WORK/new/bin/go$GOEXE $WORK/new
53+
exec $WORK/bin/check$GOEXE $WORK/new/bin/${GOOS}_${GOARCH}/go$GOEXE $WORK/new
54+
55+
-- check.go --
56+
package main
57+
58+
import (
59+
"fmt"
60+
"os"
61+
"os/exec"
62+
"path/filepath"
63+
"strings"
64+
)
65+
66+
func main() {
67+
exe := os.Args[1]
68+
want := os.Args[2]
69+
cmd := exec.Command(exe, "env", "GOROOT")
70+
out, err := cmd.CombinedOutput()
71+
if err != nil {
72+
fmt.Fprintf(os.Stderr, "%s env GOROOT: %v, %s\n", exe, err, out)
73+
os.Exit(1)
74+
}
75+
goroot, err := filepath.EvalSymlinks(strings.TrimSpace(string(out)))
76+
if err != nil {
77+
fmt.Fprintln(os.Stderr, err)
78+
os.Exit(1)
79+
}
80+
want, err = filepath.EvalSymlinks(want)
81+
if err != nil {
82+
fmt.Fprintln(os.Stderr, err)
83+
os.Exit(1)
84+
}
85+
if !strings.EqualFold(goroot, want) {
86+
fmt.Fprintf(os.Stderr, "go env GOROOT:\nhave %s\nwant %s\n", goroot, want)
87+
os.Exit(1)
88+
}
89+
fmt.Fprintf(os.Stderr, "go env GOROOT: %s\n", goroot)
90+
91+
}

0 commit comments

Comments
 (0)