Multiple fully-qualified paths in a single Task

I need to pass two sets of fully qualified file names to a task.  Fully qualified paths are required because the task, (NUnit, in my case), doesn't accept relative paths for every parameter.

The only way I know to get full paths from a relative path is to first create an item group,

<ItemGroup>
   <Foo Include="*.foo"/>
</ItemGroup>

and then reference it with the following syntax, %(Foo.FullPath)  So far, so good. 

I need another (single) fully qualified path, this time for a single file.

<ItemGroup>
   <Bar Include=".\Build\bar"/>
</ItemGroup>


Now, the problem arises when trying to use fullpaths in the same task.  

<Exec Command ="ECHO FIRST: %(foo.fullpath) SECOND: %(bar.fullpath)" />

The above sample will iterate over the bar files, with the foo files empty, then iterate over the foo files, with the bar files empty.  Not a very useful behavior, because both are needed in EACH task iteration.

1) Is there any way to convert a relative path to a fullpath besides the %(foo.fullpath) approach  
2) Is it possible to use more than one fullpath in a task   And if so, how
3) Is there documentation (with simple examples) on how to mimic Nant's for-each in MSBuild   (I found http://forums.microsoft.com/msdn/ShowPost.aspx PostID=6862 to be way confusing.)

Thanks



Answer this question

