owenG
home learn tableau about
divider
SDLC VS 2010 Coded UI Test Coded UI Test and Fitnesse MSBuild 4.0 MSBuild and IronPython MSBuild and IronPython - TFS checkins MSBuild and IronPython - Custom SQL Data








previous next

IronPython Class Factory for MSBuild 4.0 Part 1

June 2010

See also:


Overview

Before Visual Studio 2010, i.e. under MSBuild 3.5 and earlier, custom tasks needed to be hosted in a separate assembly. MSBuild 4.0 brings with it the ability to code tasks inline, where the code comprising the task is written within the project file itself. As I mentioned in my 'MSBuild 4.0 Custom Task Walkabout', language support is currently somewhat limited, with C# and Visual Basic (VB.net) being the most likely choices. Someone at Microsoft began an unofficial project that provided support for inline tasks written in PowerShell, the source code for which can be downloaded from here, while another page includes usage details. I'm no Python (or IronPython) expert and I wondered how difficult it would be to create a similiar Task Factory for IronPython. After reviewing the source code for the PowerShell project and doing some research online, the answer to the preceding question turned out to be 'not too difficult', at least not for a simple prototype. I haven't exercised 99% of the methods etc. available within IronPython/Python during testing but so far, so good. And even though it is technically a Task Factory for IronPython, that includes support for almost everything within 'standard' Python (meaning CPython). In other words, you don't need to use IronPython specific code for the inline task that references PythonClassFactory.dll, but you certainly can. Note that the most recent release of IronPython, 2.6, targets CPython 2.6. Python syntax should match that version - print 'Hello World', not the 3.0 print('Hello World').


divider element

The Approach

I created a new C# project in Visual Studio 2010 named PythonClassFactory (yeah, probably should have named it IronPythonClassFactory but it is a little too late now). After that:

  • grabbed the five .dll's at the root of my 'IronPython 2.6 for .NET 4.0' installation
    • IronPython.dll
    • IronPython.Modules.dll
    • Microsoft.Dynamic.dll
    • Microsoft.Scripting.Debugging.dll
    • Microsoft.Scripting.dll
    and copied those to a \_dlls directory at a sibling level to the PythonClassFactory folder holding the .csproj
  • from the .csproj added a Reference to each of those dll's, and since they will be needed at build runtime, confirmed 'Copy Local' on each was set to True
  • also added (.NET) References to
    • Microsoft.Build
    • Microsoft.Build.Engine
    • Microsoft.Build.Framework
    • Microsoft.Build.Tasks.v4.0
    • Microsoft.Build.Utilities.v4.0
  • created a PythonClassFactory class in the owenG.PyFactory namespace, implemented the Microsoft.Build.Framework.ITaskFactory Interface, added a using directive for Microsoft.Build.Framework
  • created a PythonTask class, inheriting from Microsoft.Build.Utilities.Task and overriding Execute(). Implemented Microsoft.Build.Framework.IGeneratedTask (only those members that were also defined in PowerShell project) and IDisposable. Added a bunch of using statements.
  • maybe other stuff I forgot about?

Once project compiled, shipped it out. Or actually spent a bunch of time testing the factory on the msbuild command line and also debugging by launching the dll against msbuild.exe. The first draft of the source code is available for download - NOTE: No claim of suitability, guarantee, etc., files provided "as-is"/"as-are".


The Environment

Next task, as it were, was to implement the Task Factory in a TFS 2010 Team Build environment and test. For this I used the same project and local directory structure as discussed in my earlier MSBuild walkabout:

local folder structure

Also, the workflow I used was a copy of the build process template from that article, renamed to PythonProcessTemplate.xaml and modified a bit. Logically enough, the new build definition I set up (PythonBuildDef1) was set to use this as its process template. Since it was for testing only, PythonBuildDef1 built $/AllInOne/Main/CSEFModelFirst.sln, which only had a single C# project -> $/AllInOne/Main/CSEFModelFirst/CSEFModelFirst.csproj. The PythonClassFactory.dll, along with the five .dll's from IronPython installation, were checked into source control under the $AllInOne/BuildProcessTemplates/CustomTasks/_compiled project. The PythonTargets.targets file that held the msbuild implementation was stored in .../CustomTasks/targets. The container Project element in PythonTargets.targets is simple and contains only two attributes:

.targets
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

   ...

</Project>

The Workflow

As mentioned above, the .xaml workflow for this example is same as was used in my earlier article. I simply renamed the custom MSBuild Activity to 'MSBuild_PythonTargets' and updated several of the related Properties:

Properties for MSBuild workflow activity

The first important property is CommandLineArguments, which aggregates several MSBuild property values that will be passed on to the project file. The different properties are going to be consumed by separate targets, the details of which will be discussed later. Note that the final argument, outputDirectory, is set by Team Build and represents the folder to which the compiled binaries will be output on the build machine:

contents of CommandLineArgumentsProperties

LogFile, in combination with LogFileDropLocation, together define the path to which the log for this MSBuild Activity will be written. For these builds that will mean something like ...drops\PythonBuildDef1\PythonBuildDef1_20100602.2\logs\myPythonTargetsLog.log.

The path in Project points to the MSBuild project file that will be run by this Activity. At build time this will be something like ...\AllInOne\PythonBuildDef1\Sources\AllInOne\BuildProcessTemplates\CustomTasks\_targets\PythonTargets.targets.

The PythonTargets.targets file defines four Targets (PyTarget1, PyTarget2 etc.) and they are listed in the Targets property. Each will examined in turn.

Next, first example...



previous next