普通文本  |  493行  |  17.93 KB

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/util/field_comparator.h>
#include <google/protobuf/util/message_differencer.h>

#include <stdio.h>

#include <fstream>
#include <iostream>

#include "perfetto/base/logging.h"

using namespace google::protobuf;
using namespace google::protobuf::compiler;
using namespace google::protobuf::io;

namespace {

static const char kHeader[] = R"(/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*******************************************************************************
 * AUTOGENERATED - DO NOT EDIT
 *******************************************************************************
 * This file has been generated from the protobuf message
 * $p$
 * by
 * $f$.
 * If you need to make changes here, change the .proto file and then run
 * ./tools/gen_tracing_cpp_headers_from_protos.py
 */

)";

class ErrorPrinter : public MultiFileErrorCollector {
  virtual void AddError(const string& filename,
                        int line,
                        int col,
                        const string& msg) {
    PERFETTO_ELOG("%s %d:%d: %s", filename.c_str(), line, col, msg.c_str());
  }
  virtual void AddWarning(const string& filename,
                          int line,
                          int col,
                          const string& msg) {
    PERFETTO_ILOG("%s %d:%d: %s", filename.c_str(), line, col, msg.c_str());
  }
};

std::string GetProtoHeader(const FileDescriptor* proto_file) {
  return StringReplace(proto_file->name(), ".proto", ".pb.h", false);
}

std::string GetFwdDeclType(const Descriptor* msg, bool with_namespace = false) {
  std::string full_type;
  full_type.append(msg->name());
  for (const Descriptor* par = msg->containing_type(); par;
       par = par->containing_type()) {
    full_type.insert(0, par->name() + "_");
  }
  if (with_namespace) {
    std::vector<std::string> namespaces = Split(msg->file()->package(), ".");
    for (auto it = namespaces.rbegin(); it != namespaces.rend(); it++) {
      full_type.insert(0, *it + "::");
    }
  }
  return full_type;
}

void GenFwdDecl(const Descriptor* msg, Printer* p) {
  p->Print("class $n$;", "n", GetFwdDeclType(msg));

  // Recurse into subtypes
  for (int i = 0; i < msg->field_count(); i++) {
    const FieldDescriptor* field = msg->field(i);
    if (field->type() == FieldDescriptor::TYPE_MESSAGE)
      GenFwdDecl(field->message_type(), p);
  }
}

}  // namespace

class ProtoToCpp {
 public:
  ProtoToCpp(const std::string& header_dir,
             const std::string& cpp_dir,
             const std::string& include_path);

  static std::string GetCppType(const FieldDescriptor* field, bool constref);

  void Convert(const std::string& src_proto);
  std::string GetHeaderPath(const FileDescriptor*);
  std::string GetCppPath(const FileDescriptor*);
  std::string GetIncludePath(const FileDescriptor*);
  void GenHeader(const Descriptor*, Printer*);
  void GenCpp(const Descriptor*, Printer*, std::string prefix);

 private:
  std::string header_dir_;
  std::string cpp_dir_;
  std::string include_path_;
  DiskSourceTree dst_;
  ErrorPrinter error_printer_;
  Importer importer_;
};

ProtoToCpp::ProtoToCpp(const std::string& header_dir,
                       const std::string& cpp_dir,
                       const std::string& include_path)
    : header_dir_(header_dir),
      cpp_dir_(cpp_dir),
      include_path_(include_path),
      importer_(&dst_, &error_printer_) {
  dst_.MapPath("", "protos");
}

std::string ProtoToCpp::GetHeaderPath(const FileDescriptor* proto_file) {
  std::string basename = Split(proto_file->name(), "/").back();
  return header_dir_ + "/" + StringReplace(basename, ".proto", ".h", false);
}

std::string ProtoToCpp::GetCppPath(const FileDescriptor* proto_file) {
  std::string basename = Split(proto_file->name(), "/").back();
  return cpp_dir_ + "/" + StringReplace(basename, ".proto", ".cc", false);
}

std::string ProtoToCpp::GetIncludePath(const FileDescriptor* proto_file) {
  std::string basename = Split(proto_file->name(), "/").back();
  return include_path_ + "/" + StringReplace(basename, ".proto", ".h", false);
}

