github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/misc/cgo/testcarchive/carchive_test.go (about) 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. 4 5 package carchive_test 6 7 import ( 8 "bufio" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "strings" 15 "syscall" 16 "testing" 17 "time" 18 "unicode" 19 ) 20 21 // Program to run. 22 var bin []string 23 24 // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)). 25 var cc []string 26 27 // An environment with GOPATH=$(pwd). 28 var gopathEnv []string 29 30 // ".exe" on Windows. 31 var exeSuffix string 32 33 var GOOS, GOARCH string 34 35 func init() { 36 bin = []string{"./testp"} 37 GOOS = goEnv("GOOS") 38 GOARCH = goEnv("GOARCH") 39 execScript := "go_" + GOOS + "_" + GOARCH + "_exec" 40 if executor, err := exec.LookPath(execScript); err == nil { 41 bin = []string{executor, "./testp"} 42 } 43 44 ccOut := goEnv("CC") 45 cc = []string{string(ccOut)} 46 47 out := goEnv("GOGCCFLAGS") 48 quote := '\000' 49 start := 0 50 lastSpace := true 51 backslash := false 52 s := string(out) 53 for i, c := range s { 54 if quote == '\000' && unicode.IsSpace(c) { 55 if !lastSpace { 56 cc = append(cc, s[start:i]) 57 lastSpace = true 58 } 59 } else { 60 if lastSpace { 61 start = i 62 lastSpace = false 63 } 64 if quote == '\000' && !backslash && (c == '"' || c == '\'') { 65 quote = c 66 backslash = false 67 } else if !backslash && quote == c { 68 quote = '\000' 69 } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { 70 backslash = true 71 } else { 72 backslash = false 73 } 74 } 75 } 76 if !lastSpace { 77 cc = append(cc, s[start:]) 78 } 79 80 if GOOS == "darwin" { 81 cc = append(cc, "-Wl,-no_pie") 82 83 // For Darwin/ARM. 84 // TODO(crawshaw): can we do better? 85 cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...) 86 } 87 cc = append(cc, "-I", filepath.Join("pkg", GOOS+"_"+GOARCH)) 88 89 // Build an environment with GOPATH=$(pwd) 90 env := os.Environ() 91 var n []string 92 for _, e := range env { 93 if !strings.HasPrefix(e, "GOPATH=") { 94 n = append(n, e) 95 } 96 } 97 dir, err := os.Getwd() 98 if err != nil { 99 fmt.Fprintln(os.Stderr, err) 100 os.Exit(2) 101 } 102 n = append(n, "GOPATH="+dir) 103 gopathEnv = n 104 105 if GOOS == "windows" { 106 exeSuffix = ".exe" 107 } 108 } 109 110 func goEnv(key string) string { 111 out, err := exec.Command("go", "env", key).Output() 112 if err != nil { 113 fmt.Fprintf(os.Stderr, "go env %s failed:\n%s", key, err) 114 fmt.Fprintf(os.Stderr, "%s", err.(*exec.ExitError).Stderr) 115 os.Exit(2) 116 } 117 return strings.TrimSpace(string(out)) 118 } 119 120 func compilemain(t *testing.T, libgo string) { 121 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main.c") 122 if GOOS == "windows" { 123 ccArgs = append(ccArgs, "main_windows.c", libgo, "-lntdll", "-lws2_32") 124 } else { 125 ccArgs = append(ccArgs, "main_unix.c", libgo) 126 } 127 t.Log(ccArgs) 128 129 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 130 t.Logf("%s", out) 131 t.Fatal(err) 132 } 133 } 134 135 func TestInstall(t *testing.T) { 136 defer func() { 137 os.Remove("libgo.a") 138 os.Remove("libgo.h") 139 os.Remove("testp") 140 os.RemoveAll("pkg") 141 }() 142 143 cmd := exec.Command("go", "install", "-buildmode=c-archive", "libgo") 144 cmd.Env = gopathEnv 145 if out, err := cmd.CombinedOutput(); err != nil { 146 t.Logf("%s", out) 147 t.Fatal(err) 148 } 149 150 compilemain(t, filepath.Join("pkg", GOOS+"_"+GOARCH, "libgo.a")) 151 152 binArgs := append(bin, "arg1", "arg2") 153 if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil { 154 t.Logf("%s", out) 155 t.Fatal(err) 156 } 157 158 os.Remove("libgo.a") 159 os.Remove("libgo.h") 160 os.Remove("testp") 161 162 // Test building libgo other than installing it. 163 // Header files are now present. 164 cmd = exec.Command("go", "build", "-buildmode=c-archive", filepath.Join("src", "libgo", "libgo.go")) 165 cmd.Env = gopathEnv 166 if out, err := cmd.CombinedOutput(); err != nil { 167 t.Logf("%s", out) 168 t.Fatal(err) 169 } 170 171 compilemain(t, "libgo.a") 172 173 if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil { 174 t.Logf("%s", out) 175 t.Fatal(err) 176 } 177 178 os.Remove("libgo.a") 179 os.Remove("libgo.h") 180 os.Remove("testp") 181 182 cmd = exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo.a", "libgo") 183 cmd.Env = gopathEnv 184 if out, err := cmd.CombinedOutput(); err != nil { 185 t.Logf("%s", out) 186 t.Fatal(err) 187 } 188 189 compilemain(t, "libgo.a") 190 191 if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil { 192 t.Logf("%s", out) 193 t.Fatal(err) 194 } 195 } 196 197 func TestEarlySignalHandler(t *testing.T) { 198 switch GOOS { 199 case "darwin": 200 switch GOARCH { 201 case "arm", "arm64": 202 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) 203 } 204 case "windows": 205 t.Skip("skipping signal test on Windows") 206 } 207 208 defer func() { 209 os.Remove("libgo2.a") 210 os.Remove("libgo2.h") 211 os.Remove("testp") 212 os.RemoveAll("pkg") 213 }() 214 215 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2") 216 cmd.Env = gopathEnv 217 if out, err := cmd.CombinedOutput(); err != nil { 218 t.Logf("%s", out) 219 t.Fatal(err) 220 } 221 222 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a") 223 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 224 t.Logf("%s", out) 225 t.Fatal(err) 226 } 227 228 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { 229 t.Logf("%s", out) 230 t.Fatal(err) 231 } 232 } 233 234 func TestSignalForwarding(t *testing.T) { 235 switch GOOS { 236 case "darwin": 237 switch GOARCH { 238 case "arm", "arm64": 239 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) 240 } 241 case "windows": 242 t.Skip("skipping signal test on Windows") 243 } 244 245 defer func() { 246 os.Remove("libgo2.a") 247 os.Remove("libgo2.h") 248 os.Remove("testp") 249 os.RemoveAll("pkg") 250 }() 251 252 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2") 253 cmd.Env = gopathEnv 254 if out, err := cmd.CombinedOutput(); err != nil { 255 t.Logf("%s", out) 256 t.Fatal(err) 257 } 258 259 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") 260 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 261 t.Logf("%s", out) 262 t.Fatal(err) 263 } 264 265 cmd = exec.Command(bin[0], append(bin[1:], "1")...) 266 267 out, err := cmd.CombinedOutput() 268 269 if err == nil { 270 t.Logf("%s", out) 271 t.Error("test program succeeded unexpectedly") 272 } else if ee, ok := err.(*exec.ExitError); !ok { 273 t.Logf("%s", out) 274 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) 275 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 276 t.Logf("%s", out) 277 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) 278 } else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV { 279 t.Logf("%s", out) 280 t.Errorf("got %v; expected SIGSEGV", ee) 281 } 282 } 283 284 func TestSignalForwardingExternal(t *testing.T) { 285 switch GOOS { 286 case "darwin": 287 switch GOARCH { 288 case "arm", "arm64": 289 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) 290 } 291 case "windows": 292 t.Skip("skipping signal test on Windows") 293 } 294 295 defer func() { 296 os.Remove("libgo2.a") 297 os.Remove("libgo2.h") 298 os.Remove("testp") 299 os.RemoveAll("pkg") 300 }() 301 302 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2") 303 cmd.Env = gopathEnv 304 if out, err := cmd.CombinedOutput(); err != nil { 305 t.Logf("%s", out) 306 t.Fatal(err) 307 } 308 309 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") 310 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 311 t.Logf("%s", out) 312 t.Fatal(err) 313 } 314 315 // We want to send the process a signal and see if it dies. 316 // Normally the signal goes to the C thread, the Go signal 317 // handler picks it up, sees that it is running in a C thread, 318 // and the program dies. Unfortunately, occasionally the 319 // signal is delivered to a Go thread, which winds up 320 // discarding it because it was sent by another program and 321 // there is no Go handler for it. To avoid this, run the 322 // program several times in the hopes that it will eventually 323 // fail. 324 const tries = 20 325 for i := 0; i < tries; i++ { 326 cmd = exec.Command(bin[0], append(bin[1:], "2")...) 327 328 stderr, err := cmd.StderrPipe() 329 if err != nil { 330 t.Fatal(err) 331 } 332 defer stderr.Close() 333 334 r := bufio.NewReader(stderr) 335 336 err = cmd.Start() 337 338 if err != nil { 339 t.Fatal(err) 340 } 341 342 // Wait for trigger to ensure that the process is started. 343 ok, err := r.ReadString('\n') 344 345 // Verify trigger. 346 if err != nil || ok != "OK\n" { 347 t.Fatalf("Did not receive OK signal") 348 } 349 350 // Give the program a chance to enter the sleep function. 351 time.Sleep(time.Millisecond) 352 353 cmd.Process.Signal(syscall.SIGSEGV) 354 355 err = cmd.Wait() 356 357 if err == nil { 358 continue 359 } 360 361 if ee, ok := err.(*exec.ExitError); !ok { 362 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) 363 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 364 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) 365 } else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV { 366 t.Errorf("got %v; expected SIGSEGV", ee) 367 } else { 368 // We got the error we expected. 369 return 370 } 371 } 372 373 t.Errorf("program succeeded unexpectedly %d times", tries) 374 } 375 376 func TestOsSignal(t *testing.T) { 377 switch GOOS { 378 case "windows": 379 t.Skip("skipping signal test on Windows") 380 } 381 382 defer func() { 383 os.Remove("libgo3.a") 384 os.Remove("libgo3.h") 385 os.Remove("testp") 386 os.RemoveAll("pkg") 387 }() 388 389 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "libgo3") 390 cmd.Env = gopathEnv 391 if out, err := cmd.CombinedOutput(); err != nil { 392 t.Logf("%s", out) 393 t.Fatal(err) 394 } 395 396 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a") 397 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 398 t.Logf("%s", out) 399 t.Fatal(err) 400 } 401 402 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { 403 t.Logf("%s", out) 404 t.Fatal(err) 405 } 406 } 407 408 func TestSigaltstack(t *testing.T) { 409 switch GOOS { 410 case "windows": 411 t.Skip("skipping signal test on Windows") 412 } 413 414 defer func() { 415 os.Remove("libgo4.a") 416 os.Remove("libgo4.h") 417 os.Remove("testp") 418 os.RemoveAll("pkg") 419 }() 420 421 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "libgo4") 422 cmd.Env = gopathEnv 423 if out, err := cmd.CombinedOutput(); err != nil { 424 t.Logf("%s", out) 425 t.Fatal(err) 426 } 427 428 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a") 429 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 430 t.Logf("%s", out) 431 t.Fatal(err) 432 } 433 434 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { 435 t.Logf("%s", out) 436 t.Fatal(err) 437 } 438 } 439 440 const testar = `#!/usr/bin/env bash 441 while expr $1 : '[-]' >/dev/null; do 442 shift 443 done 444 echo "testar" > $1 445 echo "testar" > PWD/testar.ran 446 ` 447 448 func TestExtar(t *testing.T) { 449 switch GOOS { 450 case "windows": 451 t.Skip("skipping signal test on Windows") 452 } 453 454 defer func() { 455 os.Remove("libgo4.a") 456 os.Remove("libgo4.h") 457 os.Remove("testar") 458 os.Remove("testar.ran") 459 os.RemoveAll("pkg") 460 }() 461 462 os.Remove("testar") 463 dir, err := os.Getwd() 464 if err != nil { 465 t.Fatal(err) 466 } 467 s := strings.Replace(testar, "PWD", dir, 1) 468 if err := ioutil.WriteFile("testar", []byte(s), 0777); err != nil { 469 t.Fatal(err) 470 } 471 472 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "libgo4") 473 cmd.Env = gopathEnv 474 if out, err := cmd.CombinedOutput(); err != nil { 475 t.Logf("%s", out) 476 t.Fatal(err) 477 } 478 479 if _, err := os.Stat("testar.ran"); err != nil { 480 if os.IsNotExist(err) { 481 t.Error("testar does not exist after go build") 482 } else { 483 t.Errorf("error checking testar: %v", err) 484 } 485 } 486 }