// Copyright © 2019 - 2020 Oscar Campos // Copyright © 2017 - William Edwards // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License package main import ( "fmt" "go/parser" "go/token" "os" "os/exec" "path/filepath" "runtime" "strings" "text/template" "git.alfi.li/gamelang/gdnative-go/gdnative" ) // RegistryData structure is a container for registrable classes // that is passed to the gdnative_wrapper.go.tmpl template for filling type RegistryData struct { Package string Classes map[string]gdnative.Registrable } // GDNativeInit construct and returns a SetNativeInitScript call func (rd RegistryData) GDNativeInit() string { initFunctions := []string{} for className := range rd.Classes { initFunctions = append(initFunctions, fmt.Sprintf("nativeScriptInit%s", className)) } return fmt.Sprintf("gdnative.SetNativeScriptInit(%s)", strings.Join(initFunctions, ", ")) } func (cmd *generateCmd) Run(ctx *context) error { fset := token.NewFileSet() packages, parseErr := parser.ParseDir(fset, ctx.Path, cmd.filter, parser.ParseComments) if parseErr != nil { return fmt.Errorf("could not parse Go files at %s: %w", ctx.Path, parseErr) } tplPath, pathErr := getTemplatePath("gdnative_wrapper.go") if pathErr != nil { return fmt.Errorf("could not get GDNative template: %w", pathErr) } for pkg, p := range packages { data := RegistryData{Package: pkg, Classes: map[string]gdnative.Registrable{}} registrable := gdnative.LookupRegistrableTypeDeclarations(p) if len(registrable) == 0 { fmt.Printf("not found any registrable sources on %s", ctx.Path) return nil } for className, classData := range registrable { data.Classes[className] = classData } // create a template from the template file tpl, tplErr := template.ParseFiles(tplPath) if tplErr != nil { return tplErr } outputFileName := fmt.Sprintf("%s_registrable.gen.go", pkg) outputFilePath := filepath.Join(ctx.Path, outputFileName) file, fileErr := os.Create(outputFilePath) if fileErr != nil { return fmt.Errorf("can not open output file %s for writing: %w", outputFilePath, fileErr) } execErr := tpl.Execute(file, data) if execErr != nil { return execErr } return format(outputFilePath) } return nil } // get the template path func getTemplatePath(templateType string) (string, error) { currentPath, err := getCurrentPath() if err != nil { return "", err } return filepath.Join(currentPath, "..", "..", "generate", "templates", templateType+".tmpl"), nil } // get the current in execution file path on disk func getCurrentPath() (string, error) { _, filename, _, ok := runtime.Caller(0) if !ok { return "", fmt.Errorf("could not get current file execution path") } return filename, nil } func (cmd *generateCmd) filter(info os.FileInfo) bool { if info.IsDir() { return false } length := len(info.Name()) if length > 7 && info.Name()[length-7:length-2] == ".gen." { return false } return true } // formats the given path with gofmt func format(filepath string) error { fmt.Println("gofmt", "-w", filepath) cmd := exec.Command("gofmt", "-w", filepath) return cmd.Run() }