MSBuild基本概念(续)
在上一篇简单的介绍了下MSBuild中的四个基本块,每块介绍比较单薄,在这里对在大多数的项目模版生成的*.*proj文件中比较常见一些用法和概念做些补充。主要有一下几方面:
MSBuild特殊字符
一些字符在MSBuild中代表着特殊的上下文含义,如下:
针对MSBuild的特殊字符转义需要用[%xx]这种方式,xx代表字符的ASCII十六进制值([%=%25][$=%24][@=%40] ['=%27][;=%3B][?=%3F][*=%2A])。针对XML保留字符则使用<这种方式。 一般用到这些特殊字符的情况不多,见到时能知道是转义就可以了。
MSBuild条件
条件在*.*proj项目文件中非常常见,用Condition特性来表示一个布尔表达式,类似于if条件,几乎所有的元素都可以具有Conditon特性。一个简单的例子如下:
1 2 34 5 6 7 10Debug 8x86 911 12 1413 15 16 2017 18 19
还有一些常用的表达式如!=、!、And、Or等。
MSBuild属性
上篇介绍到可以用$可以引用自定义的属性,除此之外亦可以引用系统的环境变量,如$(Path),以及 。
属性除了可以在项目文件中声明是赋值外,在MSBuild命令行也允许设置属性的值(语法:/p:propertyName=value)。称作全 局属性,这类属性会重写在项目文件中设置的属性值,保留属性除外的任何属性都可被这种方式覆盖其原值。 以上面示例为基础:[MSBuild condition.xml /p:Platform=x64],则最终输出结果就为Debug|x64了。
属性还有一种叫做任务发出属性,在上篇用到了,由Output元素的PropertyName特性指定了属性名,这类属性不像一般的声明式属性那样赋值,而是动态得到的值。是在项目文件中很常见的用法。
MSBuild项
项大都是用来引入文件用的,而文件会有一些附加信息,比如版本,语言等,而这些附加信息在项目文件中是以项的子元素的出现的,称为项的元数据。元数据是键/值的形式存储的,声明方式和属性相同。
12 3 4 5 6 8zh-cn 7
除了自定义的一些元数据外,系统还提供一些隐式存在的元数据,即不用声明即可使用,具体可参见。引用这类元数据的语法和自定义的完全相同。
项转换允许把一个项的列表与另一个列表一一变换。比如下面的例子:
1 56 7 8 9 10 11 1512 13 14
MSBuild任务
从上篇中我们对任务的认识是它是一个原子操作,用来执行某一项逻辑处理,但是xml格式的项目文件是没有这个处理能力的,所以这些任务都是映射到.NET类库中的一些类,由这些类来处理操作中的逻辑。具体来说都是实现接口的类,ITask接口位于命名空间。当然我们也可以实现自己的任务类,直接实现ITask接口或者继承自(此抽象类实现了ITask接口的部分功能,可简化自定义任务类的编写,留出一个抽象方法供子类重写自己的任务逻辑)。然后通过元素映射到出一个任务元素。我就继承Task写一个简单的示例:
1 //AddTwoNumberTask.cs,需编译为dll 2 using System; 3 using Microsoft.Build.Utilities; 4 using Microsoft.Build.Framework; 5 6 ///7 /// 继承Task,任务逻辑是处理加法 8 /// 9 public class AddTwoNumberTask : Task10 {11 ///12 /// 定义一个加数,13 /// 如果一个输入属性被要求必须输入,则用[Required]特性标识该属性14 /// 15 public String Number1 { get; set; }16 ///17 /// 定义另一个加数18 /// 19 public String Number2 { get; set; }20 public override bool Execute()21 {22 this.Sum = (Int32.Parse(this.Number1) + Int32.Parse(this.Number2)).ToString();23 return true;24 25 }26 ///27 /// 定义一个输出参数,使用Output特性修饰该属性28 /// 29 [Output]30 public String Sum { get; set; }31 }
1 2 3 45 6 8Library 79 1310 11 12 14 1615 17 18 2321 22
用MSBuild编译buildAddTaskDll.csproj项目文件。得到AddTwoNumberTask.dll程序集。再编写一个项目文件usingtask如下:
如果仔细看AddTwoNumberTask.cs文件就会发现
//如果Number1或者2不是数字,则此任务就会抛异常了 this.Sum = (Int32.Parse(this.Number1) + Int32.Parse(this.Number2)).ToString();
那么如果<AddTwoNumberTask Number1="1" Number2="2" >在这里加一个ContinueOnError=“true”,则表示会忽略掉逻辑处理中的错误,继续运行,否则会终止执行后续任务。如果任务有输出参数的话,Output元素总是作为任务的子元素出现,作为一个中间桥梁把任务的输出传输到属性或者项中。
MSBuild目标
Project根元素代表者一个项目文件,上面的例子我都会写一个DefaultTargets特性来指定该项目文件要执行的默认目标是哪一个。其实此特性是可选的,也是可以用分号分割写多个的,执行顺序依据书写顺序来判定,也可通过MSBuild命令行参数来传递:
msbuild /target:Build1;Build2
除此之外,Project元素还有一个可选特性InitialTargets,也支持多个目标。如果这两个特性都没有,则MSBuild先执行它遇到的第一个Target。Target有一个DependsOnTargets特性表示当前目标依赖另一个目标,效果就是DependsOnTargets特性指定的目标先于当前目标执行。这绕来绕去好多先后顺序关系,写一个示例看看吧。
1 2 3 4 57 8 9 10 11 12 1413 15 1716 18 2019 21 2322 24 26 2725
Import元素
项目模版产生的*.*proj项目文件大量的使用这个元素,用来导入可重用的项目文件,其中最常见的一个应该是这个吧,如果你用C#开发的话。
MSBuildToolsPath或者是MSBuildBinPath,Project特性指定要导入的项目文件。Import元素像是一个占位元素,MSBuild在执行到此时会用*.targets替换掉此元素,就像本来就声明在这里一样,所以和*.targets文件有关的所有保留属性会被重置。 Import元素对导入文件的扩展名无要求,文件是正确的项目文件就行,但一般约定为*.targets。
总结和备注
了解了以上知识点后,阅读一般的项目模版生成的项目文件(*.*proj)应该是可以的了,下篇文章先认识几个重要的*.targets,为剖析项目文件做准备。
备注:针对项目文件中所指的“特性”是表示一个xml元素的“属性”。由于属性在MSBuild中有特殊含义,则MSDN文档一律把项目文件中的xml属性称作是特性,比如Message任务的Text特性。