From 94d5f1236986103d03582b0e53e4daef0760bf93 Mon Sep 17 00:00:00 2001
From: h-vetinari <h.vetinari@gmx.com>
Date: Wed, 12 Jun 2024 08:24:41 -0700
Subject: [PATCH] Adding DLL export/import tags to generated public upb API
 (redux) (#17079)

Picking up #14981 from @dawidcha after several months of radio silence.

Quoting the OP of that PR:
> I have been collaborating with the grpc developers to make it possible to build that library as a Windows DLL - a couple of PRs were already merged, more like [grpc/grpc#34345](https://github.com/grpc/grpc/pull/34345) are pending.
>
> The grpc library incorporates some upb-generated, and upbdefs-generated code into grpc.dll, which is referenced by other code that consumes the library. Since this is now a DLL, that code doesn't know how to link to these generated symbols because they are not annotated with __declspec(dllimport).
>
> This PR aims to fix that by introducing a parameter 'dllexport_tag' to the upb and upbdefs plugins. That parameter should be a string e.g. MYAPP_DLL and when set, the extern symbols are annotated with a macro with that name. This can either be set externally to __declspec(dllimport) or, as is usual practice, when compiling code into a DLL, the macro <dllexport_tag>_EXPORT (i.e. MYAPP_DLL_EXPORT) is defined, and when consuming the DLL <dllexport_tag>_IMPORT is defined if neither are defined then the MYAPP_DLL macro becomes empty string which is what you want for building a static library.
>
> This is a continuation of #14230
>
> Fixes: #14255

Towards #13726
Closes #14981

Closes #17079

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/17079 from h-vetinari:add_dll_tags 34927b1ddeff6bac1a36dfa7eefb414894f1a538
PiperOrigin-RevId: 642622258
---
 upb_generator/common.cc             |  4 +++
 upb_generator/common.h              |  2 ++
 upb_generator/protoc-gen-upbdefs.cc | 46 +++++++++++++++++++++--------
 3 files changed, 39 insertions(+), 13 deletions(-)

diff --git a/upb_generator/common.cc b/upb_generator/common.cc
index f7f408128e..2dbe9d7f78 100644
--- a/upb_generator/common.cc
+++ b/upb_generator/common.cc
@@ -63,6 +63,10 @@ std::string MessageInitName(upb::MessageDefPtr descriptor) {
   return MessageInit(descriptor.full_name());
 }
 
+std::string PadPrefix(absl::string_view tag) {
+  return tag.empty() ? "" : absl::StrCat(" ", tag);
+}
+
 std::string MessageName(upb::MessageDefPtr descriptor) {
   return ToCIdent(descriptor.full_name());
 }
diff --git a/upb_generator/common.h b/upb_generator/common.h
index 42bad45cfe..ce92f4f63f 100644
--- a/upb_generator/common.h
+++ b/upb_generator/common.h
@@ -8,6 +8,7 @@
 #ifndef UPB_GENERATOR_COMMON_H
 #define UPB_GENERATOR_COMMON_H
 
+#include <string>
 #include <vector>
 
 #include "absl/strings/str_replace.h"
@@ -64,6 +65,7 @@ std::string MessageName(upb::MessageDefPtr descriptor);
 std::string FileLayoutName(upb::FileDefPtr file);
 std::string MiniTableHeaderFilename(upb::FileDefPtr file);
 std::string CApiHeaderFilename(upb::FileDefPtr file);
+std::string PadPrefix(absl::string_view tag);
 
 std::string EnumInit(upb::EnumDefPtr descriptor);
 
diff --git a/upb_generator/protoc-gen-upbdefs.cc b/upb_generator/protoc-gen-upbdefs.cc
index 6ee6bafe9b..ac1b3e4d59 100644
--- a/upb_generator/protoc-gen-upbdefs.cc
+++ b/upb_generator/protoc-gen-upbdefs.cc
@@ -18,6 +18,10 @@ namespace upb {
 namespace generator {
 namespace {
 
+struct Options {
+  std::string dllexport_decl;
+};
+
 std::string DefInitSymbol(upb::FileDefPtr file) {
   return ToCIdent(file.name()) + "_upbdefinit";
 }
@@ -39,7 +43,8 @@ void GenerateMessageDefAccessor(upb::MessageDefPtr d, Output& output) {
   output("\n");
 }
 
-void WriteDefHeader(upb::FileDefPtr file, Output& output) {
+void WriteDefHeader(upb::FileDefPtr file, const Options& options,
+                    Output& output) {
   EmitFileWarning(file.name(), output);
 
   output(
@@ -54,7 +59,8 @@ void WriteDefHeader(upb::FileDefPtr file, Output& output) {
       "#endif\n\n",
       ToPreproc(file.name()));
 
-  output("extern _upb_DefPool_Init $0;\n", DefInitSymbol(file));
+  output("extern$1 _upb_DefPool_Init $0;\n", DefInitSymbol(file),
+         PadPrefix(options.dllexport_decl));
   output("\n");
 
   for (auto msg : SortedMessages(file)) {
@@ -72,7 +78,8 @@ void WriteDefHeader(upb::FileDefPtr file, Output& output) {
       ToPreproc(file.name()));
 }
 
-void WriteDefSource(upb::FileDefPtr file, Output& output) {
+void WriteDefSource(upb::FileDefPtr file, const Options& options,
+                    Output& output) {
   EmitFileWarning(file.name(), output);
 
   output("#include \"upb/reflection/def.h\"\n");
@@ -81,7 +88,9 @@ void WriteDefSource(upb::FileDefPtr file, Output& output) {
   output("\n");
 
   for (int i = 0; i < file.dependency_count(); i++) {
-    output("extern _upb_DefPool_Init $0;\n", DefInitSymbol(file.dependency(i)));
+    output("extern$1 _upb_DefPool_Init $0;\n",
+           DefInitSymbol(file.dependency(i)),
+           PadPrefix(options.dllexport_decl));
   }
 
   upb::Arena arena;
@@ -123,29 +132,40 @@ void WriteDefSource(upb::FileDefPtr file, Output& output) {
   output("};\n");
 }
 
-void GenerateFile(upb::FileDefPtr file, Plugin* plugin) {
+void GenerateFile(upb::FileDefPtr file, const Options& options,
+                  Plugin* plugin) {
   Output h_def_output;
-  WriteDefHeader(file, h_def_output);
+  WriteDefHeader(file, options, h_def_output);
   plugin->AddOutputFile(DefHeaderFilename(file), h_def_output.output());
 
   Output c_def_output;
-  WriteDefSource(file, c_def_output);
+  WriteDefSource(file, options, c_def_output);
   plugin->AddOutputFile(DefSourceFilename(file), c_def_output.output());
 }
 
+bool ParseOptions(Plugin* plugin, Options* options) {
+  for (const auto& pair : ParseGeneratorParameter(plugin->parameter())) {
+    if (pair.first == "dllexport_decl") {
+      options->dllexport_decl = pair.second;
+    } else {
+      plugin->SetError(absl::Substitute("Unknown parameter: $0", pair.first));
+      return false;
+    }
+  }
+
+  return true;
+}
+
 }  // namespace
 }  // namespace generator
 }  // namespace upb
 
 int main(int argc, char** argv) {
   upb::generator::Plugin plugin;
-  if (!plugin.parameter().empty()) {
-    plugin.SetError(
-        absl::StrCat("Expected no parameters, got: ", plugin.parameter()));
-    return 0;
-  }
+  upb::generator::Options options;
+  if (!ParseOptions(&plugin, &options)) return 0;
   plugin.GenerateFiles([&](upb::FileDefPtr file) {
-    upb::generator::GenerateFile(file, &plugin);
+    upb::generator::GenerateFile(file, options, &plugin);
   });
   return 0;
 }