Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 35 additions & 35 deletions Plugins/SwiftProtobufPlugin/plugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ struct SwiftProtobufPlugin {
/// Errors thrown by the `SwiftProtobufPlugin`
enum PluginError: Error, CustomStringConvertible {
/// Indicates that the target where the plugin was applied to was not `SourceModuleTarget`.
case invalidTarget(Target)
case invalidTarget(String)
/// Indicates that the file extension of an input file was not `.proto`.
case invalidInputFileExtension(String)
/// Indicates that there was no configuration file at the required location.
case noConfigFound(String)

var description: String {
switch self {
case let .invalidTarget(target):
return "Expected a SwiftSourceModuleTarget but got '\(type(of: target))'."
case let .invalidTarget(targetDescription):
return "Expected a SwiftSourceModuleTarget but got '\(targetDescription)'."
case let .invalidInputFileExtension(path):
return "The input file '\(path)' does not have a '.proto' extension."
case let .noConfigFound(path):
Expand Down Expand Up @@ -101,49 +101,49 @@ struct SwiftProtobufPlugin {

/// Create build commands for the given arguments
/// - Parameters:
/// - pluginWorkDirectory: The path of a writable directory into which the plugin or the build
/// - pluginWorkDirectory: The URL of a writable directory into which the plugin or the build
/// commands it constructs can write anything it wants.
/// - sourceFiles: The input files that are associated with the target.
/// - tool: The tool method from the context.
/// - Returns: The build commands configured based on the arguments.
func createBuildCommands(
pluginWorkDirectory: PackagePlugin.Path,
pluginWorkDirectory: URL,
sourceFiles: FileList,
tool: (String) throws -> PackagePlugin.PluginContext.Tool
) throws -> [Command] {
guard
let configurationFilePath = sourceFiles.first(
let configurationFileURL = sourceFiles.first(
where: {
$0.path.lastComponent == Self.configurationFileName
$0.url.lastPathComponent == Self.configurationFileName
}
)?.path
)?.url
else {
throw PluginError.noConfigFound(Self.configurationFileName)
}
let data = try Data(contentsOf: URL(fileURLWithPath: "\(configurationFilePath)"))
let data = try Data(contentsOf: configurationFileURL)
let configuration = try JSONDecoder().decode(Configuration.self, from: data)
try validateConfiguration(configuration)

// We need to find the path of protoc and protoc-gen-swift
let protocPath: Path
let protocURL: URL
if let configuredProtocPath = configuration.protocPath {
// The user set the config path in the file. So let's take that
protocPath = Path(configuredProtocPath)
protocURL = URL(fileURLWithPath: configuredProtocPath)
} else if let environmentPath = ProcessInfo.processInfo.environment["PROTOC_PATH"] {
// The user set the env variable. So let's take that
protocPath = Path(environmentPath)
protocURL = URL(fileURLWithPath: environmentPath)
} else {
// The user didn't set anything so let's try see if SPM can find a binary for us
protocPath = try tool("protoc").path
protocURL = try tool("protoc").url
}
let protocGenSwiftPath = try tool("protoc-gen-swift").path
let protocGenSwiftURL = try tool("protoc-gen-swift").url

return configuration.invocations.map { invocation in
self.invokeProtoc(
directory: configurationFilePath.removingLastComponent(),
directory: configurationFileURL.deletingLastPathComponent(),
invocation: invocation,
protocPath: protocPath,
protocGenSwiftPath: protocGenSwiftPath,
protocURL: protocURL,
protocGenSwiftURL: protocGenSwiftURL,
outputDirectory: pluginWorkDirectory
)
}
Expand All @@ -154,27 +154,27 @@ struct SwiftProtobufPlugin {
/// - Parameters:
/// - directory: The plugin's target directory.
/// - invocation: The `protoc` invocation.
/// - protocPath: The path to the `protoc` binary.
/// - protocGenSwiftPath: The path to the `protoc-gen-swift` binary.
/// - protocURL: The URL of the `protoc` binary.
/// - protocGenSwiftURL: The URL of the `protoc-gen-swift` binary.
/// - outputDirectory: The output directory for the generated files.
/// - Returns: The build command configured based on the arguments.
private func invokeProtoc(
directory: PackagePlugin.Path,
directory: URL,
invocation: Configuration.Invocation,
protocPath: Path,
protocGenSwiftPath: Path,
outputDirectory: Path
protocURL: URL,
protocGenSwiftURL: URL,
outputDirectory: URL
) -> Command {
// Construct the `protoc` arguments.
var protocArgs = [
"--plugin=protoc-gen-swift=\(protocGenSwiftPath)",
"--swift_out=\(outputDirectory)",
"--plugin=protoc-gen-swift=\(protocGenSwiftURL.path)",
"--swift_out=\(outputDirectory.path)",
]

// We need to add the target directory as a search path since we require the user to specify
// the proto files relative to it.
protocArgs.append("-I")
protocArgs.append("\(directory)")
protocArgs.append(directory.path)

// Add the visibility if it was set
if let visibility = invocation.visibility {
Expand All @@ -196,20 +196,20 @@ struct SwiftProtobufPlugin {
protocArgs.append("--swift_opt=UseAccessLevelOnImports=\(useAccessLevelOnImports)")
}

var inputFiles = [Path]()
var outputFiles = [Path]()
var inputFiles = [URL]()
var outputFiles = [URL]()

for var file in invocation.protoFiles {
// Append the file to the protoc args so that it is used for generating
protocArgs.append("\(file)")
inputFiles.append(directory.appending(file))
inputFiles.append(directory.appendingPathComponent(file))

// The name of the output file is based on the name of the input file.
// We validated in the beginning that every file has the suffix of .proto
// This means we can just drop the last 5 elements and append the new suffix
file.removeLast(5)
file.append("pb.swift")
let protobufOutputPath = outputDirectory.appending(file)
let protobufOutputPath = outputDirectory.appendingPathComponent(file)

// Add the outputPath as an output file
outputFiles.append(protobufOutputPath)
Expand All @@ -220,9 +220,9 @@ struct SwiftProtobufPlugin {
// the rule engine in the build system.
return Command.buildCommand(
displayName: "Generating swift files from proto files",
executable: protocPath,
executable: protocURL,
arguments: protocArgs,
inputFiles: inputFiles + [protocGenSwiftPath],
inputFiles: inputFiles + [protocGenSwiftURL],
outputFiles: outputFiles
)
}
Expand All @@ -245,10 +245,10 @@ extension SwiftProtobufPlugin: BuildToolPlugin {
target: Target
) async throws -> [Command] {
guard let swiftTarget = target as? SwiftSourceModuleTarget else {
throw PluginError.invalidTarget(target)
throw PluginError.invalidTarget(String(describing: type(of: target)))
}
return try createBuildCommands(
pluginWorkDirectory: context.pluginWorkDirectory,
pluginWorkDirectory: context.pluginWorkDirectoryURL,
sourceFiles: swiftTarget.sourceFiles,
tool: context.tool
)
Expand All @@ -264,7 +264,7 @@ extension SwiftProtobufPlugin: XcodeBuildToolPlugin {
target: XcodeTarget
) throws -> [Command] {
try createBuildCommands(
pluginWorkDirectory: context.pluginWorkDirectory,
pluginWorkDirectory: context.pluginWorkDirectoryURL,
sourceFiles: target.inputFiles,
tool: context.tool
)
Expand Down