PluginUtils.swift 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. * Copyright 2024, 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 PackagePlugin
  18. let configFileName = "grpc-swift-proto-generator-config.json"
  19. /// Derive the path to the instance of `protoc` to be used.
  20. /// - Parameters:
  21. /// - config: The supplied config. If no path is supplied then one is discovered using the `PROTOC_PATH` environment variable or the `findTool`.
  22. /// - findTool: The context-supplied tool which is used to attempt to discover the path to a `protoc` binary.
  23. /// - Returns: The path to the instance of `protoc` to be used.
  24. func deriveProtocPath(
  25. using config: GenerationConfig,
  26. tool findTool: (String) throws -> PackagePlugin.PluginContext.Tool
  27. ) throws -> URL {
  28. if let configuredProtocPath = config.protocPath {
  29. return URL(fileURLWithPath: configuredProtocPath)
  30. } else if let environmentPath = ProcessInfo.processInfo.environment["PROTOC_PATH"] {
  31. // The user set the env variable, so let's take that
  32. return URL(fileURLWithPath: environmentPath)
  33. } else {
  34. // The user didn't set anything so let's try see if Swift Package Manager can find a binary for us
  35. return try findTool("protoc").url
  36. }
  37. }
  38. /// Construct the arguments to be passed to `protoc` when invoking the `protoc-gen-swift` `protoc` plugin.
  39. /// - Parameters:
  40. /// - config: The config for this operation.
  41. /// - fileNaming: The file naming scheme to be used.
  42. /// - inputFiles: The input `.proto` files.
  43. /// - protoDirectoryPaths: The directories in which `protoc` will look for imports.
  44. /// - protocGenSwiftPath: The path to the `protoc-gen-swift` `protoc` plugin.
  45. /// - outputDirectory: The directory in which generated source files are created.
  46. /// - Returns: The constructed arguments to be passed to `protoc` when invoking the `protoc-gen-swift` `protoc` plugin.
  47. func constructProtocGenSwiftArguments(
  48. config: GenerationConfig,
  49. fileNaming: GenerationConfig.FileNaming?,
  50. inputFiles: [URL],
  51. protoDirectoryPaths: [String],
  52. protocGenSwiftPath: URL,
  53. outputDirectory: URL
  54. ) -> [String] {
  55. var protocArgs = [
  56. "--plugin=protoc-gen-swift=\(protocGenSwiftPath.absoluteStringNoScheme)",
  57. "--swift_out=\(outputDirectory.absoluteStringNoScheme)",
  58. ]
  59. for path in protoDirectoryPaths {
  60. protocArgs.append("--proto_path=\(path)")
  61. }
  62. protocArgs.append("--swift_opt=Visibility=\(config.accessLevel.rawValue)")
  63. protocArgs.append("--swift_opt=FileNaming=\(config.fileNaming.rawValue)")
  64. protocArgs.append("--swift_opt=UseAccessLevelOnImports=\(config.accessLevelOnImports)")
  65. protocArgs.append(contentsOf: inputFiles.map { $0.absoluteStringNoScheme })
  66. return protocArgs
  67. }
  68. /// Construct the arguments to be passed to `protoc` when invoking the `protoc-gen-grpc-swift` `protoc` plugin.
  69. /// - Parameters:
  70. /// - config: The config for this operation.
  71. /// - fileNaming: The file naming scheme to be used.
  72. /// - inputFiles: The input `.proto` files.
  73. /// - protoDirectoryPaths: The directories in which `protoc` will look for imports.
  74. /// - protocGenGRPCSwiftPath: The path to the `protoc-gen-grpc-swift` `protoc` plugin.
  75. /// - outputDirectory: The directory in which generated source files are created.
  76. /// - Returns: The constructed arguments to be passed to `protoc` when invoking the `protoc-gen-grpc-swift` `protoc` plugin.
  77. func constructProtocGenGRPCSwiftArguments(
  78. config: GenerationConfig,
  79. fileNaming: GenerationConfig.FileNaming?,
  80. inputFiles: [URL],
  81. protoDirectoryPaths: [String],
  82. protocGenGRPCSwiftPath: URL,
  83. outputDirectory: URL
  84. ) -> [String] {
  85. var protocArgs = [
  86. "--plugin=protoc-gen-grpc-swift=\(protocGenGRPCSwiftPath.absoluteStringNoScheme)",
  87. "--grpc-swift_out=\(outputDirectory.absoluteStringNoScheme)",
  88. ]
  89. for path in protoDirectoryPaths {
  90. protocArgs.append("--proto_path=\(path)")
  91. }
  92. protocArgs.append("--grpc-swift_opt=Visibility=\(config.accessLevel.rawValue.capitalized)")
  93. protocArgs.append("--grpc-swift_opt=Server=\(config.servers)")
  94. protocArgs.append("--grpc-swift_opt=Client=\(config.clients)")
  95. protocArgs.append("--grpc-swift_opt=FileNaming=\(config.fileNaming.rawValue)")
  96. protocArgs.append("--grpc-swift_opt=UseAccessLevelOnImports=\(config.accessLevelOnImports)")
  97. protocArgs.append(contentsOf: inputFiles.map { $0.absoluteStringNoScheme })
  98. return protocArgs
  99. }
  100. extension URL {
  101. /// Returns `URL.absoluteString` with the `file://` scheme prefix removed
  102. ///
  103. /// Note: This method also removes percent-encoded UTF-8 characters
  104. var absoluteStringNoScheme: String {
  105. var absoluteString = self.absoluteString.removingPercentEncoding ?? self.absoluteString
  106. absoluteString.trimPrefix("file://")
  107. return absoluteString
  108. }
  109. }
  110. enum Stderr {
  111. private static let newLine = "\n".data(using: .utf8)!
  112. static func print(_ message: String) {
  113. if let data = message.data(using: .utf8) {
  114. FileHandle.standardError.write(data)
  115. FileHandle.standardError.write(Self.newLine)
  116. }
  117. }
  118. }