Multiple fully-qualified paths in a single Task

  • Graham Williams

    I'm not sure exactly what behavior you're looking for, so I'll try two possible solutions to see if either of those is what you're after:

    1.  You'd like to pass the set of fullpaths from foo and the set of fullpaths from bar, in one task call.  This is straightforward to accomplish with a transform:

    <Exec Command="ECHO FIRST: @(Foo->'%(FullPath)') SECOND: @(Bar->'%(FullPath)')" />

    In this example, each item list will be transformed into another item list whose elements contain the FullPath metadata as the ItemSpec.  There will only be one call/iteration because there is no batching (the reference to FullPath is inside a transform).

    2.  You'd like to iterate over each element foo individually, but always pass the same value of bar.  You could do this with a solution halfway between your original one and the one I explain above:

    <Exec Command="ECHO FIRST: %(Foo.FullPath) SECOND: @(Bar->'%(FullPath)')" />

    In this example, you'll end up with one call to the Exec task for each unique value of "FullPath" on the Foo items - and each call will have the same expansion of Bar.

    Below are examples of each, let us know if that doesn't solve your problem.

    Thanks,
    jeff
    tester on msbuild

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

     <ItemGroup>
      <Foo Include="Sources\*.cs" />
      <Bar Include="Sources\*.txt" />
     </ItemGroup>

     <Target Name="CompileMyProject">
      <Message Text="Solution 1:" />
      <Exec Command="ECHO FIRST: @(Foo->'%(FullPath)') SECOND: @(Bar->'%(FullPath)')" />

      <Message Text="Solution 2:" />
      <Exec Command="ECHO FIRST: %(Foo.FullPath) SECOND: @(Bar->'%(FullPath)')" />
     </Target>

    </Project>

    C:\Basic>msbuild
    Microsoft (R) Build Engine Version 2.0.50730.0
    [Microsoft .NET Framework, Version 2.0.50730.0]
    Copyright (C) Microsoft Corporation 2005. All rights reserved.

    Build started 8/4/2005 6:28:58 PM.
    __________________________________________________
    Project "C:\Basic\MyProject.csproj" (default targets):

    Target CompileMyProject:
        Solution 1:
        ECHO FIRST: C:\Basic\Sources\a.cs;C:\Basic\Sources\b.cs;C:\Basic\Sources\c.cs;C:\Basic\Sources\d.cs SECOND: C:\Basic
    \Sources\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Basic\Sources\4.txt
        FIRST: C:\Basic\Sources\a.cs;C:\Basic\Sources\b.cs;C:\Basic\Sources\c.cs;C:\Basic\Sources\d.cs SECOND: C:\Basic\Sour
    ces\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Basic\Sources\4.txt
        Solution 2:
        ECHO FIRST: C:\Basic\Sources\a.cs SECOND: C:\Basic\Sources\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Ba
    sic\Sources\4.txt
        FIRST: C:\Basic\Sources\a.cs SECOND: C:\Basic\Sources\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Basic\S
    ources\4.txt
        ECHO FIRST: C:\Basic\Sources\b.cs SECOND: C:\Basic\Sources\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Ba
    sic\Sources\4.txt
        FIRST: C:\Basic\Sources\b.cs SECOND: C:\Basic\Sources\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Basic\S
    ources\4.txt
        ECHO FIRST: C:\Basic\Sources\c.cs SECOND: C:\Basic\Sources\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Ba
    sic\Sources\4.txt
        FIRST: C:\Basic\Sources\c.cs SECOND: C:\Basic\Sources\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Basic\S
    ources\4.txt
        ECHO FIRST: C:\Basic\Sources\d.cs SECOND: C:\Basic\Sources\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Ba
    sic\Sources\4.txt
        FIRST: C:\Basic\Sources\d.cs SECOND: C:\Basic\Sources\1.txt;C:\Basic\Sources\2.txt;C:\Basic\Sources\3.txt;C:\Basic\S
    ources\4.txt

    Build succeeded.
        0 Warning(s)
        0 Error(s)

    Time Elapsed 00:00:00.37

    C:\Basic>



  • blsandhya

    Thank you, that helped a lot.

    Here is a little example I developed for the most common cases.

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

     <ItemGroup>
      <A Include="*.cs" />
      <B Include="*.txt" />
     </ItemGroup>

     <Target Name="List, Batching and Transform Examples">

      <Message Text="Environment variable: $(TEMP)"/>
      <Message Text =" "/>


      <Message Text="Iterate for each item (for-each):  %(A.Identity)"/>
      <Message Text =" "/>
      <Message Text="Iterate for each item, single transform: %(A.fullpath)"/>
      <Message Text =" "/>
      <Message Text="Iterate for each item, multiple transforms: %(A.rootdir)%(directory)%(filename)%(extension)"/>
      <Message Text =" "/>
      <Message Text="Iterate task for each A, pass whole B list each time: %(A.Identity) @(B)"/>
      <Message Text =" "/>
      <Message Text="Iterate Cartesian product of A and B (for each A, for each B):Not possible.  Write/use a task which accepts lists as

    parameters"/>
      <Message Text =" "/>

      <Message Text="Default List: @(A)"/>
      <Message Text =" "/>
      <Message Text="List with Identity transform (same as above): @(A->'%(Identity)')"/> 
      <Message Text =" "/>
      <Message Text="List with custom separator: @(A,'!')"/>
      <Message Text =" "/>
      <Message Text="Multiple lists (all passed to single task): @(A) @(B)"/>
      <Message Text =" "/>
      <Message Text="List with transform applied to each item: @(A->'%(FullPath)')"/>
      <Message Text =" "/>
      <Message Text="List with transform and arbitrary text: @(A->'%(filename).XYZ')"/>
      <Message Text =" "/>
      <Message Text="List with multiple transforms: @(A->'%(rootdir)%(directory)%(filename)%(extension)')"/>
      <Message Text =" "/>


     </Target>

    </Project>

    And the output is:

    Target Simple Batching and Transform Examples:
        Environment variable: C:\DOCUME~1\myself\LOCALS~1\Temp
         
        Iterate for each item (for-each):  bar.cs
        Iterate for each item (for-each):  foo.cs
         
        Iterate for each item, single transform: C:\Temp\bar.cs
        Iterate for each item, single transform: C:\Temp\foo.cs
         
        Iterate for each item, multiple transforms: C:\Temp\bar.cs
        Iterate for each item, multiple transforms: C:\Temp\foo.cs
         
        Iterate task for each A, pass whole B list each time: bar.cs t.txt;x.txt;y.txt
        Iterate task for each A, pass whole B list each time: foo.cs t.txt;x.txt;y.txt
         
        Iterate Cartesian product of A and B (for each A, for each B):Not possible.  Write/use a task which accepts lists as parameters
         
        Default List: bar.cs;foo.cs
         
        List with Identity transform (same as above): bar.cs;foo.cs
         
        List with custom separator: bar.cs!foo.cs
         
        Multiple lists (all passed to single task): bar.cs;foo.cs t.txt;x.txt;y.txt
         
        List with transform applied to each item: C:\Temp\bar.cs;C:\Temp\foo.cs
         
        List with transform and arbitrary text: bar.XYZ;foo.XYZ
         
        List with multiple transforms: C:\Temp\bar.cs;C:\Temp\foo.cs


  • Multiple fully-qualified paths in a single Task