therecipe / qt

Qt binding for Go (Golang) with support for Windows / macOS / Linux / FreeBSD / Android / iOS / Sailfish OS / Raspberry Pi / AsteroidOS / Ubuntu Touch / JavaScript / WebAssembly
GNU Lesser General Public License v3.0
10.49k stars 748 forks source link

getting "go build -buildmode=plugin" to work #587

Closed djahma closed 6 years ago

djahma commented 6 years ago

Hello, I'd like to create a modular GUI desktop app using Go go build -buildmode=plugin feature. I simply used the first qtexample after install, and renamed the main function "Init()" as well as main.go file into qtexample.go, but I can't get it to build:

gman@thinkpad ~/Development/Golang/src/qtexample $ go build -buildmode=plugin qtexample.go
go build github.com/therecipe/qt/quickcontrols2: invalid flag in #cgo LDFLAGS: -Wl,-O1
go build github.com/therecipe/qt/core: invalid flag in #cgo LDFLAGS: -Wl,-O1

I have no idea how cgo/qtdeploy/make work. Here is the output when I use qtdeploy:

gman@thinkpad ~/Development/Golang/src/qtexample $ qtdeploy test desktop
ERRO[0015] failed to run command                         cmd="go build -p 4 -v -ldflags=all=\"-s\" \"-w\" -o /home/gman/Development/Golang/src/qtexample/d
eploy/linux/qtexample -tags=\"minimal\"" dir=/home/gman/Development/Golang/src/qtexample env="GOARCH=amd64 CGO_CFLAGS_ALLOW=.* CGO_ENABLED=1 PATH=/home/gm
an/Development/Golang/bin:/home/gman/Development/Golang/bin:/home/gman/Development/Golang/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
:/usr/games:/usr/local/games:/usr/local/go/bin:/usr/local/go/bin:/usr/local/go/bin GOPATH=/home/gman/Development/Golang GOROOT=/usr/local/go CGO_CXXFLAGS_
ALLOW=.* CGO_LDFLAGS_ALLOW=.* GOOS=linux" error="exit status 2" func=RunCmd name="build for linux on linux"
github.com/therecipe/qt/quickcontrols2
github.com/therecipe/qt/core
# github.com/therecipe/qt/quickcontrols2
_cgo_main.c: In function 'crosscall2':
_cgo_main.c:2:23: warning: unused parameter 'fn' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                       ^
_cgo_main.c:2:61: warning: unused parameter 'a' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                             ^
_cgo_main.c:2:68: warning: unused parameter 'c' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                    ^
_cgo_main.c:2:85: warning: unused parameter 'ctxt' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                                     ^
_cgo_main.c: In function '_cgo_release_context':
_cgo_main.c:4:41: warning: unused parameter 'ctxt' [-Wunused-parameter]
 void _cgo_release_context(__SIZE_TYPE__ ctxt) { }
_cgo_main.c: In function '_cgo_allocate':
_cgo_main.c:6:26: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                          ^
_cgo_main.c:6:33: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                                 ^
_cgo_main.c: In function '_cgo_panic':
_cgo_main.c:7:23: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                       ^
_cgo_main.c:7:30: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                              ^
# github.com/therecipe/qt/core
_cgo_main.c: In function 'crosscall2':
_cgo_main.c:2:23: warning: unused parameter 'fn' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                       ^
_cgo_main.c:2:61: warning: unused parameter 'a' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                             ^
_cgo_main.c:2:68: warning: unused parameter 'c' [-Wunused-parameter]
void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                    ^
_cgo_main.c:2:85: warning: unused parameter 'ctxt' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                                     ^
_cgo_main.c: In function '_cgo_release_context':
_cgo_main.c:4:41: warning: unused parameter 'ctxt' [-Wunused-parameter]
 void _cgo_release_context(__SIZE_TYPE__ ctxt) { }
                                         ^
_cgo_main.c: In function '_cgo_allocate':
_cgo_main.c:6:26: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                          ^
_cgo_main.c:6:33: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                                 ^
_cgo_main.c: In function '_cgo_panic':
_cgo_main.c:7:23: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                       ^
_cgo_main.c:7:30: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                              ^
github.com/therecipe/qt/qml
github.com/therecipe/qt/gui
# github.com/therecipe/qt/qml
_cgo_main.c: In function 'crosscall2':
_cgo_main.c:2:23: warning: unused parameter 'fn' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                       ^
_cgo_main.c:2:61: warning: unused parameter 'a' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                             ^
_cgo_main.c:2:68: warning: unused parameter 'c' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                    ^
_cgo_main.c:2:85: warning: unused parameter 'ctxt' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                                     ^
_cgo_main.c: In function '_cgo_release_context':
_cgo_main.c:4:41: warning: unused parameter 'ctxt' [-Wunused-parameter]
 void _cgo_release_context(__SIZE_TYPE__ ctxt) { }
                                         ^
