main.swift 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. * Copyright 2017, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. import GRPCCodeGen
  18. import GRPCProtobufCodeGen
  19. import SwiftProtobuf
  20. import SwiftProtobufPluginLibrary
  21. func Log(_ message: String) {
  22. FileHandle.standardError.write((message + "\n").data(using: .utf8)!)
  23. }
  24. // from apple/swift-protobuf/Sources/protoc-gen-swift/StringUtils.swift
  25. func splitPath(pathname: String) -> (dir: String, base: String, suffix: String) {
  26. var dir = ""
  27. var base = ""
  28. var suffix = ""
  29. #if swift(>=3.2)
  30. let pathnameChars = pathname
  31. #else
  32. let pathnameChars = pathname.characters
  33. #endif
  34. for c in pathnameChars {
  35. if c == "/" {
  36. dir += base + suffix + String(c)
  37. base = ""
  38. suffix = ""
  39. } else if c == "." {
  40. base += suffix
  41. suffix = String(c)
  42. } else {
  43. suffix += String(c)
  44. }
  45. }
  46. #if swift(>=3.2)
  47. let validSuffix = suffix.isEmpty || suffix.first == "."
  48. #else
  49. let validSuffix = suffix.isEmpty || suffix.characters.first == "."
  50. #endif
  51. if !validSuffix {
  52. base += suffix
  53. suffix = ""
  54. }
  55. return (dir: dir, base: base, suffix: suffix)
  56. }
  57. enum FileNaming: String {
  58. case FullPath
  59. case PathToUnderscores
  60. case DropPath
  61. }
  62. func outputFileName(
  63. component: String,
  64. fileDescriptor: FileDescriptor,
  65. fileNamingOption: FileNaming,
  66. extension: String
  67. ) -> String {
  68. let ext = "." + component + "." + `extension`
  69. let pathParts = splitPath(pathname: fileDescriptor.name)
  70. switch fileNamingOption {
  71. case .FullPath:
  72. return pathParts.dir + pathParts.base + ext
  73. case .PathToUnderscores:
  74. let dirWithUnderscores =
  75. pathParts.dir.replacingOccurrences(of: "/", with: "_")
  76. return dirWithUnderscores + pathParts.base + ext
  77. case .DropPath:
  78. return pathParts.base + ext
  79. }
  80. }
  81. func uniqueOutputFileName(
  82. component: String,
  83. fileDescriptor: FileDescriptor,
  84. fileNamingOption: FileNaming,
  85. generatedFiles: inout [String: Int],
  86. extension: String = "swift"
  87. ) -> String {
  88. let defaultName = outputFileName(
  89. component: component,
  90. fileDescriptor: fileDescriptor,
  91. fileNamingOption: fileNamingOption,
  92. extension: `extension`
  93. )
  94. if let count = generatedFiles[defaultName] {
  95. generatedFiles[defaultName] = count + 1
  96. return outputFileName(
  97. component: "\(count)." + component,
  98. fileDescriptor: fileDescriptor,
  99. fileNamingOption: fileNamingOption,
  100. extension: `extension`
  101. )
  102. } else {
  103. generatedFiles[defaultName] = 1
  104. return defaultName
  105. }
  106. }
  107. func printVersion(args: [String]) {
  108. // Stip off the file path
  109. let program = args.first?.split(separator: "/").last ?? "protoc-gen-grpc-swift"
  110. print("\(program) \(Version.versionString)")
  111. }
  112. func main(args: [String]) throws {
  113. if args.dropFirst().contains("--version") {
  114. printVersion(args: args)
  115. return
  116. }
  117. // initialize responses
  118. var response = Google_Protobuf_Compiler_CodeGeneratorResponse(
  119. files: [],
  120. supportedFeatures: [.proto3Optional]
  121. )
  122. // read plugin input
  123. let rawRequest = FileHandle.standardInput.readDataToEndOfFile()
  124. let request = try Google_Protobuf_Compiler_CodeGeneratorRequest(serializedData: rawRequest)
  125. let options = try GeneratorOptions(parameter: request.parameter)
  126. // Build the SwiftProtobufPluginLibrary model of the plugin input
  127. let descriptorSet = DescriptorSet(protos: request.protoFile)
  128. // A count of generated files by desired name (actual name may differ to avoid collisions).
  129. var generatedFiles: [String: Int] = [:]
  130. // Only generate output for services.
  131. for name in request.fileToGenerate {
  132. if let fileDescriptor = descriptorSet.fileDescriptor(named: name) {
  133. if options.generateReflectionData {
  134. var binaryFile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  135. let binaryFileName = uniqueOutputFileName(
  136. component: "grpc",
  137. fileDescriptor: fileDescriptor,
  138. fileNamingOption: options.fileNaming,
  139. generatedFiles: &generatedFiles,
  140. extension: "reflection"
  141. )
  142. let serializedFileDescriptorProto = try fileDescriptor.proto.serializedData()
  143. .base64EncodedString()
  144. binaryFile.name = binaryFileName
  145. binaryFile.content = serializedFileDescriptorProto
  146. response.file.append(binaryFile)
  147. }
  148. if !fileDescriptor.services.isEmpty
  149. && (options.generateClient || options.generateServer || options.generateTestClient)
  150. {
  151. var grpcFile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  152. let grpcFileName = uniqueOutputFileName(
  153. component: "grpc",
  154. fileDescriptor: fileDescriptor,
  155. fileNamingOption: options.fileNaming,
  156. generatedFiles: &generatedFiles
  157. )
  158. if options.v2 {
  159. let grpcGenerator = ProtobufCodeGenerator(
  160. configuration: SourceGenerator.Configuration(options: options)
  161. )
  162. grpcFile.content = try grpcGenerator.generateCode(
  163. from: fileDescriptor,
  164. protoFileModuleMappings: options.protoToModuleMappings,
  165. extraModuleImports: options.extraModuleImports
  166. )
  167. } else {
  168. let grpcGenerator = Generator(fileDescriptor, options: options)
  169. grpcFile.content = grpcGenerator.code
  170. }
  171. grpcFile.name = grpcFileName
  172. response.file.append(grpcFile)
  173. }
  174. }
  175. }
  176. // return everything to the caller
  177. let serializedResponse = try response.serializedData()
  178. FileHandle.standardOutput.write(serializedResponse)
  179. }
  180. do {
  181. try main(args: CommandLine.arguments)
  182. } catch {
  183. Log("ERROR: \(error)")
  184. }
  185. extension SourceGenerator.Configuration {
  186. init(options: GeneratorOptions) {
  187. let accessLevel: SourceGenerator.Configuration.AccessLevel
  188. switch options.visibility {
  189. case .internal:
  190. accessLevel = .internal
  191. case .package:
  192. accessLevel = .package
  193. case .public:
  194. accessLevel = .public
  195. }
  196. self.init(
  197. accessLevel: accessLevel,
  198. client: options.generateClient,
  199. server: options.generateServer
  200. )
  201. }
  202. }