0

What version of Go are you using (go version)?

$ go version
go version go1.20.2 linux/amd64

Project structure:

Directory structure --
example --> main.go
        -->lib
            lib.c
            lib.h

main.go

package main

// #include "lib/lib.h"
// #include <stdio.h>
// #include <stdlib.h>
import "C"
import (
    "fmt"
    "unsafe"
)

func main() {
    cstrin := C.CString("welcome")
    s1 := C.struct_s1{b: cstrin, a: 100}

    C.f32_123(&s1)
    cs := C.GoString(s1.b)
    fmt.Println(cs)
    fmt.Println(s1)
    C.free(unsafe.Pointer(cstrin))
}

lib/lib.c

#include <stdlib.h>
#include <stdio.h>

void printc(char *str, int *t)
{
     str = "Test";
     printf("%d\n", *t);
     *t = 30;
     printf("%s\n", str);
}

void f32_123(struct s1 *s)
{
     printf("%s\n", s->b);
     s->a = 10;
     s->b = "Hello123";
     printf("%d\n", s->a);
     printf("%s\n", s->b);
}

lib/lib.h

struct s1 {
    int a;
    char *b;
};

void printc(char *str, int *t);
void f32_123(struct s1 *s);

Error while compiling

/usr/local/go/pkg/tool/linux_amd64/link: running gcc failed: exit status 1
/usr/bin/ld: /tmp/go-link-3024881602/000001.o: in function _cgo_cf24297edd23_Cfunc_f32_123': /tmp/go-build/cgo-gcc-prolog:49: undefined reference to f32_123'
collect2: error: ld returned 1 exit status

I am expecting the code to compile successfully, but somehow it is not. If I've read the documentation correctly, then I have to keep lib.c and lib.h in the same directory with the main.go file. But I am not sure if this can be achieved or I am doing something wrong.

  • If I keep all the files into same directory example then compile is successful.

  • If I keep lib.c and lib.h into subdir then compile fails

  • If I remove one function f32_123 from main.go then also compile is successful and that is pretty strange and that is the reason opening this bug to get more understanding why compile has not issue with printc function when lib.h and lib.c is in subdir.

double-beep
  • 5,031
  • 17
  • 33
  • 41
unicorn
  • 11
  • 3
  • The `lib.h` file looks to be missing something naming `struct s1`. You might want to debug that by including it in your c file, and trying `gcc -c lib.c`. – Tinkerer Mar 26 '23 at 14:33
  • Actually lib.h file does have struct s1, it is just that it was cut from o/p I pasted above. Here is exact content ``` struct s1 { int a; char *b; }; ``` – unicorn Mar 27 '23 at 06:21
  • How does this get defined in your provided `lib.c` file? Shouldn't it include this `lib.h` file? – Tinkerer Mar 27 '23 at 12:39
  • The source of `lib.c` really doesn't matter, you ned to compile it into a library to link via the header. If you want to specify a relative path for the header, you can use `SRCDIR` https://stackoverflow.com/questions/38202761/cgogolang-error-underfined-reference-to-hello/38204336#38204336 – JimB Mar 27 '23 at 14:21
  • @Tinkerer Here is the content of lib.h. It is defined in lib.h only not in lib.c ``` struct s1 { int a; char *b; }; void printc(char *str, int *t); void f32_123(struct s1 *s); ``` – unicorn Mar 28 '23 at 13:09
  • Right, but when you compile `lib.c`, if it doesn't include `"lib.h"`, how is it supposed to know the definition of `struct s1` for `s->a` etc to make any sense? – Tinkerer Mar 31 '23 at 02:34
  • @Tinkerer It is there as well into lib.c, just that it was not copy/pasted properly here. Sorry for that confusion. – unicorn Apr 03 '23 at 07:30
  • @JimB Can you please give me how should I include $SRCDIR into main.go. As my all the attempts are failing with that as well. – unicorn Apr 03 '23 at 07:31
  • @unicorn: search for SRCDIR in the docs here: https://pkg.go.dev/cmd/cgo – JimB Apr 03 '23 at 17:25
  • @JimB I am not able to understand usage of // #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo in my context. Currently my main.go files header looks like this, can you help me understand how I should use SRCDIR into my context. package main // #include "lib/lib.h" // #include // #include import "C" import ( "fmt" "unsafe" ) – unicorn Apr 06 '23 at 09:03

1 Answers1

0

First, @JimB gave this accepted answer a while back: https://stackoverflow.com/a/28881331/5739452 which says that building an object or library in a sub directory isn't something go build can just do.

Given that, let's say you have this structure:

lib/
lib/lib.c
lib/lib.h
main.go

Here are some simpler files to keep things clear:

/* lib/lib.h */
struct foo {
    int x;
};
void show(struct foo *arg);
/* lib/lib.c */
#include <stdio.h>
#include "lib.h"
void show(struct foo *arg) {
    printf("[%d]\n", arg->x);
}

So, you can get this to build all from go build main.go if you have a main.go like this:

package main

// #cgo CFLAGS: -I${SRCDIR}/lib
// #include "lib.c"
import "C"

func main() {
    x := C.struct_foo{ x: 42 }
    C.show(&x)
}

which works because we actually #include the "C" source code for the library (which implicitly imports the lib/lib.h file).

However, for more complicated libraries, you will likely need to build them as a separate, more normal C tool chain, pre-work build step:

$ cd lib
$ cc -c lib.c
$ ar cr libx.a lib.o
$ cd ..

and then use a different Go file: main2.go:

package main

// #cgo CFLAGS: -I${SRCDIR}/lib
// #cgo LDFLAGS: -L${SRCDIR}/lib -lx
// #include "lib.h"
import "C"

func main() {
    x := C.struct_foo{ x: 42 }
    C.show(&x)
}
Tinkerer
  • 865
  • 7
  • 9
  • Awesome Answer !!! Makes things working as expected. One follow up question, if we change source code file then in approach 2 mentioned by you , we have to again build vs if I use approach 1 then I don't need to worry about compiling c code again. Go will do it for me, right? I ask this question, because I have read on https://pkg.go.dev/cmd/cgo ```Note that changes to files in other directories do not cause the package to be recompiled, so all non-Go source code for the package should be stored in the package directory, not in subdirectories.``` – unicorn Apr 10 '23 at 10:01
  • The [pkg.go.dev/cmd/cgo](https://pkg.go.dev/cmd/cgo) docs are authoritative. You will at least need to delete your compiled program manually to have a shot of the compilation working. But I would recommend you structure your project in such a way that you think of your library as a separate build target. Use something like a `Makefile` to structure the dependencies. – Tinkerer Apr 10 '23 at 13:58