msbuilditemgroup

MSBuild - ItemGroup of all bin directories within subdirectories


My Solution has multiple projects (and therefore subdirectories), and there is a 'bin' folder in each project folder.

I'm trying to create an ItemGroup in my MSBuild script that includes all these directories.

I thought this would be sufficient, but it doesn't contain anything:

<ItemGroup>
  <BinDirs Include="**\bin" />
</ItemGroup>

I'm not sure why this doesn't work. Can anyone point me in the right direction to achieve what I'm trying to do?

Regards, Nick


Solution

  • Since this hasn't got an answer, yet comes high on the list of Google results:

    The link provided by Alexey has several answers to work around this problem, but it's not obvious why the example you've given doesn't work.

    MSBuild ItemGroup collections don't seem to like wildcard Transforms when targeting directories.

    You can use explicit paths, e.g.

    <ItemGroup>
      <BinDirs Include="C:\MyProject\bin" />
    </ItemGroup>
    

    Or paths relative to where your build script is running, e.g.

    <ItemGroup>
      <BinDirs Include="..\MyProject\bin" />
    </ItemGroup>
    

    However it does not transform your wildcards unless you are targeting files, e.g.

    <ItemGroup>
      <ThisWorks Include="..\**\bin\*" />
      <ThisDoesnt Include="..\**\bin" />
    </ItemGroup>
    

    That post contains several ways to select folders using wildcards, the one I tend to use is:

    <ItemGroup>
      <GetAllFiles Include="..\**\bin\*.*" />
      <GetFolders Include="@(GetAllFiles->'%(RootDir)%(Directory)'->Distinct())" />
    </ItemGroup>
    

    As noted on the post, it's not perfect at selecting the root folders, as it has to find where there are files. Using bin*.* would only get the bin folder if files were located in it.

    If your build is anything like a standard VS output, you will probably find your bin folder has no files, instead having directories based on your configuration names, e.g. bin\Debug, in which case targeting bin\**\* will result in your item group containing those folders.

    E.g.

    <ItemGroup>
      <GetAllFiles Include="..\**\bin\**\*" />
      <GetFolders Include="@(GetAllFiles->'%(RootDir)%(Directory)'->Distinct())" />
    </ItemGroup>
    

    Would get:

    I don't know of a wildcard way to get bin folders without files in... yet. If anybody finds one, please post as it would be useful.

    Hope this helps someone save some time.