From 8c39fb57ad842377bfea6ab5eff7608e4e8f6765 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 11 Jun 2018 23:34:51 +0000 Subject: [PATCH] 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 TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- cmd/godoc/goroot.go | 74 +++++++++++++++++++++++++++++++++++++++++++++ cmd/godoc/main.go | 2 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 cmd/godoc/goroot.go diff --git a/cmd/godoc/goroot.go b/cmd/godoc/goroot.go new file mode 100644 index 000000000..998e86936 --- /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 9e7046920..567ac89db 100644 --- a/cmd/godoc/main.go +++ b/cmd/godoc/main.go @@ -82,7 +82,7 @@ var ( // 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")