VC PlusPlus:MSBuild

提供:yonewiki

VC Cpp記事に戻る。

概要

 MSBuildは*.slnや*.vcxprojファイル(プロジェクトファイル)を実行するときに使われるコマンドのようなものです。言語によってはもう少しいろいろな拡張子が処理されます。ここでは、*.slnや*.vcxprojと*.targetファイルに絞って説明を進めます。つまりC++以外の言語の事は知らないというスタンスです。


 この記事では、MSBuildコマンドの実行の基礎Targetの基礎に触れます。その他の技術については、別の子記事として記述します。


 *.vcxprojファイルの中身を処理していくものですが、*.slnはMSBuild形式では書かれていません。MSBuildコマンドが*.slnをMSBuildフォーマットに変換しているので、実行ができるようになっています。


 VisualStudioのメニューから起動するコマンドプロンプトやパワーシェルでMSBuildというコマンドが使えるようにパス設定がなされます。起動の仕方はVisualStudioのメニュー[ツール]-[コマンドライン]-[開発者コマンドライン]or[開発者用PowerShell]から起動できます。コマンドプロンプトの方がいろいろと安心なのですが、ここでは便利なパワーシェルの方を使います。

 

MSBuildの基礎

 例えば、プロジェクトファイルをビルドするには以下のようなコマンドを使います。


REM 通常
MSBuild (プロジェクトファイル名).vcxproj /t:build /p:Configuration=Debug /p:Platform=x64
REM ログ出力 msbuild.logへ
MSBuild (プロジェクトファイル名).vcxproj /t:build /p:Configuration=Debug /p:Platform=x64 -fileLogger
REM 詳細ログ出力 指定したファイルmsbuild_log.txtへ
MSBuild (プロジェクトファイル名).vcxproj /t:build /p:Configuration=Debug /p:Platform=x64 /v:detailed > msbuild_log.txt


 /p:Configuration=Debug /p:Platform=x64の組み合わせの全てを一括でビルドするようなコマンドはないので、バッチファイルにするか && 接続による複数コマンド実行という方法を使う必要があります。


 ソリューションファイルをビルドすることもできる。以下のようにConfigurationやPlatformを省略して、


MSBuild (プロジェクトファイル名).sln /t:build


 のようにする。このようにConfigurationやPlatformの設定を省略した場合は最後に保存したときに指定されていたものを覚えているので、その設定でビルド処理がされます。/tはターゲットというものを指定しています。

 

Targetの基本

 ターゲットには、ものすごく細かいステップの一部であるものもあれば、buildのように一連のビルドプロセス全体を差すようなものまであります。ここでは一般的によく使うターゲットを列挙します。見やすさのために大文字小文字を使い分けている記述をしますが、実際は大文字小文字の区別はありません。


  • Build(ビルド処理)
  • Rebuild(BeforeRebuild;Clean;Build;AfterRebuildと同じ)
  • BeforeRebuild(リビルド前処理)
  • Clean(クリーン(消去)処理)
  • AfterRebuild(リビルド後処理)


 というものがあります。


 複数のビルドターゲット名を実行するときは ; で接続して記述します。では、MSBuildが読み取るvcxprojファイルの中身を少しづつ理解していきましょう。

 

Targetの細かい部分

 ちなみに、VisualStudioが規定で呼び出そうとする内部的なTargetには以下のようなものがあります。プロジェクトのファイルが全て最新でビルド済みの状態からでも以下に示すくらいのTargetを呼び出そうとします。


