文章出處

MSBuild 是 Microsoft 和 Visual Studio的生成系統。它不僅僅是一個構造工具,應該稱之為擁有相當強大擴展能力的自動化平臺。MSBuild平臺的主要涉及到三部分:執行引擎、構造工程、任務。其中最核心的就是執行引擎,它包括定義構造工程的規范,解釋構造工程,執行“構造動作”;構造工程是用來描述構造任務的,大多數情況下我們使用MSBuild就是遵循規范,編寫一個構造工程;MSBuild引擎執行的每一個“構造動作”就是通過任務實現的,任務就是MSBuild的擴展機制,通過編寫新的任務就能夠不斷擴充MSBuild的執行能力。所以這三部分分別代表了引擎、腳本和擴展能力。

構造工程(腳本文件)
先說說構造工程,只要通過Notepad打開任何一個Visual Studio下的C#工程(csproj)文件,就知道構造工程到底是怎么回事了。

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Root>$(MSBuildStartupDirectory)</Root>
  </PropertyGroup>
  <Target Name="Build">
    <!-- Compile -->
    <ItemGroup> 
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Common\Gimela.Common.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Infrastructure\Gimela.Infrastructure.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Management\Gimela.Management.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Security\Gimela.Security.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Tasks\Gimela.Tasks.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Text\Gimela.Text.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Net\Gimela.Net.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\ServiceModel\Gimela.ServiceModel.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Data\Gimela.Data.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Presentation\Gimela.Presentation.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Media\Gimela.Media.sln" />
      <ProjectToBuild Include="$(Root)\..\src\Foundation\Streaming\Gimela.Streaming.sln" />   
      <ProjectToBuild Include="$(Root)\..\src\Crust\Gimela.Crust.sln" />      
    </ItemGroup>
    <MSBuild Projects="@(ProjectToBuild)" Targets="Build" Properties="Configuration=Debug;">
      <Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects" />
    </MSBuild>
    
  </Target>
</Project>

在構造工程中我們可以定義和使用變量(通過Property/PropertyGourp/Item/ItemGroup等元素),可以使用條件分支(通過Choose/When/Otherwise等元素)、能夠在運行時給變量賦值(通過執行任務,獲取其返回類型參數的方式)、能夠定義執行塊(通過Target元素,相當于函數)、能夠進行異常處理(通過OnError元素)、還可以復用已有工程定義的內容(通過Import元素)。擁有這些能力和高級語言已經相差無幾了,所以筆者認為構造工程不是描述性語言,而是腳本語言。

這里還需要強調一點的是,項目級元素(Property)可以在元素下定義,也可以在構造過程中作為外部參數傳入,這是一個非常有用的特性,一般編譯時選擇配置項(Debug或者Release)就是利用這個特性實現的。

Project元素
這是每一個項目文件的最外層元素,它表示了一個項目的范圍。如果缺少了這一元素,MSBuild會報錯稱Target元素無法識別或不被支持。
Project元素擁有多個屬性,其中最常用到的是DefaultTargets屬性。我們都知道,在一個項目的生成過程中可能需要完成幾項不同的任務(比如編譯、單元測試、check-in到源代碼控制服務器中等),其中每一項任務都可以用Target來表示。對于擁有多個Target的項目,你可以通過設置Project的DefaultTargets(注意是復數)屬性來指定需要運行哪(幾)個Target,如果沒有這個設置,MSBuild將只運行排在最前面的那個Target。

Property元素

在項目中你肯定需要經常訪問一些信息,例如需要創建的路徑名、最終生成的程序集名稱等。以name/value的形式添加進Property,隨后就可以以$(PropertyName)的形式訪問。這樣你就無須為了改動一個文件名稱而讓整個項目文件傷筋動骨了。比如上面代碼中的Bin就是將要創建的路徑名稱,而AssemblyName則是最終要生成的程序集名稱。這些屬性的名稱不是固定的,你完全可以按自己的習慣來進行命名。在使用時,你需要把屬性名稱放在”$(“和”)”對內(不包括引號),以表示這里將被替換成一個Property元素的值。
另外,如果Property元素數量比較多,你還可以把它們分門別類地放在不同的PropertyGroup里,以提高代碼的可閱讀性。這對Property本身沒有任何影響。

 <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.30703</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{6C2561FB-4405-408F-B41B-ACE5E519A26E}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Gimela.Infrastructure.Patterns</RootNamespace>
    <AssemblyName>Gimela.Infrastructure.Patterns</AssemblyName>
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>

