代码改变世界

浅谈VS编译自定义编译任务—MSBuild Task(csproject)

2011-07-27 23:17  破狼  阅读(14917)  评论(5编辑  收藏  举报

     在上一篇浅谈.NET编译时注入(C#-->IL)中我们简单的反编译查看了几种c#语法糖和PostSharp在编译成IL时为我做的MSIL注入。紧接着在这节,要来看的就是MSBuild Task。在我们的代码预编译过程中我们可以创建我们自己的任务Task。下面我们就开始做一个简单的Task。

1:首先需要添加Microsoft.Build.Utilities.v3.5.dll和Microsoft.Build.Framework.dll中引用。在Microsoft.Build.Framework中为我们定义了接口ITask,自定义任务需要去实现这个契约。其定义如下: 

  public interface ITask 
    { 
       IBuildEngine BuildEngine { 
getset; } 
       ITaskHost HostObject { 
getset; } 
       
bool Execute(); 
    }

IBuildEngine 从字义上说是编译引擎,他主要承载了我们的编译生成时的信息和消息,警告,错误等事件注册。而Execute这是我们Task执行体。HostObject 任务关联宿主信息。

在Microsoft.Build.Utilities下Task为我们实现了基本的ITask信息。我们可以从这里继承开始。

下面是一个简单的实现:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace FirstBuildTask 

    
public class MyBuildTask : Microsoft.Build.Utilities.Task 
    { 
        
private string outputFile; 

        [Microsoft.Build.Framework.Required] 
        
public string OutputFile 
        { 
            
get { return outputFile; } 
            
set { outputFile = value; } 
        } 

        
public override bool Execute() 
        { 
            Log.LogWarning(
"test message:" + this.outputFile); 
            
return true
        } 
    } 
}

下面我们需要关联Task,本人不喜欢污染,所以加到csproject(也可全局所有项目使用)。

下面我们创建一个简单的控制台

程序

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace BlogSample 

    
class Program 
    { 
        
static void Main(string[] args) 
        { 
            Console.WriteLine(
"ok"); 
            Console.Read(); 
        } 
    } 
}

用记事本形式打开csproject文件:

添加我们的Task声明和任务。(xml标记含义将在后续,这里先看看效果。)

声明Task:

<UsingTask TaskName="MyBuildTask" AssemblyFile="XXXX\bin\Debug\FirstBuildTask1.dll"/>

添加任务:

<Target Name="AfterBuild"> 
<MyBuildTask OutputFile="$(MSBuildProjectFullPath)"/> 
  
</Target>

修改后的csproject文件形如:

View Code
<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003%22> 
  <PropertyGroup> 
    <Configuration Condition="
 '$(Configuration)' == '' ">Debug</Configuration> 
    
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> 
    
<ProductVersion>9.0.30729</ProductVersion> 
    
<SchemaVersion>2.0</SchemaVersion> 
    
<ProjectGuid>{844D0C87-9808-4AE9-8906-0382D9DDF88A}</ProjectGuid> 
    
<OutputType>Exe</OutputType> 
    
<AppDesignerFolder>Properties</AppDesignerFolder> 
    
<RootNamespace>BlogSample</RootNamespace> 
    
<AssemblyName>BlogSample</AssemblyName> 
    
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> 
    
<FileAlignment>512</FileAlignment> 
  
</PropertyGroup> 
  
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> 
    
<DebugSymbols>true</DebugSymbols> 
    
<DebugType>full</DebugType> 
    
<Optimize>false</Optimize> 
    
<OutputPath>bin\Debug\</OutputPath> 
    
<DefineConstants>DEBUG;TRACE</DefineConstants> 
    
<ErrorReport>prompt</ErrorReport> 
    
<WarningLevel>4</WarningLevel> 
  
</PropertyGroup> 
  
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> 
    
<DebugType>pdbonly</DebugType> 
    
<Optimize>true</Optimize> 
    
<OutputPath>bin\Release\</OutputPath> 
    
<DefineConstants>TRACE</DefineConstants> 
    
