From 758ff64c69e34965f8af5b2d6ffd65e8d7ab2150 Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Mon, 14 Feb 2011 13:23:51 +0100 Subject: Imported Upstream version 2011-02-01.1 --- src/cmd/cgo/ast.go | 21 +++++--- src/cmd/cgo/doc.go | 9 ++++ src/cmd/cgo/gcc.go | 144 +++++++++++++++++++++++++++++++++++++++++++++++----- src/cmd/cgo/main.go | 32 ++++++++++-- src/cmd/cgo/out.go | 21 ++++++-- 5 files changed, 197 insertions(+), 30 deletions(-) (limited to 'src/cmd/cgo') 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 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 + 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] == ":" { - 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 <= 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 -- cgit v1.2.3