/* * 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 #include #include #include #include #include #include #include #include #include #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 */ )"; 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 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_FATAL("Not reached"); // for gcc } 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 \n"); header_printer.Print("#include \n"); header_printer.Print("#include \n"); header_printer.Print("#include \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 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("\n#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("bool operator==(const $n$&) const;\n", "n", msg->name()); p->Print( "bool operator!=(const $n$& other) const { return !(*this == other); }\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(p), s); }\n", "n", field->lowercase_name()); } } } else { // is_repeated() p->Print( "int $n$_size() const { return static_cast($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("std::vector<$t$>* mutable_$n$() { return &$n$_; }\n", "t", GetCppType(field, false), "n", field->lowercase_name()); p->Print("void clear_$n$() { $n$_.clear(); }\n", "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"); // Comparison operator p->Print("#pragma GCC diagnostic push\n"); p->Print("#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n"); p->Print("bool $f$::operator==(const $f$& other) const {\n", "f", full_name, "n", msg->name()); p->Indent(); p->Print("return "); for (int i = 0; i < msg->field_count(); i++) { if (i > 0) p->Print("\n && "); const FieldDescriptor* field = msg->field(i); p->Print("($n$_ == other.$n$_)", "n", field->name()); } p->Print(";"); p->Outdent(); p->Print("}\n"); p->Print("#pragma GCC diagnostic pop\n\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(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(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$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$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; }