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. 12package buildcfg 13 14import ( 15 "fmt" 16 "os" 17 "path/filepath" 18 "strconv" 19 "strings" 20) 21 22var ( 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. 41var Error error 42 43// Check exits the program with a fatal error if Error is non-nil. 44func 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 51func envOr(key, value string) string { 52 if x := os.Getenv(key); x != "" { 53 return x 54 } 55 return value 56} 57 58func 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 73type goarmFeatures struct { 74 Version int 75 SoftFloat bool 76} 77 78func (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 88func 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 130type 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 142func (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 153func 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 195func 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") 202func (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 231func 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 240func 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 249func 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 262func 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 278type gowasmFeatures struct { 279 SatConv bool 280 SignExt bool 281} 282 283func (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 294func 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 310func Getgoextlinkenabled() string { 311 return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED) 312} 313 314func toolTags() []string { 315 tags := experimentTags() 316 tags = append(tags, gogoarchTags()...) 317 return tags 318} 319 320func 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". 335func 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 357func 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