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