Thursday, October 9, 2008

Running MSBuild tasks programmatically

I tried scouring the web for a description on how to run MSBuild tasks from your own code but came up with slim results. After some investigations this is one way that managed to run some tasks at least.

For this example, we'll compile a .cs file in the simplest way possible. To achieve this we need to use the Csc task.

There is one property on all tasks that deserves a special mention. It is Task.BuildEngine. The property is of type IBuildEngine and as far as I can tell the only implementation of this interface is an internal class called EngineProxy in the Microsoft.Build.BuildEngine namespace. Since it's internal we cannot use it.

We need to create our own IBuildEngine implementation. It turns out that this is pretty straightforward:


public class MyBuildEngine : IBuildEngine
{
public bool BuildProjectFile(string projectFileName, string[] targetNames,
IDictionary globalProperties,
IDictionary targetOutputs)
{
throw new NotImplementedException();
}

public int ColumnNumberOfTaskNode
{
get { return 0; }
}

public bool ContinueOnError
{
get { return false; }
}

public int LineNumberOfTaskNode
{
get { return 0; }
}

public string ProjectFileOfTaskNode
{
get { return ""; }
}

public void LogCustomEvent(CustomBuildEventArgs e)
{
Console.WriteLine("Custom: {0}", e.Message);
}

public void LogErrorEvent(BuildErrorEventArgs e)
{
Console.WriteLine("Error: {0}", e.Message);
}

public void LogMessageEvent(BuildMessageEventArgs e)
{
Console.WriteLine("Message: {0}", e.Message);
}

public void LogWarningEvent(BuildWarningEventArgs e)
{
Console.WriteLine("Warning: {0}", e.Message);
}
}
This implementation is very simple.
  • We'll simply ignore BuildProjectFile since we're running tasks manually here.
  • The *TaskNode methods are also almost ignored. I haven't investigated them but it looks like they're used for reporting errors in project files.
  • The Log* methods simply dump the message to the console.
Now we're ready to run the Csc task:


Csc cscTask = new Csc();
cscTask.BuildEngine = new MyBuildEngine();
cscTask.Sources = new TaskItem[]{
new TaskItem(@"C:\Code\Projects\MSBuildTest\HelloWorld\HelloWorld.cs")
};

if (cscTask.Execute())
{
Console.WriteLine("Task executed ok. Resulting assembly: {0}",
cscTask.OutputAssembly
);
}

This will produce an output file HelloWorld.exe in the working directory.

Obviously, there are lots of other properties available on the Csc task but this was just the simplest example possible.

No comments: