MSBuild.ExtensionPack.Xml.XmlFile TaskAction="ReadAttribute" Failing - msbuild

I cannot read the value attribute of <add name="ReleaseVersion" value="4"/> in the app.config below. I am at a complete loss. I suspect the XPath value or a Key value.
Target
<Target Name="xxx"
DependsOnTargets="CopyFilesToOutputDirectory" >
<ItemGroup>
<_DestinationAppConfigFile Include="#(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')" />
</ItemGroup>
<MSBuild.ExtensionPack.Xml.XmlFile TaskAction="ReadAttribute"
File="%(_DestinationAppConfigFile.FullPath)"
XPath="/configuration/system.diagnostics/switches/add[#name='ReleaseVersion']/#value"
Value="$(ReleaseVersion)" />
<Error Condition = " '$(ReleaseVersion)'=='' "
Text="Failed to read attribute." />
<Message Text="ReleaseVersion: $(ReleaseVersion)"
Importance="high" />
</Target>
App.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.diagnostics>
<switches>
<!-- ReleaseVersion (Conditional release switch); 0- = PRODUCTION, 1 = MIRROR, 2 = EMERGENCYRELEASE, 3 = USERACCEPTANCETESTING, 4 = QA, 5 = DEVELOPMENT, 6 = DRN DEVELOPMENT -->
<add name="ReleaseVersion" value="4"/>
<!-- Stop (Stops execution to allow for Just-In-Time (JIT) debugging); 0 = CONTINUE EXECUTION, 1 = LAUNCH DEBUGGER -->
<add name="Stop" value="0"/>
</switches>
</system.diagnostics>
</configuration>
Update
I reviewed the code for XmlFile.ReadAttribute at http://msbuildextensionpack.codeplex.com/SourceControl/changeset/view/83099#1714660 and it makes the call SelectSingleNode using the namespace syntax. That could be the problem.
private void ReadAttribute()
{
if (string.IsNullOrEmpty(this.XPath))
{
this.Log.LogError("XPath is Required");
return;
}
this.LogTaskMessage(string.Format(CultureInfo.CurrentUICulture, "Read Attribute: {0}", this.XPath));
XmlNode node = this.xmlFileDoc.SelectSingleNode(this.XPath, this.namespaceManager);
if (node != null && node.NodeType == XmlNodeType.Attribute)
{
this.Value = node.Value;
}
}

Your sample code is almost correct; it just needs a slight change to the usage of the XmlFile task. The call to XmlFile ReadAttribute should declare Value as the task's output. To do this, add the Output element to the task declaration, set to the TaskParameter value to "Value" and set the PropertyName value to "ReleaseVersion", similar to the following:
<MSBuild.ExtensionPack.Xml.XmlFile
TaskAction="ReadAttribute"
File="[example-path-to-app-config]"
XPath="/configuration/system.diagnostics/switches/add[#name='ReleaseVersion']/#value">
<Output TaskParameter="Value" PropertyName="ReleaseVersion" />
<MSBuild.ExtensionPack.Xml.XmlFile>
After making the change, the attribute value should be found/read by the task, assuming your ToolsVersion is set to the version needed by your particular implementation of MSBuild Extension Pack

Related

ArgumentOutOfRangeException during Build with Tools 12.0

