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,bool generate_fuzzing,bool generate_tests,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 bool generate_fuzzing,
56 bool generate_tests,
57 const std::filesystem::path& input_file,
58 const std::filesystem::path& include_dir,
59 const std::filesystem::path& out_dir,
60 const std::string& root_namespace) {
61 auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
62
63 auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
64 auto gen_path = out_dir / gen_relative_path;
65
66 std::filesystem::create_directories(gen_path);
67
68 auto gen_file = gen_path / (input_filename + ".h");
69
70 std::cout << "generating " << gen_file << std::endl;
71
72 std::ofstream out_file;
73 out_file.open(gen_file);
74 if (!out_file.is_open()) {
75 std::cerr << "can't open " << gen_file << std::endl;
76 return false;
77 }
78
79 out_file <<
80 R"(
81 #pragma once
82
83 #include <cstdint>
84 #include <functional>
85 #include <iomanip>
86 #include <optional>
87 #include <sstream>
88 #include <string>
89 #include <type_traits>
90
91 #include "packet/base_packet_builder.h"
92 #include "packet/bit_inserter.h"
93 #include "packet/custom_field_fixed_size_interface.h"
94 #include "packet/iterator.h"
95 #include "packet/packet_builder.h"
96 #include "packet/packet_struct.h"
97 #include "packet/packet_view.h"
98 #include "packet/checksum_type_checker.h"
99 #include "packet/custom_type_checker.h"
100
101 #if __has_include(<bluetooth/log.h>)
102
103 #include <bluetooth/log.h>
104
105 #ifndef ASSERT
106 #define ASSERT(cond) bluetooth::log::assert_that(cond, #cond)
107 #endif // !defined(ASSERT)
108
109 #else
110
111 #ifndef ASSERT
112 #define ASSERT(cond) assert(cond)
113 #endif // !defined(ASSERT)
114
115 #endif // __has_include(<bluetooth/log.h>)
116 )";
117
118 if (generate_fuzzing || generate_tests) {
119 out_file <<
120 R"(
121
122 #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
123 #include "packet/raw_builder.h"
124 #endif
125 )";
126 }
127
128 for (const auto& c : decls.type_defs_queue_) {
129 if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
130 c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
131 ((CustomFieldDef*)c.second)->GenInclude(out_file);
132 }
133 }
134 out_file << "\n\n";
135
136 std::vector<std::string> namespace_list;
137 parse_namespace(root_namespace, gen_relative_path, &namespace_list);
138 generate_namespace_open(namespace_list, out_file);
139 out_file << "\n\n";
140
141 for (const auto& c : decls.type_defs_queue_) {
142 if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
143 c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
144 ((CustomFieldDef*)c.second)->GenUsing(out_file);
145 }
146 }
147
148 out_file <<
149 R"(
150
151 using ::bluetooth::packet::BasePacketBuilder;
152 using ::bluetooth::packet::BitInserter;
153 using ::bluetooth::packet::CustomFieldFixedSizeInterface;
154 using ::bluetooth::packet::CustomTypeChecker;
155 using ::bluetooth::packet::Iterator;
156 using ::bluetooth::packet::kLittleEndian;
157 using ::bluetooth::packet::PacketBuilder;
158 using ::bluetooth::packet::PacketStruct;
159 using ::bluetooth::packet::PacketView;
160 using ::bluetooth::packet::parser::ChecksumTypeChecker;
161 )";
162
163 if (generate_fuzzing || generate_tests) {
164 out_file <<
165 R"(
166 #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
167 using ::bluetooth::packet::RawBuilder;
168 #endif
169 )";
170 }
171
172 for (const auto& e : decls.type_defs_queue_) {
173 if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
174 const auto* enum_def = static_cast<const EnumDef*>(e.second);
175 EnumGen gen(*enum_def);
176 gen.GenDefinition(out_file);
177 out_file << "\n\n";
178 }
179 }
180 for (const auto& e : decls.type_defs_queue_) {
181 if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
182 const auto* enum_def = static_cast<const EnumDef*>(e.second);
183 EnumGen gen(*enum_def);
184 gen.GenLogging(out_file);
185 out_file << "\n\n";
186 }
187 }
188 for (const auto& ch : decls.type_defs_queue_) {
189 if (ch.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
190 const auto* checksum_def = static_cast<const ChecksumDef*>(ch.second);
191 checksum_def->GenChecksumCheck(out_file);
192 }
193 }
194 out_file << "\n/* Done ChecksumChecks */\n";
195
196 for (const auto& c : decls.type_defs_queue_) {
197 if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) {
198 if (c.second->size_ == -1 /* Variable Size */) {
199 const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second);
200 custom_field_def->GenCustomFieldCheck(out_file, decls.is_little_endian);
201 } else { // fixed size
202 const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second);
203 custom_field_def->GenFixedSizeCustomFieldCheck(out_file);
204 }
205 }
206 }
207 out_file << "\n";
208
209 for (auto& s : decls.type_defs_queue_) {
210 if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
211 const auto* struct_def = static_cast<const StructDef*>(s.second);
212 struct_def->GenDefinition(out_file);
213 out_file << "\n";
214 }
215 }
216
217 {
218 StructParserGenerator spg(decls);
219 spg.Generate(out_file);
220 out_file << "\n\n";
221 }
222
223 for (const auto& packet_def : decls.packet_defs_queue_) {
224 packet_def.second->GenParserDefinition(out_file, generate_fuzzing, generate_tests);
225 out_file << "\n\n";
226 }
227
228 for (const auto& packet_def : decls.packet_defs_queue_) {
229 packet_def.second->GenBuilderDefinition(out_file, generate_fuzzing, generate_tests);
230 out_file << "\n\n";
231 }
232
233 if (input_filename == "hci_packets") {
234 out_file << "class Checker { public: static bool IsCommandStatusOpcode(OpCode op_code) {";
235 out_file << "switch (op_code) {";
236 std::set<std::string> op_codes;
237 for (const auto& packet_def : decls.packet_defs_queue_) {
238 auto packet = packet_def.second;
239 auto op_constraint = packet->parent_constraints_.find("op_code");
240 if (op_constraint == packet->parent_constraints_.end()) {
241 auto constraint = packet->parent_constraints_.find("command_op_code");
242 if (constraint == packet->parent_constraints_.end()) {
243 continue;
244 }
245 if (packet->HasAncestorNamed("CommandStatus")) {
246 out_file << "case " << std::get<std::string>(constraint->second) << ":";
247 op_codes.erase(std::get<std::string>(constraint->second));
248 }
249 if (packet->HasAncestorNamed("CommandComplete")) {
250 op_codes.erase(std::get<std::string>(constraint->second));
251 }
252 } else {
253 op_codes.insert(std::get<std::string>(op_constraint->second));
254 }
255 }
256 bool unhandled_opcode = false;
257 for (const auto& opcode : op_codes) {
258 unhandled_opcode = true;
259 std::cerr << "Opcode with no Status or Complete " << opcode << std::endl;
260 }
261 if (unhandled_opcode) {
262 ERROR() << "At least one unhandled opcode";
263 }
264 out_file << "return true; default: return false; }}};";
265 }
266
267 generate_namespace_close(namespace_list, out_file);
268
269 // Generate formatters for all enum declarations.
270 std::string namespace_prefix;
271 for (auto const& fragment : namespace_list) {
272 namespace_prefix += fragment;
273 namespace_prefix += "::";
274 }
275
276 out_file << "#if __has_include(<bluetooth/log.h>)" << std::endl << "namespace fmt {" << std::endl;
277 for (const auto& e : decls.type_defs_queue_) {
278 if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
279 const auto* enum_def = static_cast<const EnumDef*>(e.second);
280 out_file << "template <>" << std::endl
281 << "struct formatter<" << namespace_prefix << enum_def->name_ << ">"
282 << " : enum_formatter<" << namespace_prefix << enum_def->name_ << "> {};"
283 << std::endl;
284 }
285 }
286 out_file << "} // namespace fmt" << std::endl
287 << "#endif // __has_include(<bluetooth/log.h>)" << std::endl;
288
289 out_file.close();
290
291 return true;
292 }
293
294 // 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)295 std::ofstream& get_out_file(size_t symbol_count, size_t symbol_total, std::vector<std::ofstream>* out_files) {
296 auto symbols_per_shard = symbol_total / out_files->size();
297 auto file_index = std::min(symbol_count / symbols_per_shard, out_files->size() - 1);
298 return out_files->at(file_index);
299 }
300