cmd/go: use new SWIG -cgo option

This fixes SWIG to work again.  It requires SWIG 3.0.6 or later.
Earlier versions of SWIG will not work because they generate a .c file
to be compiled by [568]c, which no longer exist.  As of SWIG 3.0.6
SWIG supports a -cgo option that tells it to generate files that
import "C" and can be used with the cgo tool.  With luck this will
means that future versions of SWIG will not require changes for future
versions of Go.

Change-Id: Iad7beb196ba9dcd3e3f684196d50e5d51ed98204
Reviewed-on: https://go-review.googlesource.com/6851
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Ian Lance Taylor 2015-03-04 15:43:43 -08:00
parent f3d3328988
commit ddf958d468

View file

@ -811,9 +811,7 @@ func hasString(strings []string, s string) bool {
func (b *builder) build(a *action) (err error) {
// Return an error if the package has CXX files but it's not using
// cgo nor SWIG, since the CXX files can only be processed by cgo
// and SWIG (it's possible to have packages with C files without
// using cgo, they will get compiled with the plan9 C compiler and
// linked with the rest of the package).
// and SWIG.
if len(a.p.CXXFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() {
return fmt.Errorf("can't build package %s because it contains C++ files (%s) but it's not using cgo nor SWIG",
a.p.ImportPath, strings.Join(a.p.CXXFiles, ","))
@ -861,19 +859,35 @@ func (b *builder) build(a *action) (err error) {
}
}
var gofiles, cfiles, sfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string
var gofiles, cgofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string
gofiles = append(gofiles, a.p.GoFiles...)
cgofiles = append(cgofiles, a.p.CgoFiles...)
cfiles = append(cfiles, a.p.CFiles...)
sfiles = append(sfiles, a.p.SFiles...)
cxxfiles = append(cxxfiles, a.p.CXXFiles...)
if a.p.usesCgo() || a.p.usesSwig() {
if pcCFLAGS, pcLDFLAGS, err = b.getPkgConfigFlags(a.p); err != nil {
return
}
}
// Run SWIG on each .swig and .swigcxx file.
// Each run will generate two files, a .go file and a .c or .cxx file.
// The .go file will use import "C" and is to be processed by cgo.
if a.p.usesSwig() {
outGo, outC, outCXX, err := b.swig(a.p, obj, pcCFLAGS)
if err != nil {
return err
}
cgofiles = append(cgofiles, outGo...)
cfiles = append(cfiles, outC...)
cxxfiles = append(cxxfiles, outCXX...)
}
// Run cgo.
if a.p.usesCgo() {
if a.p.usesCgo() || a.p.usesSwig() {
// In a package using cgo, cgo compiles the C, C++ and assembly files with gcc.
// There is one exception: runtime/cgo's job is to bridge the
// cgo and non-cgo worlds, so it necessarily has files in both.
@ -902,31 +916,7 @@ func (b *builder) build(a *action) (err error) {
if a.cgo != nil && a.cgo.target != "" {
cgoExe = a.cgo.target
}
outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, gccfiles, a.p.CXXFiles, a.p.MFiles)
if err != nil {
return err
}
cgoObjects = append(cgoObjects, outObj...)
gofiles = append(gofiles, outGo...)
}
// Run SWIG.
if a.p.usesSwig() {
// In a package using SWIG, any .c or .s files are
// compiled with gcc.
gccfiles := append(cfiles, sfiles...)
cxxfiles, mfiles := a.p.CXXFiles, a.p.MFiles
cfiles = nil
sfiles = nil
// Don't build c/c++ files twice if cgo is enabled (mainly for pkg-config).
if a.p.usesCgo() {
cxxfiles = nil
gccfiles = nil
mfiles = nil
}
outGo, outObj, err := b.swig(a.p, obj, pcCFLAGS, gccfiles, cxxfiles, mfiles)
outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles)
if err != nil {
return err
}
@ -1839,9 +1829,9 @@ func packInternal(b *builder, afile string, ofiles []string) error {
func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
importArgs := b.includeArgs("-L", allactions)
cxx := len(p.CXXFiles) > 0
cxx := len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0
for _, a := range allactions {
if a.p != nil && len(a.p.CXXFiles) > 0 {
if a.p != nil && (len(a.p.CXXFiles) > 0 || len(a.p.SwigCXXFiles) > 0) {
cxx = true
}
}
@ -1972,7 +1962,7 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []
ldflags := b.gccArchArgs()
cgoldflags := []string{}
usesCgo := false
cxx := len(p.CXXFiles) > 0
cxx := len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0
objc := len(p.MFiles) > 0
// For a given package import path:
@ -2006,7 +1996,7 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []
if a.p.usesSwig() {
usesCgo = true
}
if len(a.p.CXXFiles) > 0 {
if len(a.p.CXXFiles) > 0 || len(a.p.SwigCXXFiles) > 0 {
cxx = true
}
if len(a.p.MFiles) > 0 {
@ -2116,7 +2106,7 @@ func (b *builder) ccompile(p *Package, out string, flags []string, file string,
// gccld runs the gcc linker to create an executable from a set of object files.
func (b *builder) gccld(p *Package, out string, flags []string, obj []string) error {
var cmd []string
if len(p.CXXFiles) > 0 {
if len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0 {
cmd = b.gxxCmd(p.Dir)
} else {
cmd = b.gccCmd(p.Dir)
@ -2228,7 +2218,7 @@ var (
cgoLibGccFileOnce sync.Once
)
func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true)
_, cgoexeCFLAGS, _, _ := b.cflags(p, false)
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
@ -2245,7 +2235,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi
// TODO: CGOPKGPATH, CGO_FLAGS?
gofiles := []string{obj + "_cgo_gotypes.go"}
cfiles := []string{"_cgo_main.c", "_cgo_export.c"}
for _, fn := range p.CgoFiles {
for _, fn := range cgofiles {
f := cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
gofiles = append(gofiles, obj+f+"cgo1.go")
cfiles = append(cfiles, f+"cgo2.c")
@ -2281,7 +2271,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi
}
objExt = "o"
}
if err := b.run(p.Dir, p.ImportPath, cgoenv, buildToolExec, cgoExe, "-objdir", obj, cgoflags, "--", cgoCPPFLAGS, cgoexeCFLAGS, p.CgoFiles); err != nil {
if err := b.run(p.Dir, p.ImportPath, cgoenv, buildToolExec, cgoExe, "-objdir", obj, cgoflags, "--", cgoCPPFLAGS, cgoexeCFLAGS, cgofiles); err != nil {
return nil, nil, err
}
outGo = append(outGo, gofiles...)
@ -2458,77 +2448,41 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi
// Run SWIG on all SWIG input files.
// TODO: Don't build a shared library, once SWIG emits the necessary
// pragmas for external linking.
func (b *builder) swig(p *Package, obj string, pcCFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true)
cflags := stringList(cgoCPPFLAGS, cgoCFLAGS)
cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS)
for _, file := range gccfiles {
ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o"
if err := b.gcc(p, ofile, cflags, file); err != nil {
return nil, nil, err
}
outObj = append(outObj, ofile)
}
for _, file := range gxxfiles {
// Append .o to the file, just in case the pkg has file.c and file.cpp
ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
if err := b.gxx(p, ofile, cxxflags, file); err != nil {
return nil, nil, err
}
outObj = append(outObj, ofile)
}
for _, file := range mfiles {
// Append .o to the file, just in case the pkg has file.c and file.cpp
ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
if err := b.gcc(p, ofile, cflags, file); err != nil {
return nil, nil, err
}
outObj = append(outObj, ofile)
}
func (b *builder) swig(p *Package, obj string, pcCFLAGS []string) (outGo, outC, outCXX []string, err error) {
if err := b.swigVersionCheck(); err != nil {
return nil, nil, err
return nil, nil, nil, err
}
intgosize, err := b.swigIntSize(obj)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
for _, f := range p.SwigFiles {
goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, false, intgosize)
goFile, cFile, err := b.swigOne(p, f, obj, pcCFLAGS, false, intgosize)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
if goFile != "" {
outGo = append(outGo, goFile)
}
if objFile != "" {
outObj = append(outObj, objFile)
}
if gccObjFile != "" {
outObj = append(outObj, gccObjFile)
if cFile != "" {
outC = append(outC, cFile)
}
}
for _, f := range p.SwigCXXFiles {
goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, true, intgosize)
goFile, cxxFile, err := b.swigOne(p, f, obj, pcCFLAGS, true, intgosize)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
if goFile != "" {
outGo = append(outGo, goFile)
}
if objFile != "" {
outObj = append(outObj, objFile)
}
if gccObjFile != "" {
outObj = append(outObj, gccObjFile)
if cxxFile != "" {
outCXX = append(outCXX, cxxFile)
}
}
return outGo, outObj, nil
return outGo, outC, outCXX, nil
}
// Make sure SWIG is new enough.
@ -2542,20 +2496,51 @@ func (b *builder) swigDoVersionCheck() error {
if err != nil {
return err
}
re := regexp.MustCompile(`[vV]ersion +([\d])`)
re := regexp.MustCompile(`[vV]ersion +([\d]+)([.][\d]+)?([.][\d]+)?`)
matches := re.FindSubmatch(out)
if matches == nil {
// Can't find version number; hope for the best.
return nil
}
major, err := strconv.Atoi(string(matches[1]))
if err != nil {
// Can't find version number; hope for the best.
return nil
}
const errmsg = "must have SWIG version >= 3.0.6"
if major < 3 {
return errors.New("must have SWIG version >= 3.0")
return errors.New(errmsg)
}
if major > 3 {
// 4.0 or later
return nil
}
// We have SWIG version 3.x.
if len(matches[2]) > 0 {
minor, err := strconv.Atoi(string(matches[2][1:]))
if err != nil {
return nil
}
if minor > 0 {
// 3.1 or later
return nil
}
}
// We have SWIG version 3.0.x.
if len(matches[3]) > 0 {
patch, err := strconv.Atoi(string(matches[3][1:]))
if err != nil {
return nil
}
if patch < 6 {
// Before 3.0.6.
return errors.New(errmsg)
}
}
return nil
}
@ -2593,7 +2578,7 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) {
}
// Run SWIG on one SWIG input file.
func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) {
func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true)
var cflags []string
if cxx {
@ -2608,7 +2593,6 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b
}
base := file[:len(file)-n]
goFile := base + ".go"
cBase := base + "_gc."
gccBase := base + "_wrap."
gccExt := "c"
if cxx {
@ -2620,6 +2604,7 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b
// swig
args := []string{
"-go",
"-cgo",
"-intgosize", intgosize,
"-module", base,
"-o", obj + gccBase + gccExt,
@ -2644,37 +2629,16 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b
if out, err := b.runOut(p.Dir, p.ImportPath, nil, "swig", args, file); err != nil {
if len(out) > 0 {
if bytes.Contains(out, []byte("Unrecognized option -intgosize")) {
return "", "", "", errors.New("must have SWIG version >= 3.0")
if bytes.Contains(out, []byte("-intgosize")) || bytes.Contains(out, []byte("-cgo")) {
return "", "", errors.New("must have SWIG version >= 3.0.6")
}
b.showOutput(p.Dir, p.ImportPath, b.processOutput(out))
return "", "", "", errPrintedOutput
return "", "", errPrintedOutput
}
return "", "", "", err
return "", "", err
}
var cObj string
if !gccgo {
// cc
cObj = obj + cBase + archChar
if err := buildToolchain.cc(b, p, obj, cObj, obj+cBase+"c"); err != nil {
return "", "", "", err
}
}
// gcc
gccObj := obj + gccBase + "o"
if !cxx {
if err := b.gcc(p, gccObj, cflags, obj+gccBase+gccExt); err != nil {
return "", "", "", err
}
} else {
if err := b.gxx(p, gccObj, cflags, obj+gccBase+gccExt); err != nil {
return "", "", "", err
}
}
return obj + goFile, cObj, gccObj, nil
return obj + goFile, obj + gccBase + gccExt, nil
}
// An actionQueue is a priority queue of actions.