From 7ea0520234aafb47d05bb4822cb7af54c6ac7554 Mon Sep 17 00:00:00 2001 From: Michael Matloob <matloob@golang.org> Date: Wed, 21 Sep 2022 15:51:27 -0400 Subject: [PATCH] go,cmd,internal: update to anticipate missing targets and .a files go/build and cmd/go will stop returing Targets for stdlib .a files, and stop producing the .a files is pkg/GOOS_GOARCH. update tests to anticipate that and to pass in importcfgs instead of expecting the compiler can find .a files in their old locations. Adds code to determine locations of .a files to internal/goroot. Also adds internal/goroot to dist's bootstrap directories and changes internal/goroot to build with a bootstrap version of Go. Change-Id: Ie81e51105bddb3f0e374cbf47e81c23edfb67fa5 Reviewed-on: https://go-review.googlesource.com/c/go/+/442303 Reviewed-by: Michael Matloob <matloob@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Michael Matloob <matloob@golang.org> Reviewed-by: Bryan Mills <bcmills@google.com> --- .../compile/internal/importer/gcimporter.go | 43 ++++++++++-- .../internal/importer/gcimporter_test.go | 26 +++++--- src/cmd/dist/buildtool.go | 1 + src/cmd/internal/archive/archive_test.go | 7 +- src/cmd/link/link_test.go | 36 ++++++---- src/cmd/objdump/objdump_test.go | 5 +- src/cmd/pack/pack_test.go | 17 +++-- src/go/build/deps_test.go | 2 +- src/go/importer/importer_test.go | 9 +-- src/go/internal/gcimporter/gcimporter.go | 42 ++++++++++-- src/go/internal/gcimporter/gcimporter_test.go | 30 +++++---- src/internal/abi/abi_test.go | 5 +- src/internal/goroot/gc.go | 4 +- src/internal/goroot/importcfg.go | 66 +++++++++++++++++++ src/internal/testenv/testenv.go | 18 +++++ src/internal/types/errors/codes_test.go | 3 + src/runtime/align_test.go | 3 + 17 files changed, 256 insertions(+), 61 deletions(-) create mode 100644 src/internal/goroot/importcfg.go diff --git a/src/cmd/compile/internal/importer/gcimporter.go b/src/cmd/compile/internal/importer/gcimporter.go index f6ec6554a85..0aa779441a7 100644 --- a/src/cmd/compile/internal/importer/gcimporter.go +++ b/src/cmd/compile/internal/importer/gcimporter.go @@ -9,9 +9,11 @@ import ( "bufio" "fmt" "go/build" + "internal/goroot" "internal/pkgbits" "io" "os" + "path" "path/filepath" "strings" @@ -21,7 +23,26 @@ import ( // debugging/development support const debug = false -var pkgExts = [...]string{".a", ".o"} +func lookupGorootExport(pkgpath, srcRoot, srcDir string) (string, bool) { + pkgpath = filepath.ToSlash(pkgpath) + m, err := goroot.PkgfileMap() + if err != nil { + return "", false + } + if export, ok := m[pkgpath]; ok { + return export, true + } + vendorPrefix := "vendor" + if strings.HasPrefix(srcDir, filepath.Join(srcRoot, "cmd")) { + vendorPrefix = path.Join("cmd", vendorPrefix) + } + pkgpath = path.Join(vendorPrefix, pkgpath) + fmt.Fprintln(os.Stderr, "looking up ", pkgpath) + export, ok := m[pkgpath] + return export, ok +} + +var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension // FindPkg returns the filename and unique package id for an import // path based on package information provided by build.Import (using @@ -43,11 +64,18 @@ func FindPkg(path, srcDir string) (filename, id string) { } bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) if bp.PkgObj == "" { - id = path // make sure we have an id to print in error message - return + var ok bool + if bp.Goroot { + filename, ok = lookupGorootExport(path, bp.SrcRoot, srcDir) + } + if !ok { + id = path // make sure we have an id to print in error message + return + } + } else { + noext = strings.TrimSuffix(bp.PkgObj, ".a") + id = bp.ImportPath } - noext = strings.TrimSuffix(bp.PkgObj, ".a") - id = bp.ImportPath case build.IsLocalImport(path): // "./x" -> "/this/directory/x.ext", "/this/directory/x" @@ -68,6 +96,11 @@ func FindPkg(path, srcDir string) (filename, id string) { } } + if filename != "" { + if f, err := os.Stat(filename); err == nil && !f.IsDir() { + return + } + } // try extensions for _, ext := range pkgExts { filename = noext + ext diff --git a/src/cmd/compile/internal/importer/gcimporter_test.go b/src/cmd/compile/internal/importer/gcimporter_test.go index 2fbd3f00d24..ac502e6e37e 100644 --- a/src/cmd/compile/internal/importer/gcimporter_test.go +++ b/src/cmd/compile/internal/importer/gcimporter_test.go @@ -13,6 +13,7 @@ import ( "internal/testenv" "os" "os/exec" + "path" "path/filepath" "runtime" "strings" @@ -37,14 +38,19 @@ func skipSpecialPlatforms(t *testing.T) { // compile runs the compiler on filename, with dirname as the working directory, // and writes the output file to outdirname. -func compile(t *testing.T, dirname, filename, outdirname string) string { +// compile gives the resulting package a packagepath of testdata/<filebasename>. +func compile(t *testing.T, dirname, filename, outdirname string, packagefiles map[string]string) string { // filename must end with ".go" - if !strings.HasSuffix(filename, ".go") { + basename, ok := strings.CutSuffix(filepath.Base(filename), ".go") + if !ok { t.Fatalf("filename doesn't end in .go: %s", filename) } - basename := filepath.Base(filename) - outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o") - cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p", strings.TrimSuffix(outname, ".o"), "-o", outname, filename) + objname := basename + ".o" + outname := filepath.Join(outdirname, objname) + importcfgfile := filepath.Join(outdirname, basename) + ".importcfg" + testenv.WriteImportcfg(t, importcfgfile, packagefiles) + pkgpath := path.Join("testdata", basename) + cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p", pkgpath, "-D", "testdata", "-importcfg", importcfgfile, "-o", outname, filename) cmd.Dir = dirname out, err := cmd.CombinedOutput() if err != nil { @@ -129,7 +135,7 @@ func TestImportTestdata(t *testing.T) { tmpdir := mktmpdir(t) defer os.RemoveAll(tmpdir) - compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata")) + compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"), nil) path := "./testdata/" + strings.TrimSuffix(testfile, ".go") if pkg := testPath(t, path, tmpdir); pkg != nil { @@ -436,8 +442,8 @@ func TestIssue13566(t *testing.T) { if err != nil { t.Fatal(err) } - compile(t, "testdata", "a.go", testoutdir) - compile(t, testoutdir, bpath, testoutdir) + compile(t, "testdata", "a.go", testoutdir, nil) + compile(t, testoutdir, bpath, testoutdir, map[string]string{"testdata/a": filepath.Join(testoutdir, "a.o")}) // import must succeed (test for issue at hand) pkg := importPkg(t, "./testdata/b", tmpdir) @@ -513,7 +519,7 @@ func TestIssue15517(t *testing.T) { tmpdir := mktmpdir(t) defer os.RemoveAll(tmpdir) - compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata")) + compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"), nil) // Multiple imports of p must succeed without redeclaration errors. // We use an import path that's not cleaned up so that the eventual @@ -618,7 +624,7 @@ func importPkg(t *testing.T, path, srcDir string) *types2.Package { func compileAndImportPkg(t *testing.T, name string) *types2.Package { tmpdir := mktmpdir(t) defer os.RemoveAll(tmpdir) - compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata")) + compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"), nil) return importPkg(t, "./testdata/"+name, tmpdir) } diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index e2699f7656b..2a4490b201a 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -63,6 +63,7 @@ var bootstrapDirs = []string{ "internal/coverage", "internal/buildcfg", "internal/goexperiment", + "internal/goroot", "internal/goversion", "internal/pkgbits", "internal/race", diff --git a/src/cmd/internal/archive/archive_test.go b/src/cmd/internal/archive/archive_test.go index cca65af8ed0..f5315856c15 100644 --- a/src/cmd/internal/archive/archive_test.go +++ b/src/cmd/internal/archive/archive_test.go @@ -113,11 +113,14 @@ func buildGoobj(t *testing.T) goobjPaths { go1src := filepath.Join("testdata", "go1.go") go2src := filepath.Join("testdata", "go2.go") - out, err := exec.Command(gotool, "tool", "compile", "-p=p", "-o", go1obj, go1src).CombinedOutput() + importcfgfile := filepath.Join(buildDir, "importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + + out, err := exec.Command(gotool, "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", go1obj, go1src).CombinedOutput() if err != nil { return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go1obj, go1src, err, out) } - out, err = exec.Command(gotool, "tool", "compile", "-p=p", "-o", go2obj, go2src).CombinedOutput() + out, err = exec.Command(gotool, "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", go2obj, go2src).CombinedOutput() if err != nil { return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go2obj, go2src, err, out) } diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index 35babe61fc0..ce065721649 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -50,19 +50,22 @@ func main() {} tmpdir := t.TempDir() + importcfgfile := filepath.Join(tmpdir, "importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + err := os.WriteFile(filepath.Join(tmpdir, "main.go"), []byte(source), 0666) if err != nil { t.Fatalf("failed to write main.go: %v\n", err) } - cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=main", "main.go") + cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go") cmd.Dir = tmpdir out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out) } - cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "main.o") + cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "main.o") cmd.Dir = tmpdir out, err = cmd.CombinedOutput() if err != nil { @@ -98,9 +101,12 @@ func TestIssue28429(t *testing.T) { } } + importcfgfile := filepath.Join(tmpdir, "importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + // Compile a main package. write("main.go", "package main; func main() {}") - runGo("tool", "compile", "-p=main", "main.go") + runGo("tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go") runGo("tool", "pack", "c", "main.a", "main.o") // Add an extra section with a short, non-.o name. @@ -110,7 +116,7 @@ func TestIssue28429(t *testing.T) { // Verify that the linker does not attempt // to compile the extra section. - runGo("tool", "link", "main.a") + runGo("tool", "link", "-importcfg="+importcfgfile, "main.a") } func TestUnresolved(t *testing.T) { @@ -236,15 +242,18 @@ void foo() { cc := strings.TrimSpace(runGo("env", "CC")) cflags := strings.Fields(runGo("env", "GOGCCFLAGS")) + importcfgfile := filepath.Join(tmpdir, "importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + // Compile, assemble and pack the Go and C code. runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s") - runGo("tool", "compile", "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go") + runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go") runGo("tool", "asm", "-p=main", "-o", "x2.o", "x.s") run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...) runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o") // Now attempt to link using the internal linker. - cmd := exec.Command(testenv.GoToolPath(t), "tool", "link", "-linkmode=internal", "x.a") + cmd := exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-linkmode=internal", "x.a") cmd.Dir = tmpdir out, err := cmd.CombinedOutput() if err == nil { @@ -778,20 +787,25 @@ func TestIndexMismatch(t *testing.T) { mObj := filepath.Join(tmpdir, "main.o") exe := filepath.Join(tmpdir, "main.exe") + importcfgFile := filepath.Join(tmpdir, "stdlib.importcfg") + testenv.WriteImportcfg(t, importcfgFile, nil) + importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg") + testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}) + // Build a program with main package importing package a. - cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=a", "-o", aObj, aSrc) + cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc) t.Log(cmd) out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("compiling a.go failed: %v\n%s", err, out) } - cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=main", "-I", tmpdir, "-o", mObj, mSrc) + cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc) t.Log(cmd) out, err = cmd.CombinedOutput() if err != nil { t.Fatalf("compiling main.go failed: %v\n%s", err, out) } - cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj) + cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj) t.Log(cmd) out, err = cmd.CombinedOutput() if err != nil { @@ -800,13 +814,13 @@ func TestIndexMismatch(t *testing.T) { // Now, overwrite a.o with the object of b.go. This should // result in an index mismatch. - cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=a", "-o", aObj, bSrc) + cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc) t.Log(cmd) out, err = cmd.CombinedOutput() if err != nil { t.Fatalf("compiling a.go failed: %v\n%s", err, out) } - cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj) + cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj) t.Log(cmd) out, err = cmd.CombinedOutput() if err == nil { diff --git a/src/cmd/objdump/objdump_test.go b/src/cmd/objdump/objdump_test.go index bbf942503a2..fa6a1b9a68c 100644 --- a/src/cmd/objdump/objdump_test.go +++ b/src/cmd/objdump/objdump_test.go @@ -297,8 +297,11 @@ func TestDisasmPIE(t *testing.T) { func TestDisasmGoobj(t *testing.T) { mustHaveDisasm(t) + importcfgfile := filepath.Join(tmp, "hello.importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + hello := filepath.Join(tmp, "hello.o") - args := []string{"tool", "compile", "-p=main", "-o", hello} + args := []string{"tool", "compile", "-p=main", "-importcfg=" + importcfgfile, "-o", hello} args = append(args, "testdata/fmthello.go") out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput() if err != nil { diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go index 146c27c00a4..5f16abcb02e 100644 --- a/src/cmd/pack/pack_test.go +++ b/src/cmd/pack/pack_test.go @@ -177,11 +177,14 @@ func TestHello(t *testing.T) { return doRun(t, dir, args...) } + importcfgfile := filepath.Join(dir, "hello.importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + goBin := testenv.GoToolPath(t) run(goBin, "build", "cmd/pack") // writes pack binary to dir - run(goBin, "tool", "compile", "-p=main", "hello.go") + run(goBin, "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "hello.go") run("./pack", "grc", "hello.a", "hello.o") - run(goBin, "tool", "link", "-o", "a.out", "hello.a") + run(goBin, "tool", "link", "-importcfg="+importcfgfile, "-o", "a.out", "hello.a") out := run("./a.out") if out != "hello world\n" { t.Fatalf("incorrect output: %q, want %q", out, "hello world\n") @@ -244,12 +247,16 @@ func TestLargeDefs(t *testing.T) { return doRun(t, dir, args...) } + importcfgfile := filepath.Join(dir, "hello.importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + goBin := testenv.GoToolPath(t) run(goBin, "build", "cmd/pack") // writes pack binary to dir - run(goBin, "tool", "compile", "-p=large", "large.go") + run(goBin, "tool", "compile", "-importcfg="+importcfgfile, "-p=large", "large.go") run("./pack", "grc", "large.a", "large.o") - run(goBin, "tool", "compile", "-p=main", "-I", ".", "main.go") - run(goBin, "tool", "link", "-L", ".", "-o", "a.out", "main.o") + testenv.WriteImportcfg(t, importcfgfile, map[string]string{"large": filepath.Join(dir, "large.o")}) + run(goBin, "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go") + run(goBin, "tool", "link", "-importcfg="+importcfgfile, "-L", ".", "-o", "a.out", "main.o") out := run("./a.out") if out != "ok\n" { t.Fatalf("incorrect output: %q, want %q", out, "ok\n") diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index eed5c462be2..25556ac04c6 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -535,7 +535,7 @@ var depsRules = ` internal/fuzz, internal/testlog, runtime/pprof, regexp < testing/internal/testdeps; - OS, flag, testing, internal/cfg, internal/platform + OS, flag, testing, internal/cfg, internal/platform, internal/goroot < internal/testenv; OS, encoding/base64 diff --git a/src/go/importer/importer_test.go b/src/go/importer/importer_test.go index 1b8353e8fa6..3c391380383 100644 --- a/src/go/importer/importer_test.go +++ b/src/go/importer/importer_test.go @@ -25,15 +25,12 @@ func TestForCompiler(t *testing.T) { testenv.MustHaveGoBuild(t) const thePackage = "math/big" - out, err := exec.Command(testenv.GoToolPath(t), "list", "-f={{context.Compiler}}:{{.Target}}", thePackage).CombinedOutput() + out, err := exec.Command(testenv.GoToolPath(t), "list", "-export", "-f={{context.Compiler}}:{{.Export}}", thePackage).CombinedOutput() if err != nil { t.Fatalf("go list %s: %v\n%s", thePackage, err, out) } - target := strings.TrimSpace(string(out)) - compiler, target, _ := strings.Cut(target, ":") - if !strings.HasSuffix(target, ".a") { - t.Fatalf("unexpected package %s target %q (not *.a)", thePackage, target) - } + export := strings.TrimSpace(string(out)) + compiler, target, _ := strings.Cut(export, ":") if compiler == "gccgo" { t.Skip("golang.org/issue/22500") diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go index 0b27a954041..0ec464056c0 100644 --- a/src/go/internal/gcimporter/gcimporter.go +++ b/src/go/internal/gcimporter/gcimporter.go @@ -11,9 +11,11 @@ import ( "go/build" "go/token" "go/types" + "internal/goroot" "internal/pkgbits" "io" "os" + "path" "path/filepath" "strings" ) @@ -21,7 +23,25 @@ import ( // debugging/development support const debug = false -var pkgExts = [...]string{".a", ".o"} +func lookupGorootExport(pkgpath, srcRoot, srcDir string) (string, bool) { + pkgpath = filepath.ToSlash(pkgpath) + m, err := goroot.PkgfileMap() + if err != nil { + return "", false + } + if export, ok := m[pkgpath]; ok { + return export, true + } + vendorPrefix := "vendor" + if strings.HasPrefix(srcDir, filepath.Join(srcRoot, "cmd")) { + vendorPrefix = path.Join("cmd", vendorPrefix) + } + pkgpath = path.Join(vendorPrefix, pkgpath) + export, ok := m[pkgpath] + return export, ok +} + +var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension // FindPkg returns the filename and unique package id for an import // path based on package information provided by build.Import (using @@ -43,11 +63,18 @@ func FindPkg(path, srcDir string) (filename, id string) { } bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) if bp.PkgObj == "" { - id = path // make sure we have an id to print in error message - return + var ok bool + if bp.Goroot { + filename, ok = lookupGorootExport(path, bp.SrcRoot, srcDir) + } + if !ok { + id = path // make sure we have an id to print in error message + return + } + } else { + noext = strings.TrimSuffix(bp.PkgObj, ".a") + id = bp.ImportPath } - noext = strings.TrimSuffix(bp.PkgObj, ".a") - id = bp.ImportPath case build.IsLocalImport(path): // "./x" -> "/this/directory/x.ext", "/this/directory/x" @@ -68,6 +95,11 @@ func FindPkg(path, srcDir string) (filename, id string) { } } + if filename != "" { + if f, err := os.Stat(filename); err == nil && !f.IsDir() { + return + } + } // try extensions for _, ext := range pkgExts { filename = noext + ext diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index 275287d428a..de6a791dc86 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -11,6 +11,7 @@ import ( "internal/testenv" "os" "os/exec" + "path" "path/filepath" "runtime" "strings" @@ -44,14 +45,19 @@ func skipSpecialPlatforms(t *testing.T) { // compile runs the compiler on filename, with dirname as the working directory, // and writes the output file to outdirname. -func compile(t *testing.T, dirname, filename, outdirname string) string { +// compile gives the resulting package a packagepath of testdata/<filebasename>. +func compile(t *testing.T, dirname, filename, outdirname string, packagefiles map[string]string) string { // filename must end with ".go" - if !strings.HasSuffix(filename, ".go") { + basename, ok := strings.CutSuffix(filepath.Base(filename), ".go") + if !ok { t.Fatalf("filename doesn't end in .go: %s", filename) } - basename := filepath.Base(filename) - outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o") - cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p", strings.TrimSuffix(outname, ".o"), "-o", outname, filename) + objname := basename + ".o" + outname := filepath.Join(outdirname, objname) + importcfgfile := filepath.Join(outdirname, basename) + ".importcfg" + testenv.WriteImportcfg(t, importcfgfile, packagefiles) + pkgpath := path.Join("testdata", basename) + cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p", pkgpath, "-D", "testdata", "-importcfg", importcfgfile, "-o", outname, filename) cmd.Dir = dirname out, err := cmd.CombinedOutput() if err != nil { @@ -139,7 +145,7 @@ func TestImportTestdata(t *testing.T) { tmpdir := mktmpdir(t) defer os.RemoveAll(tmpdir) - compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata")) + compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"), nil) path := "./testdata/" + strings.TrimSuffix(testfile, ".go") if pkg := testPath(t, path, tmpdir); pkg != nil { @@ -209,7 +215,7 @@ func TestImportTypeparamTests(t *testing.T) { // Compile and import, and compare the resulting package with the package // that was type-checked directly. - compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata")) + compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata"), nil) pkgName := strings.TrimSuffix(entry.Name(), ".go") imported := importPkg(t, "./testdata/"+pkgName, tmpdir) checked := checkFile(t, filename, src) @@ -564,8 +570,8 @@ func TestIssue13566(t *testing.T) { if err != nil { t.Fatal(err) } - compile(t, "testdata", "a.go", testoutdir) - compile(t, testoutdir, bpath, testoutdir) + compile(t, "testdata", "a.go", testoutdir, nil) + compile(t, testoutdir, bpath, testoutdir, map[string]string{"testdata/a": filepath.Join(testoutdir, "a.o")}) // import must succeed (test for issue at hand) pkg := importPkg(t, "./testdata/b", tmpdir) @@ -596,7 +602,7 @@ func TestTypeNamingOrder(t *testing.T) { defer os.RemoveAll(tmpdir) testoutdir := filepath.Join(tmpdir, "testdata") - compile(t, "testdata", "g.go", testoutdir) + compile(t, "testdata", "g.go", testoutdir, nil) // import must succeed (test for issue at hand) _ = importPkg(t, "./testdata/g", tmpdir) @@ -666,7 +672,7 @@ func TestIssue15517(t *testing.T) { tmpdir := mktmpdir(t) defer os.RemoveAll(tmpdir) - compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata")) + compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"), nil) // Multiple imports of p must succeed without redeclaration errors. // We use an import path that's not cleaned up so that the eventual @@ -773,7 +779,7 @@ func importPkg(t *testing.T, path, srcDir string) *types.Package { func compileAndImportPkg(t *testing.T, name string) *types.Package { tmpdir := mktmpdir(t) defer os.RemoveAll(tmpdir) - compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata")) + compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"), nil) return importPkg(t, "./testdata/"+name, tmpdir) } diff --git a/src/internal/abi/abi_test.go b/src/internal/abi/abi_test.go index 51d26f69aec..f0d8dceb3ec 100644 --- a/src/internal/abi/abi_test.go +++ b/src/internal/abi/abi_test.go @@ -42,6 +42,9 @@ func TestFuncPCCompileError(t *testing.T) { symabi := filepath.Join(tmpdir, "symabi") obj := filepath.Join(tmpdir, "x.o") + importcfgfile := filepath.Join(tmpdir, "hello.importcfg") + testenv.WriteImportcfg(t, importcfgfile, nil) + // parse assembly code for symabi. cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-gensymabis", "-o", symabi, asmSrc) out, err := cmd.CombinedOutput() @@ -50,7 +53,7 @@ func TestFuncPCCompileError(t *testing.T) { } // compile go code. - cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=p", "-symabis", symabi, "-o", obj, goSrc) + cmd = exec.Command(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/gc.go b/src/internal/goroot/gc.go index 5517598519c..79403d29fc0 100644 --- a/src/internal/goroot/gc.go +++ b/src/internal/goroot/gc.go @@ -69,8 +69,8 @@ func (gd *gccgoDirs) init() { const prefix = "libraries: =" var dirs []string for _, dirEntry := range dirsEntries { - if after, found := strings.CutPrefix(dirEntry, prefix); found { - dirs = filepath.SplitList(after) + if strings.HasPrefix(dirEntry, prefix) { + dirs = filepath.SplitList(strings.TrimPrefix(dirEntry, prefix)) break } } diff --git a/src/internal/goroot/importcfg.go b/src/internal/goroot/importcfg.go new file mode 100644 index 00000000000..51d0d77604f --- /dev/null +++ b/src/internal/goroot/importcfg.go @@ -0,0 +1,66 @@ +// 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 { + if importPath != "unsafe" && export != "" { // unsafe + 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 { + err = fmt.Errorf("determining pkgfile map: invalid line in go list output: %q", line) + return + } + importPath, export := sp[0], sp[1] + m[importPath] = export + } + stdlibPkgfileMap = m + }) + return stdlibPkgfileMap, stdlibPkgfileErr +} diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go index fe34a92d9c9..ae8413efb6a 100644 --- a/src/internal/testenv/testenv.go +++ b/src/internal/testenv/testenv.go @@ -16,6 +16,7 @@ import ( "flag" "fmt" "internal/cfg" + "internal/goroot" "internal/platform" "os" "os/exec" @@ -461,3 +462,20 @@ func RunWithTimeout(t testing.TB, cmd *exec.Cmd) ([]byte, error) { return b.Bytes(), err } + +// 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) + } + if err != nil { + t.Fatalf("preparing the importcfg failed: %s", err) + } + os.WriteFile(dstPath, []byte(importcfg), 0655) + if err != nil { + t.Fatalf("writing the importcfg failed: %s", err) + } +} diff --git a/src/internal/types/errors/codes_test.go b/src/internal/types/errors/codes_test.go index 6f671a94c6e..2490ade5c36 100644 --- a/src/internal/types/errors/codes_test.go +++ b/src/internal/types/errors/codes_test.go @@ -11,6 +11,7 @@ import ( "go/importer" "go/parser" "go/token" + "internal/testenv" "reflect" "strings" "testing" @@ -19,6 +20,8 @@ import ( ) func TestErrorCodeExamples(t *testing.T) { + testenv.MustHaveGoBuild(t) // go command needed to resolve std .a files for importer.Default(). + walkCodes(t, func(name string, value int, spec *ast.ValueSpec) { t.Run(name, func(t *testing.T) { doc := spec.Doc.Text() diff --git a/src/runtime/align_test.go b/src/runtime/align_test.go index d3bdf007dcc..5f225d63c4c 100644 --- a/src/runtime/align_test.go +++ b/src/runtime/align_test.go @@ -12,6 +12,7 @@ import ( "go/printer" "go/token" "go/types" + "internal/testenv" "os" "regexp" "runtime" @@ -22,6 +23,8 @@ import ( // Check that 64-bit fields on which we apply atomic operations // are aligned to 8 bytes. This can be a problem on 32-bit systems. func TestAtomicAlignment(t *testing.T) { + testenv.MustHaveGoBuild(t) // go command needed to resolve std .a files for importer.Default(). + // Read the code making the tables above, to see which fields and // variables we are currently checking. checked := map[string]bool{} -- GitLab