MSBuild笔记2-Task、Target

Task(任务)

任务是 MSBuild 真正要执行的东西,例如编译、打包和下载文件等等任务,可以由我们自行用 C# 或者 VB.NET 等语言实现。
关于任务的编写,我们将在以后进行介绍,这里只简单介绍一下任务的使用。
MSBuild 也内置了很多任务,例如 Message 用来打印信息、Warn 和 Error 分别用来产生警告和错误、Copy 和 Delete 分别用来复制和删除文件、 MakeDir 用来创建目录、Exec 用来执行程序以及 DownloadFile 用来下载文件等等,具体的内置任务可以去 https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-task-reference 查看。

CallTarget 任务

调用项目文件中的指定目标。

    <Target Name="RemoveDirectories">
	<CallTarget Targets="message1"/><!--调用指定target-->
        <RemoveDir Directories="dir1"/>
    </Target>
    <Target Name="message1">
        <Message Text="dir1 will remove"></Message>
    </Target>

CombinePath 任务

将指定路径合并到单个路径。

	 <PropertyGroup>
	   <PublishRoot>d:\Release</PublishRoot>
	 </PropertyGroup>
	 <ItemGroup>
	   <LangDirectories Include="en-us\;fr-fr\"/>
	 </ItemGroup>
	 <Target Name="CreateOutputDirectories">
	   <CombinePath BasePath="$(PublishRoot)" Paths="@(LangDirectories)" >
		 <Output TaskParameter="CombinedPaths" ItemName="OutputDirectories"/>
	   </CombinePath>
	   <MakeDir Directories="@(OutputDirectories)" />
	 </Target>

生成目录:
D:\Release\en-us
D:\Release\fr-fr

ConvertToAbsolutePath 任务

将相对路径或引用转换为绝对路径。

	 <Target Name="CreateOutputDirectories">
	   <ConvertToAbsolutePath Paths="en-us\;fr-fr\" >
		 <Output TaskParameter="AbsolutePaths" ItemName="OutputDirectories"/>
	   </ConvertToAbsolutePath>
	   <MakeDir Directories="@(OutputDirectories)" />
	 </Target>

在当前目录下生成两个目录
D:\en-us
D:\fr-fr

Copy 任务

将文件复制到文件系统的一个新位置。
案例1:

    <Target Name="CopyFiles">
        <Copy
            SourceFiles="D:\Release\1.txt;D:\Release\2.txt"
            DestinationFolder="D:\Release\back\"
        />
    </Target>

案例2:以下示例演示如何执行递归复制。 此项目以递归方式将所有文件从 c:\MySourceTree 复制到 c:\MyDestinationTree,同时保留目录结构

    <ItemGroup>
        <MySourceFiles Include="c:\MySourceTree\**\*.*"/>
    </ItemGroup>

    <Target Name="CopyFiles">
        <Copy
            SourceFiles="@(MySourceFiles)"
            DestinationFiles="@(MySourceFiles->'c:\MyDestinationTree\%(RecursiveDir)%(Filename)%(Extension)')"
        />
    </Target>

CreateItem 任务

创建Item

 <ItemGroup>
        <MySourceItems Include="file1.resx;file2.resx" />
    </ItemGroup>

    <Target Name="NewItems">
        <CreateItem
            Include="@(MySourceItems)"
            AdditionalMetadata="MyMetadata=Hello">
           <Output
               TaskParameter="Include"
               ItemName="MySourceItemsWithMetadata"/>
        </CreateItem>
		<Message Text="%(MySourceItemsWithMetadata.MyMetadata)"></Message>
    </Target>

CreateProperty 任务

创建新属性,使用传入的值填充属性。

 <PropertyGroup>
        <SourceFilename>Module1</SourceFilename>
        <SourceFileExtension>vb</SourceFileExtension>
    </PropertyGroup>

    <Target Name="CreateProperties">
        <CreateProperty
            Value="/$(SourceFilename).$(SourceFileExtension)">
            <Output
                TaskParameter="Value"
                PropertyName="NewFile" />
        </CreateProperty>
		<Message Text="$(NewFile)"></Message>
    </Target>

