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

View as plain text