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 foundhttp://forums.microsoft.com/msdn/ShowPost.aspx?PostID=6862 to be way confusing.)
Thanks
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>
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