MSBuild inline tasks
Starting in .NET Framework version 4, you can create tasks inline in the project file. You do not have to create a separate assembly to host the task. This makes it easier to keep track of source code and easier to deploy the task. The source code is integrated into the script.
In MSBuild 15.8, the RoslynCodeTaskFactory was added which can create .NET Standard cross-platform inline tasks. If you need to use inline tasks on .NET Core, you must use the RoslynCodeTaskFactory.
The structure of an inline task
An inline task is contained by a UsingTask element. The inline task and the
UsingTask element that contains it are typically included in a .targets file and imported into other project files as required. Here is a basic inline task. Notice that it does nothing.
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <!-- This simple inline task does nothing. --> <UsingTask TaskName="DoNothing" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" > <ParameterGroup /> <Task> <Reference Include="" /> <Using Namespace="" /> <Code Type="Fragment" Language="cs"> </Code> </Task> </UsingTask> </Project>
UsingTask element in the example has three attributes that describe the task and the inline task factory that compiles it.
TaskNameattribute names the task, in this case,
TaskFactoryattribute names the class that implements the inline task factory.
AssemblyFileattribute gives the location of the inline task factory. Alternatively, you can use the
AssemblyNameattribute to specify the fully qualified name of the inline task factory class, which is typically located in the global assembly cache (GAC).
The remaining elements of the
DoNothing task are empty and are provided to illustrate the order and structure of an inline task. A more robust example is presented later in this topic.
ParameterGroupelement is optional. When specified, it declares the parameters for the task. For more information about input and output parameters, see Input and output parameters later in this topic.
Taskelement describes and contains the task source code.
Referenceelement specifies references to the .NET assemblies that you are using in your code. This is equivalent to adding a reference to a project in Visual Studio. The
Includeattribute specifies the path of the referenced assembly.
Usingelement lists the namespaces that you want to access. This resembles the
Usingstatement in Visual C#. The
Namespaceattribute specifies the namespace to include.
Using elements are language-agnostic. Inline tasks can be written in any one of the supported .NET CodeDom languages, for example, Visual Basic or Visual C#.
Elements contained by the
Task element are specific to the task factory, in this case, the code task factory.
The last child element to appear within the
Task element is the
Code element. The
Code element contains or locates the code that you want to be compiled into a task. What you put in the
Code element depends on how you want to write the task.
Language attribute specifies the language in which your code is written. Acceptable values are
cs for C#,
vb for Visual Basic.
Type attribute specifies the type of code that is found in the
If the value of
Class, then the
Codeelement contains code for a class that derives from the ITask interface.
If the value of
Method, then the code defines an override of the
Executemethod of the ITask interface.
If the value of
Fragment, then the code defines the contents of the
Executemethod, but not the signature or the
The code itself typically appears between a
<![CDATA[ marker and a
]]> marker. Because the code is in a CDATA section, you do not have to worry about escaping reserved characters, for example, "<" or ">".
Alternatively, you can use the
Source attribute of the
Code element to specify the location of a file that contains the code for your task. The code in the source file must be of the type that is specified by the
Type attribute. If the
Source attribute is present, the default value of
Source is not present, the default value is
When defining the task class in the source file, the class name must agree with the
TaskName attribute of the corresponding UsingTask element.
Here is a more robust inline task. The HelloWorld task displays "Hello, world!" on the default error logging device, which is typically the system console or the Visual Studio Output window. The
Reference element in the example is included just for illustration.
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <!-- This simple inline task displays "Hello, world!" --> <UsingTask TaskName="HelloWorld" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" > <ParameterGroup /> <Task> <Reference Include="System.Xml"/> <Using Namespace="System"/> <Using Namespace="System.IO"/> <Code Type="Fragment" Language="cs"> <![CDATA[ // Display "Hello, world!" Log.LogError("Hello, world!"); ]]> </Code> </Task> </UsingTask> </Project>
You could save the HelloWorld task in a file that is named HelloWorld.targets, and then invoke it from a project as follows.
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="HelloWorld.targets" /> <Target Name="Hello"> <HelloWorld /> </Target> </Project>
Input and output parameters
Inline task parameters are child elements of a
ParameterGroup element. Every parameter takes the name of the element that defines it. The following code defines the parameter
<ParameterGroup> <Text /> </ParameterGroup>
Parameters may have one or more of these attributes:
Requiredis an optional attribute that is
falseby default. If
true, then the parameter is required and must be given a value before calling the task.
ParameterTypeis an optional attribute that is
System.Stringby default. It may be set to any fully qualified type that is either an item or a value that can be converted to and from a string by using System.Convert.ChangeType. (In other words, any type that can be passed to and from an external task.)
Outputis an optional attribute that is
falseby default. If
true, then the parameter must be given a value before returning from the Execute method.
<ParameterGroup> <Expression Required="true" /> <Files ParameterType="Microsoft.Build.Framework.ITaskItem" Required="true" /> <Tally ParameterType="System.Int32" Output="true" /> </ParameterGroup>
defines these three parameters:
Expressionis a required input parameter of type System.String.
Filesis a required item list input parameter.
Tallyis an output parameter of type System.Int32.
Code element has the
Type attribute of
Method, then properties are automatically created for every parameter. Otherwise, properties must be explicitly declared in the task source code, and must exactly match their parameter definitions.
The following inline task replaces every occurrence of a token in the given file with the given value.
<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="15.0"> <UsingTask TaskName="TokenReplace" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll"> <ParameterGroup> <Path ParameterType="System.String" Required="true" /> <Token ParameterType="System.String" Required="true" /> <Replacement ParameterType="System.String" Required="true" /> </ParameterGroup> <Task> <Code Type="Fragment" Language="cs"><![CDATA[ string content = File.ReadAllText(Path); content = content.Replace(Token, Replacement); File.WriteAllText(Path, content); ]]></Code> </Task> </UsingTask> <Target Name='Demo' > <TokenReplace Path="C:\Project\Target.config" Token="$MyToken$" Replacement="MyValue"/> </Target> </Project>
We'd love to hear your thoughts. Choose the type you'd like to provide:
Our feedback system is built on GitHub Issues. Read more on our blog.