diff --git a/src/cmd/compile/internal/importer/gcimporter.go b/src/cmd/compile/internal/importer/gcimporter.go index f6ec6554a85b5ae30d8f3dcc7b2ece8e26bebb8f..0aa779441a7a3a364ed81aa689f752efce6bd6ee 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 2fbd3f00d24a73897dd3f6a881d9f0829a97d3b3..ac502e6e37e8d6a8c5eeea6b53263d2fb95df2e7 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 e2699f7656bfa428caef027bed841cfe22d4193a..2a4490b201a9f7fb9cce56934ce260e72424843a 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 cca65af8ed01c547a33083731e94f8ad8f9fc412..f5315856c15f22c74552515d7bdebf9b2f3873f8 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 35babe61fc0ec93084c9a8c7260517c0244ef426..ce065721649d30baa25bc8bbbb30064f54ce98c0 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 bbf942503a24131ba8cf91fea8399b293ad6ce37..fa6a1b9a68cd8c3cca417ff14f1635e9e1e7b7c7 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 146c27c00a401161d946083605ddbf027dd93fad..5f16abcb02eb7d89fa750aa223755f557472de65 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 eed5c462be20a1c875f949459f577f78bdd7b646..25556ac04c6c55735c42aaa3f9c5b9ec930e6a59 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 1b8353e8fa63af863a63ec8ea200f291c0c2f24e..3c391380383228599cb24ed80f5f9b44da116260 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 0b27a954041821d834e4f7082e85e44713d2ec00..0ec464056c0f7bd6eb05e91ab6071275b87b4f88 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 275287d428a82acfac3f8457713edef4a9f37021..de6a791dc86fab48c42c08cbd10f84d36062c90d 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 51d26f69aecae940313b34f900471849cb0f63dd..f0d8dceb3ec9c14b9970afc9389ccd800647d768 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 5517598519c18ca1047a4b52b0305b2255cc80b9..79403d29fc02e55c30a929be3eee2478f02e6062 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 0000000000000000000000000000000000000000..51d0d77604fe9f1017ca89bcbd655ae1564de59c --- /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 fe34a92d9c9e6c44b7f58015cedef5ec0785875e..ae8413efb6ad46b5f7c88a4dfa3970f30a79d7fe 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 6f671a94c6edc986ae4757faf3d0bf8b20399539..2490ade5c369754bb5a879f8cb21de186027d65c 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 d3bdf007dccf1e18b7cced5cb3f8ef311238f3c4..5f225d63c4c5a9ab5eb14967122a9adfd5d26029 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{}