diff options
| author | Bryan C. Mills <bcmills@google.com> | 2023-02-02 10:42:46 -0500 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2023-02-02 19:27:33 +0000 |
| commit | 4b43d668c2ae42465af7cbad4bc5fa86d0b6cc15 (patch) | |
| tree | cc68055be7392a361312de837006dc8cbeb56c54 /src/internal | |
| parent | 18772915a1b9ca211a4bb707de59ee0941b4773b (diff) | |
| download | go-4b43d668c2ae42465af7cbad4bc5fa86d0b6cc15.tar.xz | |
internal/testenv: avoid rebuilding all of std in WriteImportcfg
Instead, have the caller pass in an explicit list of the packages
(if any) they need.
After #47257, a builder running a test does not necessarily have the
entire standard library already cached, especially when running tests
in sharded mode. testenv.WriteImportcfg used to write an importcfg for
the entire standard library — which required rebuilding the entire
standard library — even though most tests need only a tiny subset.
This reduces the time to test internal/abi with a cold build cache on
my workstation from ~16s to ~0.05s.
It somewhat increases the time for 'go test go/internal/gcimporter'
with a cold cache, from ~43s to ~54s, presumably due to decreased
parallelism in rebuilding the standard library and increased overhead
in re-resolving the import map. However, 'go test -short' running time
remains stable (~5.5s before and after).
Fixes #58248.
Change-Id: I9be6b61ae6e28b75b53af85207c281bb93b9346f
Reviewed-on: https://go-review.googlesource.com/c/go/+/464736
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
Diffstat (limited to 'src/internal')
| -rw-r--r-- | src/internal/abi/abi_test.go | 8 | ||||
| -rw-r--r-- | src/internal/goroot/importcfg.go | 66 | ||||
| -rw-r--r-- | src/internal/testenv/testenv.go | 51 |
3 files changed, 43 insertions, 82 deletions
diff --git a/src/internal/abi/abi_test.go b/src/internal/abi/abi_test.go index f0d8dceb3e..44b9e78a30 100644 --- a/src/internal/abi/abi_test.go +++ b/src/internal/abi/abi_test.go @@ -7,7 +7,6 @@ package abi_test import ( "internal/abi" "internal/testenv" - "os/exec" "path/filepath" "strings" "testing" @@ -42,18 +41,19 @@ func TestFuncPCCompileError(t *testing.T) { symabi := filepath.Join(tmpdir, "symabi") obj := filepath.Join(tmpdir, "x.o") + // Write an importcfg file for the dependencies of the package. importcfgfile := filepath.Join(tmpdir, "hello.importcfg") - testenv.WriteImportcfg(t, importcfgfile, nil) + testenv.WriteImportcfg(t, importcfgfile, nil, "internal/abi") // parse assembly code for symabi. - cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-gensymabis", "-o", symabi, asmSrc) + cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-gensymabis", "-o", symabi, asmSrc) out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("go tool asm -gensymabis failed: %v\n%s", err, out) } // compile go code. - cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-symabis", symabi, "-o", obj, goSrc) + cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-symabis", symabi, "-o", obj, goSrc) out, err = cmd.CombinedOutput() if err == nil { t.Fatalf("go tool compile did not fail") diff --git a/src/internal/goroot/importcfg.go b/src/internal/goroot/importcfg.go deleted file mode 100644 index e324073746..0000000000 --- a/src/internal/goroot/importcfg.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2022 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. - -package goroot - -import ( - "bytes" - "fmt" - "os/exec" - "strings" - "sync" -) - -// Importcfg returns an importcfg file to be passed to the -// Go compiler that contains the cached paths for the .a files for the -// standard library. -func Importcfg() (string, error) { - var icfg bytes.Buffer - - m, err := PkgfileMap() - if err != nil { - return "", err - } - fmt.Fprintf(&icfg, "# import config") - for importPath, export := range m { - fmt.Fprintf(&icfg, "\npackagefile %s=%s", importPath, export) - } - s := icfg.String() - return s, nil -} - -var ( - stdlibPkgfileMap map[string]string - stdlibPkgfileErr error - once sync.Once -) - -// PkgfileMap returns a map of package paths to the location on disk -// of the .a file for the package. -// The caller must not modify the map. -func PkgfileMap() (map[string]string, error) { - once.Do(func() { - m := make(map[string]string) - output, err := exec.Command("go", "list", "-export", "-e", "-f", "{{.ImportPath}} {{.Export}}", "std", "cmd").Output() - if err != nil { - stdlibPkgfileErr = err - } - for _, line := range strings.Split(string(output), "\n") { - if line == "" { - continue - } - sp := strings.SplitN(line, " ", 2) - if len(sp) != 2 { - stdlibPkgfileErr = fmt.Errorf("determining pkgfile map: invalid line in go list output: %q", line) - return - } - importPath, export := sp[0], sp[1] - if export != "" { - m[importPath] = export - } - } - stdlibPkgfileMap = m - }) - return stdlibPkgfileMap, stdlibPkgfileErr -} diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go index 6a28b25278..82fdfb6ff6 100644 --- a/src/internal/testenv/testenv.go +++ b/src/internal/testenv/testenv.go @@ -11,11 +11,11 @@ package testenv import ( + "bytes" "errors" "flag" "fmt" "internal/cfg" - "internal/goroot" "internal/platform" "os" "os/exec" @@ -347,18 +347,45 @@ func SkipIfOptimizationOff(t testing.TB) { } // WriteImportcfg writes an importcfg file used by the compiler or linker to -// dstPath containing entries for the packages in std and cmd in addition -// to the package to package file mappings in additionalPackageFiles. -func WriteImportcfg(t testing.TB, dstPath string, additionalPackageFiles map[string]string) { - importcfg, err := goroot.Importcfg() - for k, v := range additionalPackageFiles { - importcfg += fmt.Sprintf("\npackagefile %s=%s", k, v) +// dstPath containing entries for the file mappings in packageFiles, as well +// as for the packages transitively imported by the package(s) in pkgs. +// +// pkgs may include any package pattern that is valid to pass to 'go list', +// so it may also be a list of Go source files all in the same directory. +func WriteImportcfg(t testing.TB, dstPath string, packageFiles map[string]string, pkgs ...string) { + t.Helper() + + icfg := new(bytes.Buffer) + icfg.WriteString("# import config\n") + for k, v := range packageFiles { + fmt.Fprintf(icfg, "packagefile %s=%s\n", k, v) } - if err != nil { - t.Fatalf("preparing the importcfg failed: %s", err) + + if len(pkgs) > 0 { + // Use 'go list' to resolve any missing packages and rewrite the import map. + cmd := Command(t, GoToolPath(t), "list", "-export", "-deps", "-f", `{{if ne .ImportPath "command-line-arguments"}}{{if .Export}}{{.ImportPath}}={{.Export}}{{end}}{{end}}`) + cmd.Args = append(cmd.Args, pkgs...) + cmd.Stderr = new(strings.Builder) + out, err := cmd.Output() + if err != nil { + t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr) + } + + for _, line := range strings.Split(string(out), "\n") { + if line == "" { + continue + } + importPath, export, ok := strings.Cut(line, "=") + if !ok { + t.Fatalf("invalid line in output from %v:\n%s", cmd, line) + } + if packageFiles[importPath] == "" { + fmt.Fprintf(icfg, "packagefile %s=%s\n", importPath, export) + } + } } - err = os.WriteFile(dstPath, []byte(importcfg), 0655) - if err != nil { - t.Fatalf("writing the importcfg failed: %s", err) + + if err := os.WriteFile(dstPath, icfg.Bytes(), 0666); err != nil { + t.Fatal(err) } } |
