]> Cypherpunks repositories - gostls13.git/commitdiff
cgo: Make constants #define'd in C available to Go (as consts)
authorDevon H. O'Dell <devon.odell@gmail.com>
Mon, 11 Jan 2010 21:05:26 +0000 (13:05 -0800)
committerRuss Cox <rsc@golang.org>
Mon, 11 Jan 2010 21:05:26 +0000 (13:05 -0800)
  Fixes #435

R=rsc
CC=golang-dev
https://golang.org/cl/181161

src/cmd/cgo/ast.go
src/cmd/cgo/gcc.go
src/cmd/cgo/main.go
src/cmd/cgo/out.go

index 7d6221369e17658c47b29953ca0d093ae51328b8..d96a8bd9d0439ff77e2fccb4af897d398d11c075 100644 (file)
@@ -19,7 +19,7 @@ import (
 type Cref struct {
        Name     string
        Expr     *ast.Expr
-       Context  string // "type", "expr", or "call"
+       Context  string // "type", "expr", "const", or "call"
        TypeName bool   // whether xxx is a C type name
        Type     *Type  // the type of xxx
        FuncType *FuncType
@@ -36,6 +36,7 @@ type Prog struct {
        Vardef      map[string]*Type
        Funcdef     map[string]*FuncType
        Enumdef     map[string]int64
+       Constdef    map[string]string
        PtrSize     int64
        GccOptions  []string
        OutDefs     map[string]bool
index c701a6bbcae6be48f7e2efc92218ee57078d9426..dd6223ea7760342c78c3572a0fad3ad0f2e5bf75 100644 (file)
@@ -14,6 +14,7 @@ import (
        "debug/macho"
        "fmt"
        "go/ast"
+       "go/parser"
        "go/token"
        "os"
        "strconv"
@@ -21,9 +22,51 @@ import (
 )
 
 func (p *Prog) loadDebugInfo() {
+       var b bytes.Buffer
+
+       b.WriteString(p.Preamble)
+       stdout := p.gccPostProc(b.Bytes())
+       defines := make(map[string]string)
+       for _, line := range strings.Split(stdout, "\n", 0) {
+               if len(line) < 9 || line[0:7] != "#define" {
+                       continue
+               }
+
+               line = strings.TrimSpace(line[8:])
+
+               var key, val string
+               spaceIndex := strings.Index(line, " ")
+               tabIndex := strings.Index(line, "\t")
+
+               if spaceIndex == -1 && tabIndex == -1 {
+                       continue
+               } else if tabIndex == -1 || (spaceIndex != -1 && spaceIndex < tabIndex) {
+                       key = line[0:spaceIndex]
+                       val = strings.TrimSpace(line[spaceIndex:])
+               } else {
+                       key = line[0:tabIndex]
+                       val = strings.TrimSpace(line[tabIndex:])
+               }
+
+               defines[key] = val
+       }
+
        // Construct a slice of unique names from p.Crefs.
        m := make(map[string]int)
        for _, c := range p.Crefs {
+               // If we've already found this name as a define, it is not a Cref.
+               if val, ok := defines[c.Name]; ok {
+                       _, err := parser.ParseExpr("", val)
+                       if err != nil {
+                               fmt.Fprintf(os.Stderr, "The value in C.%s does not parse as a Go expression; cannot use.\n", c.Name)
+                               os.Exit(2)
+                       }
+
+                       c.Context = "const"
+                       c.TypeName = false
+                       p.Constdef[c.Name] = val
+                       continue
+               }
                m[c.Name] = -1
        }
        names := make([]string, 0, len(m))
@@ -46,7 +89,7 @@ func (p *Prog) loadDebugInfo() {
        //      x.c:2: error: 'name' undeclared (first use in this function)
        // A line number directive causes the line number to
        // correspond to the index in the names array.
-       var b bytes.Buffer
+       b.Reset()
        b.WriteString(p.Preamble)
        b.WriteString("void f(void) {\n")
        b.WriteString("#line 0 \"cgo-test\"\n")
@@ -189,7 +232,13 @@ func (p *Prog) loadDebugInfo() {
        var conv typeConv
        conv.Init(p.PtrSize)
        for _, c := range p.Crefs {
-               i := m[c.Name]
+               i, ok := m[c.Name]
+               if !ok {
+                       if _, ok := p.Constdef[c.Name]; !ok {
+                               fatal("Cref %s is no longer around", c.Name)
+                       }
+                       continue
+               }
                c.TypeName = kind[c.Name] == "type"
                f, fok := types[i].(*dwarf.FuncType)
                if c.Context == "call" && !c.TypeName && fok {
@@ -257,6 +306,21 @@ func (p *Prog) gccDebug(stdin []byte) (*dwarf.Data, string) {
        return d, ""
 }
 
+func (p *Prog) gccPostProc(stdin []byte) string {
+       machine := "-m32"
+       if p.PtrSize == 8 {
+               machine = "-m64"
+       }
+
+       base := []string{"gcc", machine, "-E", "-dM", "-xc", "-"}
+       stdout, stderr, ok := run(stdin, concat(base, p.GccOptions))
+       if !ok {
+               return string(stderr)
+       }
+
+       return string(stdout)
+}
+
 // A typeConv is a translator from dwarf types to Go types
 // with equivalent memory layout.
 type typeConv struct {
index 607f26b22c081f65be0203fd07c0b12d1664249e..e0a305c4da5eecabc77b68b23cd203d2cf603cf8 100644 (file)
@@ -76,6 +76,7 @@ func main() {
        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 {
@@ -91,6 +92,10 @@ func main() {
                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.Ident{Value: cref.Name}
+                               break
                        case "call":
                                if !cref.TypeName {
                                        // Is an actual function call.
index 4c72f4c987820a1c22fbc6832e663edc81891baf..d628bef452f2c671d7f2e13a3ca68a1bfa257225 100644 (file)
@@ -57,6 +57,10 @@ func (p *Prog) writeDefs() {
        }
        fmt.Fprintf(fc, "\n")
 
+       for name, value := range p.Constdef {
+               fmt.Fprintf(fgo2, "const %s = %s\n", name, value)
+       }
+
        for name, value := range p.Enumdef {
                fmt.Fprintf(fgo2, "const %s = %d\n", name, value)
        }