2
0
Эх сурвалжийг харах

Allow access level on imports to be disabled (#2032)

Motivation:

The v2 code generator defaults to generating access levels on imports.
SwiftProtobuf also has but hasn't yet been released. This causes
problems as explicit 'internal' imports in gRPC are ambiguous with the
implicit imports from Protobuf.

Modifications:

- Allow an opt-out of explicit imports for v2 code generation

Result:

Can build against current version of Protobuf without
`InternalImportsByDefault`.
George Barnett 1 жил өмнө
parent
commit
82a2e23370

+ 20 - 6
Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift

@@ -21,6 +21,7 @@ struct IDLToStructuredSwiftTranslator: Translator {
   func translate(
     codeGenerationRequest: CodeGenerationRequest,
     accessLevel: SourceGenerator.Configuration.AccessLevel,
+    accessLevelOnImports: Bool,
     client: Bool,
     server: Bool
   ) throws -> StructuredSwiftRepresentation {
@@ -30,11 +31,23 @@ struct IDLToStructuredSwiftTranslator: Translator {
       server: server,
       accessLevel: accessLevel
     )
+
     let topComment = Comment.preFormatted(codeGenerationRequest.leadingTrivia)
-    let imports = try codeGenerationRequest.dependencies.reduce(
-      into: [ImportDescription(accessLevel: AccessModifier(accessLevel), moduleName: "GRPCCore")]
-    ) { partialResult, newDependency in
-      try partialResult.append(translateImport(dependency: newDependency))
+
+    var imports: [ImportDescription] = []
+    imports.append(
+      ImportDescription(
+        accessLevel: accessLevelOnImports ? AccessModifier(accessLevel) : nil,
+        moduleName: "GRPCCore"
+      )
+    )
+
+    for dependency in codeGenerationRequest.dependencies {
+      let importDescription = try self.translateImport(
+        dependency: dependency,
+        accessLevelOnImports: accessLevelOnImports
+      )
+      imports.append(importDescription)
     }
 
     var codeBlocks = [CodeBlock]()
@@ -79,10 +92,11 @@ extension AccessModifier {
 
 extension IDLToStructuredSwiftTranslator {
   private func translateImport(
-    dependency: CodeGenerationRequest.Dependency
+    dependency: CodeGenerationRequest.Dependency,
+    accessLevelOnImports: Bool
   ) throws -> ImportDescription {
     var importDescription = ImportDescription(
-      accessLevel: AccessModifier(dependency.accessLevel),
+      accessLevel: accessLevelOnImports ? AccessModifier(dependency.accessLevel) : nil,
       moduleName: dependency.module
     )
     if let item = dependency.item {

+ 2 - 0
Sources/GRPCCodeGen/Internal/Translator/Translator.swift

@@ -22,6 +22,7 @@ protocol Translator {
   /// - Parameters:
   ///   - codeGenerationRequest: The IDL described RPCs representation.
   ///   - accessLevel: The access level that will restrict the protocols, extensions and methods in the generated code.
+  ///   - accessLevelOnImports: Whether access levels should be included on imports.
   ///   - client: Whether or not client code should be generated from the IDL described RPCs representation.
   ///   - server: Whether or not server code should be generated from the IDL described RPCs representation.
   /// - Returns: A structured Swift representation of the generated code.
@@ -29,6 +30,7 @@ protocol Translator {
   func translate(
     codeGenerationRequest: CodeGenerationRequest,
     accessLevel: SourceGenerator.Configuration.AccessLevel,
+    accessLevelOnImports: Bool,
     client: Bool,
     server: Bool
   ) throws -> StructuredSwiftRepresentation

+ 23 - 5
Sources/GRPCCodeGen/SourceGenerator.swift

@@ -28,6 +28,8 @@ public struct SourceGenerator: Sendable {
   public struct Configuration: Sendable {
     /// The access level the generated code will have.
     public var accessLevel: AccessLevel
+    /// Whether imports have explicit access levels.
+    public var accessLevelOnImports: Bool
     /// The indentation of the generated code as the number of spaces.
     public var indentation: Int
     /// Whether or not client code should be generated.
@@ -35,8 +37,23 @@ public struct SourceGenerator: Sendable {
     /// Whether or not server code should be generated.
     public var server: Bool
 
-    public init(accessLevel: AccessLevel, client: Bool, server: Bool, indentation: Int = 4) {
+    /// Creates a new configuration.
+    ///
+    /// - Parameters:
+    ///   - accessLevel: The access level the generated code will have.
+    ///   - accessLevelOnImports: Whether imports have explicit access levels.
+    ///   - client: Whether or not client code should be generated.
+    ///   - server: Whether or not server code should be generated.
+    ///   - indentation: The indentation of the generated code as the number of spaces.
+    public init(
+      accessLevel: AccessLevel,
+      accessLevelOnImports: Bool,
+      client: Bool,
+      server: Bool,
+      indentation: Int = 4
+    ) {
       self.accessLevel = accessLevel
+      self.accessLevelOnImports = accessLevelOnImports
       self.indentation = indentation
       self.client = client
       self.server = server
@@ -65,16 +82,17 @@ public struct SourceGenerator: Sendable {
   /// The function that transforms a ``CodeGenerationRequest`` object  into a ``SourceFile`` object containing
   /// the generated code, in accordance to the configurations set by the user for the ``SourceGenerator``.
   public func generate(
-    _ serviceRepresentation: CodeGenerationRequest
+    _ request: CodeGenerationRequest
   ) throws -> SourceFile {
     let translator = IDLToStructuredSwiftTranslator()
     let textRenderer = TextBasedRenderer(indentation: self.configuration.indentation)
 
     let structuredSwiftRepresentation = try translator.translate(
-      codeGenerationRequest: serviceRepresentation,
+      codeGenerationRequest: request,
       accessLevel: self.configuration.accessLevel,
-      client: configuration.client,
-      server: configuration.server
+      accessLevelOnImports: self.configuration.accessLevelOnImports,
+      client: self.configuration.client,
+      server: self.configuration.server
     )
     let sourceFile = try textRenderer.render(structured: structuredSwiftRepresentation)
 

+ 1 - 0
Sources/GRPCCore/Documentation.docc/Articles/Generating-stubs.md

@@ -170,6 +170,7 @@ protoc \
 | `Client`                  | `True`, `False`                            | `True`     | Generate client stubs                                    |
 | `FileNaming`              | `FullPath`, `PathToUnderscore`, `DropPath` | `FullPath` | How generated source files should be named. (See below.) |
 | `ProtoPathModuleMappings` |                                            |            | Path to module map `.asciipb` file. (See below.)         |
+| `AccessLevelOnImports`    | `True`, `False`                            | `True`     | Whether imports should have explicit access levels.      |
 
 > The `protoc-gen-grpc-swift` binary is currently shared between gRPC Swift v1.x and v2.x. To
 > generate stubs for v2.x you _must_ specify `_V2=True`.

+ 1 - 0
Sources/protoc-gen-grpc-swift/main.swift

@@ -221,6 +221,7 @@ extension SourceGenerator.Configuration {
     }
     self.init(
       accessLevel: accessLevel,
+      accessLevelOnImports: options.useAccessLevelOnImports,
       client: options.generateClient,
       server: options.generateServer
     )

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

@@ -71,6 +71,7 @@ final class GeneratorOptions {
   #if compiler(>=6.0)
   private(set) var v2 = false
   #endif
+  private(set) var useAccessLevelOnImports = true
 
   init(parameter: String?) throws {
     for pair in GeneratorOptions.parseParameter(string: parameter) {
@@ -166,6 +167,13 @@ final class GeneratorOptions {
         }
       #endif
 
+      case "UseAccessLevelOnImports":
+        if let value = Bool(pair.value) {
+          self.useAccessLevelOnImports = value
+        } else {
+          throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
+        }
+
       default:
         throw GenerationError.unknownParameter(name: pair.key)
       }

+ 9 - 0
Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift

@@ -275,6 +275,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
     let structuredSwift = try translator.translate(
       codeGenerationRequest: codeGenerationRequest,
       accessLevel: accessLevel,
+      accessLevelOnImports: true,
       client: false,
       server: server
     )
@@ -299,6 +300,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         server: true
       )
@@ -339,6 +341,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         server: true
       )
@@ -374,6 +377,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         server: true
       )
@@ -421,6 +425,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .internal,
+        accessLevelOnImports: true,
         client: true,
         server: true
       )
@@ -466,6 +471,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         server: true
       )
@@ -519,6 +525,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         server: true
       )
@@ -572,6 +579,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         server: true
       )
@@ -618,6 +626,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         server: true
       )

+ 32 - 2
Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift

@@ -471,22 +471,52 @@ final class ProtobufCodeGeneratorTests: XCTestCase {
     )
   }
 
+  func testNoAccessLevelOnImports() throws {
+    let proto = Google_Protobuf_FileDescriptorProto(name: "helloworld.proto", package: "")
+    try testCodeGeneration(
+      proto: proto,
+      indentation: 2,
+      visibility: .package,
+      client: true,
+      server: true,
+      accessLevelOnImports: false,
+      expectedCode: """
+        // DO NOT EDIT.
+        // swift-format-ignore-file
+        //
+        // Generated by the gRPC Swift generator plugin for the protocol buffer compiler.
+        // Source: helloworld.proto
+        //
+        // For information on using the generated types, please see the documentation:
+        //   https://github.com/grpc/grpc-swift
+
+        import GRPCCore
+        import GRPCProtobuf
+        import ExtraModule
+
+        """
+    )
+  }
+
   func testCodeGeneration(
     proto: Google_Protobuf_FileDescriptorProto,
     indentation: Int,
     visibility: SourceGenerator.Configuration.AccessLevel,
     client: Bool,
     server: Bool,
+    accessLevelOnImports: Bool = true,
     expectedCode: String,
     file: StaticString = #filePath,
     line: UInt = #line
   ) throws {
-    let configs = SourceGenerator.Configuration(
+    let config = SourceGenerator.Configuration(
       accessLevel: visibility,
+      accessLevelOnImports: accessLevelOnImports,
       client: client,
       server: server,
       indentation: indentation
     )
+
     let descriptorSet = DescriptorSet(
       protos: [
         Google_Protobuf_FileDescriptorProto(name: "same-module.proto", package: "same-package"),
@@ -512,7 +542,7 @@ final class ProtobufCodeGeneratorTests: XCTestCase {
         }
       ]
     }
-    let generator = ProtobufCodeGenerator(configuration: configs)
+    let generator = ProtobufCodeGenerator(configuration: config)
     try XCTAssertEqualWithDiff(
       try generator.generateCode(
         from: fileDescriptor,