| /* |
| * Copyright 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <unistd.h> |
| #include <cerrno> |
| #include <cstdio> |
| #include <filesystem> |
| #include <fstream> |
| #include <iostream> |
| #include <queue> |
| #include <regex> |
| #include <sstream> |
| #include <vector> |
| |
| #include "declarations.h" |
| #include "struct_parser_generator.h" |
| |
| void parse_namespace( |
| const std::string& root_namespace, |
| const std::filesystem::path& input_file_relative_path, |
| std::vector<std::string>* token) { |
| std::filesystem::path gen_namespace = root_namespace / input_file_relative_path; |
| for (auto it = gen_namespace.begin(); it != gen_namespace.end(); ++it) { |
| token->push_back(it->string()); |
| } |
| } |
| |
| void generate_namespace_open(const std::vector<std::string>& token, std::ostream& output) { |
| for (const auto& ns : token) { |
| output << "namespace " << ns << " {" << std::endl; |
| } |
| } |
| |
| void generate_namespace_close(const std::vector<std::string>& token, std::ostream& output) { |
| for (auto it = token.rbegin(); it != token.rend(); ++it) { |
| output << "} //namespace " << *it << std::endl; |
| } |
| } |
| |
| bool 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) { |
| auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path(); |
| |
| auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl")); |
| auto gen_path = out_dir / gen_relative_path; |
| |
| std::filesystem::create_directories(gen_path); |
| |
| auto gen_file = gen_path / (input_filename + ".h"); |
| |
| std::cout << "generating " << gen_file << std::endl; |
| |
| std::ofstream out_file; |
| out_file.open(gen_file); |
| if (!out_file.is_open()) { |
| std::cerr << "can't open " << gen_file << std::endl; |
| return false; |
| } |
| |
| out_file << |
| R"( |
| #pragma once |
| |
| #include <cstdint> |
| #include <functional> |
| #include <sstream> |
| #include <string> |
| #include <type_traits> |
| |
| #include "os/log.h" |
| #include "packet/base_packet_builder.h" |
| #include "packet/bit_inserter.h" |
| #include "packet/custom_field_fixed_size_interface.h" |
| #include "packet/iterator.h" |
| #include "packet/packet_builder.h" |
| #include "packet/packet_struct.h" |
| #include "packet/packet_view.h" |
| |
| #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET) |
| #include "packet/raw_builder.h" |
| #endif |
| #include "packet/parser/checksum_type_checker.h" |
| #include "packet/parser/custom_type_checker.h" |
| |
| )"; |
| |
| for (const auto& c : decls.type_defs_queue_) { |
| if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM || |
| c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) { |
| ((CustomFieldDef*)c.second)->GenInclude(out_file); |
| } |
| } |
| out_file << "\n\n"; |
| |
| std::vector<std::string> namespace_list; |
| parse_namespace(root_namespace, gen_relative_path, &namespace_list); |
| generate_namespace_open(namespace_list, out_file); |
| out_file << "\n\n"; |
| |
| for (const auto& c : decls.type_defs_queue_) { |
| if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM || |
| c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) { |
| ((CustomFieldDef*)c.second)->GenUsing(out_file); |
| } |
| } |
| out_file << |
| R"( |
| |
| using ::bluetooth::packet::BasePacketBuilder; |
| using ::bluetooth::packet::BitInserter; |
| using ::bluetooth::packet::CustomFieldFixedSizeInterface; |
| using ::bluetooth::packet::CustomTypeChecker; |
| using ::bluetooth::packet::Iterator; |
| using ::bluetooth::packet::kLittleEndian; |
| using ::bluetooth::packet::PacketBuilder; |
| using ::bluetooth::packet::PacketStruct; |
| using ::bluetooth::packet::PacketView; |
| |
| #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET) |
| using ::bluetooth::packet::RawBuilder; |
| #endif |
| |
| using ::bluetooth::packet::parser::ChecksumTypeChecker; |
| |
| )"; |
| |
| for (const auto& e : decls.type_defs_queue_) { |
| if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) { |
| const auto* enum_def = static_cast<const EnumDef*>(e.second); |
| EnumGen gen(*enum_def); |
| gen.GenDefinition(out_file); |
| out_file << "\n\n"; |
| } |
| } |
| for (const auto& e : decls.type_defs_queue_) { |
| if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) { |
| const auto* enum_def = static_cast<const EnumDef*>(e.second); |
| EnumGen gen(*enum_def); |
| gen.GenLogging(out_file); |
| out_file << "\n\n"; |
| } |
| } |
| for (const auto& ch : decls.type_defs_queue_) { |
| if (ch.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) { |
| const auto* checksum_def = static_cast<const ChecksumDef*>(ch.second); |
| checksum_def->GenChecksumCheck(out_file); |
| } |
| } |
| out_file << "\n/* Done ChecksumChecks */\n"; |
| |
| for (const auto& c : decls.type_defs_queue_) { |
| if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) { |
| if (c.second->size_ == -1 /* Variable Size */) { |
| const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second); |
| custom_field_def->GenCustomFieldCheck(out_file, decls.is_little_endian); |
| } else { // fixed size |
| const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second); |
| custom_field_def->GenFixedSizeCustomFieldCheck(out_file); |
| } |
| } |
| } |
| out_file << "\n"; |
| |
| for (auto& s : decls.type_defs_queue_) { |
| if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) { |
| const auto* struct_def = static_cast<const StructDef*>(s.second); |
| struct_def->GenDefinition(out_file); |
| out_file << "\n"; |
| } |
| } |
| |
| { |
| StructParserGenerator spg(decls); |
| spg.Generate(out_file); |
| out_file << "\n\n"; |
| } |
| |
| for (const auto& packet_def : decls.packet_defs_queue_) { |
| packet_def.second->GenParserDefinition(out_file); |
| out_file << "\n\n"; |
| } |
| |
| for (const auto& packet_def : decls.packet_defs_queue_) { |
| packet_def.second->GenBuilderDefinition(out_file); |
| out_file << "\n\n"; |
| } |
| |
| if (input_filename == "hci_packets") { |
| out_file << "class Checker { public: static bool IsCommandStatusOpcode(OpCode op_code) {"; |
| out_file << "switch (op_code) {"; |
| std::set<std::string> op_codes; |
| for (const auto& packet_def : decls.packet_defs_queue_) { |
| auto packet = packet_def.second; |
| auto op_constraint = packet->parent_constraints_.find("op_code"); |
| if (op_constraint == packet->parent_constraints_.end()) { |
| auto constraint = packet->parent_constraints_.find("command_op_code"); |
| if (constraint == packet->parent_constraints_.end()) { |
| continue; |
| } |
| if (packet->HasAncestorNamed("CommandStatus")) { |
| out_file << "case " << std::get<std::string>(constraint->second) << ":"; |
| op_codes.erase(std::get<std::string>(constraint->second)); |
| } |
| if (packet->HasAncestorNamed("CommandComplete")) { |
| op_codes.erase(std::get<std::string>(constraint->second)); |
| } |
| } else { |
| op_codes.insert(std::get<std::string>(op_constraint->second)); |
| } |
| } |
| bool unhandled_opcode = false; |
| for (const auto& opcode : op_codes) { |
| unhandled_opcode = true; |
| std::cerr << "Opcode with no Status or Complete " << opcode << std::endl; |
| } |
| if (unhandled_opcode) { |
| ERROR() << "At least one unhandled opcode"; |
| } |
| out_file << "return true; default: return false; }}};"; |
| } |
| |
| generate_namespace_close(namespace_list, out_file); |
| |
| out_file.close(); |
| |
| return true; |
| } |
| |
| // Get the out_file shard at a symbol_count |
| std::ofstream& get_out_file(size_t symbol_count, size_t symbol_total, std::vector<std::ofstream>* out_files) { |
| auto symbols_per_shard = symbol_total / out_files->size(); |
| auto file_index = std::min(symbol_count / symbols_per_shard, out_files->size() - 1); |
| return out_files->at(file_index); |
| } |
| |
| bool 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) { |
| auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path(); |
| |
| auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl")); |
| auto gen_path = out_dir / gen_relative_path; |
| |
| std::filesystem::create_directories(gen_path); |
| |
| auto gen_relative_header = gen_relative_path / (input_filename + ".h"); |
| |
| std::vector<std::string> namespace_list; |
| parse_namespace(root_namespace, gen_relative_path, &namespace_list); |
| |
| std::vector<std::ofstream> out_file_shards(num_shards); |
| for (size_t i = 0; i < out_file_shards.size(); i++) { |
| auto filename = gen_path / (input_filename + "_python3_shard_" + std::to_string(i) + ".cc"); |
| std::cout << "generating " << filename << std::endl; |
| auto& out_file = out_file_shards[i]; |
| out_file.open(filename); |
| if (!out_file.is_open()) { |
| std::cerr << "can't open " << filename << std::endl; |
| return false; |
| } |
| out_file << "#include <pybind11/pybind11.h>\n"; |
| out_file << "#include <pybind11/stl.h>\n"; |
| out_file << "\n\n"; |
| out_file << "#include " << gen_relative_header << "\n"; |
| out_file << "\n\n"; |
| out_file << "#include \"packet/raw_builder.h\"\n"; |
| out_file << "\n\n"; |
| |
| for (const auto& c : decls.type_defs_queue_) { |
| if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) { |
| const auto* custom_def = static_cast<const CustomFieldDef*>(c.second); |
| custom_def->GenPyBind11Include(out_file); |
| } |
| } |
| |
| out_file << "\n\n"; |
| |
| generate_namespace_open(namespace_list, out_file); |
| out_file << "\n\n"; |
| |
| for (const auto& c : decls.type_defs_queue_) { |
| if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM || |
| c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) { |
| const auto* custom_def = static_cast<const CustomFieldDef*>(c.second); |
| custom_def->GenUsing(out_file); |
| } |
| } |
| out_file << "\n\n"; |
| |
| out_file << "using ::bluetooth::packet::BasePacketBuilder;"; |
| out_file << "using ::bluetooth::packet::BitInserter;"; |
| out_file << "using ::bluetooth::packet::CustomTypeChecker;"; |
| out_file << "using ::bluetooth::packet::Iterator;"; |
| out_file << "using ::bluetooth::packet::kLittleEndian;"; |
| out_file << "using ::bluetooth::packet::PacketBuilder;"; |
| out_file << "using ::bluetooth::packet::BaseStruct;"; |
| out_file << "using ::bluetooth::packet::PacketStruct;"; |
| out_file << "using ::bluetooth::packet::PacketView;"; |
| out_file << "using ::bluetooth::packet::RawBuilder;"; |
| out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;"; |
| out_file << "\n\n"; |
| |
| out_file << "namespace py = pybind11;\n\n"; |
| |
| out_file << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(py::module& m) {\n\n"; |
| } |
| size_t symbol_total = 0; |
| // Only count types that will be generated |
| for (const auto& e : decls.type_defs_queue_) { |
| if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) { |
| symbol_total++; |
| } else if (e.second->GetDefinitionType() == TypeDef::Type::STRUCT) { |
| symbol_total++; |
| } |
| } |
| // View and builder are counted separately |
| symbol_total += decls.packet_defs_queue_.size() * 2; |
| size_t symbol_count = 0; |
| |
| for (const auto& e : decls.type_defs_queue_) { |
| if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) { |
| const auto* enum_def = static_cast<const EnumDef*>(e.second); |
| EnumGen gen(*enum_def); |
| auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards); |
| gen.GenDefinitionPybind11(out_file); |
| out_file << "\n\n"; |
| symbol_count++; |
| } |
| } |
| |
| for (const auto& s : decls.type_defs_queue_) { |
| if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) { |
| const auto* struct_def = static_cast<const StructDef*>(s.second); |
| auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards); |
| struct_def->GenDefinitionPybind11(out_file); |
| out_file << "\n"; |
| symbol_count++; |
| } |
| } |
| |
| for (const auto& packet_def : decls.packet_defs_queue_) { |
| auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards); |
| packet_def.second->GenParserDefinitionPybind11(out_file); |
| out_file << "\n\n"; |
| symbol_count++; |
| } |
| |
| for (const auto& p : decls.packet_defs_queue_) { |
| auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards); |
| p.second->GenBuilderDefinitionPybind11(out_file); |
| out_file << "\n\n"; |
| symbol_count++; |
| } |
| |
| for (auto& out_file : out_file_shards) { |
| out_file << "}\n\n"; |
| generate_namespace_close(namespace_list, out_file); |
| } |
| |
| auto gen_file_main = gen_path / (input_filename + "_python3.cc"); |
| std::ofstream out_file_main; |
| out_file_main.open(gen_file_main); |
| if (!out_file_main.is_open()) { |
| std::cerr << "can't open " << gen_file_main << std::endl; |
| return false; |
| } |
| out_file_main << "#include <pybind11/pybind11.h>\n"; |
| generate_namespace_open(namespace_list, out_file_main); |
| |
| out_file_main << "namespace py = pybind11;\n\n"; |
| |
| for (size_t i = 0; i < out_file_shards.size(); i++) { |
| out_file_main << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i) |
| << "(py::module& m);\n"; |
| } |
| |
| out_file_main << "void define_" << input_filename << "_submodule(py::module& m) {\n\n"; |
| for (size_t i = 0; i < out_file_shards.size(); i++) { |
| out_file_main << "define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(m);\n"; |
| } |
| out_file_main << "}\n\n"; |
| |
| generate_namespace_close(namespace_list, out_file_main); |
| |
| return true; |
| } |