Csc 任务

包装 csc.exe,生成可执行 (.exe) 文件、动态链接库(.dll 文件)或者代码模块(.netmodule 文件) 。
Demo:
先写如下CS代码,存为d:\MSBuildDemo.cs

public class MSBuildDemo
 {
     static void Main()
     {
         System.Console.WriteLine("MSBuild组织编译");
     }
 }

项目文件:

<?xml version="1.0" encoding="utf-8"?>
  <Project DefaultTargets="build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
      <!--指定要编译的文件-->
      <CSFile Include="MSBuildDemo.cs"/>
    </ItemGroup>
    <Target Name="build">
      <!--使用Csc任务,对应csc编译器-->
      <!--Sources属性表示要编译的文件集合-->
     <!--TargetType表示编译目标类型,对应csc编译器的/target参数-->
     <Csc Sources="@(CSFile)"
          TargetType="exe">
       <!--OutputAssembly为csc的输出参数-->
       <!--PropertyName表示把TaskParameter属性所指定的输出参数的值存储到outputExeName这个属性中-->
       <!--Output还有一个ItemName属性,表示存储到一个项中-->
       <Output TaskParameter="OutputAssembly" PropertyName="outputExeFileName"/>
     </Csc>

     <!--输出MSBuildDemo.exe-->
     <Message Text="$(outputExeFileName)"/>
     <!--Exec任务可以运行带有指定程序(可加参数)或命令-->
     <!--运行刚从MSBuildDemo.cs源文件编译好的程序-->
     <!--运行结果为"MSBuild组织编译"-->
     <Exec Command="$(outputExeFileName)"></Exec>
   </Target>
 </Project>

Delete 任务

删除指定的文件。

    <Target Name="Delete">
        <Delete Files="d:/Release/1.txt" />
    </Target>

DownloadFile 任务

使用超文本传输协议 (HTTP) 下载指定文件。

Error 任务

基于评估的条件语句,停止生成操作并记录错误。如果 Condition 参数评估为 true,将停止生成,并记录一个错误。 如果 Condition 参数不存在,将记录错误并停止执行生成。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="ValidateCommandLine">
        <Error
            Text=" The 0 property must be set on the command line."
            Condition="'$(0)' == ''" />
        <Error
            Text="The FREEBUILD property must be set on the command line."
            Condition="'$(FREEBUILD)' == ''" />
    </Target>
</Project>

Exec 任务

运行指定程序或命令。

    <Target Name="exec">
        <Exec Command="echo hello world" />
    </Target>

FindAppConfigFile 任务
FindInList 任务
FindUnderPath 任务
FormatUrl 任务
FormatVersion 任务
GenerateApplicationManifest 任务
GenerateBootstrapper 任务
GenerateDeploymentManifest 任务
GenerateResource 任务
GenerateTrustInfo 任务
GetAssemblyIdentity 任务
GetFileHash 任务
GetFrameworkPath 任务
GetFrameworkSdkPath 任务
GetReferenceAssemblyPaths 任务
LC 任务
MakeDir 任务
Message 任务
Move 任务
MSBuild 任务
ReadLinesFromFile 任务
RegisterAssembly 任务
RemoveDir 任务
RemoveDuplicates 任务
RequiresFramework35SP1Assembly 任务
ResolveAssemblyReference 任务
ResolveComReference 任务
ResolveKeySource 任务
ResolveManifestFiles 任务
ResolveNativeReference 任务
ResolveNonMSBuildProjectOutput 任务
SGen 任务
SignFile 任务
Touch 任务
UnregisterAssembly 任务
Unzip 任务
UpdateManifest 任务
Vbc 任务
VerifyFileHash 任务
Warning 任务
WriteCodeFragment 任务
WriteLinesToFile 任务
XmlPeek 任务
XmlPoke 任务
XslTransformation 任务

MakeDir、RemoveDir

  <Project DefaultTargets="RemoveDirectories" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    <Target Name="CreateDirectories">
        <MakeDir Directories="dir1"/>
    </Target>
    <Target Name="RemoveDirectories"  DependsOnTargets="CreateDirectories">
        <RemoveDir Directories="dir1"/>
    </Target>
 </Project>

