從零開始構(gòu)建MSBuild C#項目文件

本文參考自MSDN的一篇文章,從零開始創(chuàng)建MSBuild C#項目文件。

準備條件

  • 一個好用的文本編輯器,例如Atom或者Sublime Text。
  • MSBuild命令行工具。如果已經(jīng)安裝了Visual Studio的話,應(yīng)該可以在開始菜單中找到類似Visual Studio 2015的MSBuild命令提示符 這樣的項目。

創(chuàng)建程序

首先打開MSBuild命令提示符,然后切換到你想要創(chuàng)建項目的文件夾,例如我的文檔或者桌面。然后,輸入md HelloWorld創(chuàng)建一個名為HelloWorld的文件夾。然后輸入cd HelloWorld切換到這個文件夾。為簡便起見,下面所說的命令提示符,都是指這里的MSBuild命令提示符。

使用你最喜歡的文本編輯器,在HelloWorld文件夾中創(chuàng)建一個名為helloworld.cs的代碼文件,文件內(nèi)容如下:

using System;

class HelloWorld
{
    static void Main()
    {
#if DebugConfig
        Console.WriteLine("WE ARE IN THE DEBUG CONFIGURATION");
#endif

        Console.WriteLine("Hello, world!");
    }
}

將文件保存之后,就可以在命令提示符中使用C#編譯器工具csc編譯該文件了。

csc helloworld.cs

然后就可以運行生成的helloworld.exe來查看編譯生成的文件了。

helloworld.exe

應(yīng)該可以在命令提示符中看到程序的輸出。然后,刪除生成的exe,準備下一步。

創(chuàng)建MSBuild項目文件

用文本編輯器創(chuàng)建名為Helloworld.csproj的文件,文件內(nèi)容如下:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <Compile Include="helloworld.cs" />
  </ItemGroup>
  <Target Name="Build">
    <Csc Sources="@(Compile)"/>  
  </Target>
</Project>

下面來簡單解釋一下。

csproj項目文件是一個XML文件,根節(jié)點是Project節(jié)點,可以包括若干個ItemGroup節(jié)點和Target節(jié)點。ItemGroup節(jié)點是一個容器,用來包括若干個項元素。例如這里就包括了一個項元素Compile,,包括了helloworld.cs文件。這里還可以使用通配符。

<Compile Include="*.cs" />

Target元素是項目構(gòu)建的目標(biāo),每個文件可以有多個Target,執(zhí)行不同的任務(wù)。這里,名為Build的Target就包括了Csc任務(wù)來編譯一個文件,使用Source屬性來指定要編譯的文件。另外還有一些任務(wù),會在下面說明。

這里還有一種語法@(Compile),這里會引用上面定義的項。在這里就是引用上面定義的helloworld.cs文件。如果定義了多個項,Target在執(zhí)行的時候會以類似foreach的形式迭代執(zhí)行每一個項。

有了項目文件,就可以使用MSBuild來執(zhí)行項目的生成了,/t表示執(zhí)行名為Build的Target。

msbuild helloworld.csproj /t:Build

查看一下是否生成了helloworld.exe,然后將其刪除,準備下一步。

添加構(gòu)建屬性

在Project開始標(biāo)簽之后添加一個屬性組節(jié)點:

<PropertyGroup>
  <AssemblyName>MSBuildSample</AssemblyName>
  <OutputPath>Bin\</OutputPath>
</PropertyGroup>

每個項目文件可以包括若干個PropertyGroup節(jié)點,其中可以包括若干個屬性節(jié)點,每一個節(jié)點定義一個屬性,可以在項目文件中引用。這里就包括了AssemblyName和OutputPath兩個屬性。之后就可以通過$(屬性名)的語法來使用了。

在Csc節(jié)點前插入一個節(jié)點:

<MakeDir Directories="$(OutputPath)"      Condition="!Exists('$(OutputPath)')" />

然后再Csc節(jié)點中增加一個OutputAssembly屬性:

<Csc Sources="@(Compile)" OutputAssembly="$(OutputPath)$(AssemblyName).exe" />

這里增加了一個創(chuàng)建文件夾的任務(wù),創(chuàng)建的文件夾名字由上面的屬性組定義。幾乎每個任務(wù)都可以添加一個Condition屬性,指定什么條件下執(zhí)行該任務(wù)。這里是在輸出目錄不存在的情況下才執(zhí)行該任務(wù),創(chuàng)建目錄。除此之外,還有其他很多任務(wù),例如復(fù)制文件、刪除文件等等,詳細情況可以查看MSBuild任務(wù)參考。另外還有一個名字叫做MSBuild Community Tasks的開源項目,包含了其他一些任務(wù),如果有需求的可以參考一下。

