Source file src/internal/buildcfg/cfg.go

     1  // Copyright 2021 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 buildcfg provides access to the build configuration
     6  // described by the current environment. It is for use by build tools
     7  // such as cmd/go or cmd/compile and for setting up go/build's Default context.
     8  //
     9  // Note that it does NOT provide access to the build configuration used to
    10  // build the currently-running binary. For that, use runtime.GOOS etc
    11  // as well as internal/goexperiment.
    12  package buildcfg
    13  
    14  import (
    15  	"fmt"
    16  	"os"
    17  	"path/filepath"
    18  	"strconv"
    19  	"strings"
    20  )
    21  
    22  var (
    23  	GOROOT    = os.Getenv("GOROOT") // cached for efficiency
    24  	GOARCH    = envOr("GOARCH", defaultGOARCH)
    25  	GOOS      = envOr("GOOS", defaultGOOS)
    26  	GO386     = envOr("GO386", DefaultGO386)
    27  	GOAMD64   = goamd64()
    28  	GOARM     = goarm()
    29  	GOARM64   = goarm64()
    30  	GOMIPS    = gomips()
    31  	GOMIPS64  = gomips64()
    32  	GOPPC64   = goppc64()
    33  	GORISCV64 = goriscv64()
    34  	GOWASM    = gowasm()
    35  	ToolTags  = toolTags()
    36  	GO_LDSO   = defaultGO_LDSO
    37  	GOFIPS140 = gofips140()
    38  	Version   = version
    39  )
    40  
    41  // Error is one of the errors found (if any) in the build configuration.
    42  var Error error
    43  
    44  // Check exits the program with a fatal error if Error is non-nil.
    45  func Check() {
    46  	if Error != nil {
    47  		fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), Error)
    48  		os.Exit(2)
    49  	}
    50  }
    51  
    52  func envOr(key, value string) string {
    53  	if x := os.Getenv(key); x != "" {
    54  		return x
    55  	}
    56  	return value
    57  }
    58  
    59  func goamd64() int {
    60  	switch v := envOr("GOAMD64", DefaultGOAMD64); v {
    61  	case "v1":
    62  		return 1
    63  	case "v2":
    64  		return 2
    65  	case "v3":
    66  		return 3
    67  	case "v4":
    68  		return 4
    69  	}
    70  	Error = fmt.Errorf("invalid GOAMD64: must be v1, v2, v3, v4")
    71  	return int(DefaultGOAMD64[len("v")] - '0')
    72  }
    73  
    74  func gofips140() string {
    75  	v := envOr("GOFIPS140", DefaultGOFIPS140)
    76  	switch v {
    77  	case "off", "latest", "inprocess", "certified":
    78  		return v
    79  	}
    80  	if isFIPSVersion(v) {
    81  		return v
    82  	}
    83  	Error = fmt.Errorf("invalid GOFIPS140: must be off, latest, inprocess, certified, or vX.Y.Z")
    84  	return DefaultGOFIPS140
    85  }
    86  
    87  // isFIPSVersion reports whether v is a valid FIPS version,
    88  // of the form vX.Y.Z or vX.Y.Z-hash.
    89  func isFIPSVersion(v string) bool {
    90  	if !strings.HasPrefix(v, "v") {
    91  		return false
    92  	}
    93  	v, ok := skipNum(v[len("v"):])
    94  	if !ok || !strings.HasPrefix(v, ".") {
    95  		return false
    96  	}
    97  	v, ok = skipNum(v[len("."):])
    98  	if !ok || !strings.HasPrefix(v, ".") {
    99  		return false
   100  	}
   101  	v, ok = skipNum(v[len("."):])
   102  	hasHash := strings.HasPrefix(v, "-") && len(v) == len("-")+8
   103  	return ok && (v == "" || hasHash)
   104  }
   105  
   106  // skipNum skips the leading text matching [0-9]+
   107  // in s, returning the rest and whether such text was found.
   108  func skipNum(s string) (rest string, ok bool) {
   109  	i := 0
   110  	for i < len(s) && '0' <= s[i] && s[i] <= '9' {
   111  		i++
   112  	}
   113  	return s[i:], i > 0
   114  }
   115  
   116  type GoarmFeatures struct {
   117  	Version   int
   118  	SoftFloat bool
   119  }
   120  
   121  func (g GoarmFeatures) String() string {
   122  	armStr := strconv.Itoa(g.Version)
   123  	if g.SoftFloat {
   124  		armStr += ",softfloat"
   125  	} else {
   126  		armStr += ",hardfloat"
   127  	}
   128  	return armStr
   129  }
   130  
   131  func goarm() (g GoarmFeatures) {
   132  	const (
   133  		softFloatOpt = ",softfloat"
   134  		hardFloatOpt = ",hardfloat"
   135  	)
   136  	def := DefaultGOARM
   137  	if GOOS == "android" && GOARCH == "arm" {
   138  		// Android arm devices always support GOARM=7.
   139  		def = "7"
   140  	}
   141  	v := envOr("GOARM", def)
   142  
   143  	floatSpecified := false
   144  	if strings.HasSuffix(v, softFloatOpt) {
   145  		g.SoftFloat = true
   146  		floatSpecified = true
   147  		v = v[:len(v)-len(softFloatOpt)]
   148  	}
   149  	if strings.HasSuffix(v, hardFloatOpt) {
   150  		floatSpecified = true
   151  		v = v[:len(v)-len(hardFloatOpt)]
   152  	}
   153  
   154  	switch v {
   155  	case "5":
   156  		g.Version = 5
   157  	case "6":
   158  		g.Version = 6
   159  	case "7":
   160  		g.Version = 7
   161  	default:
   162  		Error = fmt.Errorf("invalid GOARM: must start with 5, 6, or 7, and may optionally end in either %q or %q", hardFloatOpt, softFloatOpt)
   163  		g.Version = int(def[0] - '0')
   164  	}
   165  
   166  	// 5 defaults to softfloat. 6 and 7 default to hardfloat.
   167  	if !floatSpecified && g.Version == 5 {
   168  		g.SoftFloat = true
   169  	}
   170  	return
   171  }
   172  
   173  type Goarm64Features struct {
   174  	Version string
   175  	// Large Systems Extension
   176  	LSE bool
   177  	// ARM v8.0 Cryptographic Extension. It includes the following features:
   178  	// * FEAT_AES, which includes the AESD and AESE instructions.
   179  	// * FEAT_PMULL, which includes the PMULL, PMULL2 instructions.
   180  	// * FEAT_SHA1, which includes the SHA1* instructions.
   181  	// * FEAT_SHA256, which includes the SHA256* instructions.
   182  	Crypto bool
   183  }
   184  
   185  func (g Goarm64Features) String() string {
   186  	arm64Str := g.Version
   187  	if g.LSE {
   188  		arm64Str += ",lse"
   189  	}
   190  	if g.Crypto {
   191  		arm64Str += ",crypto"
   192  	}
   193  	return arm64Str
   194  }
   195  
   196  func ParseGoarm64(v string) (g Goarm64Features, e error) {
   197  	const (
   198  		lseOpt    = ",lse"
   199  		cryptoOpt = ",crypto"
   200  	)
   201  
   202  	g.LSE = false
   203  	g.Crypto = false
   204  	// We allow any combination of suffixes, in any order
   205  	for {
   206  		if strings.HasSuffix(v, lseOpt) {
   207  			g.LSE = true
   208  			v = v[:len(v)-len(lseOpt)]
   209  			continue
   210  		}
   211  
   212  		if strings.HasSuffix(v, cryptoOpt) {
   213  			g.Crypto = true
   214  			v = v[:len(v)-len(cryptoOpt)]
   215  			continue
   216  		}
   217  
   218  		break
   219  	}
   220  
   221  	switch v {
   222  	case "v8.0":
   223  		g.Version = v
   224  	case "v8.1", "v8.2", "v8.3", "v8.4", "v8.5", "v8.6", "v8.7", "v8.8", "v8.9",
   225  		"v9.0", "v9.1", "v9.2", "v9.3", "v9.4", "v9.5":
   226  		g.Version = v
   227  		// LSE extension is mandatory starting from 8.1
   228  		g.LSE = true
   229  	default:
   230  		e = fmt.Errorf("invalid GOARM64: must start with v8.{0-9} or v9.{0-5} and may optionally end in %q and/or %q",
   231  			lseOpt, cryptoOpt)
   232  		g.Version = DefaultGOARM64
   233  	}
   234  
   235  	return
   236  }
   237  
   238  func goarm64() (g Goarm64Features) {
   239  	g, Error = ParseGoarm64(envOr("GOARM64", DefaultGOARM64))
   240  	return
   241  }
   242  
   243  // Returns true if g supports giving ARM64 ISA
   244  // Note that this function doesn't accept / test suffixes (like ",lse" or ",crypto")
   245  func (g Goarm64Features) Supports(s string) bool {
   246  	// We only accept "v{8-9}.{0-9}. Everything else is malformed.
   247  	if len(s) != 4 {
   248  		return false
   249  	}
   250  
   251  	major := s[1]
   252  	minor := s[3]
   253  
   254  	// We only accept "v{8-9}.{0-9}. Everything else is malformed.
   255  	if major < '8' || major > '9' ||
   256  		minor < '0' || minor > '9' ||
   257  		s[0] != 'v' || s[2] != '.' {
   258  		return false
   259  	}
   260  
   261  	g_major := g.Version[1]
   262  	g_minor := g.Version[3]
   263  
   264  	if major == g_major {
   265  		return minor <= g_minor
   266  	} else if g_major == '9' {
   267  		// v9.0 diverged from v8.5. This means we should compare with g_minor increased by five.
   268  		return minor <= g_minor+5
   269  	} else {
   270  		return false
   271  	}
   272  }
   273  
   274  func gomips() string {
   275  	switch v := envOr("GOMIPS", DefaultGOMIPS); v {
   276  	case "hardfloat", "softfloat":
   277  		return v
   278  	}
   279  	Error = fmt.Errorf("invalid GOMIPS: must be hardfloat, softfloat")
   280  	return DefaultGOMIPS
   281  }
   282  
   283  func gomips64() string {
   284  	switch v := envOr("GOMIPS64", DefaultGOMIPS64); v {
   285  	case "hardfloat", "softfloat":
   286  		return v
   287  	}
   288  	Error = fmt.Errorf("invalid GOMIPS64: must be hardfloat, softfloat")
   289  	return DefaultGOMIPS64
   290  }
   291  
   292  func goppc64() int {
   293  	switch v := envOr("GOPPC64", DefaultGOPPC64); v {
   294  	case "power8":
   295  		return 8
   296  	case "power9":
   297  		return 9
   298  	case "power10":
   299  		return 10
   300  	}
   301  	Error = fmt.Errorf("invalid GOPPC64: must be power8, power9, power10")
   302  	return int(DefaultGOPPC64[len("power")] - '0')
   303  }
   304  
   305  func goriscv64() int {
   306  	switch v := envOr("GORISCV64", DefaultGORISCV64); v {
   307  	case "rva20u64":
   308  		return 20
   309  	case "rva22u64":
   310  		return 22
   311  	case "rva23u64":
   312  		return 23
   313  	}
   314  	Error = fmt.Errorf("invalid GORISCV64: must be rva20u64, rva22u64, rva23u64")
   315  	v := DefaultGORISCV64[len("rva"):]
   316  	i := strings.IndexFunc(v, func(r rune) bool {
   317  		return r < '0' || r > '9'
   318  	})
   319  	year, _ := strconv.Atoi(v[:i])
   320  	return year
   321  }
   322  
   323  type gowasmFeatures struct {
   324  	// Legacy features, now always enabled
   325  	//SatConv bool
   326  	//SignExt bool
   327  }
   328  
   329  func (f gowasmFeatures) String() string {
   330  	var flags []string
   331  	return strings.Join(flags, ",")
   332  }
   333  
   334  func gowasm() (f gowasmFeatures) {
   335  	for opt := range strings.SplitSeq(envOr("GOWASM", ""), ",") {
   336  		switch opt {
   337  		case "satconv":
   338  			// ignore, always enabled
   339  		case "signext":
   340  			// ignore, always enabled
   341  		case "":
   342  			// ignore
   343  		default:
   344  			Error = fmt.Errorf("invalid GOWASM: no such feature %q", opt)
   345  		}
   346  	}
   347  	return
   348  }
   349  
   350  func Getgoextlinkenabled() string {
   351  	return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
   352  }
   353  
   354  func toolTags() []string {
   355  	tags := experimentTags()
   356  	tags = append(tags, gogoarchTags()...)
   357  	return tags
   358  }
   359  
   360  func experimentTags() []string {
   361  	var list []string
   362  	// For each experiment that has been enabled in the toolchain, define a
   363  	// build tag with the same name but prefixed by "goexperiment." which can be
   364  	// used for compiling alternative files for the experiment. This allows
   365  	// changes for the experiment, like extra struct fields in the runtime,
   366  	// without affecting the base non-experiment code at all.
   367  	for _, exp := range Experiment.Enabled() {
   368  		list = append(list, "goexperiment."+exp)
   369  	}
   370  	return list
   371  }
   372  
   373  // GOGOARCH returns the name and value of the GO$GOARCH setting.
   374  // For example, if GOARCH is "amd64" it might return "GOAMD64", "v2".
   375  func GOGOARCH() (name, value string) {
   376  	switch GOARCH {
   377  	case "386":
   378  		return "GO386", GO386
   379  	case "amd64":
   380  		return "GOAMD64", fmt.Sprintf("v%d", GOAMD64)
   381  	case "arm":
   382  		return "GOARM", GOARM.String()
   383  	case "arm64":
   384  		return "GOARM64", GOARM64.String()
   385  	case "mips", "mipsle":
   386  		return "GOMIPS", GOMIPS
   387  	case "mips64", "mips64le":
   388  		return "GOMIPS64", GOMIPS64
   389  	case "ppc64", "ppc64le":
   390  		return "GOPPC64", fmt.Sprintf("power%d", GOPPC64)
   391  	case "riscv64":
   392  		return "GORISCV64", fmt.Sprintf("rva%du64", GORISCV64)
   393  	case "wasm":
   394  		return "GOWASM", GOWASM.String()
   395  	}
   396  	return "", ""
   397  }
   398  
   399  func gogoarchTags() []string {
   400  	switch GOARCH {
   401  	case "386":
   402  		return []string{GOARCH + "." + GO386}
   403  	case "amd64":
   404  		var list []string
   405  		for i := 1; i <= GOAMD64; i++ {
   406  			list = append(list, fmt.Sprintf("%s.v%d", GOARCH, i))
   407  		}
   408  		return list
   409  	case "arm":
   410  		var list []string
   411  		for i := 5; i <= GOARM.Version; i++ {
   412  			list = append(list, fmt.Sprintf("%s.%d", GOARCH, i))
   413  		}
   414  		return list
   415  	case "arm64":
   416  		var list []string
   417  		major := int(GOARM64.Version[1] - '0')
   418  		minor := int(GOARM64.Version[3] - '0')
   419  		for i := 0; i <= minor; i++ {
   420  			list = append(list, fmt.Sprintf("%s.v%d.%d", GOARCH, major, i))
   421  		}
   422  		// ARM64 v9.x also includes support of v8.x+5 (i.e. v9.1 includes v8.(1+5) = v8.6).
   423  		if major == 9 {
   424  			for i := 0; i <= minor+5 && i <= 9; i++ {
   425  				list = append(list, fmt.Sprintf("%s.v%d.%d", GOARCH, 8, i))
   426  			}
   427  		}
   428  		return list
   429  	case "mips", "mipsle":
   430  		return []string{GOARCH + "." + GOMIPS}
   431  	case "mips64", "mips64le":
   432  		return []string{GOARCH + "." + GOMIPS64}
   433  	case "ppc64", "ppc64le":
   434  		var list []string
   435  		for i := 8; i <= GOPPC64; i++ {
   436  			list = append(list, fmt.Sprintf("%s.power%d", GOARCH, i))
   437  		}
   438  		return list
   439  	case "riscv64":
   440  		list := []string{GOARCH + "." + "rva20u64"}
   441  		if GORISCV64 >= 22 {
   442  			list = append(list, GOARCH+"."+"rva22u64")
   443  		}
   444  		if GORISCV64 >= 23 {
   445  			list = append(list, GOARCH+"."+"rva23u64")
   446  		}
   447  		return list
   448  	case "wasm":
   449  		var list []string
   450  		// SatConv is always enabled
   451  		list = append(list, GOARCH+".satconv")
   452  		// SignExt is always enabled
   453  		list = append(list, GOARCH+".signext")
   454  		return list
   455  	}
   456  	return nil
   457  }
   458  

View as plain text