gml/internal/build/generate.go
2019-01-11 04:01:14 +01:00

338 lines
8.3 KiB
Go

/*
* GML - Go QML
* Copyright (c) 2019 Roland Singer [roland.singer@deserbit.com]
* Copyright (c) 2019 Sebastian Borchers [sebastian@deserbit.com]
*/
package build
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
)
const (
genGoFilename = "gml_gen.go"
)
// TODO: make concurrent with multiple goroutines.
func generate(ctx *Context) (err error) {
gt, err := parseDirRecursive(ctx.SourceDir)
if err != nil {
return
}
err = generateCIncludeAllHeaderFile(gt, ctx.CGenIncludeDir)
if err != nil {
return
}
err = generateDummyFiles(ctx)
if err != nil {
return
}
for _, gp := range gt.Packages {
err = generateGoFile(gp)
if err != nil {
return
}
err = generateCHeaderFile(gp, ctx.CGenDir)
if err != nil {
return
}
err = generateCPPHeaderFile(gp, ctx.CPPGenDir)
if err != nil {
return
}
err = generateCPPSourceFile(gp, ctx.CPPGenDir)
if err != nil {
return
}
}
return
}
// TODO: only create dummies if the direcotories are empty.
// Create dummy files, because otherwise make fill fail if there are no sources files present.
// QMake is configured with a *.cpp & *.h
func generateDummyFiles(ctx *Context) (err error) {
err = ioutil.WriteFile(filepath.Join(ctx.CGenDir, "_dummy.h"), []byte("// Dummy File"), 0755)
if err != nil {
return
}
err = ioutil.WriteFile(filepath.Join(ctx.CPPGenDir, "_dummy.h"), []byte("// Dummy File"), 0755)
if err != nil {
return
}
err = ioutil.WriteFile(filepath.Join(ctx.CPPGenDir, "_dummy.cpp"), []byte("// Dummy File"), 0755)
return
}
// This shoult be in a separate directory, so no unnecessary files are in the global include dir.
func generateCIncludeAllHeaderFile(gt *genTargets, genDir string) (err error) {
filename := filepath.Join(genDir, "gml_gen.h")
// Create the file.
f, err := os.Create(filename)
if err != nil {
return
}
defer func() {
derr := f.Close()
if derr != nil && err == nil {
err = derr
}
}()
f.WriteString("// This file is auto-generated by gml.\n")
f.WriteString("#ifndef GML_GEN_C_INCLUDE_H\n")
f.WriteString("#define GML_GEN_C_INCLUDE_H\n\n")
for _, gp := range gt.Packages {
f.WriteString("#include \"../" + gp.Name + ".h\"\n")
}
f.WriteString("\n#endif\n")
return
}
func generateGoFile(gp *genPackage) (err error) {
filename := filepath.Join(gp.Dir, genGoFilename)
// Create the file.
f, err := os.Create(filename)
if err != nil {
return
}
defer func() {
derr := f.Close()
if derr != nil && err == nil {
err = derr
}
}()
// Write the header.
f.WriteString("// This file is auto-generated by gml.\n")
f.WriteString("package " + gp.Name + "\n\n")
f.WriteString("// #cgo pkg-config: Qt5Core Qt5Qml Qt5Quick\n")
f.WriteString("// #cgo LDFLAGS: -lstdc++\n")
f.WriteString("// #include <gml_gen.h>\n")
f.WriteString("import \"C\"\n")
f.WriteString("import (\n")
f.WriteString(" \"unsafe\"\n")
f.WriteString(" \"runtime\"\n")
f.WriteString(")\n\n")
for _, st := range gp.Structs {
base := "gml_gen_" + st.Name
// TODO: Error?
// GMLInit().
f.WriteString("func (_v *" + st.Name + ") GMLInit() {\n")
f.WriteString(" _v.GMLObject_SetPointer(unsafe.Pointer(C." + base + "_new()))\n")
f.WriteString(" runtime.SetFinalizer(_v, func(_v *" + st.Name + ") {\n")
f.WriteString(" C." + base + "_free((C." + base + ")(_v.GMLObject_Pointer()))\n")
f.WriteString(" })\n")
f.WriteString("}\n\n")
// Add all signals.
for _, s := range st.Signals {
f.WriteString("func (_v *" + st.Name + ") " + s.Name + "(")
// Add the parameters.
for i, p := range s.Params {
if i != 0 {
f.WriteString(", ")
}
f.WriteString(p.Name + " " + p.Type)
}
f.WriteString(") {\n")
f.WriteString(" _ptr := (C." + base + ")(_v.GMLObject_Pointer())\n")
f.WriteString(" C." + base + "_" + s.Name + "(_ptr)\n") // TODO: params + return type + error
f.WriteString("}\n\n")
}
}
return
}
// TODO: add package prefix!
func generateCHeaderFile(gp *genPackage, genDir string) (err error) {
filename := filepath.Join(genDir, gp.Name+".h")
// Create the file.
f, err := os.Create(filename)
if err != nil {
return
}
defer func() {
derr := f.Close()
if derr != nil && err == nil {
err = derr
}
}()
// Write the header.
f.WriteString("// This file is auto-generated by gml.\n")
f.WriteString("#ifndef GML_GEN_C_" + strings.ToUpper(gp.Name) + "_H\n")
f.WriteString("#define GML_GEN_C_" + strings.ToUpper(gp.Name) + "_H\n\n")
f.WriteString("#ifdef __cplusplus\n")
f.WriteString("extern \"C\" {\n")
f.WriteString("#endif\n\n")
for _, st := range gp.Structs {
f.WriteString("//###\n")
f.WriteString("//### " + st.Name + "\n")
f.WriteString("//###\n\n")
base := "gml_gen_" + st.Name
f.WriteString("typedef void* " + base + ";\n\n")
// Constructor & destructor.
f.WriteString(base + " " + base + "_new();\n")
f.WriteString("void " + base + "_free(" + base + ");\n\n")
// Add all signals.
if len(st.Signals) > 0 {
for _, s := range st.Signals {
f.WriteString("void " + base + "_" + s.Name + "(" + base + " _v") // TODO: return value.
for _, p := range s.Params {
f.WriteString(p.Type + " " + p.Name) // TODO: to C types.
}
f.WriteString(");\n")
}
}
f.WriteString("\n")
}
f.WriteString("#ifdef __cplusplus\n")
f.WriteString("}\n")
f.WriteString("#endif\n\n")
f.WriteString("#endif\n")
return
}
// TODO: add package prefix!
func generateCPPSourceFile(gp *genPackage, genDir string) (err error) {
filename := filepath.Join(genDir, gp.Name+".cpp")
// Create the file.
f, err := os.Create(filename)
if err != nil {
return
}
defer func() {
derr := f.Close()
if derr != nil && err == nil {
err = derr
}
}()
// Write the header.
f.WriteString("// This file is auto-generated by gml.\n")
f.WriteString("#include \"" + gp.Name + ".h\"\n\n")
for _, st := range gp.Structs {
f.WriteString("//###\n")
f.WriteString("//### " + st.Name + "\n")
f.WriteString("//###\n\n")
base := "gml_gen_" + st.Name
cppbase := "GMLGen" + st.Name
// Constructor. TODO: try catch
f.WriteString(base + " " + base + "_new() {\n")
f.WriteString(" auto _vv = new " + cppbase + "();\n")
f.WriteString(" return (void*)_vv;\n")
f.WriteString("}\n\n")
// Destructor. TODO: try catch
f.WriteString("void " + base + "_free(" + base + " _v) {\n")
f.WriteString(" if (_v == NULL) return;\n")
f.WriteString(" auto _vv = (" + cppbase + "*)_v;\n")
f.WriteString(" delete _vv;\n")
f.WriteString(" _v = NULL;\n")
f.WriteString("}\n\n")
// Add all signals.
if len(st.Signals) > 0 {
for _, s := range st.Signals {
f.WriteString("void " + base + "_" + s.Name + "(" + base + " _v") // TODO: return value.
for _, p := range s.Params {
f.WriteString(p.Type + " " + p.Name) // TODO: to C types.
}
f.WriteString(") {\n")
f.WriteString(" auto _vv = (" + cppbase + "*)_v;\n")
f.WriteString(" emit _vv->" + s.Name + "();\n") // TODO: params
f.WriteString("}\n")
}
}
f.WriteString("\n")
}
return
}
// TODO: add package prefix!
func generateCPPHeaderFile(gp *genPackage, genDir string) (err error) {
filename := filepath.Join(genDir, gp.Name+".h")
// Create the file.
f, err := os.Create(filename)
if err != nil {
return
}
defer func() {
derr := f.Close()
if derr != nil && err == nil {
err = derr
}
}()
// Write the header.
f.WriteString("// This file is auto-generated by gml.\n")
f.WriteString("#ifndef GML_GEN_CPP_" + strings.ToUpper(gp.Name) + "_H\n")
f.WriteString("#define GML_GEN_CPP_" + strings.ToUpper(gp.Name) + "_H\n\n")
f.WriteString("#include \"../gen_c/" + gp.Name + ".h\"\n")
f.WriteString("#include <QObject>\n\n")
for _, st := range gp.Structs {
f.WriteString("//###\n")
f.WriteString("//### " + st.Name + "\n")
f.WriteString("//###\n\n")
f.WriteString("class GMLGen" + st.Name + " : public QObject\n")
f.WriteString("{\n")
f.WriteString(" Q_OBJECT\n")
// Add all signals.
if len(st.Signals) > 0 {
f.WriteString("signals:\n")
for _, s := range st.Signals {
f.WriteString(" void " + s.Name + "(") // TODO: return value.
for i, p := range s.Params {
if i != 0 {
f.WriteString(", ")
}
f.WriteString(p.Type + " " + p.Name) // TODO: to C++ types.
}
f.WriteString(");\n")
}
}
f.WriteString("};\n\n")
}
f.WriteString("#endif\n")
return
}