diff options
Diffstat (limited to 'libgo/misc/cgo/testcarchive')
-rw-r--r-- | libgo/misc/cgo/testcarchive/carchive_test.go | 310 | ||||
-rw-r--r-- | libgo/misc/cgo/testcarchive/overlaydir_test.go | 11 | ||||
-rw-r--r-- | libgo/misc/cgo/testcarchive/testdata/libgo7/sink.go | 17 | ||||
-rw-r--r-- | libgo/misc/cgo/testcarchive/testdata/main2.c | 37 | ||||
-rw-r--r-- | libgo/misc/cgo/testcarchive/testdata/main3.c | 21 | ||||
-rw-r--r-- | libgo/misc/cgo/testcarchive/testdata/main7.c | 18 |
6 files changed, 308 insertions, 106 deletions
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; +} |