We have an (old) build definition is using the UpgradeTemplate.xaml on TFS 2015 (and an underlying TFSBuild.proj which has a heap of custom actions). As such, the task of properly modernising the build is going to take time.
I'd like to hack the UpgradeTemplate to add in C#6/VB14 support without requiring a full re-write of the build definition, in order to keep the devs happy.
I attempted to edit the UpgradeTemplate.xaml to add a ToolPath property on the TfsBuild. However, now that I have done this, I get the following error on nearly all my projects:
ArgumentOutOfRangeException: Index and length must refer to a location within the string. Parameter name: length
On investigation, the lines of code in these projects all look like this:
<MSBuild.ExtensionPack.VisualStudio.TfsVersion TaskAction="GetVersion"
BuildName="$(BuildDefinition)" TfsBuildNumber="$(BuildNumber)"
VersionFormat="DateTime" DateFormat="MMdd" Major="$(MajorVersion)"
Minor="$(MinorVersion)">
The values of these variables as set printed out by Message tasks on the vbproj:
BuildDefinition: MyBuild-Testing
BuildNumber: 57902
MajorVersion: 43
MinorVersion: 2
The Build server has version 3.5.10 on the MSBuild ExtensionPack installed.
How do I resolve this issue? I'm testing this with a new build definition to allow devs to continue working while I get this set up, so I don't want to replace the ExtensionPack with the latest release (if possible) if it is likely to break the existing build.
Upgrade Template
<Activity mc:Ignorable="sad" x:Class="TfsBuild.Process" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mtbc="clr-namespace:Microsoft.TeamFoundation.Build.Client;assembly=Microsoft.TeamFoundation.Build.Client" xmlns:mtbw="clr-namespace:Microsoft.TeamFoundation.Build.Workflow;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtbwa="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Activities;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtbwt="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Tracking;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtvc="clr-namespace:Microsoft.TeamFoundation.VersionControl.Client;assembly=Microsoft.TeamFoundation.VersionControl.Client" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:sad="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:sad1="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:this="clr-namespace:TfsBuild;" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:Members>
<x:Property Name="ConfigurationFolderPath" Type="InArgument(x:String)" />
<x:Property Name="AgentSettings" Type="InArgument(mtbwa:AgentSettings)" />
<x:Property Name="MSBuildArguments" Type="InArgument(x:String)" />
<x:Property Name="MSBuildPlatform" Type="InArgument(mtbwa:ToolPlatform)" />
<x:Property Name="DoNotDownloadBuildType" Type="InArgument(x:Boolean)" />
<x:Property Name="LogFilePerProject" Type="InArgument(x:Boolean)" />
<x:Property Name="SourcesSubdirectory" Type="InArgument(x:String)" />
<x:Property Name="BinariesSubdirectory" Type="InArgument(x:String)" />
<x:Property Name="TestResultsSubdirectory" Type="InArgument(x:String)" />
<x:Property Name="RecursionType" Type="InArgument(mtvc:RecursionType)" />
<x:Property Name="Verbosity" Type="InArgument(mtbw:BuildVerbosity)" />
<x:Property Name="Metadata" Type="mtbw:ProcessParameterMetadataCollection" />
<x:Property Name="SupportedReasons" Type="mtbc:BuildReason" />
</x:Members>
<this:Process.ConfigurationFolderPath>
<InArgument x:TypeArguments="x:String" />
</this:Process.ConfigurationFolderPath>
<this:Process.AgentSettings>[New Microsoft.TeamFoundation.Build.Workflow.Activities.AgentSettings() With {.MaxWaitTime = New System.TimeSpan(4, 0, 0), .MaxExecutionTime = New System.TimeSpan(0, 0, 0), .TagComparison = Microsoft.TeamFoundation.Build.Workflow.Activities.TagComparison.MatchExactly }]</this:Process.AgentSettings>
<this:Process.MSBuildArguments>
<InArgument x:TypeArguments="x:String" />
</this:Process.MSBuildArguments>
<this:Process.MSBuildPlatform>[Microsoft.TeamFoundation.Build.Workflow.Activities.ToolPlatform.Auto]</this:Process.MSBuildPlatform>
<this:Process.DoNotDownloadBuildType>[False]</this:Process.DoNotDownloadBuildType>
<this:Process.LogFilePerProject>[False]</this:Process.LogFilePerProject>
<this:Process.SourcesSubdirectory>
<InArgument x:TypeArguments="x:String" />
</this:Process.SourcesSubdirectory>
<this:Process.BinariesSubdirectory>
<InArgument x:TypeArguments="x:String" />
</this:Process.BinariesSubdirectory>
<this:Process.TestResultsSubdirectory>
<InArgument x:TypeArguments="x:String" />
</this:Process.TestResultsSubdirectory>
<this:Process.RecursionType>[Microsoft.TeamFoundation.VersionControl.Client.RecursionType.OneLevel]</this:Process.RecursionType>
<this:Process.Verbosity>[Microsoft.TeamFoundation.Build.Workflow.BuildVerbosity.Normal]</this:Process.Verbosity>
<this:Process.Metadata>
<mtbw:ProcessParameterMetadataCollection />
</this:Process.Metadata>
<this:Process.SupportedReasons>All</this:Process.SupportedReasons>
<mva:VisualBasic.Settings>Assembly references and imported namespaces serialized as XML namespaces</mva:VisualBasic.Settings>
<Sequence mtbwt:BuildTrackingParticipant.Importance="None">
<Sequence.Variables>
<Variable x:TypeArguments="mtbc:IBuildDetail" Name="BuildDetail" />
</Sequence.Variables>
<mtbwa:GetBuildDetail DisplayName="Get the Build" Result="[BuildDetail]" />
<mtbwa:InvokeForReason DisplayName="Update Build Number for Triggered Builds" Reason="Triggered">
<mtbwa:UpdateBuildNumber BuildNumberFormat="["$(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.r)"]" DisplayName="Update Build Number" />
</mtbwa:InvokeForReason>
<mtbwa:AgentScope DisplayName="Run On Agent" MaxExecutionTime="[AgentSettings.MaxExecutionTime]" MaxWaitTime="[AgentSettings.MaxWaitTime]" ReservationSpec="[AgentSettings.GetAgentReservationSpec()]">
<mtbwa:AgentScope.Variables>
<Variable x:TypeArguments="x:String" Name="buildDirectory" />
</mtbwa:AgentScope.Variables>
<mtbwa:GetBuildDirectory DisplayName="Get the Build Directory" Result="[buildDirectory]" />
<If Condition="[Not String.IsNullOrEmpty(ConfigurationFolderPath)]" DisplayName="If Not String.IsNullOrEmpty(ConfigurationFolderPath)">
<If.Then>
<mtbwa:TfsBuild BinariesSubdirectory="[BinariesSubdirectory]" BuildDirectory="[buildDirectory]" CommandLineArguments="[MSBuildArguments]" ConfigurationFolderPath="[ConfigurationFolderPath]" DisplayName="Run TfsBuild for Configuration Folder" DoNotDownloadBuildType="[DoNotDownloadBuildType]" LogFilePerProject="[LogFilePerProject]" RecursionType="[RecursionType]" SourcesSubdirectory="[SourcesSubdirectory]" TestResultsSubdirectory="[TestResultsSubdirectory]" ToolPath="C:\Program Files (x86)\MSBuild\12.0\Bin\" ToolPlatform="[MSBuildPlatform]" Verbosity="[Verbosity]" />
</If.Then>
</If>
<If Condition="[BuildDetail.CompilationStatus = Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Unknown]" DisplayName="If CompilationStatus = Unknown">
<If.Then>
<mtbwa:SetBuildProperties CompilationStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Succeeded]" DisplayName="Set CompilationStatus to Succeeded" PropertiesToSet="CompilationStatus" />
</If.Then>
</If>
<If Condition="[BuildDetail.TestStatus = Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Unknown]" DisplayName="If TestStatus = Unknown">
<If.Then>
<mtbwa:SetBuildProperties DisplayName="Set TestStatus to Succeeded" PropertiesToSet="TestStatus" TestStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Succeeded]" />
</If.Then>
</If>
</mtbwa:AgentScope>
<mtbwa:InvokeForReason Reason="CheckInShelveset">
<mtbwa:CheckInGatedChanges DisplayName="Check In Gated Changes" />
</mtbwa:InvokeForReason>
</Sequence>
</Activity>
In Particular, I added ToolPath="C:\Program Files (x86)\MSBuild\12.0\Bin\" to line 58.
TFSBuild.proj
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\SDC\Microsoft.Sdc.Common.tasks" />
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks"/>
<ProjectExtensions>
<!-- Team Foundation Build Version - DO NOT CHANGE -->
<ProjectFileVersion>2</ProjectFileVersion>
</ProjectExtensions>
<PropertyGroup>
<RunCodeAnalysis>Never</RunCodeAnalysis>
<UpdateAssociatedWorkItems>false</UpdateAssociatedWorkItems>
<AdditionalVCOverrides></AdditionalVCOverrides>
<CustomPropertiesForClean></CustomPropertiesForClean>
<CustomPropertiesForBuild></CustomPropertiesForBuild>
<SkipGetChangesetsAndUpdateWorkItems>False</SkipGetChangesetsAndUpdateWorkItems>
<SkipWorkItemCreation>true</SkipWorkItemCreation>
<BuildConfigurationsInParallel>true</BuildConfigurationsInParallel>
<SkipDropBuild>false</SkipDropBuild>
</PropertyGroup>
<ItemGroup>
<SolutionToBuild Include="$(BuildProjectFolderPath)/SolutionsToBuild/Common.sln">
<Targets></Targets>
<Properties></Properties>
</SolutionToBuild>
</ItemGroup>
<ItemGroup>
<ConfigurationToBuild Include="Release|Any CPU">
<FlavorToBuild>Release</FlavorToBuild>
<PlatformToBuild>Any CPU</PlatformToBuild>
</ConfigurationToBuild>
</ItemGroup>
<PropertyGroup>
<SkipClean>false</SkipClean>
<SkipInitializeWorkspace>true</SkipInitializeWorkspace>
<ForceGet>true</ForceGet>
<IncrementalBuild>false</IncrementalBuild>
</PropertyGroup>
</Project>
Update
I think it might be something to do with running a private build (Latest + Shelveset). When I run a normal build, the BuildNumber variable is MyBuild-Testing_20170328.1. This appears to be working fine.
This issue is due to a difference between Private and Public builds. With Public Builds, the build is immediately numbered as BuildName_DateFormat.BuildNumber. Private builds however, are just numeric (e.g. 57902 above).
The Code in the extension does the following:
string buildstring = this.TfsBuildNumber.Replace(string.Concat(this.BuildName, "_"),
string.Empty);
char[] chrArray = new char[] { '.' };
string[] buildParts = buildstring.Split(chrArray, StringSplitOptions.RemoveEmptyEntries);
DateTime t = new DateTime(Convert.ToInt32(buildParts[0].Substring(0, 4),
CultureInfo.CurrentCulture),
Convert.ToInt32(buildParts[0].Substring(4, 2),
CultureInfo.CurrentCulture),
Convert.ToInt32(buildParts[0].Substring(6, 2),
CultureInfo.InvariantCulture));
Are you can see, its substringing on the build name, assuming it's been stripped down to the date component (20170328). Private builds aren't this long, and so fail.
This build works fine when running as a public build - basically it means that private builds on this definition are not available until an upgrade takes place.

