-
Notifications
You must be signed in to change notification settings - Fork 17.6k
/
action.go
859 lines (772 loc) · 28.4 KB
/
action.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Action graph creation (planning).
package work
import (
"bufio"
"bytes"
"container/heap"
"context"
"debug/elf"
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
"cmd/go/internal/base"
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/trace"
"cmd/internal/buildid"
)
// A Builder holds global state about a build.
// It does not hold per-package state, because we
// build packages in parallel, and the builder is shared.
type Builder struct {
WorkDir string // the temporary work directory (ends in filepath.Separator)
actionCache map[cacheKey]*Action // a cache of already-constructed actions
mkdirCache map[string]bool // a cache of created directories
flagCache map[[2]string]bool // a cache of supported compiler flags
Print func(args ...interface{}) (int, error)
IsCmdList bool // running as part of go list; set p.Stale and additional fields below
NeedError bool // list needs p.Error
NeedExport bool // list needs p.Export
NeedCompiledGoFiles bool // list needs p.CompiledGoFiles
objdirSeq int // counter for NewObjdir
pkgSeq int
output sync.Mutex
scriptDir string // current directory in printed script
exec sync.Mutex
readySema chan bool
ready actionQueue
id sync.Mutex
toolIDCache map[string]string // tool name -> tool ID
buildIDCache map[string]string // file name -> build ID
}
// NOTE: Much of Action would not need to be exported if not for test.
// Maybe test functionality should move into this package too?
// An Action represents a single action in the action graph.
type Action struct {
Mode string // description of action operation
Package *load.Package // the package this action works on
Deps []*Action // actions that must happen before this one
Func func(*Builder, context.Context, *Action) error // the action itself (nil = no-op)
IgnoreFail bool // whether to run f even if dependencies fail
TestOutput *bytes.Buffer // test output buffer
Args []string // additional args for runProgram
triggers []*Action // inverse of deps
buggyInstall bool // is this a buggy install (see -linkshared)?
TryCache func(*Builder, *Action) bool // callback for cache bypass
// Generated files, directories.
Objdir string // directory for intermediate objects
Target string // goal of the action: the created package or executable
built string // the actual created package or executable
actionID cache.ActionID // cache ID of action input
buildID string // build ID of action output
VetxOnly bool // Mode=="vet": only being called to supply info about dependencies
needVet bool // Mode=="build": need to fill in vet config
needBuild bool // Mode=="build": need to do actual build (can be false if needVet is true)
vetCfg *vetConfig // vet config
output []byte // output redirect buffer (nil means use b.Print)
// Execution state.
pending int // number of deps yet to complete
priority int // relative execution priority
Failed bool // whether the action failed
json *actionJSON // action graph information
nonGoOverlay map[string]string // map from non-.go source files to copied files in objdir. Nil if no overlay is used.
traceSpan *trace.Span
}
// BuildActionID returns the action ID section of a's build ID.
func (a *Action) BuildActionID() string { return actionID(a.buildID) }
// BuildContentID returns the content ID section of a's build ID.
func (a *Action) BuildContentID() string { return contentID(a.buildID) }
// BuildID returns a's build ID.
func (a *Action) BuildID() string { return a.buildID }
// BuiltTarget returns the actual file that was built. This differs
// from Target when the result was cached.
func (a *Action) BuiltTarget() string { return a.built }
// An actionQueue is a priority queue of actions.
type actionQueue []*Action
// Implement heap.Interface
func (q *actionQueue) Len() int { return len(*q) }
func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority }
func (q *actionQueue) Push(x interface{}) { *q = append(*q, x.(*Action)) }
func (q *actionQueue) Pop() interface{} {
n := len(*q) - 1
x := (*q)[n]
*q = (*q)[:n]
return x
}
func (q *actionQueue) push(a *Action) {
if a.json != nil {
a.json.TimeReady = time.Now()
}
heap.Push(q, a)
}
func (q *actionQueue) pop() *Action {
return heap.Pop(q).(*Action)
}
type actionJSON struct {
ID int
Mode string
Package string
Deps []int `json:",omitempty"`
IgnoreFail bool `json:",omitempty"`
Args []string `json:",omitempty"`
Link bool `json:",omitempty"`
Objdir string `json:",omitempty"`
Target string `json:",omitempty"`
Priority int `json:",omitempty"`
Failed bool `json:",omitempty"`
Built string `json:",omitempty"`
VetxOnly bool `json:",omitempty"`
NeedVet bool `json:",omitempty"`
NeedBuild bool `json:",omitempty"`
ActionID string `json:",omitempty"`
BuildID string `json:",omitempty"`
TimeReady time.Time `json:",omitempty"`
TimeStart time.Time `json:",omitempty"`
TimeDone time.Time `json:",omitempty"`
Cmd []string // `json:",omitempty"`
CmdReal time.Duration `json:",omitempty"`
CmdUser time.Duration `json:",omitempty"`
CmdSys time.Duration `json:",omitempty"`
}
// cacheKey is the key for the action cache.
type cacheKey struct {
mode string
p *load.Package
}
func actionGraphJSON(a *Action) string {
var workq []*Action
var inWorkq = make(map[*Action]int)
add := func(a *Action) {
if _, ok := inWorkq[a]; ok {
return
}
inWorkq[a] = len(workq)
workq = append(workq, a)
}
add(a)
for i := 0; i < len(workq); i++ {
for _, dep := range workq[i].Deps {
add(dep)
}
}
var list []*actionJSON
for id, a := range workq {
if a.json == nil {
a.json = &actionJSON{
Mode: a.Mode,
ID: id,
IgnoreFail: a.IgnoreFail,
Args: a.Args,
Objdir: a.Objdir,
Target: a.Target,
Failed: a.Failed,
Priority: a.priority,
Built: a.built,
VetxOnly: a.VetxOnly,
NeedBuild: a.needBuild,
NeedVet: a.needVet,
}
if a.Package != nil {
// TODO(rsc): Make this a unique key for a.Package somehow.
a.json.Package = a.Package.ImportPath
}
for _, a1 := range a.Deps {
a.json.Deps = append(a.json.Deps, inWorkq[a1])
}
}
list = append(list, a.json)
}
js, err := json.MarshalIndent(list, "", "\t")
if err != nil {
fmt.Fprintf(os.Stderr, "go: writing debug action graph: %v\n", err)
return ""
}
return string(js)
}
// BuildMode specifies the build mode:
// are we just building things or also installing the results?
type BuildMode int
const (
ModeBuild BuildMode = iota
ModeInstall
ModeBuggyInstall
ModeVetOnly = 1 << 8
)
func (b *Builder) Init() {
b.Print = func(a ...interface{}) (int, error) {
return fmt.Fprint(os.Stderr, a...)
}
b.actionCache = make(map[cacheKey]*Action)
b.mkdirCache = make(map[string]bool)
b.toolIDCache = make(map[string]string)
b.buildIDCache = make(map[string]string)
if cfg.BuildN {
b.WorkDir = "$WORK"
} else {
tmp, err := os.MkdirTemp(cfg.Getenv("GOTMPDIR"), "go-build")
if err != nil {
base.Fatalf("go: creating work dir: %v", err)
}
if !filepath.IsAbs(tmp) {
abs, err := filepath.Abs(tmp)
if err != nil {
os.RemoveAll(tmp)
base.Fatalf("go: creating work dir: %v", err)
}
tmp = abs
}
b.WorkDir = tmp
if cfg.BuildX || cfg.BuildWork {
fmt.Fprintf(os.Stderr, "WORK=%s\n", b.WorkDir)
}
if !cfg.BuildWork {
workdir := b.WorkDir
base.AtExit(func() {
start := time.Now()
for {
err := os.RemoveAll(workdir)
if err == nil {
return
}
// On some configurations of Windows, directories containing executable
// files may be locked for a while after the executable exits (perhaps
// due to antivirus scans?). It's probably worth a little extra latency
// on exit to avoid filling up the user's temporary directory with leaked
// files. (See golang.org/issue/30789.)
if runtime.GOOS != "windows" || time.Since(start) >= 500*time.Millisecond {
fmt.Fprintf(os.Stderr, "go: failed to remove work dir: %s\n", err)
return
}
time.Sleep(5 * time.Millisecond)
}
})
}
}
if err := CheckGOOSARCHPair(cfg.Goos, cfg.Goarch); err != nil {
fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
base.SetExitStatus(2)
base.Exit()
}
for _, tag := range cfg.BuildContext.BuildTags {
if strings.Contains(tag, ",") {
fmt.Fprintf(os.Stderr, "cmd/go: -tags space-separated list contains comma\n")
base.SetExitStatus(2)
base.Exit()
}
}
}
func CheckGOOSARCHPair(goos, goarch string) error {
if _, ok := cfg.OSArchSupportsCgo[goos+"/"+goarch]; !ok && cfg.BuildContext.Compiler == "gc" {
return fmt.Errorf("unsupported GOOS/GOARCH pair %s/%s", goos, goarch)
}
return nil
}
// NewObjdir returns the name of a fresh object directory under b.WorkDir.
// It is up to the caller to call b.Mkdir on the result at an appropriate time.
// The result ends in a slash, so that file names in that directory
// can be constructed with direct string addition.
//
// NewObjdir must be called only from a single goroutine at a time,
// so it is safe to call during action graph construction, but it must not
// be called during action graph execution.
func (b *Builder) NewObjdir() string {
b.objdirSeq++
return filepath.Join(b.WorkDir, fmt.Sprintf("b%03d", b.objdirSeq)) + string(filepath.Separator)
}
// readpkglist returns the list of packages that were built into the shared library
// at shlibpath. For the native toolchain this list is stored, newline separated, in
// an ELF note with name "Go\x00\x00" and type 1. For GCCGO it is extracted from the
// .go_export section.
func readpkglist(shlibpath string) (pkgs []*load.Package) {
var stk load.ImportStack
if cfg.BuildToolchainName == "gccgo" {
f, _ := elf.Open(shlibpath)
sect := f.Section(".go_export")
data, _ := sect.Data()
scanner := bufio.NewScanner(bytes.NewBuffer(data))
for scanner.Scan() {
t := scanner.Text()
if strings.HasPrefix(t, "pkgpath ") {
t = strings.TrimPrefix(t, "pkgpath ")
t = strings.TrimSuffix(t, ";")
pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd, nil, &stk, nil, 0))
}
}
} else {
pkglistbytes, err := buildid.ReadELFNote(shlibpath, "Go\x00\x00", 1)
if err != nil {
base.Fatalf("readELFNote failed: %v", err)
}
scanner := bufio.NewScanner(bytes.NewBuffer(pkglistbytes))
for scanner.Scan() {
t := scanner.Text()
pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd, nil, &stk, nil, 0))
}
}
return
}
// cacheAction looks up {mode, p} in the cache and returns the resulting action.
// If the cache has no such action, f() is recorded and returned.
// TODO(rsc): Change the second key from *load.Package to interface{},
// to make the caching in linkShared less awkward?
func (b *Builder) cacheAction(mode string, p *load.Package, f func() *Action) *Action {
a := b.actionCache[cacheKey{mode, p}]
if a == nil {
a = f()
b.actionCache[cacheKey{mode, p}] = a
}
return a
}
// AutoAction returns the "right" action for go build or go install of p.
func (b *Builder) AutoAction(mode, depMode BuildMode, p *load.Package) *Action {
if p.Name == "main" {
return b.LinkAction(mode, depMode, p)
}
return b.CompileAction(mode, depMode, p)
}
// CompileAction returns the action for compiling and possibly installing
// (according to mode) the given package. The resulting action is only
// for building packages (archives), never for linking executables.
// depMode is the action (build or install) to use when building dependencies.
// To turn package main into an executable, call b.Link instead.
func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Action {
vetOnly := mode&ModeVetOnly != 0
mode &^= ModeVetOnly
if mode != ModeBuild && (p.Internal.Local || p.Module != nil) && p.Target == "" {
// Imported via local path or using modules. No permanent target.
mode = ModeBuild
}
if mode != ModeBuild && p.Name == "main" {
// We never install the .a file for a main package.
mode = ModeBuild
}
// Construct package build action.
a := b.cacheAction("build", p, func() *Action {
a := &Action{
Mode: "build",
Package: p,
Func: (*Builder).build,
Objdir: b.NewObjdir(),
}
if p.Error == nil || !p.Error.IsImportCycle {
for _, p1 := range p.Internal.Imports {
a.Deps = append(a.Deps, b.CompileAction(depMode, depMode, p1))
}
}
if p.Standard {
switch p.ImportPath {
case "builtin", "unsafe":
// Fake packages - nothing to build.
a.Mode = "built-in package"
a.Func = nil
return a
}
// gccgo standard library is "fake" too.
if cfg.BuildToolchainName == "gccgo" {
// the target name is needed for cgo.
a.Mode = "gccgo stdlib"
a.Target = p.Target
a.Func = nil
return a
}
}
return a
})
// Find the build action; the cache entry may have been replaced
// by the install action during (*Builder).installAction.
buildAction := a
switch buildAction.Mode {
case "build", "built-in package", "gccgo stdlib":
// ok
case "build-install":
buildAction = a.Deps[0]
default:
panic("lost build action: " + buildAction.Mode)
}
buildAction.needBuild = buildAction.needBuild || !vetOnly
// Construct install action.
if mode == ModeInstall || mode == ModeBuggyInstall {
a = b.installAction(a, mode)
}
return a
}
// VetAction returns the action for running go vet on package p.
// It depends on the action for compiling p.
// If the caller may be causing p to be installed, it is up to the caller
// to make sure that the install depends on (runs after) vet.
func (b *Builder) VetAction(mode, depMode BuildMode, p *load.Package) *Action {
a := b.vetAction(mode, depMode, p)
a.VetxOnly = false
return a
}
func (b *Builder) vetAction(mode, depMode BuildMode, p *load.Package) *Action {
// Construct vet action.
a := b.cacheAction("vet", p, func() *Action {
a1 := b.CompileAction(mode|ModeVetOnly, depMode, p)
// vet expects to be able to import "fmt".
var stk load.ImportStack
stk.Push("vet")
p1 := load.LoadImportWithFlags("fmt", p.Dir, p, &stk, nil, 0)
stk.Pop()
aFmt := b.CompileAction(ModeBuild, depMode, p1)
var deps []*Action
if a1.buggyInstall {
// (*Builder).vet expects deps[0] to be the package
// and deps[1] to be "fmt". If we see buggyInstall
// here then a1 is an install of a shared library,
// and the real package is a1.Deps[0].
deps = []*Action{a1.Deps[0], aFmt, a1}
} else {
deps = []*Action{a1, aFmt}
}
for _, p1 := range p.Internal.Imports {
deps = append(deps, b.vetAction(mode, depMode, p1))
}
a := &Action{
Mode: "vet",
Package: p,
Deps: deps,
Objdir: a1.Objdir,
VetxOnly: true,
IgnoreFail: true, // it's OK if vet of dependencies "fails" (reports problems)
}
if a1.Func == nil {
// Built-in packages like unsafe.
return a
}
deps[0].needVet = true
a.Func = (*Builder).vet
return a
})
return a
}
// LinkAction returns the action for linking p into an executable
// and possibly installing the result (according to mode).
// depMode is the action (build or install) to use when compiling dependencies.
func (b *Builder) LinkAction(mode, depMode BuildMode, p *load.Package) *Action {
// Construct link action.
a := b.cacheAction("link", p, func() *Action {
a := &Action{
Mode: "link",
Package: p,
}
a1 := b.CompileAction(ModeBuild, depMode, p)
a.Func = (*Builder).link
a.Deps = []*Action{a1}
a.Objdir = a1.Objdir
// An executable file. (This is the name of a temporary file.)
// Because we run the temporary file in 'go run' and 'go test',
// the name will show up in ps listings. If the caller has specified
// a name, use that instead of a.out. The binary is generated
// in an otherwise empty subdirectory named exe to avoid
// naming conflicts. The only possible conflict is if we were
// to create a top-level package named exe.
name := "a.out"
if p.Internal.ExeName != "" {
name = p.Internal.ExeName
} else if (cfg.Goos == "darwin" || cfg.Goos == "windows") && cfg.BuildBuildmode == "c-shared" && p.Target != "" {
// On OS X, the linker output name gets recorded in the
// shared library's LC_ID_DYLIB load command.
// The code invoking the linker knows to pass only the final
// path element. Arrange that the path element matches what
// we'll install it as; otherwise the library is only loadable as "a.out".
// On Windows, DLL file name is recorded in PE file
// export section, so do like on OS X.
_, name = filepath.Split(p.Target)
}
a.Target = a.Objdir + filepath.Join("exe", name) + cfg.ExeSuffix
a.built = a.Target
b.addTransitiveLinkDeps(a, a1, "")
// Sequence the build of the main package (a1) strictly after the build
// of all other dependencies that go into the link. It is likely to be after
// them anyway, but just make sure. This is required by the build ID-based
// shortcut in (*Builder).useCache(a1), which will call b.linkActionID(a).
// In order for that linkActionID call to compute the right action ID, all the
// dependencies of a (except a1) must have completed building and have
// recorded their build IDs.
a1.Deps = append(a1.Deps, &Action{Mode: "nop", Deps: a.Deps[1:]})
return a
})
if mode == ModeInstall || mode == ModeBuggyInstall {
a = b.installAction(a, mode)
}
return a
}
// installAction returns the action for installing the result of a1.
func (b *Builder) installAction(a1 *Action, mode BuildMode) *Action {
// Because we overwrite the build action with the install action below,
// a1 may already be an install action fetched from the "build" cache key,
// and the caller just doesn't realize.
if strings.HasSuffix(a1.Mode, "-install") {
if a1.buggyInstall && mode == ModeInstall {
// Congratulations! The buggy install is now a proper install.
a1.buggyInstall = false
}
return a1
}
// If there's no actual action to build a1,
// there's nothing to install either.
// This happens if a1 corresponds to reusing an already-built object.
if a1.Func == nil {
return a1
}
p := a1.Package
return b.cacheAction(a1.Mode+"-install", p, func() *Action {
// The install deletes the temporary build result,
// so we need all other actions, both past and future,
// that attempt to depend on the build to depend instead
// on the install.
// Make a private copy of a1 (the build action),
// no longer accessible to any other rules.
buildAction := new(Action)
*buildAction = *a1
// Overwrite a1 with the install action.
// This takes care of updating past actions that
// point at a1 for the build action; now they will
// point at a1 and get the install action.
// We also leave a1 in the action cache as the result
// for "build", so that actions not yet created that
// try to depend on the build will instead depend
// on the install.
*a1 = Action{
Mode: buildAction.Mode + "-install",
Func: BuildInstallFunc,
Package: p,
Objdir: buildAction.Objdir,
Deps: []*Action{buildAction},
Target: p.Target,
built: p.Target,
buggyInstall: mode == ModeBuggyInstall,
}
b.addInstallHeaderAction(a1)
return a1
})
}
// addTransitiveLinkDeps adds to the link action a all packages
// that are transitive dependencies of a1.Deps.
// That is, if a is a link of package main, a1 is the compile of package main
// and a1.Deps is the actions for building packages directly imported by
// package main (what the compiler needs). The linker needs all packages
// transitively imported by the whole program; addTransitiveLinkDeps
// makes sure those are present in a.Deps.
// If shlib is non-empty, then a corresponds to the build and installation of shlib,
// so any rebuild of shlib should not be added as a dependency.
func (b *Builder) addTransitiveLinkDeps(a, a1 *Action, shlib string) {
// Expand Deps to include all built packages, for the linker.
// Use breadth-first search to find rebuilt-for-test packages
// before the standard ones.
// TODO(rsc): Eliminate the standard ones from the action graph,
// which will require doing a little bit more rebuilding.
workq := []*Action{a1}
haveDep := map[string]bool{}
if a1.Package != nil {
haveDep[a1.Package.ImportPath] = true
}
for i := 0; i < len(workq); i++ {
a1 := workq[i]
for _, a2 := range a1.Deps {
// TODO(rsc): Find a better discriminator than the Mode strings, once the dust settles.
if a2.Package == nil || (a2.Mode != "build-install" && a2.Mode != "build") || haveDep[a2.Package.ImportPath] {
continue
}
haveDep[a2.Package.ImportPath] = true
a.Deps = append(a.Deps, a2)
if a2.Mode == "build-install" {
a2 = a2.Deps[0] // walk children of "build" action
}
workq = append(workq, a2)
}
}
// If this is go build -linkshared, then the link depends on the shared libraries
// in addition to the packages themselves. (The compile steps do not.)
if cfg.BuildLinkshared {
haveShlib := map[string]bool{shlib: true}
for _, a1 := range a.Deps {
p1 := a1.Package
if p1 == nil || p1.Shlib == "" || haveShlib[filepath.Base(p1.Shlib)] {
continue
}
haveShlib[filepath.Base(p1.Shlib)] = true
// TODO(rsc): The use of ModeInstall here is suspect, but if we only do ModeBuild,
// we'll end up building an overall library or executable that depends at runtime
// on other libraries that are out-of-date, which is clearly not good either.
// We call it ModeBuggyInstall to make clear that this is not right.
a.Deps = append(a.Deps, b.linkSharedAction(ModeBuggyInstall, ModeBuggyInstall, p1.Shlib, nil))
}
}
}
// addInstallHeaderAction adds an install header action to a, if needed.
// The action a should be an install action as generated by either
// b.CompileAction or b.LinkAction with mode=ModeInstall,
// and so a.Deps[0] is the corresponding build action.
func (b *Builder) addInstallHeaderAction(a *Action) {
// Install header for cgo in c-archive and c-shared modes.
p := a.Package
if p.UsesCgo() && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") {
hdrTarget := a.Target[:len(a.Target)-len(filepath.Ext(a.Target))] + ".h"
if cfg.BuildContext.Compiler == "gccgo" && cfg.BuildO == "" {
// For the header file, remove the "lib"
// added by go/build, so we generate pkg.h
// rather than libpkg.h.
dir, file := filepath.Split(hdrTarget)
file = strings.TrimPrefix(file, "lib")
hdrTarget = filepath.Join(dir, file)
}
ah := &Action{
Mode: "install header",
Package: a.Package,
Deps: []*Action{a.Deps[0]},
Func: (*Builder).installHeader,
Objdir: a.Deps[0].Objdir,
Target: hdrTarget,
}
a.Deps = append(a.Deps, ah)
}
}
// buildmodeShared takes the "go build" action a1 into the building of a shared library of a1.Deps.
// That is, the input a1 represents "go build pkgs" and the result represents "go build -buildmode=shared pkgs".
func (b *Builder) buildmodeShared(mode, depMode BuildMode, args []string, pkgs []*load.Package, a1 *Action) *Action {
name, err := libname(args, pkgs)
if err != nil {
base.Fatalf("%v", err)
}
return b.linkSharedAction(mode, depMode, name, a1)
}
// linkSharedAction takes a grouping action a1 corresponding to a list of built packages
// and returns an action that links them together into a shared library with the name shlib.
// If a1 is nil, shlib should be an absolute path to an existing shared library,
// and then linkSharedAction reads that library to find out the package list.
func (b *Builder) linkSharedAction(mode, depMode BuildMode, shlib string, a1 *Action) *Action {
fullShlib := shlib
shlib = filepath.Base(shlib)
a := b.cacheAction("build-shlib "+shlib, nil, func() *Action {
if a1 == nil {
// TODO(rsc): Need to find some other place to store config,
// not in pkg directory. See golang.org/issue/22196.
pkgs := readpkglist(fullShlib)
a1 = &Action{
Mode: "shlib packages",
}
for _, p := range pkgs {
a1.Deps = append(a1.Deps, b.CompileAction(mode, depMode, p))
}
}
// Fake package to hold ldflags.
// As usual shared libraries are a kludgy, abstraction-violating special case:
// we let them use the flags specified for the command-line arguments.
p := &load.Package{}
p.Internal.CmdlinePkg = true
p.Internal.Ldflags = load.BuildLdflags.For(p)
p.Internal.Gccgoflags = load.BuildGccgoflags.For(p)
// Add implicit dependencies to pkgs list.
// Currently buildmode=shared forces external linking mode, and
// external linking mode forces an import of runtime/cgo (and
// math on arm). So if it was not passed on the command line and
// it is not present in another shared library, add it here.
// TODO(rsc): Maybe this should only happen if "runtime" is in the original package set.
// TODO(rsc): This should probably be changed to use load.LinkerDeps(p).
// TODO(rsc): We don't add standard library imports for gccgo
// because they are all always linked in anyhow.
// Maybe load.LinkerDeps should be used and updated.
a := &Action{
Mode: "go build -buildmode=shared",
Package: p,
Objdir: b.NewObjdir(),
Func: (*Builder).linkShared,
Deps: []*Action{a1},
}
a.Target = filepath.Join(a.Objdir, shlib)
if cfg.BuildToolchainName != "gccgo" {
add := func(a1 *Action, pkg string, force bool) {
for _, a2 := range a1.Deps {
if a2.Package != nil && a2.Package.ImportPath == pkg {
return
}
}
var stk load.ImportStack
p := load.LoadImportWithFlags(pkg, base.Cwd, nil, &stk, nil, 0)
if p.Error != nil {
base.Fatalf("load %s: %v", pkg, p.Error)
}
// Assume that if pkg (runtime/cgo or math)
// is already accounted for in a different shared library,
// then that shared library also contains runtime,
// so that anything we do will depend on that library,
// so we don't need to include pkg in our shared library.
if force || p.Shlib == "" || filepath.Base(p.Shlib) == pkg {
a1.Deps = append(a1.Deps, b.CompileAction(depMode, depMode, p))
}
}
add(a1, "runtime/cgo", false)
if cfg.Goarch == "arm" {
add(a1, "math", false)
}
// The linker step still needs all the usual linker deps.
// (For example, the linker always opens runtime.a.)
for _, dep := range load.LinkerDeps(nil) {
add(a, dep, true)
}
}
b.addTransitiveLinkDeps(a, a1, shlib)
return a
})
// Install result.
if (mode == ModeInstall || mode == ModeBuggyInstall) && a.Func != nil {
buildAction := a
a = b.cacheAction("install-shlib "+shlib, nil, func() *Action {
// Determine the eventual install target.
// The install target is root/pkg/shlib, where root is the source root
// in which all the packages lie.
// TODO(rsc): Perhaps this cross-root check should apply to the full
// transitive package dependency list, not just the ones named
// on the command line?
pkgDir := a1.Deps[0].Package.Internal.Build.PkgTargetRoot
for _, a2 := range a1.Deps {
if dir := a2.Package.Internal.Build.PkgTargetRoot; dir != pkgDir {
base.Fatalf("installing shared library: cannot use packages %s and %s from different roots %s and %s",
a1.Deps[0].Package.ImportPath,
a2.Package.ImportPath,
pkgDir,
dir)
}
}
// TODO(rsc): Find out and explain here why gccgo is different.
if cfg.BuildToolchainName == "gccgo" {
pkgDir = filepath.Join(pkgDir, "shlibs")
}
target := filepath.Join(pkgDir, shlib)
a := &Action{
Mode: "go install -buildmode=shared",
Objdir: buildAction.Objdir,
Func: BuildInstallFunc,
Deps: []*Action{buildAction},
Target: target,
}
for _, a2 := range buildAction.Deps[0].Deps {
p := a2.Package
if p.Target == "" {
continue
}
a.Deps = append(a.Deps, &Action{
Mode: "shlibname",
Package: p,
Func: (*Builder).installShlibname,
Target: strings.TrimSuffix(p.Target, ".a") + ".shlibname",
Deps: []*Action{a.Deps[0]},
})
}
return a
})
}
return a
}