_CheckForInvalidConfigurationAndPlatform
SetTelemetryEnvironmentVariables
_PrepareForBuild
_PrepareForReferenceResolution
BeforeResolveReferences
_SplitProjectReferencesByFileExistence
_AddOutputPathToGlobalPropertiesToRemove
_GetProjectReferenceTargetFrameworkProperties
PrepareProjectReferences
ResolveProjectReferences
GetFrameworkPaths
GetReferenceAssemblyPaths
AddExternalIncludDirectoriesToPaths
SetBuildDefaultEnvironmentVariables
SetUserMacroEnvironmentVariables
GetResolvedWinMD
_CheckWindowsSDKInstalled
FixCAExcludePath
SetCABuildNativeEnvironmentVariables
PrepareForBuild
ResolveSDKReferences
ExpandSDKReferences
ResolveAssemblyReferences
AfterResolveReferences
ResolveReferences
InitializeBuildStatus
BuildGenerateSourcesTraverse
BeforeBuildGenerateSources
PreBuildEvent
_SelectedFiles
SelectCustomBuild
ComputeCustomBuildOutput
CustomBuild
CopyFileToFolders
_Xsd
MakeDirsForMidl
ComputeMIDLGeneratedCompileInputs
AfterMidl
_Midl
AfterBuildGenerateSources
AfterBuildGenerateSourcesEvent
_BuildGenerateSourcesAction
BuildGenerateSources
BuildCompileTraverse
BeforeClCompile
ComputeCLInputPDBName
FindReferenceAssembliesForReferences
GetReferencedVCProjectsInfo
ComputeReferenceCLInput
WarnCompileDuplicatedFilename
ComputeStdModulesCompileInputs
FixupCLCompileOptions
MakeDirsForCl
SetModuleDependencies
SelectClCompile
ClCompile
AfterClCompile
_ClCompile
BeforeResourceCompile
MakeDirsForResourceCompile
■AfterResourceCompile
_ResourceCompile
AfterBuildCompileEvent
_BuildCompileAction
BuildCompile
BuildLinkTraverse
ComputeLegacyManifestEmbedding
BeforeLink
ComputeRCOutputs
ComputeRCGeneratedLinkInputs
ComputeManifestGeneratedLinkerInputs
ComputeCLOutputs
ComputeCLGeneratedLinkInputs
ComputeLinkInputsFromProject
ComputeManifestInputsTargets
AssignTargetPaths
SplitResourcesByCulture
CreateCustomManifestResourceNames
PrepareResourceNames
MakeDirsForLink
DoLinkOutputFilesMatch
PreLinkEvent
ComputeLinkSwitches
Link
AfterLink
MakeDirsForMuiLink
_Link
_ALink
_Manifest
ResolvedXDCMake
ComputeCLCompileGeneratedXDCFiles
MakeDirsForXdcMake
_XdcMake
ComputeCLCompileGeneratedSbrFiles
MakeDirsForBscMake
_BscMake
RunMergeNativeCodeAnalysis
RunNativeCodeAnalysis
BuiltProjectOutputGroup
SatelliteDllsProjectOutputGroup
MakeDirsForFxc
ContentFilesProjectOutputGroup
CreateRecipeFile
_GenerateSatelliteAssemblyInputs
CreateSatelliteAssemblies
_Appverifier
_Deploy
_PopulateCommonStateForGetCopyToOutputDirectoryItems
GetCopyToOutputDirectoryXamlAppDefs
_GetCopyToOutputDirectoryItemsFromTransitiveProjectReferences
_GetCopyToOutputDirectoryItemsFromThisProject
GetCopyToOutputDirectoryItems
_CopySourceItemsToOutputDirectory
_CheckForCompileOutputs
CopyFilesToOutputDirectory
PrepareForRun
PostBuildEvent
_BuildLinkAction
BuildLink
AfterBuild
_CleanGetCurrentAndPriorFileWrites
IncrementalClean
TlogCleanup
FinalizeBuildStatus


 いっぱいありすぎる。けどこれらの細かいターゲットを実行しても、基本的にはなにも起こらないか、ごく一部が処理されるだけのことになります。Visual Studioはこれだけおおくのビルドステップを実行しようとするんですね。他にも特定のプロパティ値$(xxxx)という表記をする変数にビルドターゲット名を設定すると更に細かいターゲットも設定できます。例えば以下のようなものがあります。


  • $(AfterLinkTargets): リンクの後に実行されるターゲットを指定します。通常はビルドイベントや後処理に関連します。
  • $(ComputeLinkInputsTargets): リンクの入力を計算するターゲットを指定します。リンク前に特定の処理を行う際に使用されることがあります。
  • $(SourceFilesProjectOutputGroupDependsOn): ソースファイルのプロジェクト出力グループに依存するターゲットを指定します。ソースファイルに変更があった場合の処理に使用されることがあります。
  • $(ContentFilesProjectOutputGroupDependsOn): コンテンツファイルのプロジェクト出力グループに依存するターゲットを指定します。コンテンツファイルに変更があった場合の処理に使用されることがあります。


 もともと何も実行しないステップと、何かしらの処理をするターゲットがありますが、ユーザが再定義していいターゲットは、何も実行しないターゲットが使っていいターゲットと考えるべきでしょう。何もしない設定かどうかはプロジェクトファイルの設定にも左右されるので、ここで簡単に切り分けすることはできませんね。

 

