Skip to content

Commit 95f9b5a

Browse files
committed
Wire up availability overrides
Motivation: grpc-swift 2.2.0 generates code with `@available` annotations. Sometimes it's necessary to raise these. Modifications: - Add an `Availability` option to the code generator which, if set at least once overrides the default availability. - Update docs Result: Availability can be configured using `protoc-gen-grpc-swift`
1 parent fda21d4 commit 95f9b5a

File tree

5 files changed

+93
-25
lines changed

5 files changed

+93
-25
lines changed

Sources/GRPCProtobuf/Documentation.docc/Articles/Generating-stubs.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -133,26 +133,35 @@ protoc \
133133

134134
#### Generator options
135135

136-
| Name | Possible Values | Default | Description |
137-
|---------------------------|---------------------------------------------|------------|----------------------------------------------------------|
138-
| `Visibility` | `Public`, `Package`, `Internal` | `Internal` | Access level for generated stubs |
139-
| `Server` | `True`, `False` | `True` | Generate server stubs |
140-
| `Client` | `True`, `False` | `True` | Generate client stubs |
141-
| `FileNaming` | `FullPath`, `PathToUnderscores`, `DropPath` | `FullPath` | How generated source files should be named. (See below.) |
142-
| `ProtoPathModuleMappings` | | | Path to module map `.asciipb` file. (See below.) |
143-
| `UseAccessLevelOnImports` | `True`, `False` | `False` | Whether imports should have explicit access levels. |
144-
145-
The `FileNaming` option has three possible values, for an input of `foo/bar/baz.proto` the following
136+
| Name | Possible Values | Default | Description |
137+
|---------------------------|---------------------------------------------|-----------------|----------------------------------------------------------|
138+
| `Visibility` | `Public`, `Package`, `Internal` | `Internal` | Access level for generated stubs |
139+
| `Server` | `True`, `False` | `True` | Generate server stubs |
140+
| `Client` | `True`, `False` | `True` | Generate client stubs |
141+
| `FileNaming` | `FullPath`, `PathToUnderscores`, `DropPath` | `FullPath` | How generated source files should be named. † |
142+
| `ProtoPathModuleMappings` | | | Path to module map `.asciipb` file. ‡ |
143+
| `UseAccessLevelOnImports` | `True`, `False` | `False` | Whether imports should have explicit access levels. |
144+
| `GRPCModuleName` | | `GRPCCore` | The name of the `GRPCCore` module. |
145+
| `GRPCProtobufModuleName` | | `GRPCProtobuf` | The name of the `GRPCProtobuf` module. |
146+
| `SwiftProtobufModuleName` | | `SwiftProtobuf` | The name of the `SwiftProtobuf` module. |
147+
| `Availability` | String, in the form `OS Version` | | Platform availability to use in generated code. § |
148+
149+
† The `FileNaming` option has three possible values, for an input of `foo/bar/baz.proto` the following
146150
output file will be generated:
147151
- `FullPath`: `foo/bar/baz.grpc.swift`.
148152
- `PathToUnderscores`: `foo_bar_baz.grpc.swift`
149153
- `DropPath`: `baz.grpc.swift`
150154