另外微軟建議我們在定義目錄屬性的時候,最好將目錄后面的反斜杠\定義到屬性中,而不是加在引用之后。例如上面的就比下面的更好:

<OutputPath>Bin\</OutputPath>
OutputAssembly=="$(OutputPath)$(AssemblyName).exe" />
<OutputPath>Bin</OutputPath>
OutputAssembly=="$(OutputPath)\$(AssemblyName).exe" />

現(xiàn)在項目文件應(yīng)該類似這樣:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <AssemblyName>MSBuildSample</AssemblyName>
    <OutputPath>Bin\</OutputPath>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="helloworld.cs" />
  </ItemGroup>
  <Target Name="Build">
    <MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
    <Csc Sources="@(Compile)" OutputAssembly="$(OutputPath)$(AssemblyName).exe" />
  </Target>
</Project>

再次運行一下構(gòu)建命令,查看一下程序出否在輸出目錄中生成。

msbuild helloworld.csproj /t:Build

增加構(gòu)建目標(biāo)

在構(gòu)建過程中可以指定多個構(gòu)建目標(biāo),可以指定一個目標(biāo)調(diào)用其他目標(biāo),還可以指定默認的構(gòu)建目標(biāo)。

在Build目標(biāo)之后添加兩個新目標(biāo):

<Target Name="Clean" >
  <Delete Files="$(OutputPath)$(AssemblyName).exe" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />

這兩個構(gòu)建目標(biāo)很簡單,Clean目標(biāo)會刪除生成的exe文件。Rebuild目標(biāo)會運行Clean和Build兩個目標(biāo)。

在Project節(jié)點中添加一個新屬性DefaultTarget,就可以指定一個默認目標(biāo)。如果運行MSBuild命令的時候沒有使用/t指定Target,就會自動執(zhí)行默認的目標(biāo)。

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

運行一下msbuild helloworld.csproj /p:AssemblyName=Greetings,測試一下。這里通過/p參數(shù)傳入指定的參數(shù)名,這會覆蓋項目文件中指定的文件名。如果不指定參數(shù)名的話就會使用在項目中已經(jīng)定義的參數(shù)。然后運行msbuild helloworld.csproj /t:clean /p:AssemblyName=Greetings**/p:AssemblyName=Greetings,刪除已經(jīng)生成的文件。

增量構(gòu)建

在名為Build的Target中添加如下屬性:

Inputs="@(Compile)" Outputs="$(OutputPath)$(AssemblyName).exe"

Inputs屬性指定該目標(biāo)依賴的輸入文件,在這里由上面的Compile項所定義。Outputs指定項目的輸出文件。指定這兩個屬性之后,MSBuild就會在運行此目標(biāo)的時候檢查輸入和輸出文件。如果輸入文件相對于輸出文件都是最新的,那么MSBuild就會跳過構(gòu)建過程。如果有部分文件已經(jīng)修改,MSBuild就會只對這部分文件運行構(gòu)建目標(biāo)。

概念總結(jié)

MSBuild依據(jù)csproj項目文件來進行構(gòu)建。csproj文件中可以有多種節(jié)點。

ItemGroup節(jié)點是項目組,可以有多個子節(jié)點, 用來包含要處理的一個或多個文件。每個子節(jié)點都必須有Include屬性指定要包含什么文件,還有一個可選的Exclude節(jié)點指定排除什么文件。定義ItemGroup之后,就可以利用@(節(jié)點名)來引用Item了。

PropertyGroup節(jié)點是屬性組,可以有多個節(jié)點,用來包含項目構(gòu)建過程中使用到的屬性。定義了屬性之后,可以使用$(屬性名)語法來訪問。

Target是構(gòu)建目標(biāo),是MSBuild的執(zhí)行目標(biāo),每個Target下面可以包含多個任務(wù),還可以引用其他的Target構(gòu)成一個執(zhí)行鏈。微軟和C#社區(qū)定義了很多任務(wù),可以分別在其MSBuild任務(wù)參考MSBuild Community Tasks中找到。

最后,我在Github上新建了一個項目MSBuildExample,演練了一下上面的概念。這個項目添加了一個AfterBuild目標(biāo),在Release狀態(tài)下構(gòu)建成功之后,將生成的可執(zhí)行文件重命名成自定義名稱,然后和第三方庫以及一個配置文件打包生成zip壓縮包。有興趣的同學(xué)可以看一下。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容