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

View as plain text