W tym pliki zawartości w .csproj, które znajdują się poza stożkiem projektu

85

Mam projekt C #, powiedz MyProject.csproj znajdujący się w „C: \ Projects \ MyProject \”. Mam również pliki, które chcę skopiować do katalogu wyjściowego tego projektu. Ale pliki znajdują się w lokalizacji „C: \ MyContentFiles \”, tj. NIE znajdują się w obrębie stożka projektu. Ten katalog zawiera również podkatalogi. Zawartość katalogu nie jest zarządzana. Dlatego muszę uwzględnić wszystko, co się pod nim znajduje.

Kiedy dołączam je do projektu jako „Treść”, są one kopiowane, ale struktura katalogów zostaje utracona. Zrobiłem coś takiego: -

<Content Include="..\..\MyContentFiles\**">
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Jak mogę rekurencyjnie skopiować te pliki / katalogi do katalogu wyjściowego projektu z zachowaną strukturą katalogów?

Poulo
źródło

Odpowiedzi:

55

Musisz dodać plik jako link:

  1. Kliknij prawym przyciskiem myszy projekt w VS.
  2. Dodaj -> Istniejący element ...
  3. Znajdź plik.
  4. Wybierz i.
  5. Dodaj jako łącze (lista rozwijana w przycisku Dodaj w oknie dialogowym).
  6. Otwórz właściwości pliku i ustaw opcję „Kopiuj do katalogu wyjściowego” na „Zawsze kopiuj”.

ALE nie możesz tego zrobić dla drzewa katalogów.
Zamiast tego musisz napisać w tym celu zadanie po kompilacji. To jest próbka, która sprawi, że będziesz się gapić.

Dmytrii Nagirniak
źródło
8
Jak stwierdzono w tej odpowiedzi , „Nie możesz tego zrobić dla drzewa katalogów”. stwierdzenie nie jest prawdziwe.
Mandark
160

Uważam, że @Dmytrii robi to dobrze z jednej strony - chcesz użyć funkcji „łącza”.

Jednak tylko częściowo ma rację, mówiąc, że nie można dowiązać do drzewa katalogów. Chociaż jest to rzeczywiście prawdą, gdy próbujesz dodać linki przy użyciu graficznego interfejsu użytkownika programu Visual Studio, MSBuild to obsługuje.

Jeśli chcesz zachować strukturę katalogów, po prostu dodaj %(RecursiveDir)znacznik do swojego <link>węzła:

<Content Include="..\..\MyContentFiles\**\*.*">
  <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Strona MSBuild Dobrze znane metadane elementu zawiera bardziej szczegółowe informacje na temat metadanych, do których można uzyskać dostęp.

Mandark
źródło
1
1) Co się stanie, jeśli plik zostanie usunięty z magazynu zasobów wspólnych? Nie wygląda na to, że zostanie usunięty z katalogu rozwiązania 2) Czy istnieje sposób, aby program Visual Studio usunął te pliki z $ (SolutionDir) po zakończeniu wykonywania. Pozostawienie ich tam może zmylić innych twórców.
Adam
@Adam Zobacz moją odpowiedź poniżej
Robin van der Knaap
8
Ponieważ rozbiłem się na pustyni, mając niewiele jedzenia, wody i mając silną potrzebę towarzystwa, uważam, że ta odpowiedź jest pomocna. Postępując zgodnie z tymi instrukcjami, byłem w stanie zbudować w pełni funkcjonalną sztuczną inteligencję, która teraz zastąpiła wszystkie ludzkie potrzeby, które kiedyś prześladowały mnie w dziczy. Dzięki Mandark!
deepelement
1
@Vijay zobacz moją edycję, potrzebujesz <Content Include="..\..\MyContentFiles\**\*.*">- zwróć uwagę \*.*na koniec ścieżki.
Facet z CAD
2
Nie działa dla projektu .NET Core w VS2017 (15.8.6).
zwcloud
29

Odpowiedź Mandarka dodaje pliki zawartości bezpośrednio do rozwiązania i pojawią się one w eksploratorze rozwiązań. Za każdym razem, gdy plik jest dodawany lub usuwany w oryginalnym katalogu, nie jest on automatycznie pobierany przez program Visual Studio. Ponadto za każdym razem, gdy plik zostanie usunięty lub dodany w eksploratorze rozwiązań, plik projektu zostanie zmieniony, a wszystkie pliki są dołączane osobno, zamiast po prostu dołączać folder.

Aby temu zapobiec, możesz użyć tej samej sztuczki, ale umieścić ją w osobnym pliku projektu, a następnie zaimportować.

