I'm trying to compile this example (which is saved as main.go in current working dir):
package main
import (
"flag"
"log"
"runtime"
"github.com/nats-io/go-nats"
)
// rest of the code
This works:
$ go build ./
But this does not:
$ go tool compile -o main.o main.go
main.go:8:2: can't find import: "github.com/nats-io/go-nats"
Both of examples above were run on the same terminal with same environment variables, so I don't understand why the 2nd one does not work. I've tried -D and -I parameters with various directories as $GOPATH, $GOPATH/src and so on with no success.
I know what it's considered best practice to not use go tool compile and so on, but my goal is to add my go source to existing C++ makefile project and useage of go tool will make it more consistent.
Correct syntax of go tool compile is as follows:
go tool compile -I $GOPATH/pkg/linux_amd64 -o main.o main.go
The problem was that by default, compile performs lookup only in $GOROOT and ignores $GOPATH.
Related
I am writing a generic library in GoLang and want to publish it (like a dynamic library) to be used by other apps written in any language.
If I write this lib in C/C++, I would have generated a .dll or .so file which can be imported and used in any other language. How can I do this in GoLang?
If I just generate a Go executable, can I use it instead of a dynamic library?
You can build a C-shared library in Go, this will produce a regular .dll or .so with exported functions compatible with the C calling convention, so that they can be invoked from other languages.
Compile with go build -buildmode=c-shared.
See go build command - Build modes
For example:
src/go/main.go:
package main
import "C"
import "fmt"
//export helloLib
func helloLib(x C.int) {
fmt.Printf("Hello from Go! x=%d\n", x)
}
func main() {}
src/c/main.c:
void helloLib(int);
int main() {
helloLib(12345);
}
Building and running:
$ go build -buildmode=c-shared -o libmy.so ./src/go/
$ gcc -o test src/c/main.c libmy.so
$ ./test
Hello from Go! x=12345
$
I believe it is possible using cgo:
https://pkg.go.dev/cmd/cgo
It is stated that Go functions can be exported for use by C code.
In my project, I am using callbacks for bi-directional calls from C into go and vice versa using CGO. I resolved the issue of circular dependencies by compiling the C part into a library, then compiling the go part into a library, then a final linker pass puts it all together. This is working fine when not using go modules. Go source files are listed on the command line explicitly. I have been told that as of go 1.12 "this is not the right way to do it".
As the project has grown, I now want to use go modules. Unfortunately, this changes the behaviour of the go compiler. It now wants to resolve external dependencies and implicitly includes them in the output file. Due to the circular dependency, it now always ends up with an undefined reference or multiple definitions. How to resolve circular dependencies when using cgo and go modules "the right way"?
This is a minimal example to illustrate the problem. Remove the file-name "hello.go" from the call to go in the Makefile to see how it falls apart.
This is the error message:
hello.c:3: multiple definition of `c_hello'; $WORK/b001/_cgo_hello.o:/tmp/go-build/hello.c:3: first defined here
Makefile:
libchello.a: Makefile hello.c
gcc -fPIC -c -o chello.o hello.c
ar r libchello.a chello.o
libgohello.a: Makefile hello.go libchello.a
env CGO_LDFLAGS=libchello.a go build -buildmode=c-archive -o libgohello.a hello.go
main: Makefile main.c libgohello.a libchello.a
gcc -o main main.c libchello.a libgohello.a -pthread
.PHONY: clean
clean:
rm -f main *.a *.o
echo "extern void go_hello();" > libgohello.h
hello.go:
package main
/*
extern void c_hello();
*/
import "C"
import "time"
import "fmt"
//export go_hello
func go_hello() {
fmt.Printf("Hello from go\n")
time.Sleep(1 * time.Second)
C.c_hello()
}
func main() {}
libgohello.h:
extern void go_hello();
hello.c:
#include "libgohello.h"
#include <stdio.h>
void c_hello() {
printf("Hello from c\n");
go_hello();
}
main.c:
void c_hello();
int main() {
c_hello();
}
go.mod:
module hehoe.de/cgocircular
If you look at the verbose output from the go build command, you will see that when compiling the directory as a complete go package, the main.c file is being included as part of the C code used in hello.go.
From the documentation:
When the Go tool sees that one or more Go files use the special import "C", it will look for other non-Go files in the directory and compile them as part of the Go package
The easiest solution here is to separate the main C and Go packages, so that they don't interfere with each other's build process. Testing this out, removing the main.c file will build libchello.a and libgohello.a, and then adding it back in will complete the build of main.
I'm brand new to Go and am not experienced in compilation issues or with C++. So starting from zero, I'm trying to compile a dll in Go and call a function in VBA. I'm developing the Go in Linux, and switching computers to try it in Windows. The VBA part may not be essential. It does serve as a way for me to test the dll call.
Here's the simple Go code:
package main
import (
"C"
"fmt"
)
func main() {
var x int
x = Test()
fmt.Println(x)
}
func Test() int {
return 666
}
It works for go build, go install, and calling main from the console.
I compile with these two commands, which both run without error, in this order. Should I be running both of them? (I'm way out of my depth here.) The target system is Windows 10, 64 bit with Office 365 ProPlus (but is Excel fundamentally 32 bit?):
env GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc go build -buildmode=c-shared -o hello.dll hello.go
env GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -buildmode=c-shared -o hello.dll hello.go
I copy the file, hello.dll, to System32 and in VBA I declare:
Public Declare Function Test Lib "C:\Windows\System32\hello.dll" () as Long
I get the error of Run-time error '53': File Not Found, which I understand may be caused by a missing dependency.
So I run Dependency Walker. This shows me a top level of HELLO.DLL and 4 levels under it of KERNEL32.DLL, MSVCRT.DLL, WINMM.DLL, WS2_32.DLL. And it gives:
Error: At least one required implicit or forwarded depende3ncy was not found.
Warning: At least one delay-load dependency module was not found.
Warning: At least one module has an unresovled import due to a missing export function in a a delay-load dependent module.
There's a huge number of yellow question marks. And dozens of Modules with Error opening file, with names like API-MS-WIN-CORE-... and EXT-MS-WIN-NTUSER-... as well as HVSIFILETRUST.DLL and IESHIMS.DLL and EMCLIENT.DLL
So I've got a few moving parts in my lap and I'm way out of my depth. I'm hoping I'm just missing a simple step (maybe put the dll in a different folder??). If this should be an ambitious or difficult task, I should probably let it go.
First of all, all these code can be built successfully using go tool(e.g. go build, go install)
For say I got an a.go which tries to import a non-standard-library pkg from github:
package a
import (
"fmt"
"github.com/usr/pkg"
)
func init() {
fmt.Println("Import pkg", pkg.somevar)
}
when I try to compile it with gccgo:
$ gccgo -c a.go
a.go:5:20: error: import file ‘github.com/usr/pkg’ not found
...
And then I read the Setting up and using gccgo , it says
When you import the package FILE with gccgo, it will look for the
import data in the following files, and use the first one that it
finds.
FILE.gox
FILE.o
libFILE.so
libFILE.a
The gccgo compiler will look in the current directory for import files
So I cp the $GOPATH/pkg/github.com/usr/pkg.a to the current directory and rename it as libpkg.a.
It seems failed again:
$ gccgo -c a.go
a.go:9:4: error: libpkg.a: malformed archive header name at 8
a.go:9:4: error: libpkg.a exists but does not contain any Go export data
And yes, I use gccgo 4.7.2
I've got no experience woking with gcc, so I search for some help here.
The simplest way is to use the go command from the gc distribution, and run go build -compiler gccgo .
Your idea of copying pkg.a does not work because pkg.a was not built with gccgo.
I have the following package Makefile:
include ${GOROOT}/src/Make.inc
TARG=gorilla.googlecode.com/hg/gorilla/mux
GOFILES=\
doc.go\
mux.go\
DEPS=\
gorilla.googlecode.com/hg/gorilla/context
include ${GOROOT}/src/Make.pkg
I changed TARG and DEPS today to point to the Google code repository as shown above, following this advice.
The problem is: I can goinstall the package and it will install the dependency, but I cannot use gotest or gomake anymore; I get the following error (using Go r59):
moraes#yukon:~/dev/repos/gorilla/gorilla/mux$ gotest
rm -f _test/gorilla.googlecode.com/hg/gorilla/mux.a
make -C gorilla.googlecode.com/hg/gorilla/context install
make: *** gorilla.googlecode.com/hg/gorilla/context: No such file or directory. Stop.
make: *** [gorilla.googlecode.com/hg/gorilla/context.make] Error 2
gotest: "/home/moraes/dev/repos/go/go.r59/bin/gomake testpackage GOTESTFILES=mux_test.go" failed: exit status 2
I tried goinstalling the dependency first (goinstall gorilla.googlecode.com/hg/gorilla/context), and it installs correctly in $GOROOT/pkg but the same error occurs with gotest/gomake.
I think I'm missing something pretty basic. How should I proceed to use gomake/gotest with the Makefile above? Is this supposed to work at all, or should I use a different one for development?
goinstall doesn't use the Makefile at all. Instead, it will parse dependencies directly from your .go files.
To specify dependencies, annotate your import lines with a "normalised" reference to the dependency. eg.
import (
gorilla_context "gorilla.googlecode.com/hg/gorilla/context"
...
gomake doesn't automatically resolve dependencies though, so you'll have to manually install them.
Similarly, for installing cgo source with goinstall, you can specify CFLAGS and LDFLAGS in comment directives. eg.
/*
#cgo CFLAGS: -I/usr/local/include
#cgo LDFLAGS: -L/usr/local/lib -lzmq
#include <zmq.h>
*/
import "C"
I think the Makefile is trying to find the file gorilla.googlecode.com/hg/gorilla/context in the current directory. Also, why would you want to specify it in a make file as opposed to importing it from within the Source?