Home | History | Annotate | Download | only in proto_to_cpp
      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 {
     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   }
     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 
     82 std::string GetProtoHeader(const FileDescriptor* proto_file) {
     83   return StringReplace(proto_file->name(), ".proto", ".pb.h", false);
     84 }
     85 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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