Browse Source

Tool that generates serialised file descriptor protos (#1654)

Motivation:

In order to implement the reflection service we need to be able to generate serialised file descriptor protos of the
services and messages that a server offers.

Modifications:

Added an option for protoc-gen-grpc-swift that enables generating a binary file containing a base64 encoded representation of the  serialized file descriptor proto of the specified proto file.

Result:

This change enables the generation of serialised file descriptor protos that will be useful in the implementation of the reflection service.
Stefana-Ioana Dranca 2 years ago
parent
commit
bc64f149b6
2 changed files with 49 additions and 18 deletions
  1. 41 18
      Sources/protoc-gen-grpc-swift/main.swift
  2. 8 0
      Sources/protoc-gen-grpc-swift/options.swift

+ 41 - 18
Sources/protoc-gen-grpc-swift/main.swift

@@ -64,9 +64,10 @@ enum FileNaming: String {
 func outputFileName(
   component: String,
   fileDescriptor: FileDescriptor,
-  fileNamingOption: FileNaming
+  fileNamingOption: FileNaming,
+  extension: String
 ) -> String {
-  let ext = "." + component + ".swift"
+  let ext = "." + component + "." + `extension`
   let pathParts = splitPath(pathname: fileDescriptor.name)
   switch fileNamingOption {
   case .FullPath:
@@ -84,19 +85,22 @@ func uniqueOutputFileName(
   component: String,
   fileDescriptor: FileDescriptor,
   fileNamingOption: FileNaming,
-  generatedFiles: inout [String: Int]
+  generatedFiles: inout [String: Int],
+  extension: String = "swift"
 ) -> String {
   let defaultName = outputFileName(
     component: component,
     fileDescriptor: fileDescriptor,
-    fileNamingOption: fileNamingOption
+    fileNamingOption: fileNamingOption,
+    extension: `extension`
   )
   if let count = generatedFiles[defaultName] {
     generatedFiles[defaultName] = count + 1
     return outputFileName(
       component: "\(count)." + component,
       fileDescriptor: fileDescriptor,
-      fileNamingOption: fileNamingOption
+      fileNamingOption: fileNamingOption,
+      extension: `extension`
     )
   } else {
     generatedFiles[defaultName] = 1
@@ -136,19 +140,38 @@ func main(args: [String]) throws {
 
   // Only generate output for services.
   for name in request.fileToGenerate {
-    if let fileDescriptor = descriptorSet.fileDescriptor(named: name),
-       !fileDescriptor.services.isEmpty {
-      let grpcFileName = uniqueOutputFileName(
-        component: "grpc",
-        fileDescriptor: fileDescriptor,
-        fileNamingOption: options.fileNaming,
-        generatedFiles: &generatedFiles
-      )
-      let grpcGenerator = Generator(fileDescriptor, options: options)
-      var grpcFile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
-      grpcFile.name = grpcFileName
-      grpcFile.content = grpcGenerator.code
-      response.file.append(grpcFile)
+    if let fileDescriptor = descriptorSet.fileDescriptor(named: name) {
+      if (options.generateReflectionData) {
+        var binaryFile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
+        let binaryFileName = uniqueOutputFileName(
+          component: "grpc.reflection",
+          fileDescriptor: fileDescriptor,
+          fileNamingOption: options.fileNaming,
+          generatedFiles: &generatedFiles,
+          extension: "txt"
+        )
+        let serializedFileDescriptorProto = try fileDescriptor.proto.serializedData()
+          .base64EncodedString()
+        binaryFile.name = binaryFileName
+        binaryFile.content = serializedFileDescriptorProto
+        response.file.append(binaryFile)
+      }
+      if (
+        !fileDescriptor.services
+          .isEmpty && (options.generateClient || options.generateServer)
+      ) {
+        var grpcFile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
+        let grpcFileName = uniqueOutputFileName(
+          component: "grpc",
+          fileDescriptor: fileDescriptor,
+          fileNamingOption: options.fileNaming,
+          generatedFiles: &generatedFiles
+        )
+        let grpcGenerator = Generator(fileDescriptor, options: options)
+        grpcFile.name = grpcFileName
+        grpcFile.content = grpcGenerator.code
+        response.file.append(grpcFile)
+      }
     }
   }
 

+ 8 - 0
Sources/protoc-gen-grpc-swift/options.swift

@@ -64,6 +64,7 @@ final class GeneratorOptions {
   private(set) var extraModuleImports: [String] = []
   private(set) var gRPCModuleName = "GRPC"
   private(set) var swiftProtobufModuleName = "SwiftProtobuf"
+  private(set) var generateReflectionData = false
 
   init(parameter: String?) throws {
     for pair in GeneratorOptions.parseParameter(string: parameter) {
@@ -143,6 +144,13 @@ final class GeneratorOptions {
           throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
         }
 
+      case "ReflectionData":
+        if let value = Bool(pair.value) {
+          self.generateReflectionData = value
+        } else {
+          throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
+        }
+
       default:
         throw GenerationError.unknownParameter(name: pair.key)
       }