From 3211b2cca98936e94dc2e819dbe474337ecdd24e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Oudompheng?= Date: Thu, 15 Mar 2012 23:50:25 +0100 Subject: [PATCH] cmd/cgo: add support for function export for gccgo. A "gccgoprefix" flag is added and used by the go tool, to mirror the -fgo-prefix flag for gccgo, whose value is required to know how to access functions from C. Trying to export Go methods or unexported Go functions will not work. Also fix go test on "main" packages. Updates #2313. Fixes #3262. R=mpimenov, rsc, iant CC=golang-dev https://golang.org/cl/5797046 --- src/cmd/cgo/main.go | 1 + src/cmd/cgo/out.go | 83 ++++++++++++++++++++++++++++++++++++++++++++- src/cmd/go/build.go | 23 +++++++++---- src/cmd/go/pkg.go | 21 ++++++------ src/cmd/go/test.go | 3 +- 5 files changed, 112 insertions(+), 19 deletions(-) diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 5f307607ba..7449f04c4c 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -136,6 +136,7 @@ var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C 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 diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index bbadad1bed..933d7e6cab 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -107,7 +107,11 @@ 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() @@ -624,6 +628,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)) { diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 1cc2dc4ca4..eb51d2d789 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -661,7 +661,7 @@ func (b *builder) build(a *action) (err error) { } cgoExe := tool("cgo") - if a.cgo != nil { + if a.cgo != nil && a.cgo.target != "" { cgoExe = a.cgo.target } outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles) @@ -1239,12 +1239,8 @@ func (gccgcToolchain) gc(b *builder, p *Package, obj string, importArgs []string out := p.Name + ".o" ofile = obj + out gcargs := []string{"-g"} - if p.Name != "main" { - if p.fake { - gcargs = append(gcargs, "-fgo-prefix=fake_"+p.ImportPath) - } else { - gcargs = append(gcargs, "-fgo-prefix=go_"+p.ImportPath) - } + if prefix := gccgoPrefix(p); prefix != "" { + gcargs = append(gcargs, "-fgo-prefix="+gccgoPrefix(p)) } args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags) for _, f := range gofiles { @@ -1304,6 +1300,16 @@ func (gccgcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) er "-DGOOS_"+goos, "-DGOARCH_"+goarch, "-c", cfile) } +func gccgoPrefix(p *Package) string { + switch { + case p.build.IsCommand() && !p.forceLibrary: + return "" + case p.fake: + return "fake_" + p.ImportPath + } + return "go_" + p.ImportPath +} + // gcc runs the gcc C compiler to create an object from a single C file. func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error { cfile = mkAbs(p.Dir, cfile) @@ -1404,6 +1410,9 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, } if _, ok := buildToolchain.(gccgcToolchain); ok { cgoflags = append(cgoflags, "-gccgo") + if prefix := gccgoPrefix(p); prefix != "" { + cgoflags = append(cgoflags, "-gccgoprefix="+gccgoPrefix(p)) + } } if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, cgoflags, "--", cgoCFLAGS, p.CgoFiles); err != nil { return nil, nil, err diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 46ada4002b..44dbd6798a 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -64,16 +64,17 @@ type Package struct { XTestImports []string `json:",omitempty"` // imports from XTestGoFiles // Unexported fields are not part of the public API. - build *build.Package - pkgdir string // overrides build.PkgDir - imports []*Package - deps []*Package - gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths - target string // installed file for this package (may be executable) - fake bool // synthesized package - forceBuild bool // this package must be rebuilt - local bool // imported via local path (./ or ../) - localPrefix string // interpret ./ and ../ imports relative to this prefix + build *build.Package + pkgdir string // overrides build.PkgDir + imports []*Package + deps []*Package + gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths + target string // installed file for this package (may be executable) + fake bool // synthesized package + forceBuild bool // this package must be rebuilt + forceLibrary bool // this package is a library (even if named "main") + local bool // imported via local path (./ or ../) + localPrefix string // interpret ./ and ../ imports relative to this prefix } func (p *Package) copyBuild(pp *build.Package) { diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 6aecbe7c06..870ab190fc 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -446,6 +446,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, ptest.imports = append(append([]*Package{}, p.imports...), imports...) ptest.pkgdir = testDir ptest.fake = true + ptest.forceLibrary = true ptest.Stale = true ptest.build = new(build.Package) *ptest.build = *p.build @@ -489,7 +490,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, ImportPath: "testmain", Root: p.Root, imports: []*Package{ptest}, - build: &build.Package{}, + build: &build.Package{Name: "main"}, fake: true, Stale: true, }