BuildPluginConfig.swift 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. let configFileName = "grpc-swift-proto-generator-config.json"
  18. /// The config of the build plugin.
  19. struct BuildPluginConfig: Codable {
  20. /// Config defining which components should be considered when generating source.
  21. struct Generate {
  22. /// Whether server code is generated.
  23. ///
  24. /// Defaults to `true`.
  25. var servers: Bool
  26. /// Whether client code is generated.
  27. ///
  28. /// Defaults to `true`.
  29. var clients: Bool
  30. /// Whether message code is generated.
  31. ///
  32. /// Defaults to `true`.
  33. var messages: Bool
  34. static let defaults = Self(
  35. servers: true,
  36. clients: true,
  37. messages: true
  38. )
  39. private init(servers: Bool, clients: Bool, messages: Bool) {
  40. self.servers = servers
  41. self.clients = clients
  42. self.messages = messages
  43. }
  44. }
  45. /// Config relating to the generated code itself.
  46. struct GeneratedSource {
  47. /// The visibility of the generated files.
  48. ///
  49. /// Defaults to `Internal`.
  50. var accessLevel: GenerationConfig.AccessLevel
  51. /// Whether imports should have explicit access levels.
  52. ///
  53. /// Defaults to `false`.
  54. var accessLevelOnImports: Bool
  55. static let defaults = Self(
  56. accessLevel: .internal,
  57. accessLevelOnImports: false
  58. )
  59. private init(accessLevel: GenerationConfig.AccessLevel, accessLevelOnImports: Bool) {
  60. self.accessLevel = accessLevel
  61. self.accessLevelOnImports = accessLevelOnImports
  62. }
  63. }
  64. /// Config relating to the protoc invocation.
  65. struct Protoc {
  66. /// Specify the directory in which to search for imports.
  67. ///
  68. /// Paths are relative to the location of the specifying config file.
  69. /// Build plugins only have access to files within the target's source directory.
  70. /// May be specified multiple times; directories will be searched in order.
  71. /// The target source directory is always appended
  72. /// to the import paths.
  73. var importPaths: [String]
  74. /// The path to the `protoc` executable binary.
  75. ///
  76. /// If this is not set, Swift Package Manager will try to find the tool itself.
  77. var executablePath: String?
  78. static let defaults = Self(
  79. importPaths: [],
  80. executablePath: nil
  81. )
  82. private init(importPaths: [String], executablePath: String?) {
  83. self.importPaths = importPaths
  84. self.executablePath = executablePath
  85. }
  86. }
  87. /// Config defining which components should be considered when generating source.
  88. var generate: Generate
  89. /// Config relating to the nature of the generated code.
  90. var generatedSource: GeneratedSource
  91. /// Config relating to the protoc invocation.
  92. var protoc: Protoc
  93. static let defaults = Self(
  94. generate: Generate.defaults,
  95. generatedSource: GeneratedSource.defaults,
  96. protoc: Protoc.defaults
  97. )
  98. private init(generate: Generate, generatedSource: GeneratedSource, protoc: Protoc) {
  99. self.generate = generate
  100. self.generatedSource = generatedSource
  101. self.protoc = protoc
  102. }
  103. // Codable conformance with defaults
  104. enum CodingKeys: String, CodingKey {
  105. case generate
  106. case generatedSource
  107. case protoc
  108. }
  109. init(from decoder: any Decoder) throws {
  110. let container = try decoder.container(keyedBy: CodingKeys.self)
  111. self.generate =
  112. try container.decodeIfPresent(Generate.self, forKey: .generate) ?? Self.defaults.generate
  113. self.generatedSource =
  114. try container.decodeIfPresent(GeneratedSource.self, forKey: .generatedSource)
  115. ?? Self.defaults.generatedSource
  116. self.protoc =
  117. try container.decodeIfPresent(Protoc.self, forKey: .protoc) ?? Self.defaults.protoc
  118. }
  119. }
  120. extension BuildPluginConfig.Generate: Codable {
  121. // Codable conformance with defaults
  122. enum CodingKeys: String, CodingKey {
  123. case servers
  124. case clients
  125. case messages
  126. }
  127. init(from decoder: any Decoder) throws {
  128. let container = try decoder.container(keyedBy: CodingKeys.self)
  129. self.servers =
  130. try container.decodeIfPresent(Bool.self, forKey: .servers) ?? Self.defaults.servers
  131. self.clients =
  132. try container.decodeIfPresent(Bool.self, forKey: .clients) ?? Self.defaults.clients
  133. self.messages =
  134. try container.decodeIfPresent(Bool.self, forKey: .messages) ?? Self.defaults.messages
  135. }
  136. }
  137. extension BuildPluginConfig.GeneratedSource: Codable {
  138. // Codable conformance with defaults
  139. enum CodingKeys: String, CodingKey {
  140. case accessLevel
  141. case accessLevelOnImports
  142. }
  143. init(from decoder: any Decoder) throws {
  144. let container = try decoder.container(keyedBy: CodingKeys.self)
  145. self.accessLevel =
  146. try container.decodeIfPresent(GenerationConfig.AccessLevel.self, forKey: .accessLevel)
  147. ?? Self.defaults.accessLevel
  148. self.accessLevelOnImports =
  149. try container.decodeIfPresent(Bool.self, forKey: .accessLevelOnImports)
  150. ?? Self.defaults.accessLevelOnImports
  151. }
  152. }
  153. extension BuildPluginConfig.Protoc: Codable {
  154. // Codable conformance with defaults
  155. enum CodingKeys: String, CodingKey {
  156. case importPaths
  157. case executablePath
  158. }
  159. init(from decoder: any Decoder) throws {
  160. let container = try decoder.container(keyedBy: CodingKeys.self)
  161. self.importPaths =
  162. try container.decodeIfPresent([String].self, forKey: .importPaths)
  163. ?? Self.defaults.importPaths
  164. self.executablePath = try container.decodeIfPresent(String.self, forKey: .executablePath)
  165. }
  166. }
  167. extension GenerationConfig {
  168. init(buildPluginConfig: BuildPluginConfig, configFilePath: URL, outputPath: URL) {
  169. self.server = buildPluginConfig.generate.servers
  170. self.client = buildPluginConfig.generate.clients
  171. self.message = buildPluginConfig.generate.messages
  172. // hard-code full-path to avoid collisions since this goes into a temporary directory anyway
  173. self.fileNaming = .fullPath
  174. self.visibility = buildPluginConfig.generatedSource.accessLevel
  175. self.accessLevelOnImports = buildPluginConfig.generatedSource.accessLevelOnImports
  176. // Generate absolute paths for the imports relative to the config file in which they are specified
  177. self.importPaths = buildPluginConfig.protoc.importPaths.map { relativePath in
  178. configFilePath.deletingLastPathComponent().absoluteStringNoScheme + "/" + relativePath
  179. }
  180. self.protocPath = buildPluginConfig.protoc.executablePath
  181. self.outputPath = outputPath.absoluteStringNoScheme
  182. }
  183. }