| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Cgo; see gmp.go for an overview. |
| |
| // TODO(rsc): |
| // Emit correct line number annotations. |
| // Make 6g understand the annotations. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "go/ast" |
| "os" |
| "strings" |
| ) |
| |
| func usage() { fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") } |
| |
| var ptrSizeMap = map[string]int64{ |
| "386": 4, |
| "amd64": 8, |
| "arm": 4, |
| } |
| |
| var expandName = map[string]string{ |
| "schar": "signed char", |
| "uchar": "unsigned char", |
| "ushort": "unsigned short", |
| "uint": "unsigned int", |
| "ulong": "unsigned long", |
| "longlong": "long long", |
| "ulonglong": "unsigned long long", |
| } |
| |
| func main() { |
| args := os.Args |
| if len(args) < 2 { |
| usage() |
| os.Exit(2) |
| } |
| |
| // Find first arg that looks like a go file and assume everything before |
| // that are options to pass to gcc. |
| var i int |
| for i = len(args) - 1; i > 0; i-- { |
| if !strings.HasSuffix(args[i], ".go") { |
| break |
| } |
| } |
| |
| i += 1 |
| |
| gccOptions, goFiles := args[1:i], args[i:] |
| |
| arch := os.Getenv("GOARCH") |
| if arch == "" { |
| fatal("$GOARCH is not set") |
| } |
| ptrSize, ok := ptrSizeMap[arch] |
| if !ok { |
| fatal("unknown architecture %s", arch) |
| } |
| |
| // Clear locale variables so gcc emits English errors [sic]. |
| os.Setenv("LANG", "en_US.UTF-8") |
| os.Setenv("LC_ALL", "C") |
| os.Setenv("LC_CTYPE", "C") |
| |
| p := new(Prog) |
| |
| p.PtrSize = ptrSize |
| p.GccOptions = gccOptions |
| p.Vardef = make(map[string]*Type) |
| p.Funcdef = make(map[string]*FuncType) |
| p.Enumdef = make(map[string]int64) |
| p.Constdef = make(map[string]string) |
| p.OutDefs = make(map[string]bool) |
| |
| for _, input := range goFiles { |
| // Reset p.Preamble so that we don't end up with conflicting headers / defines |
| p.Preamble = builtinProlog |
| openProg(input, p) |
| for _, cref := range p.Crefs { |
| // Convert C.ulong to C.unsigned long, etc. |
| if expand, ok := expandName[cref.Name]; ok { |
| cref.Name = expand |
| } |
| } |
| p.loadDebugInfo() |
| for _, cref := range p.Crefs { |
| switch cref.Context { |
| case "const": |
| // This came from a #define and we'll output it later. |
| *cref.Expr = ast.NewIdent(cref.Name) |
| break |
| case "call": |
| if !cref.TypeName { |
| // Is an actual function call. |
| pos := (*cref.Expr).Pos() |
| *cref.Expr = &ast.Ident{Position: pos, Obj: ast.NewObj(ast.Err, pos, "_C_"+cref.Name)} |
| p.Funcdef[cref.Name] = cref.FuncType |
| break |
| } |
| *cref.Expr = cref.Type.Go |
| case "expr": |
| if cref.TypeName { |
| error((*cref.Expr).Pos(), "type C.%s used as expression", cref.Name) |
| } |
| // If the expression refers to an enumerated value, then |
| // place the identifier for the value and add it to Enumdef so |
| // it will be declared as a constant in the later stage. |
| if cref.Type.EnumValues != nil { |
| *cref.Expr = ast.NewIdent(cref.Name) |
| p.Enumdef[cref.Name] = cref.Type.EnumValues[cref.Name] |
| break |
| } |
| // Reference to C variable. |
| // We declare a pointer and arrange to have it filled in. |
| *cref.Expr = &ast.StarExpr{X: ast.NewIdent("_C_" + cref.Name)} |
| p.Vardef[cref.Name] = cref.Type |
| case "type": |
| if !cref.TypeName { |
| error((*cref.Expr).Pos(), "expression C.%s used as type", cref.Name) |
| } |
| *cref.Expr = cref.Type.Go |
| } |
| } |
| if nerrors > 0 { |
| os.Exit(2) |
| } |
| pkg := p.Package |
| if dir := os.Getenv("CGOPKGPATH"); dir != "" { |
| pkg = dir + "/" + pkg |
| } |
| p.PackagePath = pkg |
| p.writeOutput(input) |
| } |
| |
| p.writeDefs() |
| } |