mirror of
https://github.com/mappu/miqt.git
synced 2024-12-22 08:58:37 +00:00
genbindings: wip
This commit is contained in:
parent
559aca01b2
commit
7319683a3f
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
# scratch files
|
# scratch files
|
||||||
*.json
|
*.json
|
||||||
|
gen_*
|
||||||
|
|
||||||
# binaries
|
# binaries
|
||||||
miqt
|
miqt
|
||||||
|
@ -3,6 +3,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseHeader(inner []interface{}) (*parsedHeader, error) {
|
func parseHeader(inner []interface{}) (*parsedHeader, error) {
|
||||||
@ -42,7 +44,7 @@ func parseHeader(inner []interface{}) (*parsedHeader, error) {
|
|||||||
|
|
||||||
fmt.Printf("-> %q name=%q\n", kind, nodename)
|
fmt.Printf("-> %q name=%q\n", kind, nodename)
|
||||||
if classInner, ok := node["inner"].([]interface{}); ok {
|
if classInner, ok := node["inner"].([]interface{}); ok {
|
||||||
obj, err := processType(classInner)
|
obj, err := processType(classInner, nodename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -61,9 +63,11 @@ func parseHeader(inner []interface{}) (*parsedHeader, error) {
|
|||||||
return &ret, nil // done
|
return &ret, nil // done
|
||||||
}
|
}
|
||||||
|
|
||||||
func processType(inner []interface{}) (nativeClass, error) {
|
func processType(inner []interface{}, className string) (nativeClass, error) {
|
||||||
var ret nativeClass
|
var ret nativeClass
|
||||||
|
ret.className = className
|
||||||
|
|
||||||
|
nextMethod:
|
||||||
for _, node := range inner {
|
for _, node := range inner {
|
||||||
node, ok := node.(map[string]interface{})
|
node, ok := node.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -76,8 +80,69 @@ func processType(inner []interface{}) (nativeClass, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch kind {
|
switch kind {
|
||||||
|
case "CXXMethodDecl":
|
||||||
|
// Method
|
||||||
|
methodName, ok := node["name"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nativeClass{}, errors.New("method has no name")
|
||||||
|
}
|
||||||
|
|
||||||
|
var mm nativeMethod
|
||||||
|
mm.methodName = methodName
|
||||||
|
|
||||||
|
if typobj, ok := node["type"].(map[string]interface{}); ok {
|
||||||
|
if qualType, ok := typobj["qualType"].(string); ok {
|
||||||
|
// The qualType is the whole type of the method, including its parameter types
|
||||||
|
// If anything here is too complicated, skip the whole method
|
||||||
|
if strings.Contains(qualType, `::`) {
|
||||||
|
log.Printf("Skipping method %q with complex type %q", mm.methodName, qualType)
|
||||||
|
continue nextMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only want up to the first ( character
|
||||||
|
mm.returnType, _, _ = strings.Cut(qualType, `(`)
|
||||||
|
mm.returnType = strings.TrimSpace(mm.returnType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if methodInner, ok := node["inner"].([]interface{}); ok {
|
||||||
|
for _, methodObj := range methodInner {
|
||||||
|
methodObj, ok := methodObj.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nativeClass{}, errors.New("inner[] element not an object")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch methodObj["kind"] {
|
||||||
|
case "ParmVarDecl":
|
||||||
|
// Parameter variable
|
||||||
|
parmName, _ := methodObj["name"].(string) // n.b. may be unnamed
|
||||||
|
if parmName == "" {
|
||||||
|
parmName = fmt.Sprintf("param%d", len(mm.parameters)+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var parmType string
|
||||||
|
if typobj, ok := node["type"].(map[string]interface{}); ok {
|
||||||
|
if qualType, ok := typobj["qualType"].(string); ok {
|
||||||
|
parmType = qualType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mm.parameters = append(mm.parameters, nativeParameter{
|
||||||
|
name: parmName,
|
||||||
|
typ: parmType,
|
||||||
|
})
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Something else inside a declaration??
|
||||||
|
fmt.Printf("==> NOT IMPLEMENTED CXXMethodDecl->%q\n", kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.methods = append(ret.methods, mm)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fmt.Printf("==> %q\n", kind)
|
fmt.Printf("==> NOT IMPLEMENTED %q\n", kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,86 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func emitGo(src *parsedHeader) (string, error) {
|
func emitParametersCpp(params []nativeParameter) string {
|
||||||
return "", errors.New("TODO")
|
tmp := make([]string, 0, len(params))
|
||||||
|
for _, p := range params {
|
||||||
|
tmp = append(tmp, p.name+" "+p.typ)
|
||||||
|
}
|
||||||
|
return strings.Join(tmp, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitParametersGo(params []nativeParameter) string {
|
||||||
|
tmp := make([]string, 0, len(params))
|
||||||
|
for _, p := range params {
|
||||||
|
tmp = append(tmp, p.typ+" "+p.name)
|
||||||
|
}
|
||||||
|
return strings.Join(tmp, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitBindingHeader(src *parsedHeader, filename string) (string, error) {
|
||||||
|
ret := strings.Builder{}
|
||||||
|
|
||||||
|
includeGuard := strings.ToUpper(strings.Replace(filename, `.`, `_`, -1)) + "_H"
|
||||||
|
|
||||||
|
ret.WriteString(`#ifndef ` + includeGuard + `
|
||||||
|
#define ` + includeGuard + `
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
`)
|
||||||
|
|
||||||
|
for _, c := range src.classes {
|
||||||
|
ret.WriteString(`typedef void* P` + c.className + ";\n\n")
|
||||||
|
|
||||||
|
for i, ctor := range c.ctors {
|
||||||
|
suffix := ""
|
||||||
|
if i > 0 {
|
||||||
|
suffix = fmt.Sprintf("%d", i+1)
|
||||||
|
}
|
||||||
|
// TODO fixup parameters
|
||||||
|
ret.WriteString(fmt.Sprintf("P%s %s_new%s(%s);\n", c.className, suffix, emitParametersCpp(ctor.parameters)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range c.methods {
|
||||||
|
ret.WriteString(fmt.Sprintf("%s %s_%s(%s);\n", m.returnType, c.className, m.methodName, emitParametersCpp(m.parameters)))
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.WriteString(`
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern C */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
`)
|
||||||
|
return ret.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitBindingCpp(src *parsedHeader) (string, error) {
|
||||||
|
ret := strings.Builder{}
|
||||||
|
return ret.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitGo(src *parsedHeader) (string, error) {
|
||||||
|
|
||||||
|
ret := strings.Builder{}
|
||||||
|
ret.WriteString("package miqt\n\n")
|
||||||
|
|
||||||
|
// CGO block
|
||||||
|
|
||||||
|
// Pure-Go block
|
||||||
|
for _, c := range src.classes {
|
||||||
|
_ = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret.String(), nil
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
type nativeParameter struct {
|
||||||
|
name string
|
||||||
|
typ string
|
||||||
|
}
|
||||||
|
|
||||||
type nativeProperty struct {
|
type nativeProperty struct {
|
||||||
propertyName string
|
propertyName string
|
||||||
propertyType string
|
propertyType string
|
||||||
@ -9,11 +14,12 @@ type nativeProperty struct {
|
|||||||
type nativeMethod struct {
|
type nativeMethod struct {
|
||||||
methodName string
|
methodName string
|
||||||
returnType string
|
returnType string
|
||||||
parameters []string
|
parameters []nativeParameter
|
||||||
}
|
}
|
||||||
|
|
||||||
type nativeClass struct {
|
type nativeClass struct {
|
||||||
className string
|
className string
|
||||||
|
ctors []nativeMethod // only use the parameters
|
||||||
extends []string
|
extends []string
|
||||||
methods []nativeMethod
|
methods []nativeMethod
|
||||||
props []nativeProperty
|
props []nativeProperty
|
||||||
|
@ -3,8 +3,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ func main() {
|
|||||||
clang := flag.String("clang", "clang", "Custom path to clang")
|
clang := flag.String("clang", "clang", "Custom path to clang")
|
||||||
inputHeader := flag.String("inputHeader", `/usr/include/x86_64-linux-gnu/qt5/QtWidgets/qpushbutton.h`, "Input file")
|
inputHeader := flag.String("inputHeader", `/usr/include/x86_64-linux-gnu/qt5/QtWidgets/qpushbutton.h`, "Input file")
|
||||||
cflags := flag.String("cflags", `-DQT_WIDGETS_LIB -I/usr/include/x86_64-linux-gnu/qt5/QtWidgets -I/usr/include/x86_64-linux-gnu/qt5 -I/usr/include/x86_64-linux-gnu/qt5/QtCore -DQT_GUI_LIB -I/usr/include/x86_64-linux-gnu/qt5/QtGui -DQT_CORE_LIB`, "Cflags to pass to clang (e.g. `pkg-config --cflags Qt5Widgets`)")
|
cflags := flag.String("cflags", `-DQT_WIDGETS_LIB -I/usr/include/x86_64-linux-gnu/qt5/QtWidgets -I/usr/include/x86_64-linux-gnu/qt5 -I/usr/include/x86_64-linux-gnu/qt5/QtCore -DQT_GUI_LIB -I/usr/include/x86_64-linux-gnu/qt5/QtGui -DQT_CORE_LIB`, "Cflags to pass to clang (e.g. `pkg-config --cflags Qt5Widgets`)")
|
||||||
|
outDir := flag.String("outdir", "..", "Output directory for generated gen_** files")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -29,13 +31,40 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit Go code from the intermediate format
|
// Emit 3 code files from the intermediate format
|
||||||
|
outputName := filepath.Join(*outDir, "gen_"+strings.TrimSuffix(filepath.Base(*inputHeader), `.h`))
|
||||||
|
|
||||||
goSrc, err := emitGo(parsed)
|
goSrc, err := emitGo(parsed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(goSrc)
|
err = ioutil.WriteFile(outputName+".go", []byte(goSrc), 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("Processing %q completed", inputHeader)
|
bindingCppSrc, err := emitBindingCpp(parsed)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(outputName+".cpp", []byte(bindingCppSrc), 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bindingHSrc, err := emitBindingHeader(parsed, filepath.Base(*inputHeader))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(outputName+".h", []byte(bindingHSrc), 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done
|
||||||
|
|
||||||
|
log.Printf("Processing %q completed", *inputHeader)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user