zoukankan      html  css  js  c++  java
  • MSBuild入门(续)

    MSBuild基本概念(续)

    在上一篇简单的介绍了下MSBuild中的四个基本块,每块介绍比较单薄,在这里对在大多数的项目模版生成的*.*proj文件中比较常见一些用法和概念做些补充。主要有一下几方面:

    MSBuild特殊字符:MSBuild保留的一些字符,以及XML中的特殊字符处理。
    MSBuild条件: Condition特性,作用类似于C#的if。
    MSBuild属性: 使用环境变量、保留属性、全局属性。
    MSBuild项: 元数据、项转换。
    MSBuild任务: ITask接口、UsingTask[自定义任务]、ContinueOnError。
    MSBuild目标: 初始目标、默认目标、目标依赖项。
    Import元素: 导入项目文件到当前项目文件。

    MSBuild特殊字符

    一些字符在MSBuild中代表着特殊的上下文含义,如下:

    MSBuild的特殊字符[%引用元数据]、[$引用属性]、[@引用项]、['条件或其他表达式]、[;列表分隔符]、[?文件名通配符]、[*文件名通配符];
    XML的保留字符:<、>、&、"、'

    针对MSBuild的特殊字符转义需要用[%xx]这种方式,xx代表字符的ASCII十六进制值([%=%25][$=%24][@=%40] ['=%27][;=%3B][?=%3F][*=%2A])。针对XML保留字符则使用&lt这种方式。 一般用到这些特殊字符的情况不多,见到时能知道是转义就可以了。

    MSBuild条件

    条件在*.*proj项目文件中非常常见,用Condition特性来表示一个布尔表达式,类似于if条件,几乎所有的元素都可以具有Conditon特性。一个简单的例子如下:

    复制代码
     1 <?xml version="1.0" encoding="utf-8"?>
     2 <!--condition.xml文件-->
     3 <Project DefaultTargets="show" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
     4   <PropertyGroup>
     5     <!--Condition在属性、项、任务、目标生都有使用-->
     6     <!--如果Configuration为空(''),则其值为Debug-->
     7     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     8     <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
     9   </PropertyGroup>
    10   <ItemGroup>
    11     <!--如果csfile1.cs文件存在就包含在CSFile项中-->
    12     <CSFile Include="csfile1.cs" Condition="Exists('csfile1.cs')"></CSFile>
    13   </ItemGroup>
    14   <Target Name="show">
    15     <!--输出Debug|x86-->
    16     <Message Text="$(Configuration)|$(Platform)"/>
    17     <!--输出空,因为csfile1.cs并不存在-->
    18     <Message Text="@(CSFile)"/>
    19   </Target>
    20 </Project>
    复制代码

    还有一些常用的表达式如!=、!、And、Or等。

    MSBuild属性

    上篇介绍到可以用$可以引用自定义的属性,除此之外亦可以引用系统的环境变量,如$(Path),以及 MSBuild保留属性(MSDN)

    属性除了可以在项目文件中声明是赋值外,在MSBuild命令行也允许设置属性的值(语法:/p:propertyName=value)。称作全 局属性,这类属性会重写在项目文件中设置的属性值,保留属性除外的任何属性都可被这种方式覆盖其原值。 以上面示例为基础:[MSBuild condition.xml /p:Platform=x64],则最终输出结果就为Debug|x64了。

    属性还有一种叫做任务发出属性,在上篇用到了,由Output元素的PropertyName特性指定了属性名,这类属性不像一般的声明式属性那样赋值,而是动态得到的值。是在项目文件中很常见的用法。

    MSBuild项

    项大都是用来引入文件用的,而文件会有一些附加信息,比如版本,语言等,而这些附加信息在项目文件中是以项的子元素的出现的,称为项的元数据。元数据是键/值的形式存储的,声明方式和属性相同。

    复制代码
    1   <ItemGroup>
    2     <!--如果csfile1.cs文件存在就包含在CSFile项中-->
    3     <CSFile Include="csfile1.cs">
    4       <!--声明元数据,必须为项的一级子元素-->
    5       <!--引用方式:%(CSFile.Culture)-->
    6       <Culture>zh-cn</Culture>
    7     </CSFile>
    8   </ItemGroup>
    复制代码

    除了自定义的一些元数据外,系统还提供一些隐式存在的元数据,即不用声明即可使用,具体可参见MSBuild常见的已知元数据。引用这类元数据的语法和自定义的完全相同。

    项转换允许把一个项的列表与另一个列表一一变换。比如下面的例子:

    复制代码
     1 <?>
     5     <CSFile Include="1.cs;2.cs"/>
     6     <!--%(Filename)为项的元数据,由系统提供-->
     7     <VBFile Include="@(CSFile->'%(Filename).vb')"/>
     8   </ItemGroup>
     9   <Target Name="show">
    10     <!--输出1.cs;2.cs-->
    11     <Message Text="@(CSFile)"/>
    12     <!--输出1.vb;2.vb-->
    13     <Message Text="@(VBFile)"/>
    14   </Target>
    15 </Project>
    复制代码

    MSBuild任务

    从上篇中我们对任务的认识是它是一个原子操作,用来执行某一项逻辑处理,但是xml格式的项目文件是没有这个处理能力的,所以这些任务都是映射到.NET类库中的一些类,由这些类来处理操作中的逻辑。具体来说都是实现ITask接口的类,ITask接口位于Microsoft.Build.Framework命名空间。当然我们也可以实现自己的任务类,直接实现ITask接口或者继承自Task(此抽象类实现了ITask接口的部分功能,可简化自定义任务类的编写,留出一个Execute抽象方法供子类重写自己的任务逻辑)。然后通过UsingTask元素映射到出一个任务元素。我就继承Task写一个简单的示例:

    复制代码
     1 //AddTwoNumberTask.cs,需编译为dll
     2 using System;
     3 using Microsoft.Build.Utilities;
     4 using Microsoft.Build.Framework;
     5  
     6 /// <summary>
     7 /// 继承Task,任务逻辑是处理加法
     8 /// </summary>
     9 public class AddTwoNumberTask : Task
    10 {
    11     /// <summary>
    12     /// 定义一个加数,
    13     /// 如果一个输入属性被要求必须输入,则用[Required]特性标识该属性
    14     /// </summary>
    15     public String Number1 { get; set; }
    16     /// <summary>
    17     /// 定义另一个加数
    18     /// </summary>
    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     /// <summary>
    27     /// 定义一个输出参数,使用Output特性修饰该属性
    28     /// </summary>
    29     [Output]
    30     public String Sum { get; set; }
    31 }
    复制代码
     然后编写如下项目文件,放于AddTwoNumberTask.cs同目录下:
    复制代码
     1 <!--buildAddTaskDll.csproj-->
     2 <?xml version="1.0" encoding="utf-8"?>
     3 <!--从AddTwoNumberTask.cs源文件到编译成dll-->
     4 <Project DefaultTargets="buildAddTaskDll"  ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
     5 <PropertyGroup>
     6     <OutputType>Library</OutputType>
     7 </PropertyGroup>
     8 <ItemGroup>
     9     <Reference Include="Microsoft.Build.Framework" />
    10     <Reference Include="Microsoft.Build.Utilities.v4.0" />
    11     <Reference Include="System" />
    12 </ItemGroup>
    13 <ItemGroup>
    14     <Compile Include="AddTwoNumberTask.cs" />
    15 </ItemGroup>
    16 <Target Name="buildAddTaskDll">
    17     <!--@(Reference->'$(MSBuildBinPath)\%(Identity).dll')表示项转换-->
    18     <Csc Sources="@(Compile)"
    19     References="@(Reference->'$(MSBuildBinPath)\%(Identity).dll')"
    20         TargetType="$(OutputType)">
    21     </Csc>
    22 </Target>
    23 </Project>
    复制代码

    用MSBuild编译buildAddTaskDll.csproj项目文件。得到AddTwoNumberTask.dll程序集。再编写一个项目文件usingtask如下:

    复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <!--使用自定义的任务做加法-->
    <Project DefaultTargets="show"  ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <UsingTask TaskName="AddTwoNumberTask" AssemblyFile = "AddTwoNumberTask.dll"/>
      <Target Name="show">
        <AddTwoNumberTask Number1="1" Number2="2" >
          <Output TaskParameter="Sum" PropertyName="SumValue"/>
        </AddTwoNumberTask>
        <!--输出3-->
        <Message Text="$(SumValue)"/>
      </Target>
    </Project>
    复制代码

    如果仔细看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 <?xml version="1.0" encoding="utf-8"?>
     2 <!--目标执行顺序-->
     3 <!--如果InitialTargets特性存在,则首先执行此目标列表-->
     4 <!--如果DefaultTargets特性存在,则继续执行此目标列表-->
     5 <Project InitialTargets="B1;B2" DefaultTargets="B3;B4"  
     6          ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
     7   <!--如果发现Target具有DependsOnTargets特性-->
     8   <!--则先执行DependsOnTargets指定的目标-->
     9   <!--MSBuild4新加入了RunBeforeTargets和RunAfterTargets特性-->
    10   <!--其作用和DependsOnTargets类似,一前一后,不做演示了-->
    11   <Target Name="B1" DependsOnTargets="B5">
    12     <Message Text="B1"/>
    13   </Target>
    14   <Target Name="B2">
    15     <Message Text="B2"/>
    16   </Target>
    17   <Target Name="B3">
    18     <Message Text="B3"/>
    19   </Target>
    20   <Target Name="B4">
    21     <Message Text="B4"/>
    22   </Target>
    23   <Target Name="B5">
    24     <Message Text="B5"/>
    25   </Target>
    26   <!--结果为:B5 B1 B2 B3 B4-->
    27 </Project> 
    复制代码

    Import元素

    项目模版产生的*.*proj项目文件大量的使用这个元素,用来导入可重用的项目文件,其中最常见的一个应该是这个吧,如果你用C#开发的话。

    <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

    MSBuildToolsPath或者是MSBuildBinPath,Project特性指定要导入的项目文件。Import元素像是一个占位元素,MSBuild在执行到此时会用*.targets替换掉此元素,就像本来就声明在这里一样,所以和*.targets文件有关的所有保留属性会被重置。 Import元素对导入文件的扩展名无要求,文件是正确的项目文件就行,但一般约定为*.targets。

    总结和备注

    了解了以上知识点后,阅读一般的项目模版生成的项目文件(*.*proj)应该是可以的了,下篇文章先认识几个重要的*.targets,为剖析项目文件做准备。

    备注:针对项目文件中所指的“特性”是表示一个xml元素的“属性”。由于属性在MSBuild中有特殊含义,则MSDN文档一律把项目文件中的xml属性称作是特性,比如Message任务的Text特性。

  • 相关阅读:
    公用表表达式(CTE)的递归调用
    c# 如何让tooltip显示文字换行
    实战 SQL Server 2008 数据库误删除数据的恢复
    SQL SERVER数据库中 是否可以对视图进行修改删除
    asp.net中实现文件批量上传
    sql server 2008学习2 文件和文件组
    sql server 2008学习3 表组织和索引组织
    sql server 2008学习4 设计索引的建议
    sql server 2008学习10 存储过程
    .net 调用 sql server 自定义函数,并输出返回值
  • 原文地址:https://www.cnblogs.com/zhangchenliang/p/2666835.html
Copyright © 2011-2022 走看看