libgo: update to Go1.10rc1
[official-gcc.git] / libgo / misc / cgo / testcshared / cshared_test.go
blobe43422de6e6a599e2dbbaf0ef04b8e0dc7f153f3
1 // Copyright 2017 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 package cshared_test
7 import (
8 "debug/elf"
9 "fmt"
10 "io/ioutil"
11 "log"
12 "os"
13 "os/exec"
14 "path"
15 "path/filepath"
16 "strings"
17 "sync"
18 "testing"
19 "unicode"
22 // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
23 var cc []string
25 // An environment with GOPATH=$(pwd).
26 var gopathEnv []string
28 // ".exe" on Windows.
29 var exeSuffix string
31 var GOOS, GOARCH, GOROOT string
32 var installdir, androiddir string
33 var libSuffix, libgoname string
35 func TestMain(m *testing.M) {
36 GOOS = goEnv("GOOS")
37 GOARCH = goEnv("GOARCH")
38 GOROOT = goEnv("GOROOT")
40 if _, err := os.Stat(GOROOT); os.IsNotExist(err) {
41 log.Fatalf("Unable able to find GOROOT at '%s'", GOROOT)
44 // Directory where cgo headers and outputs will be installed.
45 // The installation directory format varies depending on the platform.
46 installdir = path.Join("pkg", fmt.Sprintf("%s_%s_testcshared", GOOS, GOARCH))
47 switch GOOS {
48 case "darwin":
49 libSuffix = "dylib"
50 case "windows":
51 libSuffix = "dll"
52 default:
53 libSuffix = "so"
54 installdir = path.Join("pkg", fmt.Sprintf("%s_%s_testcshared_shared", GOOS, GOARCH))
57 androiddir = fmt.Sprintf("/data/local/tmp/testcshared-%d", os.Getpid())
58 if GOOS == "android" {
59 args := append(adbCmd(), "shell", "mkdir", "-p", androiddir)
60 cmd := exec.Command(args[0], args[1:]...)
61 out, err := cmd.CombinedOutput()
62 if err != nil {
63 log.Fatalf("setupAndroid failed: %v\n%s\n", err, out)
67 libgoname = "libgo." + libSuffix
69 cc = []string{goEnv("CC")}
71 out := goEnv("GOGCCFLAGS")
72 quote := '\000'
73 start := 0
74 lastSpace := true
75 backslash := false
76 s := string(out)
77 for i, c := range s {
78 if quote == '\000' && unicode.IsSpace(c) {
79 if !lastSpace {
80 cc = append(cc, s[start:i])
81 lastSpace = true
83 } else {
84 if lastSpace {
85 start = i
86 lastSpace = false
88 if quote == '\000' && !backslash && (c == '"' || c == '\'') {
89 quote = c
90 backslash = false
91 } else if !backslash && quote == c {
92 quote = '\000'
93 } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
94 backslash = true
95 } else {
96 backslash = false
100 if !lastSpace {
101 cc = append(cc, s[start:])
104 switch GOOS {
105 case "darwin":
106 // For Darwin/ARM.
107 // TODO(crawshaw): can we do better?
108 cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
109 case "android":
110 cc = append(cc, "-pie", "-fuse-ld=gold")
112 libgodir := GOOS + "_" + GOARCH
113 switch GOOS {
114 case "darwin":
115 if GOARCH == "arm" || GOARCH == "arm64" {
116 libgodir += "_shared"
118 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
119 libgodir += "_shared"
121 cc = append(cc, "-I", filepath.Join("pkg", libgodir))
123 // Build an environment with GOPATH=$(pwd)
124 dir, err := os.Getwd()
125 if err != nil {
126 fmt.Fprintln(os.Stderr, err)
127 os.Exit(2)
129 gopathEnv = append(os.Environ(), "GOPATH="+dir)
131 if GOOS == "windows" {
132 exeSuffix = ".exe"
135 st := m.Run()
137 os.Remove(libgoname)
138 os.RemoveAll("pkg")
139 cleanupHeaders()
140 cleanupAndroid()
142 os.Exit(st)
145 func goEnv(key string) string {
146 out, err := exec.Command("go", "env", key).Output()
147 if err != nil {
148 fmt.Fprintf(os.Stderr, "go env %s failed:\n%s", key, err)
149 fmt.Fprintf(os.Stderr, "%s", err.(*exec.ExitError).Stderr)
150 os.Exit(2)
152 return strings.TrimSpace(string(out))
155 func cmdToRun(name string) string {
156 return "./" + name + exeSuffix
159 func adbCmd() []string {
160 cmd := []string{"adb"}
161 if flags := os.Getenv("GOANDROID_ADB_FLAGS"); flags != "" {
162 cmd = append(cmd, strings.Split(flags, " ")...)
164 return cmd
167 func adbPush(t *testing.T, filename string) {
168 if GOOS != "android" {
169 return
171 args := append(adbCmd(), "push", filename, fmt.Sprintf("%s/%s", androiddir, filename))
172 cmd := exec.Command(args[0], args[1:]...)
173 if out, err := cmd.CombinedOutput(); err != nil {
174 t.Fatalf("adb command failed: %v\n%s\n", err, out)
178 func adbRun(t *testing.T, env []string, adbargs ...string) string {
179 if GOOS != "android" {
180 t.Fatalf("trying to run adb command when operating system is not android.")
182 args := append(adbCmd(), "shell")
183 // Propagate LD_LIBRARY_PATH to the adb shell invocation.
184 for _, e := range env {
185 if strings.Index(e, "LD_LIBRARY_PATH=") != -1 {
186 adbargs = append([]string{e}, adbargs...)
187 break
190 shellcmd := fmt.Sprintf("cd %s; %s", androiddir, strings.Join(adbargs, " "))
191 args = append(args, shellcmd)
192 cmd := exec.Command(args[0], args[1:]...)
193 out, err := cmd.CombinedOutput()
194 if err != nil {
195 t.Fatalf("adb command failed: %v\n%s\n", err, out)
197 return strings.Replace(string(out), "\r", "", -1)
200 func run(t *testing.T, env []string, args ...string) string {
201 t.Helper()
202 cmd := exec.Command(args[0], args[1:]...)
203 cmd.Env = env
204 out, err := cmd.CombinedOutput()
205 if err != nil {
206 t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out)
207 } else {
208 t.Logf("run: %v", args)
210 return string(out)
213 func runExe(t *testing.T, env []string, args ...string) string {
214 t.Helper()
215 if GOOS == "android" {
216 return adbRun(t, env, args...)
218 return run(t, env, args...)
221 func runCC(t *testing.T, args ...string) string {
222 t.Helper()
223 // This function is run in parallel, so append to a copy of cc
224 // rather than cc itself.
225 return run(t, nil, append(append([]string(nil), cc...), args...)...)
228 func createHeaders() error {
229 args := []string{"go", "install", "-i", "-buildmode=c-shared",
230 "-installsuffix", "testcshared", "libgo"}
231 cmd := exec.Command(args[0], args[1:]...)
232 cmd.Env = gopathEnv
233 out, err := cmd.CombinedOutput()
234 if err != nil {
235 return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
238 args = []string{"go", "build", "-buildmode=c-shared",
239 "-installsuffix", "testcshared",
240 "-o", libgoname,
241 filepath.Join("src", "libgo", "libgo.go")}
242 cmd = exec.Command(args[0], args[1:]...)
243 cmd.Env = gopathEnv
244 out, err = cmd.CombinedOutput()
245 if err != nil {
246 return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
249 if GOOS == "android" {
250 args = append(adbCmd(), "push", libgoname, fmt.Sprintf("%s/%s", androiddir, libgoname))
251 cmd = exec.Command(args[0], args[1:]...)
252 out, err = cmd.CombinedOutput()
253 if err != nil {
254 return fmt.Errorf("adb command failed: %v\n%s\n", err, out)
258 return nil
261 var (
262 headersOnce sync.Once
263 headersErr error
266 func createHeadersOnce(t *testing.T) {
267 headersOnce.Do(func() {
268 headersErr = createHeaders()
270 if headersErr != nil {
271 t.Fatal(headersErr)
275 func cleanupHeaders() {
276 os.Remove("libgo.h")
279 func cleanupAndroid() {
280 if GOOS != "android" {
281 return
283 args := append(adbCmd(), "shell", "rm", "-rf", androiddir)
284 cmd := exec.Command(args[0], args[1:]...)
285 out, err := cmd.CombinedOutput()
286 if err != nil {
287 log.Fatalf("cleanupAndroid failed: %v\n%s\n", err, out)
291 // test0: exported symbols in shared lib are accessible.
292 func TestExportedSymbols(t *testing.T) {
293 t.Parallel()
295 cmd := "testp0"
296 bin := cmdToRun(cmd)
298 createHeadersOnce(t)
300 runCC(t, "-I", installdir, "-o", cmd, "main0.c", libgoname)
301 adbPush(t, cmd)
303 defer os.Remove(bin)
305 out := runExe(t, append(gopathEnv, "LD_LIBRARY_PATH=."), bin)
306 if strings.TrimSpace(out) != "PASS" {
307 t.Error(out)
311 // test1: shared library can be dynamically loaded and exported symbols are accessible.
312 func TestExportedSymbolsWithDynamicLoad(t *testing.T) {
313 t.Parallel()
315 if GOOS == "windows" {
316 t.Logf("Skipping on %s", GOOS)
317 return
320 cmd := "testp1"
321 bin := cmdToRun(cmd)
323 createHeadersOnce(t)
325 runCC(t, "-o", cmd, "main1.c", "-ldl")
326 adbPush(t, cmd)
328 defer os.Remove(bin)
330 out := runExe(t, nil, bin, "./"+libgoname)
331 if strings.TrimSpace(out) != "PASS" {
332 t.Error(out)
336 // test2: tests libgo2 which does not export any functions.
337 func TestUnexportedSymbols(t *testing.T) {
338 t.Parallel()
340 if GOOS == "windows" {
341 t.Logf("Skipping on %s", GOOS)
342 return
345 cmd := "testp2"
346 bin := cmdToRun(cmd)
347 libname := "libgo2." + libSuffix
349 run(t,
350 gopathEnv,
351 "go", "build",
352 "-buildmode=c-shared",
353 "-installsuffix", "testcshared",
354 "-o", libname, "libgo2",
356 adbPush(t, libname)
358 linkFlags := "-Wl,--no-as-needed"
359 if GOOS == "darwin" {
360 linkFlags = ""
363 runCC(t, "-o", cmd, "main2.c", linkFlags, libname)
364 adbPush(t, cmd)
366 defer os.Remove(libname)
367 defer os.Remove(bin)
369 out := runExe(t, append(gopathEnv, "LD_LIBRARY_PATH=."), bin)
371 if strings.TrimSpace(out) != "PASS" {
372 t.Error(out)
376 // test3: tests main.main is exported on android.
377 func TestMainExportedOnAndroid(t *testing.T) {
378 t.Parallel()
380 switch GOOS {
381 case "android":
382 break
383 default:
384 t.Logf("Skipping on %s", GOOS)
385 return
388 cmd := "testp3"
389 bin := cmdToRun(cmd)
391 createHeadersOnce(t)
393 runCC(t, "-o", cmd, "main3.c", "-ldl")
394 adbPush(t, cmd)
396 defer os.Remove(bin)
398 out := runExe(t, nil, bin, "./"+libgoname)
399 if strings.TrimSpace(out) != "PASS" {
400 t.Error(out)
404 func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) {
405 libname := pkgname + "." + libSuffix
406 run(t,
407 gopathEnv,
408 "go", "build",
409 "-buildmode=c-shared",
410 "-installsuffix", "testcshared",
411 "-o", libname, pkgname,
413 adbPush(t, libname)
414 runCC(t, "-pthread", "-o", cmd, cfile, "-ldl")
415 adbPush(t, cmd)
417 bin := cmdToRun(cmd)
419 defer os.Remove(libname)
420 defer os.Remove(bin)
421 defer os.Remove(pkgname + ".h")
423 out := runExe(t, nil, bin, "./"+libname)
424 if strings.TrimSpace(out) != "PASS" {
425 t.Error(run(t, nil, bin, libname, "verbose"))
429 // test4: test signal handlers
430 func TestSignalHandlers(t *testing.T) {
431 t.Parallel()
432 if GOOS == "windows" {
433 t.Logf("Skipping on %s", GOOS)
434 return
436 testSignalHandlers(t, "libgo4", "main4.c", "testp4")
439 // test5: test signal handlers with os/signal.Notify
440 func TestSignalHandlersWithNotify(t *testing.T) {
441 t.Parallel()
442 if GOOS == "windows" {
443 t.Logf("Skipping on %s", GOOS)
444 return
446 testSignalHandlers(t, "libgo5", "main5.c", "testp5")
449 func TestPIE(t *testing.T) {
450 t.Parallel()
452 switch GOOS {
453 case "linux", "android":
454 break
455 default:
456 t.Logf("Skipping on %s", GOOS)
457 return
460 createHeadersOnce(t)
462 f, err := elf.Open(libgoname)
463 if err != nil {
464 t.Fatalf("elf.Open failed: %v", err)
466 defer f.Close()
468 ds := f.SectionByType(elf.SHT_DYNAMIC)
469 if ds == nil {
470 t.Fatalf("no SHT_DYNAMIC section")
472 d, err := ds.Data()
473 if err != nil {
474 t.Fatalf("can't read SHT_DYNAMIC contents: %v", err)
476 for len(d) > 0 {
477 var tag elf.DynTag
478 switch f.Class {
479 case elf.ELFCLASS32:
480 tag = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
481 d = d[8:]
482 case elf.ELFCLASS64:
483 tag = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
484 d = d[16:]
486 if tag == elf.DT_TEXTREL {
487 t.Fatalf("%s has DT_TEXTREL flag", libgoname)
492 // Test that installing a second time recreates the header files.
493 func TestCachedInstall(t *testing.T) {
494 tmpdir, err := ioutil.TempDir("", "cshared")
495 if err != nil {
496 t.Fatal(err)
498 // defer os.RemoveAll(tmpdir)
500 copyFile(t, filepath.Join(tmpdir, "src", "libgo", "libgo.go"), filepath.Join("src", "libgo", "libgo.go"))
501 copyFile(t, filepath.Join(tmpdir, "src", "p", "p.go"), filepath.Join("src", "p", "p.go"))
503 env := append(os.Environ(), "GOPATH="+tmpdir)
505 buildcmd := []string{"go", "install", "-x", "-i", "-buildmode=c-shared", "-installsuffix", "testcshared", "libgo"}
507 cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
508 cmd.Env = env
509 t.Log(buildcmd)
510 out, err := cmd.CombinedOutput()
511 t.Logf("%s", out)
512 if err != nil {
513 t.Fatal(err)
516 var libgoh, ph string
518 walker := func(path string, info os.FileInfo, err error) error {
519 if err != nil {
520 t.Fatal(err)
522 var ps *string
523 switch filepath.Base(path) {
524 case "libgo.h":
525 ps = &libgoh
526 case "p.h":
527 ps = &ph
529 if ps != nil {
530 if *ps != "" {
531 t.Fatalf("%s found again", *ps)
533 *ps = path
535 return nil
538 if err := filepath.Walk(tmpdir, walker); err != nil {
539 t.Fatal(err)
542 if libgoh == "" {
543 t.Fatal("libgo.h not installed")
545 if ph == "" {
546 t.Fatal("p.h not installed")
549 if err := os.Remove(libgoh); err != nil {
550 t.Fatal(err)
552 if err := os.Remove(ph); err != nil {
553 t.Fatal(err)
556 cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
557 cmd.Env = env
558 t.Log(buildcmd)
559 out, err = cmd.CombinedOutput()
560 t.Logf("%s", out)
561 if err != nil {
562 t.Fatal(err)
565 if _, err := os.Stat(libgoh); err != nil {
566 t.Errorf("libgo.h not installed in second run: %v", err)
568 if _, err := os.Stat(ph); err != nil {
569 t.Errorf("p.h not installed in second run: %v", err)
573 // copyFile copies src to dst.
574 func copyFile(t *testing.T, dst, src string) {
575 t.Helper()
576 data, err := ioutil.ReadFile(src)
577 if err != nil {
578 t.Fatal(err)
580 if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
581 t.Fatal(err)
583 if err := ioutil.WriteFile(dst, data, 0666); err != nil {
584 t.Fatal(err)