1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <unistd.h>
18 #include <cerrno>
19 #include <cstdio>
20 #include <filesystem>
21 #include <fstream>
22 #include <iostream>
23 #include <queue>
24 #include <regex>
25 #include <sstream>
26 #include <vector>
27
28 #include "declarations.h"
29 #include "struct_parser_generator.h"
30
parse_namespace(const std::string & root_namespace,const std::filesystem::path & input_file_relative_path,std::vector<std::string> * token)31 void parse_namespace(
32 const std::string& root_namespace,
33 const std::filesystem::path& input_file_relative_path,
34 std::vector<std::string>* token) {
35 std::filesystem::path gen_namespace = root_namespace / input_file_relative_path;
36 std::string gen_namespace_str = gen_namespace.u8string();
37 std::regex path_tokenizer("/");
38 auto it = std::sregex_token_iterator(gen_namespace_str.cbegin(), gen_namespace_str.cend(), path_tokenizer, -1);
39 std::sregex_token_iterator it_end = {};
40 for (; it != it_end; ++it) {
41 token->push_back(it->str());
42 }
43 }
44
generate_namespace_open(const std::vector<std::string> & token,std::ostream & output)45 void generate_namespace_open(const std::vector<std::string>& token, std::ostream& output) {
46 for (const auto& ns : token) {
47 output << "namespace " << ns << " {" << std::endl;
48 }
49 }
50
generate_namespace_close(const std::vector<std::string> & token,std::ostream & output)51 void generate_namespace_close(const std::vector<std::string>& token, std::ostream& output) {
52 for (auto it = token.rbegin(); it != token.rend(); ++it) {
53 output << "} //namespace " << *it << std::endl;
54 }
55 }
56
generate_cpp_headers_one_file(const Declarations & decls,const std::filesystem::path & input_file,const std::filesystem::path & include_dir,const std::filesystem::path & out_dir,const std::string & root_namespace)57 bool generate_cpp_headers_one_file(
58 const Declarations& decls,
59 const std::filesystem::path& input_file,
60 const std::filesystem::path& include_dir,
61 const std::filesystem::path& out_dir,
62 const std::string& root_namespace) {
63 auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
64
65 auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
66 auto gen_path = out_dir / gen_relative_path;
67
68 std::filesystem::create_directories(gen_path);
69
70 auto gen_file = gen_path / (input_filename + ".h");
71
72 std::cout << "generating " << gen_file << std::endl;
73
74 std::ofstream out_file;
75 out_file.open(gen_file);
76 if (!out_file.is_open()) {
77 std::cerr << "can't open " << gen_file << std::endl;
78 return false;
79 }
80
81 out_file <<
82 R"(
83 #pragma once
84
85 #include <cstdint>
86 #include <functional>
87 #include <sstream>
88 #include <string>
89 #include <type_traits>
90
91 #include "os/log.h"
92 #include "packet/base_packet_builder.h"
93 #include "packet/bit_inserter.h"
94 #include "packet/custom_field_fixed_size_interface.h"
95 #include "packet/iterator.h"
96 #include "packet/packet_builder.h"
97 #include "packet/packet_struct.h"
98 #include "packet/packet_view.h"
99
100 #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
101 #include "packet/raw_builder.h"
102 #endif
103 #include "packet/parser/checksum_type_checker.h"
104 #include "packet/parser/custom_type_checker.h"
105
106 )";
107
108 for (const auto& c : decls.type_defs_queue_) {
109 if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
110 c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
111 ((CustomFieldDef*)c.second)->GenInclude(out_file);
112 }
113 }
114 out_file << "\n\n";
115
116 std::vector<std::string> namespace_list;
117 parse_namespace(root_namespace, gen_relative_path, &namespace_list);
118 generate_namespace_open(namespace_list, out_file);
119 out_file << "\n\n";
120
121 for (const auto& c : decls.type_defs_queue_) {
122 if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
123 c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
124 ((CustomFieldDef*)c.second)->GenUsing(out_file);
125 }
126 }
127 out_file <<
128 R"(
129
130 using ::bluetooth::packet::BasePacketBuilder;
131 using ::bluetooth::packet::BitInserter;
132 using ::bluetooth::packet::CustomFieldFixedSizeInterface;
133 using ::bluetooth::packet::CustomTypeChecker;
134 using ::bluetooth::packet::Iterator;
135 using ::bluetooth::packet::kLittleEndian;
136 using ::bluetooth::packet::PacketBuilder;
137 using ::bluetooth::packet::PacketStruct;
138 using ::bluetooth::packet::PacketView;
139
140 #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
141 using ::bluetooth::packet::RawBuilder;
142 #endif
143
144 using ::bluetooth::packet::parser::ChecksumTypeChecker;
145
146 )";
147
148 for (const auto& e : decls.type_defs_queue_) {
149 if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
150 const auto* enum_def = static_cast<const EnumDef*>(e.second);
151 EnumGen gen(*enum_def);
152 gen.GenDefinition(out_file);
153 out_file << "\n\n";
154 }
155 }
156 for (const auto& e : decls.type_defs_queue_) {
157 if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
158 const auto* enum_def = static_cast<const EnumDef*>(e.second);
159 EnumGen gen(*enum_def);
160 gen.GenLogging(out_file);
161 out_file << "\n\n";
162 }
163 }
164 for (const auto& ch : decls.type_defs_queue_) {
165 if (ch.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
166 const auto* checksum_def = static_cast<const ChecksumDef*>(ch.second);
167 checksum_def->GenChecksumCheck(out_file);
168 }
169 }
170 out_file << "\n/* Done ChecksumChecks */\n";
171
172 for (const auto& c : decls.type_defs_queue_) {
173 if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) {
174 if (c.second->size_ == -1 /* Variable Size */) {
175 const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second);
176 custom_field_def->GenCustomFieldCheck(out_file, decls.is_little_endian);
177 } else { // fixed size
178 const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second);
179 custom_field_def->GenFixedSizeCustomFieldCheck(out_file);
180 }
181 }
182 }
183 out_file << "\n";
184
185 for (auto& s : decls.type_defs_queue_) {
186 if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
187 const auto* struct_def = static_cast<const StructDef*>(s.second);
188 struct_def->GenDefinition(out_file);
189 out_file << "\n";
190 }
191 }
192
193 {
194 StructParserGenerator spg(decls);
195 spg.Generate(out_file);
196 out_file << "\n\n";
197 }
198
199 for (const auto& packet_def : decls.packet_defs_queue_) {
200 packet_def.second->GenParserDefinition(out_file);
201 out_file << "\n\n";
202 }
203
204 for (const auto& packet_def : decls.packet_defs_queue_) {
205 packet_def.second->GenBuilderDefinition(out_file);
206 out_file << "\n\n";
207 }
208
209 if (input_filename == "hci_packets") {
210 out_file << "class Checker { public: static bool IsCommandStatusOpcode(OpCode op_code) {";
211 out_file << "switch (op_code) {";
212 std::set<std::string> op_codes;
213 for (const auto& packet_def : decls.packet_defs_queue_) {
214 auto packet = packet_def.second;
215 auto op_constraint = packet->parent_constraints_.find("op_code");
216 if (op_constraint == packet->parent_constraints_.end()) {
217 auto constraint = packet->parent_constraints_.find("command_op_code");
218 if (constraint == packet->parent_constraints_.end()) {
219 continue;
220 }
221 if (packet->HasAncestorNamed("CommandStatus")) {
222 out_file << "case " << std::get<std::string>(constraint->second) << ":";
223 op_codes.erase(std::get<std::string>(constraint->second));
224 }
225 if (packet->HasAncestorNamed("CommandComplete")) {
226 op_codes.erase(std::get<std::string>(constraint->second));
227 }
228 } else {
229 op_codes.insert(std::get<std::string>(op_constraint->second));
230 }
231 }
232 bool unhandled_opcode = false;
233 for (const auto& opcode : op_codes) {
234 unhandled_opcode = true;
235 std::cerr << "Opcode with no Status or Complete " << opcode << std::endl;
236 }
237 if (unhandled_opcode) {
238 ERROR() << "At least one unhandled opcode";
239 }
240 out_file << "return true; default: return false; }}};";
241 }
242
243 generate_namespace_close(namespace_list, out_file);
244
245 out_file.close();
246
247 return true;
248 }
249
250 // Get the out_file shard at a symbol_count
get_out_file(size_t symbol_count,size_t symbol_total,std::vector<std::ofstream> * out_files)251 std::ofstream& get_out_file(size_t symbol_count, size_t symbol_total, std::vector<std::ofstream>* out_files) {
252 auto symbols_per_shard = symbol_total / out_files->size();
253 auto file_index = std::min(symbol_count / symbols_per_shard, out_files->size() - 1);
254 return out_files->at(file_index);
255 }
256
generate_pybind11_sources_one_file(const Declarations & decls,const std::filesystem::path & input_file,const std::filesystem::path & include_dir,const std::filesystem::path & out_dir,const std::string & root_namespace,size_t num_shards)257 bool generate_pybind11_sources_one_file(
258 const Declarations& decls,
259 const std::filesystem::path& input_file,
260 const std::filesystem::path& include_dir,
261 const std::filesystem::path& out_dir,
262 const std::string& root_namespace,
263 size_t num_shards) {
264 auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
265
266 auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
267 auto gen_path = out_dir / gen_relative_path;
268
269 std::filesystem::create_directories(gen_path);
270
271 auto gen_relative_header = gen_relative_path / (input_filename + ".h");
272
273 std::vector<std::string> namespace_list;
274 parse_namespace(root_namespace, gen_relative_path, &namespace_list);
275
276 std::vector<std::ofstream> out_file_shards(num_shards);
277 for (size_t i = 0; i < out_file_shards.size(); i++) {
278 auto filename = gen_path / (input_filename + "_python3_shard_" + std::to_string(i) + ".cc");
279 std::cout << "generating " << filename << std::endl;
280 auto& out_file = out_file_shards[i];
281 out_file.open(filename);
282 if (!out_file.is_open()) {
283 std::cerr << "can't open " << filename << std::endl;
284 return false;
285 }
286 out_file << "#include <pybind11/pybind11.h>\n";
287 out_file << "#include <pybind11/stl.h>\n";
288 out_file << "\n\n";
289 out_file << "#include " << gen_relative_header << "\n";
290 out_file << "\n\n";
291 out_file << "#include \"packet/raw_builder.h\"\n";
292 out_file << "\n\n";
293
294 for (const auto& c : decls.type_defs_queue_) {
295 if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) {
296 const auto* custom_def = static_cast<const CustomFieldDef*>(c.second);
297 custom_def->GenPyBind11Include(out_file);
298 }
299 }
300
301 out_file << "\n\n";
302
303 generate_namespace_open(namespace_list, out_file);
304 out_file << "\n\n";
305
306 for (const auto& c : decls.type_defs_queue_) {
307 if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
308 c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
309 const auto* custom_def = static_cast<const CustomFieldDef*>(c.second);
310 custom_def->GenUsing(out_file);
311 }
312 }
313 out_file << "\n\n";
314
315 out_file << "using ::bluetooth::packet::BasePacketBuilder;";
316 out_file << "using ::bluetooth::packet::BitInserter;";
317 out_file << "using ::bluetooth::packet::CustomTypeChecker;";
318 out_file << "using ::bluetooth::packet::Iterator;";
319 out_file << "using ::bluetooth::packet::kLittleEndian;";
320 out_file << "using ::bluetooth::packet::PacketBuilder;";
321 out_file << "using ::bluetooth::packet::BaseStruct;";
322 out_file << "using ::bluetooth::packet::PacketStruct;";
323 out_file << "using ::bluetooth::packet::PacketView;";
324 out_file << "using ::bluetooth::packet::RawBuilder;";
325 out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
326 out_file << "\n\n";
327
328 out_file << "namespace py = pybind11;\n\n";
329
330 out_file << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(py::module& m) {\n\n";
331 }
332 size_t symbol_total = 0;
333 // Only count types that will be generated
334 for (const auto& e : decls.type_defs_queue_) {
335 if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
336 symbol_total++;
337 } else if (e.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
338 symbol_total++;
339 }
340 }
341 // View and builder are counted separately
342 symbol_total += decls.packet_defs_queue_.size() * 2;
343 size_t symbol_count = 0;
344
345 for (const auto& e : decls.type_defs_queue_) {
346 if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
347 const auto* enum_def = static_cast<const EnumDef*>(e.second);
348 EnumGen gen(*enum_def);
349 auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
350 gen.GenDefinitionPybind11(out_file);
351 out_file << "\n\n";
352 symbol_count++;
353 }
354 }
355
356 for (const auto& s : decls.type_defs_queue_) {
357 if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
358 const auto* struct_def = static_cast<const StructDef*>(s.second);
359 auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
360 struct_def->GenDefinitionPybind11(out_file);
361 out_file << "\n";
362 symbol_count++;
363 }
364 }
365
366 for (const auto& packet_def : decls.packet_defs_queue_) {
367 auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
368 packet_def.second->GenParserDefinitionPybind11(out_file);
369 out_file << "\n\n";
370 symbol_count++;
371 }
372
373 for (const auto& p : decls.packet_defs_queue_) {
374 auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
375 p.second->GenBuilderDefinitionPybind11(out_file);
376 out_file << "\n\n";
377 symbol_count++;
378 }
379
380 for (auto& out_file : out_file_shards) {
381 out_file << "}\n\n";
382 generate_namespace_close(namespace_list, out_file);
383 }
384
385 auto gen_file_main = gen_path / (input_filename + "_python3.cc");
386 std::ofstream out_file_main;
387 out_file_main.open(gen_file_main);
388 if (!out_file_main.is_open()) {
389 std::cerr << "can't open " << gen_file_main << std::endl;
390 return false;
391 }
392 out_file_main << "#include <pybind11/pybind11.h>\n";
393 generate_namespace_open(namespace_list, out_file_main);
394
395 out_file_main << "namespace py = pybind11;\n\n";
396
397 for (size_t i = 0; i < out_file_shards.size(); i++) {
398 out_file_main << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i)
399 << "(py::module& m);\n";
400 }
401
402 out_file_main << "void define_" << input_filename << "_submodule(py::module& m) {\n\n";
403 for (size_t i = 0; i < out_file_shards.size(); i++) {
404 out_file_main << "define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(m);\n";
405 }
406 out_file_main << "}\n\n";
407
408 generate_namespace_close(namespace_list, out_file_main);
409
410 return true;
411 }
412