Split the code generators for .upb and .upbdefs.

Before there was a single code generator that generated both
.upb and .upbdefs, even though they are generated by different
rules. This worked fine as long as the codegen steps were
sandboxed, but if not it led to build errors.

Fixes https://github.com/protocolbuffers/upb/issues/354.
pull/13171/head
Joshua Haberman 4 years ago
parent b10b02f66f
commit 7a54a5f3d6
  1. 48
      bazel/upb_proto_library.bzl
  2. 30
      upbc/BUILD
  3. 65
      upbc/common.cc
  4. 66
      upbc/common.h
  5. 12
      upbc/generator.h
  6. 9
      upbc/main.cc
  7. 292
      upbc/protoc-gen-upb.cc
  8. 183
      upbc/protoc-gen-upbdefs.cc

@ -153,26 +153,29 @@ _UpbDefsWrappedCcInfo = provider(fields = ["cc_info"])
_WrappedGeneratedSrcsInfo = provider(fields = ["srcs"])
_WrappedDefsGeneratedSrcsInfo = provider(fields = ["srcs"])
def _compile_upb_protos(ctx, proto_info, proto_sources, ext):
def _compile_upb_protos(ctx, generator, proto_info, proto_sources):
if len(proto_sources) == 0:
return GeneratedSrcsInfo(srcs = [], hdrs = [])
ext = "." + generator
tool = getattr(ctx.executable, "_gen_" + generator)
srcs = [_generate_output_file(ctx, name, ext + ".c") for name in proto_sources]
hdrs = [_generate_output_file(ctx, name, ext + ".h") for name in proto_sources]
transitive_sets = proto_info.transitive_descriptor_sets.to_list()
fasttable_enabled = ctx.attr._fasttable_enabled[_FastTableEnabled].enabled
fasttable_enabled = (hasattr(ctx.attr, "_fasttable_enabled") and
ctx.attr._fasttable_enabled[_FastTableEnabled].enabled)
codegen_params = "fasttable:" if fasttable_enabled else ""
ctx.actions.run(
inputs = depset(
direct = [proto_info.direct_descriptor_set],
transitive = [proto_info.transitive_descriptor_sets],
),
tools = [ctx.executable._upbc],
tools = [tool],
outputs = srcs + hdrs,
executable = ctx.executable._protoc,
arguments = [
"--upb_out=" + codegen_params + _get_real_root(srcs[0]),
"--plugin=protoc-gen-upb=" + ctx.executable._upbc.path,
"--" + generator + "_out=" + codegen_params + _get_real_root(srcs[0]),
"--plugin=protoc-gen-" + generator + "=" + tool.path,
"--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]),
] +
[_get_real_short_path(file) for file in proto_sources],
@ -213,22 +216,20 @@ def _upb_proto_rule_impl(ctx):
cc_info,
]
def _upb_proto_aspect_impl(target, ctx, cc_provider, file_provider):
def _upb_proto_aspect_impl(target, ctx, generator, cc_provider, file_provider):
proto_info = target[ProtoInfo]
files = _compile_upb_protos(ctx, proto_info, proto_info.direct_sources, ctx.attr._ext)
deps = ctx.rule.attr.deps + ctx.attr._upb
if cc_provider == _UpbDefsWrappedCcInfo:
deps += ctx.attr._upb_reflection
files = _compile_upb_protos(ctx, generator, proto_info, proto_info.direct_sources)
deps = ctx.rule.attr.deps + getattr(ctx.attr, "_" + generator)
dep_ccinfos = [dep[CcInfo] for dep in deps if CcInfo in dep]
dep_ccinfos += [dep[_UpbWrappedCcInfo].cc_info for dep in deps if _UpbWrappedCcInfo in dep]
dep_ccinfos += [dep[_UpbDefsWrappedCcInfo].cc_info for dep in deps if _UpbDefsWrappedCcInfo in dep]
if cc_provider == _UpbDefsWrappedCcInfo:
if generator == "upbdefs":
if _UpbWrappedCcInfo not in target:
fail("Target should have _UpbDefsWrappedCcInfo provider")
dep_ccinfos += [target[_UpbWrappedCcInfo].cc_info]
cc_info = _cc_library_func(
ctx = ctx,
name = ctx.rule.attr.name + ctx.attr._ext,
name = ctx.rule.attr.name + "." + generator,
hdrs = files.hdrs,
srcs = files.srcs,
copts = ctx.attr._copts[_UpbProtoLibraryCopts].copts,
@ -237,10 +238,10 @@ def _upb_proto_aspect_impl(target, ctx, cc_provider, file_provider):
return [cc_provider(cc_info = cc_info), file_provider(srcs = files)]
def _upb_proto_library_aspect_impl(target, ctx):
return _upb_proto_aspect_impl(target, ctx, _UpbWrappedCcInfo, _WrappedGeneratedSrcsInfo)
return _upb_proto_aspect_impl(target, ctx, "upb", _UpbWrappedCcInfo, _WrappedGeneratedSrcsInfo)
def _upb_proto_reflection_library_aspect_impl(target, ctx):
return _upb_proto_aspect_impl(target, ctx, _UpbDefsWrappedCcInfo, _WrappedDefsGeneratedSrcsInfo)
return _upb_proto_aspect_impl(target, ctx, "upbdefs", _UpbDefsWrappedCcInfo, _WrappedDefsGeneratedSrcsInfo)
def _maybe_add(d):
if not _is_bazel:
@ -258,7 +259,7 @@ _upb_proto_library_aspect = aspect(
"_copts": attr.label(
default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use",
),
"_upbc": attr.label(
"_gen_upb": attr.label(
executable = True,
cfg = "host",
default = "//upbc:protoc-gen-upb",
@ -275,7 +276,6 @@ _upb_proto_library_aspect = aspect(
"//:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
"//:upb",
]),
"_ext": attr.string(default = ".upb"),
"_fasttable_enabled": attr.label(default = "//:fasttable_enabled"),
}),
implementation = _upb_proto_library_aspect_impl,
@ -308,10 +308,10 @@ _upb_proto_reflection_library_aspect = aspect(
"_copts": attr.label(
default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use",
),
"_upbc": attr.label(
"_gen_upbdefs": attr.label(
executable = True,
cfg = "host",
default = "//upbc:protoc-gen-upb",
default = "//upbc:protoc-gen-upbdefs",
),
"_protoc": attr.label(
executable = True,
@ -321,22 +321,12 @@ _upb_proto_reflection_library_aspect = aspect(
"_cc_toolchain": attr.label(
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
),
# For unknown reasons, this gets overwritten.
"_upb": attr.label_list(
"_upbdefs": attr.label_list(
default = [
"//:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
"//:upb",
"//:reflection",
],
),
"_upb_reflection": attr.label_list(
default = [
"//:upb",
"//:reflection",
],
),
"_ext": attr.string(default = ".upbdefs"),
"_fasttable_enabled": attr.label(default = "//:fasttable_enabled"),
}),
implementation = _upb_proto_reflection_library_aspect_impl,
provides = [

@ -6,15 +6,27 @@ load(
licenses(["notice"])
cc_library(
name = "upbc_generator",
name = "common",
hdrs = ["common.h"],
srcs = ["common.cc"],
copts = UPB_DEFAULT_CPPOPTS,
deps = [
"@com_google_protobuf//:protobuf",
"@com_google_absl//absl/strings",
],
)
cc_binary(
name = "protoc-gen-upb",
srcs = [
"generator.cc",
"protoc-gen-upb.cc",
"message_layout.cc",
"message_layout.h",
],
hdrs = ["generator.h"],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
":common",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/strings",
@ -24,12 +36,18 @@ cc_library(
)
cc_binary(
name = "protoc-gen-upb",
srcs = ["main.cc"],
name = "protoc-gen-upbdefs",
srcs = [
"protoc-gen-upbdefs.cc",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
":upbc_generator",
":common",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/strings",
"@com_google_protobuf//:protobuf",
"@com_google_protobuf//:protoc_lib",
],
)

@ -0,0 +1,65 @@
#include "absl/strings/str_replace.h"
#include "upbc/common.h"
namespace upbc {
namespace {
namespace protobuf = ::google::protobuf;
void AddMessages(const protobuf::Descriptor* message,
std::vector<const protobuf::Descriptor*>* messages) {
messages->push_back(message);
for (int i = 0; i < message->nested_type_count(); i++) {
AddMessages(message->nested_type(i), messages);
}
}
} // namespace
std::string StripExtension(absl::string_view fname) {
size_t lastdot = fname.find_last_of(".");
if (lastdot == std::string::npos) {
return std::string(fname);
}
return std::string(fname.substr(0, lastdot));
}
std::string ToCIdent(absl::string_view str) {
return absl::StrReplaceAll(str, {{".", "_"}, {"/", "_"}});
}
std::string ToPreproc(absl::string_view str) {
return absl::AsciiStrToUpper(ToCIdent(str));
}
void EmitFileWarning(const protobuf::FileDescriptor* file, Output& output) {
output(
"/* This file was generated by upbc (the upb compiler) from the input\n"
" * file:\n"
" *\n"
" * $0\n"
" *\n"
" * Do not edit -- your changes will be discarded when the file is\n"
" * regenerated. */\n\n",
file->name());
}
std::vector<const protobuf::Descriptor*> SortedMessages(
const protobuf::FileDescriptor* file) {
std::vector<const protobuf::Descriptor*> messages;
for (int i = 0; i < file->message_type_count(); i++) {
AddMessages(file->message_type(i), &messages);
}
return messages;
}
std::string MessageName(const protobuf::Descriptor* descriptor) {
return ToCIdent(descriptor->full_name());
}
std::string MessageInit(const protobuf::Descriptor* descriptor) {
return MessageName(descriptor) + "_msginit";
}
} // namespace upbc

@ -0,0 +1,66 @@
#ifndef UPBC_COMMON_H
#define UPBC_COMMON_H
#include <vector>
#include "absl/strings/substitute.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/zero_copy_stream.h"
namespace upbc {
class Output {
public:
Output(google::protobuf::io::ZeroCopyOutputStream* stream)
: stream_(stream) {}
~Output() { stream_->BackUp((int)size_); }
template <class... Arg>
void operator()(absl::string_view format, const Arg&... arg) {
Write(absl::Substitute(format, arg...));
}
private:
void Write(absl::string_view data) {
while (!data.empty()) {
RefreshOutput();
size_t to_write = std::min(data.size(), size_);
memcpy(ptr_, data.data(), to_write);
data.remove_prefix(to_write);
ptr_ += to_write;
size_ -= to_write;
}
}
void RefreshOutput() {
while (size_ == 0) {
void *ptr;
int size;
if (!stream_->Next(&ptr, &size)) {
fprintf(stderr, "upbc: Failed to write to to output\n");
abort();
}
ptr_ = static_cast<char*>(ptr);
size_ = size;
}
}
google::protobuf::io::ZeroCopyOutputStream* stream_;
char *ptr_ = nullptr;
size_t size_ = 0;
};
std::string StripExtension(absl::string_view fname);
std::string ToCIdent(absl::string_view str);
std::string ToPreproc(absl::string_view str);
void EmitFileWarning(const google::protobuf::FileDescriptor* file,
Output& output);
std::vector<const google::protobuf::Descriptor*> SortedMessages(
const google::protobuf::FileDescriptor* file);
std::string MessageInit(const google::protobuf::Descriptor* descriptor);
std::string MessageName(const google::protobuf::Descriptor* descriptor);
} // namespace upbc
# endif // UPBC_COMMON_H

@ -1,12 +0,0 @@
#ifndef UPBC_GENERATOR_H_
#define UPBC_GENERATOR_H_
#include <memory>
#include <google/protobuf/compiler/code_generator.h>
namespace upbc {
std::unique_ptr<google::protobuf::compiler::CodeGenerator> GetGenerator();
}
#endif // UPBC_GENERATOR_H_

@ -1,9 +0,0 @@
#include "google/protobuf/compiler/plugin.h"
#include "upbc/generator.h"
int main(int argc, char** argv) {
return google::protobuf::compiler::PluginMain(argc, argv,
upbc::GetGenerator().get());
}

@ -1,107 +1,31 @@
#include "upbc/generator.h"
#include <memory>
#include "absl/base/attributes.h"
#include "absl/container/flat_hash_map.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/plugin.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/zero_copy_stream.h"
#include "google/protobuf/wire_format.h"
#include "upbc/common.h"
#include "upbc/message_layout.h"
namespace upbc {
namespace {
namespace protoc = ::google::protobuf::compiler;
namespace protobuf = ::google::protobuf;
static std::string StripExtension(absl::string_view fname) {
size_t lastdot = fname.find_last_of(".");
if (lastdot == std::string::npos) {
return std::string(fname);
}
return std::string(fname.substr(0, lastdot));
}
static std::string HeaderFilename(std::string proto_filename) {
std::string HeaderFilename(std::string proto_filename) {
return StripExtension(proto_filename) + ".upb.h";
}
static std::string SourceFilename(std::string proto_filename) {
std::string SourceFilename(std::string proto_filename) {
return StripExtension(proto_filename) + ".upb.c";
}
static std::string DefHeaderFilename(std::string proto_filename) {
return StripExtension(proto_filename) + ".upbdefs.h";
}
static std::string DefSourceFilename(std::string proto_filename) {
return StripExtension(proto_filename) + ".upbdefs.c";
}
class Output {
public:
Output(protobuf::io::ZeroCopyOutputStream* stream) : stream_(stream) {}
~Output() { stream_->BackUp((int)size_); }
template <class... Arg>
void operator()(absl::string_view format, const Arg&... arg) {
Write(absl::Substitute(format, arg...));
}
private:
void Write(absl::string_view data) {
while (!data.empty()) {
RefreshOutput();
size_t to_write = std::min(data.size(), size_);
memcpy(ptr_, data.data(), to_write);
data.remove_prefix(to_write);
ptr_ += to_write;
size_ -= to_write;
}
}
void RefreshOutput() {
while (size_ == 0) {
void *ptr;
int size;
if (!stream_->Next(&ptr, &size)) {
fprintf(stderr, "upbc: Failed to write to to output\n");
abort();
}
ptr_ = static_cast<char*>(ptr);
size_ = size;
}
}
protobuf::io::ZeroCopyOutputStream* stream_;
char *ptr_ = nullptr;
size_t size_ = 0;
};
namespace upbc {
class Generator : public protoc::CodeGenerator {
~Generator() override {}
bool Generate(const protobuf::FileDescriptor* file,
const std::string& parameter, protoc::GeneratorContext* context,
std::string* error) const override;
uint64_t GetSupportedFeatures() const override {
return FEATURE_PROTO3_OPTIONAL;
}
};
void AddMessages(const protobuf::Descriptor* message,
std::vector<const protobuf::Descriptor*>* messages) {
messages->push_back(message);
for (int i = 0; i < message->nested_type_count(); i++) {
AddMessages(message->nested_type(i), messages);
}
}
void AddEnums(const protobuf::Descriptor* message,
std::vector<const protobuf::EnumDescriptor*>* enums) {
for (int i = 0; i < message->enum_type_count(); i++) {
@ -118,15 +42,6 @@ void SortDefs(std::vector<T>* defs) {
[](T a, T b) { return a->full_name() < b->full_name(); });
}
std::vector<const protobuf::Descriptor*> SortedMessages(
const protobuf::FileDescriptor* file) {
std::vector<const protobuf::Descriptor*> messages;
for (int i = 0; i < file->message_type_count(); i++) {
AddMessages(file->message_type(i), &messages);
}
return messages;
}
std::vector<const protobuf::EnumDescriptor*> SortedEnums(
const protobuf::FileDescriptor* file) {
std::vector<const protobuf::EnumDescriptor*> enums;
@ -172,18 +87,6 @@ std::vector<const protobuf::FieldDescriptor*> SortedSubmessages(
return ret;
}
std::string ToCIdent(absl::string_view str) {
return absl::StrReplaceAll(str, {{".", "_"}, {"/", "_"}});
}
std::string DefInitSymbol(const protobuf::FileDescriptor *file) {
return ToCIdent(file->name()) + "_upbdefinit";
}
std::string ToPreproc(absl::string_view str) {
return absl::AsciiStrToUpper(ToCIdent(str));
}
std::string EnumValueSymbol(const protobuf::EnumValueDescriptor* value) {
return ToCIdent(value->full_name());
}
@ -192,14 +95,6 @@ std::string GetSizeInit(const MessageLayout::Size& size) {
return absl::Substitute("UPB_SIZE($0, $1)", size.size32, size.size64);
}
std::string MessageName(const protobuf::Descriptor* descriptor) {
return ToCIdent(descriptor->full_name());
}
std::string MessageInit(const protobuf::Descriptor* descriptor) {
return MessageName(descriptor) + "_msginit";
}
std::string CTypeInternal(const protobuf::FieldDescriptor* field,
bool is_const) {
std::string maybe_const = is_const ? "const " : "";
@ -320,18 +215,6 @@ void DumpEnumValues(const protobuf::EnumDescriptor* desc, Output& output) {
}
}
void EmitFileWarning(const protobuf::FileDescriptor* file, Output& output) {
output(
"/* This file was generated by upbc (the upb compiler) from the input\n"
" * file:\n"
" *\n"
" * $0\n"
" *\n"
" * Do not edit -- your changes will be discarded when the file is\n"
" * regenerated. */\n\n",
file->name());
}
void GenerateMessageInHeader(const protobuf::Descriptor* message, Output& output) {
MessageLayout layout(message);
@ -1064,150 +947,47 @@ void WriteSource(const protobuf::FileDescriptor* file, Output& output,
output("\n");
}
void GenerateMessageDefAccessor(const protobuf::Descriptor* d, Output& output) {
output("UPB_INLINE const upb_msgdef *$0_getmsgdef(upb_symtab *s) {\n",
ToCIdent(d->full_name()));
output(" _upb_symtab_loaddefinit(s, &$0);\n", DefInitSymbol(d->file()));
output(" return upb_symtab_lookupmsg(s, \"$0\");\n", d->full_name());
output("}\n");
output("\n");
for (int i = 0; i < d->nested_type_count(); i++) {
GenerateMessageDefAccessor(d->nested_type(i), output);
}
}
void WriteDefHeader(const protobuf::FileDescriptor* file, Output& output) {
EmitFileWarning(file, output);
output(
"#ifndef $0_UPBDEFS_H_\n"
"#define $0_UPBDEFS_H_\n\n"
"#include \"upb/def.h\"\n"
"#include \"upb/port_def.inc\"\n"
"#ifdef __cplusplus\n"
"extern \"C\" {\n"
"#endif\n\n",
ToPreproc(file->name()));
output("#include \"upb/def.h\"\n");
output("\n");
output("#include \"upb/port_def.inc\"\n");
output("\n");
output("extern upb_def_init $0;\n", DefInitSymbol(file));
output("\n");
for (int i = 0; i < file->message_type_count(); i++) {
GenerateMessageDefAccessor(file->message_type(i), output);
}
output(
"#ifdef __cplusplus\n"
"} /* extern \"C\" */\n"
"#endif\n"
"\n"
"#include \"upb/port_undef.inc\"\n"
"\n"
"#endif /* $0_UPBDEFS_H_ */\n",
ToPreproc(file->name()));
}
// Escape C++ trigraphs by escaping question marks to \?
std::string EscapeTrigraphs(absl::string_view to_escape) {
return absl::StrReplaceAll(to_escape, {{"?", "\\?"}});
}
void WriteDefSource(const protobuf::FileDescriptor* file, Output& output) {
EmitFileWarning(file, output);
output("#include \"upb/def.h\"\n");
output("#include \"$0\"\n", DefHeaderFilename(file->name()));
output("\n");
for (int i = 0; i < file->dependency_count(); i++) {
output("extern upb_def_init $0;\n", DefInitSymbol(file->dependency(i)));
}
std::vector<const protobuf::Descriptor*> file_messages =
SortedMessages(file);
for (auto message : file_messages) {
output("extern const upb_msglayout $0;\n", MessageInit(message));
}
output("\n");
if (!file_messages.empty()) {
output("static const upb_msglayout *layouts[$0] = {\n", file_messages.size());
for (auto message : file_messages) {
output(" &$0,\n", MessageInit(message));
}
output("};\n");
output("\n");
}
protobuf::FileDescriptorProto file_proto;
file->CopyTo(&file_proto);
std::string file_data;
file_proto.SerializeToString(&file_data);
output("static const char descriptor[$0] = {", file_data.size());
// C90 only guarantees that strings can be up to 509 characters, and some
// implementations have limits here (for example, MSVC only allows 64k:
// https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/fatal-error-c1091.
// So we always emit an array instead of a string.
for (size_t i = 0; i < file_data.size();) {
for (size_t j = 0; j < 25 && i < file_data.size(); ++i, ++j) {
output("'$0', ", absl::CEscape(file_data.substr(i, 1)));
}
output("\n");
}
output("};\n\n");
output("static upb_def_init *deps[$0] = {\n", file->dependency_count() + 1);
for (int i = 0; i < file->dependency_count(); i++) {
output(" &$0,\n", DefInitSymbol(file->dependency(i)));
}
output(" NULL\n");
output("};\n");
output("\n");
output("upb_def_init $0 = {\n", DefInitSymbol(file));
output(" deps,\n");
if (file_messages.empty()) {
output(" NULL,\n");
} else {
output(" layouts,\n");
class Generator : public protoc::CodeGenerator {
~Generator() override {}
bool Generate(const protobuf::FileDescriptor* file,
const std::string& parameter, protoc::GeneratorContext* context,
std::string* error) const override;
uint64_t GetSupportedFeatures() const override {
return FEATURE_PROTO3_OPTIONAL;
}
output(" \"$0\",\n", file->name());
output(" UPB_STRVIEW_INIT(descriptor, $0)\n", file_data.size());
output("};\n");
}
};
bool Generator::Generate(const protobuf::FileDescriptor* file,
const std::string& parameter,
protoc::GeneratorContext* context,
std::string* /* error */) const {
bool fasttable_enabled = parameter == "fasttable";
std::string* error) const {
bool fasttable_enabled = false;
std::vector<std::pair<std::string, std::string>> params;
google::protobuf::compiler::ParseGeneratorParameter(parameter, &params);
for (const auto& pair : params) {
if (pair.first == "fasttable") {
fasttable_enabled = true;
} else {
*error = "Unknown parameter: " + pair.first;
return false;
}
}
Output h_output(context->Open(HeaderFilename(file->name())));
WriteHeader(file, h_output);
Output c_output(context->Open(SourceFilename(file->name())));
WriteSource(file, c_output, fasttable_enabled);
Output h_def_output(context->Open(DefHeaderFilename(file->name())));
WriteDefHeader(file, h_def_output);
Output c_def_output(context->Open(DefSourceFilename(file->name())));
WriteDefSource(file, c_def_output);
return true;
}
std::unique_ptr<google::protobuf::compiler::CodeGenerator> GetGenerator() {
return std::unique_ptr<google::protobuf::compiler::CodeGenerator>(
new Generator());
}
} // namespace
} // namespace upbc
int main(int argc, char** argv) {
std::unique_ptr<google::protobuf::compiler::CodeGenerator> generator(
new upbc::Generator());
return google::protobuf::compiler::PluginMain(argc, argv, generator.get());
}

@ -0,0 +1,183 @@
#include <memory>
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/plugin.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "upbc/common.h"
namespace upbc {
namespace {
namespace protoc = ::google::protobuf::compiler;
namespace protobuf = ::google::protobuf;
std::string DefInitSymbol(const protobuf::FileDescriptor *file) {
return ToCIdent(file->name()) + "_upbdefinit";
}
static std::string DefHeaderFilename(std::string proto_filename) {
return StripExtension(proto_filename) + ".upbdefs.h";
}
static std::string DefSourceFilename(std::string proto_filename) {
return StripExtension(proto_filename) + ".upbdefs.c";
}
void GenerateMessageDefAccessor(const protobuf::Descriptor* d, Output& output) {
output("UPB_INLINE const upb_msgdef *$0_getmsgdef(upb_symtab *s) {\n",
ToCIdent(d->full_name()));
output(" _upb_symtab_loaddefinit(s, &$0);\n", DefInitSymbol(d->file()));
output(" return upb_symtab_lookupmsg(s, \"$0\");\n", d->full_name());
output("}\n");
output("\n");
for (int i = 0; i < d->nested_type_count(); i++) {
GenerateMessageDefAccessor(d->nested_type(i), output);
}
}
void WriteDefHeader(const protobuf::FileDescriptor* file, Output& output) {
EmitFileWarning(file, output);
output(
"#ifndef $0_UPBDEFS_H_\n"
"#define $0_UPBDEFS_H_\n\n"
"#include \"upb/def.h\"\n"
"#include \"upb/port_def.inc\"\n"
"#ifdef __cplusplus\n"
"extern \"C\" {\n"
"#endif\n\n",
ToPreproc(file->name()));
output("#include \"upb/def.h\"\n");
output("\n");
output("#include \"upb/port_def.inc\"\n");
output("\n");
output("extern upb_def_init $0;\n", DefInitSymbol(file));
output("\n");
for (int i = 0; i < file->message_type_count(); i++) {
GenerateMessageDefAccessor(file->message_type(i), output);
}
output(
"#ifdef __cplusplus\n"
"} /* extern \"C\" */\n"
"#endif\n"
"\n"
"#include \"upb/port_undef.inc\"\n"
"\n"
"#endif /* $0_UPBDEFS_H_ */\n",
ToPreproc(file->name()));
}
void WriteDefSource(const protobuf::FileDescriptor* file, Output& output) {
EmitFileWarning(file, output);
output("#include \"upb/def.h\"\n");
output("#include \"$0\"\n", DefHeaderFilename(file->name()));
output("\n");
for (int i = 0; i < file->dependency_count(); i++) {
output("extern upb_def_init $0;\n", DefInitSymbol(file->dependency(i)));
}
std::vector<const protobuf::Descriptor*> file_messages =
SortedMessages(file);
for (auto message : file_messages) {
output("extern const upb_msglayout $0;\n", MessageInit(message));
}
output("\n");
if (!file_messages.empty()) {
output("static const upb_msglayout *layouts[$0] = {\n", file_messages.size());
for (auto message : file_messages) {
output(" &$0,\n", MessageInit(message));
}
output("};\n");
output("\n");
}
protobuf::FileDescriptorProto file_proto;
file->CopyTo(&file_proto);
std::string file_data;
file_proto.SerializeToString(&file_data);
output("static const char descriptor[$0] = {", file_data.size());
// C90 only guarantees that strings can be up to 509 characters, and some
// implementations have limits here (for example, MSVC only allows 64k:
// https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/fatal-error-c1091.
// So we always emit an array instead of a string.
for (size_t i = 0; i < file_data.size();) {
for (size_t j = 0; j < 25 && i < file_data.size(); ++i, ++j) {
output("'$0', ", absl::CEscape(file_data.substr(i, 1)));
}
output("\n");
}
output("};\n\n");
output("static upb_def_init *deps[$0] = {\n", file->dependency_count() + 1);
for (int i = 0; i < file->dependency_count(); i++) {
output(" &$0,\n", DefInitSymbol(file->dependency(i)));
}
output(" NULL\n");
output("};\n");
output("\n");
output("upb_def_init $0 = {\n", DefInitSymbol(file));
output(" deps,\n");
if (file_messages.empty()) {
output(" NULL,\n");
} else {
output(" layouts,\n");
}
output(" \"$0\",\n", file->name());
output(" UPB_STRVIEW_INIT(descriptor, $0)\n", file_data.size());
output("};\n");
}
class Generator : public protoc::CodeGenerator {
~Generator() override {}
bool Generate(const protobuf::FileDescriptor* file,
const std::string& parameter, protoc::GeneratorContext* context,
std::string* error) const override;
uint64_t GetSupportedFeatures() const override {
return FEATURE_PROTO3_OPTIONAL;
}
};
bool Generator::Generate(const protobuf::FileDescriptor* file,
const std::string& parameter,
protoc::GeneratorContext* context,
std::string* error) const {
std::vector<std::pair<std::string, std::string>> params;
google::protobuf::compiler::ParseGeneratorParameter(parameter, &params);
for (const auto& pair : params) {
*error = "Unknown parameter: " + pair.first;
return false;
}
Output h_def_output(context->Open(DefHeaderFilename(file->name())));
WriteDefHeader(file, h_def_output);
Output c_def_output(context->Open(DefSourceFilename(file->name())));
WriteDefSource(file, c_def_output);
return true;
}
} // namespace
} // namespace upbc
int main(int argc, char** argv) {
std::unique_ptr<google::protobuf::compiler::CodeGenerator> generator(
new upbc::Generator());
return google::protobuf::compiler::PluginMain(argc, argv, generator.get());
}
Loading…
Cancel
Save