webui-dev/go-webui

Cannot cross-compile a project using go-webui

Mihara opened this issue · 5 comments

Description

Let me start by saying that I'm not sure this is actually a bug, but if anything, it's something insufficiently documented...

Assume a naive example project I plan to build for Windows while running under Linux. Simply running the setup.sh script as described in README produces no useful results:

$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/webui-dev/go-webui/main/setup.sh)"
go: finding module for package github.com/webui-dev/go-webui/v2
go: downloading github.com/webui-dev/go-webui v0.1.0
go: found github.com/webui-dev/go-webui/v2 in github.com/webui-dev/go-webui/v2 v2.4.2
go: upgraded github.com/webui-dev/go-webui/v2 v2.4.2 => v2.4.3-0.20240711195955-8001a5e84f7d

$ go build
# github.com/webui-dev/go-webui/v2
../../../.local/golang/pkg/mod/github.com/webui-dev/go-webui/v2@v2.4.3-0.20240711195955-8001a5e84f7d/cgo.go:14:10: fatal error: webui/src/civetweb/civetweb.c: No such file or directory
   14 | #include "webui/src/civetweb/civetweb.c"
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.

I'm trying to patch the library in by making a local copy as described here:

require (
...
        github.com/webui-dev/go-webui/v2 v2.4.3
)
replace github.com/webui-dev/go-webui/v2 v2.4.3 => ../vendor/go-webui/v2

At this point, go build produces a native binary that runs. Which is nice, but cross-compilation is mission critical (and the reason I picked Go for this project in the first place), so...

$ GOOS=windows GOARCH=amd64 go build
....
	imports github.com/webui-dev/go-webui/v2: build constraints exclude all Go files in ...

Not big surprise here, because that's only supposed to work on projects not making use of cgo. Ok, let's try it with explicitly invoking cgo...

$ GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CXX=x86_64-w32-mingw32-g++ CC=x86_64-w64-mingw32-gcc go build -v
...
/usr/lib/go-1.22/pkg/tool/linux_amd64/link: running x86_64-w64-mingw32-gcc failed: exit status 1
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lWs2_32: No such file or directory
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lOle32: No such file or directory
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lAdvapi32: No such file or directory
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lShell32: No such file or directory
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lUser32: No such file or directory

Now this is quite cryptic, and googling does not appear to produce any clues on what could possibly be wrong. Initially I assumed I just had a misconfigured mingw32 or something, but then I try xgo (https://github.com/techknowlogick/xgo) which is supposed, in theory, to take care of everything for me, and carries everything inside a docker image:

$ $ xgo --targets=windows/amd64 .
Checking docker installation...
Client: Docker Engine - Community
 Version:           27.0.3
 API version:       1.46
 Go version:        go1.21.11
 Git commit:        7d4bcd8
 Built:             Sat Jun 29 00:02:33 2024
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          27.0.3
  API version:      1.46 (minimum version 1.24)
  Go version:       go1.21.11
  Git commit:       662f78c
  Built:            Sat Jun 29 00:02:33 2024
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.18
  GitCommit:        ae71819c4f5e67bb4d5ae76a6b735f29cc25774e
 runc:
  Version:          1.7.18
  GitCommit:        v1.1.13-0-g58aa920
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Checking for required docker image techknowlogick/xgo:latest... found.
Cross compiling local repository: . : ...
Enabled Go module support
Building /source/go.mod...
Compiling for windows-4.0/amd64...
......
/usr/local/go/pkg/tool/linux_amd64/link: running x86_64-w64-mingw32-gcc-posix failed: exit status 1
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lWs2_32
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lOle32
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lAdvapi32
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lShell32
/usr/bin/x86_64-w64-mingw32-ld: cannot find -lUser32
collect2: error: ld returned 1 exit status

Notice the identical error message. Which suggests the problem is somewhere in go-webui...

Expected Behavior

I do expect cross-compilation to work or a statement that it isn't supported to be present in the documentation.

Reproduction Steps

No response

Error Logs

No response

Possible Solution

No response

Version

2.4.3

Environment Details (OS name, version, etc.)

Linux 6.9.9-1-liquorix-amd64 x86_64 GNU/Linux
Ubuntu 22.04

After much digging, there was some progress.

Turns out, Linux cross-compilation does not tolerate using upper case in LDFLAGS, since while Windows filesystems might not be case-sensitive, Linux filesystems which the MinGW libraries are installed to are. Editing cgo.go to replace -lWs2_32 with -lws2_32 (and all the other cases) fixes this particular problem. It still doesn't compile, however, complaining about undefined reference to `clock_gettime'...

...which is solved by adding -lpthread -static to LDFLAGS, although I'm not certain this is the correct way out of this particular difficulty. (where would a native Windows compile get clock_gettime anyway?)

What is your goal? Statically linked against libwebui? Or dynamically linked? I'm cross-compiling with a statically linked libwebui-2-static.a without any issues using ming32-64. I haven't tried dynamically linked yet (although that should work just fine too).

I don't know if it's the correct way to cross-compile, but I'm having a similar problem, I'm trying to compile from Linux to Windows with the following command that I constructed from this thread:

GOOS=windows GOARCH=amd64 CXX=x86_64-w32-mingw32-g++ CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="-lpthread -static" go build

but i'm getting these errors, seems to be the same as OP stated here:

/usr/bin/x86_64-w64-mingw32-ld: /tmp/go-link-3112001602/000002.o: in function `go_webui_bind':
/home/ducking/go/pkg/mod/github.com/webui-dev/go-webui/v2@v2.4.2/lib.go:18: undefined reference to `webui_bind'
/usr/bin/x86_64-w64-mingw32-ld: /tmp/go-link-3112001602/000002.o: in function `_cgo_8434d9295eb9_Cfunc_webui_decode':
/tmp/go-build/cgo-gcc-prolog:97: undefined reference to `webui_decode'
/usr/bin/x86_64-w64-mingw32-ld: /tmp/go-link-3112001602/000002.o: in function `_cgo_8434d9295eb9_Cfunc_webui_encode':
/tmp/go-build/cgo-gcc-prolog:151: undefined reference to `webui_encode'
...
/usr/bin/x86_64-w64-mingw32-ld: /tmp/go-link-2129543397/000002.o: in function `_cgo_8434d9295eb9_Cfunc_webui_set_timeout':
/tmp/go-build/cgo-gcc-prolog:699: undefined reference to `webui_set_timeout'
/usr/bin/x86_64-w64-mingw32-ld: /tmp/go-link-2129543397/000002.o: in function `_cgo_8434d9295eb9_Cfunc_webui_wait':
/tmp/go-build/cgo-gcc-prolog:772: undefined reference to `webui_wait'
collect2: error: ld returned 1 exit status

and the list is pretty big too.

CXX=x86_64-w32-mingw32-g++

That is not required. There's no C++ involved unless you're doing some C++ FFI in your Go code.

undefined reference to webui_bind

You haven't told the linker that you want to link against libwebui and where your Windows copy of the libwebui library is located. You will typically need to tell the linker manually like so (using static linking as per your example):

GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="-L./path/to/where/you/have/webui-windows-gcc-x64 -l:libwebui-2-static.a -lpthread -static" go build

NOTE that libwebui also depends on COM on Windows and you will need to explicitly tell the linker to link against ole32.dll for COM like so:

GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="-L./path/to/where/you/have/webui-windows-gcc-x64 -l:libwebui-2-static.a -lpthread -lole32 -static" go build