From: Bryan C. Mills Date: Thu, 21 Feb 2019 19:17:19 +0000 (-0500) Subject: misc/cgo/testshared: fix tests in module mode X-Git-Tag: go1.13beta1~1437 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ffde2ddb95eb265b9031fbb24a7faa2b1d876630;p=gostls13.git misc/cgo/testshared: fix tests in module mode Updates #30228 Change-Id: I5cc739eb9fdfb648ec45e350d43d4cb02e450553 Reviewed-on: https://go-review.googlesource.com/c/163211 Run-TryBot: Bryan C. Mills Reviewed-by: Jay Conrod --- diff --git a/misc/cgo/testshared/overlaydir_test.go b/misc/cgo/testshared/overlaydir_test.go new file mode 100644 index 0000000000..68be056256 --- /dev/null +++ b/misc/cgo/testshared/overlaydir_test.go @@ -0,0 +1,81 @@ +// Copyright 2019 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 shared_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + symBase, err := filepath.Rel(srcRoot, dstRoot) + if err != nil { + symBase, err = filepath.Abs(srcRoot) + if err != nil { + return err + } + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.Mkdir(dstPath, perm) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index 41a24efe22..9a8c398976 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -44,31 +44,35 @@ func run(t *testing.T, msg string, args ...string) { // goCmd invokes the go tool with the installsuffix set up by TestMain. It calls // t.Fatalf if the command fails. -func goCmd(t *testing.T, args ...string) { +func goCmd(t *testing.T, args ...string) string { newargs := []string{args[0], "-installsuffix=" + suffix} if testing.Verbose() { newargs = append(newargs, "-x") } newargs = append(newargs, args[1:]...) c := exec.Command("go", newargs...) + + stderr := new(strings.Builder) var output []byte var err error if testing.Verbose() { - fmt.Printf("+ go %s\n", strings.Join(newargs, " ")) - c.Stdout = os.Stdout + fmt.Printf("+ go %s\n", strings.Join(args, " ")) c.Stderr = os.Stderr - err = c.Run() - output = []byte("(output above)") + stderr.WriteString("(output above)") } else { - output, err = c.CombinedOutput() + c.Stderr = stderr } + output, err = c.Output() + if err != nil { if t != nil { - t.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output) + t.Helper() + t.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr) } else { - log.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output) + log.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr) } } + return string(bytes.TrimSpace(output)) } // TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit). @@ -105,47 +109,32 @@ func testMain(m *testing.M) (int, error) { // Some tests need to edit the source in GOPATH, so copy this directory to a // temporary directory and chdir to that. - scratchDir, err := ioutil.TempDir("", "testshared") + gopath, err := ioutil.TempDir("", "testshared") if err != nil { return 0, fmt.Errorf("TempDir failed: %v", err) } if testing.Verbose() { - fmt.Printf("+ mkdir -p %s\n", scratchDir) - } - defer os.RemoveAll(scratchDir) - err = filepath.Walk(".", func(path string, info os.FileInfo, err error) error { - scratchPath := filepath.Join(scratchDir, path) - if info.IsDir() { - if path == "." { - return nil - } - if testing.Verbose() { - fmt.Printf("+ mkdir -p %s\n", scratchPath) - } - return os.Mkdir(scratchPath, info.Mode()) - } else { - fromBytes, err := ioutil.ReadFile(path) - if err != nil { - return err - } - if testing.Verbose() { - fmt.Printf("+ cp %s %s\n", path, scratchPath) - } - return ioutil.WriteFile(scratchPath, fromBytes, info.Mode()) - } - }) - if err != nil { - return 0, fmt.Errorf("walk failed: %v", err) + fmt.Printf("+ mkdir -p %s\n", gopath) + } + defer os.RemoveAll(gopath) + + modRoot := filepath.Join(gopath, "src", "testshared") + if err := overlayDir(modRoot, "testdata"); err != nil { + return 0, err } - os.Setenv("GOPATH", scratchDir) if testing.Verbose() { - fmt.Printf("+ export GOPATH=%s\n", scratchDir) + fmt.Printf("+ cd %s\n", modRoot) + } + os.Chdir(modRoot) + if err := ioutil.WriteFile("go.mod", []byte("module testshared\n"), 0666); err != nil { + return 0, err } - myContext.GOPATH = scratchDir + + os.Setenv("GOPATH", gopath) if testing.Verbose() { - fmt.Printf("+ cd %s\n", scratchDir) + fmt.Printf("+ export GOPATH=%s\n", gopath) } - os.Chdir(scratchDir) + myContext.GOPATH = gopath // All tests depend on runtime being built into a shared library. Because // that takes a few seconds, do it here and have all tests use the version @@ -154,15 +143,20 @@ func testMain(m *testing.M) (int, error) { goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs...)...) myContext.InstallSuffix = suffix + "_dynlink" - depP, err := myContext.Import("depBase", ".", build.ImportComment) + depP, err := myContext.Import("./depBase", ".", build.ImportComment) if err != nil { return 0, fmt.Errorf("import failed: %v", err) } - gopathInstallDir = depP.PkgTargetRoot + if depP.PkgTargetRoot == "" { + gopathInstallDir = filepath.Dir(goCmd(nil, "list", "-buildmode=shared", "-f", "{{.Target}}", "./depBase")) + } else { + gopathInstallDir = filepath.Join(depP.PkgTargetRoot, "testshared") + } return m.Run(), nil } func TestMain(m *testing.M) { + log.SetFlags(log.Lshortfile) flag.Parse() // Some of the tests install binaries into a custom GOPATH. @@ -350,6 +344,7 @@ func readNotes(f *elf.File) ([]*note, error) { } func dynStrings(t *testing.T, path string, flag elf.DynTag) []string { + t.Helper() f, err := elf.Open(path) if err != nil { t.Fatalf("elf.Open(%q) failed: %v", path, err) @@ -363,6 +358,7 @@ func dynStrings(t *testing.T, path string, flag elf.DynTag) []string { } func AssertIsLinkedToRegexp(t *testing.T, path string, re *regexp.Regexp) { + t.Helper() for _, dynstring := range dynStrings(t, path, elf.DT_NEEDED) { if re.MatchString(dynstring) { return @@ -372,10 +368,12 @@ func AssertIsLinkedToRegexp(t *testing.T, path string, re *regexp.Regexp) { } func AssertIsLinkedTo(t *testing.T, path, lib string) { + t.Helper() AssertIsLinkedToRegexp(t, path, regexp.MustCompile(regexp.QuoteMeta(lib))) } func AssertHasRPath(t *testing.T, path, dir string) { + t.Helper() for _, tag := range []elf.DynTag{elf.DT_RPATH, elf.DT_RUNPATH} { for _, dynstring := range dynStrings(t, path, tag) { for _, rpath := range strings.Split(dynstring, ":") { @@ -390,15 +388,15 @@ func AssertHasRPath(t *testing.T, path, dir string) { // Build a trivial program that links against the shared runtime and check it runs. func TestTrivialExecutable(t *testing.T) { - goCmd(t, "install", "-linkshared", "trivial") - run(t, "trivial executable", "./bin/trivial") - AssertIsLinkedTo(t, "./bin/trivial", soname) - AssertHasRPath(t, "./bin/trivial", gorootInstallDir) + goCmd(t, "install", "-linkshared", "./trivial") + run(t, "trivial executable", "../../bin/trivial") + AssertIsLinkedTo(t, "../../bin/trivial", soname) + AssertHasRPath(t, "../../bin/trivial", gorootInstallDir) } // Build a trivial program in PIE mode that links against the shared runtime and check it runs. func TestTrivialExecutablePIE(t *testing.T) { - goCmd(t, "build", "-buildmode=pie", "-o", "trivial.pie", "-linkshared", "trivial") + goCmd(t, "build", "-buildmode=pie", "-o", "trivial.pie", "-linkshared", "./trivial") run(t, "trivial executable", "./trivial.pie") AssertIsLinkedTo(t, "./trivial.pie", soname) AssertHasRPath(t, "./trivial.pie", gorootInstallDir) @@ -406,15 +404,15 @@ func TestTrivialExecutablePIE(t *testing.T) { // Build a division test program and check it runs. func TestDivisionExecutable(t *testing.T) { - goCmd(t, "install", "-linkshared", "division") - run(t, "division executable", "./bin/division") + goCmd(t, "install", "-linkshared", "./division") + run(t, "division executable", "../../bin/division") } // Build an executable that uses cgo linked against the shared runtime and check it // runs. func TestCgoExecutable(t *testing.T) { - goCmd(t, "install", "-linkshared", "execgo") - run(t, "cgo executable", "./bin/execgo") + goCmd(t, "install", "-linkshared", "./execgo") + run(t, "cgo executable", "../../bin/execgo") } func checkPIE(t *testing.T, name string) { @@ -433,7 +431,7 @@ func checkPIE(t *testing.T, name string) { func TestTrivialPIE(t *testing.T) { name := "trivial_pie" - goCmd(t, "build", "-buildmode=pie", "-o="+name, "trivial") + goCmd(t, "build", "-buildmode=pie", "-o="+name, "./trivial") defer os.Remove(name) run(t, name, "./"+name) checkPIE(t, name) @@ -441,7 +439,7 @@ func TestTrivialPIE(t *testing.T) { func TestCgoPIE(t *testing.T) { name := "cgo_pie" - goCmd(t, "build", "-buildmode=pie", "-o="+name, "execgo") + goCmd(t, "build", "-buildmode=pie", "-o="+name, "./execgo") defer os.Remove(name) run(t, name, "./"+name) checkPIE(t, name) @@ -450,15 +448,16 @@ func TestCgoPIE(t *testing.T) { // Build a GOPATH package into a shared library that links against the goroot runtime // and an executable that links against both. func TestGopathShlib(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") - AssertIsLinkedTo(t, filepath.Join(gopathInstallDir, "libdepBase.so"), soname) - goCmd(t, "install", "-linkshared", "exe") - AssertIsLinkedTo(t, "./bin/exe", soname) - AssertIsLinkedTo(t, "./bin/exe", "libdepBase.so") - AssertHasRPath(t, "./bin/exe", gorootInstallDir) - AssertHasRPath(t, "./bin/exe", gopathInstallDir) + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + shlib := goCmd(t, "list", "-f", "{{.Shlib}}", "-buildmode=shared", "-linkshared", "./depBase") + AssertIsLinkedTo(t, shlib, soname) + goCmd(t, "install", "-linkshared", "./exe") + AssertIsLinkedTo(t, "../../bin/exe", soname) + AssertIsLinkedTo(t, "../../bin/exe", filepath.Base(shlib)) + AssertHasRPath(t, "../../bin/exe", gorootInstallDir) + AssertHasRPath(t, "../../bin/exe", filepath.Dir(gopathInstallDir)) // And check it runs. - run(t, "executable linked to GOPATH library", "./bin/exe") + run(t, "executable linked to GOPATH library", "../../bin/exe") } // The shared library contains a note listing the packages it contains in a section @@ -470,8 +469,8 @@ func testPkgListNote(t *testing.T, f *elf.File, note *note) { if isOffsetLoaded(f, note.section.Offset) { t.Errorf("package list section contained in PT_LOAD segment") } - if note.desc != "depBase\n" { - t.Errorf("incorrect package list %q, want %q", note.desc, "depBase\n") + if note.desc != "testshared/depBase\n" { + t.Errorf("incorrect package list %q, want %q", note.desc, "testshared/depBase\n") } } @@ -528,8 +527,9 @@ func testDepsNote(t *testing.T, f *elf.File, note *note) { // The shared library contains notes with defined contents; see above. func TestNotes(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") - f, err := elf.Open(filepath.Join(gopathInstallDir, "libdepBase.so")) + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + shlib := goCmd(t, "list", "-f", "{{.Shlib}}", "-buildmode=shared", "-linkshared", "./depBase") + f, err := elf.Open(shlib) if err != nil { t.Fatal(err) } @@ -581,23 +581,24 @@ func TestNotes(t *testing.T) { // runtime, another package (dep2) that links against the first, and an // executable that links against dep2. func TestTwoGopathShlibs(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") - goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep2") - goCmd(t, "install", "-linkshared", "exe2") - run(t, "executable linked to GOPATH library", "./bin/exe2") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep2") + goCmd(t, "install", "-linkshared", "./exe2") + run(t, "executable linked to GOPATH library", "../../bin/exe2") } func TestThreeGopathShlibs(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") - goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep2") - goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep3") - goCmd(t, "install", "-linkshared", "exe3") - run(t, "executable linked to GOPATH library", "./bin/exe3") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep2") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep3") + goCmd(t, "install", "-linkshared", "./exe3") + run(t, "executable linked to GOPATH library", "../../bin/exe3") } -// If gccgo is not available or not new enough call t.Skip. Otherwise, -// return a build.Context that is set up for gccgo. -func prepGccgo(t *testing.T) build.Context { +// If gccgo is not available or not new enough, call t.Skip. +func requireGccgo(t *testing.T) { + t.Helper() + gccgoName := os.Getenv("GCCGO") if gccgoName == "" { gccgoName = "gccgo" @@ -614,61 +615,64 @@ func prepGccgo(t *testing.T) build.Context { if string(output) < "5" { t.Skipf("gccgo too old (%s)", strings.TrimSpace(string(output))) } - gccgoContext := build.Default - gccgoContext.InstallSuffix = suffix + "_fPIC" - gccgoContext.Compiler = "gccgo" - gccgoContext.GOPATH = os.Getenv("GOPATH") - return gccgoContext + + gomod, err := exec.Command("go", "env", "GOMOD").Output() + if err != nil { + t.Fatalf("go env GOMOD: %v", err) + } + if len(bytes.TrimSpace(gomod)) > 0 { + t.Skipf("gccgo not supported in module mode; see golang.org/issue/30344") + } } // Build a GOPATH package into a shared library with gccgo and an executable that // links against it. func TestGoPathShlibGccgo(t *testing.T) { - gccgoContext := prepGccgo(t) + requireGccgo(t) libgoRE := regexp.MustCompile("libgo.so.[0-9]+") - depP, err := gccgoContext.Import("depBase", ".", build.ImportComment) - if err != nil { - t.Fatalf("import failed: %v", err) - } - gccgoInstallDir := filepath.Join(depP.PkgTargetRoot, "shlibs") - goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "depBase") - AssertIsLinkedToRegexp(t, filepath.Join(gccgoInstallDir, "libdepBase.so"), libgoRE) - goCmd(t, "install", "-compiler=gccgo", "-linkshared", "exe") - AssertIsLinkedToRegexp(t, "./bin/exe", libgoRE) - AssertIsLinkedTo(t, "./bin/exe", "libdepBase.so") - AssertHasRPath(t, "./bin/exe", gccgoInstallDir) + goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./depBase") + + // Run 'go list' after 'go install': with gccgo, we apparently don't know the + // shlib location until after we've installed it. + shlib := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./depBase") + + AssertIsLinkedToRegexp(t, shlib, libgoRE) + goCmd(t, "install", "-compiler=gccgo", "-linkshared", "./exe") + AssertIsLinkedToRegexp(t, "../../bin/exe", libgoRE) + AssertIsLinkedTo(t, "../../bin/exe", filepath.Base(shlib)) + AssertHasRPath(t, "../../bin/exe", filepath.Dir(shlib)) // And check it runs. - run(t, "gccgo-built", "./bin/exe") + run(t, "gccgo-built", "../../bin/exe") } // The gccgo version of TestTwoGopathShlibs: build a GOPATH package into a shared // library with gccgo, another GOPATH package that depends on the first and an // executable that links the second library. func TestTwoGopathShlibsGccgo(t *testing.T) { - gccgoContext := prepGccgo(t) + requireGccgo(t) libgoRE := regexp.MustCompile("libgo.so.[0-9]+") - depP, err := gccgoContext.Import("depBase", ".", build.ImportComment) - if err != nil { - t.Fatalf("import failed: %v", err) - } - gccgoInstallDir := filepath.Join(depP.PkgTargetRoot, "shlibs") - goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "depBase") - goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "dep2") - goCmd(t, "install", "-compiler=gccgo", "-linkshared", "exe2") + goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./dep2") + goCmd(t, "install", "-compiler=gccgo", "-linkshared", "./exe2") + + // Run 'go list' after 'go install': with gccgo, we apparently don't know the + // shlib location until after we've installed it. + dep2 := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./dep2") + depBase := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./depBase") - AssertIsLinkedToRegexp(t, filepath.Join(gccgoInstallDir, "libdepBase.so"), libgoRE) - AssertIsLinkedToRegexp(t, filepath.Join(gccgoInstallDir, "libdep2.so"), libgoRE) - AssertIsLinkedTo(t, filepath.Join(gccgoInstallDir, "libdep2.so"), "libdepBase.so") - AssertIsLinkedToRegexp(t, "./bin/exe2", libgoRE) - AssertIsLinkedTo(t, "./bin/exe2", "libdep2") - AssertIsLinkedTo(t, "./bin/exe2", "libdepBase.so") + AssertIsLinkedToRegexp(t, depBase, libgoRE) + AssertIsLinkedToRegexp(t, dep2, libgoRE) + AssertIsLinkedTo(t, dep2, filepath.Base(depBase)) + AssertIsLinkedToRegexp(t, "../../bin/exe2", libgoRE) + AssertIsLinkedTo(t, "../../bin/exe2", filepath.Base(dep2)) + AssertIsLinkedTo(t, "../../bin/exe2", filepath.Base(depBase)) // And check it runs. - run(t, "gccgo-built", "./bin/exe2") + run(t, "gccgo-built", "../../bin/exe2") } // Testing rebuilding of shared libraries when they are stale is a bit more @@ -694,9 +698,9 @@ func resetFileStamps() { } } - reset("bin") - reset("pkg") - reset("src") + reset("../../bin") + reset("../../pkg") + reset("../../src") reset(gorootInstallDir) } @@ -746,6 +750,7 @@ func touch(t *testing.T, path string) (cleanup func()) { // isNew returns if the path is newer than the time stamp used by touch. func isNew(t *testing.T, path string) bool { + t.Helper() fi, err := os.Stat(path) if err != nil { t.Fatal(err) @@ -771,40 +776,47 @@ func AssertNotRebuilt(t *testing.T, msg, path string) { } func TestRebuilding(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") - goCmd(t, "install", "-linkshared", "exe") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-linkshared", "./exe") + info := strings.Fields(goCmd(t, "list", "-buildmode=shared", "-linkshared", "-f", "{{.Target}} {{.Shlib}}", "./depBase")) + if len(info) != 2 { + t.Fatalf("go list failed to report Target and/or Shlib") + } + target := info[0] + shlib := info[1] // If the source is newer than both the .a file and the .so, both are rebuilt. t.Run("newsource", func(t *testing.T) { resetFileStamps() - cleanup := touch(t, "src/depBase/dep.go") + cleanup := touch(t, "./depBase/dep.go") defer func() { cleanup() - goCmd(t, "install", "-linkshared", "exe") + goCmd(t, "install", "-linkshared", "./exe") }() - goCmd(t, "install", "-linkshared", "exe") - AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "depBase.a")) - AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "libdepBase.so")) + goCmd(t, "install", "-linkshared", "./exe") + AssertRebuilt(t, "new source", target) + AssertRebuilt(t, "new source", shlib) }) // If the .a file is newer than the .so, the .so is rebuilt (but not the .a) t.Run("newarchive", func(t *testing.T) { resetFileStamps() - AssertNotRebuilt(t, "new .a file before build", filepath.Join(gopathInstallDir, "depBase.a")) - goCmd(t, "list", "-linkshared", "-f={{.ImportPath}} {{.Stale}} {{.StaleReason}} {{.Target}}", "depBase") - AssertNotRebuilt(t, "new .a file before build", filepath.Join(gopathInstallDir, "depBase.a")) - cleanup := touch(t, filepath.Join(gopathInstallDir, "depBase.a")) + AssertNotRebuilt(t, "new .a file before build", target) + goCmd(t, "list", "-linkshared", "-f={{.ImportPath}} {{.Stale}} {{.StaleReason}} {{.Target}}", "./depBase") + AssertNotRebuilt(t, "new .a file before build", target) + cleanup := touch(t, target) defer func() { cleanup() - goCmd(t, "install", "-v", "-linkshared", "exe") + goCmd(t, "install", "-v", "-linkshared", "./exe") }() - goCmd(t, "install", "-v", "-linkshared", "exe") - AssertNotRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "depBase.a")) - AssertRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "libdepBase.so")) + goCmd(t, "install", "-v", "-linkshared", "./exe") + AssertNotRebuilt(t, "new .a file", target) + AssertRebuilt(t, "new .a file", shlib) }) } func appendFile(t *testing.T, path, content string) { + t.Helper() f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0660) if err != nil { t.Fatalf("os.OpenFile failed: %v", err) @@ -821,16 +833,24 @@ func appendFile(t *testing.T, path, content string) { } } -func writeFile(t *testing.T, path, content string) { - err := ioutil.WriteFile(path, []byte(content), 0644) +func createFile(t *testing.T, path, content string) { + t.Helper() + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) if err != nil { - t.Fatalf("ioutil.WriteFile failed: %v", err) + t.Fatalf("os.OpenFile failed: %v", err) + } + _, err = f.WriteString(content) + if closeErr := f.Close(); err == nil { + err = closeErr + } + if err != nil { + t.Fatalf("WriteString failed: %v", err) } } func TestABIChecking(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") - goCmd(t, "install", "-linkshared", "exe") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-linkshared", "./exe") // If we make an ABI-breaking change to depBase and rebuild libp.so but not exe, // exe will abort with a complaint on startup. @@ -838,18 +858,21 @@ func TestABIChecking(t *testing.T) { // some senses but suffices for the narrow definition of ABI compatibility the // toolchain uses today. resetFileStamps() - appendFile(t, "src/depBase/dep.go", "func ABIBreak() {}\n") - goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") - c := exec.Command("./bin/exe") + + createFile(t, "./depBase/break.go", "package depBase\nfunc ABIBreak() {}\n") + defer os.Remove("./depBase/break.go") + + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + c := exec.Command("../../bin/exe") output, err := c.CombinedOutput() if err == nil { t.Fatal("executing exe did not fail after ABI break") } scanner := bufio.NewScanner(bytes.NewReader(output)) foundMsg := false - const wantLine = "abi mismatch detected between the executable and libdepBase.so" + const wantPrefix = "abi mismatch detected between the executable and lib" for scanner.Scan() { - if scanner.Text() == wantLine { + if strings.HasPrefix(scanner.Text(), wantPrefix) { foundMsg = true break } @@ -858,20 +881,20 @@ func TestABIChecking(t *testing.T) { t.Errorf("scanner encountered error: %v", err) } if !foundMsg { - t.Fatalf("exe failed, but without line %q; got output:\n%s", wantLine, output) + t.Fatalf("exe failed, but without line %q; got output:\n%s", wantPrefix, output) } // Rebuilding exe makes it work again. - goCmd(t, "install", "-linkshared", "exe") - run(t, "rebuilt exe", "./bin/exe") + goCmd(t, "install", "-linkshared", "./exe") + run(t, "rebuilt exe", "../../bin/exe") // If we make a change which does not break ABI (such as adding an unexported // function) and rebuild libdepBase.so, exe still works, even if new function // is in a file by itself. resetFileStamps() - writeFile(t, "src/depBase/dep2.go", "package depBase\nfunc noABIBreak() {}\n") - goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") - run(t, "after non-ABI breaking change", "./bin/exe") + createFile(t, "./depBase/dep2.go", "package depBase\nfunc noABIBreak() {}\n") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + run(t, "after non-ABI breaking change", "../../bin/exe") } // If a package 'explicit' imports a package 'implicit', building @@ -881,29 +904,29 @@ func TestABIChecking(t *testing.T) { // executable rather than fetching it from the shared library. The // link still succeeds and the executable still runs though. func TestImplicitInclusion(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "explicit") - goCmd(t, "install", "-linkshared", "implicitcmd") - run(t, "running executable linked against library that contains same package as it", "./bin/implicitcmd") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./explicit") + goCmd(t, "install", "-linkshared", "./implicitcmd") + run(t, "running executable linked against library that contains same package as it", "../../bin/implicitcmd") } // Tests to make sure that the type fields of empty interfaces and itab // fields of nonempty interfaces are unique even across modules, // so that interface equality works correctly. func TestInterface(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "iface_a") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./iface_a") // Note: iface_i gets installed implicitly as a dependency of iface_a. - goCmd(t, "install", "-buildmode=shared", "-linkshared", "iface_b") - goCmd(t, "install", "-linkshared", "iface") - run(t, "running type/itab uniqueness tester", "./bin/iface") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./iface_b") + goCmd(t, "install", "-linkshared", "./iface") + run(t, "running type/itab uniqueness tester", "../../bin/iface") } // Access a global variable from a library. func TestGlobal(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "globallib") - goCmd(t, "install", "-linkshared", "global") - run(t, "global executable", "./bin/global") - AssertIsLinkedTo(t, "./bin/global", soname) - AssertHasRPath(t, "./bin/global", gorootInstallDir) + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./globallib") + goCmd(t, "install", "-linkshared", "./global") + run(t, "global executable", "../../bin/global") + AssertIsLinkedTo(t, "../../bin/global", soname) + AssertHasRPath(t, "../../bin/global", gorootInstallDir) } // Run a test using -linkshared of an installed shared package. @@ -915,5 +938,5 @@ func TestTestInstalledShared(t *testing.T) { // Test generated pointer method with -linkshared. // Issue 25065. func TestGeneratedMethod(t *testing.T) { - goCmd(t, "install", "-buildmode=shared", "-linkshared", "issue25065") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue25065") } diff --git a/misc/cgo/testshared/src/dep2/dep2.go b/misc/cgo/testshared/testdata/dep2/dep2.go similarity index 82% rename from misc/cgo/testshared/src/dep2/dep2.go rename to misc/cgo/testshared/testdata/dep2/dep2.go index c2c812adb9..94f38cf507 100644 --- a/misc/cgo/testshared/src/dep2/dep2.go +++ b/misc/cgo/testshared/testdata/dep2/dep2.go @@ -1,6 +1,6 @@ package dep2 -import "depBase" +import "testshared/depBase" var W int = 1 diff --git a/misc/cgo/testshared/src/dep3/dep3.go b/misc/cgo/testshared/testdata/dep3/dep3.go similarity index 91% rename from misc/cgo/testshared/src/dep3/dep3.go rename to misc/cgo/testshared/testdata/dep3/dep3.go index 7b7c9dac1f..6b02ad2ee5 100644 --- a/misc/cgo/testshared/src/dep3/dep3.go +++ b/misc/cgo/testshared/testdata/dep3/dep3.go @@ -7,8 +7,8 @@ package dep3 // the type data in this case and later crash. import ( - "dep2" - "depBase" + "testshared/dep2" + "testshared/depBase" ) type Dep3 struct { diff --git a/misc/cgo/testshared/src/depBase/asm.s b/misc/cgo/testshared/testdata/depBase/asm.s similarity index 100% rename from misc/cgo/testshared/src/depBase/asm.s rename to misc/cgo/testshared/testdata/depBase/asm.s diff --git a/misc/cgo/testshared/src/depBase/dep.go b/misc/cgo/testshared/testdata/depBase/dep.go similarity index 100% rename from misc/cgo/testshared/src/depBase/dep.go rename to misc/cgo/testshared/testdata/depBase/dep.go diff --git a/misc/cgo/testshared/src/depBase/gccgo.go b/misc/cgo/testshared/testdata/depBase/gccgo.go similarity index 100% rename from misc/cgo/testshared/src/depBase/gccgo.go rename to misc/cgo/testshared/testdata/depBase/gccgo.go diff --git a/misc/cgo/testshared/src/depBase/stubs.go b/misc/cgo/testshared/testdata/depBase/stubs.go similarity index 100% rename from misc/cgo/testshared/src/depBase/stubs.go rename to misc/cgo/testshared/testdata/depBase/stubs.go diff --git a/misc/cgo/testshared/src/division/division.go b/misc/cgo/testshared/testdata/division/division.go similarity index 100% rename from misc/cgo/testshared/src/division/division.go rename to misc/cgo/testshared/testdata/division/division.go diff --git a/misc/cgo/testshared/src/exe/exe.go b/misc/cgo/testshared/testdata/exe/exe.go similarity index 97% rename from misc/cgo/testshared/src/exe/exe.go rename to misc/cgo/testshared/testdata/exe/exe.go index bd864d88ad..86582581a6 100644 --- a/misc/cgo/testshared/src/exe/exe.go +++ b/misc/cgo/testshared/testdata/exe/exe.go @@ -1,7 +1,7 @@ package main import ( - "depBase" + "testshared/depBase" "os" "reflect" "runtime" diff --git a/misc/cgo/testshared/src/exe2/exe2.go b/misc/cgo/testshared/testdata/exe2/exe2.go similarity index 77% rename from misc/cgo/testshared/src/exe2/exe2.go rename to misc/cgo/testshared/testdata/exe2/exe2.go index 675fd1f365..433f331e36 100644 --- a/misc/cgo/testshared/src/exe2/exe2.go +++ b/misc/cgo/testshared/testdata/exe2/exe2.go @@ -1,6 +1,6 @@ package main -import "dep2" +import "testshared/dep2" func main() { d := &dep2.Dep2{} diff --git a/misc/cgo/testshared/src/exe3/exe3.go b/misc/cgo/testshared/testdata/exe3/exe3.go similarity index 62% rename from misc/cgo/testshared/src/exe3/exe3.go rename to misc/cgo/testshared/testdata/exe3/exe3.go index 643f2605f6..533e3a9e3d 100644 --- a/misc/cgo/testshared/src/exe3/exe3.go +++ b/misc/cgo/testshared/testdata/exe3/exe3.go @@ -1,6 +1,6 @@ package main -import "dep3" +import "testshared/dep3" func main() { dep3.D3() diff --git a/misc/cgo/testshared/src/execgo/exe.go b/misc/cgo/testshared/testdata/execgo/exe.go similarity index 100% rename from misc/cgo/testshared/src/execgo/exe.go rename to misc/cgo/testshared/testdata/execgo/exe.go diff --git a/misc/cgo/testshared/src/explicit/explicit.go b/misc/cgo/testshared/testdata/explicit/explicit.go similarity index 74% rename from misc/cgo/testshared/src/explicit/explicit.go rename to misc/cgo/testshared/testdata/explicit/explicit.go index 6a4453f775..af969fcb23 100644 --- a/misc/cgo/testshared/src/explicit/explicit.go +++ b/misc/cgo/testshared/testdata/explicit/explicit.go @@ -1,7 +1,7 @@ package explicit import ( - "implicit" + "testshared/implicit" ) func E() int { diff --git a/misc/cgo/testshared/src/global/main.go b/misc/cgo/testshared/testdata/global/main.go similarity index 98% rename from misc/cgo/testshared/src/global/main.go rename to misc/cgo/testshared/testdata/global/main.go index 94e7f247de..f43e7c3fb3 100644 --- a/misc/cgo/testshared/src/global/main.go +++ b/misc/cgo/testshared/testdata/global/main.go @@ -5,7 +5,7 @@ package main import ( - "globallib" + "testshared/globallib" ) //go:noinline diff --git a/misc/cgo/testshared/src/globallib/global.go b/misc/cgo/testshared/testdata/globallib/global.go similarity index 100% rename from misc/cgo/testshared/src/globallib/global.go rename to misc/cgo/testshared/testdata/globallib/global.go diff --git a/misc/cgo/testshared/src/iface/main.go b/misc/cgo/testshared/testdata/iface/main.go similarity index 85% rename from misc/cgo/testshared/src/iface/main.go rename to misc/cgo/testshared/testdata/iface/main.go index 3d5b54e73b..d26ebbcc9c 100644 --- a/misc/cgo/testshared/src/iface/main.go +++ b/misc/cgo/testshared/testdata/iface/main.go @@ -4,8 +4,8 @@ package main -import "iface_a" -import "iface_b" +import "testshared/iface_a" +import "testshared/iface_b" func main() { if iface_a.F() != iface_b.F() { diff --git a/misc/cgo/testshared/src/iface_a/a.go b/misc/cgo/testshared/testdata/iface_a/a.go similarity index 91% rename from misc/cgo/testshared/src/iface_a/a.go rename to misc/cgo/testshared/testdata/iface_a/a.go index e11047c166..e2cef1ecda 100644 --- a/misc/cgo/testshared/src/iface_a/a.go +++ b/misc/cgo/testshared/testdata/iface_a/a.go @@ -4,7 +4,7 @@ package iface_a -import "iface_i" +import "testshared/iface_i" //go:noinline func F() interface{} { diff --git a/misc/cgo/testshared/src/iface_b/b.go b/misc/cgo/testshared/testdata/iface_b/b.go similarity index 91% rename from misc/cgo/testshared/src/iface_b/b.go rename to misc/cgo/testshared/testdata/iface_b/b.go index 47aee2e77e..dd3e027b37 100644 --- a/misc/cgo/testshared/src/iface_b/b.go +++ b/misc/cgo/testshared/testdata/iface_b/b.go @@ -4,7 +4,7 @@ package iface_b -import "iface_i" +import "testshared/iface_i" //go:noinline func F() interface{} { diff --git a/misc/cgo/testshared/src/iface_i/i.go b/misc/cgo/testshared/testdata/iface_i/i.go similarity index 100% rename from misc/cgo/testshared/src/iface_i/i.go rename to misc/cgo/testshared/testdata/iface_i/i.go diff --git a/misc/cgo/testshared/src/implicit/implicit.go b/misc/cgo/testshared/testdata/implicit/implicit.go similarity index 100% rename from misc/cgo/testshared/src/implicit/implicit.go rename to misc/cgo/testshared/testdata/implicit/implicit.go diff --git a/misc/cgo/testshared/src/implicitcmd/implicitcmd.go b/misc/cgo/testshared/testdata/implicitcmd/implicitcmd.go similarity index 63% rename from misc/cgo/testshared/src/implicitcmd/implicitcmd.go rename to misc/cgo/testshared/testdata/implicitcmd/implicitcmd.go index f6112933e5..4d4296738e 100644 --- a/misc/cgo/testshared/src/implicitcmd/implicitcmd.go +++ b/misc/cgo/testshared/testdata/implicitcmd/implicitcmd.go @@ -1,8 +1,8 @@ package main import ( - "explicit" - "implicit" + "testshared/explicit" + "testshared/implicit" ) func main() { diff --git a/misc/cgo/testshared/src/issue25065/a.go b/misc/cgo/testshared/testdata/issue25065/a.go similarity index 100% rename from misc/cgo/testshared/src/issue25065/a.go rename to misc/cgo/testshared/testdata/issue25065/a.go diff --git a/misc/cgo/testshared/src/trivial/trivial.go b/misc/cgo/testshared/testdata/trivial/trivial.go similarity index 100% rename from misc/cgo/testshared/src/trivial/trivial.go rename to misc/cgo/testshared/testdata/trivial/trivial.go