获取MSBuild中子目录列表

4

给定一个目录列表:

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

但我想知道是否有更简单的方法。

3个回答

3

摘自书籍“MSBuild Trickery”:

<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)设置完成,你可以使用Remove属性对其进行过滤,以去除Subversion文件夹。

我假设内联任务将调用 "Environment.SetEnvironmentVariable" 函数,但是谁在调用这个任务?另外,如果你计算一下那个文件的话,它比我的示例代码还要长 :) - Marcin Wisnicki
任务通过导入中的InitialTargets声明自动运行。是的,它确实增加了一些代码行,但它们是在可重用的导入文件中添加的,该文件扩展了整个可用属性函数集,因此,如果您的构建需要任何其他扩展,您将准备就绪。这不是一个很大的改进,但也许是朝着正确方向迈出的一步?(本书深入介绍了这一点) - Brian Kretzler

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)\**\*.*",它将递归地包含所有子文件夹。

空目录怎么办? - l33t

1

为了进一步解释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>

我从这个答案中得到了UsingTask的示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接