mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cgo: various bug fixes
* remember #defined names, so that C.stdout can refer
to the real name (on OS X) __stdoutp.
* better handling of #defined constant expressions
* allow n, err = C.strtol("asdf", 0, 123) to get errno as os.Error
* write all output files to current directory
* don't require gcc output if there was no input
Fixes #533.
Fixes #709.
Fixes #756.
R=r
CC=dho, golang-dev, iant
https://golang.org/cl/1734047
This commit is contained in:
parent
e8fcf60093
commit
0432f289f7
13 changed files with 1321 additions and 732 deletions
|
|
@ -11,13 +11,91 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"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
|
||||
}
|
||||
|
||||
// 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.Position {
|
||||
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 +103,34 @@ 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",
|
||||
}
|
||||
|
||||
func main() {
|
||||
args := os.Args
|
||||
if len(args) < 2 {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
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 +138,88 @@ 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),
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
case "call", "call2":
|
||||
if cref.Name.Kind != "type" {
|
||||
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
|
||||
*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()
|
||||
}
|
||||
|
||||
// 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(noPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
|
||||
}
|
||||
|
||||
if p.Typedef == nil {
|
||||
p.Typedef = f.Typedef
|
||||
} else {
|
||||
for k, v := range f.Typedef {
|
||||
if p.Typedef[k] == nil {
|
||||
p.Typedef[k] = v
|
||||
} else if !reflect.DeepEqual(p.Typedef[k], v) {
|
||||
error(noPos, "inconsistent definitions for C type %s", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(noPos, "inconsistent definitions for C.%s", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(f.ExpFunc) > 0 {
|
||||
n := len(p.ExpFunc)
|
||||
ef := make([]*ExpFunc, n+len(f.ExpFunc))
|
||||
copy(ef, p.ExpFunc)
|
||||
copy(ef[n:], f.ExpFunc)
|
||||
p.ExpFunc = ef
|
||||
}
|
||||
|
||||
if len(f.AST.Decls) > 0 {
|
||||
n := len(p.Decl)
|
||||
d := make([]ast.Decl, n+len(f.AST.Decls))
|
||||
copy(d, p.Decl)
|
||||
copy(d[n:], f.AST.Decls)
|
||||
p.Decl = d
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue