使用nant
NAnt 是一个Visual Studio .Net应用程序的连编工具,对大而负责的工程而言,使用NAnt很方便。
1. 安装
从http://nant.sourceforge.net上可以下载源代码或者编译好的二进制文件,一般下载nant-bin.zip,解压,注册系统环境变量后,就可以使用nant命令了。
2. Build文件
XML格式,每个build文件包含一个project,project有若干target,每个target包含若干task。Task不被包含在target中,即直接包含在project中。
Ø Projects(工程)
三个特性,name、设置工程名称,default、设置默认target,和basedir、如果不设置,默认为build文件的父目录。
执行nant时,可以知道targets,如果未指定,执行默认得target,如果build文件中未指定默认得target,仅仅执行全局的task(全局的task总是执行),如果没有全局的task,那就什么都不执行了。
Ø Targets(目标、对象)
有五个特性,name、指定名称,depends、此target所依赖的targets(多个用“,”隔开),if、如果条件为true执行此target,unless、如果条件为true跳过此target,description、功能的简短描述。其中name必须指定。
执行某个target时,首先执行此target所依赖的targets。一个target可能被depends多次,仅执行一次,但是通过<call>任务(task)执行一个target时,此target及其所依赖的targets要重新执行一次。另外,当target的名称被设置为“*”时,称为wild target,一个build文件仅仅有一个wild target,在目前的build文件中,当且仅当被调用的target不存在时,才执行wild target,主要用来处理无效的请求。
Ø Task(任务)
一块可执行的代码,一个task可有多个特性和参数。特性的值可包含对属性的引用,在task执行前,这些引用会被解释出来。
Ø Properties(属性)
一个project可有很多属性,这些属性可以通过<property任务设置在build文件中,也可以设置在Nant外。一个属性有一个name和一个value,可用于task的特性中,也可用于表达式,用在task的特性中时,使用${property name}格式。Nant有很多内建属性(与nant有关的,与框架有关的,与平台有关的等)。
<property>任务有readonly和overwrite等特性,readonly指定属性是否是只读到,默认false,overwirte指定如果属性已经存在,属性值是否可以重写,默认true,对于只读属性,是不能被重写的。注意:在Nant命令行指定的属性,先于在build文件中指定的属性,并且这些属性往往是只读的。
另外可以在NAnt.exe.config文件中定义全局属性。
Ø Loggers & Listeners
Nant 通过Loggers和Listeners来监控连编过程。Listeners记录了build started,build finished,target started,target finished,task started,task finished,message logged事件,Loggers扩展了Listeners,可以按-quiet(静态的,平稳的), -verbose(详细的), -debug三个层次记录连编信息,可输出到控制台或者文件中。Nant内建了三个类:NAnt.Core.DefaultLogger、NAnt.Core.MailLogger和NAnt.Core.XmlLogger。使用时:-logger:类 -logfile:文件名。可以是普通道文本文件或者XML文件。
Ø Expressions(表达式)
表达式是一种简单而强大的机制,允许写高级的公式,用于task的参数和条件式中,这样就可以控制连编过程了。表达式能够访问project的属性、调用内建的或者用户定义的functions。
表达式通过${…}符号,可用于task的参数中。也可以使用标准的算术、逻辑和关系运算符。通过prefix::function-name(argument1, ..., argumentN)语法调用函数。访问属性,仅需指定其name(${…})
例子:
访问属性
<property name="build.version" value="3" />
<echo message="The current date is: ${build.version}" />
调用函数
<echo message="The current date is: ${datetime::now()}" />
表达式结果存储
<property name="autoexec-present" value="${file::exists('c:\autoexec.bat')}" />
Real-life expression use
<property name="myprj.basedir" value="c:\" />
<property name="filename" value="${path::combine(myprj.basedir,'version.txt')}" />
<if test="${not file::exists(filename) or file::get-length(filename) = 0}">
<echo message="The version file ${filename} doesn't exist or is empty!" />
</if>
有条件执行task
<property name="myprj.basedir" value="c:\" unless="property::exists('myprj.basedir')" />
<csc target="library" output="out.dll" ...
if="${datetime::now() - file::get-last-write-time('out.dll')) > timespan::from-hours(1)}">
...
</csc>
Functions
Nant提供了操作字符串、日期时间和路径名字的函数,还提供读取文件或者目录属性,访问目前的连编信息等函数。
调用函数语法prefix::function-name(argument1, ..., argumentN),需要的情况下会进行参数类型转换,如果转换有误,会报告错误。
自定义函数可以使用任何.Net语言实现,此外还可以通过<script>任务实现。
<script language="C#" prefix="directory">
<code><![CDATA[
[Function("set-current-directory")]
public static string SetCurrentDirectory(string path)
{
System.IO.Directory.SetCurrentDirectory(path);
return path;
}
]]></code>
</script>
3. 运行Nant
命令Nant,此外还可以指定build文件名、targets和properties等。
Nant
NAnt -buildfile:..\fileName.build
NAnt clean
NAnt -D:debug=false clean dist
Usage : NAnt [options] <target> <target> ...
Options :
-t[argetframework]:<text> Specifies the framework to target
-defaultframework:<text> Specifies the framework to target (Short format: /k)
-buildfile:<text> Use given buildfile (Short format: /f)
-pause[+|-] Pauses before program ends
-v[erbose][+|-] Displays more information during build process
-debug[+|-] Displays debug information during build process
-q[uiet][+|-] Displays only error or warning messages during build process
-e[macs][+|-] Produce logging information without adornments
-find[+|-] Search parent directories for build file
-indent:<number> Indentation level of build output
-D:<name>=<value> Use value for given property
-logger:<text> Use given type as logger
-l[ogfile]:<filename> Use value as name of log output file
-listener:<text> Add an instance of class as a project listener
-ext[ension]:<text> Load NAnt extensions from the specified assembly
-projecthelp[+|-] Prints project help information
-nologo[+|-] Suppresses display of the logo banner
-h[elp][+|-] Prints this message
@<file> Insert command-line settings from a text file.
A file ending in .build will be used if no buildfile is specified.
我的一个project
<?xml version="1.0" encoding="UTF-8" ?>
<project name="fluentmigrator" xmlns="http://nant.sf.net/release/0.85/nant.xsd" default="migrate">
<loadtasks assembly="../UpdateService2.Migration/FluentMigration/FluentMigrator.NAnt.dll" />
<property name="db.type" value="sqlite"></property>
<property name="db.path" value="../UpdateService2/sqlitedb/db.sqlite.s3db"></property>
<property name="company.name" value="ETEC"/>
<!--Location where Msbuild for .Net 4.0 is installed-->
<property name="msbuild4.exe" value="C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe"/>
<!--Location where you want source built in your local workspace -->
<property name="local.buildspace" value="${path::get-full-path('..\..\..\builds')}"/>
<!--Location where you edit code during development -->
<property name="local.workspace" value="${path::get-full-path('..\..\..\DefaultCollection')}"/>
<!--Location where you want source packaged up to be released -->
<property name="local.releasespace" value="${path::get-full-path('..\..\..\releases')}"/>
<property name="solution.name" value="WebOfficeUpdate"/>
<property name="version.tag" value="0.0.0.1"/>
<property name="release.configuration" value="Release" />
<property name="create.azure.package" value="false" />
<property name="solution.buildspace" value="${path::combine(local.buildspace, solution.name)}" />
<property name="solution.workspace" value="${path::combine(local.workspace, solution.name)}" />
<property name="solution.releasespace" value="${path::combine(local.releasespace, solution.name)}" />
<property name="solution.projects" value="UpdateService2.Migration" />
<property name="solution.src.dir" value="src" />
<property name="buildspace.src.dir" value="${path::combine(solution.buildspace, solution.src.dir)}" />
<target name="CleanLocalBuildspace">
<echo message="recreate dir ${solution.buildspace}"/>
<delete dir="${solution.buildspace}"/>
<mkdir dir="${solution.buildspace}" />
<echo message="recreate dir ${solution.releasespace}"/>
<delete dir="${solution.releasespace}"/>
<mkdir dir="${solution.releasespace}" />
</target>
<target name="AssembleFromWorkspace" depends="CleanLocalBuildspace">
<echo message="Assemble from workspace ${solution.workspace} to ${solution.buildspace}"/>
<copy todir="${buildspace.src.dir}" includeemptydirs="false">
<fileset basedir="${solution.workspace}">
<include name="**/*" />
<exclude name="**/packages/**" />
<exclude name="**/bin/**"/>
<exclude name="**/obj/**"/>
<exclude name="*/Release/**" />
<exclude name="*/Debug/**" />
<exclude name="**/.git/**" />
<exclude name="TestResults/**"/>
<exclude name="**/*.user" />
</fileset>
</copy>
</target>
<target name="UpdateAssemblyInfo">
<foreach item="String" in="${solution.projects}" delim="," property="assembly.project.name">
<property name="src.dir" value="${path::combine(buildspace.src.dir, assembly.project.name)}" />
<property name="assemblyinfo.cs" value="${path::combine(src.dir, 'Properties\AssemblyInfo.cs')}" />
<call target="CreateAssemblyInfo"/>
</foreach>
</target>
<target name="CreateAssemblyInfo">
<echo message="Creating assembly file: ${assemblyinfo.cs}" />
<asminfo output="${assemblyinfo.cs}" language="CSharp">
<imports>
<import namespace="System.Reflection" />
<import namespace="System.Runtime.CompilerServices" />
<import namespace="System.Runtime.InteropServices" />
</imports>
<attributes>
<attribute type="AssemblyTitleAttribute" value="${assembly.project.name}" />
<attribute type="AssemblyDescriptionAttribute" value="" />
<attribute type="AssemblyConfigurationAttribute" value="" />
<attribute type="AssemblyCompanyAttribute" value="${company.name}" />
<attribute type="AssemblyProductAttribute" value="${assembly.project.name}" />
<attribute type="AssemblyCopyrightAttribute" value="Copyright (c) ${company.name} ${datetime::get-year(datetime::now())}" />
<attribute type="AssemblyTrademarkAttribute" value="" />
<attribute type="AssemblyCultureAttribute" value="" />
<attribute type="ComVisibleAttribute" value="false" />
<attribute type="AssemblyVersionAttribute" value="${version.tag}" />
<attribute type="AssemblyFileVersionAttribute" value="${version.tag}" />
</attributes>
</asminfo>
</target>
<target name="compileMigration" description="Compiles and packages the solution">
<echo message="Compiling UpdateService2.Migration"/>
<echo message="out dir:${solution.releasespace}"/>
<exec program="${msbuild4.exe}">
<arg value="${path::combine(buildspace.src.dir,'UpdateService2.Migration\UpdateService2.Migration.csproj')}" />
<arg value="/property:Configuration=${release.configuration}" />
<arg value="/property:DeployOnBuild=true"/>
<arg value="/property:DeployTarget=Package"/>
<arg value="/property:AutoParameterizationWebConfigConnectionStrings=false" if="${create.azure.package}" />
<arg value="/property:OutDir=${solution.releasespace}\"/>
<!--This flag must be set to false on azure builds -->
<arg value="/property:WarningLevel=4" />
<arg value="/property:TreatWarningsAsErrors=true"/>
<arg value="/property:BuildingInsideVisualStudio=false"/>
<arg value="/verbosity:n" />
<arg value="/target:rebuild"/>
<arg value="/l:FileLogger,Microsoft.Build.Engine;logfile=.\build.log"/>
</exec>
</target>
<target name="premigrate" description="pre-target before migrate">
<call target="CleanLocalBuildspace"/>
<call target="AssembleFromWorkspace"/>
<call target="UpdateAssemblyInfo"/>
<call target="compileMigration" />
</target>
<target name="migrate" description="Migrate the database to the latest version">
<call target="premigrate"/>
<migrate
database="${db.type}"
connection="Data Source=${db.path};Version=3;"
target="${path::combine(solution.releasespace,'UpdateService2.Migration.dll')}"
task="migrate"
/>
</target>
<target name="migrate-rollback" description="Migrate the database back one version">
<call target="premigrate" />
<migrate
database="${db.type}"
connection="Data Source=${db.path};Version=3"
target="${path::combine(solution.releasespace,'UpdateService2.Migration.dll')}"
task="rollback:toversion"
to="2"
/>
</target>
<target name="migrate-rollback-all" description="Migrates the database back to original state prior to applying migrations">
<call target="premigrate" />
<migrate
database="${db.type}"
connection="Data Source=${db.path};Version=3"
target="${path::combine(solution.releasespace,'UpdateService2.Migration.dll')}"
task="rollback:all"
/>
</target>
</project>