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