了解建置程序

作者:Jason Lee

本主題提供企業級建置和部署程式的逐步解說。 本主題所述的方法會使用自訂Microsoft Build Engine (MSBuild) 專案檔,以提供程式每個層面的精細控制。 在專案檔中,自訂 MSBuild 目標可用來執行部署公用程式,例如 Internet Information Services (IIS) Web 部署工具 (MSDeploy.exe) 和資料庫部署公用程式VSDBCMD.exe。

注意

上一個主題 瞭解專案檔說明 MSBuild 專案檔的主要元件,並介紹分割專案檔的概念,以支援部署至多個目標環境。 如果您還不熟悉這些概念,您應該先檢閱 瞭解專案檔 ,再完成本主題。

本主題構成一系列教學課程的一部分,以名為 Fabrikam, Inc 的虛構公司企業部署需求為基礎。本教學課程系列使用範例解決方案連絡人 管理員解決方案來代表具有實際複雜層級的 Web 應用程式,包括 ASP.NET MVC 3 應用程式、Windows Communication Foundation (WCF) 服務和資料庫專案。

這些教學課程的核心部署方法是以 瞭解專案檔中所述的分割專案檔方法為基礎,其中建置程式是由兩個專案檔控制,一個包含適用于每個目的地環境的組建指示,另一個包含環境特定的組建和部署設定。 在建置階段,環境特定的專案檔會合並到與環境無關的專案檔中,以形成一組完整的建置指示。

建置和部署概觀

連絡人管理員解決方案中,三個檔案會控制建置和部署程式:

  • 通用 專案檔 (Publish.proj) 。 這包含不會在目的地環境之間變更的組建和部署指示。
  • 環境特定的專案檔 (Env-Dev.proj) 。 這包括特定目的地環境專屬的組建和部署設定。 例如,您可以使用 Env-Dev.proj 檔案來提供開發人員或測試環境的設定,並建立名為 Env-Stage.proj 的替代檔案來提供預備環境的設定。
  • (Publish-Dev.cmd) 命令檔。 這包含MSBuild.exe命令,指定您要執行的專案檔。 您可以為每個目的地環境建立命令檔,其中每個檔案都包含指定不同環境特定專案檔的MSBuild.exe命令。 這可讓開發人員只要執行適當的命令檔,即可部署到不同的環境。

在範例解決方案中,您可以在 [發佈方案] 資料夾中找到這三個檔案。

在範例解決方案中,您可以在 [發佈方案] 資料夾中找到三個檔案。

在更詳細地查看這些檔案之前,讓我們看看當您使用此方法時,整體建置程式的運作方式。 概括而言,建置和部署程式看起來像這樣:

建置和部署程式在高層級的外觀。

發生的第一件事是兩個專案檔,一個包含萬用群組建和部署指示,另一個包含環境特定設定,會合並成單一專案檔。 MSBuild 接著會透過專案檔中的指示運作。 它會使用每個專案的專案檔,在方案中建置每個專案。 然後它會呼叫其他工具,例如 Web Deploy (MSDeploy.exe) 和 VSDBCMD 公用程式,將 Web 內容和資料庫部署至目標環境。

從開始到完成,建置和部署程式會執行下列工作:

  1. 它會刪除輸出目錄的內容,以準備全新組建。

  2. 它會建置解決方案中的每個專案:

    1. 針對 Web 專案,在此案例中,ASP.NET MVC Web 應用程式和 WCF Web 服務—建置程式會為每個專案建立 Web 部署套件。
    2. 針對資料庫專案,建置程式會為每個專案建立部署資訊清單 (.deploymanifest 檔案) 。
  3. 它會使用VSDBCMD.exe公用程式在方案中部署每個資料庫專案,並使用專案檔中的各種屬性,也就是目標連接字串和資料庫名稱,以及 .deploymanifest 檔案。

  4. 它會使用MSDeploy.exe公用程式在方案中部署每個 Web 專案,並使用專案檔中的各種屬性來控制部署程式。

您可以使用範例解決方案來更詳細地追蹤此程式。

注意

如需如何為您自己的伺服器環境自訂環境特定專案檔的指引,請參閱 設定目標環境的部署屬性

叫用建置和部署程式

若要將連絡人管理員解決方案部署至開發人員測試環境,開發人員會執行 Publish-Dev.cmd 命令檔。 這會叫用MSBuild.exe,並將 Publish.proj 指定為要執行的專案檔,並將 Env-Dev.proj 指定為參數值。

msbuild.exe Publish.proj /fl /p:TargetEnvPropsFile=EnvConfig\Env-Dev.proj

注意

