Linking static c++ library with Go using SWIG

1,999 views
Skip to first unread message

Chun Zhang

unread,
Feb 28, 2017, 2:06:20 PM2/28/17
to golang-nuts
Hi, All, 

I have googled quite a bit about this issue, there are some tutorials online. But most of them targeted either at older go releases or C instead of C++. 

Can somebody please help me to figure out how to solve the following issues?

I have to use a static library, wrote in C++ in a go project. I have the libcolor.a and the COLOR.h header file, the libcolor.a relies on some other libraries, such as libboost_system.a, libxml2.a etc to build. 

I wrote a libcolor.swigcxx file as follows:
---------------------------
%module libcoror

%{
#include "include/COLOR.h"
#include <stddef.h>
#include <vector>

/* This is where we initialize any global parameters that are not search-thread specific */
extern void COLOR_init_global_config(int argc, char *argv[]);  // from the COLOR.h file, which is one of the APIs I would like to use

%}

#include "include/COLOR.h"
extern void COLOR_init_global_config(int argc, char *argv[]);
---------------------------

An empty libcolor.go file with the following lines was manually created

---------------------------
package libcolor

// #cgo CFLAGS: -I .
// #cgo CXXFLAGS: -std=c++11 <--- this does not seem to work
// #cgo LDFLAGS: -L${SRCDIR}/lib/ -lCOLOR.a -lz <--- this is placed at the correct place
---------------------------

When I tried to build this using "go build -x" CLI, I hit the following error:

WORK=/tmp/go-build797493895
mkdir -p $WORK/klow/libcolor/_obj/
mkdir -p $WORK/klow/
swig -version
cd $WORK
/usr/local/go/pkg/tool/linux_amd64/compile -o ./klow/libcolor/_obj/_go_.o -trimpath . -p main -complete -buildid 73a7f9534f74346db4b3e0f48875da9dbf8bc2fd -D _$WORK ./swig_intsize.go
cd /home/chzhang/go/src/klow/libcolor
swig -go -cgo -intgosize 64 -module libcolor -o $WORK/klow/libcolor/_obj/libcolor_wrap.cxx -outdir $WORK/klow/libcolor/_obj/ -c++ libcolor.swigcxx
CGO_LDFLAGS="-g" "-O2" /usr/local/go/pkg/tool/linux_amd64/cgo -objdir $WORK/klow/libcolor/_obj/ -importpath klow/libcolor -- -I $WORK/klow/libcolor/_obj/ $WORK/klow/libcolor/_obj/libcolor.go
cd $WORK
gcc -fdebug-prefix-map=a=b -c trivial.c
gcc -gno-record-gcc-switches -c trivial.c
cd /home/chzhang/go/src/klow/libcolor
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK=/tmp/go-build -gno-record-gcc-switches -I $WORK/klow/libcolor/_obj/ -g -O2 -o $WORK/klow/libcolor/_obj/_cgo_main.o -c $WORK/klow/libcolor/_obj/_cgo_main.c
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK=/tmp/go-build -gno-record-gcc-switches -I $WORK/klow/libcolor/_obj/ -g -O2 -o $WORK/klow/libcolor/_obj/_cgo_export.o -c $WORK/klow/libcolor/_obj/_cgo_export.c
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK=/tmp/go-build -gno-record-gcc-switches -I $WORK/klow/libcolor/_obj/ -g -O2 -o $WORK/klow/libcolor/_obj/_tmp_go-build797493895_klow_libcolor__obj_libcolor.cgo2.o -c $WORK/klow/libcolor/_obj/_tmp_go-build797493895_klow_libcolor__obj_libcolor.cgo2.c
g++ -I . -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK=/tmp/go-build -gno-record-gcc-switches -I $WORK/klow/libcolor/_obj/ -g -O2 -o $WORK/klow/libcolor/_obj/_tmp_go-build797493895_klow_libcolor__obj_libcolor_wrap.cxx.o -c $WORK/klow/libcolor/_obj/libcolor_wrap.cxx
# klow/libcolor
In file included from $WORK/klow/libcolor/_obj/libcolor_wrap.cxx:243:0:
./include/COLOR.h:13:43: warning: defaulted and deleted functions only available with -std=c++11 or -std=gnu++11
   PerSessionData(const PerSessionData &d)=default;
                                           ^
