Skip to content

Commit efe3e30

Browse files
Merge pull request #4 from gojekfarm/task/update-swiftprotobuf-version
Task/update swiftprotobuf version
2 parents b2a073d + d8bb6fc commit efe3e30

File tree

5 files changed

+719
-580
lines changed

5 files changed

+719
-580
lines changed

Sources/protoc-gen-swift/Docs.docc/spm-plugin.md

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,18 @@ There are multiple ways to do this. Some of the easiest are:
2222
1. If you are on macOS, installing it via `brew install protobuf`
2323
2. Download the binary from [Google's github repository](https://github.com/protocolbuffers/protobuf).
2424

25-
### Adding the proto files to your target
26-
27-
Next, you need to add the `.proto` files for which you want to generate your Swift types to your target's
28-
source directory. You should also commit these files to your git repository since the generated types
29-
are now generated on demand.
30-
31-
> Note: imports on your `.proto` files will have to include the relative path from the target source to the `.proto` file you wish to import.
32-
3325
### Adding the plugin to your manifest
3426

35-
After adding the `.proto` files you can now add the plugin to the target inside your `Package.swift` manifest.
3627
First, you need to add a dependency on `swift-protobuf`. Afterwards, you can declare the usage of the plugin
3728
for your target. Here is an example snippet of a `Package.swift` manifest:
3829

39-
> Note: imports on your `.proto` files will have to include the relative path from the target source to the `.proto` file you wish to import.
40-
> Files **must** be contained within the target source directory.
41-
4230
```swift
4331
let package = Package(
4432
name: "YourPackage",
4533
products: [...],
4634
dependencies: [
4735
...
48-
.package(url: "https://github.com/apple/swift-protobuf", from: "1.29.0.1"),
36+
.package(url: "https://github.com/apple/swift-protobuf", from: "2.0.0"),
4937
...
5038
],
5139
targets: [
@@ -63,9 +51,25 @@ let package = Package(
6351

6452
### Configuring the plugin
6553

66-
Lastly, after you have added the `.proto` files and modified your `Package.swift` manifest, you can now
67-
configure the plugin to invoke the `protoc` compiler. This is done by adding a `swift-protobuf-config.json`
68-
to the root of your target's source folder. An example configuration file looks like this:
54+
Configuring the plugin is done by adding a `swift-protobuf-config.json` file anywhere in your target's sources.
55+
Before you start configuring the plugin, you need to add the `.proto` files to your sources. You should also commit these
56+
files to your git repository since the generated types are now generated on demand.
57+
It's also important to note that the proto files in your configuration should be in
58+
the same directory as the config file. Let's see an example to have a better understanding.
59+
60+
Here's an example file structure that looks like this:
61+
62+
```text
63+
Sources
64+
├── main.swift
65+
├── ProtoBuf
66+
├── swift-protobuf-config.json
67+
├── foo.proto
68+
└── Bar
69+
└── Bar.proto
70+
```
71+
72+
So, the configuration file would look something like this:
6973

7074
```json
7175
{
@@ -79,7 +83,7 @@ to the root of your target's source folder. An example configuration file looks
7983
},
8084
{
8185
"protoFiles": [
82-
"Bar.proto"
86+
"Bar/Bar.proto"
8387
],
8488
"visibility": "public",
8589
"fileNaming": "pathToUnderscores"
@@ -88,24 +92,25 @@ to the root of your target's source folder. An example configuration file looks
8892
}
8993

9094
```
95+
As you can see in the above configuration, the paths are relative with respect to the `ProtoBuf` folder and not the root folder.
96+
If you add a file in the `Sources` folder, the plugin would be unable to access it as the path is computed relative to
97+
the `swift-protobuf-config.json` file.
9198

92-
> Note: paths to your `.proto` files will have to include the relative path from the target source to the `.proto` file location.
93-
> Files **must** be contained within the target source directory.
99+
> Note: paths to your `.proto` files will have to include the relative path from the config file directory to the `.proto` file location.
100+
> Files **must** be contained within the same directory as the config file.
94101
95102
In the above configuration, you declared two invocations to the `protoc` compiler. The first invocation
96103
is generating Swift types for the `Foo.proto` file with `internal` visibility. The second invocation
97104
is generating Swift types for the `Bar.proto` file with the `public` visibility. Furthermore, the second
98105
invocation is using the `pathToUnderscores` file naming option. This option can be used to solve
99106
problems where a single target contains two or more proto files with the same name.
100107

101-
> Note: paths to your `.proto` files will have to include the relative path from the target source to the `.proto` file location.
102-
103108
### Defining the path to the protoc binary
104109

105-
The plugin needs to be able to invoke the `protoc` binary to generate the Swift types. There are several ways to achieve this.
110+
The plugin needs to be able to invoke the `protoc` binary to generate the Swift types. There are several ways to achieve this.
106111

107-
First, by default, the package manager looks into the `$PATH` to find binaries named `protoc`.
108-
This works immediately if you use `swift build` to build your package and `protoc` is installed
112+
First, by default, the package manager looks into the `$PATH` to find binaries named `protoc`.
113+
This works immediately if you use `swift build` to build your package and `protoc` is installed
109114
in the `$PATH` (`brew` is adding it to your `$PATH` automatically).
110115
However, this doesn't work if you want to compile from Xcode since Xcode is not passed the `$PATH`.
111116

@@ -135,7 +140,15 @@ env PROTOC_PATH=/opt/homebrew/bin/protoc xcodebuild <Here goes your command>
135140
}
136141
```
137142

138-
> Warning: The configuration file option only solves the problem for leaf packages that are using the Swift package manager
139-
plugin since there you can point the package manager to the right binary. The environment variable
140-
does solve the problem for transitive packages as well; however, it requires your users to set
141-
the variable now. In general we advise against adopting the plugin as a non-leaf package!
143+
### Known Issues
144+
145+
- The configuration file _must not_ be excluded from the list of sources for the
146+
target in the package manifest (that is, it should not be present in the
147+
`exclude` argument for the target). The build system does not have access to
148+
the file if it is excluded, however, `swift build` will result in a warning
149+
that the file should be excluded.
150+
- The plugin should only be used for leaf packages. The configuration file option
151+
only solves the problem for leaf packages that are using the Swift package
152+
manager plugin since there you can point the package manager to the right
153+
binary. The environment variable does solve the problem for transitive
154+
packages as well; however, it requires your users to set the variable now.

Sources/protoc-gen-swift/FileGenerator.swift

Lines changed: 98 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@
1515
///
1616
// -----------------------------------------------------------------------------
1717
import Foundation
18-
import SwiftProtobufPluginLibrary
1918
import SwiftProtobuf
20-
19+
import SwiftProtobufPluginLibrary
2120

2221
class FileGenerator {
2322
private let fileDescriptor: FileDescriptor
@@ -39,104 +38,140 @@ class FileGenerator {
3938
}
4039
}
4140

42-
init(fileDescriptor: FileDescriptor,
43-
generatorOptions: GeneratorOptions) {
41+
init(
42+
fileDescriptor: FileDescriptor,
43+
generatorOptions: GeneratorOptions
44+
) {
4445
self.fileDescriptor = fileDescriptor
4546
self.generatorOptions = generatorOptions
46-
namer = SwiftProtobufNamer(currentFile: fileDescriptor,
47-
protoFileToModuleMappings: generatorOptions.protoToModuleMappings)
47+
namer = SwiftProtobufNamer(
48+
currentFile: fileDescriptor,
49+
protoFileToModuleMappings: generatorOptions.protoToModuleMappings
50+
)
4851
}
4952

5053
/// Generate, if `errorString` gets filled in, then report error instead of using
5154
/// what written into `printer`.
5255
func generateOutputFile(printer p: inout CodePrinter, errorString: inout String?) {
53-
guard fileDescriptor.options.swiftPrefix.isEmpty ||
54-
isValidSwiftIdentifier(fileDescriptor.options.swiftPrefix,
55-
allowQuoted: false) else {
56-
errorString = "\(fileDescriptor.name) has an 'swift_prefix' that isn't a valid Swift identifier (\(fileDescriptor.options.swiftPrefix))."
57-
return
56+
guard
57+
fileDescriptor.options.swiftPrefix.isEmpty
58+
|| isValidSwiftIdentifier(
59+
fileDescriptor.options.swiftPrefix,
60+
allowQuoted: false
61+
)
62+
else {
63+
errorString =
64+
"\(fileDescriptor.name) has an 'swift_prefix' that isn't a valid Swift identifier (\(fileDescriptor.options.swiftPrefix))."
65+
return
5866
}
59-
p.print("""
67+
p.print(
68+
"""
6069
// DO NOT EDIT.
6170
// swift-format-ignore-file
71+
// swiftlint:disable all
6272
//
6373
// Generated by the Swift generator plugin for the protocol buffer compiler.
6474
// Source: \(fileDescriptor.name)
6575
//
6676
// For information on using the generated types, please see the documentation:
6777
// https://github.com/apple/swift-protobuf/
6878
69-
""")
79+
"""
80+
)
7081

7182
// Attempt to bring over the comments at the top of the .proto file as
7283
// they likely contain copyrights/preamble/etc.
7384
//
7485
// The C++ FileDescriptor::GetSourceLocation(), says the location for
7586
// the file is an empty path. That never seems to have comments on it.
7687
// https://github.com/protocolbuffers/protobuf/issues/2249 opened to
77-
// figure out the right way to do this since the syntax entry is
78-
// optional.
79-
var comments = String()
88+
// figure out the right way to do this but going forward best bet seems
89+
// to be to look for the "edition" or the "syntax" decl.
90+
let editionPath = IndexPath(index: Google_Protobuf_FileDescriptorProto.FieldNumbers.edition)
8091
let syntaxPath = IndexPath(index: Google_Protobuf_FileDescriptorProto.FieldNumbers.syntax)
81-
if let syntaxLocation = fileDescriptor.sourceCodeInfoLocation(path: syntaxPath) {
82-
comments = syntaxLocation.asSourceComment(commentPrefix: "///",
83-
leadingDetachedPrefix: "//")
84-
// If the was a leading or tailing comment it won't have a blank
85-
// line, after it, so ensure there is one.
86-
if !comments.isEmpty && !comments.hasSuffix("\n\n") {
87-
comments.append("\n")
88-
}
92+
var commentLocation: Google_Protobuf_SourceCodeInfo.Location? = nil
93+
if self.generatorOptions.experimentalStripNonfunctionalCodegen {
94+
// Comments are inherently non-functional, and may change subtly on
95+
// transformations.
96+
} else if let location = fileDescriptor.sourceCodeInfoLocation(path: editionPath) {
97+
commentLocation = location
98+
} else if let location = fileDescriptor.sourceCodeInfoLocation(path: syntaxPath) {
99+
commentLocation = location
100+
}
101+
if let commentLocation = commentLocation {
102+
let comments = commentLocation.asSourceComment(
103+
commentPrefix: "///",
104+
leadingDetachedPrefix: "//"
105+
)
106+
if !comments.isEmpty {
107+
// If the was a leading or tailing comment it won't have a blank
108+
// line, after it, so ensure there is one.
109+
p.print(comments, newlines: !comments.hasSuffix("\n\n"))
110+
}
89111
}
90112

91-
p.print("\(comments)import Foundation")
113+
let fileDefinesTypes =
114+
!fileDescriptor.enums.isEmpty || !fileDescriptor.messages.isEmpty || !fileDescriptor.extensions.isEmpty
92115

93-
if self.generatorOptions.implementationOnlyImports,
94-
self.generatorOptions.visibility == .public {
95-
errorString = """
96-
Cannot use @_implementationOnly imports when the proto visibility is public.
97-
Either change the visibility to internal, or disable @_implementationOnly imports.
98-
"""
99-
return
116+
var hasImports = false
117+
if fileDescriptor.needsFoundationImport {
118+
p.print("\(generatorOptions.importDirective.snippet) Foundation")
119+
hasImports = true
100120
}
101121

102-
// Import all other imports as @_implementationOnly if the visiblity is
103-
// internal and the option is set, to avoid exposing internal types to users.
104-
let visibilityAnnotation: String = {
105-
if self.generatorOptions.implementationOnlyImports,
106-
self.generatorOptions.visibility == .internal {
107-
return "@_implementationOnly "
108-
} else {
109-
return ""
122+
if fileDescriptor.isBundledProto {
123+
p.print(
124+
"// 'import \(namer.swiftProtobufModuleName)' suppressed, this proto file is meant to be bundled in the runtime."
125+
)
126+
hasImports = true
127+
} else if fileDefinesTypes {
128+
p.print("\(generatorOptions.importDirective.snippet) \(namer.swiftProtobufModuleName)")
129+
hasImports = true
130+
}
131+
132+
let neededImports = fileDescriptor.computeImports(
133+
namer: namer,
134+
directive: generatorOptions.importDirective,
135+
reexportPublicImports: generatorOptions.visibility != .internal
136+
)
137+
if !neededImports.isEmpty {
138+
if hasImports {
139+
p.print()
110140
}
111-
}()
112-
if !fileDescriptor.isBundledProto {
113-
// The well known types ship with the runtime, everything else needs
114-
// to import the runtime.
115-
p.print("\(visibilityAnnotation)import \(namer.swiftProtobufModuleName)")
141+
p.print(neededImports)
142+
hasImports = true
116143
}
117-
if let neededImports = generatorOptions.protoToModuleMappings.neededModules(forFile: fileDescriptor) {
118-
p.print()
119-
for i in neededImports {
120-
p.print("\(visibilityAnnotation)import \(i)")
144+
145+
// If there is nothing to generate, then just record that and be done (usually means
146+
// there just was one or more services).
147+
guard fileDefinesTypes else {
148+
if hasImports {
149+
p.print()
121150
}
151+
p.print("// This file contained no messages, enums, or extensions.")
152+
return
122153
}
123154

124155
let extensionSet =
125-
ExtensionSetGenerator(fileDescriptor: fileDescriptor,
126-
generatorOptions: generatorOptions,
127-
namer: namer)
156+
ExtensionSetGenerator(
157+
fileDescriptor: fileDescriptor,
158+
generatorOptions: generatorOptions,
159+
namer: namer
160+
)
128161

129162
extensionSet.add(extensionFields: fileDescriptor.extensions)
130163

131164
let enums = fileDescriptor.enums.map {
132-
return EnumGenerator(descriptor: $0, generatorOptions: generatorOptions, namer: namer)
165+
EnumGenerator(descriptor: $0, generatorOptions: generatorOptions, namer: namer)
133166
}
134167

135168
let messages = fileDescriptor.messages.map {
136-
return MessageGenerator(descriptor: $0,
137-
generatorOptions: generatorOptions,
138-
namer: namer,
139-
extensionSet: extensionSet)
169+
MessageGenerator(
170+
descriptor: $0,
171+
generatorOptions: generatorOptions,
172+
namer: namer,
173+
extensionSet: extensionSet
174+
)
140175
}
141176

142177
for e in enums {
@@ -147,23 +182,13 @@ class FileGenerator {
147182
m.generateMainStruct(printer: &p, parent: nil, errorString: &errorString)
148183
}
149184

150-
var sendablePrinter = CodePrinter(p)
151-
for m in messages {
152-
m.generateSendable(printer: &sendablePrinter)
153-
}
154-
155-
if !sendablePrinter.isEmpty {
156-
p.print("", "#if swift(>=5.5) && canImport(_Concurrency)")
157-
p.append(sendablePrinter)
158-
p.print("#endif // swift(>=5.5) && canImport(_Concurrency)")
159-
}
160-
161185
if !extensionSet.isEmpty {
162186
let pathParts = splitPath(pathname: fileDescriptor.name)
163187
let filename = pathParts.base + pathParts.suffix
164188
p.print(
165189
"",
166-
"// MARK: - Extension support defined in \(filename).")
190+
"// MARK: - Extension support defined in \(filename)."
191+
)
167192

168193
// Generate the Swift Extensions on the Messages that provide the api
169194
// for using the protobuf extension.
@@ -185,11 +210,13 @@ class FileGenerator {
185210
if needsProtoPackage || !enums.isEmpty || !messages.isEmpty {
186211
p.print(
187212
"",
188-
"// MARK: - Code below here is support for the SwiftProtobuf runtime.")
213+
"// MARK: - Code below here is support for the SwiftProtobuf runtime."
214+
)
189215
if needsProtoPackage {
190216
p.print(
191217
"",
192-
"fileprivate let _protobuf_package = \"\(protoPackage)\"")
218+
"fileprivate let _protobuf_package = \"\(protoPackage)\""
219+
)
193220
}
194221
for e in enums {
195222
e.generateRuntimeSupport(printer: &p)

0 commit comments

Comments
 (0)