std::string ProtoToCpp::GetCppType(const FieldDescriptor* field,
                                   bool constref) {
  switch (field->type()) {
    case FieldDescriptor::TYPE_DOUBLE:
      return "double";
    case FieldDescriptor::TYPE_FLOAT:
      return "float";
    case FieldDescriptor::TYPE_FIXED32:
    case FieldDescriptor::TYPE_UINT32:
      return "uint32_t";
    case FieldDescriptor::TYPE_SFIXED32:
    case FieldDescriptor::TYPE_INT32:
    case FieldDescriptor::TYPE_SINT32:
      return "int32_t";
    case FieldDescriptor::TYPE_FIXED64:
    case FieldDescriptor::TYPE_UINT64:
      return "uint64_t";
    case FieldDescriptor::TYPE_SFIXED64:
    case FieldDescriptor::TYPE_SINT64:
    case FieldDescriptor::TYPE_INT64:
      return "int64_t";
    case FieldDescriptor::TYPE_BOOL:
      return "bool";
    case FieldDescriptor::TYPE_STRING:
    case FieldDescriptor::TYPE_BYTES:
      return constref ? "const std::string&" : "std::string";
    case FieldDescriptor::TYPE_MESSAGE:
      return constref ? "const " + field->message_type()->name() + "&"
                      : field->message_type()->name();
    case FieldDescriptor::TYPE_ENUM:
      return field->enum_type()->name();
    case FieldDescriptor::TYPE_GROUP:
      PERFETTO_FATAL("No cpp type for a group field.");
  }
  PERFETTO_CHECK(false);
}

void ProtoToCpp::Convert(const std::string& src_proto) {
  const FileDescriptor* proto_file = importer_.Import(src_proto);
  if (!proto_file) {
    PERFETTO_ELOG("Failed to load %s", src_proto.c_str());
    exit(1);
  }

  std::string dst_header = GetHeaderPath(proto_file);
  std::string dst_cpp = GetCppPath(proto_file);

  std::ofstream header_ostr;
  header_ostr.open(dst_header);
  PERFETTO_CHECK(header_ostr.is_open());
  OstreamOutputStream header_proto_ostr(&header_ostr);
  Printer header_printer(&header_proto_ostr, '$');

  std::ofstream cpp_ostr;
  cpp_ostr.open(dst_cpp);
  PERFETTO_CHECK(cpp_ostr.is_open());
  OstreamOutputStream cpp_proto_ostr(&cpp_ostr);
  Printer cpp_printer(&cpp_proto_ostr, '$');

  std::string include_guard = dst_header + "_";
  UpperString(&include_guard);
  StripString(&include_guard, ".-/\\", '_');
  header_printer.Print(kHeader, "f", __FILE__, "p", src_proto);
  header_printer.Print("#ifndef $g$\n#define $g$\n\n", "g", include_guard);
  header_printer.Print("#include <stdint.h>\n");
  header_printer.Print("#include <vector>\n");
  header_printer.Print("#include <string>\n");
  header_printer.Print("#include <type_traits>\n\n");
  header_printer.Print("#include \"perfetto/base/export.h\"\n\n");

  cpp_printer.Print(kHeader, "f", __FILE__, "p", src_proto);
  PERFETTO_CHECK(dst_header.find("include/") == 0);
  cpp_printer.Print("#include \"$f$\"\n", "f",
                    dst_header.substr(strlen("include/")));

  // Generate includes for translated types of dependencies.
  for (int i = 0; i < proto_file->dependency_count(); i++) {
    const FileDescriptor* dep = proto_file->dependency(i);
    header_printer.Print("#include \"$f$\"\n", "f", GetIncludePath(dep));
  }
  header_printer.Print("\n");

  cpp_printer.Print("\n#include \"$f$\"\n", "f", GetProtoHeader(proto_file));
  for (int i = 0; i < proto_file->dependency_count(); i++) {
    const FileDescriptor* dep = proto_file->dependency(i);
    cpp_printer.Print("#include \"$f$\"\n", "f", GetProtoHeader(dep));
  }

  // Generate forward declarations in the header for proto types.
  header_printer.Print("// Forward declarations for protobuf types.\n");
  std::vector<std::string> namespaces = Split(proto_file->package(), ".");
  for (size_t i = 0; i < namespaces.size(); i++)
    header_printer.Print("namespace $n$ {\n", "n", namespaces[i]);
  for (int i = 0; i < proto_file->message_type_count(); i++)
    GenFwdDecl(proto_file->message_type(i), &header_printer);
  for (size_t i = 0; i < namespaces.size(); i++)
    header_printer.Print("}\n");

  header_printer.Print("\nnamespace perfetto {\n");
  cpp_printer.Print("\nnamespace perfetto {\n");

  for (int i = 0; i < proto_file->message_type_count(); i++) {
    const Descriptor* msg = proto_file->message_type(i);
    GenHeader(msg, &header_printer);
    GenCpp(msg, &cpp_printer, "");
  }

  cpp_printer.Print("}  // namespace perfetto\n");
  header_printer.Print("}  // namespace perfetto\n");
  header_printer.Print("#endif  // $g$\n", "g", include_guard);
}

