0

I am having Msbuild XML file like this.

<?xml version="1.0" encoding="utf-8"?> 
  <Project xmlns="schemas.microsoft.com/developer/msbuild/2003">
         <ItemGroup>
             <Notifications Include="someone@gmail.com"/>
         </ItemGroup>
    <ItemGroup> 
         <Xcopy Include="..\..\..\Release\UtilitiesAssembly.dll"> 
             <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination> 
         </Xcopy>
         <Xcopy Include="..\..\..\Release\Core.assembly.dll">
             <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination>
         </Xcopy>
         <Xcopy Include="..\..\..\Release\UIAssembly.dll"> 
             <Destination>"..\..\..\Anothercomponent\Folder1</Destination> 
         </Xcopy>
         <Xcopy Include="..\..\..\Release\Core.assembly.dll">
             <Destination>"..\..\..\Anothercomponent\depl\BOM</Destination>
         </Xcopy>
    </Itemgroup>
  </Project>

Actually i would like to group XCopy Itemgroup like this

      <Xcopy Include="..\..\..\Release\UtilitiesAssembly.dll;
                    ..\..\..\Release\\Core.assembly.dll;">
      <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination>
     </Xcopy>

     <Xcopy Include="..\..\..\Release\UIAssembly.dll;">
      <Destination>"..\..\..\Anothercomponent\Folder1</Destination>
     </Xcopy>

     <Xcopy Include="..\..\..\Release\Core.assembly.dll">
             <Destination>"..\..\..\Anothercomponent\depl\BOM</Destination>
     </Xcopy>

How to achieve it using Powershell or Msbuild or by some other mechanism

Samselvaprabu
  • 16,830
  • 32
  • 144
  • 230

2 Answers2

2

Here is an example how to do this using the Group-Object cmdlet. It will group the xcopy elements by Destination and combine the Include paths into a semi-colon separated string. The output is stored in a new XML document and saved to disk. This doesn't programatically update the original. You can just copy paste the new consolidated XML over you original XML.

Update Now that I see your whole file, I see it has a default namespace which causes problems with XPath. There are two ways to get around this problem.

  1. Use namespace agnostic xpath
  2. Use a namespace manager

Here is an updated example using namespace agnostic xpath. Also in order to get the indentation you are looking for we'll need to do some text file processing after the XML object processing.

$xml="Myinput.xml"
$tempXmlFile = "C:\New.xml"

$outXml = New-Object System.Xml.XmlDocument
$outXml.AppendChild($outXml.CreateElement("root")) > $null

$x = [xml] (Get-Content $xml)
$x.SelectNodes("//*[local-name()='Xcopy']") | Group-Object Destination | % {
    $includePaths = ($_.Group | Select-Object -ExpandProperty Include) -join ";"
    $element = $outXml.CreateElement("Xcopy")
    $element2 = $outXml.CreateElement("Destination")
    $element2.InnerText = $_.Name
    $element.AppendChild($element2) > $null
    $element.SetAttribute("Include", $includePaths)
    $outXml.DocumentElement.AppendChild($element) > $null
}
$outXml.Save($tempXmlFile)

$data = Get-Content $tempXmlFile | % {
    $search = 'Include="'
    $index = $_.IndexOf('Include="')
    if ($index -gt 0) { 
        $spaces = ( ' ' * ($index + $search.length) ) 
    }
    $_ -replace ';', ";`n${spaces}"
}
$data | Set-Content $tempXmlFile
& notepad $tempXmlFile

Creates output:

<root>
  <Xcopy Include="..\..\..\Release\UtilitiesAssembly.dll
                  ..\..\..\Release\Core.assembly.dll">
    <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination>
  </Xcopy>
  <Xcopy Include="..\..\..\Release\UIAssembly.dll">
    <Destination>"..\..\..\Anothercomponent\Folder1</Destination>
  </Xcopy>
  <Xcopy Include="..\..\..\Release\Core.assembly.dll">
    <Destination>"..\..\..\Anothercomponent\depl\BOM</Destination>
  </Xcopy>
</root>
Community
  • 1
  • 1
Andy Arismendi
  • 50,577
  • 16
  • 107
  • 124
  • I am getting following issue buddy. Cannot convert value "E:\Powershellscripts\Comp1.Targets" to type "System.Xml.XmlDocument". Error: "Data at the root level is invalid. Line 1, position 1." – Samselvaprabu Jan 16 '12 at 11:31
  • My input file looks like below " "..\..\..\Mycomponent\depl\BOM\Folder1 "..\..\..\Mycomponent\depl\BOM\Folder1 – Samselvaprabu Jan 16 '12 at 11:42
  • If Get-content $xml is replaced with $xml , i got the same error i faced. So i have edited the answer. Thanks buddy. – Samselvaprabu Jan 16 '12 at 17:26
  • @Samselvaprabu Woops I was testing a little differently by putting your source XML in a here string. I copied my update into the answer without re-adjusting it for you. Thanks. – Andy Arismendi Jan 16 '12 at 17:49
1

Starting from MSBuild 3.5 you can use ItemDefinitionGroup.

  <ItemDefinitionGroup>
    <Xcopy>
      <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination>
    </Xcopy>
  </ItemDefinitionGroup>
  <ItemGroup>
    <Xcopy Include="..\..\..\Release\UtilitiesAssembly.dll" />
    <Xcopy Include="..\..\..\Release\Core.assembly.dll" />
    <Xcopy Include="..\..\..\Release\UIAssembly.dll;">
      <Destination>"..\..\..\Anothercomponent\Folder1</Destination>     
    </Xcopy>
  </ItemGroup>
KMoraz
  • 14,004
  • 3
  • 49
  • 82
  • I think Itemdefinitiongroup would work if it has common metadata for all. It is a useful information. I have added clarity to my question further. Will it work for this case? – Samselvaprabu Jan 16 '12 at 12:34