1
2
3
4
5
6
7 package work
8
9 import (
10 "bufio"
11 "bytes"
12 "container/heap"
13 "context"
14 "debug/elf"
15 "encoding/json"
16 "fmt"
17 "os"
18 "path/filepath"
19 "runtime"
20 "strings"
21 "sync"
22 "time"
23
24 "cmd/go/internal/base"
25 "cmd/go/internal/cache"
26 "cmd/go/internal/cfg"
27 "cmd/go/internal/load"
28 "cmd/go/internal/trace"
29 "cmd/internal/buildid"
30 )
31
32
33
34
35 type Builder struct {
36 WorkDir string
37 actionCache map[cacheKey]*Action
38 mkdirCache map[string]bool
39 flagCache map[[2]string]bool
40 Print func(args ...any) (int, error)
41
42 IsCmdList bool
43 NeedError bool
44 NeedExport bool
45 NeedCompiledGoFiles bool
46
47 objdirSeq int
48 pkgSeq int
49
50 output sync.Mutex
51 scriptDir string
52
53 exec sync.Mutex
54 readySema chan bool
55 ready actionQueue
56
57 id sync.Mutex
58 toolIDCache map[string]string
59 buildIDCache map[string]string
60 }
61
62
63
64
65
66 type Action struct {
67 Mode string
68 Package *load.Package
69 Deps []*Action
70 Func func(*Builder, context.Context, *Action) error
71 IgnoreFail bool
72 TestOutput *bytes.Buffer
73 Args []string
74
75 triggers []*Action
76
77 buggyInstall bool
78
79 TryCache func(*Builder, *Action) bool
80
81
82 Objdir string
83 Target string
84 built string
85 actionID cache.ActionID
86 buildID string
87
88 VetxOnly bool
89 needVet bool
90 needBuild bool
91 vetCfg *vetConfig
92 output []byte
93
94
95 pending int
96 priority int
97 Failed bool
98 json *actionJSON
99 nonGoOverlay map[string]string
100 traceSpan *trace.Span
101 }
102
103
104 func (a *Action) BuildActionID() string { return actionID(a.buildID) }
105
106
107 func (a *Action) BuildContentID() string { return contentID(a.buildID) }
108
109
110 func (a *Action) BuildID() string { return a.buildID }
111
112
113
114 func (a *Action) BuiltTarget() string { return a.built }
115
116
117 type actionQueue []*Action
118
119
120 func (q *actionQueue) Len() int { return len(*q) }
121 func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
122 func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority }
123 func (q *actionQueue) Push(x any) { *q = append(*q, x.(*Action)) }
124 func (q *actionQueue) Pop() any {
125 n := len(*q) - 1
126 x := (*q)[n]
127 *q = (*q)[:n]
128 return x
129 }
130
131 func (q *actionQueue) push(a *Action) {
132 if a.json != nil {
133 a.json.TimeReady = time.Now()
134 }
135 heap.Push(q, a)
136 }
137
138 func (q *actionQueue) pop() *Action {
139 return heap.Pop(q).(*Action)
140 }
141
142 type actionJSON struct {
143 ID int
144 Mode string
145 Package string
146 Deps []int `json:",omitempty"`
147 IgnoreFail bool `json:",omitempty"`
148 Args []string `json:",omitempty"`
149 Link bool `json:",omitempty"`
150 Objdir string `json:",omitempty"`
151 Target string `json:",omitempty"`
152 Priority int `json:",omitempty"`
153 Failed bool `json:",omitempty"`
154 Built string `json:",omitempty"`
155 VetxOnly bool `json:",omitempty"`
156 NeedVet bool `json:",omitempty"`
157 NeedBuild bool `json:",omitempty"`
158 ActionID string `json:",omitempty"`
159 BuildID string `json:",omitempty"`
160 TimeReady time.Time `json:",omitempty"`
161 TimeStart time.Time `json:",omitempty"`
162 TimeDone time.Time `json:",omitempty"`
163
164 Cmd []string
165 CmdReal time.Duration `json:",omitempty"`
166 CmdUser time.Duration `json:",omitempty"`
167 CmdSys time.Duration `json:",omitempty"`
168 }
169
170
171 type cacheKey struct {
172 mode string
173 p *load.Package
174 }
175
176 func actionGraphJSON(a *Action) string {
177 var workq []*Action
178 var inWorkq = make(map[*Action]int)
179
180 add := func(a *Action) {
181 if _, ok := inWorkq[a]; ok {
182 return
183 }
184 inWorkq[a] = len(workq)
185 workq = append(workq, a)
186 }
187 add(a)
188
189 for i := 0; i < len(workq); i++ {
190 for _, dep := range workq[i].Deps {
191 add(dep)
192 }
193 }
194
195 var list []*actionJSON
196 for id, a := range workq {
197 if a.json == nil {
198 a.json = &actionJSON{
199 Mode: a.Mode,
200 ID: id,
201 IgnoreFail: a.IgnoreFail,
202 Args: a.Args,
203 Objdir: a.Objdir,
204 Target: a.Target,
205 Failed: a.Failed,
206 Priority: a.priority,
207 Built: a.built,
208 VetxOnly: a.VetxOnly,
209 NeedBuild: a.needBuild,
210 NeedVet: a.needVet,
211 }
212 if a.Package != nil {
213
214 a.json.Package = a.Package.ImportPath
215 }
216 for _, a1 := range a.Deps {
217 a.json.Deps = append(a.json.Deps, inWorkq[a1])
218 }
219 }
220 list = append(list, a.json)
221 }
222
223 js, err := json.MarshalIndent(list, "", "\t")
224 if err != nil {
225 fmt.Fprintf(os.Stderr, "go: writing debug action graph: %v\n", err)
226 return ""
227 }
228 return string(js)
229 }
230
231
232
233 type BuildMode int
234
235 const (
236 ModeBuild BuildMode = iota
237 ModeInstall
238 ModeBuggyInstall
239
240 ModeVetOnly = 1 << 8
241 )
242
243 func (b *Builder) Init() {
244 b.Print = func(a ...any) (int, error) {
245 return fmt.Fprint(os.Stderr, a...)
246 }
247 b.actionCache = make(map[cacheKey]*Action)
248 b.mkdirCache = make(map[string]bool)
249 b.toolIDCache = make(map[string]string)
250 b.buildIDCache = make(map[string]string)
251
252 if cfg.BuildN {
253 b.WorkDir = "$WORK"
254 } else {
255 tmp, err := os.MkdirTemp(cfg.Getenv("GOTMPDIR"), "go-build")
256 if err != nil {
257 base.Fatalf("go: creating work dir: %v", err)
258 }
259 if !filepath.IsAbs(tmp) {
260 abs, err := filepath.Abs(tmp)
261 if err != nil {
262 os.RemoveAll(tmp)
263 base.Fatalf("go: creating work dir: %v", err)
264 }
265 tmp = abs
266 }
267 b.WorkDir = tmp
268 if cfg.BuildX || cfg.BuildWork {
269 fmt.Fprintf(os.Stderr, "WORK=%s\n", b.WorkDir)
270 }
271 if !cfg.BuildWork {
272 workdir := b.WorkDir
273 base.AtExit(func() {
274 start := time.Now()
275 for {
276 err := os.RemoveAll(workdir)
277 if err == nil {
278 return
279 }
280
281
282
283
284
285
286 if runtime.GOOS != "windows" || time.Since(start) >= 500*time.Millisecond {
287 fmt.Fprintf(os.Stderr, "go: failed to remove work dir: %s\n", err)
288 return
289 }
290 time.Sleep(5 * time.Millisecond)
291 }
292 })
293 }
294 }
295
296 if err := CheckGOOSARCHPair(cfg.Goos, cfg.Goarch); err != nil {
297 fmt.Fprintf(os.Stderr, "go: %v\n", err)
298 base.SetExitStatus(2)
299 base.Exit()
300 }
301
302 for _, tag := range cfg.BuildContext.BuildTags {
303 if strings.Contains(tag, ",") {
304 fmt.Fprintf(os.Stderr, "go: -tags space-separated list contains comma\n")
305 base.SetExitStatus(2)
306 base.Exit()
307 }
308 }
309 }
310
311 func CheckGOOSARCHPair(goos, goarch string) error {
312 if _, ok := cfg.OSArchSupportsCgo[goos+"/"+goarch]; !ok && cfg.BuildContext.Compiler == "gc" {
313 return fmt.Errorf("unsupported GOOS/GOARCH pair %s/%s", goos, goarch)
314 }
315 return nil
316 }
317
318
319
320
321
322
323
324
325
326 func (b *Builder) NewObjdir() string {
327 b.objdirSeq++
328 return filepath.Join(b.WorkDir, fmt.Sprintf("b%03d", b.objdirSeq)) + string(filepath.Separator)
329 }
330
331
332
333
334
335 func readpkglist(shlibpath string) (pkgs []*load.Package) {
336 var stk load.ImportStack
337 if cfg.BuildToolchainName == "gccgo" {
338 f, _ := elf.Open(shlibpath)
339 sect := f.Section(".go_export")
340 data, _ := sect.Data()
341 scanner := bufio.NewScanner(bytes.NewBuffer(data))
342 for scanner.Scan() {
343 t := scanner.Text()
344 if strings.HasPrefix(t, "pkgpath ") {
345 t = strings.TrimPrefix(t, "pkgpath ")
346 t = strings.TrimSuffix(t, ";")
347 pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd(), nil, &stk, nil, 0))
348 }
349 }
350 } else {
351 pkglistbytes, err := buildid.ReadELFNote(shlibpath, "Go\x00\x00", 1)
352 if err != nil {
353 base.Fatalf("readELFNote failed: %v", err)
354 }
355 scanner := bufio.NewScanner(bytes.NewBuffer(pkglistbytes))
356 for scanner.Scan() {
357 t := scanner.Text()
358 pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd(), nil, &stk, nil, 0))
359 }
360 }
361 return
362 }
363
364
365
366
367
368 func (b *Builder) cacheAction(mode string, p *load.Package, f func() *Action) *Action {
369 a := b.actionCache[cacheKey{mode, p}]
370 if a == nil {
371 a = f()
372 b.actionCache[cacheKey{mode, p}] = a
373 }
374 return a
375 }
376
377
378 func (b *Builder) AutoAction(mode, depMode BuildMode, p *load.Package) *Action {
379 if p.Name == "main" {
380 return b.LinkAction(mode, depMode, p)
381 }
382 return b.CompileAction(mode, depMode, p)
383 }
384
385
386
387
388
389
390 func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Action {
391 vetOnly := mode&ModeVetOnly != 0
392 mode &^= ModeVetOnly
393
394 if mode != ModeBuild && (p.Internal.Local || p.Module != nil) && p.Target == "" {
395
396 mode = ModeBuild
397 }
398 if mode != ModeBuild && p.Name == "main" {
399
400 mode = ModeBuild
401 }
402
403
404 a := b.cacheAction("build", p, func() *Action {
405 a := &Action{
406 Mode: "build",
407 Package: p,
408 Func: (*Builder).build,
409 Objdir: b.NewObjdir(),
410 }
411
412 if p.Error == nil || !p.Error.IsImportCycle {
413 for _, p1 := range p.Internal.Imports {
414 a.Deps = append(a.Deps, b.CompileAction(depMode, depMode, p1))
415 }
416 }
417
418 if p.Standard {
419 switch p.ImportPath {
420 case "builtin", "unsafe":
421
422 a.Mode = "built-in package"
423 a.Func = nil
424 return a
425 }
426
427
428 if cfg.BuildToolchainName == "gccgo" {
429
430 a.Mode = "gccgo stdlib"
431 a.Target = p.Target
432 a.Func = nil
433 return a
434 }
435 }
436
437 return a
438 })
439
440
441
442 buildAction := a
443 switch buildAction.Mode {
444 case "build", "built-in package", "gccgo stdlib":
445
446 case "build-install":
447 buildAction = a.Deps[0]
448 default:
449 panic("lost build action: " + buildAction.Mode)
450 }
451 buildAction.needBuild = buildAction.needBuild || !vetOnly
452
453
454 if mode == ModeInstall || mode == ModeBuggyInstall {
455 a = b.installAction(a, mode)
456 }
457
458 return a
459 }
460
461
462
463
464
465 func (b *Builder) VetAction(mode, depMode BuildMode, p *load.Package) *Action {
466 a := b.vetAction(mode, depMode, p)
467 a.VetxOnly = false
468 return a
469 }
470
471 func (b *Builder) vetAction(mode, depMode BuildMode, p *load.Package) *Action {
472
473 a := b.cacheAction("vet", p, func() *Action {
474 a1 := b.CompileAction(mode|ModeVetOnly, depMode, p)
475
476
477 var stk load.ImportStack
478 stk.Push("vet")
479 p1 := load.LoadImportWithFlags("fmt", p.Dir, p, &stk, nil, 0)
480 stk.Pop()
481 aFmt := b.CompileAction(ModeBuild, depMode, p1)
482
483 var deps []*Action
484 if a1.buggyInstall {
485
486
487
488
489 deps = []*Action{a1.Deps[0], aFmt, a1}
490 } else {
491 deps = []*Action{a1, aFmt}
492 }
493 for _, p1 := range p.Internal.Imports {
494 deps = append(deps, b.vetAction(mode, depMode, p1))
495 }
496
497 a := &Action{
498 Mode: "vet",
499 Package: p,
500 Deps: deps,
501 Objdir: a1.Objdir,
502 VetxOnly: true,
503 IgnoreFail: true,
504 }
505 if a1.Func == nil {
506
507 return a
508 }
509 deps[0].needVet = true
510 a.Func = (*Builder).vet
511 return a
512 })
513 return a
514 }
515
516
517
518
519 func (b *Builder) LinkAction(mode, depMode BuildMode, p *load.Package) *Action {
520
521 a := b.cacheAction("link", p, func() *Action {
522 a := &Action{
523 Mode: "link",
524 Package: p,
525 }
526
527 a1 := b.CompileAction(ModeBuild, depMode, p)
528 a.Func = (*Builder).link
529 a.Deps = []*Action{a1}
530 a.Objdir = a1.Objdir
531
532
533
534
535
536
537
538
539 name := "a.out"
540 if p.Internal.ExeName != "" {
541 name = p.Internal.ExeName
542 } else if (cfg.Goos == "darwin" || cfg.Goos == "windows") && cfg.BuildBuildmode == "c-shared" && p.Target != "" {
543
544
545
546
547
548
549
550 _, name = filepath.Split(p.Target)
551 }
552 a.Target = a.Objdir + filepath.Join("exe", name) + cfg.ExeSuffix
553 a.built = a.Target
554 b.addTransitiveLinkDeps(a, a1, "")
555
556
557
558
559
560
561
562
563 a1.Deps = append(a1.Deps, &Action{Mode: "nop", Deps: a.Deps[1:]})
564 return a
565 })
566
567 if mode == ModeInstall || mode == ModeBuggyInstall {
568 a = b.installAction(a, mode)
569 }
570
571 return a
572 }
573
574
575 func (b *Builder) installAction(a1 *Action, mode BuildMode) *Action {
576
577
578
579 if strings.HasSuffix(a1.Mode, "-install") {
580 if a1.buggyInstall && mode == ModeInstall {
581
582 a1.buggyInstall = false
583 }
584 return a1
585 }
586
587
588
589
590 if a1.Func == nil {
591 return a1
592 }
593
594 p := a1.Package
595 return b.cacheAction(a1.Mode+"-install", p, func() *Action {
596
597
598
599
600
601
602
603 buildAction := new(Action)
604 *buildAction = *a1
605
606
607
608
609
610
611
612
613
614 *a1 = Action{
615 Mode: buildAction.Mode + "-install",
616 Func: BuildInstallFunc,
617 Package: p,
618 Objdir: buildAction.Objdir,
619 Deps: []*Action{buildAction},
620 Target: p.Target,
621 built: p.Target,
622
623 buggyInstall: mode == ModeBuggyInstall,
624 }
625
626 b.addInstallHeaderAction(a1)
627 return a1
628 })
629 }
630
631
632
633
634
635
636
637
638
639
640 func (b *Builder) addTransitiveLinkDeps(a, a1 *Action, shlib string) {
641
642
643
644
645
646 workq := []*Action{a1}
647 haveDep := map[string]bool{}
648 if a1.Package != nil {
649 haveDep[a1.Package.ImportPath] = true
650 }
651 for i := 0; i < len(workq); i++ {
652 a1 := workq[i]
653 for _, a2 := range a1.Deps {
654
655 if a2.Package == nil || (a2.Mode != "build-install" && a2.Mode != "build") || haveDep[a2.Package.ImportPath] {
656 continue
657 }
658 haveDep[a2.Package.ImportPath] = true
659 a.Deps = append(a.Deps, a2)
660 if a2.Mode == "build-install" {
661 a2 = a2.Deps[0]
662 }
663 workq = append(workq, a2)
664 }
665 }
666
667
668
669 if cfg.BuildLinkshared {
670 haveShlib := map[string]bool{shlib: true}
671 for _, a1 := range a.Deps {
672 p1 := a1.Package
673 if p1 == nil || p1.Shlib == "" || haveShlib[filepath.Base(p1.Shlib)] {
674 continue
675 }
676 haveShlib[filepath.Base(p1.Shlib)] = true
677
678
679
680
681 a.Deps = append(a.Deps, b.linkSharedAction(ModeBuggyInstall, ModeBuggyInstall, p1.Shlib, nil))
682 }
683 }
684 }
685
686
687
688
689
690 func (b *Builder) addInstallHeaderAction(a *Action) {
691
692 p := a.Package
693 if p.UsesCgo() && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") {
694 hdrTarget := a.Target[:len(a.Target)-len(filepath.Ext(a.Target))] + ".h"
695 if cfg.BuildContext.Compiler == "gccgo" && cfg.BuildO == "" {
696
697
698
699 dir, file := filepath.Split(hdrTarget)
700 file = strings.TrimPrefix(file, "lib")
701 hdrTarget = filepath.Join(dir, file)
702 }
703 ah := &Action{
704 Mode: "install header",
705 Package: a.Package,
706 Deps: []*Action{a.Deps[0]},
707 Func: (*Builder).installHeader,
708 Objdir: a.Deps[0].Objdir,
709 Target: hdrTarget,
710 }
711 a.Deps = append(a.Deps, ah)
712 }
713 }
714
715
716
717 func (b *Builder) buildmodeShared(mode, depMode BuildMode, args []string, pkgs []*load.Package, a1 *Action) *Action {
718 name, err := libname(args, pkgs)
719 if err != nil {
720 base.Fatalf("%v", err)
721 }
722 return b.linkSharedAction(mode, depMode, name, a1)
723 }
724
725
726
727
728
729 func (b *Builder) linkSharedAction(mode, depMode BuildMode, shlib string, a1 *Action) *Action {
730 fullShlib := shlib
731 shlib = filepath.Base(shlib)
732 a := b.cacheAction("build-shlib "+shlib, nil, func() *Action {
733 if a1 == nil {
734
735
736 pkgs := readpkglist(fullShlib)
737 a1 = &Action{
738 Mode: "shlib packages",
739 }
740 for _, p := range pkgs {
741 a1.Deps = append(a1.Deps, b.CompileAction(mode, depMode, p))
742 }
743 }
744
745
746
747
748 p := &load.Package{}
749 p.Internal.CmdlinePkg = true
750 p.Internal.Ldflags = load.BuildLdflags.For(p)
751 p.Internal.Gccgoflags = load.BuildGccgoflags.For(p)
752
753
754
755
756
757
758
759
760
761
762
763 a := &Action{
764 Mode: "go build -buildmode=shared",
765 Package: p,
766 Objdir: b.NewObjdir(),
767 Func: (*Builder).linkShared,
768 Deps: []*Action{a1},
769 }
770 a.Target = filepath.Join(a.Objdir, shlib)
771 if cfg.BuildToolchainName != "gccgo" {
772 add := func(a1 *Action, pkg string, force bool) {
773 for _, a2 := range a1.Deps {
774 if a2.Package != nil && a2.Package.ImportPath == pkg {
775 return
776 }
777 }
778 var stk load.ImportStack
779 p := load.LoadImportWithFlags(pkg, base.Cwd(), nil, &stk, nil, 0)
780 if p.Error != nil {
781 base.Fatalf("load %s: %v", pkg, p.Error)
782 }
783
784
785
786
787
788 if force || p.Shlib == "" || filepath.Base(p.Shlib) == pkg {
789 a1.Deps = append(a1.Deps, b.CompileAction(depMode, depMode, p))
790 }
791 }
792 add(a1, "runtime/cgo", false)
793 if cfg.Goarch == "arm" {
794 add(a1, "math", false)
795 }
796
797
798
799 for _, dep := range load.LinkerDeps(nil) {
800 add(a, dep, true)
801 }
802 }
803 b.addTransitiveLinkDeps(a, a1, shlib)
804 return a
805 })
806
807
808 if (mode == ModeInstall || mode == ModeBuggyInstall) && a.Func != nil {
809 buildAction := a
810
811 a = b.cacheAction("install-shlib "+shlib, nil, func() *Action {
812
813
814
815
816
817
818 pkgDir := a1.Deps[0].Package.Internal.Build.PkgTargetRoot
819 for _, a2 := range a1.Deps {
820 if dir := a2.Package.Internal.Build.PkgTargetRoot; dir != pkgDir {
821 base.Fatalf("installing shared library: cannot use packages %s and %s from different roots %s and %s",
822 a1.Deps[0].Package.ImportPath,
823 a2.Package.ImportPath,
824 pkgDir,
825 dir)
826 }
827 }
828
829 if cfg.BuildToolchainName == "gccgo" {
830 pkgDir = filepath.Join(pkgDir, "shlibs")
831 }
832 target := filepath.Join(pkgDir, shlib)
833
834 a := &Action{
835 Mode: "go install -buildmode=shared",
836 Objdir: buildAction.Objdir,
837 Func: BuildInstallFunc,
838 Deps: []*Action{buildAction},
839 Target: target,
840 }
841 for _, a2 := range buildAction.Deps[0].Deps {
842 p := a2.Package
843 if p.Target == "" {
844 continue
845 }
846 a.Deps = append(a.Deps, &Action{
847 Mode: "shlibname",
848 Package: p,
849 Func: (*Builder).installShlibname,
850 Target: strings.TrimSuffix(p.Target, ".a") + ".shlibname",
851 Deps: []*Action{a.Deps[0]},
852 })
853 }
854 return a
855 })
856 }
857
858 return a
859 }
860
View as plain text