gdextension_interface_header_generator.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /**************************************************************************/
  2. /* gdextension_interface_header_generator.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #ifdef TOOLS_ENABLED
  31. #include "gdextension_interface_header_generator.h"
  32. #include "core/io/json.h"
  33. #include "gdextension_interface_dump.gen.h"
  34. static const char *FILE_HEADER =
  35. "/**************************************************************************/\n"
  36. "/* gdextension_interface.h */\n";
  37. static const char *INTRO =
  38. "\n"
  39. "#pragma once\n"
  40. "\n"
  41. "/* This is a C class header, you can copy it and use it directly in your own binders.\n"
  42. " * Together with the `extension_api.json` file, you should be able to generate any binder.\n"
  43. " */\n"
  44. "\n"
  45. "#ifndef __cplusplus\n"
  46. "#include <stddef.h>\n"
  47. "#include <stdint.h>\n"
  48. "\n"
  49. "typedef uint32_t char32_t;\n"
  50. "typedef uint16_t char16_t;\n"
  51. "#else\n"
  52. "#include <cstddef>\n"
  53. "#include <cstdint>\n"
  54. "\n"
  55. "extern \"C\" {\n"
  56. "#endif\n"
  57. "\n";
  58. static const char *OUTRO =
  59. "#ifdef __cplusplus\n"
  60. "}\n"
  61. "#endif\n";
  62. void GDExtensionInterfaceHeaderGenerator::generate_gdextension_interface_header(const String &p_path) {
  63. Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
  64. ERR_FAIL_COND_MSG(fa.is_null(), vformat("Cannot open file '%s' for writing.", p_path));
  65. Vector<uint8_t> bytes = GDExtensionInterfaceDump::load_gdextension_interface_file();
  66. String json_string = String::utf8(Span<char>((char *)bytes.ptr(), bytes.size()));
  67. Ref<JSON> json;
  68. json.instantiate();
  69. Error err = json->parse(json_string);
  70. ERR_FAIL_COND(err);
  71. Dictionary data = json->get_data();
  72. ERR_FAIL_COND(data.is_empty());
  73. fa->store_string(FILE_HEADER);
  74. Array copyright = data["_copyright"];
  75. for (const Variant &line : copyright) {
  76. fa->store_line(line);
  77. }
  78. fa->store_string(INTRO);
  79. Array types = data["types"];
  80. for (Dictionary type_dict : types) {
  81. if (type_dict.has("description")) {
  82. write_doc(fa, type_dict["description"]);
  83. }
  84. String kind = type_dict["kind"];
  85. if (kind == "handle") {
  86. type_dict["type"] = type_dict.get("is_const", false) ? "const void*" : "void*";
  87. write_simple_type(fa, type_dict);
  88. } else if (kind == "alias") {
  89. write_simple_type(fa, type_dict);
  90. } else if (kind == "enum") {
  91. write_enum_type(fa, type_dict);
  92. } else if (kind == "function") {
  93. write_function_type(fa, type_dict);
  94. } else if (kind == "struct") {
  95. write_struct_type(fa, type_dict);
  96. }
  97. }
  98. Array interfaces = data["interface"];
  99. for (const Variant &interface : interfaces) {
  100. write_interface(fa, interface);
  101. }
  102. fa->store_string(OUTRO);
  103. }
  104. void GDExtensionInterfaceHeaderGenerator::write_doc(const Ref<FileAccess> &p_fa, const Array &p_doc, const String &p_indent) {
  105. if (p_doc.size() == 1) {
  106. p_fa->store_string(vformat("%s/* %s */\n", p_indent, p_doc[0]));
  107. return;
  108. }
  109. bool first = true;
  110. for (const Variant &line : p_doc) {
  111. if (first) {
  112. p_fa->store_string(p_indent + "/*");
  113. first = false;
  114. } else {
  115. p_fa->store_string(p_indent + " *");
  116. }
  117. if (line == "") {
  118. p_fa->store_string("\n");
  119. } else {
  120. p_fa->store_line(String(" ") + (String)line);
  121. }
  122. }
  123. p_fa->store_string(p_indent + " */\n");
  124. }
  125. void GDExtensionInterfaceHeaderGenerator::write_simple_type(const Ref<FileAccess> &p_fa, const Dictionary &p_type) {
  126. String type_and_name = format_type_and_name(p_type["type"], p_type["name"]);
  127. p_fa->store_string(vformat("typedef %s;%s\n", type_and_name, make_deprecated_comment_for_type(p_type)));
  128. }
  129. void GDExtensionInterfaceHeaderGenerator::write_enum_type(const Ref<FileAccess> &p_fa, const Dictionary &p_enum) {
  130. p_fa->store_string("typedef enum {\n");
  131. Array values = p_enum["values"];
  132. for (Dictionary value_dict : values) {
  133. if (value_dict.has("description")) {
  134. write_doc(p_fa, value_dict["description"], "\t");
  135. }
  136. p_fa->store_string(vformat("\t%s = %s,\n", value_dict["name"], (int)value_dict["value"]));
  137. }
  138. p_fa->store_string(vformat("} %s;%s\n\n", p_enum["name"], make_deprecated_comment_for_type(p_enum)));
  139. }
  140. void GDExtensionInterfaceHeaderGenerator::write_function_type(const Ref<FileAccess> &p_fa, const Dictionary &p_func) {
  141. String args_text = p_func.has("arguments") ? make_args_text(p_func["arguments"]) : "";
  142. String name_and_args = vformat("(*%s)(%s)", p_func["name"], args_text);
  143. String return_type;
  144. if (p_func.has("return_value")) {
  145. Dictionary ret = p_func["return_value"];
  146. return_type = ret["type"];
  147. } else {
  148. return_type = "void";
  149. }
  150. p_fa->store_string(vformat("typedef %s;%s\n", format_type_and_name(return_type, name_and_args), make_deprecated_comment_for_type(p_func)));
  151. }
  152. void GDExtensionInterfaceHeaderGenerator::write_struct_type(const Ref<FileAccess> &p_fa, const Dictionary &p_struct) {
  153. p_fa->store_string("typedef struct {\n");
  154. Array members = p_struct["members"];
  155. for (Dictionary member_dict : members) {
  156. if (member_dict.has("description")) {
  157. write_doc(p_fa, member_dict["description"], "\t");
  158. }
  159. p_fa->store_string(vformat("\t%s;\n", format_type_and_name(member_dict["type"], member_dict["name"])));
  160. }
  161. p_fa->store_string(vformat("} %s;%s\n\n", p_struct["name"], make_deprecated_comment_for_type(p_struct)));
  162. }
  163. String GDExtensionInterfaceHeaderGenerator::format_type_and_name(const String &p_type, const String &p_name) {
  164. String ret = p_type;
  165. bool is_pointer = false;
  166. if (ret.ends_with("*")) {
  167. ret = ret.substr(0, ret.size() - 2) + " *";
  168. is_pointer = true;
  169. }
  170. if (!p_name.is_empty()) {
  171. if (is_pointer) {
  172. ret = ret + p_name;
  173. } else {
  174. ret = ret + " " + p_name;
  175. }
  176. }
  177. return ret;
  178. }
  179. String GDExtensionInterfaceHeaderGenerator::make_deprecated_message(const Dictionary &p_data) {
  180. PackedStringArray parts;
  181. parts.push_back(vformat("Deprecated in Godot %s.", p_data["since"]));
  182. if (p_data.has("message")) {
  183. parts.push_back(p_data["message"]);
  184. }
  185. if (p_data.has("replace_with")) {
  186. parts.push_back(vformat("Use `%s` instead.", p_data["replace_with"]));
  187. }
  188. return String(" ").join(parts);
  189. }
  190. String GDExtensionInterfaceHeaderGenerator::make_deprecated_comment_for_type(const Dictionary &p_type) {
  191. if (!p_type.has("deprecated")) {
  192. return "";
  193. }
  194. return vformat(" /* %s */", make_deprecated_message(p_type["deprecated"]));
  195. }
  196. String GDExtensionInterfaceHeaderGenerator::make_args_text(const Array &p_args) {
  197. Vector<String> combined;
  198. for (Dictionary arg_dict : p_args) {
  199. combined.push_back(format_type_and_name(arg_dict["type"], arg_dict.get("name", String())));
  200. }
  201. return String(", ").join(combined);
  202. }
  203. void GDExtensionInterfaceHeaderGenerator::write_interface(const Ref<FileAccess> &p_fa, const Dictionary &p_interface) {
  204. Vector<String> doc;
  205. doc.push_back(String("@name ") + (String)p_interface["name"]);
  206. doc.push_back(String("@since ") + (String)p_interface["since"]);
  207. if (p_interface.has("deprecated")) {
  208. doc.push_back(String("@deprecated ") + make_deprecated_message(p_interface["deprecated"]));
  209. }
  210. Array orig_doc = p_interface["description"];
  211. for (int i = 0; i < orig_doc.size(); i++) {
  212. // Put an empty line before the 1st and 2nd lines.
  213. if (i <= 1) {
  214. doc.push_back("");
  215. }
  216. doc.push_back(orig_doc[i]);
  217. }
  218. if (p_interface.has("arguments")) {
  219. Array args = p_interface["arguments"];
  220. if (args.size() > 0) {
  221. doc.push_back("");
  222. for (Dictionary arg_dict : args) {
  223. String arg_string = String("@param ") + (String)arg_dict["name"];
  224. if (arg_dict.has("description")) {
  225. Array arg_doc = arg_dict["description"];
  226. for (const Variant &d : arg_doc) {
  227. arg_string += String(" ") + (String)d;
  228. }
  229. }
  230. doc.push_back(arg_string);
  231. }
  232. }
  233. }
  234. if (p_interface.has("return_value")) {
  235. Dictionary ret = p_interface["return_value"];
  236. String ret_string = String("@return");
  237. if (ret.has("description")) {
  238. Array arg_doc = ret["description"];
  239. for (const Variant &d : arg_doc) {
  240. ret_string += String(" ") + (String)d;
  241. }
  242. }
  243. doc.push_back("");
  244. doc.push_back(ret_string);
  245. }
  246. if (p_interface.has("see")) {
  247. Array see_array = p_interface["see"];
  248. if (see_array.size() > 0) {
  249. doc.push_back("");
  250. for (const Variant &see : see_array) {
  251. doc.push_back(String("@see ") + (String)see);
  252. }
  253. }
  254. }
  255. p_fa->store_string("/**\n");
  256. for (const String &d : doc) {
  257. if (d == "") {
  258. p_fa->store_string(" *\n");
  259. } else {
  260. p_fa->store_string(vformat(" * %s\n", d));
  261. }
  262. }
  263. p_fa->store_string(" */\n");
  264. Dictionary func = p_interface.duplicate();
  265. func.erase("deprecated");
  266. if (p_interface.has("legacy_type_name")) {
  267. // @todo When we can break compat, remove this! This maintains legacy type-o's in some type names.
  268. func["name"] = p_interface["legacy_type_name"];
  269. } else {
  270. // Cannot use `to_pascal_case()` because it'll capitalize after numbers.
  271. Vector<String> words = ((String)p_interface["name"]).split("_");
  272. for (String &word : words) {
  273. // Cannot use `capitalize()` on the whole string, because it'll separate numbers with a space.
  274. word[0] = String::char_uppercase(word[0]);
  275. }
  276. func["name"] = String("GDExtensionInterface") + String().join(words);
  277. }
  278. write_function_type(p_fa, func);
  279. p_fa->store_string("\n");
  280. }
  281. #endif // TOOLS_ENABLED