1 /*
2 * Copyright (C) 2017 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 <google/protobuf/compiler/importer.h>
18 #include <google/protobuf/dynamic_message.h>
19 #include <google/protobuf/io/printer.h>
20 #include <google/protobuf/io/zero_copy_stream_impl.h>
21 #include <google/protobuf/stubs/strutil.h>
22 #include <google/protobuf/util/field_comparator.h>
23 #include <google/protobuf/util/message_differencer.h>
24
25 #include <stdio.h>
26
27 #include <fstream>
28 #include <iostream>
29
30 #include "perfetto/base/logging.h"
31
32 using namespace google::protobuf;
33 using namespace google::protobuf::compiler;
34 using namespace google::protobuf::io;
35
36 namespace {
37
38 static const char kHeader[] = R"(/*
39 * Copyright (C) 2017 The Android Open Source Project
40 *
41 * Licensed under the Apache License, Version 2.0 (the "License");
42 * you may not use this file except in compliance with the License.
43 * You may obtain a copy of the License at
44 *
45 * http://www.apache.org/licenses/LICENSE-2.0
46 *
47 * Unless required by applicable law or agreed to in writing, software
48 * distributed under the License is distributed on an "AS IS" BASIS,
49 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
50 * See the License for the specific language governing permissions and
51 * limitations under the License.
52 */
53
54 /*******************************************************************************
55 * AUTOGENERATED - DO NOT EDIT
56 *******************************************************************************
57 * This file has been generated from the protobuf message
58 * $p$
59 * by
60 * $f$.
61 * If you need to make changes here, change the .proto file and then run
62 * ./tools/gen_tracing_cpp_headers_from_protos
63 */
64
65 )";
66
67 class ErrorPrinter : public MultiFileErrorCollector {
AddError(const string & filename,int line,int col,const string & msg)68 virtual void AddError(const string& filename,
69 int line,
70 int col,
71 const string& msg) {
72 PERFETTO_ELOG("%s %d:%d: %s", filename.c_str(), line, col, msg.c_str());
73 }
AddWarning(const string & filename,int line,int col,const string & msg)74 virtual void AddWarning(const string& filename,
75 int line,
76 int col,
77 const string& msg) {
78 PERFETTO_ILOG("%s %d:%d: %s", filename.c_str(), line, col, msg.c_str());
79 }
80 };
81
GetProtoHeader(const FileDescriptor * proto_file)82 std::string GetProtoHeader(const FileDescriptor* proto_file) {
83 return StringReplace(proto_file->name(), ".proto", ".pb.h", false);
84 }
85
GetFwdDeclType(const Descriptor * msg,bool with_namespace=false)86 std::string GetFwdDeclType(const Descriptor* msg, bool with_namespace = false) {
87 std::string full_type;
88 full_type.append(msg->name());
89 for (const Descriptor* par = msg->containing_type(); par;
90 par = par->containing_type()) {
91 full_type.insert(0, par->name() + "_");
92 }
93 if (with_namespace) {
94 std::vector<std::string> namespaces = Split(msg->file()->package(), ".");
95 for (auto it = namespaces.rbegin(); it != namespaces.rend(); it++) {
96 full_type.insert(0, *it + "::");
97 }
98 }
99 return full_type;
100 }
101
GenFwdDecl(const Descriptor * msg,Printer * p)102 void GenFwdDecl(const Descriptor* msg, Printer* p) {
103 p->Print("class $n$;", "n", GetFwdDeclType(msg));
104
105 // Recurse into subtypes
106 for (int i = 0; i < msg->field_count(); i++) {
107 const FieldDescriptor* field = msg->field(i);
108 if (field->type() == FieldDescriptor::TYPE_MESSAGE)
109 GenFwdDecl(field->message_type(), p);
110 }
111 }
112
113 } // namespace
114
115 class ProtoToCpp {
116 public:
117 ProtoToCpp(const std::string& header_dir,
118 const std::string& cpp_dir,
119 const std::string& include_path);
120
121 static std::string GetCppType(const FieldDescriptor* field, bool constref);
122
123 void Convert(const std::string& src_proto);
124 std::string GetHeaderPath(const FileDescriptor*);
125 std::string GetCppPath(const FileDescriptor*);
126 std::string GetIncludePath(const FileDescriptor*);
127 void GenHeader(const Descriptor*, Printer*);
128 void GenCpp(const Descriptor*, Printer*, std::string prefix);
129
130 private:
131 std::string header_dir_;
132 std::string cpp_dir_;
133 std::string include_path_;
134 DiskSourceTree dst_;
135 ErrorPrinter error_printer_;
136 Importer importer_;
137 };
138
ProtoToCpp(const std::string & header_dir,const std::string & cpp_dir,const std::string & include_path)139 ProtoToCpp::ProtoToCpp(const std::string& header_dir,
140 const std::string& cpp_dir,
141 const std::string& include_path)
142 : header_dir_(header_dir),
143 cpp_dir_(cpp_dir),
144 include_path_(include_path),
145 importer_(&dst_, &error_printer_) {
146 dst_.MapPath("", "protos");
147 }
148
GetHeaderPath(const FileDescriptor * proto_file)149 std::string ProtoToCpp::GetHeaderPath(const FileDescriptor* proto_file) {
150 std::string basename = Split(proto_file->name(), "/").back();
151 return header_dir_ + "/" + StringReplace(basename, ".proto", ".h", false);
152 }
153
GetCppPath(const FileDescriptor * proto_file)154 std::string ProtoToCpp::GetCppPath(const FileDescriptor* proto_file) {
155 std::string basename = Split(proto_file->name(), "/").back();
156 return cpp_dir_ + "/" + StringReplace(basename, ".proto", ".cc", false);
157 }
158
GetIncludePath(const FileDescriptor * proto_file)159 std::string ProtoToCpp::GetIncludePath(const FileDescriptor* proto_file) {
160 std::string basename = Split(proto_file->name(), "/").back();
161 return include_path_ + "/" + StringReplace(basename, ".proto", ".h", false);
162 }
163
GetCppType(const FieldDescriptor * field,bool constref)164 std::string ProtoToCpp::GetCppType(const FieldDescriptor* field,
165 bool constref) {
166 switch (field->type()) {
167 case FieldDescriptor::TYPE_DOUBLE:
168 return "double";
169 case FieldDescriptor::TYPE_FLOAT:
170 return "float";
171 case FieldDescriptor::TYPE_FIXED32:
172 case FieldDescriptor::TYPE_UINT32:
173 return "uint32_t";
174 case FieldDescriptor::TYPE_SFIXED32:
175 case FieldDescriptor::TYPE_INT32:
176 case FieldDescriptor::TYPE_SINT32:
177 return "int32_t";
178 case FieldDescriptor::TYPE_FIXED64:
179 case FieldDescriptor::TYPE_UINT64:
180 return "uint64_t";
181 case FieldDescriptor::TYPE_SFIXED64:
182 case FieldDescriptor::TYPE_SINT64:
183 case FieldDescriptor::TYPE_INT64:
184 return "int64_t";
185 case FieldDescriptor::TYPE_BOOL:
186 return "bool";
187 case FieldDescriptor::TYPE_STRING:
188 case FieldDescriptor::TYPE_BYTES:
189 return constref ? "const std::string&" : "std::string";
190 case FieldDescriptor::TYPE_MESSAGE:
191 return constref ? "const " + field->message_type()->name() + "&"
192 : field->message_type()->name();
193 case FieldDescriptor::TYPE_ENUM:
194 return field->enum_type()->name();
195 case FieldDescriptor::TYPE_GROUP:
196 PERFETTO_FATAL("No cpp type for a group field.");
197 }
198 PERFETTO_FATAL("Not reached"); // for gcc
199 }
200
Convert(const std::string & src_proto)201 void ProtoToCpp::Convert(const std::string& src_proto) {
202 const FileDescriptor* proto_file = importer_.Import(src_proto);
203 if (!proto_file) {
204 PERFETTO_ELOG("Failed to load %s", src_proto.c_str());
205 exit(1);
206 }
207
208 std::string dst_header = GetHeaderPath(proto_file);
209 std::string dst_cpp = GetCppPath(proto_file);
210
211 std::ofstream header_ostr;
212 header_ostr.open(dst_header);
213 PERFETTO_CHECK(header_ostr.is_open());
214 OstreamOutputStream header_proto_ostr(&header_ostr);
215 Printer header_printer(&header_proto_ostr, '$');
216
217 std::ofstream cpp_ostr;
218 cpp_ostr.open(dst_cpp);
219 PERFETTO_CHECK(cpp_ostr.is_open());
220 OstreamOutputStream cpp_proto_ostr(&cpp_ostr);
221 Printer cpp_printer(&cpp_proto_ostr, '$');
222
223 std::string include_guard = dst_header + "_";
224 UpperString(&include_guard);
225 StripString(&include_guard, ".-/\\", '_');
226 header_printer.Print(kHeader, "f", __FILE__, "p", src_proto);
227 header_printer.Print("#ifndef $g$\n#define $g$\n\n", "g", include_guard);
228 header_printer.Print("#include <stdint.h>\n");
229 header_printer.Print("#include <vector>\n");
230 header_printer.Print("#include <string>\n");
231 header_printer.Print("#include <type_traits>\n\n");
232 header_printer.Print("#include \"perfetto/base/export.h\"\n\n");
233
234 cpp_printer.Print(kHeader, "f", __FILE__, "p", src_proto);
235 PERFETTO_CHECK(dst_header.find("include/") == 0);
236 cpp_printer.Print("#include \"$f$\"\n", "f",
237 dst_header.substr(strlen("include/")));
238
239 // Generate includes for translated types of dependencies.
240 for (int i = 0; i < proto_file->dependency_count(); i++) {
241 const FileDescriptor* dep = proto_file->dependency(i);
242 header_printer.Print("#include \"$f$\"\n", "f", GetIncludePath(dep));
243 }
244 header_printer.Print("\n");
245
246 cpp_printer.Print("\n#include \"$f$\"\n", "f", GetProtoHeader(proto_file));
247 for (int i = 0; i < proto_file->dependency_count(); i++) {
248 const FileDescriptor* dep = proto_file->dependency(i);
249 cpp_printer.Print("#include \"$f$\"\n", "f", GetProtoHeader(dep));
250 }
251
252 // Generate forward declarations in the header for proto types.
253 header_printer.Print("// Forward declarations for protobuf types.\n");
254 std::vector<std::string> namespaces = Split(proto_file->package(), ".");
255 for (size_t i = 0; i < namespaces.size(); i++)
256 header_printer.Print("namespace $n$ {\n", "n", namespaces[i]);
257 for (int i = 0; i < proto_file->message_type_count(); i++)
258 GenFwdDecl(proto_file->message_type(i), &header_printer);
259 for (size_t i = 0; i < namespaces.size(); i++)
260 header_printer.Print("}\n");
261
262 header_printer.Print("\nnamespace perfetto {\n");
263 cpp_printer.Print("\nnamespace perfetto {\n");
264
265 for (int i = 0; i < proto_file->message_type_count(); i++) {
266 const Descriptor* msg = proto_file->message_type(i);
267 GenHeader(msg, &header_printer);
268 GenCpp(msg, &cpp_printer, "");
269 }
270
271 cpp_printer.Print("} // namespace perfetto\n");
272 header_printer.Print("} // namespace perfetto\n");
273 header_printer.Print("\n#endif // $g$\n", "g", include_guard);
274 }
275
GenHeader(const Descriptor * msg,Printer * p)276 void ProtoToCpp::GenHeader(const Descriptor* msg, Printer* p) {
277 PERFETTO_ILOG("GEN %s %s", msg->name().c_str(), msg->file()->name().c_str());
278 p->Print("\nclass PERFETTO_EXPORT $n$ {\n", "n", msg->name());
279 p->Print(" public:\n");
280 p->Indent();
281 // Do a first pass to generate nested types.
282 for (int i = 0; i < msg->field_count(); i++) {
283 const FieldDescriptor* field = msg->field(i);
284 if (field->has_default_value()) {
285 PERFETTO_FATAL(
286 "Error on field %s: Explicitly declared default values are not "
287 "supported",
288 field->name().c_str());
289 }
290
291 if (field->type() == FieldDescriptor::TYPE_ENUM) {
292 const EnumDescriptor* enum_desc = field->enum_type();
293 p->Print("enum $n$ {\n", "n", enum_desc->name());
294 for (int e = 0; e < enum_desc->value_count(); e++) {
295 const EnumValueDescriptor* value = enum_desc->value(e);
296 p->Print(" $n$ = $v$,\n", "n", value->name(), "v",
297 std::to_string(value->number()));
298 }
299 p->Print("};\n");
300 } else if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
301 field->message_type()->file() == msg->file()) {
302 GenHeader(field->message_type(), p);
303 }
304 }
305
306 p->Print("$n$();\n", "n", msg->name());
307 p->Print("~$n$();\n", "n", msg->name());
308 p->Print("$n$($n$&&) noexcept;\n", "n", msg->name());
309 p->Print("$n$& operator=($n$&&);\n", "n", msg->name());
310 p->Print("$n$(const $n$&);\n", "n", msg->name());
311 p->Print("$n$& operator=(const $n$&);\n", "n", msg->name());
312 p->Print("bool operator==(const $n$&) const;\n", "n", msg->name());
313 p->Print(
314 "bool operator!=(const $n$& other) const { return !(*this == other); }\n",
315 "n", msg->name());
316 p->Print("\n");
317
318 std::string proto_type = GetFwdDeclType(msg, true);
319 p->Print("// Conversion methods from/to the corresponding protobuf types.\n");
320 p->Print("void FromProto(const $p$&);\n", "n", msg->name(), "p", proto_type);
321 p->Print("void ToProto($p$*) const;\n", "p", proto_type);
322
323 // Generate accessors.
324 for (int i = 0; i < msg->field_count(); i++) {
325 const FieldDescriptor* field = msg->field(i);
326 p->Print("\n");
327 if (!field->is_repeated()) {
328 p->Print("$t$ $n$() const { return $n$_; }\n", "t",
329 GetCppType(field, true), "n", field->lowercase_name());
330 if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
331 p->Print("$t$* mutable_$n$() { return &$n$_; }\n", "t",
332 GetCppType(field, false), "n", field->lowercase_name());
333 } else {
334 p->Print("void set_$n$($t$ value) { $n$_ = value; }\n", "t",
335 GetCppType(field, true), "n", field->lowercase_name());
336 if (field->type() == FieldDescriptor::TYPE_BYTES) {
337 p->Print(
338 "void set_$n$(const void* p, size_t s) { "
339 "$n$_.assign(reinterpret_cast<const char*>(p), s); }\n",
340 "n", field->lowercase_name());
341 }
342 }
343 } else { // is_repeated()
344 p->Print(
345 "int $n$_size() const { return static_cast<int>($n$_.size()); }\n",
346 "t", GetCppType(field, false), "n", field->lowercase_name());
347 p->Print("const std::vector<$t$>& $n$() const { return $n$_; }\n", "t",
348 GetCppType(field, false), "n", field->lowercase_name());
349 p->Print("std::vector<$t$>* mutable_$n$() { return &$n$_; }\n", "t",
350 GetCppType(field, false), "n", field->lowercase_name());
351 p->Print("void clear_$n$() { $n$_.clear(); }\n", "n",
352 field->lowercase_name());
353 p->Print("$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n",
354 "t", GetCppType(field, false), "n", field->lowercase_name());
355 }
356 }
357 p->Outdent();
358 p->Print("\n private:\n");
359 p->Indent();
360
361 // Generate fields.
362 for (int i = 0; i < msg->field_count(); i++) {
363 const FieldDescriptor* field = msg->field(i);
364 if (!field->is_repeated()) {
365 p->Print("$t$ $n$_ = {};\n", "t", GetCppType(field, false), "n",
366 field->lowercase_name());
367 } else { // is_repeated()
368 p->Print("std::vector<$t$> $n$_;\n", "t", GetCppType(field, false), "n",
369 field->lowercase_name());
370 }
371 }
372 p->Print("\n");
373 p->Print("// Allows to preserve unknown protobuf fields for compatibility\n");
374 p->Print("// with future versions of .proto files.\n");
375 p->Print("std::string unknown_fields_;\n");
376 p->Outdent();
377 p->Print("};\n\n");
378 }
379
GenCpp(const Descriptor * msg,Printer * p,std::string prefix)380 void ProtoToCpp::GenCpp(const Descriptor* msg, Printer* p, std::string prefix) {
381 p->Print("\n");
382 std::string full_name = prefix + msg->name();
383 p->Print("$f$::$n$() = default;\n", "f", full_name, "n", msg->name());
384 p->Print("$f$::~$n$() = default;\n", "f", full_name, "n", msg->name());
385 p->Print("$f$::$n$(const $f$&) = default;\n", "f", full_name, "n",
386 msg->name());
387 p->Print("$f$& $f$::operator=(const $f$&) = default;\n", "f", full_name, "n",
388 msg->name());
389 p->Print("$f$::$n$($f$&&) noexcept = default;\n", "f", full_name, "n",
390 msg->name());
391 p->Print("$f$& $f$::operator=($f$&&) = default;\n", "f", full_name, "n",
392 msg->name());
393
394 p->Print("\n");
395
396 // Comparison operator
397 p->Print("#pragma GCC diagnostic push\n");
398 p->Print("#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n");
399 p->Print("bool $f$::operator==(const $f$& other) const {\n", "f", full_name,
400 "n", msg->name());
401 p->Indent();
402
403 p->Print("return ");
404 for (int i = 0; i < msg->field_count(); i++) {
405 if (i > 0)
406 p->Print("\n && ");
407 const FieldDescriptor* field = msg->field(i);
408 p->Print("($n$_ == other.$n$_)", "n", field->name());
409 }
410 p->Print(";");
411 p->Outdent();
412 p->Print("}\n");
413 p->Print("#pragma GCC diagnostic pop\n\n");
414
415 std::string proto_type = GetFwdDeclType(msg, true);
416
417 // Genrate the FromProto() method definition.
418 p->Print("void $f$::FromProto(const $p$& proto) {\n", "f", full_name, "p",
419 proto_type);
420 p->Indent();
421 for (int i = 0; i < msg->field_count(); i++) {
422 p->Print("\n");
423 const FieldDescriptor* field = msg->field(i);
424 if (!field->is_repeated()) {
425 if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
426 p->Print("$n$_.FromProto(proto.$n$());\n", "n", field->name());
427 } else {
428 p->Print(
429 "static_assert(sizeof($n$_) == sizeof(proto.$n$()), \"size "
430 "mismatch\");\n",
431 "n", field->name());
432 p->Print("$n$_ = static_cast<decltype($n$_)>(proto.$n$());\n", "n",
433 field->name());
434 }
435 } else { // is_repeated()
436 p->Print("$n$_.clear();\n", "n", field->name());
437 p->Print("for (const auto& field : proto.$n$()) {\n", "n", field->name());
438 p->Print(" $n$_.emplace_back();\n", "n", field->name());
439 if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
440 p->Print(" $n$_.back().FromProto(field);\n", "n", field->name());
441 } else {
442 p->Print(
443 "static_assert(sizeof($n$_.back()) == sizeof(proto.$n$(0)), \"size "
444 "mismatch\");\n",
445 "n", field->name());
446 p->Print(
447 " $n$_.back() = static_cast<decltype($n$_)::value_type>(field);\n",
448 "n", field->name());
449 }
450 p->Print("}\n");
451 }
452 }
453 p->Print("unknown_fields_ = proto.unknown_fields();\n");
454 p->Outdent();
455 p->Print("}\n\n");
456
457 // Genrate the ToProto() method definition.
458 p->Print("void $f$::ToProto($p$* proto) const {\n", "f", full_name, "p",
459 proto_type);
460 p->Indent();
461 p->Print("proto->Clear();\n");
462 for (int i = 0; i < msg->field_count(); i++) {
463 p->Print("\n");
464 const FieldDescriptor* field = msg->field(i);
465 if (!field->is_repeated()) {
466 if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
467 p->Print("$n$_.ToProto(proto->mutable_$n$());\n", "n", field->name());
468 } else {
469 p->Print(
470 "static_assert(sizeof($n$_) == sizeof(proto->$n$()), \"size "
471 "mismatch\");\n",
472 "n", field->name());
473 p->Print("proto->set_$n$(static_cast<decltype(proto->$n$())>($n$_));\n",
474 "n", field->name());
475 }
476 } else { // is_repeated()
477 p->Print("for (const auto& it : $n$_) {\n", "n", field->name());
478 if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
479 p->Print(" auto* entry = proto->add_$n$();\n", "n", field->name());
480 p->Print(" it.ToProto(entry);\n");
481 } else {
482 p->Print(
483 " proto->add_$n$(static_cast<decltype(proto->$n$(0))>(it));\n",
484 "n", field->name());
485 p->Print(
486 "static_assert(sizeof(it) == sizeof(proto->$n$(0)), \"size "
487 "mismatch\");\n",
488 "n", field->name());
489 }
490 p->Print("}\n");
491 }
492 }
493 p->Print("*(proto->mutable_unknown_fields()) = unknown_fields_;\n");
494 p->Outdent();
495 p->Print("}\n\n");
496
497 // Recurse into nested types.
498 for (int i = 0; i < msg->field_count(); i++) {
499 const FieldDescriptor* field = msg->field(i);
500
501 if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
502 field->message_type()->file() == msg->file()) {
503 std::string child_prefix = prefix + msg->name() + "::";
504 GenCpp(field->message_type(), p, child_prefix);
505 }
506 }
507 }
508
main(int argc,char ** argv)509 int main(int argc, char** argv) {
510 if (argc <= 4) {
511 PERFETTO_ELOG(
512 "Usage: %s source.proto dir/for/header dir/for/cc include/path",
513 argv[0]);
514 return 1;
515 }
516 ProtoToCpp proto_to_cpp(argv[2], argv[3], argv[4]);
517 proto_to_cpp.Convert(argv[1]);
518 return 0;
519 }
520