I'm trying to compile a Go native binary to use in Java code on Mac. The binary needs to be executed on a linux host which has the following specifications:
# uname -a
Linux <hostname> 5.14.0-284.25.1.el9_2.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Jul 20 09:11:28 EDT 2023 x86_64 x86_64 x86_64 GNU/Linux
For local testing on mac, I simply generate the binary using go build -o libmybinary.so -buildmode=c-shared main.go
but to compile it in the correct format for the linux host, I use this instead:
FROM source as builder
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o libmybinary.so -buildmode=c-shared main.go
The problem is that if I don't use CGO_ENABLED=1 GOOS=linux GOARCH=amd64
, the binary cannot be executed on the host, but if I try to use it, I get this error in docker build:
1.553 # runtime/cgo
1.553 gcc: error: unrecognized command line option '-m64'
------
ERROR: failed to solve: process "/bin/bash -eo pipefail -c CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o libmybinary.so -buildmode=c-shared main.go" did not complete successfully: exit code: 1
I also tried replacing
FROM source as builder
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o libmybinary.so -buildmode=c-shared main.go
with
FROM --platform=linux/amd64 source as builder
RUN go build -o libmybinary.so -buildmode=c-shared main.go
and it seems to compile the .so in correct format for the host but my java code couldn't find the exported methods as I think this way does not essentially "cross-compile" within the native image, leading to differences in behaviour with cgo and the underlying C toolchain. I wanted to check what would be the right way to achieve the correctly compiled binary?
Thank you life888888 for such a detailed answer above, taking inspiration from it, I did something simpler for my use case to make it work. Instead of trying to create the binary in Dockerfile itself, I simply used the Dockerfile to create the environment I wanted matching the one I had on the host. This is defined in the BASE_IMAGE
argument where it pull the image I needed (which had the right OS for linux and go installed). So this is simply how my Dockerfile looked:
FROM ${BASE_IMAGE}:${BASE_TAG} as base
WORKDIR /workspace
COPY go.mod go.mod
COPY go.sum go.sum
ADD . go_mylib
RUN go mod download
Then I build the container on my M1 Mac using the command:
$ docker build -t go_mylib:v1 --platform linux/amd64 .
Run the image using:
$ docker run -i -t --sysctl net.ipv6.conf.all.disable_ipv6=0 --platform linux/amd64 --name go_mylib-v1 go_mylib:v1 /bin/bash
Once inside the container's bash, I go into the project folder and then run the command to create a shared library:
# cd go_mylib/
# go build -o libmybinary.so -buildmode=c-shared main.go
And finally exiting the container, I copy the binary generated to my local folder using this command:
$ docker cp go_mylib-v1:/workspace/go_mylib/libmybinary.so .
(where /workspace/go_mylib/libmybinary.so
is the path of the file inside my container and .
refers to the current folder in my local system.
I finally load this library from my Java code by using:
MyLib INSTANCE = Native.load("mybinary", MyLib.class);
and it works on the host as expected.