summaryrefslogtreecommitdiff
path: root/src/cmd/cgo/main.go
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
committerOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
commit3e45412327a2654a77944249962b3652e6142299 (patch)
treebc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/cmd/cgo/main.go
parentc533680039762cacbc37db8dc7eed074c3e497be (diff)
downloadgolang-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.go256
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...)
}