.NET 9 の MSBuild で Message タスクの出力がコンソールに表示されなくなっていた
今回は、.NET プログラミング、とくに MSBuild スクリプト (.csproj ファイルとか) の話です。
MSBuild スクリプト開発時は Message タスクで printf デバッグ
一般的 (?) な .NET 開発の場面では、そうそう機会はないかもしれませんが、自分は、.NET におけるビルドスクリプトである MSBuild スクリプトを色々といじる機会があります。
あいにくと今日現在、自分が知る限りでは、MSBuild スクリプトをデバッグ実行、すなわち、ブレークポイントを設定してそこで一時停止したり、変数 (プロパティやアイテム) をウォッチしたりする仕組みはないようです。なので、MSBuild スクリプトの開発時は、バイナリログの生成を指定して実行し、生成されたバイナリログをビューワーで確認したり、あるいは、MSBuild スクリプト内に <Message> タスクを記述して、いわゆる "printf デバッグ" を実施したりしてきました。すなわち、MSBuild スクリプト内に <Message> タスク呼び出しを実行しておくと、その <Message> タスクの Text 属性に指定した文字列が、その MSBuild スクリプト実行時に、コンソールに表示されるわけです。
.NET 9 から、Message タスクの出力が表示されなくなった
ところが、.NET SDK のバージョンを、.NET 9 (この記事執筆時点では、まだ正式リリース前の Preview 6) に更新したところ、この <Message> タスクを使った出力がコンソールに表示されなくなってしまいました。
例えば、以下のような、コンソールアプリケーションをビルドする MSBuild スクリプト = プロジェクトフィル (.csproj ファイル) があったとして、
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<Target Name="PreBuild" BeforeTargets="CoreCompile">
<!-- 👇ここで Message タスクを使って、メッセージの表示を行なう -->
<Message Text="Hello, World!" Importance="high" />
</Target>
</Project>
このプロジェクトを .NET 8 SDK でビルド (dotnet build) したときは、コンソールに "Hello, World!" と表示されるのですが、.NET 9 SDK でビルドすると表示されないのです (下図参照)。

これでは、折角の <Message> タスクが何の役にも立たず、printf デバッグができません。どうしたものでしょうか。
原因は "Terminal Logger" が既定で有効になったこと
ネットで検索してみたところ、下記リンク先の .NET 9 SDK Preview 1 のリリースノートに答えがありました。
すなわち、MSBuild のコンソール出力に関して、"Terminal Logger" という、近代のリッチなターミナルコンソールに対応したログ出力機構が既定で使われることになったのが要因のようです。
先の図でもわかるように、この Terminal Logger は、ビルド中の処理時間などをインプレースで表示更新するなど、よりモダンな感じで、MSBuild スクリプトの進行状況を示してくれます。しかしその代償でしょう、そのような進捗表示の途中で、<Message> タスクによる出力をコンソールに混ぜてしまうと、進捗表示が破綻してしまうでしょうから、それで、Terminal Logger が有効だと、<Message> タスクによる出力がコンソールに表示されなくなったのでしょう。
幸い、以下のように、回避策はあります。
回避策その1. コンソール出力の情報量レベルを detailed にする
dotnet build などで MSBuild スクリプトを実行する際、-v オプション (コンソール出力の情報量レベル) に detailed を指定すると、MSBuild スクリプトの処理の最後に、<Message> タスクの出力がコンソールに表示されるようです (下記例)。
dotnet build -v:d
ただし、前述のとおり、Terminal Logger による進捗表示の途中で、(<Message> タスクを含む) 他の表示を混ぜるわけにはいかないのでしょうから、それで、ひととおり MSBuild スクリプトの実行が終了した最後にまとめて、それまで溜めておいた <Message> タスクによる出力をコンソールに表示するのだと思われます。
回避策その2. Terminal Logger を無効にする
もうひとつの回避策は、Terminal Logger を無効にして MSBuild スクリプトを実行する方法です。具体的には、dotnet コマンドに -tl オプションを付与し off を指定します (下記例)。
dotnet build -tl:off
このようにして MSBuild スクリプトを実行すると、Treminal Logger によるモダンな進捗表示はなくなるものの、<Message> タスクによる出力も含めて、.NET 8 以前と同じようにコンソール表示がされるようになります。
おまけ - Terminal Logger は .NET 8 SDK でも利用可能だった
余談ですが、このモダンな進捗表示を行なう Terminal Logger は、自分は知らなかったのですが、.NET 8 SDK から利用可能だったようです。ただ、既定では有効ではなかったとのことで、試しに、.NET 8 SDK 上で以下のように Terminal Logger を明示的に有効にして (つまり、-tl:on オプションを指定して)、 dotnet build コマンドを実行したところ、.NET 8 SDK でも、モダンな進捗表示がコンソールに表示されるようになりました。
dotnet build -tl:on
まとめ
.NET 9 以降では、MSBuild スクリプトの開発時に <Message> タスクによる出力をコンソールに表示させて、いわゆる "printf デバッグ" を行いたいときは、dotnet build などのコマンド実行時に、
-
-v:dオプションを付けて最後にまとめてコンソールに表示されるようにするか、あるいは、 -
-tl:offオプションを付けて .NET 8 以前と同じコンソール表示にするか、
いずれかの回避策を選択するとよいでしょう。
Discussion