• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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