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

View as plain text