从零开始构建MSBuild C#项目文件

本文参考自MSDN的一篇文章,从零开始创建MSBuild C#项目文件

准备条件

  • 一个好用的文本编辑器,例如Atom或者Sublime Text。
  • MSBuild命令行工具。如果已经安装了Visual Studio的话,应该可以在开始菜单中找到类似Visual Studio 2015的MSBuild命令提示符 这样的项目。

创建程序

首先打开MSBuild命令提示符,然后切换到你想要创建项目的文件夹,例如我的文档或者桌面。然后,输入md HelloWorld创建一个名为HelloWorld的文件夹。然后输入cd HelloWorld切换到这个文件夹。为简便起见,下面所说的命令提示符,都是指这里的MSBuild命令提示符。

使用你最喜欢的文本编辑器,在HelloWorld文件夹中创建一个名为helloworld.cs的代码文件,文件内容如下:

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

应该可以在命令提示符中看到程序的输出。然后,删除生成的exe,准备下一步。

创建MSBuild项目文件

用文本编辑器创建名为Helloworld.csproj的文件,文件内容如下:

<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文件,根节点是Project节点,可以包括若干个ItemGroup节点和Target节点。ItemGroup节点是一个容器,用来包括若干个项元素。例如这里就包括了一个项元素Compile,,包括了helloworld.cs文件。这里还可以使用通配符。

<Compile Include="*.cs" />

Target元素是项目构建的目标,每个文件可以有多个Target,执行不同的任务。这里,名为Build的Target就包括了Csc任务来编译一个文件,使用Source属性来指定要编译的文件。另外还有一些任务,会在下面说明。

这里还有一种语法@(Compile),这里会引用上面定义的项。在这里就是引用上面定义的helloworld.cs文件。如果定义了多个项,Target在执行的时候会以类似foreach的形式迭代执行每一个项。

有了项目文件,就可以使用MSBuild来执行项目的生成了,/t表示执行名为Build的Target。

msbuild helloworld.csproj /t:Build

查看一下是否生成了helloworld.exe,然后将其删除,准备下一步。

添加构建属性

在Project开始标签之后添加一个属性组节点:

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

每个项目文件可以包括若干个PropertyGroup节点,其中可以包括若干个属性节点,每一个节点定义一个属性,可以在项目文件中引用。这里就包括了AssemblyName和OutputPath两个属性。之后就可以通过$(属性名)的语法来使用了。

在Csc节点前插入一个节点:

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

然后再Csc节点中增加一个OutputAssembly属性:

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

这里增加了一个创建文件夹的任务,创建的文件夹名字由上面的属性组定义。几乎每个任务都可以添加一个Condition属性,指定什么条件下执行该任务。这里是在输出目录不存在的情况下才执行该任务,创建目录。除此之外,还有其他很多任务,例如复制文件、删除文件等等,详细情况可以查看MSBuild任务参考。另外还有一个名字叫做MSBuild Community Tasks的开源项目,包含了其他一些任务,如果有需求的可以参考一下。

另外微软建议我们在定义目录属性的时候,最好将目录后面的反斜杠\定义到属性中,而不是加在引用之后。例如上面的就比下面的更好:

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

现在项目文件应该类似这样:

<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>

再次运行一下构建命令,查看一下程序出否在输出目录中生成。

msbuild helloworld.csproj /t:Build

增加构建目标

在构建过程中可以指定多个构建目标,可以指定一个目标调用其他目标,还可以指定默认的构建目标。

在Build目标之后添加两个新目标:

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

这两个构建目标很简单,Clean目标会删除生成的exe文件。Rebuild目标会运行Clean和Build两个目标。

在Project节点中添加一个新属性DefaultTarget,就可以指定一个默认目标。如果运行MSBuild命令的时候没有使用/t指定Target,就会自动执行默认的目标。

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

运行一下msbuild helloworld.csproj /p:AssemblyName=Greetings,测试一下。这里通过/p参数传入指定的参数名,这会覆盖项目文件中指定的文件名。如果不指定参数名的话就会使用在项目中已经定义的参数。然后运行msbuild helloworld.csproj /t:clean /p:AssemblyName=Greetings**/p:AssemblyName=Greetings,删除已经生成的文件。

增量构建

在名为Build的Target中添加如下属性:

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

Inputs属性指定该目标依赖的输入文件,在这里由上面的Compile项所定义。Outputs指定项目的输出文件。指定这两个属性之后,MSBuild就会在运行此目标的时候检查输入和输出文件。如果输入文件相对于输出文件都是最新的,那么MSBuild就会跳过构建过程。如果有部分文件已经修改,MSBuild就会只对这部分文件运行构建目标。

概念总结

MSBuild依据csproj项目文件来进行构建。csproj文件中可以有多种节点。

ItemGroup节点是项目组,可以有多个子节点, 用来包含要处理的一个或多个文件。每个子节点都必须有Include属性指定要包含什么文件,还有一个可选的Exclude节点指定排除什么文件。定义ItemGroup之后,就可以利用@(节点名)来引用Item了。

PropertyGroup节点是属性组,可以有多个节点,用来包含项目构建过程中使用到的属性。定义了属性之后,可以使用$(属性名)语法来访问。

Target是构建目标,是MSBuild的执行目标,每个Target下面可以包含多个任务,还可以引用其他的Target构成一个执行链。微软和C#社区定义了很多任务,可以分别在其MSBuild任务参考MSBuild Community Tasks中找到。

最后,我在Github上新建了一个项目MSBuildExample,演练了一下上面的概念。这个项目添加了一个AfterBuild目标,在Release状态下构建成功之后,将生成的可执行文件重命名成自定义名称,然后和第三方库以及一个配置文件打包生成zip压缩包。有兴趣的同学可以看一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容