summaryrefslogtreecommitdiff
path: root/src/cmd/cgo
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-02-14 13:23:51 +0100
committerOndřej Surý <ondrej@sury.org>2011-02-14 13:23:51 +0100
commit758ff64c69e34965f8af5b2d6ffd65e8d7ab2150 (patch)
tree6d6b34f8c678862fe9b56c945a7b63f68502c245 /src/cmd/cgo
parent3e45412327a2654a77944249962b3652e6142299 (diff)
downloadgolang-758ff64c69e34965f8af5b2d6ffd65e8d7ab2150.tar.gz
Imported Upstream version 2011-02-01.1upstream/2011-02-01.1
Diffstat (limited to 'src/cmd/cgo')
-rw-r--r--src/cmd/cgo/ast.go21
-rw-r--r--src/cmd/cgo/doc.go9
-rw-r--r--src/cmd/cgo/gcc.go144
-rw-r--r--src/cmd/cgo/main.go32
-rw-r--r--src/cmd/cgo/out.go21
5 files changed, 197 insertions, 30 deletions
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
index 8689ac3da..2eae22aed 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -35,6 +35,10 @@ func parse(name string, flags uint) *ast.File {
return ast1
}
+func sourceLine(n ast.Node) int {
+ return fset.Position(n.Pos()).Line
+}
+
// ReadGo populates f with information learned from reading the
// Go source file with the given file name. It gathers the C preamble
// attached to the import "C" comment, a list of references to C.xxx,
@@ -69,10 +73,13 @@ func (f *File) ReadGo(name string) {
if s.Name != nil {
error(s.Path.Pos(), `cannot rename import "C"`)
}
- if s.Doc != nil {
- f.Preamble += doc.CommentText(s.Doc) + "\n"
- } else if len(d.Specs) == 1 && d.Doc != nil {
- f.Preamble += doc.CommentText(d.Doc) + "\n"
+ cg := s.Doc
+ if cg == nil && len(d.Specs) == 1 {
+ cg = d.Doc
+ }
+ if cg != nil {
+ f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name)
+ f.Preamble += doc.CommentText(cg) + "\n"
}
}
}
@@ -298,6 +305,9 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
f.walk(n.Stmt, "stmt", visit)
case *ast.ExprStmt:
f.walk(&n.X, "expr", visit)
+ case *ast.SendStmt:
+ f.walk(&n.Chan, "expr", visit)
+ f.walk(&n.Value, "expr", visit)
case *ast.IncDecStmt:
f.walk(&n.X, "expr", visit)
case *ast.AssignStmt:
@@ -336,8 +346,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
f.walk(n.Assign, "stmt", visit)
f.walk(n.Body, "stmt", visit)
case *ast.CommClause:
- f.walk(n.Lhs, "expr", visit)
- f.walk(n.Rhs, "expr", visit)
+ f.walk(n.Comm, "stmt", visit)
f.walk(n.Body, "stmt", visit)
case *ast.SelectStmt:
f.walk(n.Body, "stmt", visit)
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index 0f9204d7f..c4868345c 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -23,6 +23,15 @@ the package. For example:
// #include <errno.h>
import "C"
+CFLAGS and LDFLAGS may be defined with pseudo #cgo directives
+within these comments to tweak the behavior of gcc. Values defined
+in multiple directives are concatenated together. For example:
+
+ // #cgo CFLAGS: -DPNG_DEBUG=1
+ // #cgo LDFLAGS: -lpng
+ // #include <png.h>
+ import "C"
+
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.
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index be3b8fe64..cadc6fae9 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -21,19 +21,22 @@ import (
"os"
"strconv"
"strings"
+ "unicode"
)
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
var debugGcc = flag.Bool("debug-gcc", false, "print gcc invocations")
var nameToC = 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",
+ "schar": "signed char",
+ "uchar": "unsigned char",
+ "ushort": "unsigned short",
+ "uint": "unsigned int",
+ "ulong": "unsigned long",
+ "longlong": "long long",
+ "ulonglong": "unsigned long long",
+ "complexfloat": "float complex",
+ "complexdouble": "double complex",
}
// cname returns the C name to use for C.s.
@@ -57,6 +60,107 @@ func cname(s string) string {
return s
}
+// ParseFlags extracts #cgo CFLAGS and LDFLAGS options from the file
+// preamble. Multiple occurrences are concatenated with a separating space,
+// even across files.
+func (p *Package) ParseFlags(f *File, srcfile string) {
+ linesIn := strings.Split(f.Preamble, "\n", -1)
+ linesOut := make([]string, 0, len(linesIn))
+ for _, line := range linesIn {
+ l := strings.TrimSpace(line)
+ if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) {
+ linesOut = append(linesOut, line)
+ continue
+ }
+
+ l = strings.TrimSpace(l[4:])
+ fields := strings.Split(l, ":", 2)
+ if len(fields) != 2 {
+ fatal("%s: bad #cgo line: %s", srcfile, line)
+ }
+
+ k := fields[0]
+ v := strings.TrimSpace(fields[1])
+ if k != "CFLAGS" && k != "LDFLAGS" {
+ fatal("%s: unsupported #cgo option %s", srcfile, k)
+ }
+ args, err := splitQuoted(v)
+ if err != nil {
+ fatal("%s: bad #cgo option %s: %s", srcfile, k, err.String())
+ }
+ if oldv, ok := p.CgoFlags[k]; ok {
+ p.CgoFlags[k] = oldv + " " + v
+ } else {
+ p.CgoFlags[k] = v
+ }
+ if k == "CFLAGS" {
+ p.GccOptions = append(p.GccOptions, args...)
+ }
+ }
+ f.Preamble = strings.Join(linesOut, "\n")
+}
+
+// splitQuoted splits the string s around each instance of one or more consecutive
+// white space characters while taking into account quotes and escaping, and
+// returns an array of substrings of s or an empty list if s contains only white space.
+// Single quotes and double quotes are recognized to prevent splitting within the
+// quoted region, and are removed from the resulting substrings. If a quote in s
+// isn't closed err will be set and r will have the unclosed argument as the
+// last element. The backslash is used for escaping.
+//
+// For example, the following string:
+//
+// `a b:"c d" 'e''f' "g\""`
+//
+// Would be parsed as:
+//
+// []string{"a", "b:c d", "ef", `g"`}
+//
+func splitQuoted(s string) (r []string, err os.Error) {
+ var args []string
+ arg := make([]int, len(s))
+ escaped := false
+ quoted := false
+ quote := 0
+ i := 0
+ for _, rune := range s {
+ switch {
+ case escaped:
+ escaped = false
+ case rune == '\\':
+ escaped = true
+ continue
+ case quote != 0:
+ if rune == quote {
+ quote = 0
+ continue
+ }
+ case rune == '"' || rune == '\'':
+ quoted = true
+ quote = rune
+ continue
+ case unicode.IsSpace(rune):
+ if quoted || i > 0 {
+ quoted = false
+ args = append(args, string(arg[:i]))
+ i = 0
+ }
+ continue
+ }
+ arg[i] = rune
+ i++
+ }
+ if quoted || i > 0 {
+ args = append(args, string(arg[:i]))
+ }
+ if quote != 0 {
+ err = os.ErrorString("unclosed quote")
+ } else if escaped {
+ err = os.ErrorString("unfinished escaping")
+ }
+ return args, err
+}
+
// Translate rewrites f.AST, the original Go input, to remove
// references to the imported package C, replacing them with
// references to the equivalent Go types, functions, and variables.
@@ -205,9 +309,7 @@ func (p *Package) guessKinds(f *File) []*Name {
for _, line := range strings.Split(stderr, "\n", -1) {
if len(line) < 9 || line[0:9] != "cgo-test:" {
- if len(line) > 8 && line[0:8] == "<stdin>:" {
- fatal("gcc produced unexpected output:\n%s\non input:\n%s", line, b.Bytes())
- }
+ // the user will see any compiler errors when the code is compiled later.
continue
}
line = line[9:]
@@ -568,10 +670,6 @@ func runGcc(stdin []byte, args []string) (string, string) {
os.Stderr.Write(stderr)
}
if !ok {
- fmt.Fprint(os.Stderr, "Error running gcc:\n")
- fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " "))
- os.Stderr.Write(stdin)
- fmt.Fprint(os.Stderr, "EOF\n")
os.Stderr.Write(stderr)
os.Exit(2)
}
@@ -591,6 +689,7 @@ type typeConv struct {
int8, int16, int32, int64 ast.Expr
uint8, uint16, uint32, uint64, uintptr ast.Expr
float32, float64 ast.Expr
+ complex64, complex128 ast.Expr
void ast.Expr
unsafePointer ast.Expr
string ast.Expr
@@ -617,6 +716,8 @@ func (c *typeConv) Init(ptrSize int64) {
c.uintptr = c.Ident("uintptr")
c.float32 = c.Ident("float32")
c.float64 = c.Ident("float64")
+ c.complex64 = c.Ident("complex64")
+ c.complex128 = c.Ident("complex128")
c.unsafePointer = c.Ident("unsafe.Pointer")
c.void = c.Ident("void")
c.string = c.Ident("string")
@@ -648,6 +749,8 @@ var dwarfToName = map[string]string{
"long long int": "longlong",
"long long unsigned int": "ulonglong",
"signed char": "schar",
+ "float complex": "complexfloat",
+ "double complex": "complexdouble",
}
// Type returns a *Type with the same memory layout as
@@ -749,6 +852,19 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
t.Align = c.ptrSize
}
+ case *dwarf.ComplexType:
+ switch t.Size {
+ default:
+ fatal("unexpected: %d-byte complex type - %s", t.Size, dtype)
+ case 8:
+ t.Go = c.complex64
+ case 16:
+ t.Go = c.complex128
+ }
+ if t.Align = t.Size; t.Align >= c.ptrSize {
+ t.Align = c.ptrSize
+ }
+
case *dwarf.FuncType:
// No attempt at translation: would enable calls
// directly between worlds, but we need to moderate those.
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index 942bda5f4..b15d34527 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -20,6 +20,7 @@ import (
"os"
"reflect"
"strings"
+ "runtime"
)
// A Package collects information about the package we're going to write.
@@ -28,6 +29,7 @@ type Package struct {
PackagePath string
PtrSize int64
GccOptions []string
+ CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS)
Written map[string]bool
Name map[string]*Name // accumulated Name from Files
Typedef map[string]ast.Expr // accumulated Typedef from Files
@@ -97,7 +99,8 @@ type FuncType struct {
}
func usage() {
- fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n")
+ fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
+ flag.PrintDefaults()
os.Exit(2)
}
@@ -127,6 +130,13 @@ func main() {
// specialized knowledge gcc has about where to look for imported
// symbols and which ones to use.
syms, imports := dynimport(*dynobj)
+ if runtime.GOOS == "windows" {
+ for _, sym := range syms {
+ ss := strings.Split(sym, ":", -1)
+ fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1]))
+ }
+ return
+ }
for _, sym := range syms {
fmt.Printf("#pragma dynimport %s %s %q\n", sym, sym, "")
}
@@ -152,7 +162,12 @@ func main() {
if i == len(args) {
usage()
}
- gccOptions, goFiles := args[0:i], args[i:]
+
+ // 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 == "" {
@@ -171,6 +186,7 @@ func main() {
p := &Package{
PtrSize: ptrSize,
GccOptions: gccOptions,
+ CgoFlags: make(map[string]string),
Written: make(map[string]bool),
}
@@ -190,11 +206,17 @@ func main() {
}
cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6])
- for _, input := range goFiles {
+ fs := make([]*File, len(goFiles))
+ for i, input := range goFiles {
+ // Parse flags for all files before translating due to CFLAGS.
f := new(File)
- // Reset f.Preamble so that we don't end up with conflicting headers / defines
- f.Preamble = ""
f.ReadGo(input)
+ p.ParseFlags(f, input)
+ fs[i] = f
+ }
+
+ for i, input := range goFiles {
+ f := fs[i]
p.Translate(f)
for _, cref := range f.Ref {
switch cref.Context {
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index c3f9ae60b..ede8f57d8 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -8,6 +8,7 @@ import (
"bytes"
"debug/elf"
"debug/macho"
+ "debug/pe"
"fmt"
"go/ast"
"go/printer"
@@ -32,9 +33,17 @@ func (p *Package) writeDefs() {
fc := creat("_cgo_defun.c")
fm := creat("_cgo_main.c")
+ fflg := creat("_cgo_flags")
+ for k, v := range p.CgoFlags {
+ fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v)
+ }
+ fflg.Close()
+
// Write C main file for using gcc to resolve imports.
fmt.Fprintf(fm, "int main() { return 0; }\n")
- fmt.Fprintf(fm, "int crosscall2;\n\n")
+ fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n")
+ fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n")
+ fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n")
// Write second Go output: definitions of _C_xxx.
// In a separate file so that the import of "unsafe" does not
@@ -101,12 +110,14 @@ func dynimport(obj string) (syms, imports []string) {
ImportedSymbols() ([]string, os.Error)
}
var isMacho bool
- var err1, err2 os.Error
+ var err1, err2, err3 os.Error
if f, err1 = elf.Open(obj); err1 != nil {
- if f, err2 = macho.Open(obj); err2 != nil {
- fatal("cannot parse %s as ELF (%v) or Mach-O (%v)", obj, err1, err2)
+ if f, err2 = pe.Open(obj); err2 != nil {
+ if f, err3 = macho.Open(obj); err3 != nil {
+ fatal("cannot parse %s as ELF (%v) or PE (%v) or Mach-O (%v)", obj, err1, err2, err3)
+ }
+ isMacho = true
}
- isMacho = true
}
var err os.Error