Item元素
在整個項目文件中你肯定要提供一些可被引用的輸入性資源(inputs)信息,比如源代碼文件、引用的程序集名稱、需要嵌入的圖標資源等。它們應該被放在Item里,以便隨時引用。語法是:<Item Type=”TheType”Include=”NameOrPath” />

其中Type屬性可以被看作是資源的類別名稱,比如對于.cs源文件,你可以把它們的Type都設置為Source,對于引用的程序集把Type都設置為Reference,這樣在隨后想引用這一類別的資源時只要引用這個Type就可以了,方法是@(TypeName)。可千萬別和Property的引用方法弄混了。
既然Type是資源的類名,那么Include就是具體的資源名稱了,比如在上面的示例代碼中,Include引用的就是C#源代碼文件的名稱。你也可以用使用通配符*來擴大引用范圍。比如下面這行代碼就指定了當前目錄下的所有C#文件都可以通過@(Source)來引用:

<Item Type=”Source” Include=”*.cs” />

另外,你也可以通過與PropertyGroup類似的方法把相關的Item放在ItemGroup里。

  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Data" />
    <Reference Include="System.ServiceModel" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Commands\CommandBase.cs" />
    <Compile Include="Commands\DuplexCommandBase.cs" />
    <Compile Include="Commands\ICommand.cs" />
    <Compile Include="Commands\IDuplexCommand.cs" />
    <Compile Include="Extensions\BitConverterExtensions.cs" />
    <Compile Include="Extensions\ConcurrentDictionaryExtensions.cs" />
    <Compile Include="Extensions\StopwatchExtensions.cs" />
    <Compile Include="Extensions\TimeSpanExtensions.cs" />
    <Compile Include="Flyweight\FlyweightObjectPool.cs" />
    <Compile Include="Singleton\StaticSingleton.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="SmartQueue\ISmartQueueMapper.cs" />
    <Compile Include="SmartQueue\SmartQueue.cs" />
    <Compile Include="SmartQueue\SmartQueueBase.cs" />
    <Compile Include="SmartQueue\SmartQueueMapper.cs" />
    <Compile Include="UnitOfWork\IUnitOfWork.cs" />
    <Compile Include="UnitOfWork\IUnitOfWorkFactory.cs" />
    <Compile Include="UnitOfWork\UnitOfWork.cs" />
    <Compile Include="WeakActions\IWeakActionExecuteWithObject.cs" />
    <Compile Include="WeakActions\WeakAction.cs" />
    <Compile Include="WeakActions\WeakActionGeneric.cs" />
    <Compile Include="WeakFuncs\IWeakFuncExecuteWithObjectAndResult.cs" />
    <Compile Include="WeakFuncs\WeakFunc.cs" />
    <Compile Include="WeakFuncs\WeakFuncGeneric.cs" />
  </ItemGroup>

Target元素
Target表示一個需要完成的虛擬的任務單元。每個Project可以包括一個或多個Target,從而完成一系列定制的任務。你需要給每個Target設置一個Name屬性(同一Project下的兩個Target不能擁有同樣的Name)以便引用和區別。

舉例來說,在你的項目生成過程中可能需要完成三個階段的任務:首先check-out源代碼,接下來編譯這些代碼并執行單元測試,最后把它們check-in。那么通常情況下你可以創建三個不同的Target以清晰劃分三個不同的階段:

<Target Name=”CheckOut” ></Target>

<Target Name=”Build”  DependsOnTargets=”CheckOut”> <Task Name=”Build”

.../> <Task Name=”UnitTest” ... />

</Target>

<Target Name=”CheckIn” DependsOnTargets=”CheckOut;Build”> 

</Target>

這樣,你就可以非常清晰地控制整個生成過程。為了反應不同Target之間的依賴關系(只有Check-in后才能編譯,只有編譯完成才可能Check-out……),你需要設置Target的DependsOnTargets屬性(注意是復數),以表示僅當這些Target執行完成之后才能執行當前的Target。當MSBuild引擎開始執行某項Target時(別忘了Project的DefaultTargets屬性),會自動檢測它所依賴的那些Target是否已經執行完成,從而避免因為某個生成環節缺失而導致整個生成過程發生意外。
你可以通過Project的DefaultTargets屬性指定MSBuild引擎從哪(幾)個Target開始執行,也可以在調用MSBuild.exe時使用t開關來手動指定將要運行的Target,方法如下:
MSBuild /t:CheckOut 這樣,只有CheckOut(以及它所依賴的Target,在上文中沒有)會被執行。

Task元素
這可能是整個項目文件中最重要的,因為它才是真正可執行的部分(這也是為什么我在上面說Target是虛擬的)。你可以在Target下面放置多個Task來順序地執行相應的任務。

 

相關文檔


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()