LibIDL: Support multiple import base paths for resolving imports

This allows us to specify multiple base paths to look for imported IDL
files in. This will allow us to import IDL files from sources and from
the Build directory (i.e. for generated IDL files).
This commit is contained in:
Luke Wilde 2024-11-14 16:13:44 +00:00 committed by Andreas Kling
commit 300f212044
Notes: github-actions[bot] 2024-11-14 18:51:33 +00:00
4 changed files with 49 additions and 21 deletions

View file

@ -160,9 +160,22 @@ HashMap<ByteString, ByteString> Parser::parse_extended_attributes()
static HashTable<ByteString> import_stack; static HashTable<ByteString> import_stack;
Optional<Interface&> Parser::resolve_import(auto path) Optional<Interface&> Parser::resolve_import(auto path)
{ {
auto include_path = LexicalPath::join(import_base_path, path).string(); ByteString include_path;
if (!FileSystem::exists(include_path)) for (auto import_base_path : import_base_paths) {
report_parsing_error(ByteString::formatted("{}: No such file or directory", include_path), filename, input, lexer.tell()); auto maybe_include_path = LexicalPath::join(import_base_path, path).string();
if (!FileSystem::exists(maybe_include_path))
continue;
include_path = maybe_include_path;
break;
}
if (include_path.is_empty()) {
StringBuilder error_message;
error_message.appendff("Failed to find {} in the following directories:\n", path);
error_message.join('\n', import_base_paths);
report_parsing_error(error_message.to_byte_string(), filename, input, lexer.tell());
}
auto real_path_error_or = FileSystem::real_path(include_path); auto real_path_error_or = FileSystem::real_path(include_path);
if (real_path_error_or.is_error()) if (real_path_error_or.is_error())
@ -183,7 +196,7 @@ Optional<Interface&> Parser::resolve_import(auto path)
auto data_or_error = file_or_error.value()->read_until_eof(); auto data_or_error = file_or_error.value()->read_until_eof();
if (data_or_error.is_error()) if (data_or_error.is_error())
report_parsing_error(ByteString::formatted("Failed to read {}: {}", real_path, data_or_error.error()), filename, input, lexer.tell()); report_parsing_error(ByteString::formatted("Failed to read {}: {}", real_path, data_or_error.error()), filename, input, lexer.tell());
auto& result = Parser(this, real_path, data_or_error.value(), import_base_path).parse(); auto& result = Parser(this, real_path, data_or_error.value(), import_base_paths).parse();
import_stack.remove(real_path); import_stack.remove(real_path);
top_level_resolved_imports().set(real_path, &result); top_level_resolved_imports().set(real_path, &result);
@ -1229,16 +1242,16 @@ Interface& Parser::parse()
return interface; return interface;
} }
Parser::Parser(ByteString filename, StringView contents, ByteString import_base_path) Parser::Parser(ByteString filename, StringView contents, Vector<StringView> import_base_paths)
: import_base_path(move(import_base_path)) : import_base_paths(move(import_base_paths))
, filename(move(filename)) , filename(move(filename))
, input(contents) , input(contents)
, lexer(input) , lexer(input)
{ {
} }
Parser::Parser(Parser* parent, ByteString filename, StringView contents, ByteString import_base_path) Parser::Parser(Parser* parent, ByteString filename, StringView contents, Vector<StringView> import_base_paths)
: import_base_path(move(import_base_path)) : import_base_paths(move(import_base_paths))
, filename(move(filename)) , filename(move(filename))
, input(contents) , input(contents)
, lexer(input) , lexer(input)

View file