<ErrorReport>prompt</ErrorReport> 
    
<WarningLevel>4</WarningLevel> 
  
</PropertyGroup> 
  
<ItemGroup> 
    
<Reference Include="System" /> 
    
<Reference Include="System.Core"> 
      
<RequiredTargetFramework>3.5</RequiredTargetFramework> 
    
</Reference> 
    
<Reference Include="System.Xml.Linq"> 
      
<RequiredTargetFramework>3.5</RequiredTargetFramework> 
    
</Reference> 
    
<Reference Include="System.Data.DataSetExtensions"> 
      
<RequiredTargetFramework>3.5</RequiredTargetFramework> 
    
</Reference> 
    
<Reference Include="System.Data" /> 
    
<Reference Include="System.Xml" /> 
  
</ItemGroup> 
  
<ItemGroup> 
    
<Compile Include="DisposeSubstance.cs" /> 
    
<Compile Include="Program.cs" /> 
    
<Compile Include="Properties\AssemblyInfo.cs" /> 
  
</ItemGroup> 
  
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> 
<UsingTask TaskName="MyBuildTask" AssemblyFile="XXXXX\bin\Debug\FirstBuildTask1.dll"/> 
  
<Target Name="BeforeBuild"> 
  
</Target> 
  
<Target Name="AfterBuild"> 
<MyBuildTask OutputFile="$(MSBuildProjectFullPath)"/> 
  
</Target> 
</Project>

此时我们可以用VS重新加载编译或者是MSBuild控制台编译:

效果如下:

image

上边红线注释的就是我们的Task所做的警告和输出路径的提示。

我们已经完成了一个简单Task,但还遗留这MsBuild配置,下面将简单的描述。

1:UsingTask:定义:<UsingTask TaskName="TaskName" AssemblyName = "AssemblyName" TaskFactory = "ClassName" Condition="'String A'=='String B'" />

描述我们的任务的程序集,任务类等信息,具体参见http://msdn.microsoft.com/zh-cn/library/t41tzex2.aspx

属性 说明

AssemblyName

AssemblyNameAssemblyFile 属性是必需的。

要加载的程序集的名称。尽管 AssemblyName 属性不是必需的,但它接受强名称程序集。使用此属性等效于通过 .NET Framework 中的 Load 方法加载程序集。

如果使用了 AssemblyFile 属性,便不能使用此属性。

AssemblyFile

AssemblyNameAssemblyFile 属性是必需的。

程序集的文件路径。此属性既接受完整路径,也接受相对路径。相对路径是相对于当前项目目录的路径。使用此属性等效于通过 .NET Framework 中的 LoadFrom 方法加载程序集。

如果使用了 AssemblyName 属性,便不能使用此属性。

TaskName

必选的属性。

要从程序集中引用的任务的名称。如果可能存在多义性,则此属性应该始终指定完整的命名空间。如果存在多义性,MSBuild 将选择任意匹配方式,该匹配方式可能产生意外的结果。

Condition

可选的属性。

要计算的条件。有关更多信息,请参见 MSBuild 条件

在系统中为我们定义了很多Task,有:

