如果你和我一样一直都在用NAnt管理生成过程的话,那么你一定会高度关注MSBuild。原因很简单,因为它属于微软,你可以不喜欢它,但你一定要学会用它。
在熬过了几个晚上以后,我终于让自己适应了MSBuild的语法。这可真不容易,特别是当自己已经习惯了NAnt的小写规范之后。不过这不成问题,因为随着自己对MSBuild的理解一点点加深,自己还真的喜欢上它了。
好吧,下面就让我来简单地介绍一下我在学习MSBuild使用过程中的一点经验。如果你还在MSBuild的大门外徘徊,那么希望这篇东西能带你进入那扇门。
准备工作
首先要提到的是有关如何使用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
上面这两项出自MSBuild开发组的Alex Kipman,从理论上说他应该是了解MSBuild的第一人,他给出的几个演示的确给了我非常大的帮助。(不过我非常不喜欢他的声音,又尖又细。)
3. MSBuild Doc
http://msdn.microsoft.com/longhorn/toolsamp/default.aspx
这是最重要的,其中包括Alex Kipman主笔的五份重要文档:MSBuildFileFormat、MSBuildWalkthrough、MSBuildTasks、HowToWriteATask以及MSBuildCommandLine。这可能是目前情况下外界能获得的有关MSBuild最详细的文档。
Demo
好了,一切准备工作就绪,让我们以一个简单的示例开始吧。
首先写一个简单的C# Console程序(你也可以把它改成VB.NET):
// HelloMSBuild.cs
using System;
class HelloMSBuild
{
public static void Main()
{
Console.WriteLine(“Hello MSBuild!”);
}
}
下面我们就要写一个.csproj文件来控制整个生成过程。值得注意的是,如果在调用MSBuild.exe时没有指定具体的项目文件,MSBuild引擎会在当前目录下查找一个名为*.*proj的项目文件。如果你在同一目录中写了多个这样的项目文件,那么需要手动指定MSBuild.exe的目标文件,方法是:
MSBuild a.csproj
否则MSBuild会提示出错,要求你手动指定目标项目文件。
以下是项目文件:
如果你此前没有过NAnt的开发经验,那么上面这些东西肯定看起来挺吓人。这个时候最好的办法是打开那篇MSBuildFileFormat,对照上面代码查找相应的项目元素的含义。下面我对其中重要的项目元素进行一下解释。
1. Project元素。
这是每一个项目文件的最外层元素,它表示了一个项目的范围。如果缺少了这一元素,MSBuild会报错称Target元素无法识别或不被支持。
Project元素拥有多个属性,其中最常用到的是DefaultTargets属性。我们都知道,在一个项目的生成过程中可能需要完成几项不同的任务(比如编译、单元测试、check-in到源代码控制服务器中等),其中每一项任务都可以用Target来表示。对于拥有多个Target的项目,你可以通过设置Project的DefaultTargets(注意是复数)属性来指定需要运行哪(几)个Target,比如:
或者:
如果没有这个设置,MSBuild将只运行排在最前面的那个Target。
2. Property元素。
在项目中你肯定需要经常访问一些信息,例如需要创建的路径名、最终生成的程序集名称等。这些信息你最好别hard code进项目中,除非你一次写过之后永不更改。这时Property就能派上用场了。你把上面提到的那些信息以name/value的形式添加进Property,随后就可以以$(PropertyName)的形式访问。这样你就无须为了改动一个文件名称而让整个项目文件伤筋动骨了。比如上面代码中的Bin就是将要创建的路径名称,而AssemblyName则是最终要生成的程序集名称。这些属性的名称不是固定的,你完全可以按自己的习惯来进行命名。在使用时,你需要把属性名称放在”$(“和”)”对内(不包括引号),以表示这里将被替换成一个Property元素的值。
另外,如果Property元素数量比较多,你还可以把它们分门别类地放在不同的PropertyGroup里,以提高代码的可阅读性。这对Property本身没有任何影响。比如:
3. Item元素。
在整个项目文件中你肯定要提供一些可被引用的输入性资源(inputs)信息,比如源代码文件、引用的程序集名称、需要嵌入的图标资源等。它们应该被放在Item里,以便随时引用。语法是:
其中Type属性可以被看作是资源的类别名称,比如对于.cs源文件,你可以把它们的Type都设置为Source,对于引用的程序集把Type都设置为Reference,这样在随后想引用这一类别的资源时只要引用这个Type就可以了,方法是@(TypeName)。可千万别和Property的引用方法弄混了。
既然Type是资源的类名,那么Include就是具体的资源名称了,比如在上面的示例代码中,Include引用的就是C#源代码文件的名称。你也可以用使用通配符*来扩大引用范围。比如下面这行代码就指定了当前目录下的所有C#文件都可以通过@(Source)来引用:
另外,你也可以通过与PropertyGroup类似的方法把相关的Item放在ItemGroup里。
4. Target元素。
上面已经提到了,Target表示一个需要完成的虚拟的任务单元。每个Project可以包括一个或多个Target,从而完成一系列定制的任务。你需要给每个Target设置一个Name属性(同一Project下的两个Target不能拥有同样的Name)以便引用和区别。
举例来说,在你的项目生成过程中可能需要完成三个阶段的任务:首先从VSS中check-out源代码,接下来编译这些代码并执行单元测试,最后把它们check-in回VSS。那么通常情况下你可以创建三个不同的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,在上文中没有)会被执行。
5. Task元素。
这可能是整个项目文件中最重要的,因为它才是真正可执行的部分(这也是为什么我在上面说Target是虚拟的)。你可以在Target下面放置多个Task来顺序地执行相应的任务,比如我在上面示例代码中就在两个不同的Target中安排了MakeDir、Csc和Exec三个不同的Task。这些Task通过Name属性来相互区分,并各自拥有不同的其它属性来完成不同的任务,比如Csc有Sources(源代码文件)、TargetType(目标类型)、OutputAssembly(生成程序集名称)等属性,而MakeDir则只需设置Directories(需要创建的路径名称列表)即可。
也许你会奇怪这些Task的名称和属性从哪里来。好吧,请用文本编译器打开%windir%\Microsoft.NET\Framework\v1.2.30703\Microsoft.BuildTasks文件,看到了吗?默认情况下里面应该是这样的(不同的版本可能会有细微差别):
你会注意到,在DefaultTasks元素下面排列的全是UsingTask,其中指明每一个Task的TaskName(名称)和AssemblyName(程序集)。比如说第一个UsingTask就对应着我们上面用过的Csc任务,它的完整名称(namespace+class)是Microsoft.Build.Tasks.Csc,位于MSBuildTasks.dll程序集中(请在同一目录下确认这一.dll文件的存在)。这样,MSBuild引擎在遇到对Csc任务的调用时就会通过这里的注册信息来确定Csc所在的程序集,从而最终运行相应的托管代码。这样,如果你自己也写了不同的Task,请按同样的方式对它进行注册以便使用。如果你引用了一个还没有注册的Target,那么MSBuild引擎将无法找到它的存在而导致生成失败。
当然,MSBuild Task的注册方式不止以上一种。以上注册方法的影响范围是全局,你可以在每一个Project里应用上面注册的那些Task。但你也可以选择在Project范围内注册Task,这将对应着另外一种略有不同的方法。我会在后面的一篇文章里给出具体介绍。在这里,你只需明白你所需要的Task在哪里找到,而它们的具体用法可以通过参考MSBuildTasks一文来获得,在这里我就不细说了。
OK,介绍了一长串,还是快点把我们的Build.csproj运行起来吧。请在shell的同一目录下输入以下命令:
MSBuild
或者:
MSBuild Build.csproj
运行结果如下:
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指定的两个Target和三个Task均按相应的顺序依次运行,在Csc执行时MSBuild还显示出了当前执行的具体命令,而在原来的Visual Studio .NET年代,你是无法获知当前正在执行的编译命令是什么(据Alex Kipman称,连Visual Studio .NET自己也不知道正在执行的具体命令,因为那些命令已经被hard code进了“黑盒子”,根本无法提取)。
好了,一个简单的MSBuild文件用法示例就到这儿了。如果你此前还没接触过MSBuild或者NAnt,那么希望这篇文章能让你对MSBuild的用法有个初步的了解。还有很多的细节我在文中没有涉及,如果你感兴趣的话就请下载前面我提到的那些MSBuild文档来自己研究吧。我会在下一篇文章里介绍如何开发自己的MSBuild Task。
分类目录
- arch/management (17)
- computer (38)
- java/j2ee (305)
- lnmpa (237)
- mac/iphone/ipad/android (11)
- mysql/oracle/postgresql (126)
- os/software (74)
- other (518)
- python (6)
- redis/memcached/mongo (31)
- sitebuild (143)
随便看看
标签云
程序员 创业 人生箴言 eclipse 快捷键 术语 索引 unix命令 vim wordpress java学习笔记 环境变量 oracle内置函数 index 人生 数据类型 nohup tuxedo mysql学习笔记 MS-DOS命令 servlet spring 职场进阶 职业进阶 服务器选购 服务器选型 apache JPA MongoDB 注解 tomcat 子女教育 jquery maven JVM aix命令 网络营销 java异常 seo 人生规划 关键字 css 网络推广 struts 系统优化 成长 frame iframe bluehost jdbc select 我的信仰 oracle函数 cookie HashMap 站长工具 乱码 ArrayList secureCRT jsp session tail find halt 事务 oracle单记录函数 算法 URL window table javascript操作表单元素 String 字符串处理 健康 http 域名 情感 more google A记录 域名解析 netstat 弹出对话框 弹出窗口 框架集 框架 excel 字符串 javascript函数 showModalDialog nginx number 数组 sql frameset 开源程序 java数组 软件 oracle服务友情链接
收藏链接