in Uncategorized

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.

Leave a comment


  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)

    ¦ 1

    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=”“>
    <Foo Include=”c:temp**bar*”/>
    <Target Name=”build”>
    <Copy SourceFiles=”@(Foo)” DestinationFiles=”@(Foo->’c:baz%(RecursiveDir)%(Filename)’)”/>

    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:

    ¦ 1

    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. Oh, my diagrams didn’t come out right, but hopefully this is clear enough (if you try it out)

  3. 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… 😉


  • Related Content by Tag