main.swift 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. import Stencil
  20. import PathKit
  21. func Log(_ message : String) {
  22. FileHandle.standardError.write((message + "\n").data(using:.utf8)!)
  23. }
  24. // Code templates use "//-" prefixes to comment-out template operators
  25. // to keep them from interfering with Swift code formatting tools.
  26. // Use this to remove them after templates have been expanded.
  27. func stripMarkers(_ code:String) -> String {
  28. let inputLines = code.components(separatedBy:"\n")
  29. var outputLines : [String] = []
  30. for line in inputLines {
  31. if line.contains("//-") {
  32. let removed = line.replacingOccurrences(of:"//-", with:"")
  33. if (removed.trimmingCharacters(in:CharacterSet.whitespaces) != "") {
  34. outputLines.append(removed)
  35. }
  36. } else {
  37. outputLines.append(line)
  38. }
  39. }
  40. return outputLines.joined(separator:"\n")
  41. }
  42. // from apple/swift-protobuf/Sources/protoc-gen-swift/StringUtils.swift
  43. func splitPath(pathname: String) -> (dir:String, base:String, suffix:String) {
  44. var dir = ""
  45. var base = ""
  46. var suffix = ""
  47. #if swift(>=3.2)
  48. let pathnameChars = pathname
  49. #else
  50. let pathnameChars = pathname.characters
  51. #endif
  52. for c in pathnameChars {
  53. if c == "/" {
  54. dir += base + suffix + String(c)
  55. base = ""
  56. suffix = ""
  57. } else if c == "." {
  58. base += suffix
  59. suffix = String(c)
  60. } else {
  61. suffix += String(c)
  62. }
  63. }
  64. #if swift(>=3.2)
  65. let validSuffix = suffix.isEmpty || suffix.first == "."
  66. #else
  67. let validSuffix = suffix.isEmpty || suffix.characters.first == "."
  68. #endif
  69. if !validSuffix {
  70. base += suffix
  71. suffix = ""
  72. }
  73. return (dir: dir, base: base, suffix: suffix)
  74. }
  75. enum OutputNaming : String {
  76. case FullPath
  77. case PathToUnderscores
  78. case DropPath
  79. }
  80. func outputFileName(component: String, index: Int, fileDescriptor: FileDescriptor) -> String {
  81. var ext : String
  82. if index == 0 {
  83. ext = "." + component + ".pb.swift"
  84. } else {
  85. ext = "\(index)." + component + ".pb.swift"
  86. }
  87. let pathParts = splitPath(pathname: fileDescriptor.name)
  88. let outputNamingOption = OutputNaming.FullPath // temporarily hard-coded
  89. switch outputNamingOption {
  90. case .FullPath:
  91. return pathParts.dir + pathParts.base + ext
  92. case .PathToUnderscores:
  93. let dirWithUnderscores =
  94. pathParts.dir.replacingOccurrences(of: "/", with: "_")
  95. return dirWithUnderscores + pathParts.base + ext
  96. case .DropPath:
  97. return pathParts.base + ext
  98. }
  99. }
  100. func main() throws {
  101. // initialize template engine and add custom filters
  102. let templateEnvironment = Environment(loader: InternalLoader(),
  103. extensions:[GRPCFilterExtension()])
  104. // initialize responses
  105. var response = Google_Protobuf_Compiler_CodeGeneratorResponse()
  106. var log = ""
  107. // read plugin input
  108. let rawRequest = try Stdin.readall()
  109. let request = try Google_Protobuf_Compiler_CodeGeneratorRequest(serializedData: rawRequest)
  110. let options = try GeneratorOptions(parameter: request.parameter)
  111. // Build the SwiftProtobufPluginLibrary model of the plugin input
  112. let descriptorSet = DescriptorSet(protos: request.protoFile)
  113. var generatedFileNames = Set<String>()
  114. var clientCount = 0
  115. var serverCount = 0
  116. // process each .proto file separately
  117. for fileDescriptor in descriptorSet.files {
  118. // log info about the service
  119. log += "File \(fileDescriptor.name)\n"
  120. for serviceDescriptor in fileDescriptor.services {
  121. log += "Service \(serviceDescriptor.name)\n"
  122. for methodDescriptor in serviceDescriptor.methods {
  123. log += " Method \(methodDescriptor.name)\n"
  124. log += " input \(methodDescriptor.inputType.name)\n"
  125. log += " output \(methodDescriptor.outputType.name)\n"
  126. log += " client_streaming \(methodDescriptor.proto.clientStreaming)\n"
  127. log += " server_streaming \(methodDescriptor.proto.serverStreaming)\n"
  128. }
  129. }
  130. if fileDescriptor.services.count > 0 {
  131. // a package declaration is required for file containing service(s)
  132. let package = fileDescriptor.package
  133. guard package != "" else {
  134. print("ERROR: no package for \(fileDescriptor.name)")
  135. continue
  136. }
  137. // generate separate implementation files for client and server
  138. let context : [String:Any] = [
  139. "file": fileDescriptor,
  140. "access": options.visibility.sourceSnippet]
  141. do {
  142. let clientFileName = outputFileName(component:"client", index:clientCount, fileDescriptor:fileDescriptor)
  143. if !generatedFileNames.contains(clientFileName) {
  144. generatedFileNames.insert(clientFileName)
  145. clientCount += 1
  146. let clientcode = try templateEnvironment.renderTemplate(name:"client.pb.swift",
  147. context: context)
  148. var clientfile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  149. clientfile.name = clientFileName
  150. clientfile.content = stripMarkers(clientcode)
  151. response.file.append(clientfile)
  152. }
  153. let serverFileName = outputFileName(component:"server", index:serverCount, fileDescriptor:fileDescriptor)
  154. if !generatedFileNames.contains(serverFileName) {
  155. generatedFileNames.insert(serverFileName)
  156. serverCount += 1
  157. let servercode = try templateEnvironment.renderTemplate(name:"server.pb.swift",
  158. context: context)
  159. var serverfile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  160. serverfile.name = serverFileName
  161. serverfile.content = stripMarkers(servercode)
  162. response.file.append(serverfile)
  163. }
  164. } catch (let error) {
  165. Log("ERROR \(error)")
  166. log += "ERROR: \(error)\n"
  167. }
  168. }
  169. }
  170. log += "\(request)"
  171. // add the logfile to the code generation response
  172. var logfile = Google_Protobuf_Compiler_CodeGeneratorResponse.File()
  173. logfile.name = "swiftgrpc.log"
  174. logfile.content = log
  175. response.file.append(logfile)
  176. // return everything to the caller
  177. let serializedResponse = try response.serializedData()
  178. Stdout.write(bytes: serializedResponse)
  179. }
  180. do {
  181. try main()
  182. } catch (let error) {
  183. Log("ERROR: \(error)")
  184. }