MSBuildはWindowsにおけるソースコードのコンパイル・ビルドコマンドであり、主にVisual Studioを使って開発する場合に使用されます。
ここでは、Powershellを使ってMSBuildを実行する方法を解説します。
PowershellによるMSBuild実行
Powershellを使って、プロジェクトファイル(*.vcxproj)をMSBuildでビルドします。Visual Studioのプロジェクト・プロパティファイルはこちらを参考に用意されている前提で解説します。
Powershellのファイルをbuild.ps1として用意し、以下のように実装します。
src/build.ps1
param(
[String]$Mode="Build", # 実行モード[Build|Rebuild|Clean]
[String]$Config="Debug", # ビルド構成[Debug|Release]
[String]$Platform="x64", # ターゲットプラットフォーム[x64]
[String]$Develop="VS2019", # 開発環境[VS2019]
[String]$LogDirectory="$(Split-Path $myInvocation.MyCommand.Path -parent)\build2019\log", # ログ格納ディレクトリ
[Switch]$Help # 使用方法表示
)
#--------------------------------------
# 変数定義
#--------------------------------------
$debugPreference='SilentlyContinue'
Set-Variable CUR_DIR "$(Split-Path $myInvocation.MyCommand.Path -parent)"
Set-Variable BUILD_DIR "$script:CUR_DIR\..\.."
Set-Variable START_TIME ([System.DateTime]::Now)
Set-Variable RESULT_MSG @()
Set-Variable VS2019_DIR 'C:\Program Files (x86)\Microsoft Visual Studio\Installer'
Set-Variable ENV_LIST @()
Set-Variable PROJECT_LIST @()
#--------------------------------------
# 引数チェック
#--------------------------------------
# 使用方法表示
if ($script:Help) { Print-Usage; exit 1 }
# 動作モード
if ((($script:Mode -eq "Build") -or ($script:Mode -eq "Rebuild") -or ($script:Mode -eq "Clean")) -eq $false) { Print-Usage $script:Mode; throw }
# ビルド構成×プラットフォーム
if ((($script:Config -eq "Debug") -or ($script:Config -eq "Release")) -eq $false) { Print-Usage $script:Config; throw }
if ((($script:Platform -eq "x64")) -eq $false) { Print-Usage $script:Platform; throw }
# 開発環境
if ((($script:Develop -eq "VS2019")) -eq $false) { Print-Usage $script:Develop; throw }
# 暗黙の引数は受け付けない
if($args -ne $null){ Print-Usage $args; throw }
#--------------------------------------
# バッチファイル実行後の環境変数取得
#--------------------------------------
function Set-BatchEnviron([String]$filePath, [String]$cmdOption)
{
# コマンドプロンプトで出力した結果をPowerShell上で設定する
$execCommands = "`"$filePath`" $cmdOption & set"
cmd /c $execCommands | Foreach-Object {
$p, $v = $_.Split('=')
Set-Item -path env:$p -value $v
}
}
#--------------------------------------
# VS2019環境セットアップ
#--------------------------------------
function Set-VS2019Environ64()
{
$batchFile = & "$VS2019_DIR\vswhere.exe" "-latest" "-products" "*" "-requires" "Microsoft.VisualStudio.Component.VC.Tools.x86.x64" "-property" "installationPath" | % { echo $_\VC\Auxiliary\Build\vcvarsall.bat }
$cmdOption = "x64"
Set-BatchEnviron $batchFile $cmdOption
Write-Host "setup visual studio 2019 $cmdOption" -ForeGroundColor DarkGray
}
#--------------------------------------
# 初期化
#--------------------------------------
function Initialize()
{
# 今の位置を覚えておく
Push-Location
# 環境変数を保存
$script:ENV_LIST = $(Get-Item -Path env:)
# Visual Studio環境設定
switch ($script:Develop) {
'VS2019' {
$script:PRJ_EXTENSION = ".vcxproj"
$script:LogDirectory="$script:CUR_DIR\build2019\log"
}
default { Write-Error "internal error"; exit 1 }
} # switch
# ログ格納ディレクトリ作成
if (Test-Path $script:LogDirectory) {
Remove-Item $script:LogDirectory -recurse -force
}
New-Item $script:LogDirectory -type directory -force | Out-Null
return 0
}
#--------------------------------------
# 終了処理
#--------------------------------------
function Finalize()
{
# トータル時間出力
Write-Host "`n<$script:Mode Time>"
if ($script:RESULT_MSG) {
$script:RESULT_MSG | Foreach-Object { Write-Host $_ }
}
Write-Host "Total Time=$([System.DateTime]::Now - $script:START_TIME)"
# 環境変数を元に戻す
$script:ENV_LIST | Foreach-Object { Set-Item -Path env:$($_.Name) -Value $($_.Value) }
# 元の位置に戻る
Pop-Location
}
# ------------------------------------------------------------------------------
# プロセス実行
# ------------------------------------------------------------------------------
function Start-Process(
[String]$ModulePath,
[String[]]$Arguments
)
{
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = $ModulePath
$startInfo.UseShellExecute = $false # ウィンドウが出てくると作業を妨げるので作成しない
$startInfo.CreateNoWindow = $true # ウィンドウが出てくると作業を妨げるので作成しない
$startInfo.WorkingDirectory = $script:CUR_DIR
$startInfo.Arguments = $Arguments
# この返却値を受け取らないとプロセス終了制御できないので注意
return [System.Diagnostics.Process]::Start($startInfo)
}
#--------------------------------------
# プロセス終了待ち
#--------------------------------------
function Wait-Process(
[System.Diagnostics.Process]$Target=$(throw "please input target"),
[String]$Suffix
)
{
if(!$Target){
return 0
}
Write-Debug "wait process($($Target.Name)). arg=$($Target.StartInfo.Arguments)"
if (($Target.WaitForExit()) -eq $false) {
Write-Error "error wait for exit process. name=$($Target.Name)"
return -1
}
Write-Debug "exit process($($Target.Name)). arg=$($Target.StartInfo.Arguments), code=$($Target.ExitCode)"
# 第一引数のソリューションファイルの一階層上のディレクトリ名を名前にする
$name = ((($Target.StartInfo.Arguments).Split(' '))[0] | Split-Path | Split-Path -Leaf) + $Suffix
Write-Host "$name $script:Mode Finished. ExitCode=$($Target.ExitCode), LapTime=$($Target.ExitTime - $Target.StartTime)" -ForeGroundColor DarkGray
return 0
}
#--------------------------------------
# ビルド実行
#--------------------------------------
function Start-Build(
[String]$Component=$(throw "please input Component"),
[String]$ProjectFile=$(throw "please input SolutionFile"),
[String]$Platform=$(throw "please input Platform"),
[String]$Configuration=$(throw "please input Configuration"),
[String]$Mode=$(throw "please input Mode"),
[String]$Verbosity="detailed"
)
{
Write-Host "Start [$Component] $ProjectFile $Mode $Configuration|$Platform" -ForeGroundColor Green
$logName = "$script:LogDirectory\$Component"
$process = Start-Process `
-ModulePath "msbuild.exe"`
-Arguments @($ProjectFile,`
"/v:$Verbosity",`
"/p:platform=$Platform",`
"/p:configuration=$Configuration",`
"/t:$Mode",`
"/flp1:LogFile=$logName.summary;Append;summarysonly",`
"/flp2:LogFile=$logName.error;Append;errorsonly",`
"/flp3:LogFile=$logName.warning;Append;warningsonly"
)
if ($process -eq $null) {
Write-Error "start build error(process start). name=$name"
return $null
} else {
Write-Debug "start process($($process.Name)). arg=$($process.StartInfo.Arguments)"
return $process
}
}
#--------------------------------------
# メイン関数
#--------------------------------------
function Main()
{
# Visual Studio環境設定
switch ($script:Develop) {
'VS2019' {
switch ($script:Platform) {
'x64' { Set-VS2019Environ64 }
default { Write-Error "internal error"; exit 1 }
} # switch
}
default { Write-Error "internal error"; exit 1 }
}
$process = Start-Build -Component 'components' -ProjectFile 'C:/src/components/component1/component1.vcxproj' -Platform $script:Platform -Configuration $script:Config -Mode $script:Mode
Wait-Process -Target $process
return 0
}
#--------------------------------------
# エントリーポイント
#--------------------------------------
if ((Initialize) -ne 0) { Finalize; exit 1 }
if ((Main) -ne 0) { Finalize; exit 1 }
Finalize
exit 0
Powershellで実行して確認します。
ビルド結果が「src\build2019」以下に出力されていることも確認して下さい。
PowershellによるMSBuild並列実行
build.ps1のメイン関数を以下のように書き換えます。
src/build.ps1
function Main()
{
# Visual Studio環境設定
switch ($script:Develop) {
'VS2019' {
switch ($script:Platform) {
'x64' { Set-VS2019Environ64 }
default { Write-Error "internal error"; exit 1 }
} # switch
}
default { Write-Error "internal error"; exit 1 }
}
$process1 = Start-Build -Component 'components' -ProjectFile 'C:/src/components/component1/component1.vcxproj' -Platform $script:Platform -Configuration $script:Config -Mode $script:Mode
$process2 = Start-Build -Component 'components' -ProjectFile 'C:/src/components/component2/component2.vcxproj' -Platform $script:Platform -Configuration $script:Config -Mode $script:Mode
Wait-Process -Target $process1
Wait-Process -Target $process2
return 0
}
Powershellで実行して確認します。
ビルド結果が「src\build2019」以下に出力されていることも確認して下さい。
(component1とcomponent2の両方が出力されていることを確認して下さい)