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