[转]MSBuild入门
如果你和我一樣一直都在用NAnt管理生成過(guò)程的話,那么你一定會(huì)高度關(guān)注MSBuild。原因很簡(jiǎn)單,因?yàn)樗鼘儆谖④?#xff0c;你可以不喜歡它,但你一定要學(xué)會(huì)用它。
?
在熬過(guò)了幾個(gè)晚上以后,我終于讓自己適應(yīng)了MSBuild的語(yǔ)法。這可真不容易,特別是當(dāng)自己已經(jīng)習(xí)慣了NAnt的小寫規(guī)范之后。不過(guò)這不成問(wèn)題,因 為隨著自己對(duì)MSBuild的理解一點(diǎn)點(diǎn)加深,自己還真的喜歡上它了。
?
好吧,下面就讓我來(lái)簡(jiǎn)單地介紹一下我在學(xué)習(xí)MSBuild使用過(guò)程中的一點(diǎn)經(jīng)驗(yàn)。如果你還在MSBuild的大門外徘徊,那么希望這篇東西能帶你進(jìn)入那扇門。
?
準(zhǔn)備工作
首先要提到的是有關(guān)如何使用MSBuild的一些重要資源。它們是:
?
1. Alex Kipman的MSDNTV Show:
http://msdn.microsoft.com/msdntv/episode.aspx?xml=episodes/en/20040122VSNETAK/manifest.xml
2.?Alex Kipman和Rajeev Goel在PDC2003上的演講:
http://microsoft.sitestream.com/PDC2003/TLS/TLS347.htm
上面這兩項(xiàng)出自MSBuild開發(fā)組的Alex Kipman,從理論上說(shuō)他應(yīng)該是了解MSBuild的第一人,他給出的幾個(gè)演示的確給了我非常大的幫助。(不過(guò)我非常不喜歡他的聲音,又尖又細(xì)。)
?
3. MSBuild Doc
http://msdn.microsoft.com/longhorn/toolsamp/default.aspx
這是最重要的,其中包括Alex Kipman主筆的五份重要文檔:MSBuildFileFormat、MSBuildWalkthrough、MSBuildTasks、HowToWriteATask以及MSBuildCommandLine。這 可能是目前情況下外界能獲得的有關(guān)MSBuild最詳細(xì)的文檔。
?
?
Demo
好了,一切準(zhǔn)備工作就緒,讓我們以一個(gè)簡(jiǎn)單的示例開始吧。
?
首先寫一個(gè)簡(jiǎn)單的C# Console程序(你也可以把它改成VB.NET):
?
// HelloMSBuild.cs
using System;
?
class HelloMSBuild
?{
????? public static void Main()
????? {
????????? Console.WriteLine("Hello MSBuild!");
????? }
?}
?
下面我們就要寫一個(gè).csproj文件來(lái)控制整個(gè)生成過(guò)程。值得注意的是,如果在調(diào)用MSBuild.exe時(shí)沒(méi)有指定具體的項(xiàng)目 文件,MSBuild引擎會(huì)在當(dāng)前目錄下查找一個(gè)名為*.*proj的項(xiàng)目文件。如果你在同一目錄中寫了多個(gè)這樣的項(xiàng)目文件,那么需要手動(dòng)指定MSBuild.exe的目標(biāo)文件,方法 是:
?
MSBuild a.csproj
?
否則MSBuild會(huì)提示出錯(cuò),要求你手動(dòng)指定目標(biāo)項(xiàng)目文件。
?
以下是項(xiàng)目文件:
?
<!-- ?Build.csproj -->
<Project DefaultTargets="Run">
???????
??????? <Property Bin="bin" />
??????? <Property OutputAssembly="HelloMSBuild" />
?
??????? <Item Type="Source" Include="HelloMSBuild.cs" />
?
??????? <Target Name="Build">
??????????????? <Task?? Name="MakeDir"
??????????????????????? Directories="$(Bin)"
????????????? ??????????Condition="!Exists('$(Bin)')" />
??????????????? <Task?? Name="Csc"
??????????????????????? Sources="@(Source)"
??????????????????????? TargetType="exe"
??????????????????????? OutputAssembly="$(Bin)\$(OutputAssembly).exe" />
??????? </Target>
?
??????? <Target Name="Run" DependsOnTargets="Build">
??????????????? <Task?? Name="Exec"
??????????????????????? Command="$(Bin)\$(OutputAssembly).exe" />
??????? </Target>
</Project>
?
如果你此前沒(méi)有過(guò)NAnt的開發(fā)經(jīng)驗(yàn),那么上面這些東西肯定看起來(lái)挺嚇人。這個(gè)時(shí)候最好的辦法是打開那篇MSBuildFileFormat,對(duì)照 上面代碼查找相應(yīng)的項(xiàng)目元素的含義。下面我對(duì)其中重要的項(xiàng)目元素進(jìn)行一下解釋。
?
1.???????? Project元素。這是每一個(gè)項(xiàng)目文件的 最外層元素,它表示了一個(gè)項(xiàng)目的范圍。如果缺少了這一元素,MSBuild會(huì)報(bào)錯(cuò)稱Target元素?zé)o法識(shí)別或不被支持。
?
Project元素?fù)碛卸鄠€(gè)屬性,其中最常用 到的是DefaultTargets屬性。我們都知道,在一個(gè)項(xiàng)目的生成過(guò)程中可能需要完成幾項(xiàng)不同的任務(wù)(比如編譯、單元測(cè)試、check-in到源代碼控制服務(wù)器中 等),其中每一項(xiàng)任務(wù)都可以用Target來(lái)表示。對(duì)于擁有多個(gè)Target的項(xiàng)目,你可以通過(guò)設(shè)置Project的DefaultTargets(注意是復(fù)數(shù))屬性來(lái)指定需要運(yùn)行哪(幾)個(gè)Target,比如:
?
<Project DefaultTargets=”Build” >
...
?
或者:
?
<Project DefaultTargets=”Build;Test;Run” >
...
?
如果沒(méi)有這個(gè)設(shè)置,MSBuild將只運(yùn)行排在最前面的那個(gè)Target。
?
2.???????? Property元素。在項(xiàng)目中你肯定需要 經(jīng)常訪問(wèn)一些信息,例如需要?jiǎng)?chuàng)建的路徑名、最終生成的程序集名稱等。這些信息你最好別hard code進(jìn)項(xiàng)目中,除非你一次寫過(guò)之后永不更改。這時(shí)Property就能派上用場(chǎng)了。你把上面 提到的那些信息以name/value的形式添加進(jìn)Property,隨后就可以以$(PropertyName)的形式訪問(wèn)。這樣你就無(wú)須為了改動(dòng)一個(gè)文件名稱而讓整個(gè)項(xiàng)目文件傷筋動(dòng)骨了。比如上面代碼中的Bin就是將要?jiǎng)?chuàng)建的路徑名稱,而AssemblyName則是最終要生成的 程序集名稱。這些屬性的名稱不是固定的,你完全可以按自己的習(xí)慣來(lái)進(jìn)行命名。在使用時(shí),你需要把屬性名稱放在”$(“和”)”對(duì)內(nèi)(不包括引號(hào)),以表示這里將被替 換成一個(gè)Property元素的值。
?
另外,如果Property元素?cái)?shù)量比較多,你還可以把它們分門別類地放在不同的PropertyGroup里,以提高代碼的 可閱讀性。這對(duì)Property本身沒(méi)有任何影響。比如:
?
<PropertyGroup>
?????? <Property ... />
?????? <Property ... />
</PropertyGroup>
?
3.???????? Item元素。在整個(gè)項(xiàng)目文件中你肯定要提 供一些可被引用的輸入性資源(inputs)信息,比如源代碼文件、引用的程序集名稱、需要嵌入的圖標(biāo)資源等。它們應(yīng)該被放在Item里,以便隨時(shí)引用。語(yǔ)法是:
?
<Item Type=”TheType” Include=”NameOrPath” />
?
其中Type屬性可以被看作是資源的類別名稱,比如對(duì)于.cs源文件,你可以把它們的Type都設(shè)置為Source,對(duì)于引用的程序集把Type都設(shè)置為Reference,這樣在隨后想引用這一類別的資源時(shí)只要引用這個(gè)Type就可以了,方法是@(TypeName)。可千萬(wàn)別和Property的引用方法弄混了。
?
既然Type是資源的類名,那么Include就是具體的資源名稱了,比如在上面的示例代碼中,Include引用的就是C#源代碼文件的名稱。你也可以用使用通配 符*來(lái)擴(kuò)大引用范 圍。比如下面這行代碼就指定了當(dāng)前目錄下的所有C#文件都可以通過(guò)@(Source)來(lái)引用:
?
<Item Type=”Source” Include=”*.cs” />
?
另外,你也可以通過(guò)與PropertyGroup類似的方法把相關(guān)的Item放在ItemGroup里。
?
4.???????? Target元素。上面已經(jīng)提到了,Target表示一個(gè)需要完成的虛擬的任務(wù) 單元。每個(gè)Project可以包括一個(gè)或多個(gè)Target,從而完成一系列定制的任務(wù)。你需要給每個(gè)Target設(shè)置一個(gè)Name屬性(同一Project下的兩個(gè)Target不能擁有同樣的Name)以便引用和區(qū)別。
?
舉例來(lái)說(shuō),在你的項(xiàng)目生成過(guò)程中可能需要完成三個(gè)階段的任務(wù):首先從VSS中check-out源代碼,接下來(lái)編譯這些代 碼并執(zhí)行單元測(cè)試,最后把它們check-in回VSS。那么通常情況下你可以創(chuàng)建三個(gè)不同的Target以清晰劃分三個(gè)不同的階段:
?
<Target Name=”CheckOut” >
...
</Target>
?
<Target Name=”Build” DependsOnTargets=”CheckOut”>
?????? <Task???? Name=”Build” .../>
?????? <Task???? Name=”UnitTest” ... />
</Target>
?
<Target Name=”CheckIn” DependsOnTargets=”CheckOut;Build”>
...
</Target>
?
這樣,你就可以非常清晰地控制整個(gè)生成過(guò)程。為了反應(yīng)不同Target之間的依賴關(guān)系(只有Check-in后才能編譯,只有編譯完成 才可能Check-out……),你需要設(shè)置Target的DependsOnTargets屬性(注意是復(fù)數(shù)),以表示僅當(dāng)這些Target執(zhí)行完成之后才能執(zhí)行當(dāng)前的Target。當(dāng)MSBuild引擎開始執(zhí)行某項(xiàng)Target時(shí)(別忘了Project的DefaultTargets屬性),會(huì)自動(dòng)檢測(cè)它所依賴的那些Target是否已經(jīng)執(zhí)行完成,從而避免因?yàn)槟硞€(gè)生成環(huán)節(jié)缺失而導(dǎo)致整個(gè)生成過(guò)程發(fā)生意外。
?
你可以通過(guò)Project的DefaultTargets屬性指定MSBuild引擎從哪(幾)個(gè)Target開始執(zhí)行,也可以在調(diào)用MSBuild.exe時(shí)使用t開關(guān)來(lái)手動(dòng)指定將要運(yùn)行的Target,方法如下:
?
MSBuild /t:CheckOut
?
這樣,只有CheckOut(以及它所依賴的Target,在上文中沒(méi)有)會(huì)被執(zhí)行。
?
5.???????? Task元素。這可能是整個(gè)項(xiàng)目文件中最重 要的,因?yàn)樗攀钦嬲蓤?zhí)行的部分(這也是為什么我在上面說(shuō)Target是虛擬的)。你可以在Target下面放置多個(gè)Task來(lái)順序地執(zhí)行相應(yīng)的任務(wù),比如我在上面示例代碼中就在兩個(gè)不同的Target中安排了MakeDir、Csc和Exec三個(gè)不同的Task。這些Task通過(guò)Name屬性來(lái)相互區(qū)分,并各自擁有不同的其 它屬性來(lái)完成不同的任務(wù),比如Csc有Sources(源代碼文件)、TargetType(目標(biāo)類型)、OutputAssembly(生成程序集名稱)等屬性,而MakeDir則只需設(shè)置Directories(需要?jiǎng)?chuàng)建的路徑名稱列表)即可。
?
也許你會(huì)奇怪這些Task的名稱和屬性從哪里來(lái)。好吧,請(qǐng)用文本編譯器打開%windir%\Microsoft.NET\Framework\v1.2.30703\Microsoft.BuildTasks文件,看到了嗎?默認(rèn)情況下里面應(yīng)該是這樣的(不同的版本可能會(huì)有細(xì)微差別):
?
<!-- This file lists all the tasks that ship by default with MSBuild -->
<DefaultTasks>
??? <UsingTask TaskName="Microsoft.Build.Tasks.Csc" AssemblyName="MSBuildTasks"/>
??? <UsingTask TaskName="Microsoft.Build.Tasks.MSBuild" AssemblyName="MSBuildTasks"/>
??? <UsingTask TaskName="Microsoft.Build.Tasks.Exec" AssemblyName="MSBuildTasks"/>
??? <UsingTask TaskName="Microsoft.Build.Tasks.Vbc" AssemblyName="MSBuildTasks"/>
??? <UsingTask TaskName="Microsoft.Build.Tasks.MakeDir" AssemblyName="MSBuildTasks"/>???
???<UsingTask TaskName="Microsoft.Build.Tasks.ResGen" AssemblyName="MSBuildTasks"/>???
??? <UsingTask TaskName="Microsoft.Build.Tasks.Copy" AssemblyName="MSBuildTasks"/>???
??? <UsingTask TaskName="Microsoft.Build.Tasks.NetAssemblyResolver" AssemblyName="MSBuildTasks"/>???
??? <UsingTask TaskName="Microsoft.Build.Tasks.TransformPath" AssemblyName="MSBuildTasks"/>?????
</DefaultTasks>
?
你會(huì)注意到,在DefaultTasks元素下面排列的全是UsingTask,其中指明每一個(gè)Task的TaskName(名稱)和AssemblyName(程序集)。比如說(shuō)第一個(gè)UsingTask就對(duì)應(yīng)著我們上面用過(guò)的Csc任務(wù),它的完整名稱(namespace+class)是Microsoft.Build.Tasks.Csc,位于MSBuildTasks.dll程序集中(請(qǐng)?jiān)谕荒夸浵麓_認(rèn)這一.dll文件的存在)。這樣,MSBuild引擎在遇到對(duì)Csc任務(wù)的調(diào)用時(shí)就會(huì)通過(guò)這里的注冊(cè)信息來(lái)確定Csc所在的程序集,從而最終運(yùn)行相應(yīng)的托管代碼。這樣,如果你自己也寫了不同的Task,請(qǐng)按同樣的方式對(duì)它進(jìn)行注冊(cè)以便 使用。如果你引用了一個(gè)還沒(méi)有注冊(cè)的Target,那么MSBuild引擎將無(wú)法找到它的存在而導(dǎo)致生成失敗。
?
當(dāng)然,MSBuild Task的注冊(cè)方式不止以上一種。以上注冊(cè)方法的影響范圍是全局,你可以在每一個(gè)Project里應(yīng)用上面注冊(cè)的那些Task。但你也可以選擇在Project范圍內(nèi)注冊(cè)Task,這將對(duì)應(yīng)著另外一種略有不同的方 法。我會(huì)在后面的一篇文章里給出具體介紹。在這里,你只需明白你所需要的Task在哪里找到,而它們的具體用法可以通過(guò)參考MSBuildTasks一文來(lái)獲得,在這里我就不細(xì)說(shuō)了。
?
OK,介紹了一長(zhǎng)串,還是快點(diǎn)把我們的Build.csproj運(yùn)行起來(lái)吧。請(qǐng)?jiān)?/span>shell的同一目錄下輸入以下命令:
?
MSBuild
?
或者:
?
MSBuild Build.csproj
?
運(yùn)行結(jié)果如下:
?
d:\Dev\MyMSBuildDemo>msbuild Build.csproj
msbuild Build.csproj
Microsoft (R) .NET Build Engine version 1.2.30703.4
[Microsoft .Net Framework, Version 1.2.30703.4]???????
Copyright (C) Microsoft Corporation 2003. All rights reserved.
?
Target "Build" in project "Build.csproj"
?? Task "MakeDir"
????? Creating directory "bin".
?? Task "Csc"
????? Csc.exe /out:"bin\HelloMSBuild.exe" /target:exe "HelloMSBuild.cs"
?
Target "Run" in project "Build.csproj"
?? Task "Exec"
????? Hello MSBuild!
?
可見,在Build.csproj指定的兩個(gè)Target和三個(gè)Task均按相應(yīng)的順序依次運(yùn)行,在Csc執(zhí)行時(shí)MSBuild還顯示出了當(dāng)前執(zhí)行的具體命令,而在原來(lái)的Visual Studio .NET年代,你是無(wú)法獲知當(dāng)前正在執(zhí)行的編譯命令是什 么(據(jù)Alex Kipman稱,連Visual Studio .NET自己也不知道正在執(zhí)行的具體命令,因?yàn)槟切┟钜呀?jīng)被hard code進(jìn)了“黑盒子”,根本無(wú)法 提取)。
?
好了,一個(gè)簡(jiǎn)單的MSBuild文件用法示例就到這兒了。如果你此前還沒(méi)接觸過(guò)MSBuild或者NAnt,那么希望這篇文章能讓你對(duì)MSBuild的用法有個(gè)初步的了解。還有 很多的細(xì)節(jié)我在文中沒(méi)有涉及,如果你感興趣的話就請(qǐng)下載前面我提到的那些MSBuild文檔來(lái)自己研究吧。我會(huì)在下一篇文章里介紹如何開發(fā)自己的MSBuild Task。
?
--------------------------------------------------------------------------
musicland
http://blog.joycode.com/musicland
如果你和我一樣一直都在用NAnt管理生成過(guò)程的話,那么你一定會(huì)高度關(guān)注MSBuild。原因很簡(jiǎn)單,因?yàn)樗鼘儆谖④?#xff0c;你可以不喜歡它,但你一定要學(xué)會(huì)用它。
?
在熬過(guò)了幾個(gè)晚上以后,我終于讓自己適應(yīng)了MSBuild的語(yǔ)法。這可真不容易,特別是當(dāng)自己已經(jīng)習(xí)慣了NAnt的小寫規(guī)范之后。不過(guò)這不成問(wèn)題,因 為隨著自己對(duì)MSBuild的理解一點(diǎn)點(diǎn)加深,自己還真的喜歡上它了。
?
好吧,下面就讓我來(lái)簡(jiǎn)單地介紹一下我在學(xué)習(xí)MSBuild使用過(guò)程中的一點(diǎn)經(jīng)驗(yàn)。如果你還在MSBuild的大門外徘徊,那么希望這篇東西能帶你進(jìn)入那扇門。
?
準(zhǔn)備工作
首先要提到的是有關(guān)如何使用MSBuild的一些重要資源。它們是:
?
1. Alex Kipman的MSDNTV Show:
http://msdn.microsoft.com/msdntv/episode.aspx?xml=episodes/en/20040122VSNETAK/manifest.xml
2.?Alex Kipman和Rajeev Goel在PDC2003上的演講:
http://microsoft.sitestream.com/PDC2003/TLS/TLS347.htm
上面這兩項(xiàng)出自MSBuild開發(fā)組的Alex Kipman,從理論上說(shuō)他應(yīng)該是了解MSBuild的第一人,他給出的幾個(gè)演示的確給了我非常大的幫助。(不過(guò)我非常不喜歡他的聲音,又尖又細(xì)。)
?
3. MSBuild Doc
http://msdn.microsoft.com/longhorn/toolsamp/default.aspx
這是最重要的,其中包括Alex Kipman主筆的五份重要文檔:MSBuildFileFormat、MSBuildWalkthrough、MSBuildTasks、HowToWriteATask以及MSBuildCommandLine。這 可能是目前情況下外界能獲得的有關(guān)MSBuild最詳細(xì)的文檔。
?
?
Demo
好了,一切準(zhǔn)備工作就緒,讓我們以一個(gè)簡(jiǎn)單的示例開始吧。
?
首先寫一個(gè)簡(jiǎn)單的C# Console程序(你也可以把它改成VB.NET):
?
// HelloMSBuild.cs
using System;
?
class HelloMSBuild
?{
????? public static void Main()
????? {
????????? Console.WriteLine("Hello MSBuild!");
????? }
?}
?
下面我們就要寫一個(gè).csproj文件來(lái)控制整個(gè)生成過(guò)程。值得注意的是,如果在調(diào)用MSBuild.exe時(shí)沒(méi)有指定具體的項(xiàng)目 文件,MSBuild引擎會(huì)在當(dāng)前目錄下查找一個(gè)名為*.*proj的項(xiàng)目文件。如果你在同一目錄中寫了多個(gè)這樣的項(xiàng)目文件,那么需要手動(dòng)指定MSBuild.exe的目標(biāo)文件,方法 是:
?
MSBuild a.csproj
?
否則MSBuild會(huì)提示出錯(cuò),要求你手動(dòng)指定目標(biāo)項(xiàng)目文件。
?
以下是項(xiàng)目文件:
?
<!-- ?Build.csproj -->
<Project DefaultTargets="Run">
???????
??????? <Property Bin="bin" />
??????? <Property OutputAssembly="HelloMSBuild" />
?
??????? <Item Type="Source" Include="HelloMSBuild.cs" />
?
??????? <Target Name="Build">
??????????????? <Task?? Name="MakeDir"
??????????????????????? Directories="$(Bin)"
????????????? ??????????Condition="!Exists('$(Bin)')" />
??????????????? <Task?? Name="Csc"
??????????????????????? Sources="@(Source)"
??????????????????????? TargetType="exe"
??????????????????????? OutputAssembly="$(Bin)\$(OutputAssembly).exe" />
??????? </Target>
?
??????? <Target Name="Run" DependsOnTargets="Build">
??????????????? <Task?? Name="Exec"
??????????????????????? Command="$(Bin)\$(OutputAssembly).exe" />
??????? </Target>
</Project>
?
如果你此前沒(méi)有過(guò)NAnt的開發(fā)經(jīng)驗(yàn),那么上面這些東西肯定看起來(lái)挺嚇人。這個(gè)時(shí)候最好的辦法是打開那篇MSBuildFileFormat,對(duì)照 上面代碼查找相應(yīng)的項(xiàng)目元素的含義。下面我對(duì)其中重要的項(xiàng)目元素進(jìn)行一下解釋。
?
1.???????? Project元素。這是每一個(gè)項(xiàng)目文件的 最外層元素,它表示了一個(gè)項(xiàng)目的范圍。如果缺少了這一元素,MSBuild會(huì)報(bào)錯(cuò)稱Target元素?zé)o法識(shí)別或不被支持。
?
Project元素?fù)碛卸鄠€(gè)屬性,其中最常用 到的是DefaultTargets屬性。我們都知道,在一個(gè)項(xiàng)目的生成過(guò)程中可能需要完成幾項(xiàng)不同的任務(wù)(比如編譯、單元測(cè)試、check-in到源代碼控制服務(wù)器中 等),其中每一項(xiàng)任務(wù)都可以用Target來(lái)表示。對(duì)于擁有多個(gè)Target的項(xiàng)目,你可以通過(guò)設(shè)置Project的DefaultTargets(注意是復(fù)數(shù))屬性來(lái)指定需要運(yùn)行哪(幾)個(gè)Target,比如:
?
<Project DefaultTargets=”Build” >
...
?
或者:
?
<Project DefaultTargets=”Build;Test;Run” >
...
?
如果沒(méi)有這個(gè)設(shè)置,MSBuild將只運(yùn)行排在最前面的那個(gè)Target。
?
2.???????? Property元素。在項(xiàng)目中你肯定需要 經(jīng)常訪問(wèn)一些信息,例如需要?jiǎng)?chuàng)建的路徑名、最終生成的程序集名稱等。這些信息你最好別hard code進(jìn)項(xiàng)目中,除非你一次寫過(guò)之后永不更改。這時(shí)Property就能派上用場(chǎng)了。你把上面 提到的那些信息以name/value的形式添加進(jìn)Property,隨后就可以以$(PropertyName)的形式訪問(wèn)。這樣你就無(wú)須為了改動(dòng)一個(gè)文件名稱而讓整個(gè)項(xiàng)目文件傷筋動(dòng)骨了。比如上面代碼中的Bin就是將要?jiǎng)?chuàng)建的路徑名稱,而AssemblyName則是最終要生成的 程序集名稱。這些屬性的名稱不是固定的,你完全可以按自己的習(xí)慣來(lái)進(jìn)行命名。在使用時(shí),你需要把屬性名稱放在”$(“和”)”對(duì)內(nèi)(不包括引號(hào)),以表示這里將被替 換成一個(gè)Property元素的值。
?
另外,如果Property元素?cái)?shù)量比較多,你還可以把它們分門別類地放在不同的PropertyGroup里,以提高代碼的 可閱讀性。這對(duì)Property本身沒(méi)有任何影響。比如:
?
<PropertyGroup>
?????? <Property ... />
?????? <Property ... />
</PropertyGroup>
?
3.???????? Item元素。在整個(gè)項(xiàng)目文件中你肯定要提 供一些可被引用的輸入性資源(inputs)信息,比如源代碼文件、引用的程序集名稱、需要嵌入的圖標(biāo)資源等。它們應(yīng)該被放在Item里,以便隨時(shí)引用。語(yǔ)法是:
?
<Item Type=”TheType” Include=”NameOrPath” />
?
其中Type屬性可以被看作是資源的類別名稱,比如對(duì)于.cs源文件,你可以把它們的Type都設(shè)置為Source,對(duì)于引用的程序集把Type都設(shè)置為Reference,這樣在隨后想引用這一類別的資源時(shí)只要引用這個(gè)Type就可以了,方法是@(TypeName)。可千萬(wàn)別和Property的引用方法弄混了。
?
既然Type是資源的類名,那么Include就是具體的資源名稱了,比如在上面的示例代碼中,Include引用的就是C#源代碼文件的名稱。你也可以用使用通配 符*來(lái)擴(kuò)大引用范 圍。比如下面這行代碼就指定了當(dāng)前目錄下的所有C#文件都可以通過(guò)@(Source)來(lái)引用:
?
<Item Type=”Source” Include=”*.cs” />
?
另外,你也可以通過(guò)與PropertyGroup類似的方法把相關(guān)的Item放在ItemGroup里。
?
4.???????? Target元素。上面已經(jīng)提到了,Target表示一個(gè)需要完成的虛擬的任務(wù) 單元。每個(gè)Project可以包括一個(gè)或多個(gè)Target,從而完成一系列定制的任務(wù)。你需要給每個(gè)Target設(shè)置一個(gè)Name屬性(同一Project下的兩個(gè)Target不能擁有同樣的Name)以便引用和區(qū)別。
?
舉例來(lái)說(shuō),在你的項(xiàng)目生成過(guò)程中可能需要完成三個(gè)階段的任務(wù):首先從VSS中check-out源代碼,接下來(lái)編譯這些代 碼并執(zhí)行單元測(cè)試,最后把它們check-in回VSS。那么通常情況下你可以創(chuàng)建三個(gè)不同的Target以清晰劃分三個(gè)不同的階段:
?
<Target Name=”CheckOut” >
...
</Target>
?
<Target Name=”Build” DependsOnTargets=”CheckOut”>
?????? <Task???? Name=”Build” .../>
?????? <Task???? Name=”UnitTest” ... />
</Target>
?
<Target Name=”CheckIn” DependsOnTargets=”CheckOut;Build”>
...
</Target>
?
這樣,你就可以非常清晰地控制整個(gè)生成過(guò)程。為了反應(yīng)不同Target之間的依賴關(guān)系(只有Check-in后才能編譯,只有編譯完成 才可能Check-out……),你需要設(shè)置Target的DependsOnTargets屬性(注意是復(fù)數(shù)),以表示僅當(dāng)這些Target執(zhí)行完成之后才能執(zhí)行當(dāng)前的Target。當(dāng)MSBuild引擎開始執(zhí)行某項(xiàng)Target時(shí)(別忘了Project的DefaultTargets屬性),會(huì)自動(dòng)檢測(cè)它所依賴的那些Target是否已經(jīng)執(zhí)行完成,從而避免因?yàn)槟硞€(gè)生成環(huán)節(jié)缺失而導(dǎo)致整個(gè)生成過(guò)程發(fā)生意外。
?
你可以通過(guò)Project的DefaultTargets屬性指定MSBuild引擎從哪(幾)個(gè)Target開始執(zhí)行,也可以在調(diào)用MSBuild.exe時(shí)使用t開關(guān)來(lái)手動(dòng)指定將要運(yùn)行的Target,方法如下:
?
MSBuild /t:CheckOut
?
這樣,只有CheckOut(以及它所依賴的Target,在上文中沒(méi)有)會(huì)被執(zhí)行。
?
5.???????? Task元素。這可能是整個(gè)項(xiàng)目文件中最重 要的,因?yàn)樗攀钦嬲蓤?zhí)行的部分(這也是為什么我在上面說(shuō)Target是虛擬的)。你可以在Target下面放置多個(gè)Task來(lái)順序地執(zhí)行相應(yīng)的任務(wù),比如我在上面示例代碼中就在兩個(gè)不同的Target中安排了MakeDir、Csc和Exec三個(gè)不同的Task。這些Task通過(guò)Name屬性來(lái)相互區(qū)分,并各自擁有不同的其 它屬性來(lái)完成不同的任務(wù),比如Csc有Sources(源代碼文件)、TargetType(目標(biāo)類型)、OutputAssembly(生成程序集名稱)等屬性,而MakeDir則只需設(shè)置Directories(需要?jiǎng)?chuàng)建的路徑名稱列表)即可。
?
也許你會(huì)奇怪這些Task的名稱和屬性從哪里來(lái)。好吧,請(qǐng)用文本編譯器打開%windir%\Microsoft.NET\Framework\v1.2.30703\Microsoft.BuildTasks文件,看到了嗎?默認(rèn)情況下里面應(yīng)該是這樣的(不同的版本可能會(huì)有細(xì)微差別):
?
<!-- This file lists all the tasks that ship by default with MSBuild -->
<DefaultTasks>
??? <UsingTask TaskName="Microsoft.Build.Tasks.Csc" AssemblyName="MSBuildTasks"/>
??? <UsingTask TaskName="Microsoft.Build.Tasks.MSBuild" AssemblyName="MSBuildTasks"/>
??? <UsingTask TaskName="Microsoft.Build.Tasks.Exec" AssemblyName="MSBuildTasks"/>
??? <UsingTask TaskName="Microsoft.Build.Tasks.Vbc" AssemblyName="MSBuildTasks"/>
??? <UsingTask TaskName="Microsoft.Build.Tasks.MakeDir" AssemblyName="MSBuildTasks"/>???
???<UsingTask TaskName="Microsoft.Build.Tasks.ResGen" AssemblyName="MSBuildTasks"/>???
??? <UsingTask TaskName="Microsoft.Build.Tasks.Copy" AssemblyName="MSBuildTasks"/>???
??? <UsingTask TaskName="Microsoft.Build.Tasks.NetAssemblyResolver" AssemblyName="MSBuildTasks"/>???
??? <UsingTask TaskName="Microsoft.Build.Tasks.TransformPath" AssemblyName="MSBuildTasks"/>?????
</DefaultTasks>
?
你會(huì)注意到,在DefaultTasks元素下面排列的全是UsingTask,其中指明每一個(gè)Task的TaskName(名稱)和AssemblyName(程序集)。比如說(shuō)第一個(gè)UsingTask就對(duì)應(yīng)著我們上面用過(guò)的Csc任務(wù),它的完整名稱(namespace+class)是Microsoft.Build.Tasks.Csc,位于MSBuildTasks.dll程序集中(請(qǐng)?jiān)谕荒夸浵麓_認(rèn)這一.dll文件的存在)。這樣,MSBuild引擎在遇到對(duì)Csc任務(wù)的調(diào)用時(shí)就會(huì)通過(guò)這里的注冊(cè)信息來(lái)確定Csc所在的程序集,從而最終運(yùn)行相應(yīng)的托管代碼。這樣,如果你自己也寫了不同的Task,請(qǐng)按同樣的方式對(duì)它進(jìn)行注冊(cè)以便 使用。如果你引用了一個(gè)還沒(méi)有注冊(cè)的Target,那么MSBuild引擎將無(wú)法找到它的存在而導(dǎo)致生成失敗。
?
當(dāng)然,MSBuild Task的注冊(cè)方式不止以上一種。以上注冊(cè)方法的影響范圍是全局,你可以在每一個(gè)Project里應(yīng)用上面注冊(cè)的那些Task。但你也可以選擇在Project范圍內(nèi)注冊(cè)Task,這將對(duì)應(yīng)著另外一種略有不同的方 法。我會(huì)在后面的一篇文章里給出具體介紹。在這里,你只需明白你所需要的Task在哪里找到,而它們的具體用法可以通過(guò)參考MSBuildTasks一文來(lái)獲得,在這里我就不細(xì)說(shuō)了。
?
OK,介紹了一長(zhǎng)串,還是快點(diǎn)把我們的Build.csproj運(yùn)行起來(lái)吧。請(qǐng)?jiān)?/span>shell的同一目錄下輸入以下命令:
?
MSBuild
?
或者:
?
MSBuild Build.csproj
?
運(yùn)行結(jié)果如下:
?
d:\Dev\MyMSBuildDemo>msbuild Build.csproj
msbuild Build.csproj
Microsoft (R) .NET Build Engine version 1.2.30703.4
[Microsoft .Net Framework, Version 1.2.30703.4]???????
Copyright (C) Microsoft Corporation 2003. All rights reserved.
?
Target "Build" in project "Build.csproj"
?? Task "MakeDir"
????? Creating directory "bin".
?? Task "Csc"
????? Csc.exe /out:"bin\HelloMSBuild.exe" /target:exe "HelloMSBuild.cs"
?
Target "Run" in project "Build.csproj"
?? Task "Exec"
????? Hello MSBuild!
?
可見,在Build.csproj指定的兩個(gè)Target和三個(gè)Task均按相應(yīng)的順序依次運(yùn)行,在Csc執(zhí)行時(shí)MSBuild還顯示出了當(dāng)前執(zhí)行的具體命令,而在原來(lái)的Visual Studio .NET年代,你是無(wú)法獲知當(dāng)前正在執(zhí)行的編譯命令是什 么(據(jù)Alex Kipman稱,連Visual Studio .NET自己也不知道正在執(zhí)行的具體命令,因?yàn)槟切┟钜呀?jīng)被hard code進(jìn)了“黑盒子”,根本無(wú)法 提取)。
?
好了,一個(gè)簡(jiǎn)單的MSBuild文件用法示例就到這兒了。如果你此前還沒(méi)接觸過(guò)MSBuild或者NAnt,那么希望這篇文章能讓你對(duì)MSBuild的用法有個(gè)初步的了解。還有 很多的細(xì)節(jié)我在文中沒(méi)有涉及,如果你感興趣的話就請(qǐng)下載前面我提到的那些MSBuild文檔來(lái)自己研究吧。我會(huì)在下一篇文章里介紹如何開發(fā)自己的MSBuild Task。
?
--------------------------------------------------------------------------
musicland
http://blog.joycode.com/musicland
轉(zhuǎn)載于:https://www.cnblogs.com/fromchaos/archive/2010/06/21/1762099.html
總結(jié)
以上是生活随笔為你收集整理的[转]MSBuild入门的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: paper每日谈——动机
- 下一篇: 关于SharePoint中管理列表项权限