Processing batch items in parallel

I have an ItemGroup, and want to process all its items in parallel (using a custom task or an .exe).
I could write my task/exe to accept the entire ItemGroup and process its items in parallel internally. However, I want this parallelism to work in conjunction with MSBuild's /maxCpuCount param, since otherwise I might end up over-parallelizing.
This thread says there's no way.
My testing shows that MSBuild's /maxCpuCount only works for building different projects, not items (see code below)
How can I process items from an ItemGroup in parallel?
Is there a way to author a custom task to work in parallel in conjunction with MSBuild's Parallel support?
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build" >
<!-- Runs only once - I guess MSBuild detects it's the same project -->
<!--<MSBuild Projects="$(MSBuildProjectFullPath);$(MSBuildProjectFullPath)" Targets="Wait3000" BuildInParallel="true" />-->
<!-- Runs in parallel!. Note that b.targets is a copy of the original a.targets -->
<MSBuild Projects="$(MSBuildProjectFullPath);b.targets" Targets="Wait3000" BuildInParallel="true" />
<!-- Runs sequentially -->
<ItemGroup>
<Waits Include="3000;2000"/>
</ItemGroup>
<Wait DurationMs="%(Waits.Identity)" />
</Target>
<Target Name="Wait3000">
<Wait DurationMs="3000" />
</Target>
<UsingTask TaskName="Wait" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<DurationMs ParameterType="System.Int32" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
Log.LogMessage(string.Format("{0:HH\\:mm\\:ss\\:fff} Start DurationMs={1}", DateTime.Now, DurationMs), MessageImportance.High);
System.Threading.Thread.Sleep(DurationMs);
Log.LogMessage(string.Format("{0:HH\\:mm\\:ss\\:fff} End DurationMs={1}", DateTime.Now, DurationMs), MessageImportance.High);
</Code>
</Task>
</UsingTask>
</Project>
I know this is old, but if you get a few minutes, revisit your attempt to use the MSBuild task. Using the Properties and/or AdditionalProperties reserved item metadata elements* will resolve the issue you described in your code sample ("Runs only once - I guess MSBuild detects it's the same project").
The MSBuild file below processes items from an ItemGroup in parallel via MSBuild's parallel support (including /maxCpuCount). It does not use BuildTargetsInParallel from the MSBuild Extension Pack, nor any other custom or inline task.
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build" >
<ItemGroup>
<Waits Include="3000;2000"/>
</ItemGroup>
<ItemGroup>
<ProjectItems Include="$(MSBuildProjectFullPath)">
<Properties>
WaitMs=%(Waits.Identity)
</Properties>
</ProjectItems>
</ItemGroup>
<MSBuild Projects="#(ProjectItems)" Targets="WaitSpecifiedMs" BuildInParallel="true" />
</Target>
<Target Name="WaitSpecifiedMs">
<Wait DurationMs="$(WaitMs)" />
</Target>
</Project>
* Well-hidden under "Properties Metadata" on the MSBuild Task reference page.
As you said yourself, you can't parallelize on target or task level, you can yield though.
My custom tasks parallelize heavily using TPL, i.e. my base task wrapper has a ForEach wrapper.
public bool ForEach<T>(IEnumerable<T> enumerable, Action<T> action, int max = -1)
{
return enumerable != null && Parallel.ForEach(enumerable, new ParallelOptions { MaxDegreeOfParallelism = max }, (e, s) =>
{
if (Canceled)
s.Stop();
if (s.ShouldExitCurrentIteration)
return;
action(e);
Interlocked.Increment(ref _total);
}).IsCompleted;
}
Typically limit is omitted and managed by .NET itself, with few exception like non-thread safe operations like MSDeploy, deploying SSRS reports that has a config DoS limit of 20 from single IP, or a zip task that degrades heavily if it's more than CPU count even by 1. It's probably not worth reading maxCpuCount and use Environment.ProcessorCount or %NUMBER_OF_PROCESSORS%, but you can try parsing the command line or reflecting on the host object, e.g. my base task class has this method to get all properties, targets, etc. for various extra special global flags.
private void Engine(object host)
{
var type = host.GetType();
if (type.FullName != "Microsoft.Build.BackEnd.TaskHost")
{
Log.Warn("[Host] {0}", type.AssemblyQualifiedName);
return;
}
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var taskLoggingContext = type.GetProperty("LoggingContext", flags).GetValue(host, null);
var targetLoggingContext = taskLoggingContext.GetType().GetProperty("TargetLoggingContext", flags).GetValue(taskLoggingContext, null);
ProjectTask = taskLoggingContext.GetType().GetProperty("Task", flags).GetValue(taskLoggingContext, null).To<ProjectTaskInstance>();
ProjectTarget = targetLoggingContext.GetType().GetProperty("Target", flags).GetValue(targetLoggingContext, null).To<ProjectTargetInstance>();
var entry = type.GetField("requestEntry", flags).GetValue(host);
var config = entry.GetType().GetProperty("RequestConfiguration").GetValue(entry, null);
Project = config.GetType().GetProperty("Project").GetValue(config, null).To<ProjectInstance>();
Properties = Project.Properties.ToDictionary(p => p.Name, p => p.EvaluatedValue);
Typical task would look something like this using ForEach:
public class Transform : Task
{
[Required]
public ITaskItem[] Configs { get; set; }
protected override void Exec()
{
//...
ForEach(Configs, i =>
{
//...
}, Environment.ProcessorCount);
//...
}

Why does the MsbuildExtension Detokenise class reload the project?

This is problematic because any Properties being passed in are lost.
Further Explanation: I pass in a property to the project file. This property is a path to a .props file. It contains tokens and replacement values for the detokenise class. The task apparently reloads the project and the path is not maintained. This doesn't seem to be the case for other task, for example the guid tasks.
In the example I am using a example proj entitled guids.proj
Invoked Using :
<MSBuild.ExtensionPack.FileSystem.Detokenise TaskAction="Detokenise" TargetFiles="#(FileCollectionToBeDetokenized )"/>
Some command line out put follows :
Task "MSBuild.ExtensionPack.FileSystem.Detokenise" (TaskId:11)
Detokenise Task Execution Started [13:04:35] (TaskId:11)
Loading Project: C:\Users\bstrausser\Desktop\guids.proj (TaskId:11)
Detokenising Collection: 1 files (TaskId:11)
C:\Users\*****\Desktop\guids.proj(37,9): error : Property not found: Asset
Directory
Full project file :
Project ToolsVersion="4.0" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(ParentMSBuildProjectDirectory)\Bin\MSBuild\ExtensionPack\MSBuild.ExtensionPack.tasks" Condition="Exists('$(ParentMSBuildProjectDirectory)\Bin\MSBuild\ExtensionPack\MSBuild.ExtensionPack.tasks')"/>
<Import Project="C:\Program Files (x86)\MSBuild\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks" Condition="!Exists('$(ParentMSBuildProjectDirectory)\Bin\MSBuild\ExtensionPack\MSBuild.ExtensionPack.tasks') AND Exists('C:\Program Files (x86)\MSBuild\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks')"/>
<PropertyGroup>
<TPath>$(MSBuildProjectDirectory)\..\MSBuild.ExtensionPack.tasks</TPath>
<TPath Condition="Exists('$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks')">$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks</TPath>
<PROPS>$(DACP)</PROPS>
</PropertyGroup>
<Import Project="$(PROPS)" Condition="'$(DACP)' != ''" />
<Target Name="Default">
<Message text = "$(DACP)" />
<!-- Create a new Guid and get the formatted and unformatted values -->
<MSBuild.ExtensionPack.Framework.Guid TaskAction="Create">
<Output TaskParameter="FormattedGuidString" PropertyName="FormattedGuidString1" />
<Output TaskParameter="GuidString" PropertyName="GuidStringItem" />
</MSBuild.ExtensionPack.Framework.Guid>
<Message Text="GuidStringItem: $(GuidStringItem)"/>
<Message Text="FormattedGuidString: $(FormattedGuidString1)"/>
<!-- Create a new cryptographically strong Guid and get the formatted and unformatted values -->
<MSBuild.ExtensionPack.Framework.Guid TaskAction="CreateCrypto">
<Output TaskParameter="FormattedGuidString" PropertyName="FormattedGuidString1" />
<Output TaskParameter="GuidString" PropertyName="GuidStringItem" />
</MSBuild.ExtensionPack.Framework.Guid>
<Message Text="GuidStringItem Crypto: $(GuidStringItem)"/>
<Message Text="FormattedGuidString Crypto: $(FormattedGuidString1)"/>
<ItemGroup>
<FileCollectionToBeDetokenized Include="C:\Code\MSBuildGit\Configuration\TaskExecutorConfigTransforms\App.GREEN.SCRATCH.config"/>
</ItemGroup>
<Message text = "BaseUrl : $(BaseUrl)" />
<Message text = "DetokenizedTransformFile : #(FileCollectionToBeDetokenized)" />
<MSBuild.ExtensionPack.FileSystem.Detokenise TaskAction="Detokenise" TargetFiles="#(FileCollectionToBeDetokenized )"/>
</Target>

msbuild ITaskItem output

I'm trying to use MSBuild.ExtensionPack.Web.Iis7AppPool task with GetInfo task action.
This task's output parameter is SiteInfo property, which its type is ITaskItem. This is a propery, not a collection.
How can I access the SiteInfo metadata? I can access with $, but then all I can get is the web site name. I tried to use %(SiteInfo.) with no success.
If the output parameter is an ITaskItem and you want to access the metadata then you need to assign it to an ItemGroup instead of a Property.
<Target Name="Test">
<MSBuild.ExtensionPack.Web.Iis7AppPool
TaskAction="GetInfo"
Name="DefaultAppPool">
<Output TaskParameter="AppPoolInfo" ItemName="PoolInfo" />
</MSBuild.ExtensionPack.Web.Iis7AppPool>
<Message Text="AppPool info = [#(PoolInfo)]" />
<Message Text="AppPool MaxProcesses = [%(PoolInfo.MaxProcesses)]" />
<MSBuild.ExtensionPack.Web.Iis7WebSite
TaskAction="GetInfo"
Name="Default Web Site">
<Output TaskParameter="SiteId" PropertyName="WebId" />
<Output TaskParameter="SiteInfo" ItemName="WebInfo" />
</MSBuild.ExtensionPack.Web.Iis7WebSite>
<Message Text="WebSite id = [$(WebId)]" />
<Message Text="WebSite app pool = [%(WebInfo.ApplicationPoolName)]" />
</Target>

How do I update an xml file with msbuild with two namespaces?

This msbuild below task can take into account one namespace, but in the case where I'm updating an mxml (flex) that has a mix of namespaces, can I use this task or another msbuild task to do the update?
<XmlUpdate
Prefix="fx"
Namespace="http://ns.adobe.com/mxml/2009"
XmlFileName="myFlexApp.mxml"
Xpath="//mx:Application/fx:Declarations/fx:String[#id='stringId']"
Value="xxxxx">
Here is the flex xml I'm trying to update:
<mx:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Declarations>
<fx:String id="stringId">UPDATE_ME</fx:String>
</fx:Declarations></mx:Application>
I was able to successfully update the source for XmlUpdate so that it takes multiple namespaces:
if (!string.IsNullOrEmpty(_prefix) && !string.IsNullOrEmpty(_namespace))
{
string[] prefixes = _prefix.Split(';');
string[] namespaces = _namespace.Split(';');
if (prefixes.Length != namespaces.Length)
throw new Exception("The number of prefixes is different from the number of namespaces");
for (int prefixIndex = 0; prefixIndex < prefixes.Length; prefixIndex++)
{
manager.AddNamespace(prefixes[prefixIndex], namespaces[prefixIndex]);
}
}
This works with the example of
<XmlUpdate
Prefix="fx;mx"
Namespace="http://ns.adobe.com/mxml/2009;library://ns.adobe.com/flex/mx"
XmlFileName="myFlexApp.mxml"
Xpath="//mx:Application/fx:Declarations/fx:String[#id='stringId']"
Value="xxxxx">
You'll have to use XmlMassUpdate task. (This task is from MSBuild Community Tasks)
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- The replacement value is here -->
<!-- ProjectExtensions keep MSBuild to try to evaluate the content -->
<ProjectExtensions>
<ReplacementNode>
<String id="stringId">CHANGE</String>
</ReplacementNode>
</ProjectExtensions>
<Target Name="XmlUpdate">
<XmlMassUpdate
ContentFile="myFlexApp.mxml"
NamespaceDefinitions="msb=http://schemas.microsoft.com/developer/msbuild/2003;
fx=http://ns.adobe.com/mxml/2009;
mx=library://ns.adobe.com/flex/mx"
ContentRoot="//mx:Application/fx:Declarations/fx:String[#id='stringId']"
SubstitutionsFile="$(MSBuildProjectFullPath)"
SubstitutionsRoot="msb:Project/msb:ProjectExtensions/msb:ReplacementNode/msb:String"/>
</Target>
</Project>
Changing the value during execution
The tricky part is that you can't define a value on fly using XmlMassUpdate only, you'll need to use XmlUpdate to update the value in your replacement node first.
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- The replacement value is here -->
<!-- ProjectExtensions keep MSBuild to try to evaluate the content -->
<ProjectExtensions>
<ReplacementNode>
<String id="stringId">CHANGE</String>
</ReplacementNode>
</ProjectExtensions>
<Target Name="XmlUpdate" DependsOnTargets="ChangeXmlValue">
<XmlMassUpdate
ContentFile="myFlexApp.mxml"
NamespaceDefinitions="msb=http://schemas.microsoft.com/developer/msbuild/2003;
fx=http://ns.adobe.com/mxml/2009;
mx=library://ns.adobe.com/flex/mx"
ContentRoot="//mx:Application/fx:Declarations/fx:String[#id='stringId']"
SubstitutionsFile="$(MSBuildProjectFullPath)"
SubstitutionsRoot="msb:Project/msb:ProjectExtensions/msb:ReplacementNode/msb:String"/>
</Target>
<Target Name="ChangeXmlValue">
<XmlUpdate Prefix="n"
Namespace="http://schemas.microsoft.com/developer/msbuild/2003"
XPath="n:Project/n:ProjectExtensions/n:ReplaceNode/n:String/text()"
XmlFileName="$(MSBuildProjectFullPath)"
Value="$(NewValue)" />
</Target>
</Project>