From 7a54a5f3d68e3dc806da59f381082e1428b4f78e Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Fri, 18 Dec 2020 19:39:18 -0800 Subject: [PATCH] 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. --- bazel/upb_proto_library.bzl | 48 ++-- upbc/BUILD | 30 ++- upbc/common.cc | 65 +++++ upbc/common.h | 66 +++++ upbc/generator.h | 12 - upbc/main.cc | 9 - upbc/{generator.cc => protoc-gen-upb.cc} | 292 +++-------------------- upbc/protoc-gen-upbdefs.cc | 183 ++++++++++++++ 8 files changed, 393 insertions(+), 312 deletions(-) create mode 100644 upbc/common.cc create mode 100644 upbc/common.h delete mode 100644 upbc/generator.h delete mode 100644 upbc/main.cc rename upbc/{generator.cc => protoc-gen-upb.cc} (82%) create mode 100644 upbc/protoc-gen-upbdefs.cc diff --git a/bazel/upb_proto_library.bzl b/bazel/upb_proto_library.bzl index bec5d9cbd1..155d18d98c 100644 --- a/bazel/upb_proto_library.bzl +++ b/bazel/upb_proto_library.bzl @@ -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 = [ diff --git a/upbc/BUILD b/upbc/BUILD index 23897c9ac4..3b7d69bd78 100644 --- a/upbc/BUILD +++ b/upbc/BUILD @@ -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", ], ) diff --git a/upbc/common.cc b/upbc/common.cc new file mode 100644 index 0000000000..c1b30a3401 --- /dev/null +++ b/upbc/common.cc @@ -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* 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 SortedMessages( + const protobuf::FileDescriptor* file) { + std::vector 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 diff --git a/upbc/common.h b/upbc/common.h new file mode 100644 index 0000000000..5825786fb2 --- /dev/null +++ b/upbc/common.h @@ -0,0 +1,66 @@ + +#ifndef UPBC_COMMON_H +#define UPBC_COMMON_H + +#include + +#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 + 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(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 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 diff --git a/upbc/generator.h b/upbc/generator.h deleted file mode 100644 index ed6cedc6c7..0000000000 --- a/upbc/generator.h +++ /dev/null @@ -1,12 +0,0 @@ - -#ifndef UPBC_GENERATOR_H_ -#define UPBC_GENERATOR_H_ - -#include -#include - -namespace upbc { -std::unique_ptr GetGenerator(); -} - -#endif // UPBC_GENERATOR_H_ diff --git a/upbc/main.cc b/upbc/main.cc deleted file mode 100644 index 3a38937797..0000000000 --- a/upbc/main.cc +++ /dev/null @@ -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()); -} diff --git a/upbc/generator.cc b/upbc/protoc-gen-upb.cc similarity index 82% rename from upbc/generator.cc rename to upbc/protoc-gen-upb.cc index 4f9db9ad91..e099ad4649 100644 --- a/upbc/generator.cc +++ b/upbc/protoc-gen-upb.cc @@ -1,107 +1,31 @@ -#include "upbc/generator.h" - #include -#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 - 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(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* 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* enums) { for (int i = 0; i < message->enum_type_count(); i++) { @@ -118,15 +42,6 @@ void SortDefs(std::vector* defs) { [](T a, T b) { return a->full_name() < b->full_name(); }); } -std::vector SortedMessages( - const protobuf::FileDescriptor* file) { - std::vector messages; - for (int i = 0; i < file->message_type_count(); i++) { - AddMessages(file->message_type(i), &messages); - } - return messages; -} - std::vector SortedEnums( const protobuf::FileDescriptor* file) { std::vector enums; @@ -172,18 +87,6 @@ std::vector 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 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> params; + google::protobuf::compiler::ParseGeneratorParameter(parameter, ¶ms); + + 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 GetGenerator() { - return std::unique_ptr( - new Generator()); -} - +} // namespace } // namespace upbc + +int main(int argc, char** argv) { + std::unique_ptr generator( + new upbc::Generator()); + return google::protobuf::compiler::PluginMain(argc, argv, generator.get()); +} diff --git a/upbc/protoc-gen-upbdefs.cc b/upbc/protoc-gen-upbdefs.cc new file mode 100644 index 0000000000..f51ae0792f --- /dev/null +++ b/upbc/protoc-gen-upbdefs.cc @@ -0,0 +1,183 @@ + +#include + +#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 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> params; + google::protobuf::compiler::ParseGeneratorParameter(parameter, ¶ms); + + 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 generator( + new upbc::Generator()); + return google::protobuf::compiler::PluginMain(argc, argv, generator.get()); +}