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

View as plain text