blob: b66e54f67a000e54f455bb1f4217b69228e350a4 [file] [log] [blame]
// Copyright 2017 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 cfg holds configuration shared by multiple parts
// of the go command.
package cfg
import (
"bytes"
"fmt"
"go/build"
"io/ioutil"
"log"
"os"
"path/filepath"
"runtime"
)
// These are general "build flags" used by build and other commands.
var (
BuildA bool // -a flag
BuildBuildmode string // -buildmode flag
BuildContext = build.Default
BuildGetmode string // -getmode flag
BuildI bool // -i flag
BuildLinkshared bool // -linkshared flag
BuildMSan bool // -msan flag
BuildN bool // -n flag
BuildO string // -o flag
BuildP = runtime.NumCPU() // -p flag
BuildPkgdir string // -pkgdir flag
BuildRace bool // -race flag
BuildToolexec []string // -toolexec flag
BuildToolchainName string
BuildToolchainCompiler func() string
BuildToolchainLinker func() string
BuildV bool // -v flag
BuildWork bool // -work flag
BuildX bool // -x flag
CmdName string // "build", "install", "list", etc.
DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
)
func init() {
BuildToolchainCompiler = func() string { return "missing-compiler" }
BuildToolchainLinker = func() string { return "missing-linker" }
}
// An EnvVar is an environment variable Name=Value.
type EnvVar struct {
Name string
Value string
}
// OrigEnv is the original environment of the program at startup.
var OrigEnv []string
// CmdEnv is the new environment for running go tool commands.
// User binaries (during go test or go run) are run with OrigEnv,
// not CmdEnv.
var CmdEnv []EnvVar
// Global build parameters (used during package load)
var (
Goarch = BuildContext.GOARCH
Goos = BuildContext.GOOS
ExeSuffix string
Gopath = filepath.SplitList(BuildContext.GOPATH)
// ModulesEnabled specifies whether the go command is running
// in module-aware mode (as opposed to GOPATH mode).
// It is equal to modload.Enabled, but not all packages can import modload.
ModulesEnabled bool
// GoModInGOPATH records whether we've found a go.mod in GOPATH/src
// in GO111MODULE=auto mode. In that case, we don't use modules
// but people might expect us to, so 'go get' warns.
GoModInGOPATH string
)
func init() {
if Goos == "windows" {
ExeSuffix = ".exe"
}
}
var (
GOROOT = findGOROOT()
GOBIN = os.Getenv("GOBIN")
GOROOTbin = filepath.Join(GOROOT, "bin")
GOROOTpkg = filepath.Join(GOROOT, "pkg")
GOROOTsrc = filepath.Join(GOROOT, "src")
GOROOT_FINAL = findGOROOT_FINAL()
// Used in envcmd.MkEnv and build ID computations.
GOARM, GO386, GOMIPS, GOMIPS64 = objabi()
// C and C++ compilers
CC, CXX = compilers()
)
// Update build context to use our computed GOROOT.
func init() {
BuildContext.GOROOT = GOROOT
if runtime.Compiler != "gccgo" {
// Note that we must use runtime.GOOS and runtime.GOARCH here,
// as the tool directory does not move based on environment
// variables. This matches the initialization of ToolDir in
// go/build, except for using GOROOT rather than
// runtime.GOROOT.
build.ToolDir = filepath.Join(GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
}
}
func objabi() (GOARM, GO386, GOMIPS, GOMIPS64 string) {
data, err := ioutil.ReadFile(filepath.Join(GOROOT, "src/cmd/internal/objabi/zbootstrap.go"))
if err != nil {
fmt.Fprintf(os.Stderr, "go objabi: %v\n", err)
}
find := func(key string) string {
if env := os.Getenv(key); env != "" {
return env
}
i := bytes.Index(data, []byte("default"+key+" = `"))
if i < 0 {
if key == "GOMIPS64" { // new in Go 1.11
return ""
}
fmt.Fprintf(os.Stderr, "go objabi: cannot find %s\n", key)
os.Exit(2)
}
line := data[i:]
line = line[bytes.IndexByte(line, '`')+1:]
return string(line[:bytes.IndexByte(line, '`')])
}
return find("GOARM"), find("GO386"), find("GOMIPS"), find("GOMIPS64")
}
func compilers() (CC, CXX string) {
data, err := ioutil.ReadFile(filepath.Join(GOROOT, "src/cmd/go/internal/cfg/zdefaultcc.go"))
if err != nil {
fmt.Fprintf(os.Stderr, "go compilers: %v\n", err)
}
find := func(key string) string {
if env := os.Getenv(key); env != "" {
return env
}
fi := bytes.Index(data, []byte("Default"+key+"(goos, goarch string)"))
if fi < 0 {
fmt.Fprintf(os.Stderr, "go compilers: cannot find %s\n", key)
os.Exit(2)
}
i := bytes.Index(data[fi:], []byte("\treturn "))
if i < 0 {
fmt.Fprintf(os.Stderr, "go compilers: cannot find %s\n", key)
os.Exit(2)
}
line := data[fi+i:]
line = line[bytes.IndexByte(line, '"')+1:]
return string(line[:bytes.IndexByte(line, '"')])
}
return find("CC"), find("CXX")
}
func findGOROOT() string {
goroot := findGOROOT1()
_, err := os.Stat(filepath.Join(goroot, "api/go1.10.txt"))
if err != nil {
log.SetFlags(0)
log.Fatalf("go requires Go 1.10 but VGOROOT=%s is not a Go 1.10 source tree", goroot)
}
return goroot
}
func findGOROOT1() string {
if env := os.Getenv("VGOROOT"); env != "" {
return filepath.Clean(env)
}
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
}
func findGOROOT_FINAL() string {
def := GOROOT
if env := os.Getenv("GOROOT_FINAL"); env != "" {
def = filepath.Clean(env)
}
return def
}
// 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)
}
// 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.
//
// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
func isGOROOT(path string) bool {
stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
if err != nil {
return false
}
return stat.IsDir()
}