diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-01-02 15:05:27 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-01-21 23:53:22 -0800 |
commit | 5a8ea165926cb0737ab03bc48c18dc5198ab5305 (patch) | |
tree | 962dc3357c57f019f85658f99e2e753e30201c27 /libgo/misc/cgo | |
parent | 6ac6529e155c9baa0aaaed7aca06bd38ebda5b43 (diff) | |
download | gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.zip gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.gz gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.bz2 |
libgo: update to Go1.14beta1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/214297
Diffstat (limited to 'libgo/misc/cgo')
28 files changed, 755 insertions, 296 deletions
diff --git a/libgo/misc/cgo/fortran/test.bash b/libgo/misc/cgo/fortran/test.bash index 9498da0..2b61730 100644 --- a/libgo/misc/cgo/fortran/test.bash +++ b/libgo/misc/cgo/fortran/test.bash @@ -28,7 +28,7 @@ case "$FC" in ;; esac -if ! $FC helloworld/helloworld.f90 -o main.exe >& /dev/null; then +if ! $FC helloworld/helloworld.f90 -o /dev/null >& /dev/null; then echo "skipping Fortran test: could not build helloworld.f90 with $FC" exit 0 fi diff --git a/libgo/misc/cgo/life/overlaydir_test.go b/libgo/misc/cgo/life/overlaydir_test.go index f381ea6..034c836 100644 --- a/libgo/misc/cgo/life/overlaydir_test.go +++ b/libgo/misc/cgo/life/overlaydir_test.go @@ -21,12 +21,9 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - symBase, err := filepath.Rel(srcRoot, dstRoot) + srcRoot, err := filepath.Abs(srcRoot) if err != nil { - symBase, err = filepath.Abs(srcRoot) - if err != nil { - return err - } + return err } return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { @@ -52,11 +49,11 @@ func overlayDir(dstRoot, srcRoot string) error { // 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) + return os.MkdirAll(dstPath, perm|0200) } // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + if err := os.Symlink(srcPath, dstPath); err == nil { return nil } diff --git a/libgo/misc/cgo/stdio/overlaydir_test.go b/libgo/misc/cgo/stdio/overlaydir_test.go index 8a8dcdb..027ebf1 100644 --- a/libgo/misc/cgo/stdio/overlaydir_test.go +++ b/libgo/misc/cgo/stdio/overlaydir_test.go @@ -21,12 +21,9 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - symBase, err := filepath.Rel(srcRoot, dstRoot) + srcRoot, err := filepath.Abs(srcRoot) if err != nil { - symBase, err = filepath.Abs(srcRoot) - if err != nil { - return err - } + return err } return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { @@ -52,11 +49,11 @@ func overlayDir(dstRoot, srcRoot string) error { // 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) + return os.MkdirAll(dstPath, perm|0200) } // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + if err := os.Symlink(srcPath, dstPath); err == nil { return nil } diff --git a/libgo/misc/cgo/test/cgo_test.go b/libgo/misc/cgo/test/cgo_test.go index c66df2c..b745a44 100644 --- a/libgo/misc/cgo/test/cgo_test.go +++ b/libgo/misc/cgo/test/cgo_test.go @@ -58,6 +58,7 @@ func Test27660(t *testing.T) { test27660(t) } func Test28896(t *testing.T) { test28896(t) } func Test30065(t *testing.T) { test30065(t) } func Test32579(t *testing.T) { test32579(t) } +func Test31891(t *testing.T) { test31891(t) } func TestAlign(t *testing.T) { testAlign(t) } func TestAtol(t *testing.T) { testAtol(t) } func TestBlocking(t *testing.T) { testBlocking(t) } @@ -91,5 +92,6 @@ func TestThreadLock(t *testing.T) { testThreadLockFunc(t) } func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) } func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } -func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } -func BenchmarkGoString(b *testing.B) { benchGoString(b) } +func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } +func BenchmarkGoString(b *testing.B) { benchGoString(b) } +func BenchmarkCGoCallback(b *testing.B) { benchCallback(b) } diff --git a/libgo/misc/cgo/test/issue31891.c b/libgo/misc/cgo/test/issue31891.c new file mode 100644 index 0000000..67a0dda --- /dev/null +++ b/libgo/misc/cgo/test/issue31891.c @@ -0,0 +1,13 @@ +// 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. + +#include "_cgo_export.h" + +void callIssue31891() { + Issue31891A a; + useIssue31891A(&a); + + Issue31891B b; + useIssue31891B(&b); +} diff --git a/libgo/misc/cgo/test/issue8945.go b/libgo/misc/cgo/test/issue8945.go deleted file mode 100644 index 57a5b2d..0000000 --- a/libgo/misc/cgo/test/issue8945.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2014 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. - -// +build gccgo - -package cgotest - -//typedef void (*PFunc)(); -//PFunc success_cb; -import "C" - -//export Test -func Test() { - _ = C.success_cb -} diff --git a/libgo/misc/cgo/test/overlaydir_test.go b/libgo/misc/cgo/test/overlaydir_test.go index 1b5c67d..f651979 100644 --- a/libgo/misc/cgo/test/overlaydir_test.go +++ b/libgo/misc/cgo/test/overlaydir_test.go @@ -21,12 +21,9 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - symBase, err := filepath.Rel(srcRoot, dstRoot) + srcRoot, err := filepath.Abs(srcRoot) if err != nil { - symBase, err = filepath.Abs(srcRoot) - if err != nil { - return err - } + return err } return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { @@ -52,11 +49,11 @@ func overlayDir(dstRoot, srcRoot string) error { // 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) + return os.MkdirAll(dstPath, perm|0200) } // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + if err := os.Symlink(srcPath, dstPath); err == nil { return nil } diff --git a/libgo/misc/cgo/test/test.go b/libgo/misc/cgo/test/test.go index 2d060bf..b014899 100644 --- a/libgo/misc/cgo/test/test.go +++ b/libgo/misc/cgo/test/test.go @@ -115,6 +115,44 @@ int add(int x, int y) { return x+y; }; +// Following mimicks vulkan complex definitions for benchmarking cgocheck overhead. + +typedef uint32_t VkFlags; +typedef VkFlags VkDeviceQueueCreateFlags; +typedef uint32_t VkStructureType; + +typedef struct VkDeviceQueueCreateInfo { + VkStructureType sType; + const void* pNext; + VkDeviceQueueCreateFlags flags; + uint32_t queueFamilyIndex; + uint32_t queueCount; + const float* pQueuePriorities; +} VkDeviceQueueCreateInfo; + +typedef struct VkPhysicalDeviceFeatures { + uint32_t bools[56]; +} VkPhysicalDeviceFeatures; + +typedef struct VkDeviceCreateInfo { + VkStructureType sType; + const void* pNext; + VkFlags flags; + uint32_t queueCreateInfoCount; + const VkDeviceQueueCreateInfo* pQueueCreateInfos; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; + const VkPhysicalDeviceFeatures* pEnabledFeatures; +} VkDeviceCreateInfo; + +void handleComplexPointer(VkDeviceCreateInfo *a0) {} +void handleComplexPointer8( + VkDeviceCreateInfo *a0, VkDeviceCreateInfo *a1, VkDeviceCreateInfo *a2, VkDeviceCreateInfo *a3, + VkDeviceCreateInfo *a4, VkDeviceCreateInfo *a5, VkDeviceCreateInfo *a6, VkDeviceCreateInfo *a7 +) {} + // complex alignment struct { @@ -524,6 +562,11 @@ void issue8811Execute() { issue8811Init(); } +// issue 8945 + +typedef void (*PFunc8945)(); +PFunc8945 func8945; + // issue 9557 struct issue9557_t { @@ -993,10 +1036,55 @@ type Context struct { } func benchCgoCall(b *testing.B) { - const x = C.int(2) - const y = C.int(3) + b.Run("add-int", func(b *testing.B) { + const x = C.int(2) + const y = C.int(3) + + for i := 0; i < b.N; i++ { + C.add(x, y) + } + }) + + b.Run("one-pointer", func(b *testing.B) { + var a0 C.VkDeviceCreateInfo + for i := 0; i < b.N; i++ { + C.handleComplexPointer(&a0) + } + }) + b.Run("eight-pointers", func(b *testing.B) { + var a0, a1, a2, a3, a4, a5, a6, a7 C.VkDeviceCreateInfo + for i := 0; i < b.N; i++ { + C.handleComplexPointer8(&a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7) + } + }) + b.Run("eight-pointers-nil", func(b *testing.B) { + var a0, a1, a2, a3, a4, a5, a6, a7 *C.VkDeviceCreateInfo + for i := 0; i < b.N; i++ { + C.handleComplexPointer8(a0, a1, a2, a3, a4, a5, a6, a7) + } + }) + b.Run("eight-pointers-array", func(b *testing.B) { + var a [8]C.VkDeviceCreateInfo + for i := 0; i < b.N; i++ { + C.handleComplexPointer8(&a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7]) + } + }) + b.Run("eight-pointers-slice", func(b *testing.B) { + a := make([]C.VkDeviceCreateInfo, 8) + for i := 0; i < b.N; i++ { + C.handleComplexPointer8(&a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7]) + } + }) +} + +// Benchmark measuring overhead from Go to C and back to Go (via a callback) +func benchCallback(b *testing.B) { + var x = false for i := 0; i < b.N; i++ { - C.add(x, y) + nestedCall(func() { x = true }) + } + if !x { + b.Fatal("nestedCall was not invoked") } } @@ -1956,12 +2044,14 @@ func test27660(t *testing.T) { locks[j].Lock() ints[j]++ locks[j].Unlock() - // Avoid making the loop unpreemptible - // for gccgo. - if i%0x1000000 == 0 { + // needed for gccgo, to avoid creation of an + // unpreemptible "fast path" in this loop. Choice + // of (1<<24) is somewhat arbitrary. + if i%(1<<24) == 0 { runtime.Gosched() } i++ + } }() time.Sleep(time.Millisecond) diff --git a/libgo/misc/cgo/test/testdata/issue9026/issue9026.go b/libgo/misc/cgo/test/testdata/issue9026/issue9026.go index 149c265..ff269ca 100644 --- a/libgo/misc/cgo/test/testdata/issue9026/issue9026.go +++ b/libgo/misc/cgo/test/testdata/issue9026/issue9026.go @@ -4,9 +4,9 @@ package issue9026 // per-package counter used to create fresh identifiers. /* -typedef struct {} git_merge_file_input; +typedef struct { int i; } git_merge_file_input; -typedef struct {} git_merge_file_options; +typedef struct { int j; } git_merge_file_options; void git_merge_file( git_merge_file_input *in, @@ -29,7 +29,7 @@ func Test(t *testing.T) { // Brittle: the assertion may fail spuriously when the algorithm // changes, but should remain stable otherwise. got := fmt.Sprintf("%T %T", in, opts) - want := "issue9026._Ctype_struct___0 *issue9026._Ctype_struct___0" + want := "issue9026._Ctype_struct___0 *issue9026._Ctype_struct___1" if got != want { t.Errorf("Non-deterministic type names: got %s, want %s", got, want) } diff --git a/libgo/misc/cgo/test/testx.go b/libgo/misc/cgo/test/testx.go index 67def90..42979b5 100644 --- a/libgo/misc/cgo/test/testx.go +++ b/libgo/misc/cgo/test/testx.go @@ -102,12 +102,28 @@ static void issue7978c(uint32_t *sync) { // #include'd twice. No runtime test; just make sure it compiles. #include "issue8331.h" +// issue 8945 + +typedef void (*PFunc8945)(); +extern PFunc8945 func8945; // definition is in test.go + // issue 20910 void callMulti(void); // issue 28772 part 2 - part 1 in issuex.go #define issue28772Constant2 2 + +// issue 31891 +typedef struct { + long obj; +} Issue31891A; + +typedef struct { + long obj; +} Issue31891B; + +void callIssue31891(void); */ import "C" @@ -503,6 +519,13 @@ func test7978(t *testing.T) { var issue8331Var C.issue8331 +// issue 8945 + +//export Test8945 +func Test8945() { + _ = C.func8945 +} + // issue 20910 //export multi @@ -517,3 +540,15 @@ func test20910(t *testing.T) { // issue 28772 part 2 const issue28772Constant2 = C.issue28772Constant2 + +// issue 31891 + +//export useIssue31891A +func useIssue31891A(c *C.Issue31891A) {} + +//export useIssue31891B +func useIssue31891B(c *C.Issue31891B) {} + +func test31891(t *testing.T) { + C.callIssue31891() +} diff --git a/libgo/misc/cgo/testasan/main.go b/libgo/misc/cgo/testasan/main.go index 1837c6c..bc77678 100644 --- a/libgo/misc/cgo/testasan/main.go +++ b/libgo/misc/cgo/testasan/main.go @@ -36,14 +36,21 @@ thread(void *p) import "C" import ( + "fmt" + "os" + "path/filepath" "time" ) func main() { + start := time.Now() + // ensure that we can function normally var v [][]byte for i := 0; i < 1000; i++ { time.Sleep(10 * time.Microsecond) v = append(v, make([]byte, 64<<10)) } + + fmt.Printf("ok\t%s\t%s\n", filepath.Base(os.Args[0]), time.Since(start).Round(time.Millisecond)) } diff --git a/libgo/misc/cgo/testcarchive/carchive_test.go b/libgo/misc/cgo/testcarchive/carchive_test.go index 7051670..98cd41a 100644 --- a/libgo/misc/cgo/testcarchive/carchive_test.go +++ b/libgo/misc/cgo/testcarchive/carchive_test.go @@ -36,7 +36,10 @@ var exeSuffix string var GOOS, GOARCH, GOPATH string var libgodir string +var testWork bool // If true, preserve temporary directories. + func TestMain(m *testing.M) { + flag.BoolVar(&testWork, "testwork", false, "if true, log and preserve the test's temporary working directory") flag.Parse() if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n") @@ -54,7 +57,11 @@ func testMain(m *testing.M) int { if err != nil { log.Panic(err) } - defer os.RemoveAll(GOPATH) + if testWork { + log.Println(GOPATH) + } else { + defer os.RemoveAll(GOPATH) + } os.Setenv("GOPATH", GOPATH) // Copy testdata into GOPATH/src/testarchive, along with a go.mod file @@ -164,6 +171,38 @@ func cmdToRun(name string) []string { return []string{executor, name} } +// genHeader writes a C header file for the C-exported declarations found in .go +// source files in dir. +// +// TODO(golang.org/issue/35715): This should be simpler. +func genHeader(t *testing.T, header, dir string) { + t.Helper() + + // The 'cgo' command generates a number of additional artifacts, + // but we're only interested in the header. + // Shunt the rest of the outputs to a temporary directory. + objDir, err := ioutil.TempDir(GOPATH, "_obj") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(objDir) + + files, err := filepath.Glob(filepath.Join(dir, "*.go")) + if err != nil { + t.Fatal(err) + } + + cmd := exec.Command("go", "tool", "cgo", + "-objdir", objDir, + "-exportheader", header) + cmd.Args = append(cmd.Args, files...) + t.Log(cmd.Args) + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } +} + func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { t.Helper() cmd := exec.Command(buildcmd[0], buildcmd[1:]...) @@ -172,10 +211,12 @@ func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { t.Logf("%s", out) t.Fatal(err) } - defer func() { - os.Remove(libgoa) - os.Remove(libgoh) - }() + if !testWork { + defer func() { + os.Remove(libgoa) + os.Remove(libgoh) + }() + } ccArgs := append(cc, "-o", exe, "main.c") if GOOS == "windows" { @@ -191,7 +232,9 @@ func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { t.Logf("%s", out) t.Fatal(err) } - defer os.Remove(exe) + if !testWork { + defer os.Remove(exe) + } binArgs := append(cmdToRun(exe), "arg1", "arg2") cmd = exec.Command(binArgs[0], binArgs[1:]...) @@ -227,17 +270,27 @@ func checkLineComments(t *testing.T, hdrname string) { } func TestInstall(t *testing.T) { - defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) + if !testWork { + defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) + } libgoa := "libgo.a" if runtime.Compiler == "gccgo" { libgoa = "liblibgo.a" } + // Generate the p.h header file. + // + // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that + // would also attempt to install transitive standard-library dependencies to + // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may + // be running this test in a GOROOT owned by root.) + genHeader(t, "p.h", "./p") + testInstall(t, "./testp1"+exeSuffix, filepath.Join(libgodir, libgoa), filepath.Join(libgodir, "libgo.h"), - "go", "install", "-i", "-buildmode=c-archive", "./libgo") + "go", "install", "-buildmode=c-archive", "./libgo") // Test building libgo other than installing it. // Header files are now present. @@ -259,12 +312,14 @@ func TestEarlySignalHandler(t *testing.T) { t.Skip("skipping signal test on Windows") } - defer func() { - os.Remove("libgo2.a") - os.Remove("libgo2.h") - os.Remove("testp") - os.RemoveAll(filepath.Join(GOPATH, "pkg")) - }() + if !testWork { + defer func() { + os.Remove("libgo2.a") + os.Remove("libgo2.h") + os.Remove("testp") + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") if out, err := cmd.CombinedOutput(); err != nil { @@ -282,7 +337,13 @@ func TestEarlySignalHandler(t *testing.T) { t.Fatal(err) } - if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { + darwin := "0" + if runtime.GOOS == "darwin" { + darwin = "1" + } + cmd = exec.Command(bin[0], append(bin[1:], darwin)...) + + if out, err := cmd.CombinedOutput(); err != nil { t.Logf("%s", out) t.Fatal(err) } @@ -291,12 +352,14 @@ func TestEarlySignalHandler(t *testing.T) { func TestSignalForwarding(t *testing.T) { checkSignalForwardingTest(t) - defer func() { - os.Remove("libgo2.a") - os.Remove("libgo2.h") - os.Remove("testp") - os.RemoveAll(filepath.Join(GOPATH, "pkg")) - }() + if !testWork { + defer func() { + os.Remove("libgo2.a") + os.Remove("libgo2.h") + os.Remove("testp") + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") if out, err := cmd.CombinedOutput(); err != nil { @@ -320,12 +383,15 @@ func TestSignalForwarding(t *testing.T) { t.Logf("%s", out) expectSignal(t, err, syscall.SIGSEGV) - // Test SIGPIPE forwarding - cmd = exec.Command(bin[0], append(bin[1:], "3")...) + // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. + if runtime.GOOS != "darwin" { + // Test SIGPIPE forwarding + cmd = exec.Command(bin[0], append(bin[1:], "3")...) - out, err = cmd.CombinedOutput() - t.Logf("%s", out) - expectSignal(t, err, syscall.SIGPIPE) + out, err = cmd.CombinedOutput() + t.Logf("%s", out) + expectSignal(t, err, syscall.SIGPIPE) + } } func TestSignalForwardingExternal(t *testing.T) { @@ -336,12 +402,14 @@ func TestSignalForwardingExternal(t *testing.T) { } checkSignalForwardingTest(t) - defer func() { - os.Remove("libgo2.a") - os.Remove("libgo2.h") - os.Remove("testp") - os.RemoveAll(filepath.Join(GOPATH, "pkg")) - }() + if !testWork { + defer func() { + os.Remove("libgo2.a") + os.Remove("libgo2.h") + os.Remove("testp") + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") if out, err := cmd.CombinedOutput(); err != nil { @@ -451,12 +519,14 @@ func TestOsSignal(t *testing.T) { t.Skip("skipping signal test on Windows") } - defer func() { - os.Remove("libgo3.a") - os.Remove("libgo3.h") - os.Remove("testp") - os.RemoveAll(filepath.Join(GOPATH, "pkg")) - }() + if !testWork { + defer func() { + os.Remove("libgo3.a") + os.Remove("libgo3.h") + os.Remove("testp") + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3") if out, err := cmd.CombinedOutput(); err != nil { @@ -486,12 +556,14 @@ func TestSigaltstack(t *testing.T) { t.Skip("skipping signal test on Windows") } - defer func() { - os.Remove("libgo4.a") - os.Remove("libgo4.h") - os.Remove("testp") - os.RemoveAll(filepath.Join(GOPATH, "pkg")) - }() + if !testWork { + defer func() { + os.Remove("libgo4.a") + os.Remove("libgo4.h") + os.Remove("testp") + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4") if out, err := cmd.CombinedOutput(); err != nil { @@ -535,13 +607,15 @@ func TestExtar(t *testing.T) { t.Skip("shell scripts are not executable on iOS hosts") } - defer func() { - os.Remove("libgo4.a") - os.Remove("libgo4.h") - os.Remove("testar") - os.Remove("testar.ran") - os.RemoveAll(filepath.Join(GOPATH, "pkg")) - }() + if !testWork { + defer func() { + os.Remove("libgo4.a") + os.Remove("libgo4.h") + os.Remove("testar") + os.Remove("testar.ran") + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } os.Remove("testar") dir, err := os.Getwd() @@ -575,12 +649,22 @@ func TestPIE(t *testing.T) { t.Skipf("skipping PIE test on %s", GOOS) } - defer func() { - os.Remove("testp" + exeSuffix) - os.RemoveAll(filepath.Join(GOPATH, "pkg")) - }() + if !testWork { + defer func() { + os.Remove("testp" + exeSuffix) + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } + + // Generate the p.h header file. + // + // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that + // would also attempt to install transitive standard-library dependencies to + // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may + // be running this test in a GOROOT owned by root.) + genHeader(t, "p.h", "./p") - cmd := exec.Command("go", "install", "-i", "-buildmode=c-archive", "./libgo") + cmd := exec.Command("go", "install", "-buildmode=c-archive", "./libgo") if out, err := cmd.CombinedOutput(); err != nil { t.Logf("%s", out) t.Fatal(err) @@ -660,11 +744,13 @@ func TestSIGPROF(t *testing.T) { t.Parallel() - defer func() { - os.Remove("testp6" + exeSuffix) - os.Remove("libgo6.a") - os.Remove("libgo6.h") - }() + if !testWork { + defer func() { + os.Remove("testp6" + exeSuffix) + os.Remove("libgo6.a") + os.Remove("libgo6.h") + }() + } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6") if out, err := cmd.CombinedOutput(); err != nil { @@ -700,10 +786,12 @@ func TestCompileWithoutShared(t *testing.T) { // For simplicity, reuse the signal forwarding test. checkSignalForwardingTest(t) - defer func() { - os.Remove("libgo2.a") - os.Remove("libgo2.h") - }() + if !testWork { + defer func() { + os.Remove("libgo2.a") + os.Remove("libgo2.h") + }() + } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2") t.Log(cmd.Args) @@ -742,23 +830,35 @@ func TestCompileWithoutShared(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(exe) + if !testWork { + defer os.Remove(exe) + } - binArgs := append(cmdToRun(exe), "3") + binArgs := append(cmdToRun(exe), "1") t.Log(binArgs) out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() t.Logf("%s", out) - expectSignal(t, err, syscall.SIGPIPE) + expectSignal(t, err, syscall.SIGSEGV) + + // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. + if runtime.GOOS != "darwin" { + binArgs := append(cmdToRun(exe), "3") + t.Log(binArgs) + out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() + t.Logf("%s", out) + expectSignal(t, err, syscall.SIGPIPE) + } } -// Test that installing a second time recreates the header files. +// Test that installing a second time recreates the header file. func TestCachedInstall(t *testing.T) { - defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) + if !testWork { + defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) + } - h1 := filepath.Join(libgodir, "libgo.h") - h2 := filepath.Join(libgodir, "p.h") + h := filepath.Join(libgodir, "libgo.h") - buildcmd := []string{"go", "install", "-i", "-buildmode=c-archive", "./libgo"} + buildcmd := []string{"go", "install", "-buildmode=c-archive", "./libgo"} cmd := exec.Command(buildcmd[0], buildcmd[1:]...) t.Log(buildcmd) @@ -767,17 +867,11 @@ func TestCachedInstall(t *testing.T) { t.Fatal(err) } - if _, err := os.Stat(h1); err != nil { + if _, err := os.Stat(h); err != nil { t.Errorf("libgo.h not installed: %v", err) } - if _, err := os.Stat(h2); err != nil { - t.Errorf("p.h not installed: %v", err) - } - if err := os.Remove(h1); err != nil { - t.Fatal(err) - } - if err := os.Remove(h2); err != nil { + if err := os.Remove(h); err != nil { t.Fatal(err) } @@ -788,10 +882,58 @@ func TestCachedInstall(t *testing.T) { t.Fatal(err) } - if _, err := os.Stat(h1); err != nil { + if _, err := os.Stat(h); err != nil { t.Errorf("libgo.h not installed in second run: %v", err) } - if _, err := os.Stat(h2); err != nil { - t.Errorf("p.h not installed in second run: %v", err) +} + +// Issue 35294. +func TestManyCalls(t *testing.T) { + t.Parallel() + + if !testWork { + defer func() { + os.Remove("testp7" + exeSuffix) + os.Remove("libgo7.a") + os.Remove("libgo7.h") + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7") + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + checkLineComments(t, "libgo7.h") + + ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a") + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + argv := cmdToRun("./testp7") + cmd = exec.Command(argv[0], argv[1:]...) + var sb strings.Builder + cmd.Stdout = &sb + cmd.Stderr = &sb + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + + timer := time.AfterFunc(time.Minute, + func() { + t.Error("test program timed out") + cmd.Process.Kill() + }, + ) + defer timer.Stop() + + if err := cmd.Wait(); err != nil { + t.Log(sb.String()) + t.Error(err) } } diff --git a/libgo/misc/cgo/testcarchive/overlaydir_test.go b/libgo/misc/cgo/testcarchive/overlaydir_test.go index 68878e4..67974c5 100644 --- a/libgo/misc/cgo/testcarchive/overlaydir_test.go +++ b/libgo/misc/cgo/testcarchive/overlaydir_test.go @@ -21,12 +21,9 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - symBase, err := filepath.Rel(srcRoot, dstRoot) + srcRoot, err := filepath.Abs(srcRoot) if err != nil { - symBase, err = filepath.Abs(srcRoot) - if err != nil { - return err - } + return err } return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { @@ -52,11 +49,11 @@ func overlayDir(dstRoot, srcRoot string) error { // 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) + return os.MkdirAll(dstPath, perm|0200) } // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + if err := os.Symlink(srcPath, dstPath); err == nil { return nil } diff --git a/libgo/misc/cgo/testcarchive/testdata/libgo7/sink.go b/libgo/misc/cgo/testcarchive/testdata/libgo7/sink.go new file mode 100644 index 0000000..d61638b --- /dev/null +++ b/libgo/misc/cgo/testcarchive/testdata/libgo7/sink.go @@ -0,0 +1,17 @@ +// 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 main + +import "C" + +var sink []byte + +//export GoFunction7 +func GoFunction7() { + sink = make([]byte, 4096) +} + +func main() { +} diff --git a/libgo/misc/cgo/testcarchive/testdata/main2.c b/libgo/misc/cgo/testcarchive/testdata/main2.c index 769cd49..da35673 100644 --- a/libgo/misc/cgo/testcarchive/testdata/main2.c +++ b/libgo/misc/cgo/testcarchive/testdata/main2.c @@ -123,8 +123,12 @@ int main(int argc, char** argv) { sigset_t mask; int i; struct timespec ts; + int darwin; + + darwin = atoi(argv[1]); + + verbose = argc > 2; - verbose = argc > 1; setvbuf(stdout, NULL, _IONBF, 0); // Call setsid so that we can use kill(0, SIGIO) below. @@ -186,22 +190,25 @@ int main(int argc, char** argv) { printf("provoking SIGPIPE\n"); } - GoRaiseSIGPIPE(); + // SIGPIPE is never forwarded on Darwin, see golang.org/issue/33384. + if (!darwin) { + GoRaiseSIGPIPE(); - if (verbose) { - printf("waiting for sigpipeSeen\n"); - } + if (verbose) { + printf("waiting for sigpipeSeen\n"); + } - // Wait until the signal has been delivered. - i = 0; - while (!sigpipeSeen) { - ts.tv_sec = 0; - ts.tv_nsec = 1000000; - nanosleep(&ts, NULL); - i++; - if (i > 5000) { - fprintf(stderr, "looping too long waiting for SIGPIPE\n"); - exit(EXIT_FAILURE); + // Wait until the signal has been delivered. + i = 0; + while (!sigpipeSeen) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for SIGPIPE\n"); + exit(EXIT_FAILURE); + } } } diff --git a/libgo/misc/cgo/testcarchive/testdata/main3.c b/libgo/misc/cgo/testcarchive/testdata/main3.c index 60a16cf..4d11d9c 100644 --- a/libgo/misc/cgo/testcarchive/testdata/main3.c +++ b/libgo/misc/cgo/testcarchive/testdata/main3.c @@ -12,6 +12,7 @@ #include <time.h> #include <sched.h> #include <unistd.h> +#include <pthread.h> #include "libgo3.h" @@ -51,11 +52,18 @@ static void init() { } } +static void *provokeSIGPIPE(void *arg) { + ProvokeSIGPIPE(); + return NULL; +} + int main(int argc, char** argv) { int verbose; struct sigaction sa; int i; struct timespec ts; + int res; + pthread_t tid; verbose = argc > 2; setvbuf(stdout, NULL, _IONBF, 0); @@ -68,6 +76,19 @@ int main(int argc, char** argv) { // a non-default SIGPIPE handler before the runtime initializes. ProvokeSIGPIPE(); + // Test that SIGPIPE on a non-main thread is also handled by Go. + res = pthread_create(&tid, NULL, provokeSIGPIPE, NULL); + if (res != 0) { + fprintf(stderr, "pthread_create: %s\n", strerror(res)); + exit(EXIT_FAILURE); + } + + res = pthread_join(tid, NULL); + if (res != 0) { + fprintf(stderr, "pthread_join: %s\n", strerror(res)); + exit(EXIT_FAILURE); + } + if (verbose) { printf("calling sigaction\n"); } diff --git a/libgo/misc/cgo/testcarchive/testdata/main7.c b/libgo/misc/cgo/testcarchive/testdata/main7.c new file mode 100644 index 0000000..2c6d98d --- /dev/null +++ b/libgo/misc/cgo/testcarchive/testdata/main7.c @@ -0,0 +1,18 @@ +// 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. + +// Test that lots of calls don't deadlock. + +#include <stdio.h> + +#include "libgo7.h" + +int main() { + int i; + + for (i = 0; i < 100000; i++) { + GoFunction7(); + } + return 0; +} diff --git a/libgo/misc/cgo/testcshared/cshared_test.go b/libgo/misc/cgo/testcshared/cshared_test.go index daef3a9..cb95153 100644 --- a/libgo/misc/cgo/testcshared/cshared_test.go +++ b/libgo/misc/cgo/testcshared/cshared_test.go @@ -103,7 +103,7 @@ func testMain(m *testing.M) int { // TODO(crawshaw): can we do better? cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...) case "android": - cc = append(cc, "-pie", "-fuse-ld=gold") + cc = append(cc, "-pie") } libgodir := GOOS + "_" + GOARCH switch GOOS { @@ -130,8 +130,6 @@ func testMain(m *testing.M) int { defer os.RemoveAll(GOPATH) os.Setenv("GOPATH", GOPATH) - // Copy testdata into GOPATH/src/testarchive, along with a go.mod file - // declaring the same path. modRoot := filepath.Join(GOPATH, "src", "testcshared") if err := overlayDir(modRoot, "testdata"); err != nil { log.Panic(err) @@ -257,14 +255,38 @@ func runCC(t *testing.T, args ...string) string { } func createHeaders() error { - args := []string{"go", "install", "-i", "-buildmode=c-shared", - "-installsuffix", "testcshared", "./libgo"} + // The 'cgo' command generates a number of additional artifacts, + // but we're only interested in the header. + // Shunt the rest of the outputs to a temporary directory. + objDir, err := ioutil.TempDir("", "testcshared_obj") + if err != nil { + return err + } + defer os.RemoveAll(objDir) + + // Generate a C header file for p, which is a non-main dependency + // of main package libgo. + // + // TODO(golang.org/issue/35715): This should be simpler. + args := []string{"go", "tool", "cgo", + "-objdir", objDir, + "-exportheader", "p.h", + filepath.Join(".", "p", "p.go")} cmd := exec.Command(args[0], args[1:]...) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out) } + // Generate a C header file for libgo itself. + args = []string{"go", "install", "-buildmode=c-shared", + "-installsuffix", "testcshared", "./libgo"} + cmd = exec.Command(args[0], args[1:]...) + out, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out) + } + args = []string{"go", "build", "-buildmode=c-shared", "-installsuffix", "testcshared", "-o", libgoname, @@ -522,7 +544,7 @@ func TestPIE(t *testing.T) { } } -// Test that installing a second time recreates the header files. +// Test that installing a second time recreates the header file. func TestCachedInstall(t *testing.T) { tmpdir, err := ioutil.TempDir("", "cshared") if err != nil { @@ -536,7 +558,7 @@ func TestCachedInstall(t *testing.T) { env := append(os.Environ(), "GOPATH="+tmpdir, "GOBIN="+filepath.Join(tmpdir, "bin")) - buildcmd := []string{"go", "install", "-x", "-i", "-buildmode=c-shared", "-installsuffix", "testcshared", "./libgo"} + buildcmd := []string{"go", "install", "-x", "-buildmode=c-shared", "-installsuffix", "testcshared", "./libgo"} cmd := exec.Command(buildcmd[0], buildcmd[1:]...) cmd.Dir = filepath.Join(tmpdir, "src", "testcshared") @@ -577,16 +599,10 @@ func TestCachedInstall(t *testing.T) { if libgoh == "" { t.Fatal("libgo.h not installed") } - if ph == "" { - t.Fatal("p.h not installed") - } if err := os.Remove(libgoh); err != nil { t.Fatal(err) } - if err := os.Remove(ph); err != nil { - t.Fatal(err) - } cmd = exec.Command(buildcmd[0], buildcmd[1:]...) cmd.Dir = filepath.Join(tmpdir, "src", "testcshared") @@ -601,9 +617,6 @@ func TestCachedInstall(t *testing.T) { if _, err := os.Stat(libgoh); err != nil { t.Errorf("libgo.h not installed in second run: %v", err) } - if _, err := os.Stat(ph); err != nil { - t.Errorf("p.h not installed in second run: %v", err) - } } // copyFile copies src to dst. diff --git a/libgo/misc/cgo/testcshared/overlaydir_test.go b/libgo/misc/cgo/testcshared/overlaydir_test.go index 1eaabf6..85d6b44 100644 --- a/libgo/misc/cgo/testcshared/overlaydir_test.go +++ b/libgo/misc/cgo/testcshared/overlaydir_test.go @@ -21,12 +21,9 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - symBase, err := filepath.Rel(srcRoot, dstRoot) + srcRoot, err := filepath.Abs(srcRoot) if err != nil { - symBase, err = filepath.Abs(srcRoot) - if err != nil { - return err - } + return err } return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { @@ -52,11 +49,11 @@ func overlayDir(dstRoot, srcRoot string) error { // 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) + return os.MkdirAll(dstPath, perm|0200) } // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + if err := os.Symlink(srcPath, dstPath); err == nil { return nil } diff --git a/libgo/misc/cgo/testgodefs/test.bash b/libgo/misc/cgo/testgodefs/test.bash deleted file mode 100644 index e4ce2ee..0000000 --- a/libgo/misc/cgo/testgodefs/test.bash +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2014 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. - -# We are testing cgo -godefs, which translates Go files that use -# import "C" into Go files with Go definitions of types defined in the -# import "C" block. Add more tests here. -FILE_PREFIXES="anonunion issue8478 fieldtypedef" - -cd testdata - -RM= -for FP in $FILE_PREFIXES -do - go tool cgo -godefs -srcdir . ${FP}.go > ${FP}_defs.go - RM="${RM} ${FP}_defs.go" -done - -go build -o testgodefs . && ./testgodefs -EXIT=$? -rm -rf _obj testgodefs ${RM} -exit $EXIT diff --git a/libgo/misc/cgo/testgodefs/testgodefs_test.go b/libgo/misc/cgo/testgodefs/testgodefs_test.go new file mode 100644 index 0000000..c02c3ff --- /dev/null +++ b/libgo/misc/cgo/testgodefs/testgodefs_test.go @@ -0,0 +1,83 @@ +// 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 testgodefs + +import ( + "bytes" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +// We are testing cgo -godefs, which translates Go files that use +// import "C" into Go files with Go definitions of types defined in the +// import "C" block. Add more tests here. +var filePrefixes = []string{ + "anonunion", + "issue8478", + "fieldtypedef", +} + +func TestGoDefs(t *testing.T) { + testdata, err := filepath.Abs("testdata") + if err != nil { + t.Fatal(err) + } + + gopath, err := ioutil.TempDir("", "testgodefs-gopath") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(gopath) + + dir := filepath.Join(gopath, "src", "testgodefs") + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatal(err) + } + + for _, fp := range filePrefixes { + cmd := exec.Command("go", "tool", "cgo", + "-godefs", + "-srcdir", testdata, + "-objdir", dir, + fp+".go") + cmd.Stderr = new(bytes.Buffer) + + out, err := cmd.Output() + if err != nil { + t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) + } + + if err := ioutil.WriteFile(filepath.Join(dir, fp+"_defs.go"), out, 0644); err != nil { + t.Fatal(err) + } + } + + main, err := ioutil.ReadFile(filepath.Join("testdata", "main.go")) + if err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(filepath.Join(dir, "main.go"), main, 0644); err != nil { + t.Fatal(err) + } + + if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module testgodefs\ngo 1.14\n"), 0644); err != nil { + t.Fatal(err) + } + + // Use 'go run' to build and run the resulting binary in a single step, + // instead of invoking 'go build' and the resulting binary separately, so that + // this test can pass on mobile builders, which do not copy artifacts back + // from remote invocations. + cmd := exec.Command("go", "run", ".") + cmd.Env = append(os.Environ(), "GOPATH="+gopath) + cmd.Dir = dir + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("%s [%s]: %v\n%s", strings.Join(cmd.Args, " "), dir, err, out) + } +} diff --git a/libgo/misc/cgo/testplugin/overlaydir_test.go b/libgo/misc/cgo/testplugin/overlaydir_test.go index b68436a..e2c32d8 100644 --- a/libgo/misc/cgo/testplugin/overlaydir_test.go +++ b/libgo/misc/cgo/testplugin/overlaydir_test.go @@ -21,12 +21,9 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - symBase, err := filepath.Rel(srcRoot, dstRoot) + srcRoot, err := filepath.Abs(srcRoot) if err != nil { - symBase, err = filepath.Abs(srcRoot) - if err != nil { - return err - } + return err } return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { @@ -52,11 +49,11 @@ func overlayDir(dstRoot, srcRoot string) error { // 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) + return os.MkdirAll(dstPath, perm|0200) } // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + if err := os.Symlink(srcPath, dstPath); err == nil { return nil } diff --git a/libgo/misc/cgo/testplugin/plugin_test.go b/libgo/misc/cgo/testplugin/plugin_test.go index 7e2b6eb..ab98f61 100644 --- a/libgo/misc/cgo/testplugin/plugin_test.go +++ b/libgo/misc/cgo/testplugin/plugin_test.go @@ -14,7 +14,6 @@ import ( "os" "os/exec" "path/filepath" - "runtime" "strings" "testing" "time" @@ -71,7 +70,7 @@ func testMain(m *testing.M) int { os.Setenv("LD_LIBRARY_PATH", modRoot) - goCmd(nil, "build", "-i", "-buildmode=plugin", "./plugin1") + goCmd(nil, "build", "-buildmode=plugin", "./plugin1") goCmd(nil, "build", "-buildmode=plugin", "./plugin2") so, err := ioutil.ReadFile("plugin2.so") if err != nil { @@ -114,11 +113,7 @@ func run(t *testing.T, bin string, args ...string) string { func TestDWARFSections(t *testing.T) { // test that DWARF sections are emitted for plugins and programs importing "plugin" - if runtime.GOOS != "darwin" { - // On macOS, for some reason, the linker doesn't add debug sections to .so, - // see issue #27502. - goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse") - } + goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse") goCmd(t, "run", "./checkdwarf/main.go", "./host.exe", "main.main") } diff --git a/libgo/misc/cgo/testshared/overlaydir_test.go b/libgo/misc/cgo/testshared/overlaydir_test.go index 68be056..eb587a2 100644 --- a/libgo/misc/cgo/testshared/overlaydir_test.go +++ b/libgo/misc/cgo/testshared/overlaydir_test.go @@ -21,12 +21,9 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - symBase, err := filepath.Rel(srcRoot, dstRoot) + srcRoot, err := filepath.Abs(srcRoot) if err != nil { - symBase, err = filepath.Abs(srcRoot) - if err != nil { - return err - } + return err } return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { @@ -52,11 +49,11 @@ func overlayDir(dstRoot, srcRoot string) error { // 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) + return os.MkdirAll(dstPath, perm|0200) } // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + if err := os.Symlink(srcPath, dstPath); err == nil { return nil } diff --git a/libgo/misc/cgo/testshared/shared_test.go b/libgo/misc/cgo/testshared/shared_test.go index 9d16338..b9ef6da 100644 --- a/libgo/misc/cgo/testshared/shared_test.go +++ b/libgo/misc/cgo/testshared/shared_test.go @@ -9,31 +9,33 @@ import ( "bytes" "debug/elf" "encoding/binary" - "errors" "flag" "fmt" "go/build" "io" "io/ioutil" "log" - "math/rand" "os" "os/exec" "path/filepath" "regexp" "runtime" + "sort" "strings" "testing" "time" ) -var gopathInstallDir, gorootInstallDir, suffix string +var gopathInstallDir, gorootInstallDir string // This is the smallest set of packages we can link into a shared // library (runtime/cgo is built implicitly). var minpkgs = []string{"runtime", "sync/atomic"} var soname = "libruntime,sync-atomic.so" +var testX = flag.Bool("testx", false, "if true, pass -x to 'go' subcommands invoked by the test") +var testWork = flag.Bool("testwork", false, "if true, log and do not delete the temporary working directory") + // run runs a command and calls t.Errorf if it fails. func run(t *testing.T, msg string, args ...string) { c := exec.Command(args[0], args[1:]...) @@ -45,31 +47,34 @@ 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) string { - newargs := []string{args[0], "-installsuffix=" + suffix} - if testing.Verbose() { + newargs := []string{args[0]} + if *testX { 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(args, " ")) + c.Stderr = stderr + + if testing.Verbose() && t == nil { + fmt.Fprintf(os.Stderr, "+ go %s\n", strings.Join(args, " ")) c.Stderr = os.Stderr - stderr.WriteString("(output above)") - } else { - c.Stderr = stderr } - output, err = c.Output() + output, err := c.Output() if err != nil { if t != nil { 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, stderr) + // Panic instead of using log.Fatalf so that deferred cleanup may run in testMain. + log.Panicf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr) + } + } + if testing.Verbose() && t != nil { + t.Logf("go %s", strings.Join(args, " ")) + if stderr.Len() > 0 { + t.Logf("%s", stderr) } } return string(bytes.TrimSpace(output)) @@ -77,73 +82,61 @@ func goCmd(t *testing.T, args ...string) string { // TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit). func testMain(m *testing.M) (int, error) { - // Because go install -buildmode=shared $standard_library_package always - // installs into $GOROOT, here are some gymnastics to come up with a unique - // installsuffix to use in this test that we can clean up afterwards. - myContext := build.Default - runtimeP, err := myContext.Import("runtime", ".", build.ImportComment) + workDir, err := ioutil.TempDir("", "shared_test") if err != nil { - return 0, fmt.Errorf("import failed: %v", err) - } - for i := 0; i < 10000; i++ { - try := fmt.Sprintf("%s_%d_dynlink", runtimeP.PkgTargetRoot, rand.Int63()) - err = os.Mkdir(try, 0700) - if os.IsExist(err) { - continue - } - if err == nil { - gorootInstallDir = try - } - break - } - if err != nil { - return 0, fmt.Errorf("can't create temporary directory: %v", err) + return 0, err } - if gorootInstallDir == "" { - return 0, errors.New("could not create temporary directory after 10000 tries") + if *testWork || testing.Verbose() { + fmt.Printf("+ mkdir -p %s\n", workDir) } - if testing.Verbose() { - fmt.Printf("+ mkdir -p %s\n", gorootInstallDir) + if !*testWork { + defer os.RemoveAll(workDir) } - defer os.RemoveAll(gorootInstallDir) // Some tests need to edit the source in GOPATH, so copy this directory to a // temporary directory and chdir to that. - gopath, err := ioutil.TempDir("", "testshared") + gopath := filepath.Join(workDir, "gopath") + modRoot, err := cloneTestdataModule(gopath) if err != nil { - return 0, fmt.Errorf("TempDir failed: %v", err) - } - if testing.Verbose() { - 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 } if testing.Verbose() { + fmt.Printf("+ export GOPATH=%s\n", gopath) fmt.Printf("+ cd %s\n", modRoot) } + os.Setenv("GOPATH", gopath) os.Chdir(modRoot) os.Setenv("PWD", modRoot) - if err := ioutil.WriteFile("go.mod", []byte("module testshared\n"), 0666); err != nil { + + // The test also needs to install libraries into GOROOT/pkg, so copy the + // subset of GOROOT that we need. + // + // TODO(golang.org/issue/28553): Rework -buildmode=shared so that it does not + // need to write to GOROOT. + goroot := filepath.Join(workDir, "goroot") + if err := cloneGOROOTDeps(goroot); err != nil { return 0, err } - - os.Setenv("GOPATH", gopath) if testing.Verbose() { - fmt.Printf("+ export GOPATH=%s\n", gopath) + fmt.Fprintf(os.Stderr, "+ export GOROOT=%s\n", goroot) } + os.Setenv("GOROOT", goroot) + + myContext := build.Default + myContext.GOROOT = goroot myContext.GOPATH = gopath + runtimeP, err := myContext.Import("runtime", ".", build.ImportComment) + if err != nil { + return 0, fmt.Errorf("import failed: %v", err) + } + gorootInstallDir = runtimeP.PkgTargetRoot + "_dynlink" // 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 // built here. - suffix = strings.Split(filepath.Base(gorootInstallDir), "_")[2] goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs...)...) - myContext.InstallSuffix = suffix + "_dynlink" + myContext.InstallSuffix = "_dynlink" depP, err := myContext.Import("./depBase", ".", build.ImportComment) if err != nil { return 0, fmt.Errorf("import failed: %v", err) @@ -171,6 +164,75 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } +// cloneTestdataModule clones the packages from src/testshared into gopath. +// It returns the directory within gopath at which the module root is located. +func cloneTestdataModule(gopath string) (string, error) { + modRoot := filepath.Join(gopath, "src", "testshared") + if err := overlayDir(modRoot, "testdata"); err != nil { + return "", err + } + if err := ioutil.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module testshared\n"), 0644); err != nil { + return "", err + } + return modRoot, nil +} + +// cloneGOROOTDeps copies (or symlinks) the portions of GOROOT/src and +// GOROOT/pkg relevant to this test into the given directory. +// It must be run from within the testdata module. +func cloneGOROOTDeps(goroot string) error { + oldGOROOT := strings.TrimSpace(goCmd(nil, "env", "GOROOT")) + if oldGOROOT == "" { + return fmt.Errorf("go env GOROOT returned an empty string") + } + + // Before we clone GOROOT, figure out which packages we need to copy over. + listArgs := []string{ + "list", + "-deps", + "-f", "{{if and .Standard (not .ForTest)}}{{.ImportPath}}{{end}}", + } + stdDeps := goCmd(nil, append(listArgs, minpkgs...)...) + testdataDeps := goCmd(nil, append(listArgs, "-test", "./...")...) + + pkgs := append(strings.Split(strings.TrimSpace(stdDeps), "\n"), + strings.Split(strings.TrimSpace(testdataDeps), "\n")...) + sort.Strings(pkgs) + var pkgRoots []string + for _, pkg := range pkgs { + parentFound := false + for _, prev := range pkgRoots { + if strings.HasPrefix(pkg, prev) { + // We will copy in the source for pkg when we copy in prev. + parentFound = true + break + } + } + if !parentFound { + pkgRoots = append(pkgRoots, pkg) + } + } + + gorootDirs := []string{ + "pkg/tool", + "pkg/include", + } + for _, pkg := range pkgRoots { + gorootDirs = append(gorootDirs, filepath.Join("src", pkg)) + } + + for _, dir := range gorootDirs { + if testing.Verbose() { + fmt.Fprintf(os.Stderr, "+ cp -r %s %s\n", filepath.Join(goroot, dir), filepath.Join(oldGOROOT, dir)) + } + if err := overlayDir(filepath.Join(goroot, dir), filepath.Join(oldGOROOT, dir)); err != nil { + return err + } + } + + return nil +} + // The shared library was built at the expected location. func TestSOBuilt(t *testing.T) { _, err := os.Stat(filepath.Join(gorootInstallDir, soname)) @@ -219,6 +281,7 @@ func TestNoTextrel(t *testing.T) { } // The shared library does not contain symbols called ".dup" +// (See golang.org/issue/14841.) func TestNoDupSymbols(t *testing.T) { sopath := filepath.Join(gorootInstallDir, soname) f, err := elf.Open(sopath) @@ -695,7 +758,7 @@ func resetFileStamps() { } reset := func(path string) { if err := filepath.Walk(path, chtime); err != nil { - log.Fatalf("resetFileStamps failed: %v", err) + log.Panicf("resetFileStamps failed: %v", err) } } @@ -708,6 +771,7 @@ func resetFileStamps() { // touch changes path and returns a function that changes it back. // It also sets the time of the file, so that we can see if it is rewritten. func touch(t *testing.T, path string) (cleanup func()) { + t.Helper() data, err := ioutil.ReadFile(path) if err != nil { t.Fatal(err) @@ -736,14 +800,32 @@ func touch(t *testing.T, path string) (cleanup func()) { // assume it's a text file data = append(data, '\n') } - if err := ioutil.WriteFile(path, data, 0666); err != nil { + + // If the file is still a symlink from an overlay, delete it so that we will + // replace it with a regular file instead of overwriting the symlinked one. + fi, err := os.Lstat(path) + if err == nil && !fi.Mode().IsRegular() { + fi, err = os.Stat(path) + if err := os.Remove(path); err != nil { + t.Fatal(err) + } + } + if err != nil { + t.Fatal(err) + } + + // If we're replacing a symlink to a read-only file, make the new file + // user-writable. + perm := fi.Mode().Perm() | 0200 + + if err := ioutil.WriteFile(path, data, perm); err != nil { t.Fatal(err) } if err := os.Chtimes(path, nearlyNew, nearlyNew); err != nil { t.Fatal(err) } return func() { - if err := ioutil.WriteFile(path, old, 0666); err != nil { + if err := ioutil.WriteFile(path, old, perm); err != nil { t.Fatal(err) } } diff --git a/libgo/misc/cgo/testsigfwd/main.go b/libgo/misc/cgo/testsigfwd/main.go index 61bd0da..6d97050 100644 --- a/libgo/misc/cgo/testsigfwd/main.go +++ b/libgo/misc/cgo/testsigfwd/main.go @@ -25,6 +25,7 @@ static void sigsegv() { static void segvhandler(int signum) { if (signum == SIGSEGV) { + fprintf(stdout, "ok\ttestsigfwd\n"); exit(0); // success } } diff --git a/libgo/misc/cgo/testso/overlaydir_test.go b/libgo/misc/cgo/testso/overlaydir_test.go index 10c874d..09a1d51 100644 --- a/libgo/misc/cgo/testso/overlaydir_test.go +++ b/libgo/misc/cgo/testso/overlaydir_test.go @@ -21,12 +21,9 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - symBase, err := filepath.Rel(srcRoot, dstRoot) + srcRoot, err := filepath.Abs(srcRoot) if err != nil { - symBase, err = filepath.Abs(srcRoot) - if err != nil { - return err - } + return err } return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { @@ -52,11 +49,11 @@ func overlayDir(dstRoot, srcRoot string) error { // 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) + return os.MkdirAll(dstPath, perm|0200) } // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + if err := os.Symlink(srcPath, dstPath); err == nil { return nil } diff --git a/libgo/misc/cgo/testsovar/overlaydir_test.go b/libgo/misc/cgo/testsovar/overlaydir_test.go index 10c874d..09a1d51 100644 --- a/libgo/misc/cgo/testsovar/overlaydir_test.go +++ b/libgo/misc/cgo/testsovar/overlaydir_test.go @@ -21,12 +21,9 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - symBase, err := filepath.Rel(srcRoot, dstRoot) + srcRoot, err := filepath.Abs(srcRoot) if err != nil { - symBase, err = filepath.Abs(srcRoot) - if err != nil { - return err - } + return err } return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { @@ -52,11 +49,11 @@ func overlayDir(dstRoot, srcRoot string) error { // 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) + return os.MkdirAll(dstPath, perm|0200) } // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(filepath.Join(symBase, suffix), dstPath); err == nil { + if err := os.Symlink(srcPath, dstPath); err == nil { return nil } |