Копировать в выходной каталог копирует структуру папок, но копирует только файлы


87

У меня есть VS2008. Я хочу скопировать определенные файлы из каталога в свою /bin/папку. Я установил файлы (расположенные в /common/browserhawk/) на «Копировать в выходной каталог». Однако он также копирует структуру папок: файлы копируются в/bin/common/browserhawk/

Как мне сделать так, чтобы эти файлы можно было просто скопировать /bin/? Я не хочу хранить их в корне веб-сайта, чтобы их можно было правильно скопировать.

Связанный вопрос: Visual Studio добавляет в проект .dll и .pdb после компиляции

Ответы:


59

Вы можете добавить событие Post Build, чтобы скопировать файлы.
Перейдите в свойства проекта, вкладку Build Events и добавьте следующее в командную строку Post-build event:

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

Обязательно укажите кавычки, если в пути вашего проекта есть пробелы.


Спасибо за этот совет, так как он только что сработал для меня в VS2012.
Диб

2
Я немного изменил это в моем случае:xcopy /s /d /y "$(ProjectDir)\stuff" "$(TargetDir)\stuff"
Дэнни Стейпл

44

Поскольку я не могу комментировать предыдущие ответы, я поставлю решение здесь:

В дополнение к тому, что предоставил @PaulAlexander, добавьте следующее в свой файл .csproj / .vbproj:

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

Это позволяет вам выбрать «RootContent» в качестве действия сборки в окне «Свойства», и все это можно будет получить через графический интерфейс. Более полное объяснение: опция «AvailableItemName» в основном создает новый именованный список, которому вы можете назначать элементы в проекте в свойстве «Build Action» в окне «Свойства». Затем вы можете использовать этот недавно созданный список в любой цели, которая вам нравится (например, через "@ (RootContent)").


Хороший ответ, просто использовал это сам к успеху!
Крис Марисич

Также обратите внимание, что VS 2010 (не уверен в более старых версиях) будет жаловаться AvailableItemNameна недопустимый элемент, если вы редактируете файл проекта в VS; просто игнорируйте предупреждение, оно все равно работает.
Aaronaught 08

2
К сожалению, это копирует файл только в выходной каталог проекта, которому он принадлежит, а не в выходной каталог запускаемого проекта. Это важно при использовании в библиотечном проекте.
Себастьян Крысмански

2
Это отлично подходит для сборки в режиме выпуска и отладки с vs2008, но, похоже, не работает при использовании публикации в один клик. Какие-либо предложения?
Soenhay

2
У меня не сработало в vs2015. Исправлено путем небольшого переписывания цели: <Target Name="RootOutputCopyTarget" AfterTargets="Build">... </Target>
lorond

44

Если вы редактируете .csproj / .vbproj в текстовом редакторе, вы можете контролировать, где файл будет помещен в выходной каталог, а также какое имя будет иметь файл в выходном каталоге. Например:

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

Это поместит файл content\someContent.txtв формат bin\someContentInOutputDirectory.txt. Вы также можете выбрать подкаталог в, binесли хотите; просто добавьте его в элемент Link.


3
Вау, мне повезло, что это нужно сейчас, а не два дня назад. Благодарность; это круто!
Asherah

2
Боже мой, спасибо. Я просто потратил 6 часов, пытаясь исправить какой-то угловой мусор, когда VS не копировал .dll разумным образом. Это настоящий ответ жизни, вселенной и всему остальному.
Брэндон

5
Это, безусловно, лучшее решение
PhilHdt

1
Как только я сделал это и перезагрузил проект в Visual Studio, я обнаружил, что файлы зависимостей исчезли из обозревателя решений, как если бы они не были включены в проект. Однако он по-прежнему строится, как ожидалось, и копирует для вывода по желанию. В качестве обходного пути я сейчас добавляю каждый файл дважды; один раз без действия и один раз с установленным параметром «копировать в вывод».
Éliette

1
Это не предполагаемое использование этого тега. В VS2013 это вызывает предупреждение при загрузке проекта, и файл не отображается как добавленный в проект, потому что это каталог проекта.
jpmc26

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>

Затем измените действие сборки для любых файлов, которые вы хотите в корневой папке, на RootContent.


Не через диалоги ... но вы можете редактировать свой проект в VS. Щелкните проект правой кнопкой мыши и выберите «Выгрузить проект». Затем щелкните правой кнопкой мыши и выберите «Изменить». Затем вы можете отредактировать файл MSBuild, который является вашим проектом.
Пол Александр

2
Спасибо, это полезно. К сожалению, даже после добавления вашего фрагмента в файл .vbproj RootContentон не отображается в раскрывающемся списке «Действие при сборке», а ввод его вручную приводит к ошибке «Недопустимое значение свойства» из Visual Studio. Что я упустил?
Heinzi

Не работает с проектом консольного приложения C # Visual Studio 2010.
AMissico

Не работает с проектом консольного приложения Visual Studio 2010 VB.NET.
AMissico,

Не работает с проектом консольного приложения C # Visual Studio 2008.
AMissico

5

Я использовал это в VS2010, VS2015, VS2017 и VS2019. Я предпочитаю это решение, потому что:

  1. XML можно повторно использовать в любом проекте
  2. «RootContent» выбирается в качестве действия сборки в пользовательском интерфейсе Visual Studio, как и любой другой «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» уже предоставляет нам механизм для копирования с сохранением RecursiveDirectory.
MattGerg

У меня не было возможности протестировать это некоторое время, но я бы не установил действие сборки «Content», чтобы файлы /common/browserhawk/копировались в, /bin/common/browserhawk/как сказал OP? IIRC, мое намерение состояло в том, чтобы /common/browserhawk/a.txtзакончить, /bin/a.txtно разрешить рекурсию оттуда вниз, поэтому /common/browserhawk/secret/stuff.pngзаканчивается /bin/secret/stuff.png.
Jonno

@Jonno Хорошо, да, теперь это имеет смысл. Я обновил ответ, включив в него расширение %(RecursiveDirectory). Это действительно здорово, потому что помимо выбора отдельных файлов в среде IDE вы также можете указать несколько файлов с помощью подстановочных знаков. При использовании **подстановочного знака вся структура папок будет сохранена.
MattGerg

1
Большое спасибо за такой отличный ответ! Я думаю, что это правильный ответ по следующим причинам: 1. Нет внешних команд 2. кроссплатформенность
Георгий Анисимов

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, это все еще полезный ответ
Крис Хейнс

1

Вы можете создать командный файл, чтобы скопировать файлы и выполнить его как событие после сборки.


В итоге я сделал это для одного из своих проектов (хотя я использовал скрипт Python, а не летучую мышь).
Fire Lancer
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.