diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp index c89fa11a4e..7f0e805aad 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,12 @@ #include #include +namespace IDL { +struct Interface; +} + +static Vector s_header_search_paths; + static String make_input_acceptable_cpp(String const& input) { if (input.is_one_of("class", "template", "for", "default", "char", "namespace", "delete")) { @@ -95,6 +102,95 @@ static String convert_enumeration_value_to_cpp_enum_member(String const& value, exit(EXIT_FAILURE); } +static void generate_include_for_wrapper(auto& generator, auto& wrapper_name) +{ + auto wrapper_generator = generator.fork(); + wrapper_generator.set("wrapper_class", wrapper_name); + // FIXME: These may or may not exist, because REASONS. + wrapper_generator.append(R"~~~( +#if __has_include() +# include +#endif +#if __has_include() +# include +#endif +)~~~"); +} + +static void generate_include_for_iterator(auto& generator, auto& iterator_path, auto& iterator_name) +{ + auto iterator_generator = generator.fork(); + iterator_generator.set("iterator_class.path", iterator_path); + iterator_generator.set("iterator_class.name", iterator_name); + // FIXME: These may or may not exist, because REASONS. + iterator_generator.append(R"~~~( +//#if __has_include() +# include +//#endif +#if __has_include() +# include +#endif +#if __has_include() +# include +#endif +#if __has_include() +# include +#endif +)~~~"); +} + +static void generate_include_for(auto& generator, auto& path) +{ + auto forked_generator = generator.fork(); + auto path_string = path; + for (auto& search_path : s_header_search_paths) { + if (!path.starts_with(search_path)) + continue; + auto relative_path = LexicalPath::relative_path(path, search_path); + if (relative_path.length() < path_string.length()) + path_string = relative_path; + } + + LexicalPath include_path { path_string }; + forked_generator.set("include.path", String::formatted("{}/{}.h", include_path.dirname(), include_path.title())); + forked_generator.append(R"~~~( +#include <@include.path@> +)~~~"); +} + +static void emit_includes_for_all_imports(auto& interface, auto& generator, bool is_header, bool is_iterator = false) +{ + Queue const*> interfaces; + HashTable paths_imported; + if (is_header) + paths_imported.set(interface.module_own_path); + + interfaces.enqueue(&interface); + + while (!interfaces.is_empty()) { + auto interface = interfaces.dequeue(); + if (paths_imported.contains(interface->module_own_path)) + continue; + + paths_imported.set(interface->module_own_path); + for (auto& imported_interface : interface->imported_modules) { + if (!paths_imported.contains(imported_interface.module_own_path)) + interfaces.enqueue(&imported_interface); + } + + generate_include_for(generator, interface->module_own_path); + + if (is_iterator) { + auto iterator_name = String::formatted("{}Iterator", interface->name); + auto iterator_path = String::formatted("{}Iterator", interface->fully_qualified_name.replace("::", "/")); + generate_include_for_iterator(generator, iterator_path, iterator_name); + } + + if (interface->wrapper_class != "Wrapper") + generate_include_for_wrapper(generator, interface->wrapper_class); + } +} + namespace IDL { template @@ -380,6 +476,10 @@ struct Interface { String prototype_class; String prototype_base_class; + String module_own_path; + HashTable imported_paths; + NonnullOwnPtrVector imported_modules; + // https://webidl.spec.whatwg.org/#dfn-support-indexed-properties bool supports_indexed_properties() const { return indexed_property_getter.has_value(); } @@ -390,9 +490,14 @@ struct Interface { bool is_legacy_platform_object() const { return !extended_attributes.contains("Global") && (supports_indexed_properties() || supports_named_properties()); } }; +HashTable s_all_imported_paths; static NonnullOwnPtr parse_interface(StringView filename, StringView input, StringView import_base_path) { + auto this_module = Core::File::real_path_for(filename); + s_all_imported_paths.set(this_module); + auto interface = make(); + interface->module_own_path = this_module; GenericLexer lexer(input); @@ -438,17 +543,23 @@ static NonnullOwnPtr parse_interface(StringView filename, StringView return extended_attributes; }; - auto resolve_import = [&](auto path) { + auto resolve_import = [&](auto path) -> Optional> { auto include_path = LexicalPath::join(import_base_path, path).string(); if (!Core::File::exists(include_path)) report_parsing_error(String::formatted("{}: No such file or directory", include_path), filename, input, lexer.tell()); - auto file_or_error = Core::File::open(include_path, Core::OpenMode::ReadOnly); + auto real_path = Core::File::real_path_for(include_path); + if (s_all_imported_paths.contains(real_path)) + return {}; + + s_all_imported_paths.set(real_path); + + auto file_or_error = Core::File::open(real_path, Core::OpenMode::ReadOnly); if (file_or_error.is_error()) - report_parsing_error(String::formatted("Failed to open {}: {}", include_path, file_or_error.error()), filename, input, lexer.tell()); + report_parsing_error(String::formatted("Failed to open {}: {}", real_path, file_or_error.error()), filename, input, lexer.tell()); auto data = file_or_error.value()->read_all(); - return IDL::parse_interface(include_path, data, import_base_path); + return IDL::parse_interface(real_path, data, import_base_path); }; NonnullOwnPtrVector imports; @@ -457,9 +568,15 @@ static NonnullOwnPtr parse_interface(StringView filename, StringView assert_specific('<'); auto path = lexer.consume_until('>'); lexer.ignore(); - imports.append(resolve_import(path)); + auto maybe_interface = resolve_import(path); + if (maybe_interface.has_value()) { + for (auto& entry : maybe_interface.value()->imported_paths) + s_all_imported_paths.set(entry); + imports.append(maybe_interface.release_value()); + } consume_whitespace(); } + interface->imported_paths = s_all_imported_paths; if (lexer.consume_specific('[')) interface->extended_attributes = parse_extended_attributes(); @@ -1024,6 +1141,7 @@ static NonnullOwnPtr parse_interface(StringView filename, StringView interface->enumerations.set(enumeration.key, move(enumeration_copy)); } } + interface->imported_modules = move(imports); return interface; } @@ -1172,6 +1290,17 @@ int main(int argc, char** argv) args_parser.add_option(iterator_implementation_mode, "Generate the iterator wrapper .cpp file", "iterator-implementation", 0); args_parser.add_option(iterator_prototype_header_mode, "Generate the iterator prototype .h file", "iterator-prototype-header", 0); args_parser.add_option(iterator_prototype_implementation_mode, "Generate the iterator prototype .cpp file", "iterator-prototype-implementation", 0); + args_parser.add_option(Core::ArgsParser::Option { + .requires_argument = true, + .help_string = "Add a header search path passed to the compiler", + .long_name = "header-include-path", + .short_name = 'i', + .value_name = "path", + .accept_value = [&](char const* s) { + s_header_search_paths.append(s); + return true; + }, + }); args_parser.add_positional_argument(path, "IDL file", "idl-file"); args_parser.add_positional_argument(import_base_path, "Import base path", "import-base-path", Core::ArgsParser::Required::No); args_parser.parse(argc, argv); @@ -2359,58 +2488,24 @@ static void generate_header(IDL::Interface const& interface) StringBuilder builder; SourceGenerator generator { builder }; + generator.append(R"~~~( +#pragma once + +#include +)~~~"); + + for (auto& path : interface.imported_paths) + generate_include_for(generator, path); + + emit_includes_for_all_imports(interface, generator, true); generator.set("name", interface.name); generator.set("fully_qualified_name", interface.fully_qualified_name); generator.set("wrapper_base_class", interface.wrapper_base_class); generator.set("wrapper_class", interface.wrapper_class); generator.set("wrapper_class:snakecase", interface.wrapper_class.to_snakecase()); - generator.append(R"~~~( -#pragma once - -#include - -// FIXME: This is very strange. -#if __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#endif -)~~~"); - - if (interface.wrapper_base_class != "Wrapper") { - generator.append(R"~~~( -#include -)~~~"); - } + if (interface.wrapper_base_class != "Wrapper") + generate_include_for_wrapper(generator, interface.wrapper_base_class); generator.append(R"~~~( namespace Web::Bindings { @@ -2559,44 +2654,14 @@ void generate_implementation(IDL::Interface const& interface) #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include +)~~~"); + emit_includes_for_all_imports(interface, generator, false); + + generator.append(R"~~~( // FIXME: This is a total hack until we can figure out the namespace for a given type somehow. using namespace Web::CSS; using namespace Web::DOM; @@ -3708,9 +3773,6 @@ void generate_prototype_implementation(IDL::Interface const& interface) if (interface.pair_iterator_types.has_value()) { generator.set("iterator_name", String::formatted("{}Iterator", interface.name)); generator.set("iterator_wrapper_class", String::formatted("{}IteratorWrapper", interface.name)); - generator.append(R"~~~( -#include -)~~~"); } generator.append(R"~~~( @@ -3722,134 +3784,29 @@ void generate_prototype_implementation(IDL::Interface const& interface) #include #include #include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include +#include #include #if __has_include() # include #endif -#if __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#endif )~~~"); - if (interface.pair_iterator_types.has_value()) { - generator.append(R"~~~( -#if __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#endif -)~~~"); - } + for (auto& path : interface.imported_paths) + generate_include_for(generator, path); + + emit_includes_for_all_imports(interface, generator, false, interface.pair_iterator_types.has_value()); generator.append(R"~~~( @@ -4229,41 +4186,6 @@ static void generate_iterator_header(IDL::Interface const& interface) #include -// FIXME: This is very strange. -#if __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#endif - namespace Web::Bindings { class @wrapper_class@ : public Wrapper { @@ -4316,6 +4238,15 @@ void generate_iterator_implementation(IDL::Interface const& interface) #include #include +)~~~"); + + for (auto& path : interface.imported_paths) + generate_include_for(generator, path); + + emit_includes_for_all_imports(interface, generator, false, true); + + generator.append(R"~~~( + // FIXME: This is a total hack until we can figure out the namespace for a given type somehow. using namespace Web::CSS; using namespace Web::DOM; @@ -4407,6 +4338,7 @@ void generate_iterator_prototype_implementation(IDL::Interface const& interface) generator.set("prototype_class", String::formatted("{}IteratorPrototype", interface.name)); generator.set("wrapper_class", String::formatted("{}IteratorWrapper", interface.name)); generator.set("fully_qualified_name", String::formatted("{}Iterator", interface.fully_qualified_name)); + generator.set("possible_include_path", String::formatted("{}Iterator", interface.name.replace("::", "/"))); generator.append(R"~~~( #include @@ -4416,44 +4348,17 @@ void generate_iterator_prototype_implementation(IDL::Interface const& interface) #include #include #include -#include #include #include -#if __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include -#elif __has_include() -# include +#if __has_include() +# include #endif +)~~~"); + emit_includes_for_all_imports(interface, generator, false, true); + + generator.append(R"~~~( // FIXME: This is a total hack until we can figure out the namespace for a given type somehow. using namespace Web::CSS; using namespace Web::DOM; diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index a668447816..f965b0504d 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -362,9 +362,11 @@ function(libweb_js_wrapper class) foreach(iter RANGE "${bindings_end}") list(GET BINDINGS_SOURCES ${iter} bindings_src) list(GET BINDINGS_TYPES ${iter} bindings_type) + get_property(include_paths DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) + list(TRANSFORM include_paths PREPEND -i) add_custom_command( OUTPUT "${bindings_src}" - COMMAND "$" "--${bindings_type}" "${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl" "${CMAKE_CURRENT_SOURCE_DIR}/" > "${bindings_src}.tmp" + COMMAND "$" "--${bindings_type}" ${include_paths} "${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl" "${CMAKE_CURRENT_SOURCE_DIR}/" > "${bindings_src}.tmp" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${bindings_src}.tmp" "${bindings_src}" COMMAND "${CMAKE_COMMAND}" -E remove "${bindings_src}.tmp" VERBATIM