github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/cgo/main.go (about) 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 // Cgo; see gmp.go for an overview. 6 7 // TODO(rsc): 8 // Emit correct line number annotations. 9 // Make 6g understand the annotations. 10 11 package main 12 13 import ( 14 "crypto/md5" 15 "flag" 16 "fmt" 17 "go/ast" 18 "go/printer" 19 "go/token" 20 "io" 21 "os" 22 "path/filepath" 23 "reflect" 24 "runtime" 25 "sort" 26 "strings" 27 ) 28 29 // A Package collects information about the package we're going to write. 30 type Package struct { 31 PackageName string // name of package 32 PackagePath string 33 PtrSize int64 34 IntSize int64 35 GccOptions []string 36 CgoFlags map[string][]string // #cgo flags (CFLAGS, LDFLAGS) 37 Written map[string]bool 38 Name map[string]*Name // accumulated Name from Files 39 ExpFunc []*ExpFunc // accumulated ExpFunc from Files 40 Decl []ast.Decl 41 GoFiles []string // list of Go files 42 GccFiles []string // list of gcc output files 43 Preamble string // collected preamble for _cgo_export.h 44 } 45 46 // A File collects information about a single Go input file. 47 type File struct { 48 AST *ast.File // parsed AST 49 Comments []*ast.CommentGroup // comments from file 50 Package string // Package name 51 Preamble string // C preamble (doc comment on import "C") 52 Ref []*Ref // all references to C.xxx in AST 53 ExpFunc []*ExpFunc // exported functions for this file 54 Name map[string]*Name // map from Go name to Name 55 } 56 57 func nameKeys(m map[string]*Name) []string { 58 var ks []string 59 for k := range m { 60 ks = append(ks, k) 61 } 62 sort.Strings(ks) 63 return ks 64 } 65 66 // A Ref refers to an expression of the form C.xxx in the AST. 67 type Ref struct { 68 Name *Name 69 Expr *ast.Expr 70 Context string // "type", "expr", "call", or "call2" 71 } 72 73 func (r *Ref) Pos() token.Pos { 74 return (*r.Expr).Pos() 75 } 76 77 // A Name collects information about C.xxx. 78 type Name struct { 79 Go string // name used in Go referring to package C 80 Mangle string // name used in generated Go 81 C string // name used in C 82 Define string // #define expansion 83 Kind string // "const", "type", "var", "func", "not-type" 84 Type *Type // the type of xxx 85 FuncType *FuncType 86 AddError bool 87 Const string // constant definition 88 } 89 90 // A ExpFunc is an exported function, callable from C. 91 // Such functions are identified in the Go input file 92 // by doc comments containing the line //export ExpName 93 type ExpFunc struct { 94 Func *ast.FuncDecl 95 ExpName string // name to use from C 96 } 97 98 // A TypeRepr contains the string representation of a type. 99 type TypeRepr struct { 100 Repr string 101 FormatArgs []interface{} 102 } 103 104 // A Type collects information about a type in both the C and Go worlds. 105 type Type struct { 106 Size int64 107 Align int64 108 C *TypeRepr 109 Go ast.Expr 110 EnumValues map[string]int64 111 Typedef string 112 } 113 114 // A FuncType collects information about a function type in both the C and Go worlds. 115 type FuncType struct { 116 Params []*Type 117 Result *Type 118 Go *ast.FuncType 119 } 120 121 func usage() { 122 fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n") 123 flag.PrintDefaults() 124 os.Exit(2) 125 } 126 127 var ptrSizeMap = map[string]int64{ 128 "386": 4, 129 "amd64": 8, 130 "arm": 4, 131 } 132 133 var intSizeMap = map[string]int64{ 134 "386": 4, 135 "amd64": 8, 136 "arm": 4, 137 } 138 139 var cPrefix string 140 141 var fset = token.NewFileSet() 142 143 var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") 144 var dynout = flag.String("dynout", "", "write -dynobj output to this file") 145 var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in dynimport mode") 146 147 // These flags are for bootstrapping a new Go implementation, 148 // to generate Go and C headers that match the data layout and 149 // constant values used in the host's C libraries and system calls. 150 var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output") 151 var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C file to standard output") 152 var objDir = flag.String("objdir", "", "object directory") 153 154 var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo") 155 var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo") 156 var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo") 157 var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code") 158 var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code") 159 var goarch, goos string 160 161 func main() { 162 flag.Usage = usage 163 flag.Parse() 164 165 if *dynobj != "" { 166 // cgo -dynimport is essentially a separate helper command 167 // built into the cgo binary. It scans a gcc-produced executable 168 // and dumps information about the imported symbols and the 169 // imported libraries. The 'go build' rules for cgo prepare an 170 // appropriate executable and then use its import information 171 // instead of needing to make the linkers duplicate all the 172 // specialized knowledge gcc has about where to look for imported 173 // symbols and which ones to use. 174 dynimport(*dynobj) 175 return 176 } 177 178 if *godefs && *cdefs { 179 fmt.Fprintf(os.Stderr, "cgo: cannot use -cdefs and -godefs together\n") 180 os.Exit(2) 181 } 182 183 if *godefs || *cdefs { 184 // Generating definitions pulled from header files, 185 // to be checked into Go repositories. 186 // Line numbers are just noise. 187 conf.Mode &^= printer.SourcePos 188 } 189 190 args := flag.Args() 191 if len(args) < 1 { 192 usage() 193 } 194 195 // Find first arg that looks like a go file and assume everything before 196 // that are options to pass to gcc. 197 var i int 198 for i = len(args); i > 0; i-- { 199 if !strings.HasSuffix(args[i-1], ".go") { 200 break 201 } 202 } 203 if i == len(args) { 204 usage() 205 } 206 207 goFiles := args[i:] 208 209 p := newPackage(args[:i]) 210 211 // Record CGO_LDFLAGS from the environment for external linking. 212 if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" { 213 args, err := splitQuoted(ldflags) 214 if err != nil { 215 fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err) 216 } 217 p.addToFlag("LDFLAGS", args) 218 } 219 220 // Need a unique prefix for the global C symbols that 221 // we use to coordinate between gcc and ourselves. 222 // We already put _cgo_ at the beginning, so the main 223 // concern is other cgo wrappers for the same functions. 224 // Use the beginning of the md5 of the input to disambiguate. 225 h := md5.New() 226 for _, input := range goFiles { 227 f, err := os.Open(input) 228 if err != nil { 229 fatalf("%s", err) 230 } 231 io.Copy(h, f) 232 f.Close() 233 } 234 cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6]) 235 236 fs := make([]*File, len(goFiles)) 237 for i, input := range goFiles { 238 f := new(File) 239 f.ReadGo(input) 240 f.DiscardCgoDirectives() 241 fs[i] = f 242 } 243 244 if *objDir == "" { 245 // make sure that _obj directory exists, so that we can write 246 // all the output files there. 247 os.Mkdir("_obj", 0777) 248 *objDir = "_obj" 249 } 250 *objDir += string(filepath.Separator) 251 252 for i, input := range goFiles { 253 f := fs[i] 254 p.Translate(f) 255 for _, cref := range f.Ref { 256 switch cref.Context { 257 case "call", "call2": 258 if cref.Name.Kind != "type" { 259 break 260 } 261 *cref.Expr = cref.Name.Type.Go 262 } 263 } 264 if nerrors > 0 { 265 os.Exit(2) 266 } 267 pkg := f.Package 268 if dir := os.Getenv("CGOPKGPATH"); dir != "" { 269 pkg = filepath.Join(dir, pkg) 270 } 271 p.PackagePath = pkg 272 p.Record(f) 273 if *godefs { 274 os.Stdout.WriteString(p.godefs(f, input)) 275 } else if *cdefs { 276 os.Stdout.WriteString(p.cdefs(f, input)) 277 } else { 278 p.writeOutput(f, input) 279 } 280 } 281 282 if !*godefs && !*cdefs { 283 p.writeDefs() 284 } 285 if nerrors > 0 { 286 os.Exit(2) 287 } 288 } 289 290 // newPackage returns a new Package that will invoke 291 // gcc with the additional arguments specified in args. 292 func newPackage(args []string) *Package { 293 goarch = runtime.GOARCH 294 if s := os.Getenv("GOARCH"); s != "" { 295 goarch = s 296 } 297 goos = runtime.GOOS 298 if s := os.Getenv("GOOS"); s != "" { 299 goos = s 300 } 301 ptrSize := ptrSizeMap[goarch] 302 if ptrSize == 0 { 303 fatalf("unknown ptrSize for $GOARCH %q", goarch) 304 } 305 intSize := intSizeMap[goarch] 306 if intSize == 0 { 307 fatalf("unknown intSize for $GOARCH %q", goarch) 308 } 309 310 // Reset locale variables so gcc emits English errors [sic]. 311 os.Setenv("LANG", "en_US.UTF-8") 312 os.Setenv("LC_ALL", "C") 313 314 p := &Package{ 315 PtrSize: ptrSize, 316 IntSize: intSize, 317 CgoFlags: make(map[string][]string), 318 Written: make(map[string]bool), 319 } 320 p.addToFlag("CFLAGS", args) 321 return p 322 } 323 324 // Record what needs to be recorded about f. 325 func (p *Package) Record(f *File) { 326 if p.PackageName == "" { 327 p.PackageName = f.Package 328 } else if p.PackageName != f.Package { 329 error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package) 330 } 331 332 if p.Name == nil { 333 p.Name = f.Name 334 } else { 335 for k, v := range f.Name { 336 if p.Name[k] == nil { 337 p.Name[k] = v 338 } else if !reflect.DeepEqual(p.Name[k], v) { 339 error_(token.NoPos, "inconsistent definitions for C.%s", k) 340 } 341 } 342 } 343 344 if f.ExpFunc != nil { 345 p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) 346 p.Preamble += "\n" + f.Preamble 347 } 348 p.Decl = append(p.Decl, f.AST.Decls...) 349 }