cmd/godoc: use same GOROOT discovery as Go 1.10's cmd/go
Fixes golang/go#23445
Change-Id: I4c707107e636558b49ea9a1a8690723b06dda235
Reviewed-on: https://go-review.googlesource.com/118075
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/cmd/godoc/goroot.go b/cmd/godoc/goroot.go
new file mode 100644
index 0000000..998e869
--- /dev/null
+++ b/cmd/godoc/goroot.go
@@ -0,0 +1,74 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "os"
+ "path/filepath"
+ "runtime"
+)
+
+// Copies of functions from src/cmd/go/internal/cfg/cfg.go for
+// finding the GOROOT.
+// Keep them in sync until support is moved to a common place, if ever.
+
+func findGOROOT() string {
+ if env := os.Getenv("GOROOT"); env != "" {
+ return filepath.Clean(env)
+ }
+ def := filepath.Clean(runtime.GOROOT())
+ if runtime.Compiler == "gccgo" {
+ // gccgo has no real GOROOT, and it certainly doesn't
+ // depend on the executable's location.
+ return def
+ }
+ exe, err := os.Executable()
+ if err == nil {
+ exe, err = filepath.Abs(exe)
+ if err == nil {
+ if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
+ // If def (runtime.GOROOT()) and dir are the same
+ // directory, prefer the spelling used in def.
+ if isSameDir(def, dir) {
+ return def
+ }
+ return dir
+ }
+ exe, err = filepath.EvalSymlinks(exe)
+ if err == nil {
+ if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
+ if isSameDir(def, dir) {
+ return def
+ }
+ return dir
+ }
+ }
+ }
+ }
+ return def
+}
+
+// isGOROOT reports whether path looks like a GOROOT.
+//
+// It does this by looking for the path/pkg/tool directory,
+// which is necessary for useful operation of the cmd/go tool,
+// and is not typically present in a GOPATH.
+func isGOROOT(path string) bool {
+ stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
+ if err != nil {
+ return false
+ }
+ return stat.IsDir()
+}
+
+// isSameDir reports whether dir1 and dir2 are the same directory.
+func isSameDir(dir1, dir2 string) bool {
+ if dir1 == dir2 {
+ return true
+ }
+ info1, err1 := os.Stat(dir1)
+ info2, err2 := os.Stat(dir2)
+ return err1 == nil && err2 == nil && os.SameFile(info1, info2)
+}
diff --git a/cmd/godoc/main.go b/cmd/godoc/main.go
index 9e70469..567ac89 100644
--- a/cmd/godoc/main.go
+++ b/cmd/godoc/main.go
@@ -82,7 +82,7 @@
// file system roots
// TODO(gri) consider the invariant that goroot always end in '/'
- goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
+ goroot = flag.String("goroot", findGOROOT(), "Go root directory")
// layout control
tabWidth = flag.Int("tabwidth", 4, "tab width")