aboutsummaryrefslogtreecommitdiff
path: root/libgo/misc/cgo/testcarchive
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/misc/cgo/testcarchive')
-rw-r--r--libgo/misc/cgo/testcarchive/carchive_test.go310
-rw-r--r--libgo/misc/cgo/testcarchive/overlaydir_test.go11
-rw-r--r--libgo/misc/cgo/testcarchive/testdata/libgo7/sink.go17
-rw-r--r--libgo/misc/cgo/testcarchive/testdata/main2.c37
-rw-r--r--libgo/misc/cgo/testcarchive/testdata/main3.c21
-rw-r--r--libgo/misc/cgo/testcarchive/testdata/main7.c18
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;
+}