diff --git a/Meta/CMake/libweb_generators.cmake b/Meta/CMake/libweb_generators.cmake index 7421cbb955..7c28f254ed 100644 --- a/Meta/CMake/libweb_generators.cmake +++ b/Meta/CMake/libweb_generators.cmake @@ -97,16 +97,11 @@ function (generate_js_bindings target) cmake_parse_arguments(PARSE_ARGV 1 LIBWEB_BINDINGS "NAMESPACE;ITERABLE;GLOBAL" "" "") get_filename_component(basename "${class}" NAME) - # FIXME: Instead of requiring a manual declaration of namespace bindings, we should ask BindingsGenerator if it's a namespace if (LIBWEB_BINDINGS_NAMESPACE) set(BINDINGS_SOURCES "Bindings/${basename}Namespace.h" "Bindings/${basename}Namespace.cpp" ) - set(BINDINGS_TYPES - namespace-header - namespace-implementation - ) else() set(BINDINGS_SOURCES "Bindings/${basename}Constructor.h" @@ -114,77 +109,50 @@ function (generate_js_bindings target) "Bindings/${basename}Prototype.h" "Bindings/${basename}Prototype.cpp" ) - set(BINDINGS_TYPES - constructor-header - constructor-implementation - prototype-header - prototype-implementation - ) endif() - # FIXME: Instead of requiring a manual declaration of iterable bindings, we should ask BindingsGenerator if it's iterable if(LIBWEB_BINDINGS_ITERABLE) list(APPEND BINDINGS_SOURCES "Bindings/${basename}IteratorPrototype.h" "Bindings/${basename}IteratorPrototype.cpp" ) - list(APPEND BINDINGS_TYPES - iterator-prototype-header - iterator-prototype-implementation - ) endif() - # FIXME: Instead of requiring a manual declaration of global object bindings, we should ask BindingsGenerator if it's global if(LIBWEB_BINDINGS_GLOBAL) list(APPEND BINDINGS_SOURCES "Bindings/${basename}GlobalMixin.h" "Bindings/${basename}GlobalMixin.cpp" ) - list(APPEND BINDINGS_TYPES - global-mixin-header - global-mixin-implementation - ) endif() + list(TRANSFORM BINDINGS_SOURCES PREPEND "${CMAKE_CURRENT_BINARY_DIR}/") target_sources(${target} PRIVATE ${BINDINGS_SOURCES}) - # FIXME: cmake_minimum_required(3.17) for ZIP_LISTS - list(LENGTH BINDINGS_SOURCES num_bindings) - math(EXPR bindings_end "${num_bindings} - 1") - 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) - # Ninja expects the target name in depfiles to be relative to CMAKE_BINARY_DIR, but ${bindings_src} is - # relative to CMAKE_CURRENT_BINARY_DIR. CMake >= 3.20 can do the rewriting transparently (CMP0116). - if(CMAKE_GENERATOR MATCHES "^Ninja" AND NOT POLICY CMP0116) - # FIXME: Drop this branch for cmake_minimum_required(3.20) - get_filename_component(full_path ${bindings_src} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}) - file(RELATIVE_PATH depfile_target ${CMAKE_BINARY_DIR} ${full_path}) - else() - set(depfile_target ${bindings_src}) - endif() + 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}" -o "${bindings_src}.tmp" --depfile "${bindings_src}.d" - --depfile-target "${depfile_target}" ${include_paths} "${LIBWEB_INPUT_FOLDER}/${class}.idl" "${LIBWEB_INPUT_FOLDER}" - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${bindings_src}.tmp" "${bindings_src}" - COMMAND "${CMAKE_COMMAND}" -E remove "${bindings_src}.tmp" - VERBATIM - DEPENDS Lagom::BindingsGenerator - MAIN_DEPENDENCY ${class}.idl - DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/${bindings_src}.d - ) - endforeach() + # Ninja expects the target name in depfiles to be relative to CMAKE_BINARY_DIR, but ${bindings_src} is + # relative to CMAKE_CURRENT_BINARY_DIR. CMake >= 3.20 can do the rewriting transparently (CMP0116). + set(depfile_prefix_arg "") + if(CMAKE_GENERATOR MATCHES "^Ninja" AND NOT POLICY CMP0116) + file(RELATIVE_PATH depfile_target ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + set(depfile_prefix_arg "--depfile-prefix ${depfile_target}" ) + endif() - foreach(generated_file IN LISTS BINDINGS_SOURCES) - get_filename_component(generated_name ${generated_file} NAME) - add_custom_target(generate_${generated_name} DEPENDS ${generated_file}) - add_dependencies(all_generated generate_${generated_name}) - add_dependencies(${target} generate_${generated_name}) - endforeach() + add_custom_command( + OUTPUT ${BINDINGS_SOURCES} + COMMAND "$" -o "Bindings" --depfile "Bindings/${basename}.d" + ${depfile_prefix_arg} "${LIBWEB_INPUT_FOLDER}/${class}.idl" "${LIBWEB_INPUT_FOLDER}" + VERBATIM + COMMENT "Generating Bindings for ${class}" + DEPENDS Lagom::BindingsGenerator + MAIN_DEPENDENCY ${class}.idl + DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/Bindings/${basename}.d + ) + + add_custom_target(generate_${basename} DEPENDS ${BINDINGS_SOURCES}) + add_dependencies(all_generated generate_${basename}) + add_dependencies(${target} generate_${basename}) list(APPEND LIBWEB_ALL_IDL_FILES "${LIBWEB_INPUT_FOLDER}/${class}.idl") set(LIBWEB_ALL_IDL_FILES ${LIBWEB_ALL_IDL_FILES} PARENT_SCOPE) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/main.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/main.cpp index 24c6d8d619..dd517d48d3 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/main.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/main.cpp @@ -3,6 +3,7 @@ * Copyright (c) 2021-2023, Linus Groh * Copyright (c) 2021, Luke Wilde * Copyright (c) 2022, Ali Mohammad Pur + * Copyright (c) 2023, Andrew Kaster * * SPDX-License-Identifier: BSD-2-Clause */ @@ -37,27 +38,8 @@ ErrorOr serenity_main(Main::Arguments arguments) StringView import_base_path; StringView output_path = "-"sv; StringView depfile_path; - StringView depfile_target; - bool namespace_header_mode = false; - bool namespace_implementation_mode = false; - bool constructor_header_mode = false; - bool constructor_implementation_mode = false; - bool prototype_header_mode = false; - bool prototype_implementation_mode = false; - bool iterator_prototype_header_mode = false; - bool iterator_prototype_implementation_mode = false; - bool global_mixin_header_mode = false; - bool global_mixin_implementation_mode = false; - args_parser.add_option(namespace_header_mode, "Generate the namespace .h file", "namespace-header", 'N'); - args_parser.add_option(namespace_implementation_mode, "Generate the namespace .cpp file", "namespace-implementation", 'A'); - args_parser.add_option(constructor_header_mode, "Generate the constructor .h file", "constructor-header", 'C'); - args_parser.add_option(constructor_implementation_mode, "Generate the constructor .cpp file", "constructor-implementation", 'O'); - args_parser.add_option(prototype_header_mode, "Generate the prototype .h file", "prototype-header", 'P'); - args_parser.add_option(prototype_implementation_mode, "Generate the prototype .cpp file", "prototype-implementation", 'R'); - 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(global_mixin_header_mode, "Generate the global object mixin .h file", "global-mixin-header", 0); - args_parser.add_option(global_mixin_implementation_mode, "Generate the global object mixin .cpp file", "global-mixin-implementation", 0); + StringView depfile_prefix; + args_parser.add_option(Core::ArgsParser::Option { .argument_mode = Core::ArgsParser::OptionArgumentMode::Required, .help_string = "Add a header search path passed to the compiler", @@ -69,28 +51,28 @@ ErrorOr serenity_main(Main::Arguments arguments) return true; }, }); - args_parser.add_option(output_path, "Path to output generated file into", "output-path", 'o', "output-path"); + args_parser.add_option(output_path, "Path to output generated files into", "output-path", 'o', "output-path"); args_parser.add_option(depfile_path, "Path to write dependency file to", "depfile", 'd', "depfile-path"); - args_parser.add_option(depfile_target, "Name of target in the depfile (default: output path)", "depfile-target", 't', "target"); + args_parser.add_option(depfile_prefix, "Prefix to prepend to relative paths in dependency file", "depfile-prefix", 'p', "depfile-prefix"); 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(arguments); - auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read)); + auto idl_file = TRY(Core::File::open(path, Core::File::OpenMode::Read)); LexicalPath lexical_path(path); auto& namespace_ = lexical_path.parts_view().at(lexical_path.parts_view().size() - 2); - auto data = TRY(file->read_until_eof()); + auto data = TRY(idl_file->read_until_eof()); if (import_base_path.is_null()) import_base_path = lexical_path.dirname(); - auto output_file = TRY(Core::File::open_file_or_standard_stream(output_path, Core::File::OpenMode::Write)); - IDL::Parser parser(path, data, import_base_path); auto& interface = parser.parse(); + // If the interface name is the same as its namespace, qualify the name in the generated code. + // e.g. Selection::Selection if (IDL::libweb_interface_namespaces.span().contains_slow(namespace_)) { StringBuilder builder; builder.append(namespace_); @@ -143,43 +125,85 @@ ErrorOr serenity_main(Main::Arguments arguments) StringBuilder output_builder; - if (namespace_header_mode) - IDL::generate_namespace_header(interface, output_builder); + auto write_if_changed = [&](auto generator_function, StringView file_path) -> ErrorOr { + (*generator_function)(interface, output_builder); - if (namespace_implementation_mode) - IDL::generate_namespace_implementation(interface, output_builder); + auto output_file = TRY(Core::File::open(file_path, Core::File::OpenMode::ReadWrite)); - if (constructor_header_mode) - IDL::generate_constructor_header(interface, output_builder); + // Only write to disk if contents have changed + auto previous_contents = TRY(output_file->read_until_eof()); + TRY(output_file->seek(0, SeekMode::SetPosition)); + if (previous_contents != output_builder.string_view()) + TRY(output_file->write_until_depleted(output_builder.string_view().bytes())); + // FIXME: Can we add clear_with_capacity to StringBuilder instead of throwing away the allocated buffer? + output_builder.clear(); + return {}; + }; - if (constructor_implementation_mode) - IDL::generate_constructor_implementation(interface, output_builder); + String namespace_header; + String namespace_implementation; + String constructor_header; + String constructor_implementation; + String prototype_header; + String prototype_implementation; + String iterator_prototype_header; + String iterator_prototype_implementation; + String global_mixin_header; + String global_mixin_implementation; - if (prototype_header_mode) - IDL::generate_prototype_header(interface, output_builder); + auto path_prefix = LexicalPath::join(output_path, lexical_path.basename(LexicalPath::StripExtension::Yes)); - if (prototype_implementation_mode) - IDL::generate_prototype_implementation(interface, output_builder); + if (interface.is_namespace) { + namespace_header = TRY(String::formatted("{}Namespace.h", path_prefix)); + namespace_implementation = TRY(String::formatted("{}Namespace.cpp", path_prefix)); - if (iterator_prototype_header_mode) - IDL::generate_iterator_prototype_header(interface, output_builder); + TRY(write_if_changed(&IDL::generate_namespace_header, namespace_header)); + TRY(write_if_changed(&IDL::generate_namespace_implementation, namespace_implementation)); + } else { + constructor_header = TRY(String::formatted("{}Constructor.h", path_prefix)); + constructor_implementation = TRY(String::formatted("{}Constructor.cpp", path_prefix)); + prototype_header = TRY(String::formatted("{}Prototype.h", path_prefix)); + prototype_implementation = TRY(String::formatted("{}Prototype.cpp", path_prefix)); - if (iterator_prototype_implementation_mode) - IDL::generate_iterator_prototype_implementation(interface, output_builder); + TRY(write_if_changed(&IDL::generate_constructor_header, constructor_header)); + TRY(write_if_changed(&IDL::generate_constructor_implementation, constructor_implementation)); + TRY(write_if_changed(&IDL::generate_prototype_header, prototype_header)); + TRY(write_if_changed(&IDL::generate_prototype_implementation, prototype_implementation)); + } - if (global_mixin_header_mode) - IDL::generate_global_mixin_header(interface, output_builder); + if (interface.pair_iterator_types.has_value()) { + iterator_prototype_header = TRY(String::formatted("{}IteratorPrototype.h", path_prefix)); + iterator_prototype_implementation = TRY(String::formatted("{}IteratorPrototype.cpp", path_prefix)); - if (global_mixin_implementation_mode) - IDL::generate_global_mixin_implementation(interface, output_builder); + TRY(write_if_changed(&IDL::generate_iterator_prototype_header, iterator_prototype_header)); + TRY(write_if_changed(&IDL::generate_iterator_prototype_implementation, iterator_prototype_implementation)); + } - TRY(output_file->write_until_depleted(output_builder.string_view().bytes())); + if (interface.extended_attributes.contains("Global")) { + global_mixin_header = TRY(String::formatted("{}GlobalMixin.h", path_prefix)); + global_mixin_implementation = TRY(String::formatted("{}GlobalMixin.cpp", path_prefix)); - if (!depfile_path.is_null()) { + TRY(write_if_changed(&IDL::generate_global_mixin_header, global_mixin_header)); + TRY(write_if_changed(&IDL::generate_global_mixin_implementation, global_mixin_implementation)); + } + + if (!depfile_path.is_empty()) { auto depfile = TRY(Core::File::open_file_or_standard_stream(depfile_path, Core::File::OpenMode::Write)); StringBuilder depfile_builder; - depfile_builder.append(depfile_target.is_null() ? output_path : depfile_target); + bool first_file = true; + 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 }) { + if (!s.is_empty()) { + if (!first_file) { + depfile_builder.append(' '); + } + if (!depfile_prefix.is_empty()) + depfile_builder.append(LexicalPath::join(depfile_prefix, s).string()); + else + depfile_builder.append(s); + first_file = false; + } + } depfile_builder.append(':'); for (auto const& path : parser.imported_files()) { depfile_builder.append(" \\\n "sv); diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/generate_idl_bindings.gni b/Meta/gn/secondary/Userland/Libraries/LibWeb/generate_idl_bindings.gni index e8491c6c64..0964db640b 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/generate_idl_bindings.gni +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/generate_idl_bindings.gni @@ -16,6 +16,9 @@ # type (required) string # "global", "iterable", "namespace", or "standard" # +# include_dirs (optional) [string] +# List of directories to look for imported IDL files in +# # Example use: # # standard_idl_files = [ @@ -38,320 +41,87 @@ import("//Meta/gn/build/compiled_action.gni") -# FIXME: rewrite these in terms of action_foreach -template("_invoke_bindings_generator") { - # FIXME: Can we update the bindings generator to output the .h and .cpp at the same time? - - assert(defined(invoker.type), "$target_name must have 'type' defined") - - # FIXME: This is pretty gross. Can we make the source file name match the command line argument to the generator more closely? - # GN can't (and probably shouldn't) siwzzle our strings for us in this way automagically - assert(defined(invoker.type_filename_fragment), - "$target_name must have 'type_filename_fragment' defined") - assert(defined(invoker.name), "$target_name must have 'name' defined") - assert(defined(invoker.path), "$target_name must have 'path' defined") - - forward_variables_from(invoker, - [ - "configs", - "include_dirs", - "public_configs", - "testonly", - "visibility", - ]) - - if (!defined(include_dirs)) { - include_dirs = [ "//Userland/Libraries" ] - } - rel_include_dirs = [] - foreach(d, include_dirs) { - rel_include_dirs += [ - "-i", - rebase_path(d, root_build_dir), - ] - } - - name_and_type = string_replace(invoker.name + "_" + invoker.type, "-", "_") - type = invoker.type - gen_dir = get_label_info("//Userland/Libraries/LibWeb", "target_gen_dir") - - out_name = get_path_info(invoker.path, "name") - - compiled_action(name_and_type + "_impl") { - tool = "//Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator" - inputs = [ invoker.path + ".idl" ] - outputs = [ gen_dir + "/Bindings/" + out_name + - invoker.type_filename_fragment + ".cpp" ] - depfile = outputs[0] + ".d" - args = [ - "--$type-implementation", - "-o", - rebase_path(outputs[0], root_build_dir), - "--depfile", - rebase_path(depfile, root_build_dir), - "--depfile-target", - rebase_path(outputs[0], root_build_dir), - ] + rel_include_dirs + - [ - rebase_path(inputs[0], root_build_dir), - - # FIXME: Get caller path from invoker? - rebase_path("//Userland/Libraries/LibWeb", root_build_dir), - ] - } - - compiled_action(name_and_type + "_header") { - tool = "//Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator" - inputs = [ invoker.path + ".idl" ] - outputs = [ gen_dir + "/Bindings/" + out_name + - invoker.type_filename_fragment + ".h" ] - depfile = outputs[0] + ".d" - args = [ - "--$type-header", - "-o", - rebase_path(outputs[0], root_build_dir), - "--depfile", - rebase_path(depfile, root_build_dir), - "--depfile-target", - rebase_path(outputs[0], root_build_dir), - ] + rel_include_dirs + - [ - rebase_path(inputs[0], root_build_dir), - - # FIXME: Get caller path from invoker? - rebase_path("//Userland/Libraries/LibWeb", root_build_dir), - ] - } - - source_set("generate_" + name_and_type) { - deps = [ - ":" + name_and_type + "_impl", - ":" + name_and_type + "_header", - ] - } - - source_set(name_and_type + "_sources") { - deps = [ - ":" + name_and_type + "_impl", - ":" + name_and_type + "_header", - ] - sources = get_target_outputs(deps[0]) + get_target_outputs(deps[1]) - configs += [ "//Userland/Libraries/LibWeb:configs" ] - deps += [ "//Userland/Libraries/LibWeb:all_generated" ] - } -} - -# FIXME: Deduplicate these templates -template("_bind_web_namespace") { - forward_variables_from(invoker, - [ - "configs", - "inputs", - "include_dirs", - "outputs", - "public_configs", - "testonly", - "visibility", - ]) - - interface_name = target_name - - _invoke_bindings_generator(interface_name) { - type = "namespace" - type_filename_fragment = "Namespace" - name = interface_name - path = invoker.path - } - - source_set(interface_name + "_sources") { - deps = [ ":" + interface_name + "_namespace_sources" ] - } - - source_set("generate_" + interface_name) { - deps = [ ":generate_" + interface_name + "_namespace" ] - } -} - -# FIXME: Deduplicate these templates -template("_bind_web_iterable_interface") { - forward_variables_from(invoker, - [ - "configs", - "inputs", - "include_dirs", - "outputs", - "public_configs", - "testonly", - "visibility", - ]) - - interface_name = target_name - - _invoke_bindings_generator(interface_name) { - type = "prototype" - type_filename_fragment = "Prototype" - name = interface_name - path = invoker.path - } - - _invoke_bindings_generator(interface_name) { - type = "constructor" - type_filename_fragment = "Constructor" - name = interface_name - path = invoker.path - } - - _invoke_bindings_generator(interface_name) { - type = "iterator-prototype" - type_filename_fragment = "IteratorPrototype" - name = interface_name - path = invoker.path - } - - source_set(interface_name + "_sources") { - deps = [ - ":" + interface_name + "_prototype_sources", - ":" + interface_name + "_constructor_sources", - ":" + interface_name + "_iterator_prototype_sources", - ] - } - - source_set("generate_" + interface_name) { - deps = [ - ":generate_" + interface_name + "_prototype", - ":generate_" + interface_name + "_constructor", - ":generate_" + interface_name + "_iterator_prototype", - ] - } -} - -# FIXME: Deduplicate these templates -template("_bind_web_global_interface") { - forward_variables_from(invoker, - [ - "configs", - "inputs", - "include_dirs", - "outputs", - "public_configs", - "testonly", - "visibility", - ]) - - interface_name = target_name - - _invoke_bindings_generator(interface_name) { - type = "prototype" - type_filename_fragment = "Prototype" - name = interface_name - path = invoker.path - } - - _invoke_bindings_generator(interface_name) { - type = "constructor" - type_filename_fragment = "Constructor" - name = interface_name - path = invoker.path - } - - _invoke_bindings_generator(interface_name) { - type = "global-mixin" - type_filename_fragment = "GlobalMixin" - name = interface_name - path = invoker.path - } - - source_set(interface_name + "_sources") { - deps = [ - ":" + interface_name + "_prototype_sources", - ":" + interface_name + "_constructor_sources", - ":" + interface_name + "_global_mixin_sources", - ] - } - - source_set("generate_" + interface_name) { - deps = [ - ":generate_" + interface_name + "_prototype", - ":generate_" + interface_name + "_constructor", - ":generate_" + interface_name + "_global_mixin", - ] - } -} - -# FIXME: Deduplicate these templates -template("_bind_web_interface") { - forward_variables_from(invoker, - [ - "configs", - "inputs", - "include_dirs", - "outputs", - "public_configs", - "testonly", - "visibility", - ]) - - interface_name = target_name - - _invoke_bindings_generator(interface_name) { - type = "prototype" - type_filename_fragment = "Prototype" - name = interface_name - path = invoker.path - } - - _invoke_bindings_generator(interface_name) { - type = "constructor" - type_filename_fragment = "Constructor" - name = interface_name - path = invoker.path - } - - source_set(interface_name + "_sources") { - deps = [ - ":" + interface_name + "_prototype_sources", - ":" + interface_name + "_constructor_sources", - ] - } - - source_set("generate_" + interface_name) { - deps = [ - ":generate_" + interface_name + "_prototype", - ":generate_" + interface_name + "_constructor", - ] - } -} - template("generate_idl_bindings") { forward_variables_from(invoker, [ "type", "idl_list", + "include_dirs", ]) idl_sources = [] generate_idl = [] + gen_dir = get_label_info("//Userland/Libraries/LibWeb", "target_gen_dir") + + "/Bindings/" foreach(idl, idl_list) { + out_name = get_path_info(idl, "name") path = get_path_info(rebase_path(idl, "//Userland/Libraries/LibWeb"), - "dir") + "/" + get_path_info(idl, "name") + "dir") + "/" + out_name name = string_replace(path, "/", "_") - if (type == "standard") { - _bind_web_interface(name) { - path = path - } - } else if (type == "iterable") { - _bind_web_iterable_interface(name) { - path = path - } - } else if (type == "namespace") { - _bind_web_namespace(name) { - path = path - } + + output_files = [] + if (type == "namespace") { + output_files += [ + "${gen_dir}${out_name}Namespace.h", + "${gen_dir}${out_name}Namespace.cpp", + ] } else { - assert(type == "global") - _bind_web_global_interface(name) { - path = path - } + output_files += [ + "${gen_dir}${out_name}Constructor.h", + "${gen_dir}${out_name}Constructor.cpp", + "${gen_dir}${out_name}Prototype.h", + "${gen_dir}${out_name}Prototype.cpp", + ] } + if (type == "iterable") { + output_files += [ + "${gen_dir}${out_name}IteratorPrototype.h", + "${gen_dir}${out_name}IteratorPrototype.cpp", + ] + } + if (type == "global") { + output_files += [ + "${gen_dir}${out_name}GlobalMixin.h", + "${gen_dir}${out_name}GlobalMixin.cpp", + ] + } + + if (!defined(include_dirs)) { + include_dirs = [ "//Userland/Libraries" ] + } + rel_include_dirs = [] + foreach(d, include_dirs) { + rel_include_dirs += [ + "-i", + rebase_path(d, root_build_dir), + ] + } + + compiled_action("generate_" + name) { + tool = "//Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator" + inputs = [ path + ".idl" ] + outputs = output_files + depfile = "${gen_dir}${out_name}.d" + args = [ + "-o", + rebase_path(gen_dir, root_build_dir), + "--depfile", + rebase_path(depfile, root_build_dir), + ] + rel_include_dirs + + [ + rebase_path(inputs[0], root_build_dir), + rebase_path("//Userland/Libraries/LibWeb", root_build_dir), + ] + } + + source_set(name + "_sources") { + deps = [ + ":generate_" + name, + "//Userland/Libraries/LibWeb:all_generated", + ] + sources = get_target_outputs(deps[0]) + configs += [ "//Userland/Libraries/LibWeb:configs" ] + } + generate_idl += [ ":generate_" + name ] idl_sources += [ ":" + name + "_sources" ] }