main.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /*
  2. * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
  5. * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
  6. * Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
  7. *
  8. * SPDX-License-Identifier: BSD-2-Clause
  9. */
  10. #include "Namespaces.h"
  11. #include <AK/Debug.h>
  12. #include <AK/LexicalPath.h>
  13. #include <LibCore/ArgsParser.h>
  14. #include <LibCore/File.h>
  15. #include <LibIDL/IDLParser.h>
  16. #include <LibIDL/Types.h>
  17. extern Vector<StringView> s_header_search_paths;
  18. namespace IDL {
  19. void generate_namespace_header(IDL::Interface const&, StringBuilder&);
  20. void generate_namespace_implementation(IDL::Interface const&, StringBuilder&);
  21. void generate_constructor_header(IDL::Interface const&, StringBuilder&);
  22. void generate_constructor_implementation(IDL::Interface const&, StringBuilder&);
  23. void generate_prototype_header(IDL::Interface const&, StringBuilder&);
  24. void generate_prototype_implementation(IDL::Interface const&, StringBuilder&);
  25. void generate_iterator_prototype_header(IDL::Interface const&, StringBuilder&);
  26. void generate_iterator_prototype_implementation(IDL::Interface const&, StringBuilder&);
  27. void generate_global_mixin_header(IDL::Interface const&, StringBuilder&);
  28. void generate_global_mixin_implementation(IDL::Interface const&, StringBuilder&);
  29. }
  30. ErrorOr<int> serenity_main(Main::Arguments arguments)
  31. {
  32. Core::ArgsParser args_parser;
  33. StringView path;
  34. StringView import_base_path;
  35. StringView output_path = "-"sv;
  36. StringView depfile_path;
  37. StringView depfile_prefix;
  38. args_parser.add_option(Core::ArgsParser::Option {
  39. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  40. .help_string = "Add a header search path passed to the compiler",
  41. .long_name = "header-include-path",
  42. .short_name = 'i',
  43. .value_name = "path",
  44. .accept_value = [&](StringView s) {
  45. s_header_search_paths.append(s);
  46. return true;
  47. },
  48. });
  49. args_parser.add_option(output_path, "Path to output generated files into", "output-path", 'o', "output-path");
  50. args_parser.add_option(depfile_path, "Path to write dependency file to", "depfile", 'd', "depfile-path");
  51. args_parser.add_option(depfile_prefix, "Prefix to prepend to relative paths in dependency file", "depfile-prefix", 'p', "depfile-prefix");
  52. args_parser.add_positional_argument(path, "IDL file", "idl-file");
  53. args_parser.add_positional_argument(import_base_path, "Import base path", "import-base-path", Core::ArgsParser::Required::No);
  54. args_parser.parse(arguments);
  55. auto idl_file = TRY(Core::File::open(path, Core::File::OpenMode::Read));
  56. LexicalPath lexical_path(path);
  57. auto& namespace_ = lexical_path.parts_view().at(lexical_path.parts_view().size() - 2);
  58. auto data = TRY(idl_file->read_until_eof());
  59. if (import_base_path.is_null())
  60. import_base_path = lexical_path.dirname();
  61. IDL::Parser parser(path, data, import_base_path);
  62. auto& interface = parser.parse();
  63. // If the interface name is the same as its namespace, qualify the name in the generated code.
  64. // e.g. Selection::Selection
  65. if (IDL::libweb_interface_namespaces.span().contains_slow(namespace_)) {
  66. StringBuilder builder;
  67. builder.append(namespace_);
  68. builder.append("::"sv);
  69. builder.append(interface.name);
  70. interface.fully_qualified_name = builder.to_deprecated_string();
  71. } else {
  72. interface.fully_qualified_name = interface.name;
  73. }
  74. if constexpr (BINDINGS_GENERATOR_DEBUG) {
  75. dbgln("Attributes:");
  76. for (auto& attribute : interface.attributes) {
  77. dbgln(" {}{}{}{} {}",
  78. attribute.inherit ? "inherit " : "",
  79. attribute.readonly ? "readonly " : "",
  80. attribute.type->name(),
  81. attribute.type->is_nullable() ? "?" : "",
  82. attribute.name);
  83. }
  84. dbgln("Functions:");
  85. for (auto& function : interface.functions) {
  86. dbgln(" {}{} {}",
  87. function.return_type->name(),
  88. function.return_type->is_nullable() ? "?" : "",
  89. function.name);
  90. for (auto& parameter : function.parameters) {
  91. dbgln(" {}{} {}",
  92. parameter.type->name(),
  93. parameter.type->is_nullable() ? "?" : "",
  94. parameter.name);
  95. }
  96. }
  97. dbgln("Static Functions:");
  98. for (auto& function : interface.static_functions) {
  99. dbgln(" static {}{} {}",
  100. function.return_type->name(),
  101. function.return_type->is_nullable() ? "?" : "",
  102. function.name);
  103. for (auto& parameter : function.parameters) {
  104. dbgln(" {}{} {}",
  105. parameter.type->name(),
  106. parameter.type->is_nullable() ? "?" : "",
  107. parameter.name);
  108. }
  109. }
  110. }
  111. StringBuilder output_builder;
  112. auto write_if_changed = [&](auto generator_function, StringView file_path) -> ErrorOr<void> {
  113. (*generator_function)(interface, output_builder);
  114. auto current_file_or_error = Core::File::open(file_path, Core::File::OpenMode::Read);
  115. if (current_file_or_error.is_error() && current_file_or_error.error().code() != ENOENT)
  116. return current_file_or_error.release_error();
  117. ByteBuffer current_contents;
  118. if (!current_file_or_error.is_error())
  119. current_contents = TRY(current_file_or_error.value()->read_until_eof());
  120. // Only write to disk if contents have changed
  121. if (current_contents != output_builder.string_view().bytes()) {
  122. auto output_file = TRY(Core::File::open(file_path, Core::File::OpenMode::Write | Core::File::OpenMode::Truncate));
  123. TRY(output_file->write_until_depleted(output_builder.string_view().bytes()));
  124. }
  125. // FIXME: Can we add clear_with_capacity to StringBuilder instead of throwing away the allocated buffer?
  126. output_builder.clear();
  127. return {};
  128. };
  129. String namespace_header;
  130. String namespace_implementation;
  131. String constructor_header;
  132. String constructor_implementation;
  133. String prototype_header;
  134. String prototype_implementation;
  135. String iterator_prototype_header;
  136. String iterator_prototype_implementation;
  137. String global_mixin_header;
  138. String global_mixin_implementation;
  139. auto path_prefix = LexicalPath::join(output_path, lexical_path.basename(LexicalPath::StripExtension::Yes));
  140. if (interface.is_namespace) {
  141. namespace_header = TRY(String::formatted("{}Namespace.h", path_prefix));
  142. namespace_implementation = TRY(String::formatted("{}Namespace.cpp", path_prefix));
  143. TRY(write_if_changed(&IDL::generate_namespace_header, namespace_header));
  144. TRY(write_if_changed(&IDL::generate_namespace_implementation, namespace_implementation));
  145. } else {
  146. constructor_header = TRY(String::formatted("{}Constructor.h", path_prefix));
  147. constructor_implementation = TRY(String::formatted("{}Constructor.cpp", path_prefix));
  148. prototype_header = TRY(String::formatted("{}Prototype.h", path_prefix));
  149. prototype_implementation = TRY(String::formatted("{}Prototype.cpp", path_prefix));
  150. TRY(write_if_changed(&IDL::generate_constructor_header, constructor_header));
  151. TRY(write_if_changed(&IDL::generate_constructor_implementation, constructor_implementation));
  152. TRY(write_if_changed(&IDL::generate_prototype_header, prototype_header));
  153. TRY(write_if_changed(&IDL::generate_prototype_implementation, prototype_implementation));
  154. }
  155. if (interface.pair_iterator_types.has_value()) {
  156. iterator_prototype_header = TRY(String::formatted("{}IteratorPrototype.h", path_prefix));
  157. iterator_prototype_implementation = TRY(String::formatted("{}IteratorPrototype.cpp", path_prefix));
  158. TRY(write_if_changed(&IDL::generate_iterator_prototype_header, iterator_prototype_header));
  159. TRY(write_if_changed(&IDL::generate_iterator_prototype_implementation, iterator_prototype_implementation));
  160. }
  161. if (interface.extended_attributes.contains("Global")) {
  162. global_mixin_header = TRY(String::formatted("{}GlobalMixin.h", path_prefix));
  163. global_mixin_implementation = TRY(String::formatted("{}GlobalMixin.cpp", path_prefix));
  164. TRY(write_if_changed(&IDL::generate_global_mixin_header, global_mixin_header));
  165. TRY(write_if_changed(&IDL::generate_global_mixin_implementation, global_mixin_implementation));
  166. }
  167. if (!depfile_path.is_empty()) {
  168. auto depfile = TRY(Core::File::open_file_or_standard_stream(depfile_path, Core::File::OpenMode::Write));
  169. StringBuilder depfile_builder;
  170. for (StringView s : { constructor_header, constructor_implementation, prototype_header, prototype_implementation, namespace_header, namespace_implementation, iterator_prototype_header, iterator_prototype_implementation, global_mixin_header, global_mixin_implementation }) {
  171. if (s.is_empty())
  172. continue;
  173. if (!depfile_prefix.is_empty())
  174. depfile_builder.append(LexicalPath::join(depfile_prefix, s).string());
  175. else
  176. depfile_builder.append(s);
  177. break;
  178. }
  179. depfile_builder.append(':');
  180. for (auto const& path : parser.imported_files()) {
  181. depfile_builder.append(" \\\n "sv);
  182. depfile_builder.append(path);
  183. }
  184. depfile_builder.append('\n');
  185. TRY(depfile->write_until_depleted(depfile_builder.string_view().bytes()));
  186. }
  187. return 0;
  188. }