You can read all about RecursiveDir here, but basically MSBuild supports a syntax for recursive directories that goes a little something like this:
C:My Projects***.xml
Basically this says, go recursively through every directory under C:My Project and find me all the files with an .xml extension. Simple right? Ok, now imagine you structured all your projects in such a way that you kept certain xml files in certain subdirectory of each project… let’s call it “Test Results”. So, if I want to grab all the test results for all projects, but not any other .xml files, I would do this:
C:My Projects**Test Results*.xml
Follow so far? Now consider the following example directory structure:
C:My Projects
|- Project A
|- Test Results
|- 4-11-06.xml
|- 4-12-06.xml
Ok, the documentation states that the RecursiveDir metadata will be equivalent to the path that is expanded by the ** notation. When I use the first path declaration I end up with the following for RecursiveDir:
Project ATest Results
Great, that’s just what I’d expect because the ** resulted in both the Project A and Test Results directory being selected. However if I use the second path I still end up with this for RecursiveDir:
Project ATest Results
Hmmm… not what I’d expect. I would have expected only Project A to be included in the RecursiveDir value because Test Results was an explicit part of the path.
The documentation isn’t totally accurate, or at least could be more accurate. %(RecursiveDir) is everything from the ** up to the slash before the filename part. It’s really designed for one purpose, and that is to recursively reproduce a tree. Imagine I have a tree like this (c:tempfoobar1 and c:tempfoobarbar2)
C:TEMP
¦
+—foo
+—bar
¦ 1
¦
+—bar
2
And I wish to copy the files that are within a folder named bar to c:baz, preserving their relative locations.
So I create this project;
<Project xmlns=”http://schemas.microsoft.com/developer/msbuild/2003“>
<ItemGroup>
<Foo Include=”c:temp**bar*”/>
</ItemGroup>
<Target Name=”build”>
<Copy SourceFiles=”@(Foo)” DestinationFiles=”@(Foo->’c:baz%(RecursiveDir)%(Filename)’)”/>
</Target>
</Project>
And build it:
Target build:
Creating directory “c:bazfoobar”.
Copying file from “c:tempfoobar1” to “c:bazfoobar1”.
Creating directory “c:bazfoobarbar”.
Copying file from “c:tempfoobarbar2” to “c:bazfoobarbar2”.
Giving this final result:
C:BAZ
+—foo
+—bar
¦ 1
¦
+—bar
2
Does this example help explain why %(RecursiveDir) includes directories after the **? By modifying my project slightly, I could rename the files, too.
I’ll see if I can get the docs fixed, though.
Oh, my diagrams didn’t come out right, but hopefully this is clear enough (if you try it out)
Dan,
Thanks, it’s just not what I expected with the wording in the docs, but I totally get it.
It would be cool for future releases to add a new bit of meta-data that does contain only the part of the path represented by the **. Not sure what to call it… 😉
Cheers,
Drew