1 // Copyright 2009 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 main 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/token" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 ) 15 16 // run runs the command argv, feeding in stdin on standard input. 17 // It returns the output to standard output and standard error. 18 // ok indicates whether the command exited successfully. 19 func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { 20 if i := find(argv, "-xc"); i >= 0 && argv[len(argv)-1] == "-" { 21 // Some compilers have trouble with standard input. 22 // Others have trouble with -xc. 23 // Avoid both problems by writing a file with a .c extension. 24 f, err := ioutil.TempFile("", "cgo-gcc-input-") 25 if err != nil { 26 fatalf("%s", err) 27 } 28 name := f.Name() 29 f.Close() 30 if err := ioutil.WriteFile(name+".c", stdin, 0666); err != nil { 31 os.Remove(name) 32 fatalf("%s", err) 33 } 34 defer os.Remove(name) 35 defer os.Remove(name + ".c") 36 37 // Build new argument list without -xc and trailing -. 38 new := append(argv[:i:i], argv[i+1:len(argv)-1]...) 39 40 // Since we are going to write the file to a temporary directory, 41 // we will need to add -I . explicitly to the command line: 42 // any #include "foo" before would have looked in the current 43 // directory as the directory "holding" standard input, but now 44 // the temporary directory holds the input. 45 // We've also run into compilers that reject "-I." but allow "-I", ".", 46 // so be sure to use two arguments. 47 // This matters mainly for people invoking cgo -godefs by hand. 48 new = append(new, "-I", ".") 49 50 // Finish argument list with path to C file. 51 new = append(new, name+".c") 52 53 argv = new 54 stdin = nil 55 } 56 57 p := exec.Command(argv[0], argv[1:]...) 58 p.Stdin = bytes.NewReader(stdin) 59 var bout, berr bytes.Buffer 60 p.Stdout = &bout 61 p.Stderr = &berr 62 err := p.Run() 63 if _, ok := err.(*exec.ExitError); err != nil && !ok { 64 fatalf("%s", err) 65 } 66 ok = p.ProcessState.Success() 67 stdout, stderr = bout.Bytes(), berr.Bytes() 68 return 69 } 70 71 func find(argv []string, target string) int { 72 for i, arg := range argv { 73 if arg == target { 74 return i 75 } 76 } 77 return -1 78 } 79 80 func lineno(pos token.Pos) string { 81 return fset.Position(pos).String() 82 } 83 84 // Die with an error message. 85 func fatalf(msg string, args ...interface{}) { 86 // If we've already printed other errors, they might have 87 // caused the fatal condition. Assume they're enough. 88 if nerrors == 0 { 89 fmt.Fprintf(os.Stderr, msg+"\n", args...) 90 } 91 os.Exit(2) 92 } 93 94 var nerrors int 95 96 func error_(pos token.Pos, msg string, args ...interface{}) { 97 nerrors++ 98 if pos.IsValid() { 99 fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String()) 100 } 101 fmt.Fprintf(os.Stderr, msg, args...) 102 fmt.Fprintf(os.Stderr, "\n") 103 } 104 105 // isName reports whether s is a valid C identifier 106 func isName(s string) bool { 107 for i, v := range s { 108 if v != '_' && (v < 'A' || v > 'Z') && (v < 'a' || v > 'z') && (v < '0' || v > '9') { 109 return false 110 } 111 if i == 0 && '0' <= v && v <= '9' { 112 return false 113 } 114 } 115 return s != "" 116 } 117 118 func creat(name string) *os.File { 119 f, err := os.Create(name) 120 if err != nil { 121 fatalf("%s", err) 122 } 123 return f 124 } 125 126 func slashToUnderscore(c rune) rune { 127 if c == '/' || c == '\\' || c == ':' { 128 c = '_' 129 } 130 return c 131 } 132