./include/COLOR.h:14:53: warning: defaulted and deleted functions only available with -std=c++11 or -std=gnu++11
   PerSessionData& operator=(const PerSessionData&d)=default;
                                                     ^
g++ -I . -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK=/tmp/go-build -gno-record-gcc-switches -o $WORK/klow/libcolor/_obj/_cgo_.o $WORK/klow/libcolor/_obj/_cgo_main.o $WORK/klow/libcolor/_obj/_cgo_export.o $WORK/klow/libcolor/_obj/_tmp_go-build797493895_klow_libcolor__obj_libcolor.cgo2.o $WORK/klow/libcolor/_obj/_tmp_go-build797493895_klow_libcolor__obj_libcolor_wrap.cxx.o -g -O2
# klow/libcolor
/tmp/go-build797493895/klow/libcolor/_obj/_tmp_go-build797493895_klow_libcolor__obj_libcolor_wrap.cxx.o: In function `_wrap_COLOR_init_global_config_libcolor_3cea422eb6211fe0':
/tmp/go-build/klow/libcolor/_obj/libcolor_wrap.cxx:285: undefined reference to `COLOR_init_global_config(int, char**)'
collect2: error: ld returned 1 exit status


Looks like there are two errors:
1, the C++11 warning
2, the linker can't find the function COLOR_init_global_config in the static library. That means the compiler directives I defined in libcolor.go was not successfully passed to the compiler. From the log, looks like another libcolor.go was generated in the $WORK directory by swig. The one I manually created was not used at all. 

Can somebody please help me to figure out what I need to do to get this compiled? The tutorial on swig.org does not seem to help. I tried to manually build as in 
But the 6c etc go tools does not exist anymore. 


Thanks,
Chun

--------------------------------

This is my setup
go version go1.7.1 linux/amd64
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/chzhang/go"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build155119108=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"


Ian Lance Taylor

unread,
Feb 28, 2017, 2:44:21 PM2/28/17
to Chun Zhang, golang-nuts
On Tue, Feb 28, 2017 at 11:04 AM, Chun Zhang <czh...@gmail.com> wrote:
>
> An empty libcolor.go file with the following lines was manually created
>
> ---------------------------
> package libcolor
>
> // #cgo CFLAGS: -I .
> // #cgo CXXFLAGS: -std=c++11 <--- this does not seem to work
> // #cgo LDFLAGS: -L${SRCDIR}/lib/ -lCOLOR.a -lz <--- this is placed at the
> correct place

Note that this is only effective if those comments appear directly
before an `import "C"` line.

Ian

Chun Zhang

unread,
Feb 28, 2017, 2:49:19 PM2/28/17
to golang-nuts, czh...@gmail.com
Thank you for your reply! I read a few posts between you and Stephen before posting this. 

Sorry, it was a bad copy and paste. 

I did have the import "C" line in the file. But I have the same error. 

I can "go build libcolor.go" itself with no error. But that does not seem to build the whole library.

Best Regards,
Chun

Chun Zhang

unread,
Mar 1, 2017, 11:13:10 AM3/1/17
to golang-nuts, czh...@gmail.com
Just wondering, is there any chance that I didn't place some file in the correct place? The directory tree is as follows, 
├── bin
├── example
│   └── COLOR_test
│       ├── COLOR_test.cpp
│       └── SessionKey.hpp
├── include
│   └── COLOR.h
├── lib
│   ├── libboost_filesystem.a
│   ├── libCOLOR.a
│   └── libxml2.a
├── libcolor
├── libcolor.go
├── libcolor.swigcxx
├── README

