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.
22 // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
25 // An environment with GOPATH=$(pwd).
26 var gopathEnv
[]string
31 var GOOS
, GOARCH
, GOROOT
string
32 var installdir
, androiddir
string
33 var libSuffix
, libgoname
string
35 func TestMain(m
*testing
.M
) {
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
))
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()
63 log
.Fatalf("setupAndroid failed: %v\n%s\n", err
, out
)
67 libgoname
= "libgo." + libSuffix
69 cc
= []string{goEnv("CC")}
71 out
:= goEnv("GOGCCFLAGS")
78 if quote
== '\000' && unicode
.IsSpace(c
) {
80 cc
= append(cc
, s
[start
:i
])
88 if quote
== '\000' && !backslash
&& (c
== '"' || c
== '\'') {
91 } else if !backslash
&& quote
== c
{
93 } else if (quote
== '\000' || quote
== '"') && !backslash
&& c
== '\\' {
101 cc
= append(cc
, s
[start
:])
107 // TODO(crawshaw): can we do better?
108 cc
= append(cc
, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
110 cc
= append(cc
, "-pie", "-fuse-ld=gold")
112 libgodir
:= GOOS
+ "_" + GOARCH
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()
126 fmt
.Fprintln(os
.Stderr
, err
)
129 gopathEnv
= append(os
.Environ(), "GOPATH="+dir
)
131 if GOOS
== "windows" {
145 func goEnv(key
string) string {
146 out
, err
:= exec
.Command("go", "env", key
).Output()
148 fmt
.Fprintf(os
.Stderr
, "go env %s failed:\n%s", key
, err
)
149 fmt
.Fprintf(os
.Stderr
, "%s", err
.(*exec
.ExitError
).Stderr
)
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
, " ")...)
167 func adbPush(t
*testing
.T
, filename
string) {
168 if GOOS
!= "android" {
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
...)
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()
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 {
202 cmd
:= exec
.Command(args
[0], args
[1:]...)
204 out
, err
:= cmd
.CombinedOutput()
206 t
.Fatalf("command failed: %v\n%v\n%s\n", args
, err
, out
)
208 t
.Logf("run: %v", args
)
213 func runExe(t
*testing
.T
, env
[]string, args
...string) string {
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 {
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:]...)
233 out
, err
:= cmd
.CombinedOutput()
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",
241 filepath
.Join("src", "libgo", "libgo.go")}
242 cmd
= exec
.Command(args
[0], args
[1:]...)
244 out
, err
= cmd
.CombinedOutput()
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()
254 return fmt
.Errorf("adb command failed: %v\n%s\n", err
, out
)
262 headersOnce sync
.Once
266 func createHeadersOnce(t
*testing
.T
) {
267 headersOnce
.Do(func() {
268 headersErr
= createHeaders()
270 if headersErr
!= nil {
275 func cleanupHeaders() {
279 func cleanupAndroid() {
280 if GOOS
!= "android" {
283 args
:= append(adbCmd(), "shell", "rm", "-rf", androiddir
)
284 cmd
:= exec
.Command(args
[0], args
[1:]...)
285 out
, err
:= cmd
.CombinedOutput()
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
) {
300 runCC(t
, "-I", installdir
, "-o", cmd
, "main0.c", libgoname
)
305 out
:= runExe(t
, append(gopathEnv
, "LD_LIBRARY_PATH=."), bin
)
306 if strings
.TrimSpace(out
) != "PASS" {
311 // test1: shared library can be dynamically loaded and exported symbols are accessible.
312 func TestExportedSymbolsWithDynamicLoad(t
*testing
.T
) {
315 if GOOS
== "windows" {
316 t
.Logf("Skipping on %s", GOOS
)
325 runCC(t
, "-o", cmd
, "main1.c", "-ldl")
330 out
:= runExe(t
, nil, bin
, "./"+libgoname
)
331 if strings
.TrimSpace(out
) != "PASS" {
336 // test2: tests libgo2 which does not export any functions.
337 func TestUnexportedSymbols(t
*testing
.T
) {
340 if GOOS
== "windows" {
341 t
.Logf("Skipping on %s", GOOS
)
347 libname
:= "libgo2." + libSuffix
352 "-buildmode=c-shared",
353 "-installsuffix", "testcshared",
354 "-o", libname
, "libgo2",
358 linkFlags
:= "-Wl,--no-as-needed"
359 if GOOS
== "darwin" {
363 runCC(t
, "-o", cmd
, "main2.c", linkFlags
, libname
)
366 defer os
.Remove(libname
)
369 out
:= runExe(t
, append(gopathEnv
, "LD_LIBRARY_PATH=."), bin
)
371 if strings
.TrimSpace(out
) != "PASS" {
376 // test3: tests main.main is exported on android.
377 func TestMainExportedOnAndroid(t
*testing
.T
) {
384 t
.Logf("Skipping on %s", GOOS
)
393 runCC(t
, "-o", cmd
, "main3.c", "-ldl")
398 out
:= runExe(t
, nil, bin
, "./"+libgoname
)
399 if strings
.TrimSpace(out
) != "PASS" {
404 func testSignalHandlers(t
*testing
.T
, pkgname
, cfile
, cmd
string) {
405 libname
:= pkgname
+ "." + libSuffix
409 "-buildmode=c-shared",
410 "-installsuffix", "testcshared",
411 "-o", libname
, pkgname
,
414 runCC(t
, "-pthread", "-o", cmd
, cfile
, "-ldl")
419 defer os
.Remove(libname
)
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
) {
432 if GOOS
== "windows" {
433 t
.Logf("Skipping on %s", GOOS
)
436 testSignalHandlers(t
, "libgo4", "main4.c", "testp4")
439 // test5: test signal handlers with os/signal.Notify
440 func TestSignalHandlersWithNotify(t
*testing
.T
) {
442 if GOOS
== "windows" {
443 t
.Logf("Skipping on %s", GOOS
)
446 testSignalHandlers(t
, "libgo5", "main5.c", "testp5")
449 func TestPIE(t
*testing
.T
) {
453 case "linux", "android":
456 t
.Logf("Skipping on %s", GOOS
)
462 f
, err
:= elf
.Open(libgoname
)
464 t
.Fatalf("elf.Open failed: %v", err
)
468 ds
:= f
.SectionByType(elf
.SHT_DYNAMIC
)
470 t
.Fatalf("no SHT_DYNAMIC section")
474 t
.Fatalf("can't read SHT_DYNAMIC contents: %v", err
)
480 tag
= elf
.DynTag(f
.ByteOrder
.Uint32(d
[:4]))
483 tag
= elf
.DynTag(f
.ByteOrder
.Uint64(d
[:8]))
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")
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:]...)
510 out
, err
:= cmd
.CombinedOutput()
516 var libgoh
, ph
string
518 walker
:= func(path
string, info os
.FileInfo
, err error
) error
{
523 switch filepath
.Base(path
) {
531 t
.Fatalf("%s found again", *ps
)
538 if err
:= filepath
.Walk(tmpdir
, walker
); err
!= nil {
543 t
.Fatal("libgo.h not installed")
546 t
.Fatal("p.h not installed")
549 if err
:= os
.Remove(libgoh
); err
!= nil {
552 if err
:= os
.Remove(ph
); err
!= nil {
556 cmd
= exec
.Command(buildcmd
[0], buildcmd
[1:]...)
559 out
, err
= cmd
.CombinedOutput()
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) {
576 data
, err
:= ioutil
.ReadFile(src
)
580 if err
:= os
.MkdirAll(filepath
.Dir(dst
), 0777); err
!= nil {
583 if err
:= ioutil
.WriteFile(dst
, data
, 0666); err
!= nil {