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, ","))
}
}
- 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.
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
}
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
}
}
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:
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 {
// 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)
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...)
// 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")
}
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...)
// 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.
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
}
}
// 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 {
}
base := file[:len(file)-n]
goFile := base + ".go"
- cBase := base + "_gc."
gccBase := base + "_wrap."
gccExt := "c"
if cxx {
// swig
args := []string{
"-go",
+ "-cgo",
"-intgosize", intgosize,
"-module", base,
"-o", obj + gccBase + gccExt,
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 "", "", "", 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 "", "", errPrintedOutput
}
+ return "", "", err
}
- return obj + goFile, cObj, gccObj, nil
+ return obj + goFile, obj + gccBase + gccExt, nil
}
// An actionQueue is a priority queue of actions.