2011-09-01 54 views
1

鉴于目录的列表:获得子目录列表中的MSBuild

<ItemGroup> 
    <Dirs Include="Foo\Dir1" /> 
    <Dirs Include="Foo\Dir2" /> 
</ItemGroup> 

我怎样才能得到所有子目录的列表。

将此列表转换为"$(Identity)\**"不匹配任何内容,并且使用"$(Identity)\**\*"进行转换,然后使用RelativeDir转换,仅生成包含文件的目录。

目前,我不得不求助于C#:

<UsingTask TaskName="GetSubdirectories" TaskFactory="CodeTaskFactory" 
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"> 
    <ParameterGroup> 
     <Directories ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" /> 
     <SubDirectories ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" /> 
    </ParameterGroup> 
    <Task> 
     <Code Type="Fragment" Language="cs"><![CDATA[ 
      var result = new List<ITaskItem>(); 
      foreach (var dirItem in Directories) { 
       foreach (var dir in Directory.GetDirectories(dirItem.ItemSpec, "*", SearchOption.AllDirectories)) { 
        if (dir.Contains(@"\.svn\") || dir.EndsWith(@"\.svn")) continue; 
        result.Add(new TaskItem(dir)); 
       } 
      } 
      SubDirectories = result.ToArray(); 
     ]]></Code> 
    </Task> 
</UsingTask> 

<GetSubdirectories Directories="@(Dirs)"> 
    <Output TaskParameter="SubDirectories" ItemName="SubDirs" /> 
</GetSubdirectories> 

但我想知道如果有一个更简单的方法。

回答

2

从书“的MSBuild挂羊头卖狗肉”摘录:

<Import Project="EnableAllPropertyFunctions.tasks" /> 

<Target Name="GetSubdirectories"> 
    <ItemGroup> 
     <Dirs Include="$([System.IO.Directory]:: 
     EnumerateDirectories(
      `.\Foo`, 
      `*`, 
      System.IO.SearchOption.AllDirectories))" 
      /> 
    </ItemGroup> 

    <Message Text="%(Dirs.Identity)" /> 
</Target> 

您需要先启用扩展属性功能,确保环境变量MSBuildEnableAllPropertyFunctions被设置为值1(设置是什么通过内联任务导入.tasks文件完成)。

一旦@(DIRS)设置完毕后,您可以使用删除属性过滤它摆脱了Subversion的文件夹。

+0

我假设内联任务将调用'Environment.SetEnvironmentVariable',但谁调用任务? 此外,如果您计算该文件,它会比我的示例中的代码更多:) –

+0

该任务将在导入中使用InitialTargets声明自动运行。诚然,它确实添加了一些代码行,但它们被添加到一个可重用的导入文件中,该文件扩展了整套可用的属性函数,因此如果您的构建需要任何其他扩展,您就可以做好准备。不是一个很大的改进,但也许朝着正确的方向迈出了一步? (本书深入介绍了这一点) –

1
<CreateItem Include="$(OutputFolder)\*\*.*"> 
    <Output TaskParameter="Include" ItemName="FilesInSubFolders" /> 
</CreateItem> 

<RemoveDuplicates Inputs="@(FilesInSubFolders->'%(RelativeDir)')"> 
    <Output TaskParameter="Filtered" ItemName="SubDirs"/> 
</RemoveDuplicates> 

<Message Text="@(SubDirs)"/> 

这将把所有直接的子文件夹路径放到@(SubDirs)中。如果将Include="$(OutputFolder)\*\*.*"更改为Include="$(OutputFolder)\**\*.*",它将递归地包含所有子文件夹。

0

要使用完全独立的例如在Brian的回答扩大:

<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="GetSubdirectories"> 
    <UsingTask TaskName="SetEnvironmentVariable" 
      TaskFactory="CodeTaskFactory" 
      AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll"> 
    <ParameterGroup> 
     <Name ParameterType="System.String" Required="true" /> 
     <Value ParameterType="System.String" Required="true" /> 
    </ParameterGroup> 
    <Task> 
     <Using Namespace="System" /> 
     <Code Type="Fragment" Language="cs"><![CDATA[ 
     Environment.SetEnvironmentVariable(Name, Value); 
     ]]></Code> 
    </Task> 
    </UsingTask> 

    <Target Name="GetSubdirectories"> 
    <SetEnvironmentVariable Name="MSBuildEnableAllPropertyFunctions" Value="1" /> 
    <ItemGroup> 
     <Dirs Include="$([System.IO.Directory]::EnumerateFiles('.\Stuff', '*', System.IO.SearchOption.AllDirectories))"/> 
    </ItemGroup> 
    <Message Text="%(Dirs.Identity)" /> 
    </Target> 
</Project> 

我从this answer得到了UsingTask例子。