151-
The code generator assumes all inputs are generated into the same module, `ProtoPathModuleMappings`
155+
The code generator assumes all inputs are generated into the same module, `ProtoPathModuleMappings`
152156
allows you to specify a mapping from `.proto` files to the Swift module they are generated in. This
153157
allows the code generator to add appropriate imports to your generated stubs. This is described in
154158
more detail in the [SwiftProtobuf documentation](https://github.com/apple/swift-protobuf/blob/main/Documentation/PLUGIN.md).
155159

160+
§ If unspecified the following availability is used: macOS 15, iOS 18, tvOS 18,
161+
watchOS 11, visionOS 2. The `Availability` option may be specified multiple
162+
times, where each value is a space delimited pair of platform and version, e.g.
163+
`Availability=macOS 15.0`.
164+
156165
#### Building the protoc plugin
157166

158167
> The version of `protoc-gen-grpc-swift` you use mustn't be newer than the version of

Sources/GRPCProtobufCodeGen/ProtobufCodeGenerator.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ package struct ProtobufCodeGenerator {
2929
package func generateCode(
3030
fileDescriptor: FileDescriptor,
3131
protoFileModuleMappings: ProtoFileToModuleMappings,
32-
extraModuleImports: [String]
32+
extraModuleImports: [String],
33+
availabilityOverrides: [(os: String, version: String)] = []
3334
) throws -> String {
3435
let parser = ProtobufCodeGenParser(
3536
protoFileModuleMappings: protoFileModuleMappings,
@@ -46,6 +47,17 @@ package struct ProtobufCodeGenerator {
4647
indentation: self.config.indentation
4748
)
4849
codeGeneratorConfig.grpcCoreModuleName = self.config.moduleNames.grpcCore
50+
51+
if availabilityOverrides.isEmpty {
52+
codeGeneratorConfig.availability = .default
53+
} else {
54+
codeGeneratorConfig.availability = .custom(
55+
availabilityOverrides.map { (os, version) in
56+
.init(os: os, version: version)
57+
}
58+
)
59+
}
60+
4961
let codeGenerator = GRPCCodeGen.CodeGenerator(config: codeGeneratorConfig)
5062

5163
let codeGenerationRequest = try parser.parse(descriptor: fileDescriptor)

Sources/protoc-gen-grpc-swift/GenerateGRPC.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ final class GenerateGRPC: SwiftProtobufPluginLibrary.CodeGenerator {
7373
let contents = try fileGenerator.generateCode(
7474
fileDescriptor: descriptor,
7575
protoFileModuleMappings: options.protoToModuleMappings,
76-
extraModuleImports: options.extraModuleImports
76+
extraModuleImports: options.extraModuleImports,
77+
availabilityOverrides: options.availabilityOverrides
7778
)
7879

7980
try outputs.add(fileName: fileName, contents: contents)

Sources/protoc-gen-grpc-swift/Options.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ struct GeneratorOptions {
5252
private(set) var protoToModuleMappings = ProtoFileToModuleMappings()
5353
private(set) var fileNaming = FileNaming.fullPath
5454
private(set) var extraModuleImports: [String] = []
55+
private(set) var availabilityOverrides: [(os: String, version: String)] = []
5556

5657
private(set) var config: ProtobufCodeGenerator.Config = .defaults
5758

@@ -130,6 +131,18 @@ struct GeneratorOptions {
130131
throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
131132
}
132133

134+
case "Availability":
135+
if !pair.value.isEmpty {
136+
let parts = pair.value.split(separator: " ", maxSplits: 1)
137+
if parts.count == 2 {
138+
self.availabilityOverrides.append((os: String(parts[0]), version: String(parts[1])))
139+
} else {
140+
throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
141+
}
142+
} else {
143+
throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
144+
}
145+
133146
case "ReflectionData":
134147
throw GenerationError.unsupportedParameter(
135148
name: pair.key,

Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,38 @@ struct ProtobufCodeGeneratorTests {
2525
static let descriptorSetName = "test-service"
2626
static let fileDescriptorName = "test-service"
2727

28-
@Test("Generate", arguments: [CodeGenerator.Config.AccessLevel.internal])
29-
func generate(accessLevel: GRPCCodeGen.CodeGenerator.Config.AccessLevel) throws {
28+
enum Availability {
29+
case `default`
30+
case fooOS
31+
32+
var override: [(String, String)] {
33+
switch self {
34+
case .default:
35+
return []
36+
case .fooOS:
37+
return [("fooOS", "42.0")]
38+
}
39+
}
40+
41+
var expected: String {
42+
switch self {
43+
case .default:
44+
return "macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0"
45+
case .fooOS:
46+
return "fooOS 42.0"
47+
}
48+
}
49+
}
50+
51+
@Test(
52+
"Generate",
53+
arguments: [CodeGenerator.Config.AccessLevel.internal],
54+
[Availability.default, Availability.fooOS]
55+
)
56+
func generate(
57+
accessLevel: GRPCCodeGen.CodeGenerator.Config.AccessLevel,
58+
availability: Availability
59+
) throws {
3060
var config = ProtobufCodeGenerator.Config.defaults
3161
config.accessLevel = accessLevel
3262
config.indentation = 2
@@ -44,10 +74,13 @@ struct ProtobufCodeGeneratorTests {
4474
fatalError()
4575
}
4676

77+
let expectedAvailability = availability.expected
78+
4779
let generated = try generator.generateCode(
4880
fileDescriptor: Self.fileDescriptor,
4981
protoFileModuleMappings: ProtoFileToModuleMappings(),
50-
extraModuleImports: []
82+
extraModuleImports: [],
83+
availabilityOverrides: availability.override
5184
)
5285

5386
let expected = """
@@ -70,7 +103,7 @@ struct ProtobufCodeGeneratorTests {
70103
// MARK: - test.TestService
71104
72105
/// Namespace containing generated types for the "test.TestService" service.
73-
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
106+
@available(\(expectedAvailability), *)
74107
\(access) enum Test_TestService {
75108
/// Service descriptor for the "test.TestService" service.
76109
\(access) static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "test.TestService")
@@ -134,15 +167,15 @@ struct ProtobufCodeGeneratorTests {
134167
}
135168
}
136169
137-
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
170+
@available(\(expectedAvailability), *)
138171
extension GRPCCore.ServiceDescriptor {
139172
/// Service descriptor for the "test.TestService" service.
140173
\(access) static let test_TestService = GRPCCore.ServiceDescriptor(fullyQualifiedService: "test.TestService")
141174
}
142175
143176
// MARK: test.TestService (server)
144177
145-
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
178+
@available(\(expectedAvailability), *)
146179
extension Test_TestService {
147180
/// Streaming variant of the service protocol for the "test.TestService" service.
148181
///
@@ -404,7 +437,7 @@ struct ProtobufCodeGeneratorTests {
404437
}
405438
406439
// Default implementation of 'registerMethods(with:)'.
407-
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
440+
@available(\(expectedAvailability), *)
408441
extension Test_TestService.StreamingServiceProtocol {
409442
\(access) func registerMethods<Transport>(with router: inout GRPCCore.RPCRouter<Transport>) where Transport: GRPCCore.ServerTransport {
410443
router.registerHandler(
@@ -455,7 +488,7 @@ struct ProtobufCodeGeneratorTests {
455488
}
456489
457490
// Default implementation of streaming methods from 'StreamingServiceProtocol'.
458-
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
491+
@available(\(expectedAvailability), *)
459492
extension Test_TestService.ServiceProtocol {
460493
\(access) func unary(
461494
request: GRPCCore.StreamingServerRequest<Test_TestInput>,
@@ -492,7 +525,7 @@ struct ProtobufCodeGeneratorTests {
492525
}
493526
494527
// Default implementation of methods from 'ServiceProtocol'.
495-
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
528+
@available(\(expectedAvailability), *)
496529
extension Test_TestService.SimpleServiceProtocol {
497530
\(access) func unary(
498531
request: GRPCCore.ServerRequest<Test_TestInput>,
@@ -557,7 +590,7 @@ struct ProtobufCodeGeneratorTests {
557590
558591
// MARK: test.TestService (client)
559592
560-
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
593+
@available(\(expectedAvailability), *)
561594
extension Test_TestService {
562595
/// Generated client protocol for the "test.TestService" service.
563596
///
@@ -816,7 +849,7 @@ struct ProtobufCodeGeneratorTests {
816849
}
817850
818851
// Helpers providing default arguments to 'ClientProtocol' methods.
819-
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
852+
@available(\(expectedAvailability), *)
820853
extension Test_TestService.ClientProtocol {
821854
/// Call the "Unary" method.
822855
///
@@ -932,7 +965,7 @@ struct ProtobufCodeGeneratorTests {
932965
}
933966
934967
// Helpers providing sugared APIs for 'ClientProtocol' methods.
935-
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
968+
@available(\(expectedAvailability), *)
936969
extension Test_TestService.ClientProtocol {
937970
/// Call the "Unary" method.
938971
///

0 commit comments

Comments
 (0)