| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package buildcfg provides access to the build configuration |
| // described by the current environment. It is for use by build tools |
| // such as cmd/go or cmd/compile and for setting up go/build's Default context. |
| // |
| // Note that it does NOT provide access to the build configuration used to |
| // build the currently-running binary. For that, use runtime.GOOS etc |
| // as well as internal/goexperiment. |
| package buildcfg |
| |
| import ( |
| "fmt" |
| "os" |
| "path/filepath" |
| "strconv" |
| "strings" |
| ) |
| |
| var ( |
| GOROOT = os.Getenv("GOROOT") // cached for efficiency |
| GOARCH = envOr("GOARCH", defaultGOARCH) |
| GOOS = envOr("GOOS", defaultGOOS) |
| GO386 = envOr("GO386", defaultGO386) |
| GOAMD64 = goamd64() |
| GOARM = goarm() |
| GOARM64 = goarm64() |
| GOMIPS = gomips() |
| GOMIPS64 = gomips64() |
| GOPPC64 = goppc64() |
| GORISCV64 = goriscv64() |
| GOWASM = gowasm() |
| ToolTags = toolTags() |
| GO_LDSO = defaultGO_LDSO |
| Version = version |
| ) |
| |
| // Error is one of the errors found (if any) in the build configuration. |
| var Error error |
| |
| // Check exits the program with a fatal error if Error is non-nil. |
| func Check() { |
| if Error != nil { |
| fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), Error) |
| os.Exit(2) |
| } |
| } |
| |
| func envOr(key, value string) string { |
| if x := os.Getenv(key); x != "" { |
| return x |
| } |
| return value |
| } |
| |
| func goamd64() int { |
| switch v := envOr("GOAMD64", defaultGOAMD64); v { |
| case "v1": |
| return 1 |
| case "v2": |
| return 2 |
| case "v3": |
| return 3 |
| case "v4": |
| return 4 |
| } |
| Error = fmt.Errorf("invalid GOAMD64: must be v1, v2, v3, v4") |
| return int(defaultGOAMD64[len("v")] - '0') |
| } |
| |
| type goarmFeatures struct { |
| Version int |
| SoftFloat bool |
| } |
| |
| func (g goarmFeatures) String() string { |
| armStr := strconv.Itoa(g.Version) |
| if g.SoftFloat { |
| armStr += ",softfloat" |
| } else { |
| armStr += ",hardfloat" |
| } |
| return armStr |
| } |
| |
| func goarm() (g goarmFeatures) { |
| const ( |
| softFloatOpt = ",softfloat" |
| hardFloatOpt = ",hardfloat" |
| ) |
| def := defaultGOARM |
| if GOOS == "android" && GOARCH == "arm" { |
| // Android arm devices always support GOARM=7. |
| def = "7" |
| } |
| v := envOr("GOARM", def) |
| |
| floatSpecified := false |
| if strings.HasSuffix(v, softFloatOpt) { |
| g.SoftFloat = true |
| floatSpecified = true |
| v = v[:len(v)-len(softFloatOpt)] |
| } |
| if strings.HasSuffix(v, hardFloatOpt) { |
| floatSpecified = true |
| v = v[:len(v)-len(hardFloatOpt)] |
| } |
| |
| switch v { |
| case "5": |
| g.Version = 5 |
| case "6": |
| g.Version = 6 |
| case "7": |
| g.Version = 7 |
| default: |
| Error = fmt.Errorf("invalid GOARM: must start with 5, 6, or 7, and may optionally end in either %q or %q", hardFloatOpt, softFloatOpt) |
| g.Version = int(def[0] - '0') |
| } |
| |
| // 5 defaults to softfloat. 6 and 7 default to hardfloat. |
| if !floatSpecified && g.Version == 5 { |
| g.SoftFloat = true |
| } |
| return |
| } |
| |
| type Goarm64Features struct { |
| Version string |
| // Large Systems Extension |
| LSE bool |
| // ARM v8.0 Cryptographic Extension. It includes the following features: |
| // * FEAT_AES, which includes the AESD and AESE instructions. |
| // * FEAT_PMULL, which includes the PMULL, PMULL2 instructions. |
| // * FEAT_SHA1, which includes the SHA1* instructions. |
| // * FEAT_SHA256, which includes the SHA256* instructions. |
| Crypto bool |
| } |
| |
| func (g Goarm64Features) String() string { |
| arm64Str := g.Version |
| if g.LSE { |
| arm64Str += ",lse" |
| } |
| if g.Crypto { |
| arm64Str += ",crypto" |
| } |
| return arm64Str |
| } |
| |
| func ParseGoarm64(v string) (g Goarm64Features, e error) { |
| const ( |
| lseOpt = ",lse" |
| cryptoOpt = ",crypto" |
| ) |
| |
| g.LSE = false |
| g.Crypto = false |
| // We allow any combination of suffixes, in any order |
| for { |
| if strings.HasSuffix(v, lseOpt) { |
| g.LSE = true |
| v = v[:len(v)-len(lseOpt)] |
| continue |
| } |
| |
| if strings.HasSuffix(v, cryptoOpt) { |
| g.Crypto = true |
| v = v[:len(v)-len(cryptoOpt)] |
| continue |
| } |
| |
| break |
| } |
| |
| switch v { |
| case "v8.0": |
| g.Version = v |
| case "v8.1", "v8.2", "v8.3", "v8.4", "v8.5", "v8.6", "v8.7", "v8.8", "v8.9", |
| "v9.0", "v9.1", "v9.2", "v9.3", "v9.4", "v9.5": |
| g.Version = v |
| // LSE extension is mandatory starting from 8.1 |
| g.LSE = true |
| default: |
| e = fmt.Errorf("invalid GOARM64: must start with v8.{0-9} or v9.{0-5} and may optionally end in %q and/or %q", |
| lseOpt, cryptoOpt) |
| g.Version = defaultGOARM64 |
| } |
| |
| return |
| } |
| |
| func goarm64() (g Goarm64Features) { |
| g, Error = ParseGoarm64(envOr("GOARM64", defaultGOARM64)) |
| return |
| } |
| |
| // Returns true if g supports giving ARM64 ISA |
| // Note that this function doesn't accept / test suffixes (like ",lse" or ",crypto") |
| func (g Goarm64Features) Supports(s string) bool { |
| // We only accept "v{8-9}.{0-9}. Everything else is malformed. |
| if len(s) != 4 { |
| return false |
| } |
| |
| major := s[1] |
| minor := s[3] |
| |
| // We only accept "v{8-9}.{0-9}. Everything else is malformed. |
| if major < '8' || major > '9' || |
| minor < '0' || minor > '9' || |
| s[0] != 'v' || s[2] != '.' { |
| return false |
| } |
| |
| g_major := g.Version[1] |
| g_minor := g.Version[3] |
| |
| if major == g_major { |
| return minor <= g_minor |
| } else if g_major == '9' { |
| // v9.0 diverged from v8.5. This means we should compare with g_minor increased by five. |
| return minor <= g_minor+5 |
| } else { |
| return false |
| } |
| } |
| |
| func gomips() string { |
| switch v := envOr("GOMIPS", defaultGOMIPS); v { |
| case "hardfloat", "softfloat": |
| return v |
| } |
| Error = fmt.Errorf("invalid GOMIPS: must be hardfloat, softfloat") |
| return defaultGOMIPS |
| } |
| |
| func gomips64() string { |
| switch v := envOr("GOMIPS64", defaultGOMIPS64); v { |
| case "hardfloat", "softfloat": |
| return v |
| } |
| Error = fmt.Errorf("invalid GOMIPS64: must be hardfloat, softfloat") |
| return defaultGOMIPS64 |
| } |
| |
| func goppc64() int { |
| switch v := envOr("GOPPC64", defaultGOPPC64); v { |
| case "power8": |
| return 8 |
| case "power9": |
| return 9 |
| case "power10": |
| return 10 |
| } |
| Error = fmt.Errorf("invalid GOPPC64: must be power8, power9, power10") |
| return int(defaultGOPPC64[len("power")] - '0') |
| } |
| |
| func goriscv64() int { |
| switch v := envOr("GORISCV64", defaultGORISCV64); v { |
| case "rva20u64": |
| return 20 |
| case "rva22u64": |
| return 22 |
| } |
| Error = fmt.Errorf("invalid GORISCV64: must be rva20u64, rva22u64") |
| v := defaultGORISCV64[len("rva"):] |
| i := strings.IndexFunc(v, func(r rune) bool { |
| return r < '0' || r > '9' |
| }) |
| year, _ := strconv.Atoi(v[:i]) |
| return year |
| } |
| |
| type gowasmFeatures struct { |
| SatConv bool |
| SignExt bool |
| } |
| |
| func (f gowasmFeatures) String() string { |
| var flags []string |
| if f.SatConv { |
| flags = append(flags, "satconv") |
| } |
| if f.SignExt { |
| flags = append(flags, "signext") |
| } |
| return strings.Join(flags, ",") |
| } |
| |
| func gowasm() (f gowasmFeatures) { |
| for _, opt := range strings.Split(envOr("GOWASM", ""), ",") { |
| switch opt { |
| case "satconv": |
| f.SatConv = true |
| case "signext": |
| f.SignExt = true |
| case "": |
| // ignore |
| default: |
| Error = fmt.Errorf("invalid GOWASM: no such feature %q", opt) |
| } |
| } |
| return |
| } |
| |
| func Getgoextlinkenabled() string { |
| return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED) |
| } |
| |
| func toolTags() []string { |
| tags := experimentTags() |
| tags = append(tags, gogoarchTags()...) |
| return tags |
| } |
| |
| func experimentTags() []string { |
| var list []string |
| // For each experiment that has been enabled in the toolchain, define a |
| // build tag with the same name but prefixed by "goexperiment." which can be |
| // used for compiling alternative files for the experiment. This allows |
| // changes for the experiment, like extra struct fields in the runtime, |
| // without affecting the base non-experiment code at all. |
| for _, exp := range Experiment.Enabled() { |
| list = append(list, "goexperiment."+exp) |
| } |
| return list |
| } |
| |
| // GOGOARCH returns the name and value of the GO$GOARCH setting. |
| // For example, if GOARCH is "amd64" it might return "GOAMD64", "v2". |
| func GOGOARCH() (name, value string) { |
| switch GOARCH { |
| case "386": |
| return "GO386", GO386 |
| case "amd64": |
| return "GOAMD64", fmt.Sprintf("v%d", GOAMD64) |
| case "arm": |
| return "GOARM", GOARM.String() |
| case "arm64": |
| return "GOARM64", GOARM64.String() |
| case "mips", "mipsle": |
| return "GOMIPS", GOMIPS |
| case "mips64", "mips64le": |
| return "GOMIPS64", GOMIPS64 |
| case "ppc64", "ppc64le": |
| return "GOPPC64", fmt.Sprintf("power%d", GOPPC64) |
| case "wasm": |
| return "GOWASM", GOWASM.String() |
| } |
| return "", "" |
| } |
| |
| func gogoarchTags() []string { |
| switch GOARCH { |
| case "386": |
| return []string{GOARCH + "." + GO386} |
| case "amd64": |
| var list []string |
| for i := 1; i <= GOAMD64; i++ { |
| list = append(list, fmt.Sprintf("%s.v%d", GOARCH, i)) |
| } |
| return list |
| case "arm": |
| var list []string |
| for i := 5; i <= GOARM.Version; i++ { |
| list = append(list, fmt.Sprintf("%s.%d", GOARCH, i)) |
| } |
| return list |
| case "arm64": |
| var list []string |
| major := int(GOARM64.Version[1] - '0') |
| minor := int(GOARM64.Version[3] - '0') |
| for i := 0; i <= minor; i++ { |
| list = append(list, fmt.Sprintf("%s.v%d.%d", GOARCH, major, i)) |
| } |
| // ARM64 v9.x also includes support of v8.x+5 (i.e. v9.1 includes v8.(1+5) = v8.6). |
| if major == 9 { |
| for i := 0; i <= minor+5 && i <= 9; i++ { |
| list = append(list, fmt.Sprintf("%s.v%d.%d", GOARCH, 8, i)) |
| } |
| } |
| return list |
| case "mips", "mipsle": |
| return []string{GOARCH + "." + GOMIPS} |
| case "mips64", "mips64le": |
| return []string{GOARCH + "." + GOMIPS64} |
| case "ppc64", "ppc64le": |
| var list []string |
| for i := 8; i <= GOPPC64; i++ { |
| list = append(list, fmt.Sprintf("%s.power%d", GOARCH, i)) |
| } |
| return list |
| case "riscv64": |
| list := []string{GOARCH + "." + "rva20u64"} |
| if GORISCV64 >= 22 { |
| list = append(list, GOARCH+"."+"rva22u64") |
| } |
| return list |
| case "wasm": |
| var list []string |
| if GOWASM.SatConv { |
| list = append(list, GOARCH+".satconv") |
| } |
| if GOWASM.SignExt { |
| list = append(list, GOARCH+".signext") |
| } |
| return list |
| } |
| return nil |
| } |