IronPython Class Factory for MSBuild 4.0 Part 3
Example 2: PyCombinePath task, get IronPython and the CLR involved
Next example illustrates using IronPython and the ability to communicate directly with the .Net runtime, in this case to do a simple
System.IO.Path.Combine:
.targets
<!-- Task: PyCombinePath, use IronPython to combine paths via System.IO.Path -->
<UsingTask TaskName="PyCombinePath" TaskFactory="PythonClassFactory"
AssemblyFile="..\_compiled\PythonClassFactory.dll" >
<ParameterGroup>
<inFirstPath ParameterType="System.String" Required="true" />
<inSecondPath ParameterType="System.String" Required="true" />
<outCombinedPath1 Output="true" />
<outCombinedPath2 Output="true" />
</ParameterGroup>
<Task>
<![CDATA[
from System.IO import Path
outCombinedPath1 = Path.Combine(inFirstPath, inSecondPath)
class FileInformation(object):
def __init__(self):
self.fullPath = Path.Combine(inFirstPath, inSecondPath)
self.fileName = Path.GetFileName(self.fullPath)
def PrintFileInfo(self):
print 'Full path: ', self.fullPath, '\nFile name: ', self.fileName
myFile = FileInformation()
myFile.PrintFileInfo()
outCombinedPath2 = myFile.fullPath
]]>
</Task>
</UsingTask>
<PropertyGroup>
<inFirstPathValue>C:\source</inFirstPathValue>
<inSecondPathValue>subFolder1\someFile.txt</inSecondPathValue>
</PropertyGroup>
<Target Name="PyTarget2" >
<PyCombinePath inFirstPath="$(inFirstPathValue)" inSecondPath="$(inSecondPathValue)" >
<Output PropertyName="localProperty1" TaskParameter="outCombinedPath1" />
<Output PropertyName="localProperty2" TaskParameter="outCombinedPath2" />
</PyCombinePath>
<Message Text="First path: $(inFirstPathValue) :: Second path: $(inSecondPathValue)"
Importance="High" />
<Message Text="Path from simple Path.Combine: $(localProperty1)" Importance="High" />
<Message Text="Path from custom FileInformation class: $(localProperty2)" Importance="High" />
</Target>
Three parent elements again:
1) UsingTask element
Two string parameters going in and two coming out. The two output values should be the same but are populated using slightly different methodologies.
The CDATA holds an IronPython snippet that is somewhat more complicated than the first example. Right off the bat IronPython knows about some of the most
common .Net namespaces like System.IO, allowing me to directly import
(System.IO).Path. To access other .Net libraries I would have needed to insert an 'import clr' line, followed by one or more
clr.AddReference calls. The first output parameter, outCombinedPath1, is assigned the result of a .Net
Path.Combine on the two input parameters. Then a very simple FileInformation class is
defined and initialized with a .fullPath attribute set to the result of Path.Combine on
inFirstPath and inSecondPath parameters. An instance of this class is then created and the value of .fullPath assigned to
the second output parameter, outCombinedPath2.
2) PropertyGroup element
Provides default values for two path values. Overridden in this example via the command line call from MSBuild_PythonTargets, which sets first and second
properties to "C:\temp" and "logfile.log" respectively.
3) Target element
PyTarget2 is among the targets called by MSBuild_PythonTargets. Tasks within include the custom PyCombinePath, with two parameters passed in
and two passed out, values for which are then assigned to local properties. Three Message tasks perform the job of logging the current values of all
related variables.
Queue a build, review contents of log file:
log
PyTarget2:
First path: C:\temp :: Second path: logfile.log
Path from simple Path.Combine: C:\temp\logfile.log
Path from custom FileInformation class: C:\temp\logfile.log
Example 3: PyJoinPath task, 'combine' paths again but use standard Python library
Again, combining two path values into a valid full path, taking care of things like making sure there is only a single path separator character between
the joined sections. Use strictly Python libraries:
.targets
<!-- Task: PyJoinPath, use Python to join paths via os.path.join -->
<UsingTask TaskName="PyJoinPath" TaskFactory="PythonClassFactory"
AssemblyFile="..\_compiled\PythonClassFactory.dll" >
<ParameterGroup>
<inFirstPath ParameterType="System.String" Required="true" />
<inSecondPath ParameterType="System.String" Required="true" />
<outJoinedPath Output="true" />
</ParameterGroup>
<Task>
<![CDATA[
import sys
sys.path.append(r'\\localhost\drops\_Python\Lib')
import os
outJoinedPath = os.path.join(inFirstPath, inSecondPath)
]]>
</Task>
</UsingTask>
<Target Name="PyTarget3" >
<PyJoinPath inFirstPath="$(inFirstPathValue)" inSecondPath="$(inSecondPathValue)" >
<Output PropertyName="localProperty3" TaskParameter="outJoinedPath" />
</PyJoinPath>
<Message Text="First path: $(inFirstPathValue) :: Second path: $(inSecondPathValue)"
Importance="High" />
<Message Text="Path from simple path.join: $(localProperty3)" Importance="High" />
</Target>
Only two main elements now, since PyTarget3 will use MSBuild properties discussed in Example 2:
1) UsingTask element
Same parameter setup as with IronPython PyCombinePath but with only one output parameter. The new Task body code illustrates how to use additional Python libraries
and presupposes the existence of those library files on the target build server, specifically in a (local) shared \drops folder. To set that up manually,
I would have needed to copy the Lib folder from the original IronPython installation (e.g. C:\Program Files (x86)\IronPython 2.6 for .NET 4.0\Lib) and place it in
\\localhost\drops\_Python\ before kicking off the build. Then at build runtime the PyJoinPath task will load the 'os' Python library from that directory
and use its .path.join() method to combine the two paths and store in outJoinedPath output parameter.
2) Target element
Similar structure as that of earlier PyTarget2 but there is only one output parameter, storing the result of the single code path.
The myPythonTargetsLog.log from a new build includes expected data:
log
PyTarget3:
First path: C:\temp :: Second path: logfile.log
Path from simple path.join: C:\temp\logfile.log
Next, something a little bit more complicated...