/fl參數 (/fileLogger的簡短) 會將組建輸出記錄到目前目錄中名為msbuild.log的檔案。 如需詳細資訊,請參閱 MSBuild 命令列參考

此時,MSBuild 會開始執行、載入 Publish.proj 檔案,並開始處理其中指示。 第一個指令會指示 MSBuild 匯入 TargetEnvPropsFile 參數指定的專案檔。

<Import Project="$(TargetEnvPropsFile)" />

TargetEnvPropsFile參數會指定Env-Dev.proj檔案,因此 MSBuild 會將Env-Dev.proj檔案的內容合併到Publish.proj檔案中。

MSBuild 在合併專案檔中遇到的下一個專案是屬性群組。 屬性會依照出現在檔案中的順序進行處理。 MSBuild 會為每個屬性建立索引鍵/值組,前提是符合任何指定的條件。 稍後在檔案中定義的屬性,將會以稍早在檔案中定義的相同名稱覆寫任何屬性。 例如,請考慮 OutputRoot 屬性。

<OutputRoot Condition=" '$(OutputRoot)'=='' ">..\Publish\Out\</OutputRoot>
<OutputRoot Condition=" '$(BuildingInTeamBuild)'=='true' ">$(OutDir)</OutputRoot>

當 MSBuild 處理第一個OutputRoot元素時,未提供類似的具名參數,它會將OutputRoot屬性的值設定為。\Publish\Out。遇到第二個OutputRoot元素時,如果條件評估為true,則會使用OutDir參數的值覆寫OutputRoot屬性值。

MSBuild 遇到的下一個專案是單一專案群組,其中包含名為 ProjectsToBuild的專案。

<ItemGroup>
   <ProjectsToBuild Include="$(SourceRoot)ContactManager-WCF.sln"/>
</ItemGroup>

MSBuild 會建置名為 ProjectsToBuild的專案清單,以處理此指令。 在此情況下,專案清單包含單一值,也就是方案檔的路徑和檔案名。

此時,其餘元素是目標。 目標處理方式與屬性和專案不同,基本上,除非使用者明確指定目標,或是由專案檔內的另一個建構叫用,否則不會處理目標。 回想一下,開啟 的 Project 標籤包含 DefaultTargets 屬性。

<Project ToolsVersion="4.0" 
         DefaultTargets="FullPublish" 
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

如果叫用MSBuild.exe時未指定目標,這會指示 MSBuild 叫用 FullPublish 目標。 FullPublish目標不包含任何工作;相反地,它只會指定相依性清單。

<PropertyGroup>   
  <FullPublishDependsOn>
     Clean;
     BuildProjects;      
     GatherPackagesForPublishing;
     PublishDbPackages;
     PublishWebPackages;
  </FullPublishDependsOn>
</PropertyGroup>
<Target Name="FullPublish" DependsOnTargets="$(FullPublishDependsOn)" />

此相依性會告訴 MSBuild,若要執行 FullPublish 目標,它必須依提供的順序叫用此目標清單:

  1. 它必須叫用 Clean 目標。
  2. 它必須叫用 BuildProjects 目標。
  3. 它必須叫用 GatherPackagesForPublishing 目標。
  4. 它必須叫用 PublishDbPackages 目標。
  5. 它必須叫用 PublishWebPackages 目標。

清除目標

Clean目標基本上會刪除輸出目錄及其所有內容,作為新組建的準備。

<Target Name="Clean" Condition=" '$(BuildingInTeamBuild)'!='true' ">
  <Message Text="Cleaning up the output directory [$(OutputRoot)]"/>
  <ItemGroup>
     <_FilesToDelete Include="$(OutputRoot)**\*"/>
  </ItemGroup>
  <Delete Files="@(_FilesToDelete)"/>
  <RemoveDir Directories="$(OutputRoot)"/>
</Target>

請注意,目標包含 ItemGroup 元素。 當您在 Target 元素內定義屬性或專案時,您會建立 動態 屬性和專案。 換句話說,在執行目標之前,不會處理屬性或專案。 在建置程式開始之前,輸出目錄可能不存在或包含任何檔案,因此您無法將 _FilesToDelete 清單建置為靜態專案;您必須等到執行正在進行。 因此,您會將清單建置為目標內的動態專案。

注意

在此情況下,由於 Clean 目標是第一個執行的目標,因此不需要實際使用動態專案群組。 不過,建議您在這種類型的案例中使用動態屬性和專案,因為您可能想要以不同順序在某個時間點執行目標。
您也應該避免宣告永遠不會使用的專案。 如果您有只供特定目標使用的專案,請考慮將它們放在目標內,以移除建置程式上任何不必要的額外負荷。

