1
2
3
4
5 package work
6
7 import (
8 "bufio"
9 "bytes"
10 "fmt"
11 "internal/platform"
12 "io"
13 "log"
14 "os"
15 "path/filepath"
16 "runtime"
17 "strings"
18
19 "cmd/go/internal/base"
20 "cmd/go/internal/cfg"
21 "cmd/go/internal/fsys"
22 "cmd/go/internal/gover"
23 "cmd/go/internal/load"
24 "cmd/go/internal/str"
25 "cmd/internal/quoted"
26 "crypto/sha1"
27 )
28
29
30 var ToolchainVersion = runtime.Version()
31
32
33 const trimPathGoRootFinal string = "$GOROOT"
34
35
36
37 type gcToolchain struct{}
38
39 func (gcToolchain) compiler() string {
40 return base.Tool("compile")
41 }
42
43 func (gcToolchain) linker() string {
44 return base.Tool("link")
45 }
46
47 func pkgPath(a *Action) string {
48 p := a.Package
49 ppath := p.ImportPath
50 if cfg.BuildBuildmode == "plugin" {
51 ppath = pluginPath(a)
52 } else if p.Name == "main" && !p.Internal.ForceLibrary {
53 ppath = "main"
54 }
55 return ppath
56 }
57
58 func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
59 p := a.Package
60 sh := b.Shell(a)
61 objdir := a.Objdir
62 if archive != "" {
63 ofile = archive
64 } else {
65 out := "_go_.o"
66 ofile = objdir + out
67 }
68
69 pkgpath := pkgPath(a)
70 defaultGcFlags := []string{"-p", pkgpath}
71 if p.Module != nil {
72 v := p.Module.GoVersion
73 if v == "" {
74 v = gover.DefaultGoModVersion
75 }
76 if allowedVersion(v) {
77 defaultGcFlags = append(defaultGcFlags, "-lang=go"+gover.Lang(v))
78 }
79 }
80 if p.Standard {
81 defaultGcFlags = append(defaultGcFlags, "-std")
82 }
83
84
85
86
87
88 extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
89 if p.Standard {
90 switch p.ImportPath {
91 case "bytes", "internal/poll", "net", "os":
92 fallthrough
93 case "runtime/metrics", "runtime/pprof", "runtime/trace":
94 fallthrough
95 case "sync", "syscall", "time":
96 extFiles++
97 }
98 }
99 if extFiles == 0 {
100 defaultGcFlags = append(defaultGcFlags, "-complete")
101 }
102 if cfg.BuildContext.InstallSuffix != "" {
103 defaultGcFlags = append(defaultGcFlags, "-installsuffix", cfg.BuildContext.InstallSuffix)
104 }
105 if a.buildID != "" {
106 defaultGcFlags = append(defaultGcFlags, "-buildid", a.buildID)
107 }
108 if p.Internal.OmitDebug || cfg.Goos == "plan9" || cfg.Goarch == "wasm" {
109 defaultGcFlags = append(defaultGcFlags, "-dwarf=false")
110 }
111 if strings.HasPrefix(ToolchainVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") {
112 defaultGcFlags = append(defaultGcFlags, "-goversion", ToolchainVersion)
113 }
114 if p.Internal.Cover.Cfg != "" {
115 defaultGcFlags = append(defaultGcFlags, "-coveragecfg="+p.Internal.Cover.Cfg)
116 }
117 if p.Internal.PGOProfile != "" {
118 defaultGcFlags = append(defaultGcFlags, "-pgoprofile="+p.Internal.PGOProfile)
119 }
120 if symabis != "" {
121 defaultGcFlags = append(defaultGcFlags, "-symabis", symabis)
122 }
123
124 gcflags := str.StringList(forcedGcflags, p.Internal.Gcflags)
125 if p.Internal.FuzzInstrument {
126 gcflags = append(gcflags, fuzzInstrumentFlags()...)
127 }
128
129 if c := gcBackendConcurrency(gcflags); c > 1 {
130 defaultGcFlags = append(defaultGcFlags, fmt.Sprintf("-c=%d", c))
131 }
132
133 args := []any{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), defaultGcFlags, gcflags}
134 if p.Internal.LocalPrefix == "" {
135 args = append(args, "-nolocalimports")
136 } else {
137 args = append(args, "-D", p.Internal.LocalPrefix)
138 }
139 if importcfg != nil {
140 if err := sh.writeFile(objdir+"importcfg", importcfg); err != nil {
141 return "", nil, err
142 }
143 args = append(args, "-importcfg", objdir+"importcfg")
144 }
145 if embedcfg != nil {
146 if err := sh.writeFile(objdir+"embedcfg", embedcfg); err != nil {
147 return "", nil, err
148 }
149 args = append(args, "-embedcfg", objdir+"embedcfg")
150 }
151 if ofile == archive {
152 args = append(args, "-pack")
153 }
154 if asmhdr {
155 args = append(args, "-asmhdr", objdir+"go_asm.h")
156 }
157
158 for _, f := range gofiles {
159 f := mkAbs(p.Dir, f)
160
161
162
163
164
165
166
167
168
169
170
171
172
173 f, _ = fsys.OverlayPath(f)
174
175 args = append(args, f)
176 }
177
178 output, err = sh.runOut(base.Cwd(), nil, args...)
179 return ofile, output, err
180 }
181
182
183 func gcBackendConcurrency(gcflags []string) int {
184
185 canDashC := concurrentGCBackendCompilationEnabledByDefault
186
187 switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e {
188 case "0":
189 canDashC = false
190 case "1":
191 canDashC = true
192 case "":
193
194 default:
195 log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e)
196 }
197
198
199 if cfg.ExperimentErr != nil || cfg.Experiment.FieldTrack || cfg.Experiment.PreemptibleLoops {
200 canDashC = false
201 }
202
203 if !canDashC {
204 return 1
205 }
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230 c := runtime.GOMAXPROCS(0)
231 if cfg.BuildP == 1 {
232
233 return c
234 }
235
236 if c > 4 {
237 c = 4
238 }
239 return c
240 }
241
242
243
244 func (a *Action) trimpath() string {
245
246
247
248
249
250 objdir := a.Objdir
251 if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator {
252 objdir = objdir[:len(objdir)-1]
253 }
254 rewrite := ""
255
256 rewriteDir := a.Package.Dir
257 if cfg.BuildTrimpath {
258 importPath := a.Package.Internal.OrigImportPath
259 if m := a.Package.Module; m != nil && m.Version != "" {
260 rewriteDir = m.Path + "@" + m.Version + strings.TrimPrefix(importPath, m.Path)
261 } else {
262 rewriteDir = importPath
263 }
264 rewrite += a.Package.Dir + "=>" + rewriteDir + ";"
265 }
266
267
268
269
270
271 cgoFiles := make(map[string]bool)
272 for _, f := range a.Package.CgoFiles {
273 cgoFiles[f] = true
274 }
275
276
277
278
279
280 var overlayNonGoRewrites string
281 hasCgoOverlay := false
282 if fsys.OverlayFile != "" {
283 for _, filename := range a.Package.AllFiles() {
284 path := filename
285 if !filepath.IsAbs(path) {
286 path = filepath.Join(a.Package.Dir, path)
287 }
288 base := filepath.Base(path)
289 isGo := strings.HasSuffix(filename, ".go") || strings.HasSuffix(filename, ".s")
290 isCgo := cgoFiles[filename] || !isGo
291 overlayPath, isOverlay := fsys.OverlayPath(path)
292 if isCgo && isOverlay {
293 hasCgoOverlay = true
294 }
295 if !isCgo && isOverlay {
296 rewrite += overlayPath + "=>" + filepath.Join(rewriteDir, base) + ";"
297 } else if isCgo {
298
299 if filepath.Dir(path) == a.Package.Dir {
300
301 overlayNonGoRewrites += filepath.Join(objdir, base) + "=>" + filepath.Join(rewriteDir, base) + ";"
302 }
303 } else {
304
305 }
306 }
307 }
308 if hasCgoOverlay {
309 rewrite += overlayNonGoRewrites
310 }
311 rewrite += objdir + "=>"
312
313 return rewrite
314 }
315
316 func asmArgs(a *Action, p *load.Package) []any {
317
318 inc := filepath.Join(cfg.GOROOT, "pkg", "include")
319 pkgpath := pkgPath(a)
320 args := []any{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
321 if p.ImportPath == "runtime" && cfg.Goarch == "386" {
322 for _, arg := range forcedAsmflags {
323 if arg == "-dynlink" {
324 args = append(args, "-D=GOBUILDMODE_shared=1")
325 }
326 }
327 }
328
329 if cfg.Goarch == "386" {
330
331 args = append(args, "-D", "GO386_"+cfg.GO386)
332 }
333
334 if cfg.Goarch == "amd64" {
335
336 args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64)
337 }
338
339 if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
340
341 args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS)
342 }
343
344 if cfg.Goarch == "mips64" || cfg.Goarch == "mips64le" {
345
346 args = append(args, "-D", "GOMIPS64_"+cfg.GOMIPS64)
347 }
348
349 if cfg.Goarch == "ppc64" || cfg.Goarch == "ppc64le" {
350
351
352 switch cfg.GOPPC64 {
353 case "power10":
354 args = append(args, "-D", "GOPPC64_power10")
355 fallthrough
356 case "power9":
357 args = append(args, "-D", "GOPPC64_power9")
358 fallthrough
359 default:
360 args = append(args, "-D", "GOPPC64_power8")
361 }
362 }
363
364 if cfg.Goarch == "arm" {
365
366 switch cfg.GOARM {
367 case "7":
368 args = append(args, "-D", "GOARM_7")
369 fallthrough
370 case "6":
371 args = append(args, "-D", "GOARM_6")
372 fallthrough
373 default:
374 args = append(args, "-D", "GOARM_5")
375 }
376 }
377
378 return args
379 }
380
381 func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
382 p := a.Package
383 args := asmArgs(a, p)
384
385 var ofiles []string
386 for _, sfile := range sfiles {
387 overlayPath, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
388 ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
389 ofiles = append(ofiles, ofile)
390 args1 := append(args, "-o", ofile, overlayPath)
391 if err := b.Shell(a).run(p.Dir, p.ImportPath, nil, args1...); err != nil {
392 return nil, err
393 }
394 }
395 return ofiles, nil
396 }
397
398 func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
399 sh := b.Shell(a)
400
401 mkSymabis := func(p *load.Package, sfiles []string, path string) error {
402 args := asmArgs(a, p)
403 args = append(args, "-gensymabis", "-o", path)
404 for _, sfile := range sfiles {
405 if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") {
406 continue
407 }
408 op, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
409 args = append(args, op)
410 }
411
412
413
414
415 if err := sh.writeFile(a.Objdir+"go_asm.h", nil); err != nil {
416 return err
417 }
418
419 return sh.run(p.Dir, p.ImportPath, nil, args...)
420 }
421
422 var symabis string
423 p := a.Package
424 if len(sfiles) != 0 {
425 symabis = a.Objdir + "symabis"
426 if err := mkSymabis(p, sfiles, symabis); err != nil {
427 return "", err
428 }
429 }
430
431 return symabis, nil
432 }
433
434
435
436
437 func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []any) error {
438 newArgs := make([]any, len(args))
439 copy(newArgs, args)
440 newArgs[1] = base.Tool(newTool)
441 newArgs[3] = ofile + ".new"
442 if err := b.Shell(a).run(p.Dir, p.ImportPath, nil, newArgs...); err != nil {
443 return err
444 }
445 data1, err := os.ReadFile(ofile)
446 if err != nil {
447 return err
448 }
449 data2, err := os.ReadFile(ofile + ".new")
450 if err != nil {
451 return err
452 }
453 if !bytes.Equal(data1, data2) {
454 return fmt.Errorf("%s and %s produced different output files:\n%s\n%s", filepath.Base(args[1].(string)), newTool, strings.Join(str.StringList(args...), " "), strings.Join(str.StringList(newArgs...), " "))
455 }
456 os.Remove(ofile + ".new")
457 return nil
458 }
459
460 func (gcToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
461 var absOfiles []string
462 for _, f := range ofiles {
463 absOfiles = append(absOfiles, mkAbs(a.Objdir, f))
464 }
465 absAfile := mkAbs(a.Objdir, afile)
466
467
468
469 if !cfg.BuildN {
470 if _, err := os.Stat(absAfile); err != nil {
471 base.Fatalf("os.Stat of archive file failed: %v", err)
472 }
473 }
474
475 p := a.Package
476 sh := b.Shell(a)
477 if cfg.BuildN || cfg.BuildX {
478 cmdline := str.StringList(base.Tool("pack"), "r", absAfile, absOfiles)
479 sh.ShowCmd(p.Dir, "%s # internal", joinUnambiguously(cmdline))
480 }
481 if cfg.BuildN {
482 return nil
483 }
484 if err := packInternal(absAfile, absOfiles); err != nil {
485 return sh.reportCmd("", "", nil, err)
486 }
487 return nil
488 }
489
490 func packInternal(afile string, ofiles []string) error {
491 dst, err := os.OpenFile(afile, os.O_WRONLY|os.O_APPEND, 0)
492 if err != nil {
493 return err
494 }
495 defer dst.Close()
496 w := bufio.NewWriter(dst)
497
498 for _, ofile := range ofiles {
499 src, err := os.Open(ofile)
500 if err != nil {
501 return err
502 }
503 fi, err := src.Stat()
504 if err != nil {
505 src.Close()
506 return err
507 }
508
509
510 name := fi.Name()
511 if len(name) > 16 {
512 name = name[:16]
513 } else {
514 name += strings.Repeat(" ", 16-len(name))
515 }
516 size := fi.Size()
517 fmt.Fprintf(w, "%s%-12d%-6d%-6d%-8o%-10d`\n",
518 name, 0, 0, 0, 0644, size)
519 n, err := io.Copy(w, src)
520 src.Close()
521 if err == nil && n < size {
522 err = io.ErrUnexpectedEOF
523 } else if err == nil && n > size {
524 err = fmt.Errorf("file larger than size reported by stat")
525 }
526 if err != nil {
527 return fmt.Errorf("copying %s to %s: %v", ofile, afile, err)
528 }
529 if size&1 != 0 {
530 w.WriteByte(0)
531 }
532 }
533
534 if err := w.Flush(); err != nil {
535 return err
536 }
537 return dst.Close()
538 }
539
540
541 func setextld(ldflags []string, compiler []string) ([]string, error) {
542 for _, f := range ldflags {
543 if f == "-extld" || strings.HasPrefix(f, "-extld=") {
544
545 return ldflags, nil
546 }
547 }
548 joined, err := quoted.Join(compiler)
549 if err != nil {
550 return nil, err
551 }
552 return append(ldflags, "-extld="+joined), nil
553 }
554
555
556
557
558
559
560
561
562 func pluginPath(a *Action) string {
563 p := a.Package
564 if p.ImportPath != "command-line-arguments" {
565 return p.ImportPath
566 }
567 h := sha1.New()
568 buildID := a.buildID
569 if a.Mode == "link" {
570
571
572
573
574
575
576
577
578
579 id := strings.Split(buildID, buildIDSeparator)
580 buildID = id[1] + buildIDSeparator + id[1]
581 }
582 fmt.Fprintf(h, "build ID: %s\n", buildID)
583 for _, file := range str.StringList(p.GoFiles, p.CgoFiles, p.SFiles) {
584 data, err := os.ReadFile(filepath.Join(p.Dir, file))
585 if err != nil {
586 base.Fatalf("go: %s", err)
587 }
588 h.Write(data)
589 }
590 return fmt.Sprintf("plugin/unnamed-%x", h.Sum(nil))
591 }
592
593 func (gcToolchain) ld(b *Builder, root *Action, targetPath, importcfg, mainpkg string) error {
594 cxx := len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
595 for _, a := range root.Deps {
596 if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
597 cxx = true
598 }
599 }
600 var ldflags []string
601 if cfg.BuildContext.InstallSuffix != "" {
602 ldflags = append(ldflags, "-installsuffix", cfg.BuildContext.InstallSuffix)
603 }
604 if root.Package.Internal.OmitDebug {
605 ldflags = append(ldflags, "-s", "-w")
606 }
607 if cfg.BuildBuildmode == "plugin" {
608 ldflags = append(ldflags, "-pluginpath", pluginPath(root))
609 }
610
611
612
613 if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "cmd/") {
614
615
616
617
618 if !platform.MustLinkExternal(cfg.Goos, cfg.Goarch, false) {
619 ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID)
620 }
621 }
622
623
624 if root.Package.DefaultGODEBUG != "" {
625 ldflags = append(ldflags, "-X=runtime.godebugDefault="+root.Package.DefaultGODEBUG)
626 }
627
628
629
630
631
632 var compiler []string
633 if cxx {
634 compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
635 } else {
636 compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
637 }
638 ldflags = append(ldflags, "-buildmode="+ldBuildmode)
639 if root.buildID != "" {
640 ldflags = append(ldflags, "-buildid="+root.buildID)
641 }
642 ldflags = append(ldflags, forcedLdflags...)
643 ldflags = append(ldflags, root.Package.Internal.Ldflags...)
644 ldflags, err := setextld(ldflags, compiler)
645 if err != nil {
646 return err
647 }
648
649
650
651
652
653
654
655
656
657
658
659
660 dir := "."
661 if cfg.BuildBuildmode == "c-shared" || cfg.BuildBuildmode == "plugin" {
662 dir, targetPath = filepath.Split(targetPath)
663 }
664
665 env := []string{}
666 if cfg.BuildTrimpath {
667 env = append(env, "GOROOT_FINAL="+trimPathGoRootFinal)
668 }
669 return b.Shell(root).run(dir, root.Package.ImportPath, env, cfg.BuildToolexec, base.Tool("link"), "-o", targetPath, "-importcfg", importcfg, ldflags, mainpkg)
670 }
671
672 func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, targetPath, importcfg string, allactions []*Action) error {
673 ldflags := []string{"-installsuffix", cfg.BuildContext.InstallSuffix}
674 ldflags = append(ldflags, "-buildmode=shared")
675 ldflags = append(ldflags, forcedLdflags...)
676 ldflags = append(ldflags, root.Package.Internal.Ldflags...)
677 cxx := false
678 for _, a := range allactions {
679 if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
680 cxx = true
681 }
682 }
683
684
685
686
687 var compiler []string
688 if cxx {
689 compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
690 } else {
691 compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
692 }
693 ldflags, err := setextld(ldflags, compiler)
694 if err != nil {
695 return err
696 }
697 for _, d := range toplevelactions {
698 if !strings.HasSuffix(d.Target, ".a") {
699 continue
700 }
701 ldflags = append(ldflags, d.Package.ImportPath+"="+d.Target)
702 }
703 return b.Shell(root).run(".", targetPath, nil, cfg.BuildToolexec, base.Tool("link"), "-o", targetPath, "-importcfg", importcfg, ldflags)
704 }
705
706 func (gcToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
707 return fmt.Errorf("%s: C source files not supported without cgo", mkAbs(a.Package.Dir, cfile))
708 }
709
View as plain text