In the last few days I was intensively working with MsBuild, as I had to automate the building and deployment of 3 projects to a large amount of environments. In order to achieve this, an obvious choice was to use the MsBuild, which in my opinion is a great framework that perfectly fits with the .NET world. Obviously there is a learning curve, but once mastered it’s really a joy to use.
MsBuild is primarily made of Targets and Tasks. Especially Tasks have a function to enable and enrich the MsBuild platform, and the great deal is that everyone can write a Task and extend the pre-existing functionalities.
Some of the typical, already built in the platform, tasks are
Copy, Move, Delete files, just to name some of the probably most useful and very simple ones.
Popular extensions
MsBuild is very easy to extend, and there are two very important sources with tons of already written and ready to use Tasks. In case you are interested, feel free to download from the below links, as both are free to use and maintained by the community. There you will find Tasks such as Zip, SVN integration, SQL Server integration, and so on…
- Microsoft’s MSBuild ExtensionPack
- MSBuild Community Tasks
Using and integrating the above libraries goes beyond this post.
Creating own tasks
There are mainly two ways of creating tasks by:
- Implementing and compiling an ITask interface: That’s it creating a class that would implement ITask interface. In order to integrate this into the MSBuild script file, one would need to specify a path to the compiled assembly that needs to be deployed together with the script.
- Implementing an inline task.
While there are many posts in internet related to the MSBuild task creating by implementing the ITask interface, in this post I would love to concentrate on Inline tasks.
In the .NET Framework version 4, you can create tasks directly 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 itself.
I’ve created a very simple Tasks that is responsible for deleting files, given a list of files to be deleted.
So, let’s start with explaining a skeleton of an inline task:
UsingTask |
UsingTask is a declaring element that needs to be used in order to create a Task. Attributes of the element would specify the Task Name, the so called TaskFactory, which will specify what would be the engine to be used in order to interpret the underlying code, and the Assembly that provides such a functionality. In our case the CodeTaskFactory and the $(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll are default values. |
ParameterGroup |
ParameterGroupsection provides a possibility to declare the input and output parameters. Important thing is to specify a full signature of the DataType to be used , such as System.String[], etc..In my case, I am creating a task that accepts
|
Task |
And, finally the Task element that defines the task logic itself. There are several subelements that could be specified, but in my opinion the most useful and typical ones are the ones specified in the below example. The possibility to specify the “Import” of namespaces by using the Using element and the actual Code itself. In my case the Task below is written in CSharp, and so it’s specified in the Language attribute. |
I think that is kind of useless to go through the implementation of the task as it is really trivial. Looping through the list of files received as the FilesToDelete parameter, and filling a list of deleted items.
Here is what an Inline Task looks like:
<UsingTask TaskName="DeleteFiles"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<FilesToDelete ParameterType="System.String[]"
Required="true"
Output="false"/>
<IgnoreErrors ParameterType="System.Boolean"
Required="true"
Output="false"/>
<ShowErrors ParameterType="System.Boolean"
Required="true"
Output="false"/>
<DeletedFiles ParameterType="System.String[]"
Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System.Collections.Generic" />
<Using Namespace="System.IO" />
<Code Type="Fragment" Language="cs">
<![CDATA[
List<string> DeletedFilesList = new List<string>();
if(this.FilesToDelete!=null && this.FilesToDelete.Count()>0)
{
foreach(string file in this.FilesToDelete)
{
try
{
if(File.Exists(file))
{
File.Delete(file);
DeletedFilesList.Add(file);
}
}
catch(Exception exc)
{
if(this.ShowErrors == true)
{
Console.WriteLine("Deleting file " + file
+ " failed because of " + exc.ToString());
}
if(this.IgnoreErrors == false)
{
break;
}
}
}
}
this.DeletedFiles = DeletedFilesList.ToArray();
]]>
</Code>
</Task>
</UsingTask>
Integration
Using the above created inline task is pretty much straightforward. As shown here below, we simply need to create a tag with the task name, send parameters, and get back the output, if any.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="4.0">
<UsingTask> ... here goes the above implementation ... </UsingTask>
<!-- Creating the target, as we would do usually -->
<Target Name="DemoDeleteFiles" >
<Message Text="Starting execution of the DemoDeleteFiles..."/>
<ItemGroup>
<FileToDelete Include="E:\svn\trunk\xxx.txt"/>
<FileToDelete Include="E:\svn\trunk\xxx1.txt"/>
</ItemGroup>
<!-- Simply create an element with the same name
of the inline task created in the previous example -->
<DeleteFiles FilesToDelete="@(FileToDelete)"
ShowErrors="true"
IgnoreErrors="false">
<!--Define the output...-->
<Output TaskParameter="DeletedFiles"
ItemName="FilesSuccessfulyDeleted"/>
</DeleteFiles>
<!--Showing the output on the screen-->
<Message Text="Files deleted: @(FilesSuccessfulyDeleted)"/>
</Target>
</Project>
Save the full content in a file called myfile.build and execute it from the command line
Behind the scenes
When executing the task, the MSBuild Code Factory will implement a task as follows and compile it before usage. Te below example is exactly the script generated for the above defined inline task.
Things to note:
- Input and output parameters are exposed as public properties
- Execute method contains the logic written in the inline task
The full source code:
//-------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18010
//
// Changes to this file may cause incorrect behavior and will be lost
// if the code is regenerated.
// </auto-generated>
//-------------------------------------------------------------------------
namespace InlineCode
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class DeleteFiles : Microsoft.Build.Utilities.Task
{
private bool _Success = true;
public virtual bool Success
{
get
{
return _Success;
}
set
{
_Success = value;
}
}
private string[] _FilesToDelete;
public virtual string[] FilesToDelete
{
get
{
return _FilesToDelete;
}
set
{
_FilesToDelete = value;
}
}
private bool _IgnoreErrors;
public virtual bool IgnoreErrors
{
get
{
return _IgnoreErrors;
}
set
{
_IgnoreErrors = value;
}
}
private bool _ShowErrors;
public virtual bool ShowErrors
{
get
{
return _ShowErrors;
}
set
{
_ShowErrors = value;
}
}
private string[] _DeletedFiles;
public virtual string[] DeletedFiles
{
get
{
return _DeletedFiles;
}
set
{
_DeletedFiles = value;
}
}
public override bool Execute()
{
List<string> DeletedFilesList = new List<string>();
if (this.FilesToDelete != null && this.FilesToDelete.Count() > 0)
{
foreach (string file in this.FilesToDelete)
{
try
{
if (File.Exists(file))
{
File.Delete(file);
DeletedFilesList.Add(file);
}
}
catch (Exception exc)
{
if (this.ShowErrors == true)
{
Console.WriteLine("Deleting file " + file
+ " failed because of " + exc.ToString());
}
if (this.IgnoreErrors == false)
{
break;
}
}
}
}
this.DeletedFiles = DeletedFilesList.ToArray();
return _Success;
}
}
}
Final Thoughts
I’ve learned about inline tasks while working on the above mentioned project and it turned out to be a great functionality as I didn’t want to fragment my script. Indeed I find Inline tasks are really great if you want to keep the MSBuild project script file external-reference free.
Per se, inline tasks are not more or less powerful than implementing c# Tasks, but I would say that inline tasks give us a bit less freedom i.e. in case we need to create some methods to be reused, or creating inline classes.


By PRIMEFREBTS free bets