_cgo_main.c: In function '_cgo_allocate':
_cgo_main.c:6:26: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                          ^
_cgo_main.c:6:33: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                                 ^
_cgo_main.c: In function '_cgo_panic':
_cgo_main.c:7:23: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
_cgo_main.c:7:30: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                              ^
# github.com/therecipe/qt/gui
_cgo_main.c: In function 'crosscall2':
_cgo_main.c:2:23: warning: unused parameter 'fn' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                       ^
_cgo_main.c:2:61: warning: unused parameter 'a' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                             ^
_cgo_main.c:2:68: warning: unused parameter 'c' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                    ^
_cgo_main.c:2:85: warning: unused parameter 'ctxt' [-Wunused-parameter]
_cgo_main.c: In function '_cgo_release_context':
_cgo_main.c:4:41: warning: unused parameter 'ctxt' [-Wunused-parameter]
 void _cgo_release_context(__SIZE_TYPE__ ctxt) { }
                                         ^
_cgo_main.c: In function '_cgo_allocate':
_cgo_main.c:6:26: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                          ^
_cgo_main.c:6:33: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                                 ^
_cgo_main.c: In function '_cgo_panic':
_cgo_main.c:7:23: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
_cgo_main.c:7:30: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                              ^
qtexample
# qtexample
_cgo_main.c: In function 'crosscall2':
_cgo_main.c:2:23: warning: unused parameter 'fn' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                       ^
_cgo_main.c:2:61: warning: unused parameter 'a' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                             ^
_cgo_main.c:2:68: warning: unused parameter 'c' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                    ^
_cgo_main.c:2:85: warning: unused parameter 'ctxt' [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                                     ^
_cgo_main.c: In function '_cgo_release_context':
_cgo_main.c:4:41: warning: unused parameter 'ctxt' [-Wunused-parameter]
 void _cgo_release_context(__SIZE_TYPE__ ctxt) { }
                                         ^
_cgo_main.c: In function '_cgo_allocate':
_cgo_main.c:6:26: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                          ^
_cgo_main.c:6:33: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                                 ^
_cgo_main.c: In function '_cgo_panic':
_cgo_main.c:7:23: warning: unused parameter 'a' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                       ^
_cgo_main.c:7:30: warning: unused parameter 'c' [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                              ^
# qtexample
runtime.main_main·f: relocation target main.main not defined
runtime.main_main·f: undefined: "main.main"

Any help getting Qt in a Go plugin would be much appreciated.

By the way, I'm on Mint 17.3, using Go 1.10 and Qt 5.10.1 Cheers.

therecipe commented 6 years ago

Hey

Seems like you just need to set some of the CGO_* flags first. Take a look here: https://github.com/therecipe/qt/wiki/Installation#go--110-specific-environmental-variables

djahma commented 6 years ago

Thanks for the lead...but still not working :-( So I exported the 3 CGO env variables from your link, and now go build spit similar errors to qtdeploy and would not complete, so I must Ctrl+C my way out. Any other idea? :-)

therecipe commented 6 years ago

How does the errors look like? Something about undefined callbacks or something? I think I remember something about plugins not working nicely with cgo ...

djahma commented 6 years ago

here's an excerpt:

gman@thinkpad ~/Development/Golang/src/qtexample $ go build -buildmode=plugin qtexample.go
# github.com/therecipe/qt/quickcontrols2
_cgo_main.c: In function ‘crosscall2’:
_cgo_main.c:2:23: warning: unused parameter ‘fn’ [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                       ^
_cgo_main.c:2:61: warning: unused parameter ‘a’ [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                             ^
_cgo_main.c:2:68: warning: unused parameter ‘c’ [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                    ^
_cgo_main.c:2:85: warning: unused parameter ‘ctxt’ [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                                     ^
_cgo_main.c: In function ‘_cgo_release_context’:
_cgo_main.c:4:41: warning: unused parameter ‘ctxt’ [-Wunused-parameter]
 void _cgo_release_context(__SIZE_TYPE__ ctxt) { }
                                         ^
_cgo_main.c: In function ‘_cgo_allocate’:
_cgo_main.c:6:26: warning: unused parameter ‘a’ [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                          ^
_cgo_main.c:6:33: warning: unused parameter ‘c’ [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
_cgo_main.c: In function ‘_cgo_panic’:
_cgo_main.c:7:23: warning: unused parameter ‘a’ [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                       ^
_cgo_main.c:7:30: warning: unused parameter ‘c’ [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                              ^
# github.com/therecipe/qt/core
cgo-gcc-prolog: In function ‘_cgo_30c91e82db81_Cfunc_QThread_QThread_YieldCurrentThread’:
cgo-gcc-prolog:52609:49: warning: unused variable ‘a’ [-Wunused-variable]
# github.com/therecipe/qt/core
core.cpp:662:6: warning: unused parameter ‘ptr’ [-Wunused-parameter]
 char QAbstractEventDispatcher_RegisterEventNotifier(void* ptr, void* notifier)
core.cpp:662:6: warning: unused parameter ‘notifier’ [-Wunused-parameter]
core.cpp:746:6: warning: unused parameter ‘ptr’ [-Wunused-parameter]
 void QAbstractEventDispatcher_UnregisterEventNotifier(void* ptr, void* notifier)
      ^
core.cpp:746:6: warning: unused parameter ‘notifier’ [-Wunused-parameter]
core.cpp: In function ‘void* QAbstractTableModel_DataDefault(void*, void*, int)’:
core.cpp:3124:1: warning: no return statement in function returning non-void [-Wreturn-type]
 }
 ^
core.cpp: In function ‘int QAbstractTableModel_ColumnCountDefault(void*, void*)’:
core.cpp:3136:1: warning: no return statement in function returning non-void [-Wreturn-type]
 }

[...]

# github.com/therecipe/qt/core
_cgo_main.c: In function ‘crosscall2’:
_cgo_main.c:2:23: warning: unused parameter ‘fn’ [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                       ^
_cgo_main.c:2:61: warning: unused parameter ‘a’ [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                             ^
_cgo_main.c:2:68: warning: unused parameter ‘c’ [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
_cgo_main.c:2:85: warning: unused parameter ‘ctxt’ [-Wunused-parameter]
 void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }
                                                                                     ^
_cgo_main.c: In function ‘_cgo_release_context’:
_cgo_main.c:4:41: warning: unused parameter ‘ctxt’ [-Wunused-parameter]
 void _cgo_release_context(__SIZE_TYPE__ ctxt) { }
                                         ^
_cgo_main.c: In function ‘_cgo_allocate’:
_cgo_main.c:6:26: warning: unused parameter ‘a’ [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                          ^
_cgo_main.c:6:33: warning: unused parameter ‘c’ [-Wunused-parameter]
 void _cgo_allocate(void *a, int c) { }
                                 ^
_cgo_main.c: In function ‘_cgo_panic’:
_cgo_main.c:7:23: warning: unused parameter ‘a’ [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                       ^
_cgo_main.c:7:30: warning: unused parameter ‘c’ [-Wunused-parameter]
 void _cgo_panic(void *a, int c) { }
                              ^
^C
therecipe commented 6 years ago

Mh, those are just warnings.

Is this maybe the same issue as in your first post?

runtime.main_main·f: relocation target main.main not defined
runtime.main_main·f: undefined: "main.main"

If yes, then you probably only need to create a dummy "func main(){}"

djahma commented 6 years ago

Well, that's the trick with Go plugins...they are like binaries but they don't sport a "main" function. When I add a dummy main(), go build generates the same errors, and qtdeploy treats the code like a bin and creates it even tough that qtexample bin doesn't do anything.

djahma commented 6 years ago

Could you copy paste that code and get a .so from it?

package main

import (
    "os"

    "github.com/therecipe/qt/core"
    "github.com/therecipe/qt/gui"
    "github.com/therecipe/qt/qml"
    "github.com/therecipe/qt/quickcontrols2"
)

func Init() {
    // Create application
    app := gui.NewQGuiApplication(len(os.Args), os.Args)

    // Enable high DPI scaling
    app.SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)

    // Use the material style for qml
    quickcontrols2.QQuickStyle_SetStyle("Fusion")

    // Create a QML application engine
    engine := qml.NewQQmlApplicationEngine(nil)

    // Load the main qml file
    engine.Load(core.NewQUrl3("qrc:/qml/main.qml", 0))

    // Execute app
    gui.QGuiApplication_Exec()
}
therecipe commented 6 years ago

Mh, no it compiles for a while. But then stops with a lot of issues like this:

Undefined symbols for architecture x86_64:
  "__cgoexp_77eb3915ded4_callbackQJSEngine_ChildEvent", referenced from:
      _callbackQJSEngine_ChildEvent in 000008.o
  "__cgoexp_77eb3915ded4_callbackQJSEngine_ConnectNotify", referenced from:
      _callbackQJSEngine_ConnectNotify in 000008.o
  "__cgoexp_77eb3915ded4_callbackQJSEngine_CustomEvent", referenced from:
      _callbackQJSEngine_CustomEvent in 000008.o
  "__cgoexp_77eb3915ded4_callbackQJSEngine_DeleteLater", referenced from:
      _callbackQJSEngine_DeleteLater in 000008.o
  "__cgoexp_77eb3915ded4_callbackQJSEngine_DestroyQJSEngine", referenced from:
      _callbackQJSEngine_DestroyQJSEngine in 000008.o
  "__cgoexp_77eb3915ded4_callbackQJSEngine_Destroyed", referenced from:
      _callbackQJSEngine_Destroyed in 000008.o
  "__cgoexp_77eb3915ded4_callbackQJSEngine_DisconnectNotify", referenced from:
      _callbackQJSEngine_DisconnectNotify in 000008.o
  "__cgoexp_77eb3915ded4_callbackQJSEngine_Event", referenced from:
      _callbackQJSEngine_Event in 000008.o

I'm also on macOS btw. These errors are probably caused because the "callback" function in Go are somehow not exported. Like this one: https://github.com/therecipe/qt/blob/master/qml/qml.go#L324-L331

Could it be that the buildmode plugin somehow uses the "//export" comment to export Go functions or something? (So that they can be used by a program consuming the plugin?)

djahma commented 6 years ago

Hmm...something interesting happened as I commented out each line, one by one. go build generated the .so library up to the point when I commented out the line where "engine" is declared, that is, when "github.com/therecipe/qt/qml" gets involved. I still had all the warnings and errors from above when running go build, plus this:

# qtexample
/usr/local/go/pkg/tool/linux_amd64/link: running g++ failed: exit status 1
/usr/bin/ld: $WORK/b001/exe/a.out.so: version node not found for symbol ��<��/y"!��g�&�,ɓe͊�\@~�*W��2  ��      ���i�Xs��tR@�����;Q.D|u#��tqh@��
                                                                                                                                                ըS�2    ��
飿�
   ըS�2 ��l�"�N�&�l�ngľ"�
                         �>c���0!��r��<�7�{�
                                            ���J_���"����1��p��er�k�! ���       ����Ug|�        �u��y(��&�:��=T9&���
�Lmi*��9��
          ըS�2  ��������.8��6Cl�"�N�&�l�ngĉ
[...]
                                                     ըS�2       ��l�"�N�&�l�ng�
/usr/bin/ld: failed to set dynamic section sizes: Bad value
collect2: error: ld returned 1 exit status

No idea how to decypher what symbol stdout is talking about, but I assume it's one in the qml package.

I don't understand the links between C and Go, but adding "//export" comment did not change the output.

Plugins are only supposed to work on Linux and Darwin at the moment, so it's fine.

therecipe commented 6 years ago

Mh, I get a similar issue like in https://github.com/therecipe/qt/issues/587#issuecomment-379895064 when compiling:

package main

import "C"

//export abc
func abc() {}

with go build -buildmode=plugin

/usr/local/go/pkg/tool/darwin_amd64/link: running clang failed: exit status 1
Undefined symbols for architecture x86_64:
  "__cgoexp_324fb94d1b32_abc", referenced from:
      _abc in 000000.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

So, I'm not quite sure. I guess the plugin support is still not really useful.

However, you should be able to use buildmode=c-shared instead of plugin if you just want to call an Init function to start your gui.

Or maybe "buildmode=shared" but I haven't tested that.

djahma commented 6 years ago

Nevermind, I'll return to RPC, gRPC seems to be the way to go. But thanks for looking into it.