I want to make a simple example of calling C code from Go with CGO. But for some reason I can't achieve desired. Compilation fails with the following error:
go build main.go
# awesomeProject1/print
duplicate symbol '_do_print' in:
$WORK/b002/_x002.o
$WORK/b002/_x003.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The code:
// print/print.c
#include <stdio.h>
void do_print(char * x){
printf("%s", x);
}
// print/print.go
package print
// #include <print.c>
import "C"
func DoPrint() {
C.do_print(C.CString("Hello!"))
}
// main.go
package main
import "awesomeProject1/print"
func main() {
print.DoPrint()
}
If I make do_print
function static it compiles but I wouldn't be able to do that for 3rd party code I want to integrate with later.
Am I missing some essential piece from documentation? Tutorials are all alike and claim to work where my example fails. Please help!
Go version 1.16.4
There are two things going on here:
go build
compiles *.c
in addition to *.go
1#include <print.c>
is exactly equivalent to overwriting the include statement with the contents of print.c
As a result, you are compiling the contents of print.c
twice: once when print.c
is compiled by CC, and once when print.go
is compiled by CGo. Thus, the object files for print.c
and print.go
each contain all of the exported symbols defined in print.c
. So you get two copies of do_print
. Making do_print
static works because a function declared as static
will not be exported by CC.
Including a .c
file (e.g. #include <file.c>
) is pretty much always a bad idea. If you have a legitimate reason to #include
function bodies, the convention is to use a header (.h
) file. This is what C++ templating libraries do (like Boost, IIRC). Because C files normally are not included, and H files normally are included, including a C file subverts expectations and thus is likely to cause confusion and problems.
1 IIRC, Go will compile C files in a package if any of the Go files in the package import "C"
. That is, if no Go files in a package use CGo, Go will ignore C files in that package.