msbuild: Exec ConsoleToMSBuild=true doesn't preserve colors

@rainersigwald I’ve spent some time pondering some deep, Confucious stuff lately regarding my build toolchain, and started asking:

“How can I run a .NET Core CLI Tool in an MSBuild Exec Task AND preserve the color output?”

Oh, boy.

Along the way, I found that MSBuild output itself can emit ANSI Escape Codes to processes like Travis CI… so it got me wondering, why can’t MSBuild parse ANSI Escape Codes rather than generate them? Searching StackOverflow, I don’t see any good answers. People seem to think fundamentally there is no way to capture color, it’s drastically impractical, because color is a property of the console, not the standard output and error streams. I say, let there be color. Pastel, maybe.

Here is a sample repro of the issue:

build.targets

<?xml version="1.0" encoding="utf-8" ?>
  <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="PowershellColoredOutput">
  <Target Name="PowershellColoredOutput">
    <!-- Clear your mind with this zen action: -->
    <Exec Command="pwsh.exe -Command &quot;Write-Host 'hi' -ForegroundColor red&quot;" ConsoleToMSBuild="true">
	  <Output TaskParameter="ConsoleOutput" ItemName="OutputOfExec" />
    </Exec>
  </Target>
</Project>

Expected Output

“hi” in red.

Actual Output

“hi” in console default foreground color and default background color

Deep Thoughts

“I for one believe that if you give people a thorough understanding of what confronts them and the basic causes that produce it, they’ll create their own program, and when the people create a program, you get action.” — Malcolm X

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 2
  • Comments: 29 (2 by maintainers)

Most upvoted comments

Thanks for filing this; I’ve been thinking about it for a long time, too. Unfortunately, I haven’t found a good solution either. There are a few problems:

  1. It’s extremely common for tools to detect that they’re being redirected and stop emitting color codes. Powershell does this: image
  2. On Windows, there are two ways to change the console color: ANSI escape codes, and Win32 Console APIs. We could only ever hope to capture the former–I definitely don’t think it’s worth building a ConDrv-aware Exec into core MSBuild just for colorization.
  3. We have to decide what to do with the output. Right now, messages exist only as text. That means they can be streamed to the console (with our minimal colorization applied) and to text logs and to the binary log and to custom loggers. It’s very likely that a subset of those things would be very confused if VT escape codes suddenly appeared in the text.

This came up in the context of dotnet test, which runs inside MSBuild but wants to control its output completely, which currently requires various workarounds.

Hey so lemme chime in real quick before we get off the rails on ConPty.

Conpty exists to enable applications to act as terminal emulators on Windows. It acts as the console host, servicing console API calls in the same way the console normally does, but then “renders” the side effects of the API calls to a stream of VT sequences. This means that client applications (like cmd.exe, msbuild.exe, etc) can use the same old Console API’s they’ve been using since Windows 3.1, but now instead being forced to use conhost as the terminal window, another application could step in as the terminal instead, and the new terminal application could be written just the same as a terminal emulator on linux.

We (the console team) certainly haven’t done any work to expose ConPTY in any sort of managed sense. The sample you linked is community code, but there’s no official support currently.

Conpty is not a new magic commandline client API. It will not magically make your client app emit output as VT.

For commandline-client application developers targeting Win10+, our general recommendation is to use VT sequences always, and enable VT processing with SetConsoleMode. We’re expanding our support for VT sequences, but we’re leaving the console API as it is, for compatibility reasons. As noted here, the Console API isn’t really portable to other platforms.

the ability to dynamically reflect-over/query command-line apps built atop System.CommandLine for its list of supported args

And a footnote to this is that the mechanism by which this query is performed is also the directive syntax I mentioned earlier, e.g.:

> dotnet-interactive [suggest] stdio h
--help
--log-path
-h
/h
http

@jonsequitur - Got it, thanks. Though one suggestion: You may want to rename ANSI to VT since ANSI stopped standardizing escape codes and VT sequences in the '80s, deferring instead to industry standards. ISO/IEC and ECMA both maintain standards, but haven’t really kept up with industry’s advancements to support, for example, 24-bit color, etc.

And perhaps nonansi could be replaced with legacy or similar?

System.CommandLine.Rendering will include VT if [output:ansi] is set, it will attempt to render using System.Console APIs if [output:nonansi] is set, and it will render plain text (no VT, table layouts via whitespace) if [output:plaintext] is set.

We typically try to detect the terminal capabilities and set this for you, so these are explicit overrides to that behavior.

LOLZ. Sorry if I dove deep, but I wanted to be sure that people realize that the command-line today is LITERALLY a mirror of the command-line as it was in 1960: It’s chars out and chars in. That’s it; everything else is just details! 😜

It’s for this very reason that I wrote this multi-part series on the command-line and how it evolved, and how Windows differs from *NIX, etc.

I’m not entirely sure there’s anyone maintaining the .NET Console API.

Yes there are. For .NET Core, we live in https://github.com/dotnet/corefx