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