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

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