Home | History | Annotate | Download | only in cfg
      1 // Copyright 2017 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Package cfg holds configuration shared by multiple parts
      6 // of the go command.
      7 package cfg
      8 
      9 import (
     10 	"fmt"
     11 	"go/build"
     12 	"os"
     13 	"path/filepath"
     14 	"runtime"
     15 
     16 	"cmd/internal/objabi"
     17 )
     18 
     19 // These are general "build flags" used by build and other commands.
     20 var (
     21 	BuildA                 bool   // -a flag
     22 	BuildBuildmode         string // -buildmode flag
     23 	BuildContext           = build.Default
     24 	BuildI                 bool               // -i flag
     25 	BuildLinkshared        bool               // -linkshared flag
     26 	BuildMSan              bool               // -msan flag
     27 	BuildN                 bool               // -n flag
     28 	BuildO                 string             // -o flag
     29 	BuildP                 = runtime.NumCPU() // -p flag
     30 	BuildPkgdir            string             // -pkgdir flag
     31 	BuildRace              bool               // -race flag
     32 	BuildToolexec          []string           // -toolexec flag
     33 	BuildToolchainName     string
     34 	BuildToolchainCompiler func() string
     35 	BuildToolchainLinker   func() string
     36 	BuildV                 bool // -v flag
     37 	BuildWork              bool // -work flag
     38 	BuildX                 bool // -x flag
     39 
     40 	CmdName string // "build", "install", "list", etc.
     41 
     42 	DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
     43 )
     44 
     45 func init() {
     46 	BuildToolchainCompiler = func() string { return "missing-compiler" }
     47 	BuildToolchainLinker = func() string { return "missing-linker" }
     48 }
     49 
     50 // An EnvVar is an environment variable Name=Value.
     51 type EnvVar struct {
     52 	Name  string
     53 	Value string
     54 }
     55 
     56 // OrigEnv is the original environment of the program at startup.
     57 var OrigEnv []string
     58 
     59 // CmdEnv is the new environment for running go tool commands.
     60 // User binaries (during go test or go run) are run with OrigEnv,
     61 // not CmdEnv.
     62 var CmdEnv []EnvVar
     63 
     64 // Global build parameters (used during package load)
     65 var (
     66 	Goarch    = BuildContext.GOARCH
     67 	Goos      = BuildContext.GOOS
     68 	ExeSuffix string
     69 	Gopath    = filepath.SplitList(BuildContext.GOPATH)
     70 )
     71 
     72 func init() {
     73 	if Goos == "windows" {
     74 		ExeSuffix = ".exe"
     75 	}
     76 }
     77 
     78 var (
     79 	GOROOT       = findGOROOT()
     80 	GOBIN        = os.Getenv("GOBIN")
     81 	GOROOTbin    = filepath.Join(GOROOT, "bin")
     82 	GOROOTpkg    = filepath.Join(GOROOT, "pkg")
     83 	GOROOTsrc    = filepath.Join(GOROOT, "src")
     84 	GOROOT_FINAL = findGOROOT_FINAL()
     85 
     86 	// Used in envcmd.MkEnv and build ID computations.
     87 	GOARM  = fmt.Sprint(objabi.GOARM)
     88 	GO386  = objabi.GO386
     89 	GOMIPS = objabi.GOMIPS
     90 )
     91 
     92 // Update build context to use our computed GOROOT.
     93 func init() {
     94 	BuildContext.GOROOT = GOROOT
     95 	// Note that we must use runtime.GOOS and runtime.GOARCH here,
     96 	// as the tool directory does not move based on environment variables.
     97 	// This matches the initialization of ToolDir in go/build,
     98 	// except for using GOROOT rather than runtime.GOROOT().
     99 	build.ToolDir = filepath.Join(GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
    100 }
    101 
    102 func findGOROOT() string {
    103 	if env := os.Getenv("GOROOT"); env != "" {
    104 		return filepath.Clean(env)
    105 	}
    106 	def := filepath.Clean(runtime.GOROOT())
    107 	exe, err := os.Executable()
    108 	if err == nil {
    109 		exe, err = filepath.Abs(exe)
    110 		if err == nil {
    111 			if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
    112 				// If def (runtime.GOROOT()) and dir are the same
    113 				// directory, prefer the spelling used in def.
    114 				if isSameDir(def, dir) {
    115 					return def
    116 				}
    117 				return dir
    118 			}
    119 			exe, err = filepath.EvalSymlinks(exe)
    120 			if err == nil {
    121 				if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
    122 					if isSameDir(def, dir) {
    123 						return def
    124 					}
    125 					return dir
    126 				}
    127 			}
    128 		}
    129 	}
    130 	return def
    131 }
    132 
    133 func findGOROOT_FINAL() string {
    134 	def := GOROOT
    135 	if env := os.Getenv("GOROOT_FINAL"); env != "" {
    136 		def = filepath.Clean(env)
    137 	}
    138 	return def
    139 }
    140 
    141 // isSameDir reports whether dir1 and dir2 are the same directory.
    142 func isSameDir(dir1, dir2 string) bool {
    143 	if dir1 == dir2 {
    144 		return true
    145 	}
    146 	info1, err1 := os.Stat(dir1)
    147 	info2, err2 := os.Stat(dir2)
    148 	return err1 == nil && err2 == nil && os.SameFile(info1, info2)
    149 }
    150 
    151 // isGOROOT reports whether path looks like a GOROOT.
    152 //
    153 // It does this by looking for the path/pkg/tool directory,
    154 // which is necessary for useful operation of the cmd/go tool,
    155 // and is not typically present in a GOPATH.
    156 func isGOROOT(path string) bool {
    157 	stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
    158 	if err != nil {
    159 		return false
    160 	}
    161 	return stat.IsDir()
    162 }
    163