generate.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Package gdnative is responsible for parsing and generating binding code for
  2. // Go.
  3. package gdnative
  4. import (
  5. "encoding/json"
  6. "errors"
  7. "io/ioutil"
  8. "log"
  9. "os"
  10. "text/template"
  11. )
  12. // View is a structure that holds the api struct, so it can be used inside
  13. // our template.
  14. type View struct {
  15. API API
  16. StructType string
  17. }
  18. // NotLastElement is a function we use inside the template to test whether or
  19. // not the given element is the last in the slice or not. This is so we can
  20. // correctly insert commas for argument lists.
  21. func (v View) NotLastElement(n int, slice [][]string) bool {
  22. return n != (len(slice) - 1)
  23. }
  24. // NotVoid checks to see if the return string is void or not. This is used inside
  25. // our template so we can determine if we need to use the `return` keyword in
  26. // the function body.
  27. func (v View) NotVoid(ret string) bool {
  28. return ret != "void"
  29. }
  30. // HasArgs is a function we use inside the template to test whether or not the
  31. // function has arguments. This is so we can determine if we need to place a
  32. // comma.
  33. func (v View) HasArgs(args [][]string) bool {
  34. return len(args) != 0
  35. }
  36. // Generate generates the bindings from the JSON definition
  37. func Generate() {
  38. // Get the API Path so we can locate the godot api JSON.
  39. apiPath := os.Getenv("API_PATH")
  40. if apiPath == "" {
  41. panic("$API_PATH is not defined.")
  42. }
  43. packagePath := apiPath
  44. // Create a structure for our template view. This will contain all of
  45. // the data we need to construct our binding methods.
  46. var view View
  47. // Unmarshal the JSON into our struct.
  48. apis := Parse(packagePath)
  49. // Add the core API to our view first
  50. view.API = apis.Core
  51. view.StructType = "core"
  52. // Generate the C bindings
  53. log.Println("Generating", view.StructType, "C headers...")
  54. WriteTemplate(
  55. packagePath+"/cmd/generate/templates/gdnative.h.tmpl",
  56. packagePath+"/gdnative/gdnative.gen.h",
  57. view,
  58. )
  59. log.Println("Generating", view.StructType, "C bindings...")
  60. WriteTemplate(
  61. packagePath+"/cmd/generate/templates/gdnative.c.tmpl",
  62. packagePath+"/gdnative/gdnative.gen.c",
  63. view,
  64. )
  65. // Loop through all of our extensions and generate the bindings for those.
  66. for _, api := range apis.Extensions {
  67. view.API = api
  68. view.StructType = "ext_" + api.Name
  69. log.Println("Generating", view.StructType, "C headers...")
  70. WriteTemplate(
  71. packagePath+"/cmd/generate/templates/gdnative.h.tmpl",
  72. packagePath+"/gdnative/"+api.Name+".gen.h",
  73. view,
  74. )
  75. log.Println("Generating", view.StructType, "C bindings...")
  76. WriteTemplate(
  77. packagePath+"/cmd/generate/templates/gdnative.c.tmpl",
  78. packagePath+"/gdnative/"+api.Name+".gen.c",
  79. view,
  80. )
  81. }
  82. }
  83. // Parse parses the given package path and returns go APIs wrapping Godot C gdnative API
  84. func Parse(packagePath string) APIs {
  85. // Open the gdnative_api.json file that defines the GDNative API.
  86. body, err := ioutil.ReadFile(packagePath + "/godot_headers/gdnative_api.json")
  87. if err != nil {
  88. panic(err)
  89. }
  90. // Unmarshal the JSON into our struct.
  91. var apis APIs
  92. if err := json.Unmarshal(body, &apis); err != nil {
  93. panic(errors.New("could not unmarshal Godot JSON API"))
  94. }
  95. return apis
  96. }
  97. // WriteTemplate writes the parsed template on the disk
  98. func WriteTemplate(templatePath, outputPath string, view View) {
  99. // Create a template from our template file.
  100. t, err := template.ParseFiles(templatePath)
  101. if err != nil {
  102. log.Fatal("Error parsing template:", err)
  103. }
  104. // Open the output file for writing
  105. f, err := os.Create(outputPath)
  106. if err != nil {
  107. panic(err)
  108. }
  109. defer f.Close()
  110. // Write the template with the given view.
  111. err = t.Execute(f, view)
  112. if err != nil {
  113. panic(err)
  114. }
  115. }