Compile Go for windows/arm and arm64 with buildmode=c-shared - windows

I need to compilet a go library via C to a DLL that can be used through PInvoke on ARM/ARM64 Windows. I find many open issues, topics and discussions about this and it sounds like it might partially work. But if I try it like this:
export CC="arm-none-eabi-gcc"
export CXX="arm-none-eabi-g++"
export GOOS="windows"
export GOARCH="arm"
export GOARM=7
export CGO_ENABLED="1"
go build -ldflags="-s -w" -o my_library.dll -buildmode c-shared
I get the result buildmode c-shares is not supported on windows/arm. So it seems to still be not supported.
Another problem is that I need to set CGO_ENABLED and route the compiling through a C/C++-Toolchain as I have to add a C-file generated by SWIG. I tried the above on Ubunu 20.04 with the toolchain of the package gcc-arm-none-eabi.
I'm no C/C++/Go-pro - but the same works for nearly all other platforms like Windows, Linux, Android, Mac and iOS. The latter also is based on ARM64, so I do not really understand why this is not possible - though I value the difficulties with all this.
So, if someone with more in-depth-knowledge can help me here that would be great.
Just to clarifiy: I do not want/need to compile Go itself for ARM/ARM64. I need to compile a Go-program for that platform (to use my library from .Net on e.g. the Surface or a Hololens).
Update from 04.08.2021:
Go 1.17rc2 should include windows arm64 now. And I got the hint to use Zig for cross-compiling. So I've changed my build pipeline to something like this (I'm using Azure Devops in a Ubuntu VM):
go get -v golang.org/dl/go1.17rc2
/home/vsts/go/bin/go1.17rc2 download
/home/vsts/go/bin/go1.17rc2 version
sudo snap install zig --classic --beta
zig version
export CC="zig cc -target aarch64-windows-gnu"
export CXX="zig c++ -target aarch64-windows-gnu"
export GOOS="windows"
export GOARCH="arm64"
export GOARM=7
export CGO_ENABLED="1"
/home/vsts/go/bin/go1.17rc2 build -ldflags="-s -w" -o storj_uplink.dll -buildmode c-shared -tags extended
I then get this error:
2021-08-03T19:24:52.0641737Z # runtime/cgo
2021-08-03T19:24:52.0642803Z info: Usage: zig [command] [options]
2021-08-03T19:24:52.0643335Z
2021-08-03T19:24:52.0643827Z Commands:
2021-08-03T19:24:52.0643940Z
2021-08-03T19:24:52.0644276Z build Build project from build.zig
2021-08-03T19:24:52.0645203Z init-exe Initialize a `zig build` application in the cwd
2021-08-03T19:24:52.0645768Z init-lib Initialize a `zig build` library in the cwd
2021-08-03T19:24:52.0645950Z
2021-08-03T19:24:52.0646407Z ast-check Look for simple compile errors in any set of files
2021-08-03T19:24:52.0646936Z build-exe Create executable from source or object files
2021-08-03T19:24:52.0647468Z build-lib Create library from source or object files
2021-08-03T19:24:52.0647994Z build-obj Create object from source or object files
2021-08-03T19:24:52.0648390Z fmt Reformat Zig source into canonical form
2021-08-03T19:24:52.0648753Z run Create executable and run immediately
2021-08-03T19:24:52.0649088Z test Create and run a test build
2021-08-03T19:24:52.0649551Z translate-c Convert C code to Zig code
2021-08-03T19:24:52.0649707Z
2021-08-03T19:24:52.0650109Z ar Use Zig as a drop-in archiver
2021-08-03T19:24:52.0650576Z cc Use Zig as a drop-in C compiler
2021-08-03T19:24:52.0651070Z c++ Use Zig as a drop-in C++ compiler
2021-08-03T19:24:52.0651549Z dlltool Use Zig as a drop-in dlltool.exe
2021-08-03T19:24:52.0652033Z lib Use Zig as a drop-in lib.exe
2021-08-03T19:24:52.0652495Z ranlib Use Zig as a drop-in ranlib
2021-08-03T19:24:52.0652670Z
2021-08-03T19:24:52.0652962Z env Print lib path, std path, cache directory, and version
2021-08-03T19:24:52.0653531Z help Print this help and exit
2021-08-03T19:24:52.0653879Z libc Display native libc paths file or validate one
2021-08-03T19:24:52.0654250Z targets List available compilation targets
2021-08-03T19:24:52.0654579Z version Print version number and exit
2021-08-03T19:24:52.0655062Z zen Print Zen of Zig and exit
2021-08-03T19:24:52.0655220Z
2021-08-03T19:24:52.0655445Z General Options:
2021-08-03T19:24:52.0655565Z
2021-08-03T19:24:52.0655982Z -h, --help Print command-specific usage
2021-08-03T19:24:52.0656154Z
2021-08-03T19:24:52.0656502Z error: unknown command: -E
2021-08-03T19:25:03.2047129Z # golang.org/x/sys/windows
2021-08-03T19:25:03.2048568Z /home/vsts/go/pkg/mod/golang.org/x/sys#v0.0.0-20210112091331-59c308dcf3cc/windows/types_windows.go:1620:24: undefined: JOBOBJECT_BASIC_LIMIT_INFORMATION
2021-08-03T19:25:03.2049594Z /home/vsts/go/pkg/mod/golang.org/x/sys#v0.0.0-20210112091331-59c308dcf3cc/windows/zsyscall_windows.go:3020:38: undefined: WSAData
2021-08-03T19:25:03.2050606Z /home/vsts/go/pkg/mod/golang.org/x/sys#v0.0.0-20210112091331-59c308dcf3cc/windows/zsyscall_windows.go:3096:51: undefined: Servent
2021-08-03T19:25:03.2051572Z /home/vsts/go/pkg/mod/golang.org/x/sys#v0.0.0-20210112091331-59c308dcf3cc/windows/zsyscall_windows.go:3110:50: undefined: Servent
2021-08-03T19:25:04.7947309Z ##[error]Bash exited with code '1'.
Basically "unknown command: -E" which is described here. But from my understanding this should work already. And furthermore this blog post does it directly with zig, too.
Second update from 04.08.2021
Go is now calling zig! The workaround using a bash-script is working. Now I get the following error:
2021-08-04T11:54:47.2530981Z # golang.org/x/sys/windows
2021-08-04T11:54:47.2532284Z /home/vsts/go/pkg/mod/golang.org/x/sys#v0.0.0-20210112091331-59c308dcf3cc/windows/types_windows.go:1620:24: undefined: JOBOBJECT_BASIC_LIMIT_INFORMATION
2021-08-04T11:54:47.2533180Z /home/vsts/go/pkg/mod/golang.org/x/sys#v0.0.0-20210112091331-59c308dcf3cc/windows/zsyscall_windows.go:3020:38: undefined: WSAData
2021-08-04T11:54:47.2534002Z /home/vsts/go/pkg/mod/golang.org/x/sys#v0.0.0-20210112091331-59c308dcf3cc/windows/zsyscall_windows.go:3096:51: undefined: Servent
2021-08-04T11:54:47.2534797Z /home/vsts/go/pkg/mod/golang.org/x/sys#v0.0.0-20210112091331-59c308dcf3cc/windows/zsyscall_windows.go:3110:50: undefined: Servent
2021-08-04T11:54:57.4223210Z # runtime/cgo
2021-08-04T11:54:57.4224911Z /snap/zig/3678/lib/libc/mingw/secapi/vsprintf_s.c:39:10: warning: implicit declaration of function '__ms_vsnprintf' is invalid in C99 [-Wimplicit-function-declaration]
2021-08-04T11:54:57.4225714Z return __ms_vsnprintf (_DstBuf, _Size, _Format, _ArgList);
2021-08-04T11:54:57.4226223Z ^
2021-08-04T11:54:57.4226624Z 1 warning generated.
2021-08-04T11:54:57.4227534Z /snap/zig/3678/lib/libc/mingw/math/arm/s_trunc.c/snap/zig/3678/lib/libc/mingw/math/arm/s_truncf.c:24:10: fatal error: '../bsd_private_base.h' file not found
2021-08-04T11:54:57.4228188Z #include "../bsd_private_base.h"
2021-08-04T11:54:57.4228651Z ^~~~~~~~~~~~~~~~~~~~~~~
2021-08-04T11:54:57.4229332Z :26:10: fatal error: '../bsd_private_base.h' file not found
2021-08-04T11:54:57.4229850Z #include "../bsd_private_base.h"
2021-08-04T11:54:57.4230310Z ^~~~~~~~~~~~~~~~~~~~~~~
2021-08-04T11:54:57.4230966Z 1 error generated.
2021-08-04T11:54:57.4231397Z 1 error generated.
2021-08-04T11:54:57.4522549Z ##[error]Bash exited with code '1'.
Update from 05.08.2021:
I finally found a toolchain that at least does not throw an error. But now it quits silently without generating a DLL. Not sure what happens now, though. This is my call:
go get -v golang.org/dl/go1.17rc2
/home/vsts/go/bin/go1.17rc2 download
/home/vsts/go/bin/go1.17rc2 version
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz
tar -xf gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz
cd gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf
cd bin
export PATH=$PATH:$(pwd)
cd ..
cd ..
cd uplink-c
export CC="arm-none-linux-gnueabihf-gcc -v"
export CXX="arm-none-linux-gnueabihf-g++ -v"
export GOOS="windows"
export GOARCH="arm64"
export GOARM=7
export CGO_ENABLED="1"
home/vsts/go/bin/go1.17rc2 build -ldflags="-s -w" -o storj_uplink.dll -buildmode c-shared -tags extended -v