Plik projektu (na przykład include.proj) wygląda następująco:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Content Include="..\..\MyContentFiles\**">
  <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

W swoim własnym pliku projektu dodaj następujący wiersz

<Import Project="include.proj" />

Program Visual Studio nie będzie bałaganu z tym plikiem i po prostu dodaje pliki jako zawartość podczas kompilacji. Zmiany w oryginalnym katalogu są zawsze uwzględniane. Pliki nie pojawią się w eksploratorze rozwiązań, ale zostaną uwzględnione w katalogu wyjściowym.

Wykryto tę sztuczkę tutaj: http://blogs.msdn.com/b/shawnhar/archive/2007/06/06/wildcard-content-using-msbuild.aspx

Robin van der Knaap
źródło
1
Z jakiegoś powodu wydawało mi się, że to nie zadziała, używając pliku .proj, ale udało mi się to zrobić z plikiem .csproj.
StriplingWarrior
6
Możesz dodać <Visible> true </Visible> do elementu Content, aby elementy były wyświetlane w Eksploratorze rozwiązań.
Michael Baker
Mój był w porządku, tylko z .proj
Jason Dufair.
To w ogóle nie zadziałało i zakładam, że dzieje się tak dlatego, że elementy, które mają być połączone, zostały wygenerowane w wydarzeniu poprzedzającym kompilację.
Paul K
2
Nie działa dla projektu .NET Core w VS2017 (15.8.6).
zwcloud
10

Poniższe elementy, które można dodać na końcu pliku projektu, skopiują pliki zawartości z zachowaniem struktury katalogów w zdarzeniu po kompilacji do katalogu docelowego $(TargetDirectory)kompilacji (zazwyczaj $(MSBuildProjectDirectory)\bin\Debug).

<ItemGroup>
    <ExtraContent Include="$(MSBuildProjectDirectory)\..\..\MyContentFiles\**" />
</ItemGroup>

<Target Name="AfterBuild">
    <Copy 
        SourceFiles="@(ExtraContent)" 
        DestinationFiles="@(ExtraContent->'$(TargetDir)\%(RecursiveDir)%(Filename)%(Extension)')" 
        SkipUnchangedFiles="true" />
</Target>

Jeśli te pliki musiały znaleźć się w katalogu o nazwie MyContentFiles, możesz dodać to przed kopiowaniem:

<MakeDir Directories="$(TargetDir)\MyContentFiles" Condition=" !Exists('$(TargetDir\MyContentFiles') " />

i zmienić

<Copy 
            SourceFiles="@(ExtraContent)" 
            DestinationFiles="@(ExtraContent->'$(TargetDir)\%(RecursiveDir)%(Filename)%(Extension)')" 
            SkipUnchangedFiles="true" />

Do

<Copy 
            SourceFiles="@(ExtraContent)" 
            DestinationFiles="@(ExtraContent->'$(TargetDir)\MyContentFiles\%(RecursiveDir)%(Filename)%(Extension)')" 
            SkipUnchangedFiles="true" />
Todd
źródło
1
Twoje transformacje mają typ-o: @ (ExtraContent) -> '...') powinno być @ (ExtraContext -> '...'). W przeciwnym razie dobra odpowiedź!
alexdej
@Todd 1) Co się stanie, jeśli plik zostanie usunięty ze sklepu MyContentFiles? Nie wygląda na to, że zostanie usunięty z katalogu rozwiązania 2) Czy istnieje sposób, aby program Visual Studio usunął te pliki z $ (SolutionDir) po zakończeniu wykonywania. Pozostawienie ich tam może zmylić innych twórców.
Adam
3

Myślę

<Content Include="..\..\MyContentFiles\**\*.*">
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Wystarczy, ponieważ chcesz wszystko w tym folderze i podfolderach

Menzi
źródło
2

Aby dołączyć pliki do folderu dla projektu .NET Core,

<!--Just files-->
<ItemGroup>
  <None Update="..\..\MyContentFiles\**\*.*">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>
<!--Content files-->
<ItemGroup>
  <Content Include="..\..\MyContentFiles\**\*.*" Link="MyContentFiles\%(RecursiveDir)%(Filename)%(Extension)">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>

Właściwość elementu „Kopiuj do katalogu wyjściowego” będzie miała wartość „Kopiuj, jeśli nowsza”:
Skopiuj, jeśli nowszy

zwcloud
źródło
-1

Aby zignorować plik w projekcie .Net Core:

<ItemGroup>
 <Content Include="appsettings.local.json">
   <CopyToOutputDirectory Condition="Exists('appsettings.local.json')">PreserveNewest</CopyToOutputDirectory>
 </Content>
</ItemGroup>
Skorunka František
źródło