将文件复制到输出目录会复制文件夹结构,但我只想复制文件。

88

我有一个VS2008,我想从一个目录中复制某些文件到我的/bin/文件夹。我已将这些文件(位于/common/browserhawk/中)设置为“复制到输出目录”。但是,它会同时复制文件夹结构:文件被复制到/bin/common/browserhawk/

如何让这些文件只复制到/bin/?我不想将它们存储在网站的根目录中,以使其正确复制。

相关问题:Visual Studio编译后会向项目添加.dll和.pdb文件

8个回答

61

你可以添加一个“Post Build Event”来复制文件。
进入项目属性,选择“生成事件”选项卡,然后在“后期生成事件命令行”中添加以下内容:

copy "$(ProjectDir)\common\browserhawk\*.*" "$(TargetDir)"

如果您的项目路径中有空格,请确保包含引号。


谢谢你的建议,它在我的VS2012中也起作用了。 - Dib
2
在我的情况下,我稍微修改了这个命令: xcopy /s /d /y "$(ProjectDir)\stuff" "$(TargetDir)\stuff" - Danny Staple

47

由于我不能在之前的答案中进行评论,因此我将在这里提供解决方案:

在你的 .csproj/.vbproj 文件中加入以下内容,以补充 @PaulAlexander 的回答:

<ItemGroup>
    <AvailableItemName Include="RootContent">
      <Visible>false</Visible>
    </AvailableItemName>  
</ItemGroup>
<Target Name="AfterBuild">
    <Copy
        DestinationFolder="$(OutputPath)"
        SourceFiles="@(RootContent)"
        SkipUnchangedFiles="true"
        />  
</Target>

这使您可以在属性窗口中将"RootContent"选择为构建操作,并且所有内容都可以通过GUI访问。 更详细的解释: "AvailableItemName"选项基本上创建了一个新的命名列表,您可以在属性窗口下的"Build Action"属性中将项目项分配给该列表。然后,您可以在任何目标中使用此新创建的列表(例如通过"@(RootContent)")。


2
不幸的是,这只会将文件复制到它所属项目的输出目录中 - 而不是启动项目的输出目录。在库项目中使用时,这一点非常重要。 - Sebastian Krysmanski
2
这在使用vs2008的发布和调试模式下非常有效,但在使用一键发布时似乎不起作用。有什么建议吗? - Soenhay
但它没有将RootContent文件复制到我的项目输出文件夹中。我已经在VS2008和VS2010上进行了测试。有什么建议吗?有人可以发布一个示例项目吗? - Masood Khaari
不确定这是否已经不再相关或发生了什么,但它无法正常工作。测试了“复制到输出目录”设置为“仅在更新时复制”和“不复制”两种情况。使用“只有在更新时复制”仍会复制目录结构,并且使用“不复制”时不会执行任何操作。 - Brandon
3
在vs2015中对我没用。通过稍微重写目标来解决:<Target Name="RootOutputCopyTarget" AfterTargets="Build"> ... </Target> - lorond
显示剩余4条评论

44

如果您在文本编辑器中编辑 .csproj / .vbproj 文件,您可以控制文件在输出目录中的位置,以及文件在输出目录中的名称。例如:

<None Include="content\someContent.txt">
  <Link>someContentInOutputDirectory.txt</Link>
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

这将把文件content\someContent.txt放入bin\someContentInOutputDirectory.txt中。如果您想要选择bin中的子目录,则可以在链接元素中添加它。


3
哇,我现在需要这个真是太幸运了,如果是两天前就好了。谢谢,这太棒了! - Asherah
2
天啊,谢谢。我刚刚花了6个小时来解决一些边角案例的问题,其中VS没有以智能的方式复制.dll文件。这就是生命、宇宙和万物的真正答案。 - Brandon
5
这是目前为止最好的解决方案。 - PhilHdt
1
我发现,一旦我完成了这个操作并重新在Visual Studio中加载项目,依赖文件就会从解决方案资源管理器中消失,好像没有包含在项目中。但是它仍然按预期构建,并按所需进行输出复制。作为一种解决方法,我目前正在将每个文件添加两次;一次不执行任何操作,另一次设置“复制到输出”。 - Élie
1
这不是此标签的预期用法。在VS2013中,加载项目时会引发警告,并且该文件不可见,因为它是项目目录。 - jpmc26
显示剩余9条评论

15

我认为XCOPY命令更好地处理目录和文件。因此,

    XCOPY "$(ProjectDir)common/browserhawk" "$(TargetDir)" /E /I /F /Y

这允许在目标目录下创建文件夹。

    XCOPY "$(ProjectDir)Templates" "$(TargetDir)" /E /I /F /Y

项目的文件夹/文件结构:

    A:\TEMP\CONSOLEAPPLICATION3\TEMPLATES
    ├───NewFolder1
    ├───NewFolder2
    │       TextFile1.txt
    │       TextFile2.txt
    └───NewFolder3
            TextFile1.txt
            TextFile2.txt
            TextFile3.txt

变成:

    A:\TEMP\CONSOLEAPPLICATION3\BIN\DEBUG
    │   ConsoleApplication3.exe
    │   ConsoleApplication3.pdb
    │   ConsoleApplication3.vshost.exe
    │   ConsoleApplication3.vshost.exe.manifest
    ├───NewFolder1
    ├───NewFolder2
    │       TextFile1.txt
    │       TextFile2.txt
    │
    └───NewFolder3
            TextFile1.txt
            TextFile2.txt
            TextFile3.txt

