MSBuild’s RecursiveDir Metadata Not Exactly What I Expected

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.

0 thoughts on “MSBuild’s RecursiveDir Metadata Not Exactly What I Expected

  1. 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.

  2. 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

Leave a Reply