mirror of
https://github.com/golang/go
synced 2024-11-22 03:04:41 -07:00
Allow cgo to accept multiple .go inputs for a package
Fixes #342. R=rsc CC=golang-dev https://golang.org/cl/179062
This commit is contained in:
parent
7a5f4be97e
commit
9277b02537
67
src/Make.pkg
67
src/Make.pkg
@ -36,18 +36,21 @@ INSTALLFILES=$(pkgdir)/$(TARG).a
|
||||
|
||||
# The rest of the cgo rules are below, but these variable updates
|
||||
# must be done here so they apply to the main rules.
|
||||
ifdef CGOFILES
|
||||
GOFILES+=$(patsubst %.go,%.cgo1.go,$(CGOFILES))
|
||||
GOFILES+=$(patsubst %.go,%.cgo2.go,$(CGOFILES))
|
||||
OFILES+=$(patsubst %.go,%.cgo3.$O,$(CGOFILES))
|
||||
INSTALLFILES+=$(patsubst %.go,$(pkgdir)/$(dir)/$(elem)_%.so,$(CGOFILES))
|
||||
GOFILES+=_cgo_gotypes.go
|
||||
OFILES+=_cgo_defun.$O
|
||||
GCC_OFILES=$(patsubst %.go,%.cgo2.o,$(CGOFILES))
|
||||
INSTALLFILES+=$(pkgdir)/$(dir)/$(TARG).so
|
||||
PREREQ+=$(patsubst %,%.make,$(DEPS))
|
||||
endif
|
||||
|
||||
coverage:
|
||||
$(QUOTED_GOBIN)/gotest
|
||||
$(QUOTED_GOBIN)/6cov -g $(shell pwd) $O.out | grep -v '_test\.go:'
|
||||
|
||||
clean:
|
||||
rm -rf *.[$(OS)o] *.a [$(OS)].out *.cgo[12].go *.cgo[34].c *.so _obj _test _testmain.go $(CLEANFILES)
|
||||
rm -rf *.[$(OS)o] *.a [$(OS)].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go *.so _obj _test _testmain.go $(CLEANFILES)
|
||||
|
||||
test:
|
||||
$(QUOTED_GOBIN)/gotest
|
||||
@ -91,32 +94,42 @@ dir:
|
||||
|
||||
# To use cgo in a Go package, add a line
|
||||
#
|
||||
# CGOFILES=x.go
|
||||
# CGOFILES=x.go y.go
|
||||
#
|
||||
# to the main Makefile. This signals that cgo should process x.go.
|
||||
# to the main Makefile. This signals that cgo should process x.go
|
||||
# and y.go when building the package.
|
||||
# There are two optional variables to set, CGO_CFLAGS and CGO_LDFLAGS,
|
||||
# which specify compiler and linker flags to use when compiling
|
||||
# (using gcc) the C support for x.go.
|
||||
# (using gcc) the C support for x.go and y.go.
|
||||
|
||||
# Cgo translates each x.go file listed in $(CGOFILES) into
|
||||
# Cgo translates each x.go file listed in $(CGOFILES) into a basic
|
||||
# translation of x.go, called x.cgo1.go. Additionally, three other
|
||||
# files are created:
|
||||
#
|
||||
# x.cgo1.go - basic translation of x.go
|
||||
# x.cgo2.go - declarations needed for x.cgo1.go; imports "unsafe"
|
||||
# x.cgo3.c - C trampoline code to be compiled with 6c and linked into the package
|
||||
# x.cgo4.c - C implementations compiled with gcc to create dynamic library
|
||||
# _cgo_gotypes.go - declarations needed for all .go files in the package; imports "unsafe"
|
||||
# _cgo_defun.c - C trampoline code to be compiled with 6c and linked into the package
|
||||
# x.cgo2.c - C implementations compiled with gcc to create a dynamic library
|
||||
#
|
||||
%.cgo1.go %.cgo2.go %.cgo3.c %.cgo4.c: %.go
|
||||
CGOPKGPATH=$(dir) $(QUOTED_GOBIN)/cgo $(CGO_CFLAGS) $*.go
|
||||
|
||||
# The rules above added x.cgo1.go and x.cgo2.go to $(GOFILES),
|
||||
# added x.cgo3.$O to $OFILES, and added the installed copy of
|
||||
# package_x.so (built from x.cgo4.c) to $(INSTALLFILES).
|
||||
_cgo_defun.c _cgo_gotypes.go: $(CGOFILES)
|
||||
CGOPKGPATH=$(dir) $(QUOTED_GOBIN)/cgo $(CGO_CFLAGS) $(CGOFILES)
|
||||
|
||||
# Ugly but necessary
|
||||
%.cgo1.go: _cgo_defun.c _cgo_gotypes.go
|
||||
@true
|
||||
|
||||
%.cgo2.c: _cgo_defun.c _cgo_gotypes.go
|
||||
@true
|
||||
|
||||
%.cgo2.o: %.cgo2.c
|
||||
gcc $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $*.cgo2.c
|
||||
|
||||
# The rules above added x.cgo1.go and _cgo_gotypes.go to $(GOFILES),
|
||||
# added _cgo_defun.$O to $OFILES, and added the installed copy of
|
||||
# package_x.so (built from x.cgo2.c) to $(INSTALLFILES).
|
||||
|
||||
# Compile x.cgo3.c with 6c; needs access to the runtime headers.
|
||||
RUNTIME_CFLAGS_amd64=-D_64BIT
|
||||
RUNTIME_CFLAGS=-I"$(GOROOT)/src/pkg/runtime" $(RUNTIME_CFLAGS_$(GOARCH))
|
||||
%.cgo3.$O: %.cgo3.c
|
||||
$(QUOTED_GOBIN)/$(CC) $(CFLAGS) $(RUNTIME_CFLAGS) $*.cgo3.c
|
||||
|
||||
# Have to run gcc with the right size argument on hybrid 32/64 machines.
|
||||
_CGO_CFLAGS_386=-m32
|
||||
@ -127,15 +140,17 @@ _CGO_LDFLAGS_darwin=-dynamiclib -Wl,-undefined,dynamic_lookup
|
||||
|
||||
|
||||
# Compile x.cgo4.c with gcc to make package_x.so.
|
||||
%.cgo4.o: %.cgo4.c
|
||||
gcc $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $*.cgo4.c
|
||||
|
||||
$(elem)_%.so: %.cgo4.o
|
||||
gcc $(_CGO_CFLAGS_$(GOARCH)) -o $@ $*.cgo4.o $(CGO_LDFLAGS) $(_CGO_LDFLAGS_$(GOOS))
|
||||
# Compile _cgo_defun.c with 6c; needs access to the runtime headers.
|
||||
_cgo_defun.$O: _cgo_defun.c
|
||||
$(QUOTED_GOBIN)/$(CC) $(CFLAGS) $(RUNTIME_CFLAGS) _cgo_defun.c
|
||||
|
||||
$(pkgdir)/$(dir)/$(elem)_%.so: $(elem)_%.so
|
||||
_cgo_.so: $(GCC_OFILES)
|
||||
gcc $(_CGO_CFLAGS_$(GOARCH)) -o $@ $(GCC_OFILES) $(CGO_LDFLAGS) $(_CGO_LDFLAGS_$(GOOS))
|
||||
|
||||
$(pkgdir)/$(dir)/$(TARG).so: _cgo_.so
|
||||
@test -d $(QUOTED_GOROOT/pkg && mkdir -p $(pkgdir)/$(dir)
|
||||
cp $(elem)_$*.so "$@"
|
||||
cp _cgo_.so "$@"
|
||||
|
||||
# Generic build rules.
|
||||
# These come last so that the rules above can override them
|
||||
|
@ -38,6 +38,7 @@ type Prog struct {
|
||||
Enumdef map[string]int64
|
||||
PtrSize int64
|
||||
GccOptions []string
|
||||
OutDefs map[string]bool
|
||||
}
|
||||
|
||||
// A Type collects information about a type in both the C and Go worlds.
|
||||
@ -56,8 +57,7 @@ type FuncType struct {
|
||||
Go *ast.FuncType
|
||||
}
|
||||
|
||||
func openProg(name string) *Prog {
|
||||
p := new(Prog)
|
||||
func openProg(name string, p *Prog) {
|
||||
var err os.Error
|
||||
p.AST, err = parser.ParsePkgFile("", name, parser.ParseComments)
|
||||
if err != nil {
|
||||
@ -120,7 +120,6 @@ func openProg(name string) *Prog {
|
||||
// Accumulate pointers to uses of C.x.
|
||||
p.Crefs = make([]*Cref, 0, 8)
|
||||
walk(p.AST, p, "prog")
|
||||
return p
|
||||
}
|
||||
|
||||
func walk(x interface{}, p *Prog, context string) {
|
||||
|
@ -14,9 +14,10 @@ import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func usage() { fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go\n") }
|
||||
func usage() { fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") }
|
||||
|
||||
var ptrSizeMap = map[string]int64{
|
||||
"386": 4,
|
||||
@ -40,8 +41,19 @@ func main() {
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
gccOptions := args[1 : len(args)-1]
|
||||
input := args[len(args)-1]
|
||||
|
||||
// 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") {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
i += 1
|
||||
|
||||
gccOptions, goFiles := args[1:i], args[i:]
|
||||
|
||||
arch := os.Getenv("GOARCH")
|
||||
if arch == "" {
|
||||
@ -57,22 +69,26 @@ func main() {
|
||||
os.Setenv("LC_ALL", "C")
|
||||
os.Setenv("LC_CTYPE", "C")
|
||||
|
||||
p := openProg(input)
|
||||
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.OutDefs = 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.PtrSize = ptrSize
|
||||
p.Preamble = p.Preamble + "\n" + builtinProlog
|
||||
p.GccOptions = gccOptions
|
||||
p.loadDebugInfo()
|
||||
p.Vardef = make(map[string]*Type)
|
||||
p.Funcdef = make(map[string]*FuncType)
|
||||
p.Enumdef = make(map[string]int64)
|
||||
|
||||
for _, cref := range p.Crefs {
|
||||
switch cref.Context {
|
||||
case "call":
|
||||
@ -112,4 +128,7 @@ func main() {
|
||||
|
||||
p.PackagePath = os.Getenv("CGOPKGPATH") + "/" + p.Package
|
||||
p.writeOutput(input)
|
||||
}
|
||||
|
||||
p.writeDefs()
|
||||
}
|
||||
|
@ -20,24 +20,13 @@ func creat(name string) *os.File {
|
||||
return f
|
||||
}
|
||||
|
||||
// writeOutput creates output files to be compiled by 6g, 6c, and gcc.
|
||||
// writeDefs creates output files to be compiled by 6g, 6c, and gcc.
|
||||
// (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.)
|
||||
func (p *Prog) writeOutput(srcfile string) {
|
||||
func (p *Prog) writeDefs() {
|
||||
pkgroot := os.Getenv("GOROOT") + "/pkg/" + os.Getenv("GOOS") + "_" + os.Getenv("GOARCH")
|
||||
|
||||
base := srcfile
|
||||
if strings.HasSuffix(base, ".go") {
|
||||
base = base[0 : len(base)-3]
|
||||
}
|
||||
fgo1 := creat(base + ".cgo1.go")
|
||||
fgo2 := creat(base + ".cgo2.go")
|
||||
fc := creat(base + ".cgo3.c")
|
||||
fgcc := creat(base + ".cgo4.c")
|
||||
|
||||
// Write Go output: Go input with rewrites of C.xxx to _C_xxx.
|
||||
fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n")
|
||||
fmt.Fprintf(fgo1, "//line %s:1\n", srcfile)
|
||||
printer.Fprint(fgo1, p.AST)
|
||||
fgo2 := creat("_cgo_gotypes.go")
|
||||
fc := creat("_cgo_defun.c")
|
||||
|
||||
// Write second Go output: definitions of _C_xxx.
|
||||
// In a separate file so that the import of "unsafe" does not
|
||||
@ -54,15 +43,10 @@ func (p *Prog) writeOutput(srcfile string) {
|
||||
}
|
||||
fmt.Fprintf(fgo2, "type _C_void [0]byte\n")
|
||||
|
||||
// While we process the vars and funcs, also write 6c and gcc output.
|
||||
// Gcc output starts with the preamble.
|
||||
fmt.Fprintf(fgcc, "%s\n", p.Preamble)
|
||||
fmt.Fprintf(fgcc, "%s\n", gccProlog)
|
||||
|
||||
fmt.Fprintf(fc, cProlog, pkgroot, pkgroot, pkgroot, pkgroot, p.Package, p.Package)
|
||||
|
||||
for name, def := range p.Vardef {
|
||||
fmt.Fprintf(fc, "#pragma dynld %s·_C_%s %s \"%s/%s_%s.so\"\n", p.Package, name, name, pkgroot, p.PackagePath, base)
|
||||
fmt.Fprintf(fc, "#pragma dynld %s·_C_%s %s \"%s/%s.so\"\n", p.Package, name, name, pkgroot, p.PackagePath)
|
||||
fmt.Fprintf(fgo2, "var _C_%s ", name)
|
||||
printer.Fprint(fgo2, &ast.StarExpr{X: def.Go})
|
||||
fmt.Fprintf(fgo2, "\n")
|
||||
@ -137,7 +121,7 @@ func (p *Prog) writeOutput(srcfile string) {
|
||||
|
||||
// C wrapper calls into gcc, passing a pointer to the argument frame.
|
||||
// Also emit #pragma to get a pointer to the gcc wrapper.
|
||||
fmt.Fprintf(fc, "#pragma dynld _cgo_%s _cgo_%s \"%s/%s_%s.so\"\n", name, name, pkgroot, p.PackagePath, base)
|
||||
fmt.Fprintf(fc, "#pragma dynld _cgo_%s _cgo_%s \"%s/%s.so\"\n", name, name, pkgroot, p.PackagePath)
|
||||
fmt.Fprintf(fc, "void (*_cgo_%s)(void*);\n", name)
|
||||
fmt.Fprintf(fc, "\n")
|
||||
fmt.Fprintf(fc, "void\n")
|
||||
@ -146,6 +130,86 @@ func (p *Prog) writeOutput(srcfile string) {
|
||||
fmt.Fprintf(fc, "\tcgocall(_cgo_%s, &p);\n", name)
|
||||
fmt.Fprintf(fc, "}\n")
|
||||
fmt.Fprintf(fc, "\n")
|
||||
}
|
||||
|
||||
fgo2.Close()
|
||||
fc.Close()
|
||||
}
|
||||
|
||||
// writeOutput creates stubs for a specific source file to be compiled by 6g
|
||||
// (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.)
|
||||
func (p *Prog) writeOutput(srcfile string) {
|
||||
base := srcfile
|
||||
if strings.HasSuffix(base, ".go") {
|
||||
base = base[0 : len(base)-3]
|
||||
}
|
||||
fgo1 := creat(base + ".cgo1.go")
|
||||
fgcc := creat(base + ".cgo2.c")
|
||||
|
||||
// Write Go output: Go input with rewrites of C.xxx to _C_xxx.
|
||||
fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n")
|
||||
fmt.Fprintf(fgo1, "//line %s:1\n", srcfile)
|
||||
printer.Fprint(fgo1, p.AST)
|
||||
|
||||
// While we process the vars and funcs, also write 6c and gcc output.
|
||||
// Gcc output starts with the preamble.
|
||||
fmt.Fprintf(fgcc, "%s\n", p.Preamble)
|
||||
fmt.Fprintf(fgcc, "%s\n", gccProlog)
|
||||
|
||||
for name, def := range p.Funcdef {
|
||||
_, ok := p.OutDefs[name]
|
||||
if name == "CString" || name == "GoString" || ok {
|
||||
// The builtins are already defined in the C prolog, and we don't
|
||||
// want to duplicate function definitions we've already done.
|
||||
continue
|
||||
}
|
||||
p.OutDefs[name] = true
|
||||
|
||||
// Construct a gcc struct matching the 6c argument frame.
|
||||
// Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes.
|
||||
// These assumptions are checked by the gccProlog.
|
||||
// Also assumes that 6c convention is to word-align the
|
||||
// input and output parameters.
|
||||
structType := "struct {\n"
|
||||
off := int64(0)
|
||||
npad := 0
|
||||
for i, t := range def.Params {
|
||||
if off%t.Align != 0 {
|
||||
pad := t.Align - off%t.Align
|
||||
structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad)
|
||||
off += pad
|
||||
npad++
|
||||
}
|
||||
structType += fmt.Sprintf("\t\t%s p%d;\n", t.C, i)
|
||||
off += t.Size
|
||||
}
|
||||
if off%p.PtrSize != 0 {
|
||||
pad := p.PtrSize - off%p.PtrSize
|
||||
structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad)
|
||||
off += pad
|
||||
npad++
|
||||
}
|
||||
if t := def.Result; t != nil {
|
||||
if off%t.Align != 0 {
|
||||
pad := t.Align - off%t.Align
|
||||
structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad)
|
||||
off += pad
|
||||
npad++
|
||||
}
|
||||
structType += fmt.Sprintf("\t\t%s r;\n", t.C)
|
||||
off += t.Size
|
||||
}
|
||||
if off%p.PtrSize != 0 {
|
||||
pad := p.PtrSize - off%p.PtrSize
|
||||
structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad)
|
||||
off += pad
|
||||
npad++
|
||||
}
|
||||
if len(def.Params) == 0 && def.Result == nil {
|
||||
structType += "\t\tchar unused;\n" // avoid empty struct
|
||||
off++
|
||||
}
|
||||
structType += "\t}"
|
||||
|
||||
// Gcc wrapper unpacks the C argument struct
|
||||
// and calls the actual C function.
|
||||
@ -170,8 +234,6 @@ func (p *Prog) writeOutput(srcfile string) {
|
||||
}
|
||||
|
||||
fgo1.Close()
|
||||
fgo2.Close()
|
||||
fc.Close()
|
||||
fgcc.Close()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user