@ -17,7 +17,7 @@ namespace IDL {
class Parser { class Parser {
public: public:
Parser(ByteString filename, StringView contents, ByteString import_base_path); Parser(ByteString filename, StringView contents, Vector<StringView> import_base_paths);
Interface& parse(); Interface& parse();
Vector<ByteString> imported_files() const; Vector<ByteString> imported_files() const;
@ -35,7 +35,7 @@ private:
Yes, Yes,
}; };
Parser(Parser* parent, ByteString filename, StringView contents, ByteString import_base_path); Parser(Parser* parent, ByteString filename, StringView contents, Vector<StringView> import_base_path);
void assert_specific(char ch); void assert_specific(char ch);
void assert_string(StringView expected); void assert_string(StringView expected);
@ -68,7 +68,7 @@ private:
ByteString parse_identifier_ending_with_space(); ByteString parse_identifier_ending_with_space();
ByteString parse_identifier_ending_with_space_or(auto... possible_terminating_characters); ByteString parse_identifier_ending_with_space_or(auto... possible_terminating_characters);
ByteString import_base_path; Vector<StringView> import_base_paths;
ByteString filename; ByteString filename;
StringView input; StringView input;
LineTrackingLexer lexer; LineTrackingLexer lexer;

View file

@ -21,7 +21,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
{ {
Core::ArgsParser args_parser; Core::ArgsParser args_parser;
StringView path; StringView path;
StringView import_base_path; Vector<StringView> import_base_paths;
StringView output_path = "-"sv; StringView output_path = "-"sv;
StringView depfile_path; StringView depfile_path;
StringView depfile_prefix; StringView depfile_prefix;
@ -41,7 +41,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
args_parser.add_option(depfile_path, "Path to write dependency file to", "depfile", 'd', "depfile-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_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(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.add_positional_argument(import_base_paths, "Import base path", "import-base-path", Core::ArgsParser::Required::No);
args_parser.parse(arguments); args_parser.parse(arguments);
auto idl_file = TRY(Core::File::open(path, Core::File::OpenMode::Read)); auto idl_file = TRY(Core::File::open(path, Core::File::OpenMode::Read));
@ -51,10 +51,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
auto data = TRY(idl_file->read_until_eof()); auto data = TRY(idl_file->read_until_eof());
if (import_base_path.is_null()) if (import_base_paths.is_empty())
import_base_path = lexical_path.dirname(); import_base_paths.append(lexical_path.dirname());
IDL::Parser parser(path, data, import_base_path); IDL::Parser parser(path, data, move(import_base_paths));
auto& interface = parser.parse(); auto& interface = parser.parse();
// If the interface name is the same as its namespace, qualify the name in the generated code. // If the interface name is the same as its namespace, qualify the name in the generated code.

View file

@ -335,18 +335,33 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
Core::ArgsParser args_parser; Core::ArgsParser args_parser;
StringView output_path; StringView output_path;
StringView base_path; Vector<ByteString> base_paths;
Vector<ByteString> paths; Vector<ByteString> paths;
args_parser.add_option(output_path, "Path to output generated files 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(base_path, "Path to root of IDL file tree", "base-path", 'b', "base-path"); args_parser.add_option(Core::ArgsParser::Option {
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
.help_string = "Path to root of IDL file tree(s)",
.long_name = "base-path",
.short_name = 'b',
.value_name = "base-path",
.accept_value = [&](StringView s) {
base_paths.append(s);
return true;
},
});
args_parser.add_positional_argument(paths, "Paths of every IDL file that could be Exposed", "paths"); args_parser.add_positional_argument(paths, "Paths of every IDL file that could be Exposed", "paths");
args_parser.parse(arguments); args_parser.parse(arguments);
VERIFY(!paths.is_empty()); VERIFY(!paths.is_empty());
VERIFY(!base_path.is_empty()); VERIFY(!base_paths.is_empty());
LexicalPath const lexical_base(base_path); Vector<StringView> lexical_bases;
for (auto const& base_path : base_paths) {
VERIFY(!base_path.is_empty());
LexicalPath lexical_path(base_path);
lexical_bases.append(lexical_path.string());
}
// Read in all IDL files, we must own the storage for all of these for the lifetime of the program // Read in all IDL files, we must own the storage for all of these for the lifetime of the program
Vector<ByteString> file_contents; Vector<ByteString> file_contents;
@ -372,7 +387,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
for (size_t i = 0; i < paths.size(); ++i) { for (size_t i = 0; i < paths.size(); ++i) {
auto const& path = paths[i]; auto const& path = paths[i];
IDL::Parser parser(path, file_contents[i], lexical_base.string()); IDL::Parser parser(path, file_contents[i], lexical_bases);
auto& interface = parser.parse(); auto& interface = parser.parse();
if (interface.name.is_empty()) { if (interface.name.is_empty()) {
s_error_string = ByteString::formatted("Interface for file {} missing", path); s_error_string = ByteString::formatted("Interface for file {} missing", path);