When I issue `go build -x`, go and swig insists to generate another libcolor.go file in the tmp $WORK directory, thus none of the directives defined in above libcolor.go file is picked up. 

Any help will be appreciated. 

Thanks,
Chun

Ian Lance Taylor

unread,
Mar 1, 2017, 1:01:45 PM3/1/17
to Chun Zhang, golang-nuts
On Wed, Mar 1, 2017 at 8:13 AM, Chun Zhang <czh...@gmail.com> wrote:
> Just wondering, is there any chance that I didn't place some file in the
> correct place? The directory tree is as follows,
> ├── bin
> ├── example
> │ └── COLOR_test
> │ ├── COLOR_test.cpp
> │ └── SessionKey.hpp
> ├── include
> │ └── COLOR.h
> ├── lib
> │ ├── libboost_filesystem.a
> │ ├── libCOLOR.a
> │ └── libxml2.a
> ├── libcolor
> ├── libcolor.go
> ├── libcolor.swigcxx
> ├── README
>
> When I issue `go build -x`, go and swig insists to generate another
> libcolor.go file in the tmp $WORK directory, thus none of the directives
> defined in above libcolor.go file is picked up.

It's possible that there is a bug such that it won't work to have both
x.swigcxx and x.go in the same package. Maybe you should rename
libcolor.go.

Ian

Chun Zhang

unread,
Mar 1, 2017, 1:16:07 PM3/1/17
to golang-nuts, czh...@gmail.com
Thanks Ian! But that does not seem to be the issue, I changed libcolor.go to color.go and still get the same error.

However, I did made tiny progress, I can specifically pass the parameters in the CLI and get it build, for example

CGO_CXXFLAGS="-I$GOPATH/src/kflow/libcolor/include" CGO_LDFLAGS="-L$GOPATH/src/kflow/libcolor/lib -l:libCOLOR.a -l:libboost_system.a -l:libboost_regex.a -l:libboost_program_options.a -l:libboost_filesystem.a -l:libxml2.a -lz" go build -x

but, the installed libcolor.a in the pkg directory looks suspiciously small, only 55k, while libCOLOR.a itself is roughly 300MB. I wrote a simple main file, the application is around 140MB. The numbers do not seem to agree with each other. 

I do notice though, the 
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build471211808=/tmp/go-build -gno-record-gcc-switches"
in the `go env` output has the -fdebug-prefix-map changed each and everytime, is that expected?

go version go1.7.1 linux/amd64

Best Regards,
Chun

Ian Lance Taylor

unread,
Mar 1, 2017, 1:20:16 PM3/1/17
to Chun Zhang, golang-nuts
On Wed, Mar 1, 2017 at 10:16 AM, Chun Zhang <czh...@gmail.com> wrote:
> Thanks Ian! But that does not seem to be the issue, I changed libcolor.go to
> color.go and still get the same error.
>
> However, I did made tiny progress, I can specifically pass the parameters in
> the CLI and get it build, for example
>
> CGO_CXXFLAGS="-I$GOPATH/src/kflow/libcolor/include"
> CGO_LDFLAGS="-L$GOPATH/src/kflow/libcolor/lib -l:libCOLOR.a
> -l:libboost_system.a -l:libboost_regex.a -l:libboost_program_options.a
> -l:libboost_filesystem.a -l:libxml2.a -lz" go build -x
>
> but, the installed libcolor.a in the pkg directory looks suspiciously small,
> only 55k, while libCOLOR.a itself is roughly 300MB. I wrote a simple main
> file, the application is around 140MB. The numbers do not seem to agree with
> each other.

That does not necessarily indicate a problem. When the linker sees a
.a file, it only pulls in referenced symbols, not the entire archive.
So if your program only uses a small part of libCOLOR.a, it would be
natural for the program to be smaller than libCOLOR.a. I don't know
whether there is a problem there or not, but the file size doesn't
necessarily mean that there is.

