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