TargetのImport部分

 Visual Studioでコンソールアプリケーションのプロジェクトを新規作成したときにできるvcxprojファイルには以下のようなターゲットが設定されているのが見受けられます。


  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />


 とここだけですね。


 *.vcxprojファイルにTargetタグはなく、ビルド時に何も呼ばれないように見えますが、Importで外部ファイルにMSBuild形式のファイルを取り込む事ができます。一般的なビルドターゲットはMicrosoft.Cpp.targetsというファイルに記述されているという事です。


 この中身っていうのは、VisualStudioのバージョンによってパスは異なりますが、2022版ならC:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.Cpp.targetsのことです。その中にTargetの設定が以下のように設定されています。


<!--省略-->
    <Target Name="InstallBuildTools"  />
<!--省略-->


 このような部分があり、これこそがTargetの定義になります。この場合 InstallBuildTools という名前のTargetが設定されたことになります。Targetタグ記載があっても必ずしも呼び出されるとは限りません。ビルド時のファイル構成状態の条件によってTargetが呼び出されるかどうかが決まります。

 

Targetの自己定義

 このような<Target Name="">の宣言を開発者が宣言することもできます。例えば、何も実行する予定の無いTargetを自分の開発しているプロジェクトの*.vcxprojファイルやImport設定した外部ファイルに以下のように定義すると、ビルド処理の流れに自分で考えた処理を挟み込むことができます。


<!--省略-->
  <Target Name="AfterResourceCompile">
  </Target>
<!--省略-->


 もともとあった定義を乗っ取る上記のような定義をさけるべく、AfterTargets要素やBeforeTargets要素を活用することができます。


 リンク処理の前に実行されるターゲットを設定したい場合


<!--省略-->
  <Target Name="MyLinkStep" BeforeTargets="Link">
  </Target>
<!--省略-->


 ビルド処理の後に実行されるターゲットを設定したい場合。


<!--省略-->
  <Target Name="MyBuildStep" AfterTargets="Build">
  </Target>
<!--省略-->


 既存のTargetを知っていれば、柔軟に知っているタイミングの前後にターゲットを追加できます。

 

TargetによるMessage HelloWorldの出力

 何もしない部分を追加しても何も起こらないので、何かビルド中に文字列を出力するように設定するHelloWorld!的なものを記述してみましょう。


<!--省略-->
  <Target Name="AfterResourceCompile">
    <Message Text="MSbBuild Hello,World!" Importance="High" />
  </Target>
<!--省略-->


 上記のようにMessageタグでテキストを出力できます。Importance="High"という要素を指定しておくとVisualStudioのビルド出力の対象にもなります。Importance="High"を省くとMSbuildコマンドでビルドしたときだけ表示される灰色のメッセージになります。Importance="High"が指定されているとMSBuildでも強調された白色文字になります。パワーシェルで操作したときの文字色の違いが発生します。

 

TargetからDependsOnTargetsで更に子Targetを設定した方法によるMessage HelloWorldの出力

 更に、違うTargetsを呼び出すことができます。例えばTargetタグに DependsOnTargets という要素を新たに追加し、その値をMessageTextという内容にして、Targetを呼び出すなら以下のようにします。


<!--省略-->
  <Target Name="AfterResourceCompile" DependsOnTargets="MessageText">
    <Message Text="MSbBuild Hello,World!" Importance="High" />
  </Target>
  <Target Name="MessageText">
    <Message Text="MessageText Target Hello,World!" Importance="High" />
  </Target>
<!--省略-->


 これでMessageTextターゲットが先に呼ばれ、その後に自分で定義したAfterResourceCompileが呼び出されることになる。MessageTextターゲット以外のターゲットを続けて呼び出したい場合はDependsOnTargets="MessageText;MessageText1;MessageText2"のようにセミコロン区切りで複数を設定できます。


>MSBuild (プロジェクトファイル名).vcxproj /t:build /p:Configuration=Debug /p:Platform=x64


 のように実行すると


>MSBuild (プロジェクトファイル名).vcxproj /t:build /p:Configuration=Debug /p:Platform=x64
(省略)
MessageText:
MessageText Target Hello,World!
AfterResourceCompile:
MSbBuild Hello,World!
(省略)


 のように出力されるようになります。

 

関連記事

MSBuildで使われる変数

 

MSBuildでの項目関数


 

MSBuildでの特別な要素

 

タスク

 特定の意味を持つタグをタスクと呼びます。

 

VC Cpp記事に戻る。