diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
commit | 3e45412327a2654a77944249962b3652e6142299 (patch) | |
tree | bc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/cmd/cgo/main.go | |
parent | c533680039762cacbc37db8dc7eed074c3e497be (diff) | |
download | golang-upstream/2011.01.12.tar.gz |
Imported Upstream version 2011.01.12upstream/2011.01.12
Diffstat (limited to 'src/cmd/cgo/main.go')
-rw-r--r-- | src/cmd/cgo/main.go | 256 |
1 files changed, 181 insertions, 75 deletions
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 070146c9a..942bda5f4 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -11,13 +11,95 @@ package main import ( + "crypto/md5" + "flag" "fmt" "go/ast" + "go/token" + "io" "os" + "reflect" "strings" ) -func usage() { fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") } +// A Package collects information about the package we're going to write. +type Package struct { + PackageName string // name of package + PackagePath string + PtrSize int64 + GccOptions []string + Written map[string]bool + Name map[string]*Name // accumulated Name from Files + Typedef map[string]ast.Expr // accumulated Typedef from Files + ExpFunc []*ExpFunc // accumulated ExpFunc from Files + Decl []ast.Decl + GoFiles []string // list of Go files + GccFiles []string // list of gcc output files +} + +// A File collects information about a single Go input file. +type File struct { + AST *ast.File // parsed AST + Package string // Package name + Preamble string // C preamble (doc comment on import "C") + Ref []*Ref // all references to C.xxx in AST + ExpFunc []*ExpFunc // exported functions for this file + Name map[string]*Name // map from Go name to Name + Typedef map[string]ast.Expr // translations of all necessary types from C +} + +// A Ref refers to an expression of the form C.xxx in the AST. +type Ref struct { + Name *Name + Expr *ast.Expr + Context string // "type", "expr", "call", or "call2" +} + +func (r *Ref) Pos() token.Pos { + return (*r.Expr).Pos() +} + +// A Name collects information about C.xxx. +type Name struct { + Go string // name used in Go referring to package C + Mangle string // name used in generated Go + C string // name used in C + Define string // #define expansion + Kind string // "const", "type", "var", "func", "not-type" + Type *Type // the type of xxx + FuncType *FuncType + AddError bool + Const string // constant definition +} + +// A ExpFunc is an exported function, callable from C. +// Such functions are identified in the Go input file +// by doc comments containing the line //export ExpName +type ExpFunc struct { + Func *ast.FuncDecl + ExpName string // name to use from C +} + +// A Type collects information about a type in both the C and Go worlds. +type Type struct { + Size int64 + Align int64 + C string + Go ast.Expr + EnumValues map[string]int64 +} + +// A FuncType collects information about a function type in both the C and Go worlds. +type FuncType struct { + Params []*Type + Result *Type + Go *ast.FuncType +} + +func usage() { + fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") + os.Exit(2) +} var ptrSizeMap = map[string]int64{ "386": 4, @@ -25,43 +107,60 @@ var ptrSizeMap = map[string]int64{ "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", -} +var cPrefix string + +var fset = token.NewFileSet() + +var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") func main() { - args := os.Args - if len(args) < 2 { + flag.Usage = usage + flag.Parse() + + if *dynobj != "" { + // cgo -dynimport is essentially a separate helper command + // built into the cgo binary. It scans a gcc-produced executable + // and dumps information about the imported symbols and the + // imported libraries. The Make.pkg rules for cgo prepare an + // appropriate executable and then use its import information + // instead of needing to make the linkers duplicate all the + // specialized knowledge gcc has about where to look for imported + // symbols and which ones to use. + syms, imports := dynimport(*dynobj) + for _, sym := range syms { + fmt.Printf("#pragma dynimport %s %s %q\n", sym, sym, "") + } + for _, p := range imports { + fmt.Printf("#pragma dynimport %s %s %q\n", "_", "_", p) + } + return + } + + args := flag.Args() + if len(args) < 1 { 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") { + for i = len(args); i > 0; i-- { + if !strings.HasSuffix(args[i-1], ".go") { break } } - - i += 1 - - gccOptions, goFiles := args[1:i], args[i:] + if i == len(args) { + usage() + } + gccOptions, goFiles := args[0: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) + ptrSize := ptrSizeMap[arch] + if ptrSize == 0 { + fatal("unknown $GOARCH %q", arch) } // Clear locale variables so gcc emits English errors [sic]. @@ -69,75 +168,82 @@ func main() { 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) + p := &Package{ + PtrSize: ptrSize, + GccOptions: gccOptions, + Written: make(map[string]bool), + } + // Need a unique prefix for the global C symbols that + // we use to coordinate between gcc and ourselves. + // We already put _cgo_ at the beginning, so the main + // concern is other cgo wrappers for the same functions. + // Use the beginning of the md5 of the input to disambiguate. + h := md5.New() 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 - } + f, err := os.Open(input, os.O_RDONLY, 0) + if err != nil { + fatal("%s", err) } - p.loadDebugInfo() - for _, cref := range p.Crefs { + io.Copy(h, f) + f.Close() + } + cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6]) + + for _, input := range goFiles { + f := new(File) + // Reset f.Preamble so that we don't end up with conflicting headers / defines + f.Preamble = "" + f.ReadGo(input) + p.Translate(f) + for _, cref := range f.Ref { 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] + case "call", "call2": + if cref.Name.Kind != "type" { 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 + *cref.Expr = cref.Name.Type.Go } } if nerrors > 0 { os.Exit(2) } - pkg := p.Package + pkg := f.Package if dir := os.Getenv("CGOPKGPATH"); dir != "" { pkg = dir + "/" + pkg } p.PackagePath = pkg - p.writeOutput(input) + p.writeOutput(f, input) + + p.Record(f) } p.writeDefs() + if nerrors > 0 { + os.Exit(2) + } +} + +// Record what needs to be recorded about f. +func (p *Package) Record(f *File) { + if p.PackageName == "" { + p.PackageName = f.Package + } else if p.PackageName != f.Package { + error(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package) + } + + if p.Name == nil { + p.Name = f.Name + } else { + for k, v := range f.Name { + if p.Name[k] == nil { + p.Name[k] = v + } else if !reflect.DeepEqual(p.Name[k], v) { + error(token.NoPos, "inconsistent definitions for C.%s", k) + } + } + } + + p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) + p.Decl = append(p.Decl, f.AST.Decls...) } |