cgo-less interop related changes

This commit is contained in:
therecipe 2020-08-17 18:37:48 +02:00
parent 550ceb9a59
commit 494d2f8c1e
9 changed files with 90 additions and 27 deletions

1
.gitignore vendored
View file

@ -9,6 +9,7 @@
*.vagrant *.vagrant
.DS_Store .DS_Store
Mfile* Mfile*
qtbox
*/*-minimal.* */*-minimal.*
*/*.o */*.o

View file

@ -5,11 +5,11 @@ Introduction
[Go](https://en.wikipedia.org/wiki/Go_(programming_language)), also known as Golang, is a programming language designed at Google. [Go](https://en.wikipedia.org/wiki/Go_(programming_language)), also known as Golang, is a programming language designed at Google.
[therecipe/qt](https://github.com/therecipe/qt) allows you to write Qt applications entirely in Go or JavaScript. [therecipe/qt](https://github.com/therecipe/qt) allows you to write Qt applications entirely in Go, [JavaScript/TypeScript](https://github.com/therecipe/entry), [Dart/Flutter](https://github.com/therecipe/flutter), [Haxe](https://github.com/therecipe/haxe) and [Swift](https://github.com/therecipe/swift)
Beside the language bindings provided, `therecipe/qt` also greatly simplifies the deployment of Qt applications to various software and hardware platforms. Beside the language bindings provided, `therecipe/qt` also greatly simplifies the deployment of Qt applications to various software and hardware platforms.
At the time of writing, almost all Qt functions and classes are accessible from Go and JavaScript, and you should be able to find everything you need to build fully featured Qt applications. At the time of writing, almost all Qt functions and classes are accessible, and you should be able to find everything you need to build fully featured Qt applications.
Impressions Impressions
----------- -----------
@ -23,6 +23,22 @@ Installation
The following instructions assume that you already installed [Go](https://golang.org/dl/) and [Git](https://git-scm.com/downloads) The following instructions assume that you already installed [Go](https://golang.org/dl/) and [Git](https://git-scm.com/downloads)
#### (Experimental) cgo-less version (try this first, if you are new and want to test this binding)
##### Windows
```powershell
go get -ldflags="-w" github.com/therecipe/examples/basic/widgets && for /f %v in ('go env GOPATH') do %v\bin\widgets.exe
```
##### macOS/Linux
```bash
go get -ldflags="-w" github.com/therecipe/examples/basic/widgets && $(go env GOPATH)/bin/widgets
```
#### Default version
##### Windows [(more info)](https://github.com/therecipe/qt/wiki/Installation-on-Windows) ##### Windows [(more info)](https://github.com/therecipe/qt/wiki/Installation-on-Windows)
```powershell ```powershell

View file

@ -95,6 +95,23 @@ func goFunctionBody(function *parser.Function) string {
default: default:
//TODO:
if class.IsSubClassOf("QCoreApplication") {
if function.Meta == parser.CONSTRUCTOR {
return fmt.Sprintf("\ngow.InitProcess()\nreturn New%vFromPointer(%vQCoreApplication_Instance().Pointer())", class.Name, func() string {
if goModule(class.Module) != "core" {
return "core."
}
return ""
}())
}
if function.Name == "exec" {
return "\ngow.Exec()\nreturn 0"
}
}
//
var input []string var input []string
for _, p := range function.Parameters { for _, p := range function.Parameters {
input = append(input, parser.CleanName(p.Name, p.Value)) input = append(input, parser.CleanName(p.Name, p.Value))
@ -110,7 +127,9 @@ func goFunctionBody(function *parser.Function) string {
ret_pre = "return " ret_pre = "return "
ret_suf = fmt.Sprintf(".(%v)", out) ret_suf = fmt.Sprintf(".(%v)", out)
if strings.Contains(ret_suf, "__") && !strings.Contains(ret_suf, "[") { //TODO: support for slices and maps containing enums //TODO: is there some better way ?
//TODO: support for slices and maps as well
if (strings.Contains(ret_suf, "__") || strings.HasPrefix(ret_suf, ".(int") || strings.HasPrefix(ret_suf, ".(uint")) && !strings.Contains(ret_suf, "[") {
ret_pre += out + "(" ret_pre += out + "("
ret_suf = ".(float64))" ret_suf = ".(float64))"
} }

View file

@ -1456,7 +1456,7 @@ import "C"
var dartInput []string var dartInput []string
fmt.Fprint(bb, "import (\n") fmt.Fprint(bb, "import (\n")
for _, m := range append(parser.GetLibs(), "qt", "strings", "unsafe", "log", "runtime", "fmt", "errors", "js", "time", "hex", "reflect", "math", "sync", "strconv", "internal") { for _, m := range append(parser.GetLibs(), "qt", "strings", "unsafe", "log", "runtime", "fmt", "errors", "js", "time", "hex", "reflect", "math", "sync", "strconv", "internal", "gow") {
mlow := strings.ToLower(m) mlow := strings.ToLower(m)
if strings.Contains(inputString, fmt.Sprintf(" %v.", mlow)) || if strings.Contains(inputString, fmt.Sprintf(" %v.", mlow)) ||
strings.Contains(inputString, fmt.Sprintf("\t%v.", mlow)) || strings.Contains(inputString, fmt.Sprintf("\t%v.", mlow)) ||
@ -1480,6 +1480,9 @@ import "C"
case "internal": case "internal":
fmt.Fprintln(bb, "\"github.com/therecipe/qt/internal\"") fmt.Fprintln(bb, "\"github.com/therecipe/qt/internal\"")
case "gow":
fmt.Fprintln(bb, "\"github.com/therecipe/qt/interop/gow\"")
case "js": case "js":
if parser.UseWasm() { if parser.UseWasm() {
fmt.Fprintln(bb, "\"syscall/js\"") fmt.Fprintln(bb, "\"syscall/js\"")

View file

@ -289,7 +289,7 @@ func Minimal(path, target, tags string, skipSetup bool) {
if utils.QT_STATIC() { if utils.QT_STATIC() {
exportClass(parser.State.ClassMap["QSvgWidget"], files) exportClass(parser.State.ClassMap["QSvgWidget"], files)
} }
if utils.QT_FELGO() { if utils.QT_FELGO() || utils.QT_GEN_GO_WRAPPER() {
exportClass(parser.State.ClassMap["QCoreApplication"], files) exportClass(parser.State.ClassMap["QCoreApplication"], files)
exportFunction(parser.State.ClassMap["QCoreApplication"].GetFunction("instance"), files) exportFunction(parser.State.ClassMap["QCoreApplication"].GetFunction("instance"), files)
} }

View file

@ -55,7 +55,7 @@ func Check(target string, docker, vagrant bool) {
{"QT_QMAKE_DIR", utils.QT_QMAKE_DIR()}, {"QT_QMAKE_DIR", utils.QT_QMAKE_DIR()},
{"QT_WEBKIT", fmt.Sprint(utils.QT_WEBKIT())}, {"QT_WEBKIT", fmt.Sprint(utils.QT_WEBKIT())},
{"QT_STATIC", fmt.Sprint(utils.QT_STATIC())}, {"QT_STATIC", fmt.Sprint(utils.QT_STATIC())},
{"QT_GEN_TSD", fmt.Sprint(utils.QT_GEN_TSD())}, {"QT_GEN_GO", fmt.Sprint(utils.QT_GEN_GO_WRAPPER())},
{"QT_GEN_OPENGL", fmt.Sprint(utils.QT_GEN_OPENGL())}, {"QT_GEN_OPENGL", fmt.Sprint(utils.QT_GEN_OPENGL())},
{"QT_GEN_QUICK_EXTRAS", fmt.Sprint(utils.QT_GEN_QUICK_EXTRAS())}, {"QT_GEN_QUICK_EXTRAS", fmt.Sprint(utils.QT_GEN_QUICK_EXTRAS())},
{"QT_RESOURCES_BIG", fmt.Sprint(utils.QT_RESOURCES_BIG())}, {"QT_RESOURCES_BIG", fmt.Sprint(utils.QT_RESOURCES_BIG())},

View file

@ -36,7 +36,7 @@ func Generate(target string, docker, vagrant bool) {
mode = "stub" mode = "stub"
} }
if target == "windows" && runtime.GOOS == target { if target == "windows" && runtime.GOOS == target && os.Getenv("QT_DEBUG_CONSOLE") != "false" {
os.Setenv("QT_DEBUG_CONSOLE", "true") os.Setenv("QT_DEBUG_CONSOLE", "true")
} }
@ -53,8 +53,7 @@ func Generate(target string, docker, vagrant bool) {
} }
utils.Log.Infof("generating %v qt/%v%v", mode, strings.ToLower(module), license) utils.Log.Infof("generating %v qt/%v%v", mode, strings.ToLower(module), license)
if target == runtime.GOOS || utils.QT_FAT() || (mode == "full" && (target == "js" || target == "wasm")) || if target == runtime.GOOS || utils.QT_FAT() || (mode == "full" && (target == "js" || target == "wasm")) { //TODO: REVIEW
(utils.QT_STATIC() || utils.QT_MXE_STATIC()) && utils.QT_DOCKER() { //TODO: REVIEW
templater.GenModule(module, target, templater.NONE) templater.GenModule(module, target, templater.NONE)
} else { } else {
templater.CgoTemplate(module, "", target, templater.MINIMAL, "", "") //TODO: collect errors templater.CgoTemplate(module, "", target, templater.MINIMAL, "", "") //TODO: collect errors

View file

@ -15,7 +15,7 @@ jobs:
template: docker_showcase_template.yml template: docker_showcase_template.yml
parameters: parameters:
tag: box tag: box
# dep: windows_64_static dep: windows_64_shared_wine
- -
template: docker_windows_ci_template.yml template: docker_windows_ci_template.yml
parameters: parameters:

View file

@ -62,13 +62,15 @@ func handleCallback(message string) string {
var msg []interface{} var msg []interface{}
json.Unmarshal([]byte(message), &msg) json.Unmarshal([]byte(message), &msg)
meth := reflect.ValueOf(callbackTable[uintptr(msg[0].(float64))][msg[1].(string)])
rv := make([]reflect.Value, len(msg)-2) rv := make([]reflect.Value, len(msg)-2)
for i, v := range convertList(msg[2:]) { for i, v := range convertList(msg[2:]) {
rv[i] = reflect.ValueOf(v) rv[i] = reflect.ValueOf(v).Convert(meth.Type().In(i))
} }
var output []byte var output []byte
if ret := reflect.ValueOf(callbackTable[uintptr(msg[0].(float64))][msg[1].(string)]).Call(rv); len(ret) > 0 { if ret := meth.Call(rv); len(ret) > 0 {
output, _ = json.Marshal(convertToJson(ret[0].Interface())) output, _ = json.Marshal(convertToJson(ret[0].Interface()))
} }
@ -154,17 +156,25 @@ func convertToJson(i interface{}) interface{} {
return convertMapToJson(i.(map[string]interface{})) return convertMapToJson(i.(map[string]interface{}))
case reflect.Slice: case reflect.Slice:
return convertListToJson(i.([]interface{})) switch i.(type) {
//TODO:
case []string:
case []uint, []uint8, []uint16, []uint32, []uint64:
case []int, []int8, []int16, []int32, []int64:
//
//case []*qml.QQmlError:
default:
return convertListToJson(i.([]interface{}))
}
case reflect.Ptr: case reflect.Ptr:
return map[string]interface{}{ return map[string]interface{}{
"___pointer": uintptr(reflect.ValueOf(i).MethodByName("Pointer").Call(nil)[0].Interface().(unsafe.Pointer)), "___pointer": uintptr(reflect.ValueOf(i).MethodByName("Pointer").Call(nil)[0].Interface().(unsafe.Pointer)),
"___className": reflect.ValueOf(i).MethodByName("ClassNameInternalF").Call(nil)[0].Interface(), "___className": reflect.ValueOf(i).MethodByName("ClassNameInternalF").Call(nil)[0].Interface(),
} }
default:
return i
} }
return i
} }
var inited = false var inited = false
@ -222,8 +232,13 @@ var Config = &InteropServerConfig{
"", "",
} }
var (
proc *exec.Cmd
stderr io.ReadCloser
)
// TODO: NewQApplication // TODO: NewQApplication
func InitProcess() (*exec.Cmd, io.ReadCloser) { func InitProcess() {
var runPath string var runPath string
@ -258,7 +273,11 @@ func InitProcess() (*exec.Cmd, io.ReadCloser) {
println("final qtbox location:", runPath) println("final qtbox location:", runPath)
if _, err := os.Stat(runPath); err == nil && Config.Override || err != nil { dst := filepath.Dir(runPath)
_, err := os.Stat(runPath)
_, errF := os.Stat(filepath.Join(dst, "qtbox"))
if Config.Override || (err != nil && errF != nil) {
var copyWithProgress = func(w io.Writer, r io.Reader, callback func(off int64)) error { var copyWithProgress = func(w io.Writer, r io.Reader, callback func(off int64)) error {
tee := io.TeeReader(r, w) tee := io.TeeReader(r, w)
@ -306,16 +325,21 @@ func InitProcess() (*exec.Cmd, io.ReadCloser) {
fw.Close() fw.Close()
} else { } else {
dst := filepath.Dir(runPath)
for _, f := range r.File { for _, f := range r.File {
//TODO: pack runtimes with correct name
fns := strings.Split(f.Name, "/")
fns[0] = "qtbox"
f.Name = strings.Join(fns, "/")
//
if f.FileInfo().IsDir() { if f.FileInfo().IsDir() {
os.MkdirAll(filepath.Join(dst, f.Name), f.Mode()) os.MkdirAll(filepath.Join(dst, f.Name), f.Mode())
continue continue
} }
dn, fn := filepath.Split(f.Name) dn, fn := filepath.Split(f.Name)
if strings.HasPrefix(fn, "full") { if strings.HasPrefix(fn, "full") { //TODO: pack runtimes with correct name
fn = filepath.Join(dn, "qtbox"+ending) fn = filepath.Join(dn, "qtbox"+ending)
} else { } else {
fn = f.Name fn = f.Name
@ -337,8 +361,6 @@ func InitProcess() (*exec.Cmd, io.ReadCloser) {
fr.Close() fr.Close()
} }
os.Rename(filepath.Join(dst, r.File[0].Name), filepath.Join(dst, "qtbox"))
runPath = filepath.Join(dst, "qtbox", "qtbox"+ending) runPath = filepath.Join(dst, "qtbox", "qtbox"+ending)
} }
} }
@ -355,13 +377,16 @@ func InitProcess() (*exec.Cmd, io.ReadCloser) {
println(err.Error()) println(err.Error())
} }
process.Start() process.Start()
time.Sleep(3 * time.Second) //TODO: time.Sleep(3 * time.Second) //TODO:
return process, rc
proc = process
stderr = rc
} }
// TODO: QApplication_Exec // TODO: QApplication_Exec
func Exec(p *exec.Cmd, rc io.ReadCloser) { func Exec() {
scanner := bufio.NewScanner(rc) scanner := bufio.NewScanner(stderr)
go func() { go func() {
for scanner.Scan() { for scanner.Scan() {
@ -376,5 +401,5 @@ func Exec(p *exec.Cmd, rc io.ReadCloser) {
} }
}() }()
p.Wait() proc.Wait()
} }