1
2
3
4
5
6
7 package work
8
9 import (
10 "bytes"
11 "cmd/go/internal/base"
12 "cmd/go/internal/cfg"
13 "cmd/go/internal/fsys"
14 "cmd/go/internal/modload"
15 "cmd/internal/quoted"
16 "fmt"
17 "internal/platform"
18 "os"
19 "os/exec"
20 "path/filepath"
21 "regexp"
22 "runtime"
23 "slices"
24 "strconv"
25 "sync"
26 )
27
28 var buildInitStarted = false
29
30
31
32
33
34
35
36
37 var cfgChangedEnv []string
38
39 func makeCfgChangedEnv() []string {
40 var env []string
41 if cfg.Getenv("GOOS") != cfg.Goos {
42 env = append(env, "GOOS="+cfg.Goos)
43 }
44 if cfg.Getenv("GOARCH") != cfg.Goarch {
45 env = append(env, "GOARCH="+cfg.Goarch)
46 }
47 if archenv, val, changed := cfg.GetArchEnv(); changed {
48 env = append(env, archenv+"="+val)
49 }
50 return slices.Clip(env)
51 }
52
53 func BuildInit() {
54 if buildInitStarted {
55 base.Fatalf("go: internal error: work.BuildInit called more than once")
56 }
57 buildInitStarted = true
58 base.AtExit(closeBuilders)
59
60 modload.Init()
61 instrumentInit()
62 buildModeInit()
63 cfgChangedEnv = makeCfgChangedEnv()
64
65 if err := fsys.Init(); err != nil {
66 base.Fatal(err)
67 }
68 if from, replaced := fsys.DirContainsReplacement(cfg.GOMODCACHE); replaced {
69 base.Fatalf("go: overlay contains a replacement for %s. Files beneath GOMODCACHE (%s) must not be replaced.", from, cfg.GOMODCACHE)
70 }
71
72
73
74 if cfg.BuildPkgdir != "" && !filepath.IsAbs(cfg.BuildPkgdir) {
75 p, err := filepath.Abs(cfg.BuildPkgdir)
76 if err != nil {
77 fmt.Fprintf(os.Stderr, "go: evaluating -pkgdir: %v\n", err)
78 base.SetExitStatus(2)
79 base.Exit()
80 }
81 cfg.BuildPkgdir = p
82 }
83
84 if cfg.BuildP <= 0 {
85 base.Fatalf("go: -p must be a positive integer: %v\n", cfg.BuildP)
86 }
87
88
89 for _, key := range []string{"CC", "CXX", "FC"} {
90 value := cfg.Getenv(key)
91 args, err := quoted.Split(value)
92 if err != nil {
93 base.Fatalf("go: %s environment variable could not be parsed: %v", key, err)
94 }
95 if len(args) == 0 {
96 continue
97 }
98 path := args[0]
99 if !filepath.IsAbs(path) && path != filepath.Base(path) {
100 base.Fatalf("go: %s environment variable is relative; must be absolute path: %s\n", key, path)
101 }
102 }
103
104
105
106 if cfg.BuildCoverMode == "" {
107 cfg.BuildCoverMode = "set"
108 if cfg.BuildRace {
109
110 cfg.BuildCoverMode = "atomic"
111 }
112 }
113 if cfg.BuildRace && cfg.BuildCoverMode != "atomic" {
114 base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, cfg.BuildCoverMode)
115 }
116 }
117
118
119
120
121
122
123
124 func fuzzInstrumentFlags() []string {
125 if !platform.FuzzInstrumented(cfg.Goos, cfg.Goarch) {
126 return nil
127 }
128 return []string{"-d=libfuzzer"}
129 }
130
131 func instrumentInit() {
132 if !cfg.BuildRace && !cfg.BuildMSan && !cfg.BuildASan {
133 return
134 }
135 if cfg.BuildRace && cfg.BuildMSan {
136 fmt.Fprintf(os.Stderr, "go: may not use -race and -msan simultaneously\n")
137 base.SetExitStatus(2)
138 base.Exit()
139 }
140 if cfg.BuildRace && cfg.BuildASan {
141 fmt.Fprintf(os.Stderr, "go: may not use -race and -asan simultaneously\n")
142 base.SetExitStatus(2)
143 base.Exit()
144 }
145 if cfg.BuildMSan && cfg.BuildASan {
146 fmt.Fprintf(os.Stderr, "go: may not use -msan and -asan simultaneously\n")
147 base.SetExitStatus(2)
148 base.Exit()
149 }
150 if cfg.BuildMSan && !platform.MSanSupported(cfg.Goos, cfg.Goarch) {
151 fmt.Fprintf(os.Stderr, "-msan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
152 base.SetExitStatus(2)
153 base.Exit()
154 }
155 if cfg.BuildRace && !platform.RaceDetectorSupported(cfg.Goos, cfg.Goarch) {
156 fmt.Fprintf(os.Stderr, "-race is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
157 base.SetExitStatus(2)
158 base.Exit()
159 }
160 if cfg.BuildASan && !platform.ASanSupported(cfg.Goos, cfg.Goarch) {
161 fmt.Fprintf(os.Stderr, "-asan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
162 base.SetExitStatus(2)
163 base.Exit()
164 }
165
166
167
168
169
170 if cfg.BuildASan {
171 if err := compilerRequiredAsanVersion(); err != nil {
172 fmt.Fprintf(os.Stderr, "%v\n", err)
173 base.SetExitStatus(2)
174 base.Exit()
175 }
176 }
177
178 mode := "race"
179 if cfg.BuildMSan {
180 mode = "msan"
181
182
183 if cfg.BuildBuildmode == "default" && (cfg.Goos != "linux" || cfg.Goarch != "amd64") {
184 cfg.BuildBuildmode = "pie"
185 }
186 }
187 if cfg.BuildASan {
188 mode = "asan"
189 }
190 modeFlag := "-" + mode
191
192
193
194 if !cfg.BuildContext.CgoEnabled && (cfg.Goos != "darwin" || cfg.BuildASan || cfg.BuildMSan) {
195 if runtime.GOOS != cfg.Goos || runtime.GOARCH != cfg.Goarch {
196 fmt.Fprintf(os.Stderr, "go: %s requires cgo\n", modeFlag)
197 } else {
198 fmt.Fprintf(os.Stderr, "go: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", modeFlag)
199 }
200
201 base.SetExitStatus(2)
202 base.Exit()
203 }
204 forcedGcflags = append(forcedGcflags, modeFlag)
205 forcedLdflags = append(forcedLdflags, modeFlag)
206
207 if cfg.BuildContext.InstallSuffix != "" {
208 cfg.BuildContext.InstallSuffix += "_"
209 }
210 cfg.BuildContext.InstallSuffix += mode
211 cfg.BuildContext.ToolTags = append(cfg.BuildContext.ToolTags, mode)
212 }
213
214 func buildModeInit() {
215 gccgo := cfg.BuildToolchainName == "gccgo"
216 var codegenArg string
217
218
219
220
221
222 switch cfg.BuildBuildmode {
223 case "archive":
224 pkgsFilter = pkgsNotMain
225 case "c-archive":
226 pkgsFilter = oneMainPkg
227 if gccgo {
228 codegenArg = "-fPIC"
229 } else {
230 switch cfg.Goos {
231 case "darwin", "ios":
232 switch cfg.Goarch {
233 case "arm64":
234 codegenArg = "-shared"
235 }
236
237 case "dragonfly", "freebsd", "illumos", "linux", "netbsd", "openbsd", "solaris":
238
239
240
241 codegenArg = "-shared"
242 }
243 }
244 cfg.ExeSuffix = ".a"
245 ldBuildmode = "c-archive"
246 case "c-shared":
247 pkgsFilter = oneMainPkg
248 if gccgo {
249 codegenArg = "-fPIC"
250 } else {
251 switch cfg.Goos {
252 case "linux", "android", "freebsd":
253 codegenArg = "-shared"
254 case "windows":
255
256 cfg.ExeSuffix = ""
257 }
258 }
259 ldBuildmode = "c-shared"
260 case "default":
261 ldBuildmode = "exe"
262 if platform.DefaultPIE(cfg.Goos, cfg.Goarch, cfg.BuildRace) {
263 ldBuildmode = "pie"
264 if cfg.Goos != "windows" && !gccgo {
265 codegenArg = "-shared"
266 }
267 }
268 case "exe":
269 pkgsFilter = pkgsMain
270 ldBuildmode = "exe"
271
272
273
274 if cfg.BuildO != "" {
275 pkgsFilter = oneMainPkg
276 }
277 case "pie":
278 if cfg.BuildRace && !platform.DefaultPIE(cfg.Goos, cfg.Goarch, cfg.BuildRace) {
279 base.Fatalf("-buildmode=pie not supported when -race is enabled on %s/%s", cfg.Goos, cfg.Goarch)
280 }
281 if gccgo {
282 codegenArg = "-fPIE"
283 } else {
284 switch cfg.Goos {
285 case "aix", "windows":
286 default:
287 codegenArg = "-shared"
288 }
289 }
290 ldBuildmode = "pie"
291 case "shared":
292 pkgsFilter = pkgsNotMain
293 if gccgo {
294 codegenArg = "-fPIC"
295 } else {
296 codegenArg = "-dynlink"
297 }
298 if cfg.BuildO != "" {
299 base.Fatalf("-buildmode=shared and -o not supported together")
300 }
301 ldBuildmode = "shared"
302 case "plugin":
303 pkgsFilter = oneMainPkg
304 if gccgo {
305 codegenArg = "-fPIC"
306 } else {
307 codegenArg = "-dynlink"
308 }
309 cfg.ExeSuffix = ".so"
310 ldBuildmode = "plugin"
311 default:
312 base.Fatalf("buildmode=%s not supported", cfg.BuildBuildmode)
313 }
314
315 if cfg.BuildBuildmode != "default" && !platform.BuildModeSupported(cfg.BuildToolchainName, cfg.BuildBuildmode, cfg.Goos, cfg.Goarch) {
316 base.Fatalf("-buildmode=%s not supported on %s/%s\n", cfg.BuildBuildmode, cfg.Goos, cfg.Goarch)
317 }
318
319 if cfg.BuildLinkshared {
320 if !platform.BuildModeSupported(cfg.BuildToolchainName, "shared", cfg.Goos, cfg.Goarch) {
321 base.Fatalf("-linkshared not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
322 }
323 if gccgo {
324 codegenArg = "-fPIC"
325 } else {
326 forcedAsmflags = append(forcedAsmflags, "-D=GOBUILDMODE_shared=1",
327 "-linkshared")
328 codegenArg = "-dynlink"
329 forcedGcflags = append(forcedGcflags, "-linkshared")
330
331 forcedLdflags = append(forcedLdflags, "-linkshared", "-w")
332 }
333 }
334 if codegenArg != "" {
335 if gccgo {
336 forcedGccgoflags = append([]string{codegenArg}, forcedGccgoflags...)
337 } else {
338 forcedAsmflags = append([]string{codegenArg}, forcedAsmflags...)
339 forcedGcflags = append([]string{codegenArg}, forcedGcflags...)
340 }
341
342 if cfg.BuildBuildmode != "default" || cfg.BuildLinkshared {
343 if cfg.BuildContext.InstallSuffix != "" {
344 cfg.BuildContext.InstallSuffix += "_"
345 }
346 cfg.BuildContext.InstallSuffix += codegenArg[1:]
347 }
348 }
349
350 switch cfg.BuildMod {
351 case "":
352
353 case "readonly", "vendor", "mod":
354 if !cfg.ModulesEnabled && !base.InGOFLAGS("-mod") {
355 base.Fatalf("build flag -mod=%s only valid when using modules", cfg.BuildMod)
356 }
357 default:
358 base.Fatalf("-mod=%s not supported (can be '', 'mod', 'readonly', or 'vendor')", cfg.BuildMod)
359 }
360 if !cfg.ModulesEnabled {
361 if cfg.ModCacheRW && !base.InGOFLAGS("-modcacherw") {
362 base.Fatalf("build flag -modcacherw only valid when using modules")
363 }
364 if cfg.ModFile != "" && !base.InGOFLAGS("-mod") {
365 base.Fatalf("build flag -modfile only valid when using modules")
366 }
367 }
368 }
369
370 type version struct {
371 name string
372 major, minor int
373 }
374
375 var compiler struct {
376 sync.Once
377 version
378 err error
379 }
380
381
382
383
384 func compilerVersion() (version, error) {
385 compiler.Once.Do(func() {
386 compiler.err = func() error {
387 compiler.name = "unknown"
388 cc := os.Getenv("CC")
389 cmd := exec.Command(cc, "--version")
390 cmd.Env = append(cmd.Environ(), "LANG=C")
391 out, err := cmd.Output()
392 if err != nil {
393
394 return err
395 }
396
397 var match [][]byte
398 if bytes.HasPrefix(out, []byte("gcc")) {
399 compiler.name = "gcc"
400 cmd := exec.Command(cc, "-v")
401 cmd.Env = append(cmd.Environ(), "LANG=C")
402 out, err := cmd.CombinedOutput()
403 if err != nil {
404
405 return err
406 }
407 gccRE := regexp.MustCompile(`gcc version (\d+)\.(\d+)`)
408 match = gccRE.FindSubmatch(out)
409 } else {
410 clangRE := regexp.MustCompile(`clang version (\d+)\.(\d+)`)
411 if match = clangRE.FindSubmatch(out); len(match) > 0 {
412 compiler.name = "clang"
413 }
414 }
415
416 if len(match) < 3 {
417 return nil
418 }
419 if compiler.major, err = strconv.Atoi(string(match[1])); err != nil {
420 return err
421 }
422 if compiler.minor, err = strconv.Atoi(string(match[2])); err != nil {
423 return err
424 }
425 return nil
426 }()
427 })
428 return compiler.version, compiler.err
429 }
430
431
432
433
434
435 func compilerRequiredAsanVersion() error {
436 compiler, err := compilerVersion()
437 if err != nil {
438 return fmt.Errorf("-asan: the version of $(go env CC) could not be parsed")
439 }
440
441 switch compiler.name {
442 case "gcc":
443 if runtime.GOARCH == "ppc64le" && compiler.major < 9 {
444 return fmt.Errorf("-asan is not supported with %s compiler %d.%d\n", compiler.name, compiler.major, compiler.minor)
445 }
446 if compiler.major < 7 {
447 return fmt.Errorf("-asan is not supported with %s compiler %d.%d\n", compiler.name, compiler.major, compiler.minor)
448 }
449 case "clang":
450 if compiler.major < 9 {
451 return fmt.Errorf("-asan is not supported with %s compiler %d.%d\n", compiler.name, compiler.major, compiler.minor)
452 }
453 default:
454 return fmt.Errorf("-asan: C compiler is not gcc or clang")
455 }
456 return nil
457 }
458
View as plain text