void ProtoToCpp::GenHeader(const Descriptor* msg, Printer* p) {
  PERFETTO_ILOG("GEN %s %s", msg->name().c_str(), msg->file()->name().c_str());
  p->Print("\nclass PERFETTO_EXPORT $n$ {\n", "n", msg->name());
  p->Print(" public:\n");
  p->Indent();
  // Do a first pass to generate nested types.
  for (int i = 0; i < msg->field_count(); i++) {
    const FieldDescriptor* field = msg->field(i);
    if (field->has_default_value()) {
      PERFETTO_FATAL(
          "Error on field %s: Explicitly declared default values are not "
          "supported",
          field->name().c_str());
    }

    if (field->type() == FieldDescriptor::TYPE_ENUM) {
      const EnumDescriptor* enum_desc = field->enum_type();
      p->Print("enum $n$ {\n", "n", enum_desc->name());
      for (int e = 0; e < enum_desc->value_count(); e++) {
        const EnumValueDescriptor* value = enum_desc->value(e);
        p->Print("  $n$ = $v$,\n", "n", value->name(), "v",
                 std::to_string(value->number()));
      }
      p->Print("};\n");
    } else if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
               field->message_type()->file() == msg->file()) {
      GenHeader(field->message_type(), p);
    }
  }

  p->Print("$n$();\n", "n", msg->name());
  p->Print("~$n$();\n", "n", msg->name());
  p->Print("$n$($n$&&) noexcept;\n", "n", msg->name());
  p->Print("$n$& operator=($n$&&);\n", "n", msg->name());
  p->Print("$n$(const $n$&);\n", "n", msg->name());
  p->Print("$n$& operator=(const $n$&);\n", "n", msg->name());
  p->Print("\n");

  std::string proto_type = GetFwdDeclType(msg, true);
  p->Print("// Conversion methods from/to the corresponding protobuf types.\n");
  p->Print("void FromProto(const $p$&);\n", "n", msg->name(), "p", proto_type);
  p->Print("void ToProto($p$*) const;\n", "p", proto_type);

  // Generate accessors.
  for (int i = 0; i < msg->field_count(); i++) {
    const FieldDescriptor* field = msg->field(i);
    p->Print("\n");
    if (!field->is_repeated()) {
      p->Print("$t$ $n$() const { return $n$_; }\n", "t",
               GetCppType(field, true), "n", field->lowercase_name());
      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
        p->Print("$t$* mutable_$n$() { return &$n$_; }\n", "t",
                 GetCppType(field, false), "n", field->lowercase_name());
      } else {
        p->Print("void set_$n$($t$ value) { $n$_ = value; }\n", "t",
                 GetCppType(field, true), "n", field->lowercase_name());
        if (field->type() == FieldDescriptor::TYPE_BYTES) {
          p->Print(
              "void set_$n$(const void* p, size_t s) { "
              "$n$_.assign(reinterpret_cast<const char*>(p), s); }\n",
              "n", field->lowercase_name());
        }
      }
    } else {  // is_repeated()
      p->Print(
          "int $n$_size() const { return static_cast<int>($n$_.size()); }\n",
          "t", GetCppType(field, false), "n", field->lowercase_name());
      p->Print("const std::vector<$t$>& $n$() const { return $n$_; }\n", "t",
               GetCppType(field, false), "n", field->lowercase_name());
      p->Print("$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n",
               "t", GetCppType(field, false), "n", field->lowercase_name());
    }
  }
  p->Outdent();
  p->Print("\n private:\n");
  p->Indent();

  // Generate fields.
  for (int i = 0; i < msg->field_count(); i++) {
    const FieldDescriptor* field = msg->field(i);
    if (!field->is_repeated()) {
      p->Print("$t$ $n$_ = {};\n", "t", GetCppType(field, false), "n",
               field->lowercase_name());
    } else {  // is_repeated()
      p->Print("std::vector<$t$> $n$_;\n", "t", GetCppType(field, false), "n",
               field->lowercase_name());
    }
  }
  p->Print("\n");
  p->Print("// Allows to preserve unknown protobuf fields for compatibility\n");
  p->Print("// with future versions of .proto files.\n");
  p->Print("std::string unknown_fields_;\n");
  p->Outdent();
  p->Print("};\n\n");
}