除了動態專案之外, Clean 目標相當簡單,而且會使用內建 的 MessageDeleteRemoveDir 工作:

  1. 將訊息傳送至記錄器。
  2. 建置要刪除的檔案清單。
  3. 刪除檔案。
  4. 移除輸出目錄。

BuildProjects 目標

BuildProjects目標基本上會建置範例解決方案中的所有專案。

<Target Name="BuildProjects" Condition=" '$(BuildingInTeamBuild)'!='true' ">
   <MSBuild Projects="@(ProjectsToBuild)"
            Properties="OutDir=$(OutputRoot);
                        Configuration=$(Configuration);
                        DeployOnBuild=true;
                        DeployTarget=Package"
            Targets="Build" />
  </Target>

在上一個主題 瞭解專案檔中,已詳細說明此目標,以說明工作和目標參考屬性和專案的方式。 此時,您主要對 MSBuild 工作感興趣。 您可以使用這項工作來建置多個專案。 工作不會建立新的實例MSBuild.exe;它會使用目前的執行實例來建置每個專案。 此範例中的重點在於部署屬性:

  • DeployOnBuild屬性會指示 MSBuild 在每個專案的組建完成時,在專案設定中執行任何部署指示。
  • DeployTarget屬性會識別您想要在建置專案之後叫用的目標。 在此情況下, 套件 目標會將專案輸出建置至可部署的 Web 套件。

注意

套件目標會叫用 Web 發佈管線 (WPP) ,以提供 MSBuild 與 Web Deploy 之間的整合。 如果您想要查看 WPP 提供的內建目標,請檢閱 %PROGRAMFILES (x86) %\MSBuild\Microsoft\VisualStudio\v10.0\Web 資料夾中的 Microsoft.Web.Publishing.targets 檔案。

GatherPackagesForPublishing 目標

如果您研究 GatherPackagesForPublishing 目標,您會發現它實際上不包含任何工作。 而是包含定義三個動態專案的單一專案群組。

<Target Name="GatherPackagesForPublishing">
   <ItemGroup>
      <PublishPackages 
         Include="$(_ContactManagerDest)ContactManager.Mvc.deploy.cmd">
         <WebPackage>true</WebPackage>
         <!-- More item metadata -->  
      </PublishPackages>
      <PublishPackages 
         Include="$(_ContactManagerSvcDest)ContactManager.Service.deploy.cmd">
         <WebPackage>true</WebPackage>
         <!-- More item metadata -->
      </PublishPackages>
      <DbPublishPackages Include="$(_DbDeployManifestPath)">
         <DbPackage>true</DbPackage>
         <!-- More item metadata -->
      </DbPublishPackages>
   </ItemGroup>
</Target>

這些專案是指執行 BuildProjects 目標時所建立的部署套件。 您無法在專案檔中以靜態方式定義這些專案,因為在執行 BuildProjects 目標之前,專案所參考的檔案不存在。 相反地,必須在執行 BuildProjects 目標之後,才會在目標內動態定義專案。

此目標內不會使用此專案—此目標只會建置專案和與每個專案值相關聯的中繼資料。 處理這些專案之後, PublishPackages 專案會包含兩個值: ContactManager.Mvc.deploy.cmd 檔案的路徑,以及 ContactManager.Service.deploy.cmd 檔案的路徑。 Web Deploy 會為每個專案建立這些檔案作為 Web 套件的一部分,而這些檔案是您必須在目的地伺服器上叫用的檔案,才能部署套件。 如果您開啟其中一個檔案,您基本上會看到具有各種組建特定參數值的MSDeploy.exe命令。

DbPublishPackages專案會包含單一值,也就是 ContactManager.Database.deploymanifest檔案的路徑。

注意

當您建置資料庫專案時,會產生 .deploymanifest 檔案,並使用與 MSBuild 專案檔相同的架構。 其中包含部署資料庫所需的所有資訊,包括資料庫架構的位置 (.dbschema) ,以及任何預先部署和部署後腳本的詳細資料。 如需詳細資訊,請參閱 資料庫建置和部署的概觀

您將深入瞭解如何建立及使用部署套件和資料庫部署資訊清單,以及 建置和封裝 Web 應用程式專案部署資料庫專案

PublishDbPackages 目標

簡言之, PublishDbPackages 目標會叫用 VSDBCMD 公用程式,以將 ContactManager 資料庫部署至目標環境。 設定資料庫部署牽涉到許多決策和細微差異,您將深入瞭解部署 資料庫專案自訂多個環境的資料庫部署。 在本主題中,我們將著重于此目標實際運作的方式。

