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