void ProtoToCpp::GenCpp(const Descriptor* msg, Printer* p, std::string prefix) {
  p->Print("\n");
  std::string full_name = prefix + msg->name();
  p->Print("$f$::$n$() = default;\n", "f", full_name, "n", msg->name());
  p->Print("$f$::~$n$() = default;\n", "f", full_name, "n", msg->name());
  p->Print("$f$::$n$(const $f$&) = default;\n", "f", full_name, "n",
           msg->name());
  p->Print("$f$& $f$::operator=(const $f$&) = default;\n", "f", full_name, "n",
           msg->name());
  p->Print("$f$::$n$($f$&&) noexcept = default;\n", "f", full_name, "n",
           msg->name());
  p->Print("$f$& $f$::operator=($f$&&) = default;\n", "f", full_name, "n",
           msg->name());

  p->Print("\n");

  std::string proto_type = GetFwdDeclType(msg, true);

  // Genrate the FromProto() method definition.
  p->Print("void $f$::FromProto(const $p$& proto) {\n", "f", full_name, "p",
           proto_type);
  p->Indent();
  for (int i = 0; i < msg->field_count(); i++) {
    p->Print("\n");
    const FieldDescriptor* field = msg->field(i);
    if (!field->is_repeated()) {
      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
        p->Print("$n$_.FromProto(proto.$n$());\n", "n", field->name());
      } else {
        p->Print(
            "static_assert(sizeof($n$_) == sizeof(proto.$n$()), \"size "
            "mismatch\");\n",
            "n", field->name());
        p->Print("$n$_ = static_cast<decltype($n$_)>(proto.$n$());\n", "n",
                 field->name());
      }
    } else {  // is_repeated()
      p->Print("$n$_.clear();\n", "n", field->name());
      p->Print("for (const auto& field : proto.$n$()) {\n", "n", field->name());
      p->Print("  $n$_.emplace_back();\n", "n", field->name());
      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
        p->Print("  $n$_.back().FromProto(field);\n", "n", field->name());
      } else {
        p->Print(
            "static_assert(sizeof($n$_.back()) == sizeof(proto.$n$(0)), \"size "
            "mismatch\");\n",
            "n", field->name());
        p->Print(
            "  $n$_.back() = static_cast<decltype($n$_)::value_type>(field);\n",
            "n", field->name());
      }
      p->Print("}\n");
    }
  }
  p->Print("unknown_fields_ = proto.unknown_fields();\n");
  p->Outdent();
  p->Print("}\n\n");

  // Genrate the ToProto() method definition.
  p->Print("void $f$::ToProto($p$* proto) const {\n", "f", full_name, "p",
           proto_type);
  p->Indent();
  p->Print("proto->Clear();\n");
  for (int i = 0; i < msg->field_count(); i++) {
    p->Print("\n");
    const FieldDescriptor* field = msg->field(i);
    if (!field->is_repeated()) {
      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
        p->Print("$n$_.ToProto(proto->mutable_$n$());\n", "n", field->name());
      } else {
        p->Print(
            "static_assert(sizeof($n$_) == sizeof(proto->$n$()), \"size "
            "mismatch\");\n",
            "n", field->name());
        p->Print("proto->set_$n$(static_cast<decltype(proto->$n$())>($n$_));\n",
                 "n", field->name());
      }
    } else {  // is_repeated()
      p->Print("for (const auto& it : $n$_) {\n", "n", field->name());
      if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
        p->Print("  auto* entry = proto->add_$n$();\n", "n", field->name());
        p->Print("  it.ToProto(entry);\n");
      } else {
        p->Print(
            "  proto->add_$n$(static_cast<decltype(proto->$n$(0))>(it));\n",
            "n", field->name());
        p->Print(
            "static_assert(sizeof(it) == sizeof(proto->$n$(0)), \"size "
            "mismatch\");\n",
            "n", field->name());
      }
      p->Print("}\n");
    }
  }
  p->Print("*(proto->mutable_unknown_fields()) = unknown_fields_;\n");
  p->Outdent();
  p->Print("}\n\n");

  // Recurse into nested types.
  for (int i = 0; i < msg->field_count(); i++) {
    const FieldDescriptor* field = msg->field(i);

    if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
        field->message_type()->file() == msg->file()) {
      std::string child_prefix = prefix + msg->name() + "::";
      GenCpp(field->message_type(), p, child_prefix);
    }
  }
}

int main(int argc, char** argv) {
  if (argc <= 4) {
    PERFETTO_ELOG(
        "Usage: %s source.proto dir/for/header dir/for/cc include/path",
        argv[0]);
    return 1;
  }
  ProtoToCpp proto_to_cpp(argv[2], argv[3], argv[4]);
  proto_to_cpp.Convert(argv[1]);
  return 0;
}