diff options
Diffstat (limited to 'src/cmd/cgo')
-rw-r--r-- | src/cmd/cgo/Makefile | 15 | ||||
-rw-r--r-- | src/cmd/cgo/ast.go | 33 | ||||
-rw-r--r-- | src/cmd/cgo/doc.go | 45 | ||||
-rw-r--r-- | src/cmd/cgo/gcc.go | 391 | ||||
-rw-r--r-- | src/cmd/cgo/godefs.go | 289 | ||||
-rw-r--r-- | src/cmd/cgo/main.go | 125 | ||||
-rw-r--r-- | src/cmd/cgo/out.go | 324 | ||||
-rw-r--r-- | src/cmd/cgo/util.go | 21 |
8 files changed, 1000 insertions, 243 deletions
diff --git a/src/cmd/cgo/Makefile b/src/cmd/cgo/Makefile deleted file mode 100644 index 5458c3e4f..000000000 --- a/src/cmd/cgo/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# 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. - -include ../../Make.inc - -TARG=cgo -GOFILES=\ - ast.go\ - gcc.go\ - main.go\ - out.go\ - util.go\ - -include ../../Make.cmd diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index 73b7313d6..381e606ef 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -9,7 +9,6 @@ package main import ( "fmt" "go/ast" - "go/doc" "go/parser" "go/scanner" "go/token" @@ -17,7 +16,7 @@ import ( "strings" ) -func parse(name string, flags uint) *ast.File { +func parse(name string, flags parser.Mode) *ast.File { ast1, err := parser.ParseFile(fset, name, nil, flags) if err != nil { if list, ok := err.(scanner.ErrorList); ok { @@ -71,7 +70,7 @@ func (f *File) ReadGo(name string) { } sawC = true if s.Name != nil { - error(s.Path.Pos(), `cannot rename import "C"`) + error_(s.Path.Pos(), `cannot rename import "C"`) } cg := s.Doc if cg == nil && len(d.Specs) == 1 { @@ -79,12 +78,12 @@ func (f *File) ReadGo(name string) { } if cg != nil { f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) - f.Preamble += doc.CommentText(cg) + "\n" + f.Preamble += cg.Text() + "\n" } } } if !sawC { - error(token.NoPos, `cannot find import "C"`) + error_(token.NoPos, `cannot find import "C"`) } // In ast2, strip the import "C" line. @@ -128,6 +127,7 @@ func (f *File) ReadGo(name string) { f.walk(ast1, "prog", (*File).saveExport) f.walk(ast2, "prog", (*File).saveExport2) + f.Comments = ast1.Comments f.AST = ast2 } @@ -147,9 +147,12 @@ func (f *File) saveRef(x interface{}, context string) { if context == "as2" { context = "expr" } + if context == "embed-type" { + error_(sel.Pos(), "cannot embed C type") + } goname := sel.Sel.Name if goname == "errno" { - error(sel.Pos(), "cannot refer to errno directly; see documentation") + error_(sel.Pos(), "cannot refer to errno directly; see documentation") return } name := f.Name[goname] @@ -186,11 +189,11 @@ func (f *File) saveExport(x interface{}, context string) { name := strings.TrimSpace(string(c.Text[9:])) if name == "" { - error(c.Pos(), "export missing name") + error_(c.Pos(), "export missing name") } if name != n.Name.Name { - error(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name) + error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name) } f.ExpFunc = append(f.ExpFunc, &ExpFunc{ @@ -225,14 +228,18 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} // everything else just recurs default: - error(token.NoPos, "unexpected type %T in walk", x, visit) + error_(token.NoPos, "unexpected type %T in walk", x, visit) panic("unexpected type") case nil: // These are ordered and grouped to match ../../pkg/go/ast/ast.go case *ast.Field: - f.walk(&n.Type, "type", visit) + if len(n.Names) == 0 && context == "field" { + f.walk(&n.Type, "embed-type", visit) + } else { + f.walk(&n.Type, "type", visit) + } case *ast.FieldList: for _, field := range n.List { f.walk(field, context, visit) @@ -289,9 +296,9 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} case *ast.StructType: f.walk(n.Fields, "field", visit) case *ast.FuncType: - f.walk(n.Params, "field", visit) + f.walk(n.Params, "param", visit) if n.Results != nil { - f.walk(n.Results, "field", visit) + f.walk(n.Results, "param", visit) } case *ast.InterfaceType: f.walk(n.Methods, "field", visit) @@ -379,7 +386,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} f.walk(n.Specs, "spec", visit) case *ast.FuncDecl: if n.Recv != nil { - f.walk(n.Recv, "field", visit) + f.walk(n.Recv, "param", visit) } f.walk(n.Type, "type", visit) if n.Body != nil { diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index a4219867c..1bb48f44e 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -6,7 +6,8 @@ Cgo enables the creation of Go packages that call C code. -Usage: cgo [compiler options] file.go +Usage: + go tool cgo [compiler options] file.go The compiler options are passed through uninterpreted when invoking gcc to compile the C parts of the package. @@ -16,8 +17,8 @@ the pseudo-package "C" and then refers to types such as C.size_t, variables such as C.stdout, or functions such as C.putchar. If the import of "C" is immediately preceded by a comment, that -comment is used as a header when compiling the C parts of -the package. For example: +comment, called the preamble, is used as a header when compiling +the C parts of the package. For example: // #include <stdio.h> // #include <errno.h> @@ -43,6 +44,11 @@ For example: // #include <png.h> import "C" +The CGO_CFLAGS and CGO_LDFLAGS environment variables are added +to the flags derived from these directives. Package-specific flags should +be set using the directives, not the environment variables, so that builds +work in unmodified environments. + Within the Go file, C identifiers or field names that are keywords in Go can be accessed by prefixing them with an underscore: if x points at a C struct with a field named "type", x._type accesses the field. @@ -57,9 +63,11 @@ The C type void* is represented by Go's unsafe.Pointer. To access a struct, union, or enum type directly, prefix it with struct_, union_, or enum_, as in C.struct_stat. +Go structs cannot embed fields with C types. + Any C function that returns a value may be called in a multiple assignment context to retrieve both the return value and the -C errno variable as an os.Error. For example: +C errno variable as an error. For example: n, err := C.atoi("abc") @@ -73,6 +81,9 @@ A few special functions convert between Go and C types by making copies of the data. In pseudo-Go definitions: // Go string to C string + // The C string is allocated in the C heap using malloc. + // It is the caller's responsibility to arrange for it to be + // freed, such as by calling C.free. func C.CString(string) *C.char // C string to Go string @@ -84,16 +95,34 @@ by making copies of the data. In pseudo-Go definitions: // C pointer, length to Go []byte func C.GoBytes(unsafe.Pointer, C.int) []byte +Go functions can be exported for use by C code in the following way: + + //export MyFunction + func MyFunction(arg1, arg2 int, arg3 string) int64 {...} + + //export MyFunction2 + func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {...} + +They will be available in the C code as: + + extern int64 MyFunction(int arg1, int arg2, GoString arg3); + extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3); + +found in _cgo_export.h generated header, after any preambles +copied from the cgo input files. Functions with multiple +return values are mapped to functions returning a struct. +Not all Go types can be mapped to C types in a useful way. + Cgo transforms the input file into four output files: two Go source files, a C file for 6c (or 8c or 5c), and a C file for gcc. -The standard package makefile rules in Make.pkg automate the -process of using cgo. See $GOROOT/misc/cgo/stdio and -$GOROOT/misc/cgo/gmp for examples. +The standard package construction rules of the go command +automate the process of using cgo. See $GOROOT/misc/cgo/stdio +and $GOROOT/misc/cgo/gmp for examples. Cgo does not yet work with gccgo. See "C? Go? Cgo!" for an introduction to using cgo: -http://blog.golang.org/2011/03/c-go-cgo.html +http://golang.org/doc/articles/c_go_cgo.html */ package documentation diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 7ec4d8ccf..98a847e6f 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -14,16 +14,17 @@ import ( "debug/macho" "debug/pe" "encoding/binary" + "errors" "flag" "fmt" "go/ast" "go/parser" "go/token" "os" - "runtime" "strconv" "strings" "unicode" + "unicode/utf8" ) var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") @@ -59,6 +60,9 @@ func cname(s string) string { if strings.HasPrefix(s, "enum_") { return "enum " + s[len("enum_"):] } + if strings.HasPrefix(s, "sizeof_") { + return "sizeof(" + cname(s[len("sizeof_"):]) + ")" + } return s } @@ -72,7 +76,7 @@ func (p *Package) ParseFlags(f *File, srcfile string) { NextLine: for _, line := range linesIn { l := strings.TrimSpace(line) - if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) { + if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) { linesOut = append(linesOut, line) continue } @@ -91,9 +95,9 @@ NextLine: case 2: k = kf[1] switch kf[0] { - case runtime.GOOS: - case runtime.GOARCH: - case runtime.GOOS + "/" + runtime.GOARCH: + case goos: + case goarch: + case goos + "/" + goarch: default: continue NextLine } @@ -148,10 +152,10 @@ func (p *Package) addToFlag(flag string, args []string) { // pkgConfig runs pkg-config and extracts --libs and --cflags information // for packages. -func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { +func pkgConfig(packages []string) (cflags, ldflags []string, err error) { for _, name := range packages { if len(name) == 0 || name[0] == '-' { - return nil, nil, os.NewError(fmt.Sprintf("invalid name: %q", name)) + return nil, nil, errors.New(fmt.Sprintf("invalid name: %q", name)) } } @@ -159,7 +163,7 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { stdout, stderr, ok := run(nil, args) if !ok { os.Stderr.Write(stderr) - return nil, nil, os.NewError("pkg-config failed") + return nil, nil, errors.New("pkg-config failed") } cflags, err = splitQuoted(string(stdout)) if err != nil { @@ -170,7 +174,7 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { stdout, stderr, ok = run(nil, args) if !ok { os.Stderr.Write(stderr) - return nil, nil, os.NewError("pkg-config failed") + return nil, nil, errors.New("pkg-config failed") } ldflags, err = splitQuoted(string(stdout)) return @@ -192,30 +196,30 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { // // []string{"a", "b:c d", "ef", `g"`} // -func splitQuoted(s string) (r []string, err os.Error) { +func splitQuoted(s string) (r []string, err error) { var args []string - arg := make([]int, len(s)) + arg := make([]rune, len(s)) escaped := false quoted := false - quote := 0 + quote := '\x00' i := 0 - for _, rune := range s { + for _, r := range s { switch { case escaped: escaped = false - case rune == '\\': + case r == '\\': escaped = true continue case quote != 0: - if rune == quote { + if r == quote { quote = 0 continue } - case rune == '"' || rune == '\'': + case r == '"' || r == '\'': quoted = true - quote = rune + quote = r continue - case unicode.IsSpace(rune): + case unicode.IsSpace(r): if quoted || i > 0 { quoted = false args = append(args, string(arg[:i])) @@ -223,21 +227,21 @@ func splitQuoted(s string) (r []string, err os.Error) { } continue } - arg[i] = rune + arg[i] = r i++ } if quoted || i > 0 { args = append(args, string(arg[:i])) } if quote != 0 { - err = os.NewError("unclosed quote") + err = errors.New("unclosed quote") } else if escaped { - err = os.NewError("unfinished escaping") + err = errors.New("unfinished escaping") } return args, err } -var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz") +var safeBytes = []byte(`+-.,/0123456789:=ABCDEFGHIJKLMNOPQRSTUVWXYZ\_abcdefghijklmnopqrstuvwxyz`) func safeName(s string) bool { if s == "" { @@ -340,14 +344,22 @@ func (p *Package) guessKinds(f *File) []*Name { if _, err := strconv.Atoi(n.Define); err == nil { ok = true } else if n.Define[0] == '"' || n.Define[0] == '\'' { - _, err := parser.ParseExpr(fset, "", n.Define) - if err == nil { + if _, err := parser.ParseExpr(n.Define); err == nil { ok = true } } if ok { n.Kind = "const" - n.Const = n.Define + // Turn decimal into hex, just for consistency + // with enum-derived constants. Otherwise + // in the cgo -godefs output half the constants + // are in hex and half are in whatever the #define used. + i, err := strconv.ParseInt(n.Define, 0, 64) + if err == nil { + n.Const = fmt.Sprintf("%#x", i) + } else { + n.Const = n.Define + } continue } @@ -421,7 +433,7 @@ func (p *Package) guessKinds(f *File) []*Name { case strings.Contains(line, ": statement with no effect"): what = "not-type" // const or func or var case strings.Contains(line, "undeclared"): - error(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) + error_(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) case strings.Contains(line, "is not an integer constant"): isConst[i] = false continue @@ -440,6 +452,11 @@ func (p *Package) guessKinds(f *File) []*Name { for i, b := range isConst { if b { names[i].Kind = "const" + if toSniff[i] != nil && names[i].Const == "" { + j := len(needType) + needType = needType[0 : j+1] + needType[j] = names[i] + } } } for _, n := range toSniff { @@ -449,7 +466,7 @@ func (p *Package) guessKinds(f *File) []*Name { if n.Kind != "" { continue } - error(token.NoPos, "could not determine kind of name for C.%s", n.Go) + error_(token.NoPos, "could not determine kind of name for C.%s", n.Go) } if nerrors > 0 { fatalf("unresolved names") @@ -507,6 +524,10 @@ func (p *Package) loadDWARF(f *File, names []*Name) { for i, n := range names { nameToIndex[n] = i } + nameToRef := make(map[*Name]*Ref) + for _, ref := range f.Ref { + nameToRef[ref.Name] = ref + } r := d.Reader() for { e, err := r.Next() @@ -577,21 +598,28 @@ func (p *Package) loadDWARF(f *File, names []*Name) { var conv typeConv conv.Init(p.PtrSize) for i, n := range names { + if types[i] == nil { + continue + } + pos := token.NoPos + if ref, ok := nameToRef[n]; ok { + pos = ref.Pos() + } f, fok := types[i].(*dwarf.FuncType) if n.Kind != "type" && fok { n.Kind = "func" - n.FuncType = conv.FuncType(f) + n.FuncType = conv.FuncType(f, pos) } else { - n.Type = conv.Type(types[i]) + n.Type = conv.Type(types[i], pos) if enums[i] != 0 && n.Type.EnumValues != nil { k := fmt.Sprintf("__cgo_enum__%d", i) n.Kind = "const" - n.Const = strconv.Itoa64(n.Type.EnumValues[k]) + n.Const = fmt.Sprintf("%#x", n.Type.EnumValues[k]) // Remove injected enum to ensure the value will deep-compare // equally in future loads of the same constant. - n.Type.EnumValues[k] = 0, false + delete(n.Type.EnumValues, k) } else if n.Kind == "const" && i < len(enumVal) { - n.Const = strconv.Itoa64(enumVal[i]) + n.Const = fmt.Sprintf("%#x", enumVal[i]) } } } @@ -600,7 +628,8 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // rewriteRef rewrites all the C.xxx references in f.AST to refer to the // Go equivalents, now that we have figured out the meaning of all -// the xxx. +// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names +// with full definitions instead of mangled names. func (p *Package) rewriteRef(f *File) { // Assign mangled names. for _, n := range f.Name { @@ -618,7 +647,7 @@ func (p *Package) rewriteRef(f *File) { // functions are only used in calls. for _, r := range f.Ref { if r.Name.Kind == "const" && r.Name.Const == "" { - error(r.Pos(), "unable to find value of constant C.%s", r.Name.Go) + error_(r.Pos(), "unable to find value of constant C.%s", r.Name.Go) } var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default switch r.Context { @@ -629,12 +658,12 @@ func (p *Package) rewriteRef(f *File) { expr = r.Name.Type.Go break } - error(r.Pos(), "call of non-function C.%s", r.Name.Go) + error_(r.Pos(), "call of non-function C.%s", r.Name.Go) break } if r.Context == "call2" { if r.Name.FuncType.Result == nil { - error(r.Pos(), "assignment count mismatch: 2 = 0") + error_(r.Pos(), "assignment count mismatch: 2 = 0") } // Invent new Name for the two-result function. n := f.Name["2"+r.Name.Go] @@ -651,7 +680,7 @@ func (p *Package) rewriteRef(f *File) { } case "expr": if r.Name.Kind == "func" { - error(r.Pos(), "must call C.%s", r.Name.Go) + error_(r.Pos(), "must call C.%s", r.Name.Go) } if r.Name.Kind == "type" { // Okay - might be new(T) @@ -663,13 +692,28 @@ func (p *Package) rewriteRef(f *File) { case "type": if r.Name.Kind != "type" { - error(r.Pos(), "expression C.%s used as type", r.Name.Go) + error_(r.Pos(), "expression C.%s used as type", r.Name.Go) + } else if r.Name.Type == nil { + // Use of C.enum_x, C.struct_x or C.union_x without C definition. + // GCC won't raise an error when using pointers to such unknown types. + error_(r.Pos(), "type C.%s: undefined C type '%s'", r.Name.Go, r.Name.C) } else { expr = r.Name.Type.Go } default: if r.Name.Kind == "func" { - error(r.Pos(), "must call C.%s", r.Name.Go) + error_(r.Pos(), "must call C.%s", r.Name.Go) + } + } + if *godefs || *cdefs { + // Substitute definition for mangled type name. + if id, ok := expr.(*ast.Ident); ok { + if t := typedef[id.Name]; t != nil { + expr = t.Go + } + if id.Name == r.Name.Mangle && r.Name.Const != "" { + expr = ast.NewIdent(r.Name.Const) + } } } *r.Expr = expr @@ -688,7 +732,7 @@ func (p *Package) gccName() (ret string) { // gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". func (p *Package) gccMachine() []string { - switch runtime.GOARCH { + switch goarch { case "amd64": return []string{"-m64"} case "386": @@ -697,7 +741,9 @@ func (p *Package) gccMachine() []string { return nil } -var gccTmp = objDir + "_cgo_.o" +func gccTmp() string { + return *objDir + "_cgo_.o" +} // gccCmd returns the gcc command line to use for compiling // the input. @@ -706,7 +752,7 @@ func (p *Package) gccCmd() []string { p.gccName(), "-Wall", // many warnings "-Werror", // warnings are errors - "-o" + gccTmp, // write object to tmp + "-o" + gccTmp(), // write object to tmp "-gdwarf-2", // generate DWARF v2 debugging symbols "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise "-c", // do not link @@ -723,10 +769,10 @@ func (p *Package) gccCmd() []string { func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) { runGcc(stdin, p.gccCmd()) - if f, err := macho.Open(gccTmp); err == nil { + if f, err := macho.Open(gccTmp()); err == nil { d, err := f.DWARF() if err != nil { - fatalf("cannot load DWARF output from %s: %v", gccTmp, err) + fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } var data []byte if f.Symtab != nil { @@ -752,23 +798,23 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) // Can skip debug data block in ELF and PE for now. // The DWARF information is complete. - if f, err := elf.Open(gccTmp); err == nil { + if f, err := elf.Open(gccTmp()); err == nil { d, err := f.DWARF() if err != nil { - fatalf("cannot load DWARF output from %s: %v", gccTmp, err) + fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } return d, f.ByteOrder, nil } - if f, err := pe.Open(gccTmp); err == nil { + if f, err := pe.Open(gccTmp()); err == nil { d, err := f.DWARF() if err != nil { - fatalf("cannot load DWARF output from %s: %v", gccTmp, err) + fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } return d, binary.LittleEndian, nil } - fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp) + fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp()) panic("not reached") } @@ -848,7 +894,8 @@ type typeConv struct { } var tagGen int -var typedef = make(map[string]ast.Expr) +var typedef = make(map[string]*Type) +var goIdent = make(map[string]*ast.Ident) func (c *typeConv) Init(ptrSize int64) { c.ptrSize = ptrSize @@ -933,10 +980,10 @@ func (tr *TypeRepr) Set(repr string, fargs ...interface{}) { // Type returns a *Type with the same memory layout as // dtype when used as the type of a variable or a struct field. -func (c *typeConv) Type(dtype dwarf.Type) *Type { +func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { if t, ok := c.m[dtype]; ok { if t.Go == nil { - fatalf("type conversion loop at %s", dtype) + fatalf("%s: type conversion loop at %s", lineno(pos), dtype) } return t } @@ -959,11 +1006,11 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { switch dt := dtype.(type) { default: - fatalf("unexpected type: %s", dtype) + fatalf("%s: unexpected type: %s", lineno(pos), dtype) case *dwarf.AddrType: if t.Size != c.ptrSize { - fatalf("unexpected: %d-byte address type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte address type - %s", lineno(pos), t.Size, dtype) } t.Go = c.uintptr t.Align = t.Size @@ -978,7 +1025,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { Len: c.intExpr(dt.Count), } t.Go = gt // publish before recursive call - sub := c.Type(dt.Type) + sub := c.Type(dt.Type, pos) t.Align = sub.Align gt.Elt = sub.Go t.C.Set("typeof(%s[%d])", sub.C, dt.Count) @@ -989,7 +1036,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.CharType: if t.Size != 1 { - fatalf("unexpected: %d-byte char type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte char type - %s", lineno(pos), t.Size, dtype) } t.Go = c.int8 t.Align = 1 @@ -1009,7 +1056,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { } switch t.Size + int64(signed) { default: - fatalf("unexpected: %d-byte enum type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte enum type - %s", lineno(pos), t.Size, dtype) case 1: t.Go = c.uint8 case 2: @@ -1031,7 +1078,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.FloatType: switch t.Size { default: - fatalf("unexpected: %d-byte float type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte float type - %s", lineno(pos), t.Size, dtype) case 4: t.Go = c.float32 case 8: @@ -1044,7 +1091,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.ComplexType: switch t.Size { default: - fatalf("unexpected: %d-byte complex type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte complex type - %s", lineno(pos), t.Size, dtype) case 8: t.Go = c.complex64 case 16: @@ -1062,11 +1109,11 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.IntType: if dt.BitSize > 0 { - fatalf("unexpected: %d-bit int type - %s", dt.BitSize, dtype) + fatalf("%s: unexpected: %d-bit int type - %s", lineno(pos), dt.BitSize, dtype) } switch t.Size { default: - fatalf("unexpected: %d-byte int type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte int type - %s", lineno(pos), t.Size, dtype) case 1: t.Go = c.int8 case 2: @@ -1092,13 +1139,13 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { gt := &ast.StarExpr{} t.Go = gt // publish before recursive call - sub := c.Type(dt.Type) + sub := c.Type(dt.Type, pos) gt.X = sub.Go t.C.Set("%s*", sub.C) case *dwarf.QualType: // Ignore qualifier. - t = c.Type(dt.Type) + t = c.Type(dt.Type, pos) c.m[dtype] = t return t @@ -1114,19 +1161,26 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { } name := c.Ident("_Ctype_" + dt.Kind + "_" + tag) t.Go = name // publish before recursive calls + goIdent[name.Name] = name switch dt.Kind { case "union", "class": - typedef[name.Name] = c.Opaque(t.Size) + t.Go = c.Opaque(t.Size) if t.C.Empty() { t.C.Set("typeof(unsigned char[%d])", t.Size) } + typedef[name.Name] = t case "struct": - g, csyntax, align := c.Struct(dt) + g, csyntax, align := c.Struct(dt, pos) if t.C.Empty() { t.C.Set(csyntax) } t.Align = align - typedef[name.Name] = g + tt := *t + if tag != "" { + tt.C = &TypeRepr{"struct %s", []interface{}{tag}} + } + tt.Go = g + typedef[name.Name] = &tt } case *dwarf.TypedefType: @@ -1148,29 +1202,35 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { t.Align = c.ptrSize break } - name := c.Ident("_Ctypedef_" + dt.Name) + name := c.Ident("_Ctype_" + dt.Name) + goIdent[name.Name] = name t.Go = name // publish before recursive call - sub := c.Type(dt.Type) + sub := c.Type(dt.Type, pos) t.Size = sub.Size t.Align = sub.Align if _, ok := typedef[name.Name]; !ok { - typedef[name.Name] = sub.Go + tt := *t + tt.Go = sub.Go + typedef[name.Name] = &tt + } + if *godefs || *cdefs { + t.Go = sub.Go } case *dwarf.UcharType: if t.Size != 1 { - fatalf("unexpected: %d-byte uchar type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte uchar type - %s", lineno(pos), t.Size, dtype) } t.Go = c.uint8 t.Align = 1 case *dwarf.UintType: if dt.BitSize > 0 { - fatalf("unexpected: %d-bit uint type - %s", dt.BitSize, dtype) + fatalf("%s: unexpected: %d-bit uint type - %s", lineno(pos), dt.BitSize, dtype) } switch t.Size { default: - fatalf("unexpected: %d-byte uint type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte uint type - %s", lineno(pos), t.Size, dtype) case 1: t.Go = c.uint8 case 2: @@ -1198,13 +1258,16 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { } s = strings.Join(strings.Split(s, " "), "") // strip spaces name := c.Ident("_Ctype_" + s) - typedef[name.Name] = t.Go - t.Go = name + tt := *t + typedef[name.Name] = &tt + if !*godefs && !*cdefs { + t.Go = name + } } } if t.C.Empty() { - fatalf("internal error: did not create C name for %s", dtype) + fatalf("%s: internal error: did not create C name for %s", lineno(pos), dtype) } return t @@ -1212,8 +1275,8 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { // FuncArg returns a Go type with the same memory layout as // dtype when used as the type of a C function argument. -func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { - t := c.Type(dtype) +func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type { + t := c.Type(dtype, pos) switch dt := dtype.(type) { case *dwarf.ArrayType: // Arrays are passed implicitly as pointers in C. @@ -1234,9 +1297,18 @@ func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { if ptr, ok := base(dt.Type).(*dwarf.PtrType); ok { // Unless the typedef happens to point to void* since // Go has special rules around using unsafe.Pointer. - if _, void := base(ptr.Type).(*dwarf.VoidType); !void { - return c.Type(ptr) + if _, void := base(ptr.Type).(*dwarf.VoidType); void { + break + } + + t = c.Type(ptr, pos) + if t == nil { + return nil } + + // Remember the C spelling, in case the struct + // has __attribute__((unavailable)) on it. See issue 2888. + t.Typedef = dt.Name } } return t @@ -1244,7 +1316,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { // FuncType returns the Go type analogous to dtype. // There is no guarantee about matching memory layout. -func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType { +func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType { p := make([]*Type, len(dtype.ParamType)) gp := make([]*ast.Field, len(dtype.ParamType)) for i, f := range dtype.ParamType { @@ -1257,14 +1329,14 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType { p, gp = nil, nil break } - p[i] = c.FuncArg(f) + p[i] = c.FuncArg(f, pos) gp[i] = &ast.Field{Type: p[i].Go} } var r *Type var gr []*ast.Field if _, ok := dtype.ReturnType.(*dwarf.VoidType); !ok && dtype.ReturnType != nil { - r = c.Type(dtype.ReturnType) - gr = []*ast.Field{&ast.Field{Type: r.Go}} + r = c.Type(dtype.ReturnType, pos) + gr = []*ast.Field{{Type: r.Go}} } return &FuncType{ Params: p, @@ -1293,7 +1365,7 @@ func (c *typeConv) Opaque(n int64) ast.Expr { func (c *typeConv) intExpr(n int64) ast.Expr { return &ast.BasicLit{ Kind: token.INT, - Value: strconv.Itoa64(n), + Value: strconv.FormatInt(n, 10), } } @@ -1306,7 +1378,7 @@ func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field { } // Struct conversion: return Go and (6g) C syntax for type. -func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax string, align int64) { +func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) { var buf bytes.Buffer buf.WriteString("struct {") fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field @@ -1324,38 +1396,61 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s ident[f.Name] = f.Name used[f.Name] = true } - for cid, goid := range ident { - if token.Lookup([]byte(goid)).IsKeyword() { - // Avoid keyword - goid = "_" + goid - // Also avoid existing fields - for _, exist := used[goid]; exist; _, exist = used[goid] { + if !*godefs && !*cdefs { + for cid, goid := range ident { + if token.Lookup(goid).IsKeyword() { + // Avoid keyword goid = "_" + goid - } - used[goid] = true - ident[cid] = goid + // Also avoid existing fields + for _, exist := used[goid]; exist; _, exist = used[goid] { + goid = "_" + goid + } + + used[goid] = true + ident[cid] = goid + } } } + anon := 0 for _, f := range dt.Field { - if f.BitSize > 0 && f.BitSize != f.ByteSize*8 { - continue - } if f.ByteOffset > off { fld = c.pad(fld, f.ByteOffset-off) off = f.ByteOffset } - t := c.Type(f.Type) + t := c.Type(f.Type, pos) + tgo := t.Go + size := t.Size + + if f.BitSize > 0 { + if f.BitSize%8 != 0 { + continue + } + size = f.BitSize / 8 + name := tgo.(*ast.Ident).String() + if strings.HasPrefix(name, "int") { + name = "int" + } else { + name = "uint" + } + tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize)) + } + n := len(fld) fld = fld[0 : n+1] - - fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go} - off += t.Size + name := f.Name + if name == "" { + name = fmt.Sprintf("anon%d", anon) + anon++ + ident[name] = name + } + fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[name])}, Type: tgo} + off += size buf.WriteString(t.C.String()) buf.WriteString(" ") - buf.WriteString(f.Name) + buf.WriteString(name) buf.WriteString("; ") if t.Align > align { align = t.Align @@ -1366,10 +1461,100 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s off = dt.ByteSize } if off != dt.ByteSize { - fatalf("struct size calculation error") + fatalf("%s: struct size calculation error off=%d bytesize=%d", lineno(pos), off, dt.ByteSize) } buf.WriteString("}") csyntax = buf.String() + + if *godefs || *cdefs { + godefsFields(fld) + } expr = &ast.StructType{Fields: &ast.FieldList{List: fld}} return } + +func upper(s string) string { + if s == "" { + return "" + } + r, size := utf8.DecodeRuneInString(s) + if r == '_' { + return "X" + s + } + return string(unicode.ToUpper(r)) + s[size:] +} + +// godefsFields rewrites field names for use in Go or C definitions. +// It strips leading common prefixes (like tv_ in tv_sec, tv_usec) +// converts names to upper case, and rewrites _ into Pad_godefs_n, +// so that all fields are exported. +func godefsFields(fld []*ast.Field) { + prefix := fieldPrefix(fld) + npad := 0 + for _, f := range fld { + for _, n := range f.Names { + if strings.HasPrefix(n.Name, prefix) && n.Name != prefix { + n.Name = n.Name[len(prefix):] + } + if n.Name == "_" { + // Use exported name instead. + n.Name = "Pad_cgo_" + strconv.Itoa(npad) + npad++ + } + if !*cdefs { + n.Name = upper(n.Name) + } + } + p := &f.Type + t := *p + if star, ok := t.(*ast.StarExpr); ok { + star = &ast.StarExpr{X: star.X} + *p = star + p = &star.X + t = *p + } + if id, ok := t.(*ast.Ident); ok { + if id.Name == "unsafe.Pointer" { + *p = ast.NewIdent("*byte") + } + } + } +} + +// fieldPrefix returns the prefix that should be removed from all the +// field names when generating the C or Go code. For generated +// C, we leave the names as is (tv_sec, tv_usec), since that's what +// people are used to seeing in C. For generated Go code, such as +// package syscall's data structures, we drop a common prefix +// (so sec, usec, which will get turned into Sec, Usec for exporting). +func fieldPrefix(fld []*ast.Field) string { + if *cdefs { + return "" + } + prefix := "" + for _, f := range fld { + for _, n := range f.Names { + // Ignore field names that don't have the prefix we're + // looking for. It is common in C headers to have fields + // named, say, _pad in an otherwise prefixed header. + // If the struct has 3 fields tv_sec, tv_usec, _pad1, then we + // still want to remove the tv_ prefix. + // The check for "orig_" here handles orig_eax in the + // x86 ptrace register sets, which otherwise have all fields + // with reg_ prefixes. + if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") { + continue + } + i := strings.Index(n.Name, "_") + if i < 0 { + continue + } + if prefix == "" { + prefix = n.Name[:i+1] + } else if prefix != n.Name[:i+1] { + return "" + } + } + } + return prefix +} diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go new file mode 100644 index 000000000..fec70a334 --- /dev/null +++ b/src/cmd/cgo/godefs.go @@ -0,0 +1,289 @@ +// Copyright 2011 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. + +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/printer" + "go/token" + "os" + "strings" +) + +// godefs returns the output for -godefs mode. +func (p *Package) godefs(f *File, srcfile string) string { + var buf bytes.Buffer + + fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n") + fmt.Fprintf(&buf, "// %s\n", strings.Join(os.Args, " ")) + fmt.Fprintf(&buf, "\n") + + override := make(map[string]string) + + // Allow source file to specify override mappings. + // For example, the socket data structures refer + // to in_addr and in_addr6 structs but we want to be + // able to treat them as byte arrays, so the godefs + // inputs in package syscall say + // + // // +godefs map struct_in_addr [4]byte + // // +godefs map struct_in_addr6 [16]byte + // + for _, g := range f.Comments { + for _, c := range g.List { + i := strings.Index(c.Text, "+godefs map") + if i < 0 { + continue + } + s := strings.TrimSpace(c.Text[i+len("+godefs map"):]) + i = strings.Index(s, " ") + if i < 0 { + fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text) + continue + } + override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:]) + } + } + for _, n := range f.Name { + if s := override[n.Go]; s != "" { + override[n.Mangle] = s + } + } + + // Otherwise, if the source file says type T C.whatever, + // use "T" as the mangling of C.whatever, + // except in the definition (handled at end of function). + refName := make(map[*ast.Expr]*Name) + for _, r := range f.Ref { + refName[r.Expr] = r.Name + } + for _, d := range f.AST.Decls { + d, ok := d.(*ast.GenDecl) + if !ok || d.Tok != token.TYPE { + continue + } + for _, s := range d.Specs { + s := s.(*ast.TypeSpec) + n := refName[&s.Type] + if n != nil && n.Mangle != "" { + override[n.Mangle] = s.Name.Name + } + } + } + + // Extend overrides using typedefs: + // If we know that C.xxx should format as T + // and xxx is a typedef for yyy, make C.yyy format as T. + for typ, def := range typedef { + if new := override[typ]; new != "" { + if id, ok := def.Go.(*ast.Ident); ok { + override[id.Name] = new + } + } + } + + // Apply overrides. + for old, new := range override { + if id := goIdent[old]; id != nil { + id.Name = new + } + } + + // Any names still using the _C syntax are not going to compile, + // although in general we don't know whether they all made it + // into the file, so we can't warn here. + // + // The most common case is union types, which begin with + // _Ctype_union and for which typedef[name] is a Go byte + // array of the appropriate size (such as [4]byte). + // Substitute those union types with byte arrays. + for name, id := range goIdent { + if id.Name == name && strings.Contains(name, "_Ctype_union") { + if def := typedef[name]; def != nil { + id.Name = gofmt(def) + } + } + } + + conf.Fprint(&buf, fset, f.AST) + + return buf.String() +} + +// cdefs returns the output for -cdefs mode. +// The easiest way to do this is to translate the godefs Go to C. +func (p *Package) cdefs(f *File, srcfile string) string { + godefsOutput := p.godefs(f, srcfile) + + lines := strings.Split(godefsOutput, "\n") + lines[0] = "// Created by cgo -cdefs - DO NOT EDIT" + + for i, line := range lines { + lines[i] = strings.TrimSpace(line) + } + + var out bytes.Buffer + printf := func(format string, args ...interface{}) { fmt.Fprintf(&out, format, args...) } + + didTypedef := false + for i := 0; i < len(lines); i++ { + line := lines[i] + + // Delete + // package x + if strings.HasPrefix(line, "package ") { + continue + } + + // Convert + // const ( + // A = 1 + // B = 2 + // ) + // + // to + // + // enum { + // A = 1, + // B = 2, + // }; + if line == "const (" { + printf("enum {\n") + for i++; i < len(lines) && lines[i] != ")"; i++ { + line = lines[i] + if line != "" { + printf("\t%s,", line) + } + printf("\n") + } + printf("};\n") + continue + } + + // Convert + // const A = 1 + // to + // enum { A = 1 }; + if strings.HasPrefix(line, "const ") { + printf("enum { %s };\n", line[len("const "):]) + continue + } + + // On first type definition, typedef all the structs + // in case there are dependencies between them. + if !didTypedef && strings.HasPrefix(line, "type ") { + didTypedef = true + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") { + s := line[len("type ") : len(line)-len(" struct {")] + printf("typedef struct %s %s;\n", s, s) + } + } + printf("\n") + printf("#pragma pack on\n") + printf("\n") + } + + // Convert + // type T struct { + // X int64 + // Y *int32 + // Z [4]byte + // } + // + // to + // + // struct T { + // int64 X; + // int32 *Y; + // byte Z[4]; + // } + if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") { + s := line[len("type ") : len(line)-len(" struct {")] + printf("struct %s {\n", s) + for i++; i < len(lines) && lines[i] != "}"; i++ { + line := lines[i] + if line != "" { + f := strings.Fields(line) + if len(f) != 2 { + fmt.Fprintf(os.Stderr, "cgo: cannot parse struct field: %s\n", line) + nerrors++ + continue + } + printf("\t%s;", cdecl(f[0], f[1])) + } + printf("\n") + } + printf("};\n") + continue + } + + // Convert + // type T int + // to + // typedef int T; + if strings.HasPrefix(line, "type ") { + f := strings.Fields(line[len("type "):]) + if len(f) != 2 { + fmt.Fprintf(os.Stderr, "cgo: cannot parse type definition: %s\n", line) + nerrors++ + continue + } + printf("typedef\t%s;\n", cdecl(f[0], f[1])) + continue + } + + printf("%s\n", line) + } + + if didTypedef { + printf("\n") + printf("#pragma pack off\n") + } + + return out.String() +} + +// cdecl returns the C declaration for the given Go name and type. +// It only handles the specific cases necessary for converting godefs output. +func cdecl(name, typ string) string { + // X *[0]byte -> X *void + if strings.HasPrefix(typ, "*[0]") { + typ = "*void" + } + // X *byte -> *X byte + if strings.HasPrefix(typ, "*") { + name = "*" + name + typ = typ[1:] + } + // X [4]byte -> X[4] byte + if strings.HasPrefix(typ, "[") { + i := strings.Index(typ, "]") + 1 + name = name + typ[:i] + typ = typ[i:] + } + // X T -> T X + // Handle the special case: 'unsafe.Pointer' is 'void *' + if typ == "unsafe.Pointer" { + typ = "void" + name = "*" + name + } + return typ + "\t" + name +} + +var gofmtBuf bytes.Buffer + +// gofmt returns the gofmt-formatted string for an AST node. +func gofmt(n interface{}) string { + gofmtBuf.Reset() + err := printer.Fprint(&gofmtBuf, fset, n) + if err != nil { + return "<" + err.Error() + ">" + } + return gofmtBuf.String() +} diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index be9c2bc4f..7449f04c4 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -15,11 +15,13 @@ import ( "flag" "fmt" "go/ast" + "go/printer" "go/token" "io" "os" "path/filepath" "reflect" + "runtime" "strings" ) @@ -37,11 +39,13 @@ type Package struct { Decl []ast.Decl GoFiles []string // list of Go files GccFiles []string // list of gcc output files + Preamble string // collected preamble for _cgo_export.h } // A File collects information about a single Go input file. type File struct { AST *ast.File // parsed AST + Comments []*ast.CommentGroup // comments from file Package string // Package name Preamble string // C preamble (doc comment on import "C") Ref []*Ref // all references to C.xxx in AST @@ -95,6 +99,7 @@ type Type struct { C *TypeRepr Go ast.Expr EnumValues map[string]int64 + Typedef string } // A FuncType collects information about a function type in both the C and Go worlds. @@ -121,6 +126,19 @@ var cPrefix string var fset = token.NewFileSet() var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") +var dynout = flag.String("dynout", "", "write -dynobj output to this file") + +// These flags are for bootstrapping a new Go implementation, +// to generate Go and C headers that match the data layout and +// constant values used in the host's C libraries and system calls. +var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output") +var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C file to standard output") +var objDir = flag.String("objdir", "", "object directory") + +var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo") +var gccgoprefix = flag.String("gccgoprefix", "go", "prefix of symbols generated by gccgo") +var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code") +var goarch, goos string func main() { flag.Usage = usage @@ -130,7 +148,7 @@ func main() { // 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 + // imported libraries. The 'go build' 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 @@ -139,6 +157,18 @@ func main() { return } + if *godefs && *cdefs { + fmt.Fprintf(os.Stderr, "cgo: cannot use -cdefs and -godefs together\n") + os.Exit(2) + } + + if *godefs || *cdefs { + // Generating definitions pulled from header files, + // to be checked into Go repositories. + // Line numbers are just noise. + conf.Mode &^= printer.SourcePos + } + args := flag.Args() if len(args) < 1 { usage() @@ -156,32 +186,9 @@ func main() { usage() } - // Copy it to a new slice so it can grow. - gccOptions := make([]string, i) - copy(gccOptions, args[0:i]) - goFiles := args[i:] - arch := os.Getenv("GOARCH") - if arch == "" { - fatalf("$GOARCH is not set") - } - ptrSize := ptrSizeMap[arch] - if ptrSize == 0 { - fatalf("unknown $GOARCH %q", 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 := &Package{ - PtrSize: ptrSize, - GccOptions: gccOptions, - CgoFlags: make(map[string]string), - Written: make(map[string]bool), - } + p := newPackage(args[:i]) // Need a unique prefix for the global C symbols that // we use to coordinate between gcc and ourselves. @@ -197,7 +204,7 @@ func main() { io.Copy(h, f) f.Close() } - cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6]) + cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6]) fs := make([]*File, len(goFiles)) for i, input := range goFiles { @@ -208,9 +215,13 @@ func main() { fs[i] = f } - // make sure that _obj directory exists, so that we can write - // all the output files there. - os.Mkdir("_obj", 0777) + if *objDir == "" { + // make sure that _obj directory exists, so that we can write + // all the output files there. + os.Mkdir("_obj", 0777) + *objDir = "_obj" + } + *objDir += string(filepath.Separator) for i, input := range goFiles { f := fs[i] @@ -232,23 +243,64 @@ func main() { pkg = filepath.Join(dir, pkg) } p.PackagePath = pkg - p.writeOutput(f, input) - p.Record(f) + if *godefs { + os.Stdout.WriteString(p.godefs(f, input)) + } else if *cdefs { + os.Stdout.WriteString(p.cdefs(f, input)) + } else { + p.writeOutput(f, input) + } } - p.writeDefs() + if !*godefs && !*cdefs { + p.writeDefs() + } if nerrors > 0 { os.Exit(2) } } +// newPackage returns a new Package that will invoke +// gcc with the additional arguments specified in args. +func newPackage(args []string) *Package { + // Copy the gcc options to a new slice so the list + // can grow without overwriting the slice that args is in. + gccOptions := make([]string, len(args)) + copy(gccOptions, args) + + goarch = runtime.GOARCH + if s := os.Getenv("GOARCH"); s != "" { + goarch = s + } + goos = runtime.GOOS + if s := os.Getenv("GOOS"); s != "" { + goos = s + } + ptrSize := ptrSizeMap[goarch] + if ptrSize == 0 { + fatalf("unknown $GOARCH %q", goarch) + } + + // Reset locale variables so gcc emits English errors [sic]. + os.Setenv("LANG", "en_US.UTF-8") + os.Setenv("LC_ALL", "C") + + p := &Package{ + PtrSize: ptrSize, + GccOptions: gccOptions, + CgoFlags: make(map[string]string), + Written: make(map[string]bool), + } + return p +} + // 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) + error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package) } if p.Name == nil { @@ -258,11 +310,14 @@ func (p *Package) Record(f *File) { 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) + error_(token.NoPos, "inconsistent definitions for C.%s", k) } } } - p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) + if f.ExpFunc != nil { + p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) + p.Preamble += "\n" + f.Preamble + } p.Decl = append(p.Decl, f.AST.Decls...) } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 498ab1566..814250c2e 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -14,20 +14,19 @@ import ( "go/printer" "go/token" "os" - "path/filepath" "strings" ) -var objDir = "_obj" + string(filepath.Separator) +var conf = printer.Config{Mode: printer.SourcePos, Tabwidth: 8} // writeDefs creates output files to be compiled by 6g, 6c, and gcc. // (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) func (p *Package) writeDefs() { - fgo2 := creat(objDir + "_cgo_gotypes.go") - fc := creat(objDir + "_cgo_defun.c") - fm := creat(objDir + "_cgo_main.c") + fgo2 := creat(*objDir + "_cgo_gotypes.go") + fc := creat(*objDir + "_cgo_defun.c") + fm := creat(*objDir + "_cgo_main.c") - fflg := creat(objDir + "_cgo_flags") + fflg := creat(*objDir + "_cgo_flags") for k, v := range p.CgoFlags { fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) } @@ -35,7 +34,13 @@ func (p *Package) writeDefs() { // Write C main file for using gcc to resolve imports. fmt.Fprintf(fm, "int main() { return 0; }\n") - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") + if *importRuntimeCgo { + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") + } else { + // If we're not importing runtime/cgo, we *are* runtime/cgo, + // which provides crosscall2. We just need a prototype. + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c);") + } fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") @@ -45,19 +50,25 @@ func (p *Package) writeDefs() { fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") - fmt.Fprintf(fgo2, "import \"os\"\n\n") - fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") + fmt.Fprintf(fgo2, "import \"syscall\"\n\n") + if !*gccgo && *importRuntimeCgo { + fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") + } fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n") - fmt.Fprintf(fgo2, "func _Cerrno(dst *os.Error, x int) { *dst = os.Errno(x) }\n") + fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int) { *dst = syscall.Errno(x) }\n") for name, def := range typedef { fmt.Fprintf(fgo2, "type %s ", name) - printer.Fprint(fgo2, fset, def) - fmt.Fprintf(fgo2, "\n") + conf.Fprint(fgo2, fset, def.Go) + fmt.Fprintf(fgo2, "\n\n") } fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") - fmt.Fprintf(fc, cProlog) + if *gccgo { + fmt.Fprintf(fc, cPrologGccgo) + } else { + fmt.Fprintf(fc, cProlog) + } cVars := make(map[string]bool) for _, n := range p.Name { @@ -78,7 +89,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fc, "\n") fmt.Fprintf(fgo2, "var %s ", n.Mangle) - printer.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go}) + conf.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go}) fmt.Fprintf(fgo2, "\n") } fmt.Fprintf(fc, "\n") @@ -96,13 +107,26 @@ func (p *Package) writeDefs() { } } - p.writeExports(fgo2, fc, fm) + if *gccgo { + p.writeGccgoExports(fgo2, fc, fm) + } else { + p.writeExports(fgo2, fc, fm) + } fgo2.Close() fc.Close() } func dynimport(obj string) { + stdout := os.Stdout + if *dynout != "" { + f, err := os.Create(*dynout) + if err != nil { + fatalf("%s", err) + } + stdout = f + } + if f, err := elf.Open(obj); err == nil { sym, err := f.ImportedSymbols() if err != nil { @@ -113,14 +137,14 @@ func dynimport(obj string) { if s.Version != "" { targ += "@" + s.Version } - fmt.Printf("#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) + fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) } for _, l := range lib { - fmt.Printf("#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) } return } @@ -134,14 +158,14 @@ func dynimport(obj string) { if len(s) > 0 && s[0] == '_' { s = s[1:] } - fmt.Printf("#pragma dynimport %s %s %q\n", s, s, "") + fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s, s, "") } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) } for _, l := range lib { - fmt.Printf("#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) } return } @@ -153,7 +177,7 @@ func dynimport(obj string) { } for _, s := range sym { ss := strings.Split(s, ":") - fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) + fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) } return } @@ -176,7 +200,11 @@ func (p *Package) structType(n *Name) (string, int64) { fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) off += pad } - fmt.Fprintf(&buf, "\t\t%s p%d;\n", t.C, i) + c := t.Typedef + if c == "" { + c = t.C.String() + } + fmt.Fprintf(&buf, "\t\t%s p%d;\n", c, i) off += t.Size } if off%p.PtrSize != 0 { @@ -203,7 +231,7 @@ func (p *Package) structType(n *Name) (string, int64) { off += pad } if n.AddError { - fmt.Fprint(&buf, "\t\tvoid *e[2]; /* os.Error */\n") + fmt.Fprint(&buf, "\t\tvoid *e[2]; /* error */\n") off += 2 * p.PtrSize } if off == 0 { @@ -217,9 +245,9 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { name := n.Go gtype := n.FuncType.Go if n.AddError { - // Add "os.Error" to return type list. + // Add "error" to return type list. // Type list is known to be 0 or 1 element - it's a C function. - err := &ast.Field{Type: ast.NewIdent("os.Error")} + err := &ast.Field{Type: ast.NewIdent("error")} l := gtype.Results.List if len(l) == 0 { l = []*ast.Field{err} @@ -237,8 +265,45 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { Name: ast.NewIdent(n.Mangle), Type: gtype, } - printer.Fprint(fgo2, fset, d) - fmt.Fprintf(fgo2, "\n") + + if *gccgo { + // Gccgo style hooks. + // we hook directly into C. gccgo goes not support cgocall yet. + if !n.AddError { + fmt.Fprintf(fgo2, "//extern %s\n", n.C) + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, "\n") + } else { + // write a small wrapper to retrieve errno. + cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle) + paramnames := []string(nil) + for i, param := range d.Type.Params.List { + paramName := fmt.Sprintf("p%d", i) + param.Names = []*ast.Ident{ast.NewIdent(paramName)} + paramnames = append(paramnames, paramName) + } + conf.Fprint(fgo2, fset, d) + fmt.Fprintf(fgo2, "{\n") + fmt.Fprintf(fgo2, "\tsyscall.SetErrno(0)\n") + fmt.Fprintf(fgo2, "\tr := %s(%s)\n", cname, strings.Join(paramnames, ", ")) + fmt.Fprintf(fgo2, "\te := syscall.GetErrno()\n") + fmt.Fprintf(fgo2, "\tif e != 0 {\n") + fmt.Fprintf(fgo2, "\t\treturn r, e\n") + fmt.Fprintf(fgo2, "\t}\n") + fmt.Fprintf(fgo2, "\treturn r, nil\n") + fmt.Fprintf(fgo2, "}\n") + // declare the C function. + fmt.Fprintf(fgo2, "//extern %s\n", n.C) + d.Name = ast.NewIdent(cname) + l := d.Type.Results.List + d.Type.Results.List = l[:len(l)-1] + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, "\n") + } + return + } + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, "\n") if name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" { // The builtins are already defined in the C prolog. @@ -276,7 +341,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { v[0] = 0; v[1] = 0; } else { - ·_Cerrno(v, e); /* fill in v as os.Error for errno e */ + ·_Cerrno(v, e); /* fill in v as error for errno e */ } }`) } @@ -292,16 +357,15 @@ func (p *Package) writeOutput(f *File, srcfile string) { base = base[0 : len(base)-3] } base = strings.Map(slashToUnderscore, base) - fgo1 := creat(objDir + base + ".cgo1.go") - fgcc := creat(objDir + base + ".cgo2.c") + fgo1 := creat(*objDir + base + ".cgo1.go") + fgcc := creat(*objDir + base + ".cgo2.c") p.GoFiles = append(p.GoFiles, base+".cgo1.go") p.GccFiles = append(p.GccFiles, base+".cgo2.c") // Write Go output: Go input with rewrites of C.xxx to _C_xxx. fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n") - fmt.Fprintf(fgo1, "//line %s:1\n", srcfile) - printer.Fprint(fgo1, fset, f.AST) + conf.Fprint(fgo1, fset, f.AST) // While we process the vars and funcs, also write 6c and gcc output. // Gcc output starts with the preamble. @@ -327,6 +391,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { } p.Written[name] = true + if *gccgo { + // we don't use wrappers with gccgo. + return + } + ctype, _ := p.structType(n) // Gcc wrapper unpacks the C argument struct @@ -351,10 +420,20 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { } } fmt.Fprintf(fgcc, "%s(", n.C) - for i := range n.FuncType.Params { + for i, t := range n.FuncType.Params { if i > 0 { fmt.Fprintf(fgcc, ", ") } + // We know the type params are correct, because + // the Go equivalents had good type params. + // However, our version of the type omits the magic + // words const and volatile, which can provoke + // C compiler warnings. Silence them by casting + // all pointers to void*. (Eventually that will produce + // other warnings.) + if c := t.C.String(); c[len(c)-1] == '*' { + fmt.Fprintf(fgcc, "(void*)") + } fmt.Fprintf(fgcc, "a->p%d", i) } fmt.Fprintf(fgcc, ");\n") @@ -368,10 +447,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { // Write out the various stubs we need to support functions exported // from Go so that they are callable from C. func (p *Package) writeExports(fgo2, fc, fm *os.File) { - fgcc := creat(objDir + "_cgo_export.c") - fgcch := creat("_cgo_export.h") + fgcc := creat(*objDir + "_cgo_export.c") + fgcch := creat(*objDir + "_cgo_export.h") fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcch, "%s\n", p.Preamble) fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog) fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") @@ -501,8 +581,10 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { if fn.Recv != nil { goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname } - fmt.Fprintf(fc, "extern void ·%s();\n", goname) - fmt.Fprintf(fc, "\nvoid\n") + fmt.Fprintf(fc, "#pragma dynexport %s %s\n", goname, goname) + fmt.Fprintf(fc, "extern void ·%s();\n\n", goname) + fmt.Fprintf(fc, "#pragma textflag 7\n") // no split stack, so no use of m or g + fmt.Fprintf(fc, "void\n") fmt.Fprintf(fc, "_cgoexp%s_%s(void *a, int32 n)\n", cPrefix, exp.ExpName) fmt.Fprintf(fc, "{\n") fmt.Fprintf(fc, "\truntime·cgocallback(·%s, a, n);\n", goname) @@ -514,11 +596,11 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { // a Go wrapper function. if fn.Recv != nil { fmt.Fprintf(fgo2, "func %s(recv ", goname) - printer.Fprint(fgo2, fset, fn.Recv.List[0].Type) + conf.Fprint(fgo2, fset, fn.Recv.List[0].Type) forFieldList(fntype.Params, func(i int, atype ast.Expr) { fmt.Fprintf(fgo2, ", p%d ", i) - printer.Fprint(fgo2, fset, atype) + conf.Fprint(fgo2, fset, atype) }) fmt.Fprintf(fgo2, ")") if gccResult != "void" { @@ -528,7 +610,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { if i > 0 { fmt.Fprint(fgo2, ", ") } - printer.Fprint(fgo2, fset, atype) + conf.Fprint(fgo2, fset, atype) }) fmt.Fprint(fgo2, ")") } @@ -551,6 +633,83 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { } } +// Write out the C header allowing C code to call exported gccgo functions. +func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) { + fgcc := creat(*objDir + "_cgo_export.c") + fgcch := creat(*objDir + "_cgo_export.h") + _ = fgcc + + fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcch, "%s\n", p.Preamble) + fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog) + fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n") + + clean := func(r rune) rune { + switch { + case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', + '0' <= r && r <= '9': + return r + } + return '_' + } + gccgoSymbolPrefix := strings.Map(clean, *gccgoprefix) + + for _, exp := range p.ExpFunc { + // TODO: support functions with receivers. + fn := exp.Func + fntype := fn.Type + + if !ast.IsExported(fn.Name.Name) { + fatalf("cannot export unexported function %s with gccgo", fn.Name) + } + + cdeclBuf := new(bytes.Buffer) + resultCount := 0 + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { resultCount++ }) + switch resultCount { + case 0: + fmt.Fprintf(cdeclBuf, "void") + case 1: + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + t := p.cgoType(atype) + fmt.Fprintf(cdeclBuf, "%s", t.C) + }) + default: + // Declare a result struct. + fmt.Fprintf(fgcch, "struct %s_result {\n", exp.ExpName) + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + t := p.cgoType(atype) + fmt.Fprintf(fgcch, "\t%s r%d;\n", t.C, i) + }) + fmt.Fprintf(fgcch, "};\n") + fmt.Fprintf(cdeclBuf, "struct %s_result", exp.ExpName) + } + + // The function name. + fmt.Fprintf(cdeclBuf, " "+exp.ExpName) + gccgoSymbol := fmt.Sprintf("%s.%s.%s", gccgoSymbolPrefix, p.PackageName, exp.Func.Name) + fmt.Fprintf(cdeclBuf, " (") + // Function parameters. + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprintf(cdeclBuf, ", ") + } + t := p.cgoType(atype) + fmt.Fprintf(cdeclBuf, "%s p%d", t.C, i) + }) + fmt.Fprintf(cdeclBuf, ")") + cdecl := cdeclBuf.String() + + fmt.Fprintf(fgcch, "extern %s __asm__(\"%s\");\n", cdecl, gccgoSymbol) + // Dummy declaration for _cgo_main.c + fmt.Fprintf(fm, "%s {}\n", cdecl) + } +} + // Call a function for each entry in an ast.FieldList, passing the // index into the list and the type. func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) { @@ -577,22 +736,25 @@ func c(repr string, args ...interface{}) *TypeRepr { // Map predeclared Go types to Type. var goTypes = map[string]*Type{ - "int": &Type{Size: 4, Align: 4, C: c("int")}, - "uint": &Type{Size: 4, Align: 4, C: c("uint")}, - "int8": &Type{Size: 1, Align: 1, C: c("schar")}, - "uint8": &Type{Size: 1, Align: 1, C: c("uchar")}, - "int16": &Type{Size: 2, Align: 2, C: c("short")}, - "uint16": &Type{Size: 2, Align: 2, C: c("ushort")}, - "int32": &Type{Size: 4, Align: 4, C: c("int")}, - "uint32": &Type{Size: 4, Align: 4, C: c("uint")}, - "int64": &Type{Size: 8, Align: 8, C: c("int64")}, - "uint64": &Type{Size: 8, Align: 8, C: c("uint64")}, - "float": &Type{Size: 4, Align: 4, C: c("float")}, - "float32": &Type{Size: 4, Align: 4, C: c("float")}, - "float64": &Type{Size: 8, Align: 8, C: c("double")}, - "complex": &Type{Size: 8, Align: 8, C: c("__complex float")}, - "complex64": &Type{Size: 8, Align: 8, C: c("__complex float")}, - "complex128": &Type{Size: 16, Align: 16, C: c("__complex double")}, + "bool": {Size: 1, Align: 1, C: c("uchar")}, + "byte": {Size: 1, Align: 1, C: c("uchar")}, + "int": {Size: 4, Align: 4, C: c("int")}, + "uint": {Size: 4, Align: 4, C: c("uint")}, + "rune": {Size: 4, Align: 4, C: c("int")}, + "int8": {Size: 1, Align: 1, C: c("schar")}, + "uint8": {Size: 1, Align: 1, C: c("uchar")}, + "int16": {Size: 2, Align: 2, C: c("short")}, + "uint16": {Size: 2, Align: 2, C: c("ushort")}, + "int32": {Size: 4, Align: 4, C: c("int")}, + "uint32": {Size: 4, Align: 4, C: c("uint")}, + "int64": {Size: 8, Align: 8, C: c("int64")}, + "uint64": {Size: 8, Align: 8, C: c("uint64")}, + "float": {Size: 4, Align: 4, C: c("float")}, + "float32": {Size: 4, Align: 4, C: c("float")}, + "float64": {Size: 8, Align: 8, C: c("double")}, + "complex": {Size: 8, Align: 8, C: c("__complex float")}, + "complex64": {Size: 8, Align: 8, C: c("__complex float")}, + "complex128": {Size: 16, Align: 16, C: c("__complex double")}, } // Map an ast type to a Type. @@ -610,7 +772,7 @@ func (p *Package) cgoType(e ast.Expr) *Type { case *ast.FuncType: return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")} case *ast.InterfaceType: - return &Type{Size: 3 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} + return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} case *ast.MapType: return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoMap")} case *ast.ChanType: @@ -633,10 +795,8 @@ func (p *Package) cgoType(e ast.Expr) *Type { } } } - for name, def := range typedef { - if name == t.Name { - return p.cgoType(def) - } + if def := typedef[t.Name]; def != nil { + return def } if t.Name == "uintptr" { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("uintptr")} @@ -644,13 +804,16 @@ func (p *Package) cgoType(e ast.Expr) *Type { if t.Name == "string" { return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: c("GoString")} } + if t.Name == "error" { + return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} + } if r, ok := goTypes[t.Name]; ok { if r.Align > p.PtrSize { r.Align = p.PtrSize } return r } - error(e.Pos(), "unrecognized Go type %s", t.Name) + error_(e.Pos(), "unrecognized Go type %s", t.Name) return &Type{Size: 4, Align: 4, C: c("int")} case *ast.SelectorExpr: id, ok := t.X.(*ast.Ident) @@ -658,7 +821,7 @@ func (p *Package) cgoType(e ast.Expr) *Type { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")} } } - error(e.Pos(), "unrecognized Go type %T", e) + error_(e.Pos(), "Go type not supported in export: %s", gofmt(e)) return &Type{Size: 4, Align: 4, C: c("int")} } @@ -729,6 +892,43 @@ void } ` +const cPrologGccgo = ` +#include <stdint.h> +#include <string.h> + +struct __go_string { + const unsigned char *__data; + int __length; +}; + +typedef struct __go_open_array { + void* __values; + int __count; + int __capacity; +} Slice; + +struct __go_string __go_byte_array_to_string(const void* p, int len); +struct __go_open_array __go_string_to_byte_array (struct __go_string str); + +const char *CString(struct __go_string s) { + return strndup((const char*)s.__data, s.__length); +} + +struct __go_string GoString(char *p) { + int len = (p != NULL) ? strlen(p) : 0; + return __go_byte_array_to_string(p, len); +} + +struct __go_string GoStringN(char *p, int n) { + return __go_byte_array_to_string(p, n); +} + +Slice GoBytes(char *p, int n) { + struct __go_string s = { (const unsigned char *)p, n }; + return __go_string_to_byte_array(s); +} +` + const gccExportHeaderProlog = ` typedef unsigned int uint; typedef signed char schar; diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index e79b0e1bf..a0f216614 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -5,11 +5,11 @@ package main import ( - "exec" "fmt" "go/token" "io/ioutil" "os" + "os/exec" ) // run runs the command argv, feeding in stdin on standard input. @@ -36,7 +36,6 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { if err != nil { fatalf("%s", err) } - defer p.Release() r0.Close() w1.Close() w2.Close() @@ -56,23 +55,31 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { <-c <-c - w, err := p.Wait(0) + state, err := p.Wait() if err != nil { fatalf("%s", err) } - ok = w.Exited() && w.ExitStatus() == 0 + ok = state.Success() return } +func lineno(pos token.Pos) string { + return fset.Position(pos).String() +} + // Die with an error message. func fatalf(msg string, args ...interface{}) { - fmt.Fprintf(os.Stderr, msg+"\n", args...) + // If we've already printed other errors, they might have + // caused the fatal condition. Assume they're enough. + if nerrors == 0 { + fmt.Fprintf(os.Stderr, msg+"\n", args...) + } os.Exit(2) } var nerrors int -func error(pos token.Pos, msg string, args ...interface{}) { +func error_(pos token.Pos, msg string, args ...interface{}) { nerrors++ if pos.IsValid() { fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String()) @@ -102,7 +109,7 @@ func creat(name string) *os.File { return f } -func slashToUnderscore(c int) int { +func slashToUnderscore(c rune) rune { if c == '/' || c == '\\' || c == ':' { c = '_' } |