mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 01:35:08 +02:00
132 lines
5.2 KiB
C++
132 lines
5.2 KiB
C++
/*
|
|
* Copyright (c) 2020-2021, Andreas Kling <andreas@ladybird.org>
|
|
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
|
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
|
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
|
|
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include "IDLGenerators.h"
|
|
#include "Namespaces.h"
|
|
#include <AK/Debug.h>
|
|
#include <AK/LexicalPath.h>
|
|
#include <LibCore/ArgsParser.h>
|
|
#include <LibCore/File.h>
|
|
#include <LibIDL/IDLParser.h>
|
|
#include <LibIDL/Types.h>
|
|
|
|
template<typename GeneratorFunction>
|
|
static ErrorOr<void> 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<void> generate_depfile(StringView depfile_path, ReadonlySpan<ByteString> dependency_paths, ReadonlySpan<ByteString> 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<int> ladybird_main(Main::Arguments arguments)
|
|
{
|
|
Core::ArgsParser args_parser;
|
|
StringView path;
|
|
Vector<ByteString> 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::Parser parser(path, data, move(import_base_paths));
|
|
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_);
|
|
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, parser.imported_files(), { { header_path, implementation_path } }));
|
|
}
|
|
return 0;
|
|
}
|