Where does the happy-path output (stdout?) from a prebuild plugin tool using an external binary go? How do I see it?

I have a prebuild plugin that runs ls and one that runs echo. If I purposefully pass them bad input I see the error message in both Xcode and in the command line. If I pass them something valid, my expectation would be to see output messages in the build log as well but I don't. Where does it go? How do I see it? And is this related to why > and >> can't be passed as arguments?

FWIW when I put print statements in my own custom local executable they do show up in the build log. Print statements from the plugin code also show up in the build log. It's just the output from the executable when it's an external binary so far. I have not yet tried a non-system binary.

    func logDirectoryContents(toLog:Path, output:Path) -> PackagePlugin.Command {
            displayName: "Test prebuild",
            executable: .init("/bin/ls"),
            arguments: ["-1t", toLog.string],
             //known can't be done... a shame.  
            //arguments: ["-1", toLog.string, ">>", output.string ], 
            //environment: T##[String : CustomStringConvertible],
            outputFilesDirectory: output.removingLastComponent()
    func echoTest(toEcho:some StringProtocol, outputDir:Path) -> PackagePlugin.Command {
            displayName: "Results from echo",
            executable: .init("/bin/zsh"),
            arguments: ["-c", "echo \(toEcho)"],
            outputFilesDirectory: outputDir

I understand that to make this work I can build my own tool that opens a Process. In the mean time I'm curious though about how the plugin system works.

The invocationDelegate has a handleOutput function which the comment says handles both stderr and stdout?

/// Invoked when the plugin emits arbtirary data on its stdout/stderr. There is no guarantee that the data is split on UTF-8 character encoding boundaries etc.  The script runner delegate just passes it on to the invocation delegate.
func handleOutput(data: Data) {

code link

But looking in the code base it appear to only be called for stderr not for stdout ever. From handleOutput the stderr infor can be easily traced through the code to where it ends up in the Diagnostics in BuildOperation

stdout appears instead to be handled by a FileHandle.writePluginMessage and the file handle eventually winds up at

            if let handler = handle.readabilityHandler {

link to code

info on readabilityHandler

But then it I can't tell what actually gets DONE with it? Who gets the contents of stdout? Why do the print statements in my local executable show up but output from ls and echo don't? Are they not all stdout?


Thank you Ed & Ray for pointing me in the right direction.

In Xcode each line in the build log has a hamburger menu on the right had side when it is selected that I had failed to notice. I hadn't realized those were a thing :exploding_head:

Clicking on it reveals script and its output.

In the command line you won't see them with verbose, but will see them with very verbose

# Does not contain script output
swift run -v plugin-tester fruit_list
# Does contain script output
swift run --vv plugin-tester fruit_list

Very verbose contains a lot of other stuff as well so its not ideal for just scanning for a result, but at least it's parse-able!

Still would personally prefer to have them hit the Diagnostic level/behave the same as prints from the same script but at least I can find them now!