I do not want/need to compile Go itself for ARM/ARM64. I need to compile a Go-program for that platform
That should work, using Go 1.17 beta
Its documentation do mention:
Windows
Go 1.17 adds support of 64-bit ARM architecture on Windows (the windows/arm64 port).
This port supports cgo.
The OP topperdel refers in the comments to CL 326310:
cmd/internal/sys: mark windows/arm64 as c-shared-capable
The platform supports c-shared now, so flip this on.
I've given this a small smoke test using WireGuard Tunnel Library, and it was able to pass packets and generally function well.
Since the WireGuard Tunnel Library uses quite a bit of Go functionality under the hood, I think it's a decent test that a lot of things that should be working are working. So this commit enables it.
In order to get all tests passing, we make a few small changes, such as
passing -Wno-dll-attribute-on-redeclaration to clang and avoiding
loading shared libraries into Powershell on arm.
As illustrated by those issues, this is this is still a work in progress:
issue 46502 ("runtime: several tests are failing on windows-arm64-aws builder due to redeclaration warnings (upgraded to errors in testing)"),
issue 46701 ("Powershell on arm64/arm cannot load arm64/arm binaries because it is an intel process")
The test part is now (June 13th, 2021) closed with golang/go commit 1ed0d12:
runtime: testprogcgo: don't call exported Go functions directly from Go
Instead route through a C function, to avoid declaration conflicts
between the declaration needed in the cgo comment and the declaration
generated by cgo in _cgo_export.h.
This is not something user code will ever do, so no need to make it
work in cgo.
I mentioned in the comments
From ziglang/zig issue 7342, zig should be supported now.
Example, with Go 1.17 : "Zig Makes Go Cross Compilation Just Work" from Loris Cro (VP of community #ziglang):
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
CC="zig cc -target x86_64-linux" \
CXX="zig c++ -target x86_64-linux" \
go build --tags extended
You would need to adapt the arch targets to your need
Note: a bash shell session is needed. So git bash on Windows, for instance.

Related

Compile go program with C dependency for Windows

Building go works fine for pure go project with pure go dependencies. But when building a project with a C dependency, it fails on Windows:
go build -a -o bin/xyz.exe ./xyz/main.go
go: downloading gopkg.in/confluentinc/confluent-kafka-go.v1 v1.4.2
go: downloading github.com/confluentinc/confluent-kafka-go v1.4.2
# gopkg.in/confluentinc/confluent-kafka-go.v1/kafka
In file included from C:\Users\VssAdministrator\go\pkg\mod\gopkg.in\confluentinc\confluent-kafka-go.v1#v1.4.2\kafka\00version.go:24:
./librdkafka/rdkafka.h:83:10: fatal error: sys/socket.h: No such file or directory
#include <sys/socket.h> /* for sockaddr, .. */
^~~~~~~~~~~~~~
compilation terminated.
mingw32-make: *** [Makefile:10: build-windows] Error 2
##[error]Cmd.exe exited with code '2'.
Finishing: CmdLine
As can be seen from the output above, I'm using a Makefile, and my azure-pipelines.yml looks like this:
...
- script: 'make package-windows'
...
Here's my Makefile:
build-windows:
go build -a -o bin/xyz.exe ./xyz/main.go
I also tried setting GOOS and GOARCH, to no avail:
build-windows:
GOOS=windows GOARCH=amd64 go build -a -o bin/xyz.exe ./xyz/main.go
So how can I build this?
This doesn't have much to do with Go - it seems you're doing everything correctly in that regard. The issue is that the library you're using needs sys/socket.h which simply doesn't exist on Windows (see Using sys/socket.h functions on windows).
Your options are similar to what's mentioned in the other answer:
Try to build using Cygwin.
Modify the library to use Winsock instead of sys/socket.h.
Find a different library, one that supports Windows (the author of the library you're using specifically said Windows is not supported).

Flags needed to create static binaries in golang

I was building a Docker image using scratch as base.
The following build command:
RUN go build -o /go/bin/myapp
created a binary that kept failing when executed:
standard_init_linux.go:211: exec user process caused "no such file or directory"
By trial and error I found out that I needed to build as follows:
RUN CGO_ENABLED=0 go build -o /go/bin/myapp -a -ldflags '-extldflags "-static"' .
Why are both CGO_ENABLED=0 and -ldflags '-extldflags "-static"' necessary?
Don't both options create static binaries? (i.e. binaries that will need absolutely nothing in terms of libraries from the environments they will run on?)
Just before we start, a heads up by Russ Cox's: Comment
Read this as well: Comment
Yes, I agree with Volker's comment that some systems don't really allow static binaries.
Read on: Compile packages and dependencies
-a
force rebuilding of packages that are already up-to-date.
-ldflags '[pattern=]arg list'
arguments to pass on each go tool link invocation.
Read: go tool link
-extldflags flags
Set space-separated flags to pass to the external linker.
Hence, it tries to rebuild all the packages (dependencies as well) with CGO disabled and also -static means do not link against shared libraries.
Some of the points related to static linking are explained well: Linking golang statically

cannot implicitly include runtime/cgo in a shared library

Following my earlier question, now I'm getting this error when trying to perform all the same steps from this article, since I've upgraded from Go 1.6.1 to Go 1.7.1 (I cannot go back to Go 1.6.1 because the linker crashes when tries to compile some shared libraries).
What I did:
Installed go in ~/.go/go (this is later referred to as GOROOT).
Compiled libstd.so:
GOROOT=~/.go/go GOPATH=~/tests go install -buildmode=shared -linkshared std
Compiled calc library:
GOROOT=~/.go/go GOPATH=~/tests go install -a -x -buildmode=shared -linkshared calc
Tried to compile app:
GOROOT=~/.go/go GOPATH=~/tests go build -a -x -linkshared -o app cashier
and received this error:
~/.go/go/pkg/tool/linux_amd64/link: cannot implicitly include runtime/cgo in a shared library
I tried to repeat the steps with CGO_ENABLED=0 prepended to the environment, but nothing would build this way. Giving me this error:
imports runtime/cgo: C source files not allowed when not using cgo or SWIG: gcc_fatalf.c gcc_linux_amd64.c gcc_mmap.c gcc_util.c
Is this a known bug? Is making shared libraries meant to be supported?
The only way I've been able to make working DLLs is to use buildmode=c-archive and write C stubs for all the go functions I want to export. But I was working on Windows for this, I haven't had to mess with this on Linux. It is a path to investigate.

Go/Golang Cross-Compile from Mac to Windows: fatal error: 'windows.h' file not found

Summary: when I try cross-compiling a .go source file that includes a C file somewhere in the file chain, targeting Windows AMD64 from a Mac host, I get:
/usr/local/go/src/runtime/cgo/gcc_windows_amd64.c:8:10: fatal error: 'windows.h' file not found
Purely Go code seems to cross compile without error; is there a way to get the proper header files for cross compilation when C files are involved?
More details: I installed LiteIDE on my Mac for working on some .go projects, and LiteIDE makes it relatively simple to target other platforms as build targets. I tested it on a small test project I had, purely Go, and it seemed to run without error.
Later I tried it on a current, larger project and had to adjust several env settings in the IDE to get it to work (complaints about C files without CGO enabled, and GOPATH not set properly even though it's set in .bash_profile and verified in echo $VARIABLE just fine.) The result is
/usr/local/go/src/runtime/cgo/gcc_windows_amd64.c:8:10: fatal error: 'windows.h' file not found
Trying to target Linux (os linux, arch amd64) gives
# runtime/cgo
ld: unknown option: --build-id=none
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I've double checked I have XCode installed; gcc is installed:
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr with-gxx-include- dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/c++/4.2.1
Apple LLVM version 7.3.0 (clang-703.0.29)
Target: x86_64-apple-darwin15.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
...and Go is the latest version:
go version go1.6.2 darwin/amd64
I also checked that this isn't just from LiteIDE (since LiteIDE seems to override env settings and ignore what's in the terminal?); an example attempt at the console gives:
MyUsername$ env GOOS=windows GOARCH=amd64 CGO_ENABLED=1 go build -v
golang.org/x/net/html/atom
runtime/cgo
golang.org/x/crypto/ssh/terminal
golang.org/x/net/html
github.com/howeyc/gopass
# runtime/cgo
/usr/local/go/src/runtime/cgo/gcc_windows_amd64.c:8:10: fatal error: 'windows.h' file not found
github.com/andybalholm/cascadia
github.com/PuerkitoBio/goquery
I suspect this is because the application uses networking libraries in Go and I think the native libraries are still calling some C files to fill in gaps. Is there a way to get the proper libraries for building on Linux/Windows or does this need to be done on the target platforms in order to work?
Building native applications on the host platform seems to work without issue.
To enable cross-compiling for CGO you need to have a local toolchain that can compile C code for that target.
I'm not very familiar with Mac OS X, but on Arch Linux all I had to do was install mingw-w64-toolchain and compile my go code with:
env GOOS="windows" GOARCH="386" CGO_ENABLED="1" CC="i686-w64-mingw32-gcc" go build
// or to target win 64
env GOOS="windows" GOARCH="amd64" CGO_ENABLED="1" CC="x86_64-w64-mingw32-gcc" go build
On OSX, you can install mingw with homebrew: brew install mingw-w64
About the other error message though, ld: unknown option: --build-id=none seems like a bug, you might want to report that on the Go issue tracker.

Cross compile Go on OSX?

I am trying to cross-compile a go app on OSX to build binaries for windows and linux. I have read everything what I could find on the net. Closest example that I have found has been published on (apart from many unfinished discussions on go-nuts mailing list):
http://solovyov.net/en/2012/03/09/cross-compiling-go/
yet it does not work on my installation. I have go 1.0.2. As 1.0.2 is quite recent it looks to me that all above examples do not apply to this version.
Tried to do ./make.bash --no-clean with ENV vars set to 386/windows, it does build go, however it builds go for my installation which is darwin/amd64 and completely ignores what is set in ENV that suppose to build different compiler.
Any advises how it can be done (if it can be done at all)?
With Go 1.5 they seem to have improved the cross compilation process, meaning it is built in now. No ./make.bash-ing or brew-ing required. The process is described here but for the TLDR-ers (like me) out there: you just set the GOOS and the GOARCH environment variables and run the go build.
For the even lazier copy-pasters (like me) out there, do something like this if you're on a *nix system:
env GOOS=linux GOARCH=arm go build -v github.com/path/to/your/app
You even learned the env trick, which let you set environment variables for that command only, completely free of charge.
Thanks to kind and patient help from golang-nuts, recipe is the following:
1) One needs to compile Go compiler for different target platforms and architectures. This is done from src folder in go installation. In my case Go installation is located in /usr/local/go thus to compile a compiler you need to issue make utility. Before doing this you need to know some caveats.
There is an issue about CGO library when cross compiling so it is needed to disable CGO library.
Compiling is done by changing location to source dir, since compiling has to be done in that folder
cd /usr/local/go/src
then compile the Go compiler:
sudo GOOS=windows GOARCH=386 CGO_ENABLED=0 ./make.bash --no-clean
You need to repeat this step for each OS and Architecture you wish to cross compile by changing the GOOS and GOARCH parameters.
If you are working in user mode as I do, sudo is needed because Go compiler is in the system dir. Otherwise you need to be logged in as super user. On Mac you may need to enable/configure SU access (it is not available by default), but if you have managed to install Go you possibly already have root access.
2) Once you have all cross compilers built, you can happily cross compile your application by using the following settings for example:
GOOS=windows GOARCH=386 go build -o appname.exe appname.go
GOOS=linux GOARCH=386 CGO_ENABLED=0 go build -o appname.linux appname.go
Change the GOOS and GOARCH to targets you wish to build.
If you encounter problems with CGO include CGO_ENABLED=0 in the command line. Also note that binaries for linux and mac have no extension so you may add extension for the sake of having different files. -o switch instructs Go to make output file similar to old compilers for c/c++ thus above used appname.linux can be any other extension.
If you use Homebrew on OS X, then you have a simpler solution:
$ brew install go --with-cc-common # Linux, Darwin, and Windows
or..
$ brew install go --with-cc-all # All the cross-compilers
Use reinstall if you already have go installed.
You can do this pretty easily using Docker, so no extra libs required. Just run this command:
docker run --rm -it -v "$GOPATH":/go -w /go/src/github.com/iron-io/ironcli golang:1.4.2-cross sh -c '
for GOOS in darwin linux windows; do
for GOARCH in 386 amd64; do
echo "Building $GOOS-$GOARCH"
export GOOS=$GOOS
export GOARCH=$GOARCH
go build -o bin/ironcli-$GOOS-$GOARCH
done
done
'
You can find more details in this post:
https://medium.com/iron-io-blog/how-to-cross-compile-go-programs-using-docker-beaa102a316d
The process of creating executables for many platforms can be a little tedious, so I suggest to use a script:
#!/usr/bin/env bash
package=$1
if [[ -z "$package" ]]; then
echo "usage: $0 <package-name>"
exit 1
fi
package_name=$package
#the full list of the platforms: https://golang.org/doc/install/source#environment
platforms=(
"darwin/386"
"dragonfly/amd64"
"freebsd/386"
"freebsd/amd64"
"freebsd/arm"
"linux/386"
"linux/amd64"
"linux/arm"
"linux/arm64"
"netbsd/386"
"netbsd/amd64"
"netbsd/arm"
"openbsd/386"
"openbsd/amd64"
"openbsd/arm"
"plan9/386"
"plan9/amd64"
"solaris/amd64"
"windows/amd64"
"windows/386" )
for platform in "${platforms[#]}"
do
platform_split=(${platform//\// })
GOOS=${platform_split[0]}
GOARCH=${platform_split[1]}
output_name=$package_name'-'$GOOS'-'$GOARCH
if [ $GOOS = "windows" ]; then
output_name+='.exe'
fi
env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package
if [ $? -ne 0 ]; then
echo 'An error has occurred! Aborting the script execution...'
exit 1
fi
done
I checked this script on OSX only
gist - go-executable-build.sh
for people who need CGO enabled and cross compile from OSX targeting
windows
I needed CGO enabled while compiling for windows from my mac since I had imported the https://github.com/mattn/go-sqlite3 and it needed it.
Compiling according to other answers gave me and error:
/usr/local/go/src/runtime/cgo/gcc_windows_amd64.c:8:10: fatal error: 'windows.h' file not found
If you're like me and you have to compile with CGO. This is what I did:
1.We're going to cross compile for windows with a CGO dependent library. First we need a cross compiler installed like mingw-w64
brew install mingw-w64
This will probably install it here /usr/local/opt/mingw-w64/bin/.
2.Just like other answers we first need to add our windows arch to our go compiler toolchain now. Compiling a compiler needs a compiler (weird sentence) compiling go compiler needs a separate pre-built compiler. We can download a prebuilt binary or build from source in a folder eg: ~/Documents/go
now we can improve our Go compiler, according to top answer but this time with CGO_ENABLED=1 and our separate prebuilt compiler GOROOT_BOOTSTRAP(Pooya is my username):
cd /usr/local/go/src
sudo GOOS=windows GOARCH=amd64 CGO_ENABLED=1 GOROOT_BOOTSTRAP=/Users/Pooya/Documents/go ./make.bash --no-clean
sudo GOOS=windows GOARCH=386 CGO_ENABLED=1 GOROOT_BOOTSTRAP=/Users/Pooya/Documents/go ./make.bash --no-clean
3.Now while compiling our Go code use mingw to compile our go file targeting windows with CGO enabled:
GOOS="windows" GOARCH="386" CGO_ENABLED="1" CC="/usr/local/opt/mingw-w64/bin/i686-w64-mingw32-gcc" go build hello.go
GOOS="windows" GOARCH="amd64" CGO_ENABLED="1" CC="/usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-gcc" go build hello.go
Since go 1.17.1 you must explicitly set go env variables.
I've done it in a shell script
#!/bin/bash
GOOS=linux
GOARCH=amd64
go.exe get -d -v ./...
go.exe env -w GOOS=$GOOS
go.exe env -w GOARCH=$GOARCH
go.exe build -v ./cmd/tecnoalarm/main.go
for people who need CGO enabled and cross-compile from another system
There is one docker solution golang-crossbuild
Here is one example to build linux/armv7 on Windows/MacOS
docker run -it --rm \
-v $GOPATH/src/github.com/user/go-project:/go/src/github.com/user/go-project \
-w /go/src/github.com/user/go-project \
-e CGO_ENABLED=1 \
docker.elastic.co/beats-dev/golang-crossbuild:1.16.4-armhf \
--build-cmd "make build" \
-p "linux/armv7"

Resources