Browse Source

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 year ago
parent
commit
82a2e23370

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

@@ -21,6 +21,7 @@ struct IDLToStructuredSwiftTranslator: Translator {
   func translate(
   func translate(
     codeGenerationRequest: CodeGenerationRequest,
     codeGenerationRequest: CodeGenerationRequest,
     accessLevel: SourceGenerator.Configuration.AccessLevel,
     accessLevel: SourceGenerator.Configuration.AccessLevel,
+    accessLevelOnImports: Bool,
     client: Bool,
     client: Bool,
     server: Bool
     server: Bool
   ) throws -> StructuredSwiftRepresentation {
   ) throws -> StructuredSwiftRepresentation {
@@ -30,11 +31,23 @@ struct IDLToStructuredSwiftTranslator: Translator {
       server: server,
       server: server,
       accessLevel: accessLevel
       accessLevel: accessLevel
     )
     )
+
     let topComment = Comment.preFormatted(codeGenerationRequest.leadingTrivia)
     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]()
     var codeBlocks = [CodeBlock]()
@@ -79,10 +92,11 @@ extension AccessModifier {
 
 
 extension IDLToStructuredSwiftTranslator {
 extension IDLToStructuredSwiftTranslator {
   private func translateImport(
   private func translateImport(
-    dependency: CodeGenerationRequest.Dependency
+    dependency: CodeGenerationRequest.Dependency,
+    accessLevelOnImports: Bool
   ) throws -> ImportDescription {
   ) throws -> ImportDescription {
     var importDescription = ImportDescription(
     var importDescription = ImportDescription(
-      accessLevel: AccessModifier(dependency.accessLevel),
+      accessLevel: accessLevelOnImports ? AccessModifier(dependency.accessLevel) : nil,
       moduleName: dependency.module
       moduleName: dependency.module
     )
     )
     if let item = dependency.item {
     if let item = dependency.item {

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

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

+ 23 - 5
Sources/GRPCCodeGen/SourceGenerator.swift

@@ -28,6 +28,8 @@ public struct SourceGenerator: Sendable {
   public struct Configuration: Sendable {
   public struct Configuration: Sendable {
     /// The access level the generated code will have.
     /// The access level the generated code will have.
     public var accessLevel: AccessLevel
     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.
     /// The indentation of the generated code as the number of spaces.
     public var indentation: Int
     public var indentation: Int
     /// Whether or not client code should be generated.
     /// Whether or not client code should be generated.
@@ -35,8 +37,23 @@ public struct SourceGenerator: Sendable {
     /// Whether or not server code should be generated.
     /// Whether or not server code should be generated.
     public var server: Bool
     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.accessLevel = accessLevel
+      self.accessLevelOnImports = accessLevelOnImports
       self.indentation = indentation
       self.indentation = indentation
       self.client = client
       self.client = client
       self.server = server
       self.server = server
@@ -65,16 +82,17 @@ public struct SourceGenerator: Sendable {
   /// The function that transforms a ``CodeGenerationRequest`` object  into a ``SourceFile`` object containing
   /// 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``.
   /// the generated code, in accordance to the configurations set by the user for the ``SourceGenerator``.
   public func generate(
   public func generate(
-    _ serviceRepresentation: CodeGenerationRequest
+    _ request: CodeGenerationRequest
   ) throws -> SourceFile {
   ) throws -> SourceFile {
     let translator = IDLToStructuredSwiftTranslator()
     let translator = IDLToStructuredSwiftTranslator()
     let textRenderer = TextBasedRenderer(indentation: self.configuration.indentation)
     let textRenderer = TextBasedRenderer(indentation: self.configuration.indentation)
 
 
     let structuredSwiftRepresentation = try translator.translate(
     let structuredSwiftRepresentation = try translator.translate(
-      codeGenerationRequest: serviceRepresentation,
+      codeGenerationRequest: request,
       accessLevel: self.configuration.accessLevel,
       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)
     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                                    |
 | `Client`                  | `True`, `False`                            | `True`     | Generate client stubs                                    |
 | `FileNaming`              | `FullPath`, `PathToUnderscore`, `DropPath` | `FullPath` | How generated source files should be named. (See below.) |
 | `FileNaming`              | `FullPath`, `PathToUnderscore`, `DropPath` | `FullPath` | How generated source files should be named. (See below.) |
 | `ProtoPathModuleMappings` |                                            |            | Path to module map `.asciipb` file. (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
 > 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`.
 > 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(
     self.init(
       accessLevel: accessLevel,
       accessLevel: accessLevel,
+      accessLevelOnImports: options.useAccessLevelOnImports,
       client: options.generateClient,
       client: options.generateClient,
       server: options.generateServer
       server: options.generateServer
     )
     )

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

@@ -71,6 +71,7 @@ final class GeneratorOptions {
   #if compiler(>=6.0)
   #if compiler(>=6.0)
   private(set) var v2 = false
   private(set) var v2 = false
   #endif
   #endif
+  private(set) var useAccessLevelOnImports = true
 
 
   init(parameter: String?) throws {
   init(parameter: String?) throws {
     for pair in GeneratorOptions.parseParameter(string: parameter) {
     for pair in GeneratorOptions.parseParameter(string: parameter) {
@@ -166,6 +167,13 @@ final class GeneratorOptions {
         }
         }
       #endif
       #endif
 
 
+      case "UseAccessLevelOnImports":
+        if let value = Bool(pair.value) {
+          self.useAccessLevelOnImports = value
+        } else {
+          throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
+        }
+
       default:
       default:
         throw GenerationError.unknownParameter(name: pair.key)
         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(
     let structuredSwift = try translator.translate(
       codeGenerationRequest: codeGenerationRequest,
       codeGenerationRequest: codeGenerationRequest,
       accessLevel: accessLevel,
       accessLevel: accessLevel,
+      accessLevelOnImports: true,
       client: false,
       client: false,
       server: server
       server: server
     )
     )
@@ -299,6 +300,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         client: true,
         server: true
         server: true
       )
       )
@@ -339,6 +341,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         client: true,
         server: true
         server: true
       )
       )
@@ -374,6 +377,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         client: true,
         server: true
         server: true
       )
       )
@@ -421,6 +425,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .internal,
         accessLevel: .internal,
+        accessLevelOnImports: true,
         client: true,
         client: true,
         server: true
         server: true
       )
       )
@@ -466,6 +471,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         client: true,
         server: true
         server: true
       )
       )
@@ -519,6 +525,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         client: true,
         server: true
         server: true
       )
       )
@@ -572,6 +579,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         client: true,
         server: true
         server: true
       )
       )
@@ -618,6 +626,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
       try translator.translate(
       try translator.translate(
         codeGenerationRequest: codeGenerationRequest,
         codeGenerationRequest: codeGenerationRequest,
         accessLevel: .public,
         accessLevel: .public,
+        accessLevelOnImports: true,
         client: true,
         client: true,
         server: 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(
   func testCodeGeneration(
     proto: Google_Protobuf_FileDescriptorProto,
     proto: Google_Protobuf_FileDescriptorProto,
     indentation: Int,
     indentation: Int,
     visibility: SourceGenerator.Configuration.AccessLevel,
     visibility: SourceGenerator.Configuration.AccessLevel,
     client: Bool,
     client: Bool,
     server: Bool,
     server: Bool,
+    accessLevelOnImports: Bool = true,
     expectedCode: String,
     expectedCode: String,
     file: StaticString = #filePath,
     file: StaticString = #filePath,
     line: UInt = #line
     line: UInt = #line
   ) throws {
   ) throws {
-    let configs = SourceGenerator.Configuration(
+    let config = SourceGenerator.Configuration(
       accessLevel: visibility,
       accessLevel: visibility,
+      accessLevelOnImports: accessLevelOnImports,
       client: client,
       client: client,
       server: server,
       server: server,
       indentation: indentation
       indentation: indentation
     )
     )
+
     let descriptorSet = DescriptorSet(
     let descriptorSet = DescriptorSet(
       protos: [
       protos: [
         Google_Protobuf_FileDescriptorProto(name: "same-module.proto", package: "same-package"),
         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 XCTAssertEqualWithDiff(
       try generator.generateCode(
       try generator.generateCode(
         from: fileDescriptor,
         from: fileDescriptor,