Selaa lähdekoodia

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 vuotta sitten
vanhempi
commit
ff2d161e1f

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

@@ -91,6 +91,18 @@ class Generator {
     for moduleName in moduleNames {
       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()
     
     if options.generateClient {

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

@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 import Foundation
+import SwiftProtobufPluginLibrary
 
 enum GenerationError: Error {
   /// Raised when parsing the parameter string and found an unknown key
   case unknownParameter(name: String)
   /// Raised when a parameter was giving an invalid value
   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 {
     switch self {
@@ -27,6 +30,8 @@ enum GenerationError: Error {
       return "Unknown generation parameter '\(name)'"
     case .invalidParameterValue(let name, let 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 generateTestStubs = false
   private(set) var generateNIOImplementation = false
+  private(set) var protoToModuleMappings = ProtoFileToModuleMappings()
 
   init(parameter: String?) throws {
     for pair in GeneratorOptions.parseParameter(string: parameter) {
@@ -106,6 +112,17 @@ final class GeneratorOptions {
           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:
         throw GenerationError.unknownParameter(name: pair.key)
       }