DependsOnTargets属性:这个属性会告诉MSBuild,每次执行当前任务之前先执行指定任务。

目标

目标是一组任务的集合,我们简单理解为:为了完成一个目标,需要执行一系列的任务。
例如:

<Target Name="Print">
  <Message Text="Hello" />
</Target>

这样我们就定义了一个叫做 Print 的目标,它用来输出一个 Hello

此时我们用 -target-t 指定执行 Print

msbuild build.proj -target:Print

将会输出 Hello

目标执行顺序

目标之间可以用过 BeforeTargetsAfterTargets 来设置顺序(但是相互之间没有依赖),还可以使用 DependsOnTargets 来设置依赖,例如:

<Target Name="PrintBye" DependsOnTargets="PrintHello;PrintWorld">
  <Message Text="Bye" />
</Target>

<Target Name="PrintHello">
  <Message Text="Hello" />
</Target>

<Target Name="PrintWorld" AfterTargets="PrintWorld">
  <Message Text="World" />
</Target>

执行构建 PrintBye:

msbuild build.proj -target:PrintBye

将会输出:

Copy
Hello
World
Bye

可以通过在项目上设置 DefaultTargets 表示如果没有通过命令行参数传入目标则默认执行的目标,还可以设置 InitialTargets 来表示始终最先执行的目标。

调用目标

我们可以利用 CallTarget 任务来调用一个目标,然后获取调用的目标的 Outputs 的输出,通过这种方式,我们不需要手动编写任务也能实现函数调用,例如:

<Target Name="Hello" Returns="$(Result)">
  <Message Text="你好,$(Name)" Importance="high" />
  <PropertyGroup>
    <Result>和 $(Name) 打了招呼</Result>
  </PropertyGroup>
</Target>

<Target Name="Build">
  <PropertyGroup>
    <MyResult>还没调用结果</MyResult>
  </PropertyGroup>
  <Message Text="$(MyResult)" Importance="high" />
  <CallTarget Targets="Hello">
    <!-- 把 Hello 目标的输出存到 MyResult 属性中 -->
    <Output TaskParameter="TargetOutputs" PropertyName="MyResult"/>
  </CallTarget>
  <Message Text="$(MyResult)" Importance="high" />
</Target>

我们执行构建:

msbuild build.proj -target:Build -property:Name=Bob

得到输出:

还没调用结果
你好,Bob
和 Bob 打了招呼

如果要给这个 Hello 目标像函数调用那样传入参数怎么办?此时可以用 MSBuild 任务,通过 Properties 来传递属性,多个属性同样是通过 ; 分隔,并且其中可以用 $、@ 等引用其他属性和项:

<Target Name="Hello" Returns="$(Result)">
  <Message Text="你好,$(Age) 岁的 $(Name)" Importance="high" />
  <PropertyGroup>
    <Result>和 $(Name) 打了招呼</Result>
  </PropertyGroup>
</Target>

<Target Name="Build">
  <PropertyGroup>
    <MyResult>还没调用结果</MyResult>
  </PropertyGroup>
  <Message Text="$(MyResult)" Importance="high" />
  <MSBuild Targets="Hello" Properties="Age=18" Projects="build.proj">
    <Output TaskParameter="TargetOutputs" PropertyName="MyResult"/>
  </MSBuild>
  <Message Text="$(MyResult)" Importance="high" />
</Target>

然后我们传入一个 Age 属性进去来调用构建:

msbuild build.proj -target:Build -property:Name=Bob -p:Age=18

这次将会得到输出:

还没调用结果
你好,18 岁的 Bob
和 Bob 打了招呼

要注意的是,目标的相互调用并不是一个好的实践,容易带来性能问题,本文之所以写了这个只是为了展示有这样的功能,实际编写构建文件时不要滥用这个特性。

参考:
https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-task-reference?view=vs-2019

posted @ 2020-10-17 17:48  .Neterr  阅读(439)  评论(0编辑  收藏  举报