proto_to_cpp.cc revision 954d8ed604c478e636e9c2f60829b46efd7abf01
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
32using namespace google::protobuf;
33using namespace google::protobuf::compiler;
34using namespace google::protobuf::io;
35
36namespace {
37
38static 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.py
63 */
64
65)";
66
67class 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
82std::string GetProtoHeader(const FileDescriptor* proto_file) {
83  return StringReplace(proto_file->name(), ".proto", ".pb.h", false);
84}
85
86std::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
102void 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
115class 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
139ProtoToCpp::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
149std::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
154std::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
159std::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
164std::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_CHECK(false);
197  }
198}
199
200void ProtoToCpp::Convert(const std::string& src_proto) {
201  const FileDescriptor* proto_file = importer_.Import(src_proto);
202  if (!proto_file) {
203    PERFETTO_ELOG("Failed to load %s", src_proto.c_str());
204    exit(1);
205  }
206
207  std::string dst_header = GetHeaderPath(proto_file);
208  std::string dst_cpp = GetCppPath(proto_file);
209
210  std::ofstream header_ostr;
211  header_ostr.open(dst_header);
212  PERFETTO_CHECK(header_ostr.is_open());
213  OstreamOutputStream header_proto_ostr(&header_ostr);
214  Printer header_printer(&header_proto_ostr, '$');
215
216  std::ofstream cpp_ostr;
217  cpp_ostr.open(dst_cpp);
218  PERFETTO_CHECK(cpp_ostr.is_open());
219  OstreamOutputStream cpp_proto_ostr(&cpp_ostr);
220  Printer cpp_printer(&cpp_proto_ostr, '$');
221
222  std::string include_guard = dst_header + "_";
223  UpperString(&include_guard);
224  StripString(&include_guard, ".-/\\", '_');
225  header_printer.Print(kHeader, "f", __FILE__, "p", src_proto);
226  header_printer.Print("#ifndef $g$\n#define $g$\n\n", "g", include_guard);
227  header_printer.Print("#include <stdint.h>\n");
228  header_printer.Print("#include <vector>\n");
229  header_printer.Print("#include <string>\n");
230  header_printer.Print("#include <type_traits>\n\n");
231
232  cpp_printer.Print(kHeader, "f", __FILE__, "p", src_proto);
233  PERFETTO_CHECK(dst_header.find("include/") == 0);
234  cpp_printer.Print("#include \"$f$\"\n", "f",
235                    dst_header.substr(strlen("include/")));
236
237  // Generate includes for translated types of dependencies.
238  for (int i = 0; i < proto_file->dependency_count(); i++) {
239    const FileDescriptor* dep = proto_file->dependency(i);
240    header_printer.Print("#include \"$f$\"\n", "f", GetIncludePath(dep));
241  }
242  header_printer.Print("\n");
243
244  cpp_printer.Print("\n#include \"$f$\"\n", "f", GetProtoHeader(proto_file));
245  for (int i = 0; i < proto_file->dependency_count(); i++) {
246    const FileDescriptor* dep = proto_file->dependency(i);
247    cpp_printer.Print("#include \"$f$\"\n", "f", GetProtoHeader(dep));
248  }
249
250  // Generate forward declarations in the header for proto types.
251  header_printer.Print("// Forward declarations for protobuf types.\n");
252  std::vector<std::string> namespaces = Split(proto_file->package(), ".");
253  for (size_t i = 0; i < namespaces.size(); i++)
254    header_printer.Print("namespace $n$ {\n", "n", namespaces[i]);
255  for (int i = 0; i < proto_file->message_type_count(); i++)
256    GenFwdDecl(proto_file->message_type(i), &header_printer);
257  for (size_t i = 0; i < namespaces.size(); i++)
258    header_printer.Print("}\n");
259
260  header_printer.Print("\nnamespace perfetto {\n");
261  cpp_printer.Print("\nnamespace perfetto {\n");
262
263  for (int i = 0; i < proto_file->message_type_count(); i++) {
264    const Descriptor* msg = proto_file->message_type(i);
265    GenHeader(msg, &header_printer);
266    GenCpp(msg, &cpp_printer, "");
267  }
268
269  cpp_printer.Print("}  // namespace perfetto\n");
270  header_printer.Print("}  // namespace perfetto\n");
271  header_printer.Print("#endif  // $g$\n", "g", include_guard);
272}
273
274void ProtoToCpp::GenHeader(const Descriptor* msg, Printer* p) {
275  PERFETTO_ILOG("GEN %s %s", msg->name().c_str(), msg->file()->name().c_str());
276  p->Print("\nclass $n$ {\n", "n", msg->name());
277  p->Print(" public:\n");
278  p->Indent();
279  // Do a first pass to generate nested types.
280  for (int i = 0; i < msg->field_count(); i++) {
281    const FieldDescriptor* field = msg->field(i);
282    if (field->has_default_value()) {
283      PERFETTO_ELOG(
284          "Error on field %s: Explicitly declared default values are not "
285          "supported",
286          field->name().c_str());
287      PERFETTO_CHECK(false);
288    }
289
290    if (field->type() == FieldDescriptor::TYPE_ENUM) {
291      const EnumDescriptor* enum_desc = field->enum_type();
292      p->Print("enum $n$ {\n", "n", enum_desc->name());
293      for (int e = 0; e < enum_desc->value_count(); e++) {
294        const EnumValueDescriptor* value = enum_desc->value(e);
295        p->Print("  $n$ = $v$,\n", "n", value->name(), "v",
296                 std::to_string(value->number()));
297      }
298      p->Print("};\n");
299    } else if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
300               field->message_type()->file() == msg->file()) {
301      GenHeader(field->message_type(), p);
302    }
303  }
304
305  p->Print("$n$();\n", "n", msg->name());
306  p->Print("~$n$();\n", "n", msg->name());
307  p->Print("$n$($n$&&) noexcept;\n", "n", msg->name());
308  p->Print("$n$& operator=($n$&&);\n", "n", msg->name());
309  p->Print("$n$(const $n$&);\n", "n", msg->name());
310  p->Print("$n$& operator=(const $n$&);\n", "n", msg->name());
311  p->Print("\n");
312
313  std::string proto_type = GetFwdDeclType(msg, true);
314  p->Print("// Conversion methods from/to the corresponding protobuf types.\n");
315  p->Print("void FromProto(const $p$&);\n", "n", msg->name(), "p", proto_type);
316  p->Print("void ToProto($p$*) const;\n", "p", proto_type);
317
318  // Generate accessors.
319  for (int i = 0; i < msg->field_count(); i++) {
320    const FieldDescriptor* field = msg->field(i);
321    p->Print("\n");
322    if (!field->is_repeated()) {
323      p->Print("$t$ $n$() const { return $n$_; }\n", "t",
324               GetCppType(field, true), "n", field->lowercase_name());
325      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
326        p->Print("$t$* mutable_$n$() { return &$n$_; }\n", "t",
327                 GetCppType(field, false), "n", field->lowercase_name());
328      } else {
329        p->Print("void set_$n$($t$ value) { $n$_ = value; }\n", "t",
330                 GetCppType(field, true), "n", field->lowercase_name());
331      }
332    } else {  // is_repeated()
333      p->Print(
334          "int $n$_size() const { return static_cast<int>($n$_.size()); }\n",
335          "t", GetCppType(field, false), "n", field->lowercase_name());
336      p->Print("const std::vector<$t$>& $n$() const { return $n$_; }\n", "t",
337               GetCppType(field, false), "n", field->lowercase_name());
338      p->Print("$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n",
339               "t", GetCppType(field, false), "n", field->lowercase_name());
340    }
341  }
342  p->Outdent();
343  p->Print("\n private:\n");
344  p->Indent();
345
346  // Generate fields.
347  for (int i = 0; i < msg->field_count(); i++) {
348    const FieldDescriptor* field = msg->field(i);
349    if (!field->is_repeated()) {
350      p->Print("$t$ $n$_ = {};\n", "t", GetCppType(field, false), "n",
351               field->lowercase_name());
352    } else {  // is_repeated()
353      p->Print("std::vector<$t$> $n$_;\n", "t", GetCppType(field, false), "n",
354               field->lowercase_name());
355    }
356  }
357  p->Print("\n");
358  p->Print("// Allows to preserve unknown protobuf fields for compatibility\n");
359  p->Print("// with future versions of .proto files.\n");
360  p->Print("std::string unknown_fields_;\n");
361  p->Outdent();
362  p->Print("};\n\n");
363}
364
365void ProtoToCpp::GenCpp(const Descriptor* msg, Printer* p, std::string prefix) {
366  p->Print("\n");
367  std::string full_name = prefix + msg->name();
368  p->Print("$f$::$n$() = default;\n", "f", full_name, "n", msg->name());
369  p->Print("$f$::~$n$() = default;\n", "f", full_name, "n", msg->name());
370  p->Print("$f$::$n$(const $f$&) = default;\n", "f", full_name, "n",
371           msg->name());
372  p->Print("$f$& $f$::operator=(const $f$&) = default;\n", "f", full_name, "n",
373           msg->name());
374  p->Print("$f$::$n$($f$&&) noexcept = default;\n", "f", full_name, "n",
375           msg->name());
376  p->Print("$f$& $f$::operator=($f$&&) = default;\n", "f", full_name, "n",
377           msg->name());
378
379  p->Print("\n");
380
381  std::string proto_type = GetFwdDeclType(msg, true);
382
383  // Genrate the FromProto() method definition.
384  p->Print("void $f$::FromProto(const $p$& proto) {\n", "f", full_name, "p",
385           proto_type);
386  p->Indent();
387  for (int i = 0; i < msg->field_count(); i++) {
388    p->Print("\n");
389    const FieldDescriptor* field = msg->field(i);
390    if (!field->is_repeated()) {
391      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
392        p->Print("$n$_.FromProto(proto.$n$());\n", "n", field->name());
393      } else {
394        p->Print(
395            "static_assert(sizeof($n$_) == sizeof(proto.$n$()), \"size "
396            "mismatch\");\n",
397            "n", field->name());
398        p->Print("$n$_ = static_cast<decltype($n$_)>(proto.$n$());\n", "n",
399                 field->name());
400      }
401    } else {  // is_repeated()
402      p->Print("$n$_.clear();\n", "n", field->name());
403      p->Print("for (const auto& field : proto.$n$()) {\n", "n", field->name());
404      p->Print("  $n$_.emplace_back();\n", "n", field->name());
405      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
406        p->Print("  $n$_.back().FromProto(field);\n", "n", field->name());
407      } else {
408        p->Print(
409            "static_assert(sizeof($n$_.back()) == sizeof(proto.$n$(0)), \"size "
410            "mismatch\");\n",
411            "n", field->name());
412        p->Print(
413            "  $n$_.back() = static_cast<decltype($n$_)::value_type>(field);\n",
414            "n", field->name());
415      }
416      p->Print("}\n");
417    }
418  }
419  p->Print("unknown_fields_ = proto.unknown_fields();\n");
420  p->Outdent();
421  p->Print("}\n\n");
422
423  // Genrate the ToProto() method definition.
424  p->Print("void $f$::ToProto($p$* proto) const {\n", "f", full_name, "p",
425           proto_type);
426  p->Indent();
427  p->Print("proto->Clear();\n");
428  for (int i = 0; i < msg->field_count(); i++) {
429    p->Print("\n");
430    const FieldDescriptor* field = msg->field(i);
431    if (!field->is_repeated()) {
432      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
433        p->Print("$n$_.ToProto(proto->mutable_$n$());\n", "n", field->name());
434      } else {
435        p->Print(
436            "static_assert(sizeof($n$_) == sizeof(proto->$n$()), \"size "
437            "mismatch\");\n",
438            "n", field->name());
439        p->Print("proto->set_$n$(static_cast<decltype(proto->$n$())>($n$_));\n",
440                 "n", field->name());
441      }
442    } else {  // is_repeated()
443      p->Print("for (const auto& it : $n$_) {\n", "n", field->name());
444      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
445        p->Print("  auto* entry = proto->add_$n$();\n", "n", field->name());
446        p->Print("  it.ToProto(entry);\n");
447      } else {
448        p->Print("  proto->add_$n$(it);\n", "n", field->name());
449        p->Print(
450            "static_assert(sizeof(it) == sizeof(proto->$n$(0)), \"size "
451            "mismatch\");\n",
452            "n", field->name());
453      }
454      p->Print("}\n");
455    }
456  }
457  p->Print("*(proto->mutable_unknown_fields()) = unknown_fields_;\n");
458  p->Outdent();
459  p->Print("}\n\n");
460
461  // Recurse into nested types.
462  for (int i = 0; i < msg->field_count(); i++) {
463    const FieldDescriptor* field = msg->field(i);
464
465    if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
466        field->message_type()->file() == msg->file()) {
467      std::string child_prefix = prefix + msg->name() + "::";
468      GenCpp(field->message_type(), p, child_prefix);
469    }
470  }
471}
472
473int main(int argc, char** argv) {
474  if (argc <= 4) {
475    PERFETTO_ELOG(
476        "Usage: %s source.proto dir/for/header dir/for/cc include/path",
477        argv[0]);
478    return 1;
479  }
480  ProtoToCpp proto_to_cpp(argv[2], argv[3], argv[4]);
481  proto_to_cpp.Convert(argv[1]);
482  return 0;
483}
484