1 // Copyright 2016 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.
26 // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
29 // An environment with GOPATH=$(pwd).
30 var gopathEnv
[]string
35 var GOOS
, GOARCH
string
40 GOARCH
= goEnv("GOARCH")
41 bin
= cmdToRun("./testp")
44 cc
= []string{string(ccOut
)}
46 out
:= goEnv("GOGCCFLAGS")
53 if quote
== '\000' && unicode
.IsSpace(c
) {
55 cc
= append(cc
, s
[start
:i
])
63 if quote
== '\000' && !backslash
&& (c
== '"' || c
== '\'') {
66 } else if !backslash
&& quote
== c
{
68 } else if (quote
== '\000' || quote
== '"') && !backslash
&& c
== '\\' {
76 cc
= append(cc
, s
[start
:])
81 // TODO(crawshaw): can we do better?
82 cc
= append(cc
, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
84 libgodir
= GOOS
+ "_" + GOARCH
85 if runtime
.Compiler
== "gccgo" {
86 libgodir
= "gccgo_" + libgodir
+ "_fPIC"
90 if GOARCH
== "arm" || GOARCH
== "arm64" {
93 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
97 cc
= append(cc
, "-I", filepath
.Join("pkg", libgodir
))
99 // Build an environment with GOPATH=$(pwd)
102 for _
, e
:= range env
{
103 if !strings
.HasPrefix(e
, "GOPATH=") {
107 dir
, err
:= os
.Getwd()
109 fmt
.Fprintln(os
.Stderr
, err
)
112 n
= append(n
, "GOPATH="+dir
)
115 if GOOS
== "windows" {
120 func goEnv(key
string) string {
121 out
, err
:= exec
.Command("go", "env", key
).Output()
123 fmt
.Fprintf(os
.Stderr
, "go env %s failed:\n%s\n", key
, err
)
124 if ee
, ok
:= err
.(*exec
.ExitError
); ok
{
125 fmt
.Fprintf(os
.Stderr
, "%s", ee
.Stderr
)
129 return strings
.TrimSpace(string(out
))
132 func cmdToRun(name
string) []string {
133 execScript
:= "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
134 executor
, err
:= exec
.LookPath(execScript
)
136 return []string{name
}
138 return []string{executor
, name
}
141 func testInstall(t
*testing
.T
, exe
, libgoa
, libgoh
string, buildcmd
...string) {
142 cmd
:= exec
.Command(buildcmd
[0], buildcmd
[1:]...)
144 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
153 ccArgs
:= append(cc
, "-o", exe
, "main.c")
154 if GOOS
== "windows" {
155 ccArgs
= append(ccArgs
, "main_windows.c", libgoa
, "-lntdll", "-lws2_32", "-lwinmm")
157 ccArgs
= append(ccArgs
, "main_unix.c", libgoa
)
159 if runtime
.Compiler
== "gccgo" {
160 ccArgs
= append(ccArgs
, "-lgo")
163 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
169 binArgs
:= append(cmdToRun(exe
), "arg1", "arg2")
170 cmd
= exec
.Command(binArgs
[0], binArgs
[1:]...)
171 if runtime
.Compiler
== "gccgo" {
172 cmd
.Env
= append(os
.Environ(), "GCCGO=1")
174 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
180 func TestInstall(t
*testing
.T
) {
181 defer os
.RemoveAll("pkg")
184 if runtime
.Compiler
== "gccgo" {
185 libgoa
= "liblibgo.a"
188 testInstall(t
, "./testp1"+exeSuffix
,
189 filepath
.Join("pkg", libgodir
, libgoa
),
190 filepath
.Join("pkg", libgodir
, "libgo.h"),
191 "go", "install", "-buildmode=c-archive", "libgo")
193 // Test building libgo other than installing it.
194 // Header files are now present.
195 testInstall(t
, "./testp2"+exeSuffix
, "libgo.a", "libgo.h",
196 "go", "build", "-buildmode=c-archive", filepath
.Join("src", "libgo", "libgo.go"))
198 testInstall(t
, "./testp3"+exeSuffix
, "libgo.a", "libgo.h",
199 "go", "build", "-buildmode=c-archive", "-o", "libgo.a", "libgo")
202 func TestEarlySignalHandler(t
*testing
.T
) {
207 t
.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS
, GOARCH
)
210 t
.Skip("skipping signal test on Windows")
214 os
.Remove("libgo2.a")
215 os
.Remove("libgo2.h")
220 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
222 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
227 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main2.c", "libgo2.a")
228 if runtime
.Compiler
== "gccgo" {
229 ccArgs
= append(ccArgs
, "-lgo")
231 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
236 if out
, err
:= exec
.Command(bin
[0], bin
[1:]...).CombinedOutput(); err
!= nil {
242 func TestSignalForwarding(t
*testing
.T
) {
243 checkSignalForwardingTest(t
)
246 os
.Remove("libgo2.a")
247 os
.Remove("libgo2.h")
252 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
254 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
259 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main5.c", "libgo2.a")
260 if runtime
.Compiler
== "gccgo" {
261 ccArgs
= append(ccArgs
, "-lgo")
263 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
268 cmd
= exec
.Command(bin
[0], append(bin
[1:], "1")...)
270 out
, err
:= cmd
.CombinedOutput()
272 expectSignal(t
, err
, syscall
.SIGSEGV
)
274 // Test SIGPIPE forwarding
275 cmd
= exec
.Command(bin
[0], append(bin
[1:], "3")...)
277 out
, err
= cmd
.CombinedOutput()
279 expectSignal(t
, err
, syscall
.SIGPIPE
)
282 func TestSignalForwardingExternal(t
*testing
.T
) {
283 checkSignalForwardingTest(t
)
286 os
.Remove("libgo2.a")
287 os
.Remove("libgo2.h")
292 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
294 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
299 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main5.c", "libgo2.a")
300 if runtime
.Compiler
== "gccgo" {
301 ccArgs
= append(ccArgs
, "-lgo")
303 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
308 // We want to send the process a signal and see if it dies.
309 // Normally the signal goes to the C thread, the Go signal
310 // handler picks it up, sees that it is running in a C thread,
311 // and the program dies. Unfortunately, occasionally the
312 // signal is delivered to a Go thread, which winds up
313 // discarding it because it was sent by another program and
314 // there is no Go handler for it. To avoid this, run the
315 // program several times in the hopes that it will eventually
318 for i
:= 0; i
< tries
; i
++ {
319 cmd
= exec
.Command(bin
[0], append(bin
[1:], "2")...)
321 stderr
, err
:= cmd
.StderrPipe()
327 r
:= bufio
.NewReader(stderr
)
335 // Wait for trigger to ensure that the process is started.
336 ok
, err
:= r
.ReadString('\n')
339 if err
!= nil || ok
!= "OK\n" {
340 t
.Fatalf("Did not receive OK signal")
343 // Give the program a chance to enter the sleep function.
344 time
.Sleep(time
.Millisecond
)
346 cmd
.Process
.Signal(syscall
.SIGSEGV
)
354 if expectSignal(t
, err
, syscall
.SIGSEGV
) {
359 t
.Errorf("program succeeded unexpectedly %d times", tries
)
362 // checkSignalForwardingTest calls t.Skip if the SignalForwarding test
363 // doesn't work on this platform.
364 func checkSignalForwardingTest(t
*testing
.T
) {
369 t
.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS
, GOARCH
)
372 t
.Skip("skipping signal test on Windows")
376 // expectSignal checks that err, the exit status of a test program,
377 // shows a failure due to a specific signal. Returns whether we found
378 // the expected signal.
379 func expectSignal(t
*testing
.T
, err error
, sig syscall
.Signal
) bool {
381 t
.Error("test program succeeded unexpectedly")
382 } else if ee
, ok
:= err
.(*exec
.ExitError
); !ok
{
383 t
.Errorf("error (%v) has type %T; expected exec.ExitError", err
, err
)
384 } else if ws
, ok
:= ee
.Sys().(syscall
.WaitStatus
); !ok
{
385 t
.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee
.Sys(), ee
.Sys())
386 } else if !ws
.Signaled() || ws
.Signal() != sig
{
387 t
.Errorf("got %v; expected signal %v", ee
, sig
)
394 func TestOsSignal(t
*testing
.T
) {
397 t
.Skip("skipping signal test on Windows")
401 os
.Remove("libgo3.a")
402 os
.Remove("libgo3.h")
407 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "libgo3")
409 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
414 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main3.c", "libgo3.a")
415 if runtime
.Compiler
== "gccgo" {
416 ccArgs
= append(ccArgs
, "-lgo")
418 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
423 if out
, err
:= exec
.Command(bin
[0], bin
[1:]...).CombinedOutput(); err
!= nil {
429 func TestSigaltstack(t
*testing
.T
) {
432 t
.Skip("skipping signal test on Windows")
436 os
.Remove("libgo4.a")
437 os
.Remove("libgo4.h")
442 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "libgo4")
444 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
449 ccArgs
:= append(cc
, "-o", "testp"+exeSuffix
, "main4.c", "libgo4.a")
450 if runtime
.Compiler
== "gccgo" {
451 ccArgs
= append(ccArgs
, "-lgo")
453 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
458 if out
, err
:= exec
.Command(bin
[0], bin
[1:]...).CombinedOutput(); err
!= nil {
464 const testar
= `#!/usr/bin/env bash
465 while expr $1 : '[-]' >/dev/null; do
469 echo "testar" > PWD/testar.ran
472 func TestExtar(t
*testing
.T
) {
475 t
.Skip("skipping signal test on Windows")
477 if runtime
.Compiler
== "gccgo" {
478 t
.Skip("skipping -extar test when using gccgo")
482 os
.Remove("libgo4.a")
483 os
.Remove("libgo4.h")
485 os
.Remove("testar.ran")
490 dir
, err
:= os
.Getwd()
494 s
:= strings
.Replace(testar
, "PWD", dir
, 1)
495 if err
:= ioutil
.WriteFile("testar", []byte(s
), 0777); err
!= nil {
499 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath
.Join(dir
, "testar"), "-o", "libgo4.a", "libgo4")
501 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
506 if _
, err
:= os
.Stat("testar.ran"); err
!= nil {
507 if os
.IsNotExist(err
) {
508 t
.Error("testar does not exist after go build")
510 t
.Errorf("error checking testar: %v", err
)
515 func TestPIE(t
*testing
.T
) {
517 case "windows", "darwin", "plan9":
518 t
.Skipf("skipping PIE test on %s", GOOS
)
522 os
.Remove("testp" + exeSuffix
)
526 cmd
:= exec
.Command("go", "install", "-buildmode=c-archive", "libgo")
528 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
534 if runtime
.Compiler
== "gccgo" {
535 libgoa
= "liblibgo.a"
538 ccArgs
:= append(cc
, "-fPIE", "-pie", "-o", "testp"+exeSuffix
, "main.c", "main_unix.c", filepath
.Join("pkg", libgodir
, libgoa
))
539 if runtime
.Compiler
== "gccgo" {
540 ccArgs
= append(ccArgs
, "-lgo")
542 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
547 binArgs
:= append(bin
, "arg1", "arg2")
548 cmd
= exec
.Command(binArgs
[0], binArgs
[1:]...)
549 if runtime
.Compiler
== "gccgo" {
550 cmd
.Env
= append(os
.Environ(), "GCCGO=1")
552 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
557 f
, err
:= elf
.Open("testp" + exeSuffix
)
559 t
.Fatal("elf.Open failed: ", err
)
562 if hasDynTag(t
, f
, elf
.DT_TEXTREL
) {
563 t
.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix
)
567 func hasDynTag(t
*testing
.T
, f
*elf
.File
, tag elf
.DynTag
) bool {
568 ds
:= f
.SectionByType(elf
.SHT_DYNAMIC
)
570 t
.Error("no SHT_DYNAMIC section")
575 t
.Errorf("can't read SHT_DYNAMIC contents: %v", err
)
582 t
= elf
.DynTag(f
.ByteOrder
.Uint32(d
[:4]))
585 t
= elf
.DynTag(f
.ByteOrder
.Uint64(d
[:8]))
595 func TestSIGPROF(t
*testing
.T
) {
597 case "windows", "plan9":
598 t
.Skipf("skipping SIGPROF test on %s", GOOS
)
604 os
.Remove("testp6" + exeSuffix
)
605 os
.Remove("libgo6.a")
606 os
.Remove("libgo6.h")
609 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "libgo6")
611 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
616 ccArgs
:= append(cc
, "-o", "testp6"+exeSuffix
, "main6.c", "libgo6.a")
617 if runtime
.Compiler
== "gccgo" {
618 ccArgs
= append(ccArgs
, "-lgo")
620 if out
, err
:= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput(); err
!= nil {
625 argv
:= cmdToRun("./testp6")
626 cmd
= exec
.Command(argv
[0], argv
[1:]...)
627 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
633 // TestCompileWithoutShared tests that if we compile code without the
634 // -shared option, we can put it into an archive. When we use the go
635 // tool with -buildmode=c-archive, it passes -shared to the compiler,
636 // so we override that. The go tool doesn't work this way, but Bazel
637 // will likely do it in the future. And it ought to work. This test
638 // was added because at one time it did not work on PPC GNU/Linux.
639 func TestCompileWithoutShared(t
*testing
.T
) {
640 // For simplicity, reuse the signal forwarding test.
641 checkSignalForwardingTest(t
)
644 os
.Remove("libgo2.a")
645 os
.Remove("libgo2.h")
648 cmd
:= exec
.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "libgo2")
651 out
, err
:= cmd
.CombinedOutput()
657 exe
:= "./testnoshared" + exeSuffix
658 ccArgs
:= append(cc
, "-o", exe
, "main5.c", "libgo2.a")
659 if runtime
.Compiler
== "gccgo" {
660 ccArgs
= append(ccArgs
, "-lgo")
663 out
, err
= exec
.Command(ccArgs
[0], ccArgs
[1:]...).CombinedOutput()
670 binArgs
:= append(cmdToRun(exe
), "3")
672 out
, err
= exec
.Command(binArgs
[0], binArgs
[1:]...).CombinedOutput()
674 expectSignal(t
, err
, syscall
.SIGPIPE
)