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