Commit 3211b2cc authored by Rémy Oudompheng's avatar Rémy Oudompheng

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
parent 86c7bc6e
...@@ -136,6 +136,7 @@ var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C ...@@ -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 objDir = flag.String("objdir", "", "object directory")
var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo") 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 importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
var goarch, goos string var goarch, goos string
......
...@@ -107,7 +107,11 @@ func (p *Package) writeDefs() { ...@@ -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() fgo2.Close()
fc.Close() fc.Close()
...@@ -624,6 +628,83 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { ...@@ -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 // Call a function for each entry in an ast.FieldList, passing the
// index into the list and the type. // index into the list and the type.
func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) { func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) {
......
...@@ -661,7 +661,7 @@ func (b *builder) build(a *action) (err error) { ...@@ -661,7 +661,7 @@ func (b *builder) build(a *action) (err error) {
} }
cgoExe := tool("cgo") cgoExe := tool("cgo")
if a.cgo != nil { if a.cgo != nil && a.cgo.target != "" {
cgoExe = a.cgo.target cgoExe = a.cgo.target
} }
outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles) 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 ...@@ -1239,12 +1239,8 @@ func (gccgcToolchain) gc(b *builder, p *Package, obj string, importArgs []string
out := p.Name + ".o" out := p.Name + ".o"
ofile = obj + out ofile = obj + out
gcargs := []string{"-g"} gcargs := []string{"-g"}
if p.Name != "main" { if prefix := gccgoPrefix(p); prefix != "" {
if p.fake { gcargs = append(gcargs, "-fgo-prefix="+gccgoPrefix(p))
gcargs = append(gcargs, "-fgo-prefix=fake_"+p.ImportPath)
} else {
gcargs = append(gcargs, "-fgo-prefix=go_"+p.ImportPath)
}
} }
args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags) args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags)
for _, f := range gofiles { for _, f := range gofiles {
...@@ -1304,6 +1300,16 @@ func (gccgcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) er ...@@ -1304,6 +1300,16 @@ func (gccgcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) er
"-DGOOS_"+goos, "-DGOARCH_"+goarch, "-c", cfile) "-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. // 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 { func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error {
cfile = mkAbs(p.Dir, cfile) cfile = mkAbs(p.Dir, cfile)
...@@ -1404,6 +1410,9 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, ...@@ -1404,6 +1410,9 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
} }
if _, ok := buildToolchain.(gccgcToolchain); ok { if _, ok := buildToolchain.(gccgcToolchain); ok {
cgoflags = append(cgoflags, "-gccgo") 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 { if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, cgoflags, "--", cgoCFLAGS, p.CgoFiles); err != nil {
return nil, nil, err return nil, nil, err
......
...@@ -64,16 +64,17 @@ type Package struct { ...@@ -64,16 +64,17 @@ type Package struct {
XTestImports []string `json:",omitempty"` // imports from XTestGoFiles XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
// Unexported fields are not part of the public API. // Unexported fields are not part of the public API.
build *build.Package build *build.Package
pkgdir string // overrides build.PkgDir pkgdir string // overrides build.PkgDir
imports []*Package imports []*Package
deps []*Package deps []*Package
gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths
target string // installed file for this package (may be executable) target string // installed file for this package (may be executable)
fake bool // synthesized package fake bool // synthesized package
forceBuild bool // this package must be rebuilt forceBuild bool // this package must be rebuilt
local bool // imported via local path (./ or ../) forceLibrary bool // this package is a library (even if named "main")
localPrefix string // interpret ./ and ../ imports relative to this prefix local bool // imported via local path (./ or ../)
localPrefix string // interpret ./ and ../ imports relative to this prefix
} }
func (p *Package) copyBuild(pp *build.Package) { func (p *Package) copyBuild(pp *build.Package) {
......
...@@ -446,6 +446,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, ...@@ -446,6 +446,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
ptest.imports = append(append([]*Package{}, p.imports...), imports...) ptest.imports = append(append([]*Package{}, p.imports...), imports...)
ptest.pkgdir = testDir ptest.pkgdir = testDir
ptest.fake = true ptest.fake = true
ptest.forceLibrary = true
ptest.Stale = true ptest.Stale = true
ptest.build = new(build.Package) ptest.build = new(build.Package)
*ptest.build = *p.build *ptest.build = *p.build
...@@ -489,7 +490,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, ...@@ -489,7 +490,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
ImportPath: "testmain", ImportPath: "testmain",
Root: p.Root, Root: p.Root,
imports: []*Package{ptest}, imports: []*Package{ptest},
build: &build.Package{}, build: &build.Package{Name: "main"},
fake: true, fake: true,
Stale: true, Stale: true,
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment