Serenity Operating System
1/*
2 * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
4 * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
5 * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 */
9
10#include "Namespaces.h"
11#include <AK/Debug.h>
12#include <AK/LexicalPath.h>
13#include <LibCore/ArgsParser.h>
14#include <LibCore/File.h>
15#include <LibIDL/IDLParser.h>
16#include <LibIDL/Types.h>
17
18extern Vector<StringView> s_header_search_paths;
19
20namespace IDL {
21void generate_constructor_header(IDL::Interface const&, StringBuilder&);
22void generate_constructor_implementation(IDL::Interface const&, StringBuilder&);
23void generate_prototype_header(IDL::Interface const&, StringBuilder&);
24void generate_prototype_implementation(IDL::Interface const&, StringBuilder&);
25void generate_iterator_prototype_header(IDL::Interface const&, StringBuilder&);
26void generate_iterator_prototype_implementation(IDL::Interface const&, StringBuilder&);
27void generate_global_mixin_header(IDL::Interface const&, StringBuilder&);
28void generate_global_mixin_implementation(IDL::Interface const&, StringBuilder&);
29}
30
31ErrorOr<int> serenity_main(Main::Arguments arguments)
32{
33 Core::ArgsParser args_parser;
34 StringView path;
35 StringView import_base_path;
36 StringView output_path = "-"sv;
37 StringView depfile_path;
38 StringView depfile_target;
39 bool constructor_header_mode = false;
40 bool constructor_implementation_mode = false;
41 bool prototype_header_mode = false;
42 bool prototype_implementation_mode = false;
43 bool iterator_prototype_header_mode = false;
44 bool iterator_prototype_implementation_mode = false;
45 bool global_mixin_header_mode = false;
46 bool global_mixin_implementation_mode = false;
47 args_parser.add_option(constructor_header_mode, "Generate the constructor .h file", "constructor-header", 'C');
48 args_parser.add_option(constructor_implementation_mode, "Generate the constructor .cpp file", "constructor-implementation", 'O');
49 args_parser.add_option(prototype_header_mode, "Generate the prototype .h file", "prototype-header", 'P');
50 args_parser.add_option(prototype_implementation_mode, "Generate the prototype .cpp file", "prototype-implementation", 'R');
51 args_parser.add_option(iterator_prototype_header_mode, "Generate the iterator prototype .h file", "iterator-prototype-header", 0);
52 args_parser.add_option(iterator_prototype_implementation_mode, "Generate the iterator prototype .cpp file", "iterator-prototype-implementation", 0);
53 args_parser.add_option(global_mixin_header_mode, "Generate the global object mixin .h file", "global-mixin-header", 0);
54 args_parser.add_option(global_mixin_implementation_mode, "Generate the global object mixin .cpp file", "global-mixin-implementation", 0);
55 args_parser.add_option(Core::ArgsParser::Option {
56 .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
57 .help_string = "Add a header search path passed to the compiler",
58 .long_name = "header-include-path",
59 .short_name = 'i',
60 .value_name = "path",
61 .accept_value = [&](StringView s) {
62 s_header_search_paths.append(s);
63 return true;
64 },
65 });
66 args_parser.add_option(output_path, "Path to output generated file into", "output-path", 'o', "output-path");
67 args_parser.add_option(depfile_path, "Path to write dependency file to", "depfile", 'd', "depfile-path");
68 args_parser.add_option(depfile_target, "Name of target in the depfile (default: output path)", "depfile-target", 't', "target");
69 args_parser.add_positional_argument(path, "IDL file", "idl-file");
70 args_parser.add_positional_argument(import_base_path, "Import base path", "import-base-path", Core::ArgsParser::Required::No);
71 args_parser.parse(arguments);
72
73 auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read));
74
75 LexicalPath lexical_path(path);
76 auto& namespace_ = lexical_path.parts_view().at(lexical_path.parts_view().size() - 2);
77
78 auto data = TRY(file->read_until_eof());
79
80 if (import_base_path.is_null())
81 import_base_path = lexical_path.dirname();
82
83 auto output_file = TRY(Core::File::open_file_or_standard_stream(output_path, Core::File::OpenMode::Write));
84
85 IDL::Parser parser(path, data, import_base_path);
86 auto& interface = parser.parse();
87
88 if (IDL::libweb_interface_namespaces.span().contains_slow(namespace_)) {
89 StringBuilder builder;
90 builder.append(namespace_);
91 builder.append("::"sv);
92 builder.append(interface.name);
93 interface.fully_qualified_name = builder.to_deprecated_string();
94 } else {
95 interface.fully_qualified_name = interface.name;
96 }
97
98 if constexpr (BINDINGS_GENERATOR_DEBUG) {
99 dbgln("Attributes:");
100 for (auto& attribute : interface.attributes) {
101 dbgln(" {}{}{}{} {}",
102 attribute.inherit ? "inherit " : "",
103 attribute.readonly ? "readonly " : "",
104 attribute.type->name(),
105 attribute.type->is_nullable() ? "?" : "",
106 attribute.name);
107 }
108
109 dbgln("Functions:");
110 for (auto& function : interface.functions) {
111 dbgln(" {}{} {}",
112 function.return_type->name(),
113 function.return_type->is_nullable() ? "?" : "",
114 function.name);
115 for (auto& parameter : function.parameters) {
116 dbgln(" {}{} {}",
117 parameter.type->name(),
118 parameter.type->is_nullable() ? "?" : "",
119 parameter.name);
120 }
121 }
122
123 dbgln("Static Functions:");
124 for (auto& function : interface.static_functions) {
125 dbgln(" static {}{} {}",
126 function.return_type->name(),
127 function.return_type->is_nullable() ? "?" : "",
128 function.name);
129 for (auto& parameter : function.parameters) {
130 dbgln(" {}{} {}",
131 parameter.type->name(),
132 parameter.type->is_nullable() ? "?" : "",
133 parameter.name);
134 }
135 }
136 }
137
138 StringBuilder output_builder;
139 if (constructor_header_mode)
140 IDL::generate_constructor_header(interface, output_builder);
141
142 if (constructor_implementation_mode)
143 IDL::generate_constructor_implementation(interface, output_builder);
144
145 if (prototype_header_mode)
146 IDL::generate_prototype_header(interface, output_builder);
147
148 if (prototype_implementation_mode)
149 IDL::generate_prototype_implementation(interface, output_builder);
150
151 if (iterator_prototype_header_mode)
152 IDL::generate_iterator_prototype_header(interface, output_builder);
153
154 if (iterator_prototype_implementation_mode)
155 IDL::generate_iterator_prototype_implementation(interface, output_builder);
156
157 if (global_mixin_header_mode)
158 IDL::generate_global_mixin_header(interface, output_builder);
159
160 if (global_mixin_implementation_mode)
161 IDL::generate_global_mixin_implementation(interface, output_builder);
162
163 TRY(output_file->write_until_depleted(output_builder.string_view().bytes()));
164
165 if (!depfile_path.is_null()) {
166 auto depfile = TRY(Core::File::open_file_or_standard_stream(depfile_path, Core::File::OpenMode::Write));
167
168 StringBuilder depfile_builder;
169 depfile_builder.append(depfile_target.is_null() ? output_path : depfile_target);
170 depfile_builder.append(':');
171 for (auto const& path : parser.imported_files()) {
172 depfile_builder.append(" \\\n "sv);
173 depfile_builder.append(path);
174 }
175 depfile_builder.append('\n');
176 TRY(depfile->write_until_depleted(depfile_builder.string_view().bytes()));
177 }
178 return 0;
179}