/D 可能也很有用,以避免覆盖可能的更改。 - KurzedMetal

10
将以下内容添加到您的.csproj/.vbproj文件中。
<Target Name="AfterBuild">
    <Copy
        DestinationFolder="$(OutputPath)"
        SourceFiles="@(RootContent)"
        SkipUnchangedFiles="true"
        />  
</Target>

然后将任何想要在根目录下的文件的构建操作(Build Action)更改为RootContent。


2
谢谢,这看起来很有用。不幸的是,即使将您的代码片段添加到 .vbproj 文件中,"RootContent" 仍然没有出现在“生成操作”下拉列表中,手动输入会导致 Visual Studio 显示“属性值无效”的错误。我错过了什么? - Heinzi
无法在Visual Studio 2008 C#控制台应用程序项目中工作。 - AMissico
实际的项目列表可能会有不同的名称。您可以将项目构建设置为诊断模式,然后构建项目。在输出窗口中,您将看到一个长列表的属性/项值,这些值是MSBuild用于构建项目的。查找最匹配的属性值。 - Paul Alexander
如果你要这样做,你也应该更新FileWrites。否则,Clean不会真正清理那些文件。请参阅CopyFilesToOutputDirectory内置目标。 - Daniel Yankowsky
手动编辑项目文件,将元素类型设置为“RootContent”,然后它将显示在下拉列表中并正常工作。 - LMK
显示剩余3条评论

5

我在VS2010、VS2015、VS2017、VS2019和VS2022中使用过这个解决方案。我喜欢这个解决方案,因为:

  1. XML可在任何项目中重复使用
  2. "RootContent"被选择为Visual Studio UI中的Build Action,就像任何其他"Content"一样
  3. "CopyToOutputDirectory"也会被遵守,就像你期望的那样
  4. RootContent被添加到项目的输出中:通过Project-References传递,遵守"Clean"等规则
  5. 可以使用通配符指定RootContent,保留递归文件夹结构:
<RootContent Include="common\browserhawk\**">
  <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</RootContent>

项目文件开头处:

  <ItemGroup>
    <AvailableItemName Include="RootContent">
      <!-- Add "RootContent" as a choice in the "Build Action" dropdown. -->
      <Visible>True</Visible>
    </AvailableItemName>
  </ItemGroup>

借鉴自这个答案

在导入 Microsoft .targets 之后:

  <PropertyGroup>
    <AssignTargetPathsDependsOn>
      $(AssignTargetPathsDependsOn);
      IncludeRootContentAsContent;
    </AssignTargetPathsDependsOn>
  </PropertyGroup>
  <Target Name="IncludeRootContentAsContent">
    <CreateItem Include="@(RootContent)" AdditionalMetadata="TargetPath=%(RecursiveDir)%(Filename)%(Extension)">
      <Output ItemName="ContentWithTargetPath" TaskParameter="Include" />
    </CreateItem>
  </Target>

来自这个答案


这很好-我将RootContent替换为CustomContent,以指示这是我们添加到此项目中的专业内容,并且我们需要以特定方式复制。 另外,我需要使用TargetPath=%(RecursiveDir)%(Filename)%(Extension)来重新创建从指定的“CustomContent”文件到输出路径的文件夹结构。 - Jonno
@Jonno 目标是将文件直接复制到根输出目录,而不考虑RecursiveDirectory。内置的“Content” Build Action已经为我们提供了一种机制,可以在保持RecursiveDirectory的同时进行复制。 - MattGerg
我有一段时间没有测试过这个了,但是设置“Content”构建操作会不会像OP说的那样将/common/browserhawk/下的文件复制到/bin/common/browserhawk/下呢? 如果我没记错的话,我的意图是让/common/browserhawk/a.txt最终出现在/bin/a.txt中,但允许从那里向下递归,所以/common/browserhawk/secret/stuff.png最终出现在/bin/secret/stuff.png中。 - Jonno
@Jonno 好的,现在完全明白了。我更新了答案,包括%(RecursiveDirectory)。这真的很酷,因为除了在IDE中选择单个文件外,还可以使用通配符指定多个文件。当使用**通配符时,整个文件夹结构将被保留。 - MattGerg
1
非常感谢您提供如此好的答案!我认为这个答案是正确的,因为它满足以下条件:1. 没有外部命令;2. 跨平台。 - Yuriy Anisimov

2
我在nant构建文件中添加了一个步骤,在成功编译后进行复制。
<target name="action.copy.browserhawk.config" depends="compile.source">
    <copy todir="${App.Web.dir}/bin/" includeemptydirs="false">
        <fileset basedir="${browserhawk.config.dir}">
            <include name="bhawk_bb.dat" />
            <include name="bhawk_sp.dat" />
            <include name="browserhawk.properties" />
            <include name="maindefs.bdd" />
            <include name="maindefs.bdf" />
            <include name="BH_PRO.lic" />
        </fileset>
    </copy>
    <echo message="COPY BROWSERHAWK CONFIG: SUCCESS   ${datetime::now()}" />
</target>

2
+1 是因为对于使用 NAnt 的人来说,这仍然是一个有用的答案。 - Chris Haines

1
你可以建立一个批处理文件来复制这些文件,并将其作为后期构建事件执行。

我最终在我的一个项目中这样做了(尽管我使用的是Python脚本而不是批处理文件)。 - Fire Lancer

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