/* * Copyright (c) 2020-2021, Andreas Kling * 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 */ #include "IDLGenerators.h" #include "Namespaces.h" #include #include #include #include #include #include template static ErrorOr write_if_changed(GeneratorFunction generator_function, IDL::Interface const& interface, StringView file_path) { StringBuilder output_builder; generator_function(interface, output_builder); auto current_file_or_error = Core::File::open(file_path, Core::File::OpenMode::Read); if (current_file_or_error.is_error() && current_file_or_error.error().code() != ENOENT) return current_file_or_error.release_error(); ByteBuffer current_contents; if (!current_file_or_error.is_error()) current_contents = TRY(current_file_or_error.value()->read_until_eof()); // Only write to disk if contents have changed if (current_contents != output_builder.string_view().bytes()) { auto output_file = TRY(Core::File::open(file_path, Core::File::OpenMode::Write | Core::File::OpenMode::Truncate)); TRY(output_file->write_until_depleted(output_builder.string_view().bytes())); } return {}; } static ErrorOr generate_depfile(StringView depfile_path, ReadonlySpan dependency_paths, ReadonlySpan output_files) { auto depfile = TRY(Core::File::open_file_or_standard_stream(depfile_path, Core::File::OpenMode::Write)); StringBuilder depfile_builder; bool first_output = true; for (auto const& s : output_files) { if (!first_output) depfile_builder.append(' '); depfile_builder.append(s); first_output = false; } depfile_builder.append(':'); for (auto const& path : dependency_paths) { depfile_builder.append(" \\\n "sv); depfile_builder.append(path); } depfile_builder.append('\n'); return depfile->write_until_depleted(depfile_builder.string_view().bytes()); } ErrorOr ladybird_main(Main::Arguments arguments) { Core::ArgsParser args_parser; StringView path; Vector import_base_paths; StringView output_path = "-"sv; StringView depfile_path; 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", .long_name = "header-include-path", .short_name = 'i', .value_name = "path", .accept_value = [&](StringView s) { IDL::g_header_search_paths.append(s); return true; }, }); 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_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_paths, "Import base path", "import-base-path", Core::ArgsParser::Required::No); args_parser.parse(arguments); 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(idl_file->read_until_eof()); if (import_base_paths.is_empty()) import_base_paths.append(lexical_path.dirname()); IDL::Context context; auto module = IDL::Parser::parse(path, data, move(import_base_paths), context); VERIFY(module.interface.has_value()); auto& interface = module.interface.value(); // 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_); builder.append("::"sv); builder.append(interface.implemented_name); interface.fully_qualified_name = builder.to_byte_string(); } else { interface.fully_qualified_name = interface.implemented_name; } if constexpr (BINDINGS_GENERATOR_DEBUG) { interface.dump(); } auto path_prefix = LexicalPath::join(output_path, lexical_path.basename(LexicalPath::StripExtension::Yes)); auto header_path = ByteString::formatted("{}.h", path_prefix); auto implementation_path = ByteString::formatted("{}.cpp", path_prefix); TRY(write_if_changed(&IDL::generate_header, interface, header_path)); TRY(write_if_changed(&IDL::generate_implementation, interface, implementation_path)); if (!depfile_path.is_empty()) { TRY(generate_depfile(depfile_path, module.imported_files, { { header_path, implementation_path } })); } return 0; }