main.swift 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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 SwiftProtobuf
  18. import SwiftProtobufPluginLibrary
  19. #if compiler(>=6.0)
  20. import GRPCCodeGen
  21. import GRPCProtobufCodeGen
  22. #endif
  23. func Log(_ message: String) {
  24. FileHandle.standardError.write((message + "\n").data(using: .utf8)!)
  25. }
  26. // from apple/swift-protobuf/Sources/protoc-gen-swift/StringUtils.swift
  27. func splitPath(pathname: String) -> (dir: String, base: String, suffix: String) {
  28. var dir = ""
  29. var base = ""
  30. var suffix = ""
  31. #if swift(>=3.2)
  32. let pathnameChars = pathname
  33. #else
  34. let pathnameChars = pathname.characters
  35. #endif
  36. for c in pathnameChars {
  37. if c == "/" {
  38. dir += base + suffix + String(c)
  39. base = ""
  40. suffix = ""
  41. } else if c == "." {
  42. base += suffix
  43. suffix = String(c)
  44. } else {
  45. suffix += String(c)
  46. }
  47. }
  48. #if swift(>=3.2)
  49. let validSuffix = suffix.isEmpty || suffix.first == "."
  50. #else
  51. let validSuffix = suffix.isEmpty || suffix.characters.first == "."
  52. #endif
  53. if !validSuffix {
  54. base += suffix
  55. suffix = ""
  56. }
  57. return (dir: dir, base: base, suffix: suffix)
  58. }
  59. enum FileNaming: String {
  60. case FullPath
  61. case PathToUnderscores
  62. case DropPath
  63. }
  64. func outputFileName(
  65. component: String,
  66. fileDescriptor: FileDescriptor,
  67. fileNamingOption: FileNaming,
  68. extension: String
  69. ) -> String {
  70. let ext = "." + component + "." + `extension`
  71. let pathParts = splitPath(pathname: fileDescriptor.name)
  72. switch fileNamingOption {
  73. case .FullPath:
  74. return pathParts.dir + pathParts.base + ext
  75. case .PathToUnderscores:
  76. let dirWithUnderscores =
  77. pathParts.dir.replacingOccurrences(of: "/", with: "_")
  78. return dirWithUnderscores + pathParts.base + ext
  79. case .DropPath:
  80. return pathParts.base + ext
  81. }
  82. }
  83. func uniqueOutputFileName(
  84. component: String,
  85. fileDescriptor: FileDescriptor,
  86. fileNamingOption: FileNaming,
  87. generatedFiles: inout [String: Int],
  88. extension: String = "swift"
  89. ) -> String {
  90. let defaultName = outputFileName(
  91. component: component,
  92. fileDescriptor: fileDescriptor,
  93. fileNamingOption: fileNamingOption,
  94. extension: `extension`
  95. )
  96. if let count = generatedFiles[defaultName] {
  97. generatedFiles[defaultName] = count + 1
  98. return outputFileName(
  99. component: "\(count)." + component,
  100. fileDescriptor: fileDescriptor,
  101. fileNamingOption: fileNamingOption,
  102. extension: `extension`
  103. )
  104. } else {
  105. generatedFiles[defaultName] = 1
  106. return defaultName
  107. }
  108. }
  109. func printVersion(args: [String]) {
  110. // Stip off the file path
  111. let program = args.first?.split(separator: "/").last ?? "protoc-gen-grpc-swift"
  112. print("\(program) \(Version.versionString)")
  113. }
  114. func main(args: [String]) throws {
  115. if args.dropFirst().contains("--version") {
  116. printVersion(args: args)
  117. return
  118. }
  119. // initialize responses
  120. var response = Google_Protobuf_Compiler_CodeGeneratorResponse(
  121. files: [],
  122. supportedFeatures: [.proto3Optional]
  123. )
  124. // read plugin input
  125. let rawRequest = FileHandle.standardInput.readDataToEndOfFile()
  126. let request = try Google_Protobuf_Compiler_CodeGeneratorRequest(serializedData: rawRequest)
  127. let options = try GeneratorOptions(parameter: request.parameter)
  128. // Build the SwiftProtobufPluginLibrary model of the plugin input
  129. let descriptorSet = DescriptorSet(protos: request.protoFile)
  130. // A count of generated files by desired name (actual name may differ to avoid collisions).
  131. var generatedFiles: [String: Int] = [:]
  132. // Only generate output for services.
  133. for name in request.fileToGenerate {
  134. if let fileDescriptor = descriptorSet.fileDescriptor(named: name) {
  135. if options.generateReflectionData {
  136. var binaryFile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  137. let binaryFileName = uniqueOutputFileName(
  138. component: "grpc",
  139. fileDescriptor: fileDescriptor,
  140. fileNamingOption: options.fileNaming,
  141. generatedFiles: &generatedFiles,
  142. extension: "reflection"
  143. )
  144. let serializedFileDescriptorProto = try fileDescriptor.proto.serializedData()
  145. .base64EncodedString()
  146. binaryFile.name = binaryFileName
  147. binaryFile.content = serializedFileDescriptorProto
  148. response.file.append(binaryFile)
  149. }
  150. if !fileDescriptor.services.isEmpty
  151. && (options.generateClient || options.generateServer || options.generateTestClient)
  152. {
  153. var grpcFile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  154. let grpcFileName = uniqueOutputFileName(
  155. component: "grpc",
  156. fileDescriptor: fileDescriptor,
  157. fileNamingOption: options.fileNaming,
  158. generatedFiles: &generatedFiles
  159. )
  160. #if compiler(>=6.0)
  161. if options.v2 {
  162. let grpcGenerator = ProtobufCodeGenerator(
  163. configuration: SourceGenerator.Configuration(options: options)
  164. )
  165. grpcFile.content = try grpcGenerator.generateCode(
  166. from: fileDescriptor,
  167. protoFileModuleMappings: options.protoToModuleMappings,
  168. extraModuleImports: options.extraModuleImports
  169. )
  170. } else {
  171. let grpcGenerator = Generator(fileDescriptor, options: options)
  172. grpcFile.content = grpcGenerator.code
  173. }
  174. #else
  175. let grpcGenerator = Generator(fileDescriptor, options: options)
  176. grpcFile.content = grpcGenerator.code
  177. #endif
  178. grpcFile.name = grpcFileName
  179. response.file.append(grpcFile)
  180. }
  181. }
  182. }
  183. // return everything to the caller
  184. let serializedResponse = try response.serializedData()
  185. FileHandle.standardOutput.write(serializedResponse)
  186. }
  187. do {
  188. try main(args: CommandLine.arguments)
  189. } catch {
  190. Log("ERROR: \(error)")
  191. }
  192. #if compiler(>=6.0)
  193. extension SourceGenerator.Configuration {
  194. init(options: GeneratorOptions) {
  195. let accessLevel: SourceGenerator.Configuration.AccessLevel
  196. switch options.visibility {
  197. case .internal:
  198. accessLevel = .internal
  199. case .package:
  200. accessLevel = .package
  201. case .public:
  202. accessLevel = .public
  203. }
  204. self.init(
  205. accessLevel: accessLevel,
  206. accessLevelOnImports: options.useAccessLevelOnImports,
  207. client: options.generateClient,
  208. server: options.generateServer
  209. )
  210. }
  211. }
  212. #endif