首先,請注意開頭標記包含 Outputs 屬性。

<Target Name="PublishDbPackages" Outputs="%(DbPublishPackages.Identity)">

這是 目標批次處理的範例。 在 MSBuild 專案檔中,批次處理是逐一查看集合的技術。 Outputs屬性的值 「% (DbPublishPackages.Identity) 」,是指 DbPublishPackages專案清單的Identity中繼資料屬性。 此標記法,Outputs=% (ItemList.ItemMetadataName) 會轉譯為:

  • DbPublishPackages 中的專案分割成包含相同 身分識別 中繼資料值的批次專案。
  • 每個批次執行一次目標。

注意

身分識別 是其中一個 內建中繼資料值 ,會指派給建立時的每個專案。 它會參考Item元素中Include屬性的值,換句話說,是專案的路徑和檔案名。

在此案例中,由於不應該有多個具有相同路徑和檔案名的專案,因此我們基本上會使用一個批次大小。 每個資料庫封裝都會執行一次目標。

您可以在 _Cmd 屬性中看到類似的標記法,它會使用適當的參數來建置 VSDBCMD 命令。

<_Cmd>"$(VsdbCmdExe)" 
   /a:Deploy 
   /cs:"%(DbPublishPackages.DatabaseConnectionString)" 
   /p:TargetDatabase=%(DbPublishPackages.TargetDatabase)             
   /manifest:"%(DbPublishPackages.FullPath)" 
   /script:"$(_CmDbScriptPath)" 
   $(_DbDeployOrScript)
</_Cmd>

在此情況下, % (DbPublishPackages.DatabaseConnectionString) % (DbPublishPackages.TargetDatabase) % (DbPublishPackages.FullPath) 全都參考 DbPublishPackages 專案集合的中繼資料值。 Exec工作會使用_Cmd屬性,以叫用 命令。

<Exec Command="$(_Cmd)"/>

因此, Exec 工作會根據 DatabaseConnectionStringTargetDatabaseFullPath 中繼資料值的唯一組合來建立批次,而工作會針對每個批次執行一次。 這是 工作批次處理的範例。 不過,因為目標層級批次處理已經將專案集合分割成單一專案批次, 所以 Exec 工作會執行一次,而且只會針對目標的每個反復專案執行一次。 換句話說,此工作會針對解決方案中的每個資料庫套件叫用 VSDBCMD 公用程式一次。

注意

如需目標與工作批次處理的詳細資訊,請參閱 MSBuild 批次處理目標批次處理中的專案中繼資料,以及 工作批次處理中的專案中繼資料

PublishWebPackages 目標

此時,您已叫用 BuildProjects 目標,這會為範例解決方案中的每個專案產生 Web 部署套件。 隨附每個套件都是 deploy.cmd 檔案,其中包含將套件部署至目標環境所需的MSDeploy.exe命令,以及指定目標環境的必要詳細資料 SetParameters.xml 檔案。 您也叫用 CollectionPackagesForPublishing 目標,其會產生包含您感興趣的 deploy.cmd 檔案的專案集合。 基本上, PublishWebPackages 目標會執行下列函式:

  • 它會使用XmlPoke工作,操作每個套件的SetParameters.xml檔案,以包含目標環境的正確詳細資料。
  • 它會使用適當的參數,為每個套件叫用 deploy.cmd 檔案。

就像 PublishDbPackages 目標一樣, PublishWebPackages 目標會使用目標批次處理,以確保每個 Web 套件都會執行一次目標。

<Target Name="PublishWebPackages" Outputs="%(PublishPackages.Identity)">

在目標內, Exec 工作是用來執行每個 Web 套件的 deploy.cmd 檔案。

<PropertyGroup>
   <_Cmd>
      %(PublishPackages.FullPath) 
      $(_WhatifSwitch) 
      /M:$(MSDeployComputerName) 
      %(PublishPackages.AdditionalMSDeployParameters)
   </_Cmd>
</PropertyGroup>
<Exec Command="$(_Cmd)"/>

如需設定 Web 套件部署的詳細資訊,請參閱 建置和封裝 Web 應用程式專案

結論

本主題提供的逐步解說說明如何使用分割專案檔來控制連絡人管理員範例解決方案的建置和部署程式。 使用此方法可讓您在單一、可重複的步驟中執行複雜的企業級部署,只要執行環境特定的命令檔案即可。

深入閱讀

如需專案檔和 WPP 的更深入簡介,請參閱在Microsoft Build Engine內:使用 MSBuild 和 Team Foundation Build by Sayed Ibimiim Hashimi 和 William Bartholomew,ISBN:978-0-7356-4524-0。