浏览代码

Support proto-gen-swift's proto-to-module mapping.

This feature lets us support build systems like Bazel where proto
files are spread out among multiple proto_library target, and each
such target becomes its own Swift module. Downstream modules (and
in our case, generated service code) must know which module has
the proto definitions that they depend on so that it can import
them. This information is conveyed using a text proto formatted
file that the build system generates as it traverses the build
graph.
Tony Allevato 7 年之前
父节点
当前提交
ff2d161e1f
共有 2 个文件被更改,包括 29 次插入0 次删除
  1. 12 0
      Sources/protoc-gen-swiftgrpc/Generator.swift
  2. 17 0
      Sources/protoc-gen-swiftgrpc/options.swift

+ 12 - 0
Sources/protoc-gen-swiftgrpc/Generator.swift

@@ -91,6 +91,18 @@ class Generator {
     for moduleName in moduleNames {
     for moduleName in moduleNames {
       println("import \(moduleName)")
       println("import \(moduleName)")
     }
     }
+    // Build systems like Bazel will generate the Swift service definitions in a different module
+    // than the rest of the protos defined in the same file (because they are generated by separate
+    // build rules that invoke the separate generator plugins). So, we need to specify module
+    // mappings to import the service protos as well as and any other proto modules that the file
+    // imports.
+    let moduleMappings = options.protoToModuleMappings
+    if let serviceProtoModuleName = moduleMappings.moduleName(forFile: file) {
+      println("import \(serviceProtoModuleName)")
+    }
+    for importedProtoModuleName in moduleMappings.neededModules(forFile: file) ?? [] {
+      println("import \(importedProtoModuleName)")
+    }
     println()
     println()
     
     
     if options.generateClient {
     if options.generateClient {

+ 17 - 0
Sources/protoc-gen-swiftgrpc/options.swift

@@ -14,12 +14,15 @@
  * limitations under the License.
  * limitations under the License.
  */
  */
 import Foundation
 import Foundation
+import SwiftProtobufPluginLibrary
 
 
 enum GenerationError: Error {
 enum GenerationError: Error {
   /// Raised when parsing the parameter string and found an unknown key
   /// Raised when parsing the parameter string and found an unknown key
   case unknownParameter(name: String)
   case unknownParameter(name: String)
   /// Raised when a parameter was giving an invalid value
   /// Raised when a parameter was giving an invalid value
   case invalidParameterValue(name: String, value: String)
   case invalidParameterValue(name: String, value: String)
+  /// Raised to wrap another error but provide a context message.
+  case wrappedError(message: String, error: Error)
 
 
   var localizedDescription: String {
   var localizedDescription: String {
     switch self {
     switch self {
@@ -27,6 +30,8 @@ enum GenerationError: Error {
       return "Unknown generation parameter '\(name)'"
       return "Unknown generation parameter '\(name)'"
     case .invalidParameterValue(let name, let value):
     case .invalidParameterValue(let name, let value):
       return "Unknown value for generation parameter '\(name)': '\(value)'"
       return "Unknown value for generation parameter '\(name)': '\(value)'"
+    case .wrappedError(let message, let error):
+      return "\(message): \(error.localizedDescription)"
     }
     }
   }
   }
 }
 }
@@ -53,6 +58,7 @@ final class GeneratorOptions {
   private(set) var generateSynchronous = true
   private(set) var generateSynchronous = true
   private(set) var generateTestStubs = false
   private(set) var generateTestStubs = false
   private(set) var generateNIOImplementation = false
   private(set) var generateNIOImplementation = false
+  private(set) var protoToModuleMappings = ProtoFileToModuleMappings()
 
 
   init(parameter: String?) throws {
   init(parameter: String?) throws {
     for pair in GeneratorOptions.parseParameter(string: parameter) {
     for pair in GeneratorOptions.parseParameter(string: parameter) {
@@ -106,6 +112,17 @@ final class GeneratorOptions {
           throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
           throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
         }
         }
 
 
+      case "ProtoPathModuleMappings":
+        if !pair.value.isEmpty {
+          do {
+            protoToModuleMappings = try ProtoFileToModuleMappings(path: pair.value)
+          } catch let e {
+            throw GenerationError.wrappedError(
+              message: "Parameter 'ProtoPathModuleMappings=\(pair.value)'",
+              error: e)
+          }
+        }
+
       default:
       default:
         throw GenerationError.unknownParameter(name: pair.key)
         throw GenerationError.unknownParameter(name: pair.key)
       }
       }