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