cmd/compilebench: make it work without installed .a's
Currently compilebench relies on installed .a files for std and
cmd, as it runs the compiler and linker directly. For the upcoming
Go 1.20, compiled .a files will not be installed. Don't rely on
them. Instead, build importcfg file and pass it to the compiler
and the linker.
Verified that this approach still works with previous versions of
Go (1.19 and 1.18).
For golang/go#47257.
Change-Id: Ie0eb9541fb995649e5b68d4481a5acfbdfe8f2a7
Reviewed-on: https://go-review.googlesource.com/c/tools/+/448118
Reviewed-by: Michael Pratt <mpratt@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/cmd/compilebench/main.go b/cmd/compilebench/main.go
index abdf28a..754acdc 100644
--- a/cmd/compilebench/main.go
+++ b/cmd/compilebench/main.go
@@ -335,10 +335,10 @@
func (compile) long() bool { return false }
func (c compile) run(name string, count int) error {
- // Make sure dependencies needed by go tool compile are installed to GOROOT/pkg.
- out, err := exec.Command(*flagGoCmd, "build", "-a", c.dir).CombinedOutput()
+ // Make sure dependencies needed by go tool compile are built.
+ out, err := exec.Command(*flagGoCmd, "build", c.dir).CombinedOutput()
if err != nil {
- return fmt.Errorf("go build -a %s: %v\n%s", c.dir, err, out)
+ return fmt.Errorf("go build %s: %v\n%s", c.dir, err, out)
}
// Find dir and source file list.
@@ -347,6 +347,11 @@
return err
}
+ importcfg, err := genImportcfgFile(c.dir, false)
+ if err != nil {
+ return err
+ }
+
// If this package has assembly files, we'll need to pass a symabis
// file to the compiler; call a helper to invoke the assembler
// to do that.
@@ -371,6 +376,10 @@
if symAbisFile != "" {
args = append(args, "-symabis", symAbisFile)
}
+ if importcfg != "" {
+ args = append(args, "-importcfg", importcfg)
+ defer os.Remove(importcfg)
+ }
args = append(args, pkg.GoFiles...)
if err := runBuildCmd(name, count, pkg.Dir, compiler, args); err != nil {
return err
@@ -406,18 +415,28 @@
}
// Build dependencies.
- out, err := exec.Command(*flagGoCmd, "build", "-a", "-o", "/dev/null", r.dir).CombinedOutput()
+ out, err := exec.Command(*flagGoCmd, "build", "-o", "/dev/null", r.dir).CombinedOutput()
if err != nil {
return fmt.Errorf("go build -a %s: %v\n%s", r.dir, err, out)
}
+ importcfg, err := genImportcfgFile(r.dir, true)
+ if err != nil {
+ return err
+ }
+ defer os.Remove(importcfg)
+
// Build the main package.
pkg, err := goList(r.dir)
if err != nil {
return err
}
- args := []string{"-o", "_compilebench_.o"}
+ args := []string{"-o", "_compilebench_.o", "-importcfg", importcfg}
args = append(args, pkg.GoFiles...)
+ if *flagTrace {
+ fmt.Fprintf(os.Stderr, "running: %s %+v\n",
+ compiler, args)
+ }
cmd := exec.Command(compiler, args...)
cmd.Dir = pkg.Dir
cmd.Stdout = os.Stderr
@@ -429,7 +448,7 @@
defer os.Remove(pkg.Dir + "/_compilebench_.o")
// Link the main package.
- args = []string{"-o", "_compilebench_.exe"}
+ args = []string{"-o", "_compilebench_.exe", "-importcfg", importcfg}
args = append(args, strings.Fields(*flagLinkerFlags)...)
args = append(args, strings.Fields(r.flags)...)
args = append(args, "_compilebench_.o")
@@ -578,3 +597,49 @@
}
return nil
}
+
+// genImportcfgFile generates an importcfg file for building package
+// dir. Returns the generated importcfg file path (or empty string
+// if the package has no dependency).
+func genImportcfgFile(dir string, full bool) (string, error) {
+ need := "{{.Imports}}"
+ if full {
+ // for linking, we need transitive dependencies
+ need = "{{.Deps}}"
+ }
+
+ // find imported/dependent packages
+ cmd := exec.Command(*flagGoCmd, "list", "-f", need, dir)
+ cmd.Stderr = os.Stderr
+ out, err := cmd.Output()
+ if err != nil {
+ return "", fmt.Errorf("go list -f %s %s: %v", need, dir, err)
+ }
+ // trim [ ]\n
+ if len(out) < 3 || out[0] != '[' || out[len(out)-2] != ']' || out[len(out)-1] != '\n' {
+ return "", fmt.Errorf("unexpected output from go list -f %s %s: %s", need, dir, out)
+ }
+ out = out[1 : len(out)-2]
+ if len(out) == 0 {
+ return "", nil
+ }
+
+ // build importcfg for imported packages
+ cmd = exec.Command(*flagGoCmd, "list", "-export", "-f", "{{if .Export}}packagefile {{.ImportPath}}={{.Export}}{{end}}")
+ cmd.Args = append(cmd.Args, strings.Fields(string(out))...)
+ cmd.Stderr = os.Stderr
+ out, err = cmd.Output()
+ if err != nil {
+ return "", fmt.Errorf("generating importcfg for %s: %s: %v", dir, cmd, err)
+ }
+
+ f, err := os.CreateTemp("", "importcfg")
+ if err != nil {
+ return "", fmt.Errorf("creating tmp importcfg file failed: %v", err)
+ }
+ defer f.Close()
+ if _, err := f.Write(out); err != nil {
+ return "", fmt.Errorf("writing importcfg file %s failed: %v", f.Name(), err)
+ }
+ return f.Name(), nil
+}