> I do notice though, the
> GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0
> -fdebug-prefix-map=/tmp/go-build471211808=/tmp/go-build
> -gno-record-gcc-switches"
> in the `go env` output has the -fdebug-prefix-map changed each and
> everytime, is that expected?

Yes. That option is keeping the Go temporary directory out of the
debug info stored in the object file. This helps makes builds
reproducible.

Ian

Chun Zhang

unread,
Mar 1, 2017, 1:52:28 PM3/1/17
to golang-nuts, czh...@gmail.com
Ian, thanks for the explanation!

I tried to split the go/swigcxx code and c++ code to two different directories as the example given by Stephen earlier. That didn't work. No matter what I do, I don't see the flags being passed to the compiler. 

For now, I will simply use the build script and passing all compiler parameters explicitly. But this does not seem to be an optimal solution. If there is anything else that I can try, please let me know. 

Thanks,
Chun

therecipe

unread,
Mar 1, 2017, 1:57:15 PM3/1/17
to golang-nuts
did you try it without the ".a" suffix in the LDFLAGS?

Chun Zhang

unread,
Mar 1, 2017, 2:04:50 PM3/1/17
to golang-nuts
Yes, I tried quite a few varieties, such as:
//#cgo LDFLAGS: $GOPATH/src/kflow/libcolor/lib/libCOLOR with/without .a
//#cgo LDFLAGS: ${SRCDIR}kflow/libcolor/lib/libCOLOR with/without .a
//#cgo LDFLAGS: -L${SRCDIR}kflow/libcolor/lib -l:libCOLOR with/without .a
//#cgo LDFLAGS: -L${SRCDIR}kflow/libcolor/lib -lCOLOR with/without .a

None above works for me so far, they are simply ignored during the building process. I even played with the empty spaces at various places, still no luck :(

Thanks,
Chun

therecipe

unread,
Mar 1, 2017, 2:30:12 PM3/1/17
to golang-nuts
ah okay, I had problems with the ".a" suffix myself, so I though it was worth a shot.

I just know that this
>//#cgo LDFLAGS: $GOPATH/src/kflow/libcolor/lib/libCOLOR 

won't work, as the env variables won't be resolved by go afaik.

maybe test it with an absolute path instead

and/or use "go build -n" to see how go would invoke "link"

and maybe you need to link against "libboost_system" and "libxml2" as well, if "libCOLOR" really depends on it.

Chun Zhang

unread,
Mar 1, 2017, 3:02:25 PM3/1/17
to golang-nuts
Thanks! I didn't write out libboost etc just to make sure that at least one lib is correctly linked, if so, I should see other link errors and I can just keep passing in required libs. This is exactly what happens when I pass CGO_LDFLAGS= explicitly in the CLI. 

However, when using the directives in the go file, the CGO_LDFLAGS= as far as I see in the `go build -x` log never includes any the link directives. 

Regards,
Chun

therecipe

unread,
Mar 1, 2017, 3:50:04 PM3/1/17
to golang-nuts
ah, sorry I didn't properly read the whole thread before posting
if it works for you when you set the flags through the CLI, but not when you put them in a go file, then there is something wrong with how you embedded the flags in you go file.

like Ian said, you need to define the flags in a comment directly before using 
import "C"

so, something like this should work
package main

/*
#cgo LDFLAGS: -L${SRCDIR}kflow/libcolor/lib -lCOLOR
*/
import "C"


Chun Zhang

unread,
Mar 1, 2017, 4:45:31 PM3/1/17
to golang-nuts
Quick update: 

The issue was resolved after a `go clean`...
Now everything build just as if the directives are passed through the CLI. 

I don't know what happened... 

Thank all for your help!
Reply all
Reply to author
Forward
0 new messages