皆さんごきげんよう。11/8 に Visual Studio 2022 がリリースされました。
Visual Studio 2022 は、史上初の 64bit プロセス化された統合開発環境(IDE)であり、32bit 版は提供されず、64bit 版のリリースのみとなっています。今回は、この Visual Studio 2022 のプロセス(devenv.exe)の 64bit 化と、MSBuild の関係性・影響についてご案内します。
元となったポストはこちら。(本件は、以下のポストの意訳みたいなものです)
#MSBuild とは
Microsoft Build Engine (MSBuild) とは、文字通りアプリケーションをビルドするためのプラットフォームです。
意外かもしれませんが、MSBuild は Visual Studio に依存していません。Visual Studio が MSBuild をホストしているのです。(なので、プロジェクトとソリューションに対して msbuild.exe を実行しても、ビルドが可能です)つまり、Visual Studio のプロセスが 64bit になるということは、MSBuild のプロセス自体も、64bit 版のものが使われるということを意味しています。通常、それが何か問題を及ぼすことはありませんが、Visual Studio 2022 のビルド対象に 32bit のみのタスクが含まれていて、かつ 32bit のタスクとして正しくマークされていない場合、ビルドが失敗する可能性があります。
##問題に合致するかどうかをチェックするために最も簡単な方法
64bit 版の MSBuild でビルドを試してみることです。ちなみに、Visual Studio と Build Tools には、Visual Studio 2013 から 64bit の MSBuild が含まれているので、VS2022 に移行する前に確認することができます。
#背景と変更点
MSBuild は、Visual Studio と .NET SDK 双方に同梱されています。ここで説明する変更点は、Visual Studio MSBuild のみに影響し、dotnet build で実施するビルドには影響しません。
MSBuild には 32 ビットと 64 ビットの実行ファイルがあります。両方とも Visual Studio および Visual Studio Build Tools に含まれていますので、インストール先のフルパス指定で msbuild.exe を呼び出す際、32bit 版と、64bit 版どちらを使用するかを選択できます。また、Visual Studio の Developer Command Prompt では、MSBuild の bin フォルダを含むように PATH を設定することも可能です。
Visual Studio 2019 は、Visual Studio でのビルドの際、32bit 版の MSBuild を使用していた最後のバージョンの IDE です。Visual Studio 2022 は 64bit プロセスとなり、MSBuild をインプロセスで実行するようになったため、F5
や Ctrl
-F5
時含めて、内部では 64bit 版のMSBuild が実行されるようになりました。つまり、64bit プロセス化への変更に伴い、Visual Studio 開発者コマンド プロンプトは PATH 内の 64bit 版 MSBuild を呼び出すようになりました (以前のリリースでは 32bit 版の実行ファイルのパスが PATH 内に含まれていました)。
32bit 版の MSBuild は、Azure Pipelines の Visual Studio Build および MSBuild タスク でデフォルトで実行されます。これを変更するには、YAML Job の定義で msbuildArchitecture: 'x64'
を指定します。GitHub Actions では、setup-msbuild
アクションで、msbuild-architecture: 'x64'
を使って、同じく設定を指定できます。
#結局どんな影響があるの?
環境間の Windows の変更により、ビルドの一部が 32bit のファイル システムやレジストリのリダイレクトに依存している場合、問題が発生する可能性があります。
ほとんどの MSBuild タスクは AnyCPU 用にビルドするので、64bit 環境での実行に問題ありません。多くのタスクはコマンド ラインからツールを起動しますが ("shell out")、ツールは独自のプロセスで実行されるため、タスクが 32bit であろうが 64bit であろうが違いはありません。
しかし、一部のタスクのには、ネイティブ ライブラリを p/invoke で起動するものがあるために実行されるアーキテクチャにセンシティブです。
タスクをメンテナンスする場合、64bit 環境で動作するように書き直す必要はありません。代わりに、MSBuild が正しい bit 数のプロセスで実行するようにマークすることができます。
64bit 版の MSBuild は 32bit タスクをプロセス外で起動でき、32bit 版は 64bit タスクを起動することができます。次項を参照ください。
Visual Studio SDK でも、64bit 版の MSBuild の互換性の問題に遭遇する場合があったかもしれません。そうしたタスクの中には、C++ で書かれた 32bit (x86 用) のライブラリの Thin Wrapper があります。この問題は、Visual Studio 2022 のライフサイクルの早期段階で発見され、Microsoft.VSSDK.BuildTools 16.8.1015 以降、Visual Studio SDK は、以下で説明する方法を用いて 64bit MSBuild をサポートするように更新されました。
タスク オーナー(NuGetパッケージ)へのガイダンス
32bit または 64bit 環境での実行をサポートするようにタスクを書き換えて、ネイティブ アセンブリの両方のコピーをデプロイするオプションがありますが、この方法は困難な場合が多いので、(64bit のビルドからでも)32bit プロセスでタスクを実行するように MSBuild を設定することがおすすめです。
この場合、新しいプロセスを起動して通信を行う必要があるため、既存の MSBuild プロセスでタスクを実行するよりもビルド中に時間がかかります。ただ、それはタスクがビルド中に何度も何度も呼び出されるようなことがない限り、通常は問題にならないでしょう。
タスクは UsingTask
要素で使用できるようになります。UsingTask
要素の Architecture
属性を指定することで、特定の実行環境を必要とするようにタスクを設定することができます。
32bit 用アセンブリの UsingTask
をうまく指定すると、次のようになります。
<UsingTask TaskName="TaskThatNeedsX86Library"
AssemblyFile="$(MSBuildThisFileDirectory)ArchSpecificTasks.dll"
Architecture="x86" />
MSBuild は、.NET 4.5 および Visual Studio 2012 以降、異なるアーキテクチャでのタスクの実行をサポートしているため、この変更には後方互換性があります。Architecture
を指定しても、32bit 版の MSBuild 環境でのタスクの実行方法は変わりませんが、64bit 版の MSBuild では 32bit のセカンダリ プロセスでタスク実行されます。このため、この変更を無条件に行うことや、Visual Studio 2022(MSBuild 17)を特別に検出するようなことはしないことをお勧めします。
#テストしてみる
変更内容が機能していることを確認するために、いくつかのビルドシナリオでタスクをテストすることをお勧めします。
- Visual Studio 2019 での UI-driven ビルド(そのシナリオでリグレッションが発生していないことを確認する)
- Visual Studio 2019(MSBuild 16)の 32bit 版
MSBuild.exe
を使用したコマンドライン ビルド - Visual Studio 2019 (MSBuild 16) の 64bit 版
MSBuild.exe
を使用したコマンドライン ビルド - Visual Studio 2022 での UI-driven ビルド(これは今後の主要な開発者のシナリオです)
- Visual Studio 2022(MSBuild 17)の 64bit 版
MSBuild.exe
を使用したコマンドライン ビルド - .NET SDK を使用したコマンドライン ビルド(タスクがこの環境をサポートしている場合)。これらは
dotnet build
を使用しているため、今回の変更による影響はありません。
#既知の問題
タスクが 32bit のみでコンパイルされた .NET アセンブリで定義されている場合、UsingTask
要素が正しく Architecture
を宣言していても、MSBuild は次のようなエラーでタスクのロードに失敗します。
S:\BitnessInMSBuild\ShowErrors.proj(13,5): error MSB4062: The "TaskCompiledForx86" task could not be loaded from the assembly S:\BitnessInMSBuild\TaskCompiledForx86\bin\Debug\net472\TaskCompiledForx86.dll. Could not load file or assembly 'file:///S:BitnessInMSBuildTaskCompiledForx86binDebugnet472TaskCompiledForx86.dll' or one of its dependencies. An attempt was made to load a program with an incorrect format. Confirm that the <UsingTask> declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.
これは dotnet/msbuild#6461 で追跡されています。
この問題を回避するには、タスクアセンブリを AnyCPU として再コンパイルし、さらに Architecture
を指定します。この場合、MSBuild エンジンは 64bit 環境でタスクをロードできますが、実行は 32bit プロセスでのみ行われます。
#自分が所有していないタスクのユーザーに対する緩和策
残念ながら、UsingTask
要素をコントロールしていない場合、タスクの設定ミスを回避することは困難です。 dotnet/msbuild#5541 では、プロジェクト内の誤った UsingTask
を簡単にオーバーライドできるようにするための変更を追跡します。将来の Visual Studio/MSBuild のリリースで、この機能が役に立つかどうかを是非おしえてください。
#Next Step
Visual Studio 2022 へのアップグレードは超わくわくするものです。64bit 版 MSBuild は、私たちが用意した新機能のほんの一部分です。皆様にお試しいただけることを楽しみにしています。アップグレードや 64bit 版 MSBuild についての体験談をぜひお聞かせください! フィードバックをお待ちしています。
以上、Rainer Sigwald さんでした。
それでは皆様ごきげんよう。