Skip to content
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ struct diagnostics_stub: CommandPlugin {
}

// Diagnostics are collected by SwiftPM and printed to standard error, depending on the current log verbosity level.
if arguments.contains("progress") {
Diagnostics.progress("command plugin: Diagnostics.progress") // prefixed with [plugin_name]
}

if arguments.contains("remark") {
Diagnostics.remark("command plugin: Diagnostics.remark") // prefixed with 'info:' when printed
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/Commands/Utilities/PluginDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ final class PluginDelegate: PluginInvocationDelegate {
swiftTool.observabilityScope.emit(diagnostic)
}

func pluginEmittedProgress(_ message: String) {
swiftTool.outputStream.write("[\(plugin.name)] \(message)\n")
swiftTool.outputStream.flush()
}

func pluginRequestedBuildOperation(
subset: PluginInvocationBuildSubset,
parameters: PluginInvocationBuildParameters,
Expand Down
5 changes: 5 additions & 0 deletions Sources/PackagePlugin/Diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ public struct Diagnostics {
public static func remark(_ message: String, file: String? = #file, line: Int? = #line) {
self.emit(.remark, message, file: file, line: line)
}

/// Emits a progress message
public static func progress(_ message: String) {
try? pluginHostConnection.sendMessage(.emitProgress(message: message))
}
}
5 changes: 4 additions & 1 deletion Sources/PackagePlugin/PluginMessages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,10 @@ enum PluginToHostMessage: Codable {
enum DiagnosticSeverity: String, Codable {
case error, warning, remark
}


/// The plugin emits a progress message.
case emitProgress(message: String)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be extended in future with progress bars or n/m completed prefixes.


/// The plugin defines a build command.
case defineBuildCommand(configuration: CommandConfiguration, inputFiles: [URL], outputFiles: [URL])

Expand Down
12 changes: 10 additions & 2 deletions Sources/SPMBuildCore/Plugins/PluginInvocation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,10 @@ extension PluginTarget {
diagnostic = .info(message, metadata: metadata)
}
self.invocationDelegate.pluginEmittedDiagnostic(diagnostic)


case .emitProgress(let message):
self.invocationDelegate.pluginEmittedProgress(message)

case .defineBuildCommand(let config, let inputFiles, let outputFiles):
if config.version != 2 {
throw PluginEvaluationError.pluginUsesIncompatibleVersion(expected: 2, actual: config.version)
Expand Down Expand Up @@ -485,7 +488,9 @@ extension PackageGraph {
dispatchPrecondition(condition: .onQueue(delegateQueue))
outputData.append(contentsOf: data)
}


func pluginEmittedProgress(_ message: String) {}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not clear what is the best option here. Could append to outputData or add a progress array field similar to diagnostics below.


func pluginEmittedDiagnostic(_ diagnostic: Basics.Diagnostic) {
dispatchPrecondition(condition: .onQueue(delegateQueue))
diagnostics.append(diagnostic)
Expand Down Expand Up @@ -819,6 +824,9 @@ public protocol PluginInvocationDelegate {
/// Called when a plugin emits a diagnostic through the PackagePlugin APIs.
func pluginEmittedDiagnostic(_: Basics.Diagnostic)

/// Called when a plugin emits a progress message through the PackagePlugin APIs.
func pluginEmittedProgress(_: String)

/// Called when a plugin defines a build command through the PackagePlugin APIs.
func pluginDefinedBuildCommand(displayName: String?, executable: AbsolutePath, arguments: [String], environment: [String: String], workingDirectory: AbsolutePath?, inputFiles: [AbsolutePath], outputFiles: [AbsolutePath])

Expand Down
46 changes: 34 additions & 12 deletions Tests/CommandsTests/PackageToolTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1886,6 +1886,7 @@ final class PackageToolTests: CommandsTestCase {
// Match patterns for expected messages
let isEmpty = StringPattern.equal("")
let isOnlyPrint = StringPattern.equal("command plugin: print\n")
let containsProgress = StringPattern.contains("[diagnostics-stub] command plugin: Diagnostics.progress")
let containsRemark = StringPattern.contains("command plugin: Diagnostics.remark")
let containsWarning = StringPattern.contains("command plugin: Diagnostics.warning")
let containsError = StringPattern.contains("command plugin: Diagnostics.error")
Expand Down Expand Up @@ -1915,18 +1916,25 @@ final class PackageToolTests: CommandsTestCase {
XCTAssertMatch(stderr, isEmpty)
}

try runPlugin(flags: [], diagnostics: ["print", "remark"]) { stdout, stderr in
try runPlugin(flags: [], diagnostics: ["print", "progress"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, containsProgress)
}

try runPlugin(flags: [], diagnostics: ["print", "progress", "remark"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, isEmpty)
XCTAssertMatch(stderr, containsProgress)
}

try runPlugin(flags: [], diagnostics: ["print", "remark", "warning"]) { stdout, stderr in
try runPlugin(flags: [], diagnostics: ["print", "progress", "remark", "warning"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, containsProgress)
XCTAssertMatch(stderr, containsWarning)
}

try runPluginWithError(flags: [], diagnostics: ["print", "remark", "warning", "error"]) { stdout, stderr in
try runPluginWithError(flags: [], diagnostics: ["print", "progress", "remark", "warning", "error"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, containsProgress)
XCTAssertMatch(stderr, containsWarning)
XCTAssertMatch(stderr, containsError)
}
Expand All @@ -1940,18 +1948,24 @@ final class PackageToolTests: CommandsTestCase {
XCTAssertMatch(stderr, isEmpty)
}

try runPlugin(flags: ["-q"], diagnostics: ["print", "remark"]) { stdout, stderr in
try runPlugin(flags: ["-q"], diagnostics: ["print", "progress"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, isEmpty)
XCTAssertMatch(stderr, containsProgress)
}

try runPlugin(flags: ["-q"], diagnostics: ["print", "remark", "warning"]) { stdout, stderr in
try runPlugin(flags: ["-q"], diagnostics: ["print", "progress", "remark"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, isEmpty)
XCTAssertMatch(stderr, containsProgress)
}

try runPluginWithError(flags: ["-q"], diagnostics: ["print", "remark", "warning", "error"]) { stdout, stderr in
try runPlugin(flags: ["-q"], diagnostics: ["print", "progress", "remark", "warning"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, containsProgress)
}

try runPluginWithError(flags: ["-q"], diagnostics: ["print", "progress", "remark", "warning", "error"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, containsProgress)
XCTAssertNoMatch(stderr, containsRemark)
XCTAssertNoMatch(stderr, containsWarning)
XCTAssertMatch(stderr, containsError)
Expand All @@ -1967,19 +1981,27 @@ final class PackageToolTests: CommandsTestCase {
// At this level stderr contains extra compiler output even if the plugin does not print diagnostics
}

try runPlugin(flags: ["-v"], diagnostics: ["print", "remark"]) { stdout, stderr in
try runPlugin(flags: ["-v"], diagnostics: ["print", "progress"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, containsProgress)
}

try runPlugin(flags: ["-v"], diagnostics: ["print", "progress", "remark"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, containsProgress)
XCTAssertMatch(stderr, containsRemark)
}

try runPlugin(flags: ["-v"], diagnostics: ["print", "remark", "warning"]) { stdout, stderr in
try runPlugin(flags: ["-v"], diagnostics: ["print", "progress", "remark", "warning"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, containsProgress)
XCTAssertMatch(stderr, containsRemark)
XCTAssertMatch(stderr, containsWarning)
}

try runPluginWithError(flags: ["-v"], diagnostics: ["print", "remark", "warning", "error"]) { stdout, stderr in
try runPluginWithError(flags: ["-v"], diagnostics: ["print", "progress", "remark", "warning", "error"]) { stdout, stderr in
XCTAssertMatch(stdout, isOnlyPrint)
XCTAssertMatch(stderr, containsProgress)
XCTAssertMatch(stderr, containsRemark)
XCTAssertMatch(stderr, containsWarning)
XCTAssertMatch(stderr, containsError)
Expand Down
4 changes: 4 additions & 0 deletions Tests/FunctionalTests/PluginTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,8 @@ class PluginTests: XCTestCase {
print("[DIAG] \(diagnostic)")
diagnostics.append(diagnostic)
}

func pluginEmittedProgress(_ message: String) {}
}

// Helper function to invoke a plugin with given input and to check its outputs.
Expand Down Expand Up @@ -767,6 +769,8 @@ class PluginTests: XCTestCase {
dispatchPrecondition(condition: .onQueue(delegateQueue))
diagnostics.append(diagnostic)
}

func pluginEmittedProgress(_ message: String) {}
}

// Find the relevant plugin.
Expand Down