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