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
目标执行顺序
目标之间可以用过 BeforeTargets
和 AfterTargets
来设置顺序(但是相互之间没有依赖),还可以使用 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