使用 PowerShell 呼叫 MSBuild 建置專案
0 |
一般要執行 MSBuild 指令,標準做法是由開始選單找到 Developer Command Prompt for VS 2019 或 Developer PowerShell for VS 2019:
而這兩個環境跟一般命令視窗的差別在於它會先執行 "X:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" 設定必要的環境變數,確保相關作業能正確執行。
開發團隊成員所安裝的 VS 版本跟位置可能有差異,甚至有些環境只有裝 MSBuild 沒裝 IDE,所以我想寫一個 PowerShell 腳本,自動找到 VS2019/VS2017 的安裝位置呼叫 MSBuild 編譯專案,減少人為操作,也可能當成日後轉批次或自動化的基礎。
關於尋找 VS 安裝路徑的方法,我做了些研究:
- 較早的 Visual Studio 版本,可由 Registry HKLM\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\xx.0 找安裝路徑
- VS2017 開始改用新的安裝架構,還一併推出可以取得 Visual Studio 安裝資訊的程式元件(Microsoft.VisualStudio.Setup.Configuration.Interop),另外有個 C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe 工具可以列出已安裝版本。
- 除了前述程式元件,Windows Management Framework (WMF) 5.0 還內建 Visual Studio Setup PowerShell Module
網友有應用它找安裝路徑的範例
工作環境會以 VS2017+ 為主,看起來 VSSetup PowerShell Module 是最方便的選擇,下指令 Install-Module VSSetup -Scope CurrentUser
便可下載安裝,又可跟 PowerShell 無縫整合。
程式原理是先用 Get-Command 檢查 VSSetup 模組是否已安裝,若無則現場安裝。透過 Get-VSSetupInstance | Select-VSSetupInstance 找到最新版 VS,找到 VsMSBuildCmd.bat。這裡有個眉角,執行 VsMSBuildCmd.bat 環境變數是設在 Cmd.exe 工作階段,不會傳到 PowerShell 端。參考 PowerShell Community Extension (Pscx) Invoke-BatchFile 的巧妙做法,在 Cmd.exe 執行 SET 將所有環境變數匯出成檔案,再從 PowerShell 讀檔全部重設一次,將環境變數通通複製過來。
我的想法是在為每個專案寫一個專屬的編譯腳本,其中可包含各式客製化編譯程序,擁有最大彈性。以昨天提到的 Web Site Project 為例,將 MSBuildProj.ps1 與 .sln 放在同目錄;
MSBuildProj.ps1 範例如下:
function LoadVSMSBuildCmd() {
# 檢查 VSSetup 指令是否存在,若無則現場安裝
[bool] $vsSetupInstalled = $null -ne (Get-Command Get-VSSetupINstance -ErrorAction SilentlyContinue)
if (!$vsSetupInstalled) {
Write-Verbose "Installing VSSetup module..."
Install-Module VSSetup -Scope CurrentUser -Force
}
# 取得最新版本 VS 的安裝路徑,類似 C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise
$instPath = (Get-VSSetupInstance | Select-VSSetupInstance -Latest -Require Microsoft.Component.MSBuild).InstallationPath
$batPath = "$instPath\Common7\Tools\VsMSBuildCmd.bat"
if (Test-Path $batPath -PathType Leaf) {
# 執行 VsMSBuildCmd.bat 後,將所有環境變數複製到 PowerShell 環境中
# ref: https://github.com/Pscx/Pscx/blob/master/Src/Pscx/Modules/Utility/Pscx.Utility.psm1
$tempFile = [IO.Path]::GetTempFileName()
cmd.exe /c " `"$batPath`" && set " > $tempFile
Get-Content $tempFile | Foreach-Object {
if ($_ -match "^(.*?)=(.*)$") {
Set-Content "env:\$($matches[1])" $matches[2]
}
else {
$_
}
}
Remove-Item $tempFile
Invoke-BatchFile $batPath
}
}
LoadVSMSBuildCmd
# 將工作目錄切換到 .ps1 所在資料夾
Set-Location $PSScriptRoot
MSBuild.exe WebSite\website.publishproj "/p:DeployOnBuild=true;PublishProfile=$(Get-Location)\WebSite\App_Data\PublishProfiles\FolderPRofile.pubxml"
如此,從版控拉回原始碼後,執行 .ps1 即可進行編譯,不易受環境影響,編譯部署工作可以更簡單化標準化,應是不錯的策略。
Tips of writing flexible and customized PowerShell script to build .NET project.
Comments
Be the first to post a comment