MSBuild:复制整个文件夹

67

我试图复制一个整个文件夹,但是当我这样做时:

<Copy SourceFiles="$(TargetDir)\*.*" DestinationFolder="$(BuildOutput)\SomeDir" />

该复制命令试图执行以下操作:将 c:\source\. 复制到 c:\destination\SomeDir\.,但是会因为出现了非法字符而失败。


2
这里曾经有一个类似的问题被提出:https://dev59.com/fnVD5IYBdhLWcg3wAWoO - joshhendo
8个回答

90

明确指定您的SourceFiles的ItemGroup。

<ItemGroup>
    <_CopyItems Include="$(TargetDir)\*.*" />
</ItemGroup>
<Copy
    SourceFiles="@(_CopyItems)"
    DestinationFolder="$(BuildOutput)\SomeDir"
    />
请注意,_CopyItems是一种项目类型,因此使用“@”符号而不是“$”进行引用。

14
最好在项目组中添加 <InProject>false</InProject>,否则您将在解决方案资源管理器中看到所有的 bin 文件夹内容。 <ItemGroup> <_CopyItems Include="$(TargetDir)*.*"> <InProject>false</InProject> </_CopyItems> </ItemGroup> - Zelenov

60

可以使用以下代码片段复制文件,该代码片段处理防病毒程序和子目录

  <ItemGroup>
        <SomeAppStuff Include="$(SolutionDir)\ProjectXXX\bins\**\*.*" />
  </ItemGroup>
  <Copy 
      SourceFiles="@(SomeAppStaff)" 
      DestinationFolder="$(OutputPath)\%(RecursiveDir)" 
      SkipUnchangedFiles="true"
      OverwriteReadOnlyFiles="true" 
      Retries="3"
      RetryDelayMilliseconds="300"/>

指定$(OutputPath)\%(RecursiveDir)将要求复制任务尊重子文件夹,因此它将把源目录的子文件夹放置到目标目录的子文件夹中。

SkipUnchangedFiles可提高具有足够内存的计算机上的构建速度,因为Windows会针对经常使用的文件优化IO。

RetriesRetryDelayMilliseconds处理以下问题: a)压缩的NTFS文件系统,在极少数情况下构建失败; b)使用SSD驱动器的杀毒软件。


DestinationFolder 设置为 path%(RecursiveDir) 的后果是什么?它会将目录放置在预期位置,还是放置在递归路径中最深的位置? - kayleeFrye_onDeck
1
kayleeFrye_onDeck,它将把源目录的子文件夹放在目标目录的子文件夹中,如果要删除它,则源目录的所有子文件夹的内容都将被放置到目标目录中。 - Siarhei Kuchuk
谢谢,我的文件夹已经被创建了,但是所有的文件都被放在根目录下,直到我改成了%(RecursiveDir) - Aaron
好奇MS Build能否处理驱动器号,我必须在msbuild时创建一个物理文件夹,但它不能在主网站中。 - c-sharp-and-swiftui-devni

40

如果你将文件夹放在C#项目的根目录下,那么你可以简单地将此内容放入你的csproj文件中。

<ItemGroup>
    <None Update="FolderToCopy\**\*.*">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
</ItemGroup>

我只在2017版本的csproj中进行了测试,但我认为它是向后兼容的。不过可能我错了。


14

根据MSDN文档,我认为SourceFiles参数需要一个ITaskItem[]值。请参见MSDN MSBuild Copy Task

上述链接中的最后一个示例是将一个目录递归复制到另一个目录,同时保留文件夹结构。


9
成功完成这项任务的方法如下。
<Target Name="AfterBuild">
  <ItemGroup>
    <SomeDir Include="$(SolutionDir)\SomeOtherProject\SomeDir\**\*" />
  </ItemGroup>
  <Copy 
    SourceFiles="@(SomeDir)" 
    DestinationFiles="@(SomeDir->'$(OutDir)\SomeDir\%(RecursiveDir)%(Filename)%(Extension)')" 
    SkipUnchangedFiles="true" 
    OverwriteReadOnlyFiles="true" 
    Retries="3" 
    RetryDelayMilliseconds="300" />


8
对于我来说有效的方法是: - 保持文件夹结构 - 复制文件夹中的所有文件 - 这适用于任何文件夹,不必在项目或同一项目文件夹中
<ItemGroup>
    <_CopyItems Include="<path relative to project>\**\*.*" />
</ItemGroup>

<Target Name="AfterBuild">
  <Copy SourceFiles="@(_CopyItems)" DestinationFiles="@(_CopyItems->'$(OutDir)\<output folder>\%(RecursiveDir)%(Filename)%(Extension)')"/>
</Target>

说明:

  • <相对于项目的路径>:这可以是任何路径,使用 ..\ 跳出 proj 文件夹即可。
  • <输出文件夹>:你想要整个文件结构被放置的文件夹,不包括源文件夹。
  • $(OutDir) 将会是 bin\Debug 或者任何其他的编译模式,如果你想要其他的,请更改它。

4

在 Visual Studio 15.4+ 中,有一个功能使这更容易 - 您可以设置LinkBaseLink来控制目标路径:

<ItemGroup>
  <Content Include="FolderToCopy\**" LinkBase="FolderInOutput\" CopyToOutputDirectory="Always" />
</ItemGroup>

来源:https://github.com/dotnet/msbuild/issues/2949#issuecomment-362823310

这是一个已知问题,MSBuild在处理多个项目引用时可能会出现这种情况。

我们已经为此创建了一个修复程序,该程序将在下一个.NET Core更新中发布。

在等待此修复程序发布之前,您可以通过在单独的msbuild.exe进程中构建解决方案来避免此问题。


谢谢!简单明了! 这种方法也适用于像$(ProjectDir)这样的常见宏 - riskeez

-2

对我来说最好的解决方案是使用神奇的XCOPY,因为我需要复制所有文件和子目录。

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
  <PropertyGroup>
    <FilesSource>$(ProjectDir)\lib</FilesSource>
    <FilesDestination Condition=" '$(SolutionName)' == 'any name a' ">$(ProjectDir)\..\..\Something\lib</FilesDestination>
    <FilesDestination Condition=" '$(SolutionName)' == 'the top solution name' ">$(SolutionDir)\Something\lib</FilesDestination>
  </PropertyGroup>
  <Error Condition=" '$(FilesDestination)' == '' " Text="Lib not delivered. To disable this message, remove the 'Target' tag from the project file" />
  <Exec Command="RD /S /Q &quot;$(FilesDestination)&quot;" />
  <Exec Command="XCOPY &quot;$(FilesSource)&quot; &quot;$(FilesDestination)&quot; /E /I /H /R /K /Y" />
  <Exec Command="RD /S /Q &quot;$(FilesSource)&quot;" />
</Target>

这个构建事件在构建成功后触发,清理FilesDestination文件夹,然后将FilesSource中的带有目录结构的全部文件复制到FilesDestination中,最后删除FilesSource文件夹以使一切保持“bien propre” :)

注意

对于FilesDestination,请确保使用Condition属性或将其删除,以便将复制过程执行到底。


1
这只兼容Windows操作系统。 - user1623521
可以使用兼容Mac或本地版本的XCopy。 - Fabrice T
1
这将给您的构建系统增加过多的复杂性。 - user1623521
这样,构建系统“没有完整的图片”,因此,这将导致不必要的重建部分,这是其他事情中的一部分。实际上,与为什么不应使用递归make的原因相似。 - 0xC0000022L

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