Skip to content

Commit 9a9780a

Browse files
committed
cmd/go: separate compile and link steps in action graph
To the extent that invoking the compiler and invoking the linker have different dependency requirements, representing both steps by a single action node leads to confusion. If we move to having separate .a and .x (import metadata) files in the standard builds, then the .a is a link dependency but not a compile dependency, and vice versa for .x. Today, in shared library builds, the .a is a compile dependency and a link dependency, while the .so is only a link dependency. Also in this CL: change the gccgo link step to extract _cgo_flags into root.Objdir, which is private to the link step, instead of into b.WorkDir, which is shared by all the link steps that could possibly be running in parallel. And attempt to handle the -n and -x flags when loading _cgo_flags, instead of dying attempting to read an archive that wasn't written. Also in this CL: create a.Objdir before running a.Func, to avoid duplicating the Mkdir(a.Objdir) in every a.Func. A future CL will update the link action's Deps to be accurate. (Right now the link steps search out the true Deps by walking the entire action graph.) Change-Id: I15128ce2bd064887f98abc3a4cf204241f518631 Reviewed-on: https://go-review.googlesource.com/69830 Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: David Crawshaw <[email protected]>
1 parent 0c6fc0d commit 9a9780a

File tree

3 files changed

+67
-64
lines changed

3 files changed

+67
-64
lines changed

src/cmd/go/go_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,10 +729,10 @@ func TestBuildComplex(t *testing.T) {
729729
defer tg.cleanup()
730730
tg.parallel()
731731
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
732-
tg.run("build", "-o", os.DevNull, "complex")
732+
tg.run("build", "-x", "-o", os.DevNull, "complex")
733733

734734
if _, err := exec.LookPath("gccgo"); err == nil {
735-
tg.run("build", "-o", os.DevNull, "-compiler=gccgo", "complex")
735+
tg.run("build", "-x", "-o", os.DevNull, "-compiler=gccgo", "complex")
736736
}
737737
}
738738

src/cmd/go/internal/test/test.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -896,11 +896,6 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
896896

897897
load.ComputeStale(pmain)
898898

