I have been working with RazorGenerator at work to precompile MVC razor views into an assembly, and for reasons that I won’t go into, we were unable to use the included MSBuild task on our build system. The workaround, or hack if you want, was to wrap the call to task within a ConsoleApplication which we would call from our build system.
Here is a simplified version of the ConsoleApp:
static int Main(string[] args)
{
var engine = new RazorCodeGen()
{
ProjectRoot = args[0];
CodeGenDirectory = args[1];
RootNamespace = args[2];
FilesToPrecompile = args.Skip(3).Select(filePath => new TaskItem(filePath)).ToArray();
}
return codeGenTask.Execute() ? 0 : 1;
}
Nothing astonishing here, besides the lack of error handling. We new up a RazorCodeGen task, setup its properties from arguments that we got from the command line, and call execute. If you run this you will get an error that says “Task attempted to log before it was initialized.”. The reason is that the task is accessing the Task.Log property and it is not running under the MSBuild host. To work around this, we can create our own BuildEngine that the Task can use:
public class ConsoleBuildEngine: IBuildEngine
{
public void LogErrorEvent(BuildErrorEventArgs e)
{
Console.WriteLine("[Error] " + e.Message);
}
public void LogMessageEvent(BuildMessageEventArgs e)
{
Console.WriteLine("[Message] " + e.Message);
}
// Additional methods
}
The interface has a lot of methods, but the only ones you need are the LogEvent and a couple of properties that you can stub. And now, we set our build engine before calling Execute:
var engine = new RazorCodeGen()
{
BuildEngine = new ConsoleBuildEngine(),
// other properties
}
That’s all there is to it! You can use this to unit test your task and Mock the IBuildEngine, or if you live in the dark ages of build systems like I do, you can call into the console app.
- Federico
PS: For extra points, you can format the messages according to MSBuild error message formats to get better reporting when running the task