AL(程序集链接器)任务:描述 AL 任务及其参数。
AspNetCompiler 任务:包装 aspnet_compiler.exe,它是预编译 ASP.NET 应用程序的实用工具。
AssignCulture 任务:为项分配区域性标识符。
CallTarget 任务:调用项目文件中的目标。
Copy 任务:描述 Copy 任务及其参数。
CreateItem 任务:描述 CreateItem 任务及其参数。
CreateProperty 任务:描述 CreateProperty 任务及其参数。
Csc 任务:描述 Csc 任务及其参数。
Delete 任务:描述 Delete 任务及其参数。
Error 元素 (MSBuild):根据计算的条件语句停止生成操作并记录错误。
Exec 任务:描述 Exec 任务及其参数。
FindUnderPath 任务:确定指定项集合中的哪些项存在于指定的文件夹及其所有子文件夹中。
GenerateApplicationManifest 任务:描述 GenerateApplicationManifest 任务及其参数。
GenerateBootstrapper 任务:提供一种自动化方式来检测、下载和安装应用程序及其必备组件。
GenerateDeploymentManifest 任务:描述 GenerateDeployManifest 任务及其参数。
GenerateResource 任务:将 .txt 和 .resx 文件转换为公共语言运行库二进制 .resources 文件。
GetAssemblyIdentity 任务:从指定的文件检索程序集标识并输出标识信息。
GetFrameworkPath 任务:检索 .NET Framework 程序集的路径。
GetFrameworkSdkPath 任务:检索 .NET Framework SDK 的路径。
LC 任务:描述 LC 任务及其参数。
MakeDir 任务:描述 MakeDir 任务及其参数。
Message 元素 (MSBuild):在生成期间记录消息。
MSBuild 任务:描述 MSBuild 任务及其参数。
ReadLinesFromFile 任务:从文本文件读取项列表。
RegisterAssembly 任务:描述 RegisterAssembly 任务及其参数。
RemoveDir 任务:描述 RemoveDir 任务及其参数。
ResGen 任务:描述 ResGen 任务及其参数。
ResolveAssemblyReference 任务:描述 ResolveAssemblyReference 任务及其参数。
ResolveComReference 任务:描述 ResolveCOMReference 任务及其参数。
ResolveKeySource 任务:确定强名称密钥源
ResolveNativeReference 任务:解析本机引用。
SGen 任务:为指定程序集中的类型创建一个 XML 序列化程序集。
SignFile 任务:使用指定证书对指定文件进行签名。
Touch 任务:描述 Touch 任务及其参数。
UnregisterAssembly 任务:描述 UnregisterAssembly 任务及其参数。
Vbc 任务”描述 Vbc 任务及其参数。
VCBuild 任务“描述 VCBuild 任务及其参数。
Warning 元素 (MSBuild):根据计算的条件语句在生成期间记录警告。
WriteLinesToFile 任务:将指定项写入指定的文本文件。
其实很多我都不知道,我经常是用的时候在去查MSDN:http://msdn.microsoft.com/zh-cn/library/7z253716(v=VS.80).aspx

2:Target:定义

<Target Name="Target Name" DependsOnTargets="DependentTarget" Inputs="Inputs" Outputs="Outputs" Condition="'String A' == 'String B'"> <Task>... </Task> <OnError... /> </Target>

描述了我们的目标:可以包含0个多个任务。

属性

属性 说明

Name

必选的属性。

目标的名称。

DependsOnTargets

可选的属性。

在执行此目标或者进行顶级依赖项分析之前必须执行的目标。多个目标之间用分号分隔。

Inputs

可选的属性。

此目标的项输入。此属性中的项用作顶级依赖项分析中的输入。

Outputs

可选的属性。

此目标的预期输出。可以通过对输入项应用转换来生成输出项。有关转换的更多信息,请参见 MSBuild 转换

Condition

可选的属性。

要计算的条件。如果条件的计算结果为 false,目标将不会执行该目标的体或者在 DependsOnTargets 属性中设置的任何目标的体。有关条件的更多信息,请参见 MSBuild

msdn:http://msdn.microsoft.com/zh-cn/library/t50z2hka(VS.80).aspx

参考文献:

http://msdn.microsoft.com/zh-cn/library/t41tzex2.aspx

http://msdn.microsoft.com/zh-cn/library/t50z2hka(VS.80).aspx

http://msdn.microsoft.com/zh-cn/library/ms171466(v=VS.80).aspx

http://msdn.microsoft.com/zh-cn/library/7z253716(v=VS.80).aspx

http://msdn.microsoft.com/zh-cn/library/ms164313(v=VS.80).aspx

http://msdn.microsoft.com/zh-cn/library/t9883dzc(v=VS.80).aspx

http://msdn.microsoft.com/zh-cn/library/ms171464(v=VS.80).aspx

MSBuild概念:http://msdn.microsoft.com/zh-cn/library/ms171451(v=VS.80).aspx