899-
if ptest != p {
900-
a := b.Action(work.ModeBuild, work.ModeBuild, ptest)
901-
a.Link = false
902-
}
903-
904899
a := b.Action(work.ModeBuild, work.ModeBuild, pmain)
905900
a.Target = testDir + testBinary + cfg.ExeSuffix
906901
if cfg.Goos == "windows" {

src/cmd/go/internal/work/build.go

Lines changed: 65 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,6 @@ type Action struct {
691691
triggers []*Action // inverse of deps
692692

693693
// Generated files, directories.
694-
Link bool // target is executable, not just package
695694
Objdir string // directory for intermediate objects
696695
Target string // goal of the action: the created package or executable
697696

@@ -749,7 +748,6 @@ func actionGraphJSON(a *Action) string {
749748
ID: id,
750749
IgnoreFail: a.IgnoreFail,
751750
Args: a.Args,
752-
Link: a.Link,
753751
Objdir: a.Objdir,
754752
Target: a.Target,
755753
Failed: a.Failed,
@@ -955,7 +953,7 @@ func (b *Builder) action1(mode BuildMode, depMode BuildMode, p *load.Package, lo
955953
mode = ModeBuild
956954
}
957955
a.Objdir = b.NewObjdir()
958-
a.Link = p.Name == "main" && !p.Internal.ForceLibrary
956+
link := p.Name == "main" && !p.Internal.ForceLibrary
959957

960958
switch mode {
961959
case ModeInstall:
@@ -989,7 +987,16 @@ func (b *Builder) action1(mode BuildMode, depMode BuildMode, p *load.Package, lo
989987
a.Func = (*Builder).build
990988
a.Target = a.Objdir + "_pkg_.a"
991989
a.Package.Internal.Pkgfile = a.Target
992-
if a.Link {
990+
991+
if link {
992+
a = &Action{
993+
Mode: "link",
994+
Func: (*Builder).link,
995+
Package: a.Package,
996+
Objdir: a.Objdir,
997+
Deps: []*Action{a},
998+
}
999+
9931000
// An executable file. (This is the name of a temporary file.)
9941001
// Because we run the temporary file in 'go run' and 'go test',
9951002
// the name will show up in ps listings. If the caller has specified
@@ -1018,7 +1025,10 @@ func (b *Builder) action1(mode BuildMode, depMode BuildMode, p *load.Package, lo
10181025
}
10191026

10201027
func (b *Builder) libaction(libname string, pkgs []*load.Package, mode, depMode BuildMode) *Action {
1021-
a := &Action{Mode: "libaction???"}
1028+
a := &Action{
1029+
Mode: "libaction", // should be overwritten below
1030+
Objdir: b.NewObjdir(),
1031+
}
10221032
switch mode {
10231033
default:
10241034
base.Fatalf("unrecognized mode %v", mode)
@@ -1201,8 +1211,14 @@ func (b *Builder) Do(root *Action) {
12011211
// any actions that are runnable as a result.
12021212
handle := func(a *Action) {
12031213
var err error
1214+
12041215
if a.Func != nil && (!a.Failed || a.IgnoreFail) {
1205-
err = a.Func(b, a)
1216+
if a.Objdir != "" {
1217+
err = b.Mkdir(a.Objdir)
1218+
}
1219+
if err == nil {
1220+
err = a.Func(b, a)
1221+
}
12061222
}
12071223

12081224
// The actions run in parallel but all the updates to the
@@ -1316,11 +1332,7 @@ func (b *Builder) build(a *Action) (err error) {
13161332
b.Print(a.Package.ImportPath + "\n")
13171333
}
13181334

1319-
// Make build directory.
13201335
objdir := a.Objdir
1321-
if err := b.Mkdir(objdir); err != nil {
1322-
return err
1323-
}
13241336

13251337
// make target directory
13261338
dir, _ := filepath.Split(a.Target)
@@ -1554,22 +1566,32 @@ func (b *Builder) build(a *Action) (err error) {
15541566
}
15551567
}
15561568

1557-
// Link if needed.
1558-
if a.Link {
1559-
importcfg := a.Objdir + "importcfg.link"
1560-
if err := b.writeLinkImportcfg(a, importcfg); err != nil {
1561-
return err
1562-
}
1569+
return nil
1570+
}
15631571

1564-
// The compiler only cares about direct imports, but the
1565-
// linker needs the whole dependency tree.
1566-
all := ActionList(a)
1567-
all = all[:len(all)-1] // drop a
1568-
if err := BuildToolchain.ld(b, a, a.Target, importcfg, all, objpkg, objects); err != nil {
1572+
func (b *Builder) link(a *Action) (err error) {
1573+
importcfg := a.Objdir + "importcfg.link"
1574+
if err := b.writeLinkImportcfg(a, importcfg); err != nil {
1575+
return err
1576+
}
1577+
1578+
// make target directory
1579+
dir, _ := filepath.Split(a.Target)
1580+
if dir != "" {
1581+
if err := b.Mkdir(dir); err != nil {
15691582
return err
15701583
}
15711584
}
15721585

1586+
// The compiler only cares about direct imports, but the
1587+
// linker needs the whole dependency tree.
1588+
all := ActionList(a)
1589+
all = all[:len(all)-1] // drop a
1590+
objpkg := a.Objdir + "_pkg_.a"
1591+
if err := BuildToolchain.ld(b, a, a.Target, importcfg, all, objpkg, nil); err != nil { // TODO: ofiles
1592+
return err
1593+
}
1594+
15731595
return nil
15741596
}
15751597

@@ -1698,7 +1720,7 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
16981720
}()
16991721
a1 := a.Deps[0]
17001722
perm := os.FileMode(0666)
1701-
if a1.Link {
1723+
if a1.Mode == "link" {
17021724
switch cfg.BuildBuildmode {
17031725
case "c-archive", "c-shared", "plugin":
17041726
default:
@@ -2859,7 +2881,7 @@ func (gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string)
28592881
return b.run(p.Dir, p.ImportPath, nil, "ar", "rc", mkAbs(objdir, afile), absOfiles)
28602882
}
28612883

2862-
func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, mainpkg string, ofiles []string, buildmode, desc string) error {
2884+
func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
28632885
// gccgo needs explicit linking with all package dependencies,
28642886
// and all LDFLAGS from cgo dependencies.
28652887
apackagePathsSeen := make(map[string]bool)
@@ -2899,42 +2921,36 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string
28992921
return nil
29002922
}
29012923

2924+
newID := 0
29022925
readAndRemoveCgoFlags := func(archive string) (string, error) {
2903-
newa, err := ioutil.TempFile(b.WorkDir, filepath.Base(archive))
2904-
if err != nil {
2905-
return "", err
2906-
}
2907-
olda, err := os.Open(archive)
2908-
if err != nil {
2909-
return "", err
2910-
}
2911-
_, err = io.Copy(newa, olda)
2912-
if err != nil {
2926+
newID++
2927+
newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
2928+
if err := b.copyFile(root, newArchive, archive, 0666, false); err != nil {
29132929
return "", err
29142930
}
2915-
err = olda.Close()
2916-
if err != nil {
2917-
return "", err
2918-
}
2919-
err = newa.Close()
2920-
if err != nil {
2921-
return "", err
2931+
if cfg.BuildN || cfg.BuildX {
2932+
b.Showcmd("", "ar d %s _cgo_flags", newArchive)
2933+
if cfg.BuildN {
2934+
// TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
2935+
// Either the archive is already built and we can read them out,
2936+
// or we're printing commands to build the archive and can
2937+
// forward the _cgo_flags directly to this step.
2938+
return "", nil
2939+
}
29222940
}
2923-
2924-
newarchive := newa.Name()
2925-
err = b.run(b.WorkDir, desc, nil, "ar", "x", newarchive, "_cgo_flags")
2941+
err := b.run(root.Objdir, desc, nil, "ar", "x", newArchive, "_cgo_flags")
29262942
if err != nil {
29272943
return "", err
29282944
}
2929-
err = b.run(".", desc, nil, "ar", "d", newarchive, "_cgo_flags")
2945+
err = b.run(".", desc, nil, "ar", "d", newArchive, "_cgo_flags")
29302946
if err != nil {
29312947
return "", err
29322948
}
2933-
err = readCgoFlags(filepath.Join(b.WorkDir, "_cgo_flags"))
2949+
err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
29342950
if err != nil {
29352951
return "", err
29362952
}
2937-
return newarchive, nil
2953+
return newArchive, nil
29382954
}
29392955

29402956
actionsSeen := make(map[*Action]bool)
@@ -3018,14 +3034,6 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string
30183034
}
30193035
}
30203036

3021-
for i, o := range ofiles {
3022-
if filepath.Base(o) == "_cgo_flags" {
3023-
readCgoFlags(o)
3024-
ofiles = append(ofiles[:i], ofiles[i+1:]...)
3025-
break
3026-
}
3027-
}
3028-
30293037
ldflags = append(ldflags, "-Wl,--whole-archive")
30303038
ldflags = append(ldflags, afiles...)
30313039
ldflags = append(ldflags, "-Wl,--no-whole-archive")
@@ -3112,7 +3120,7 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string
31123120
}
31133121
}
31143122

3115-
if err := b.run(".", desc, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil {
3123+
if err := b.run(".", desc, nil, tools.linker(), "-o", out, ldflags, buildGccgoflags); err != nil {
31163124
return err
31173125
}
31183126

@@ -3126,13 +3134,13 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string
31263134
}
31273135

31283136
func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg string, allactions []*Action, mainpkg string, ofiles []string) error {
3129-
return tools.link(b, root, out, importcfg, allactions, mainpkg, ofiles, ldBuildmode, root.Package.ImportPath)
3137+
return tools.link(b, root, out, importcfg, allactions, ldBuildmode, root.Package.ImportPath)
31303138
}
31313139

31323140
func (tools gccgoToolchain) ldShared(b *Builder, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
31333141
fakeRoot := &Action{Mode: "gccgo ldshared"}
31343142
fakeRoot.Deps = toplevelactions
3135-
return tools.link(b, fakeRoot, out, importcfg, allactions, "", nil, "shared", out)
3143+
return tools.link(b, fakeRoot, out, importcfg, allactions, "shared", out)
31363144
}
31373145

31383146
func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {

0 commit comments

Comments
 (0)