Skopiuj wszystkie pliki i foldery za pomocą msbuild

94

Zastanawiam się tylko, czy ktoś mógłby mi pomóc z kilkoma skryptami msbuild, które próbuję napisać. To, co chciałbym zrobić, to skopiować wszystkie pliki i podfoldery z folderu do innego folderu za pomocą msbuild.

{ProjectName}
      |----->Source
      |----->Tools
              |----->Viewer
                       |-----{about 5 sub dirs}

To, co muszę zrobić, to skopiować wszystkie pliki i podfoldery z folderu narzędzi do folderu debugowania aplikacji. To jest kod, który mam do tej pory.

 <ItemGroup>
<Viewer Include="..\$(ApplicationDirectory)\Tools\viewer\**\*.*" />
 </ItemGroup>

<Target Name="BeforeBuild">
        <Copy SourceFiles="@(Viewer)" DestinationFolder="@(Viewer->'$(OutputPath)\\Tools')" />
  </Target>

Skrypt kompilacji działa, ale nie kopiuje żadnych plików ani folderów.

Dzięki

Nathan W.
źródło

Odpowiedzi:

136

W tym też szukałem pomocy. Zajęło mi to trochę czasu, ale oto, co zrobiłem, że zadziałało naprawdę dobrze.

<Target Name="AfterBuild">
    <ItemGroup>
        <ANTLR Include="..\Data\antlrcs\**\*.*" />
    </ItemGroup>
    <Copy SourceFiles="@(ANTLR)" DestinationFolder="$(TargetDir)\%(RecursiveDir)" SkipUnchangedFiles="true" />
</Target>

To rekurencyjnie skopiowało zawartość folderu o nazwie antlrcsdo $(TargetDir).

Rodolfo Neuber
źródło
4
Tak, to najlepsza odpowiedź. To samo, co zalecane tutaj na blogu msdn
Karsten
18
Wydaje się, że sztuczka polega na tym, że dodanie %(RecursiveDir)do folderu docelowego odtworzy strukturę katalogów. W przeciwnym razie wyjście jest płaskie. To jest najlepsza odpowiedź.
JB. Z Monicą.
1
Należy umieścić na dole pliku .fsproj lub nie zajmuje.
Henrik,
Kluczowym momentem, który zadziałał dla mnie, jest przeniesienie deklaracji zmiennej ( <ANTLR Include = ".. \ Data \ antlrcs ***. *" /> ) W ramach celu AfterBuild. W moim przypadku zostało to zadeklarowane w zakresie zewnętrznym i nie zadziałało.
Shpand
To świetny pomysł! Znalazłem %(RecursiveDir)w MSBuild dobrze znane metadane elementu : docs.microsoft.com/en-us/visualstudio/msbuild/ ...
kenjiuno
72

Myślę, że problem może tkwić w tym, jak tworzysz ItemGroup i wywołujesz zadanie kopiowania. Sprawdź, czy to ma sens:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
    <PropertyGroup>
        <YourDestinationDirectory>..\SomeDestinationDirectory</YourDestinationDirectory>
        <YourSourceDirectory>..\SomeSourceDirectory</YourSourceDirectory>
    </PropertyGroup>

    <Target Name="BeforeBuild">
        <CreateItem Include="$(YourSourceDirectory)\**\*.*">
            <Output TaskParameter="Include" ItemName="YourFilesToCopy" />
        </CreateItem>

        <Copy SourceFiles="@(YourFilesToCopy)"
                DestinationFiles="@(YourFilesToCopy->'$(YourDestinationDirectory)\%(RecursiveDir)%(Filename)%(Extension)')" />
    </Target>
</Project>
brock.holum
źródło
CreateItemzadanie jest przestarzałe. regex ma alternatywę. msdn.microsoft.com/en-us/library/s2y3e43x.aspx
Ray Cheng
35

Jestem trochę nowy w MSBuild, ale uważam, że zadanie EXEC jest przydatne w takich sytuacjach. Spotkałem się z tym samym wyzwaniem w moim projekcie i to zadziałało i było znacznie prostsze. Niech ktoś mi powie, jeśli to nie jest dobra praktyka.

<Target Name="CopyToDeployFolder" DependsOnTargets="CompileWebSite">
    <Exec Command="xcopy.exe  $(OutputDirectory) $(DeploymentDirectory) /e" WorkingDirectory="C:\Windows\" />
</Target>
Denzil Brown
źródło
9
Odważę się zadać to pytanie w drugą stronę. Czy jest jakiś powód, aby kiedykolwiek używać zadania kopiowania MSBuild wypełniającego dziennik?
bernd_k
4
Potencjalnie. Jeśli masz farmę kompilacji (Jenkins, TeamCity itp.), Usługa agenta może działać na innym koncie, które nie ma xcopy w ścieżce. Możesz wypróbować na ścieżce rzeczy takie jak% windir% \ system32, ale nawet to czasami nie działa.
Andrew dh
To rozwiązanie zadziałało dla mnie. Nie musiałem też ustawiać katalogu roboczego.
Aebsubis
FYI, muszę dodać / Y, aby pominąć monit o zastąpienie pliku / folderu. Również jeśli $ (DeploymentDirectory) jest folderem, pozostawienie znaku „\” po ścieżce usunie monit: „miejsce docelowe to folder czy plik?”
Hoàng Long
6
Wiem, że ten problem nie pojawia się często, ale moim głównym powodem, dla którego używam Copyzadania zamiast polecenia, jest kompatybilność. Wcześniej budowałem na Linuksie przy użyciu Mono i oczywiście xcopytam nie działa.
GregRos
13
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
    <PropertyGroup>
        <YourDestinationDirectory>..\SomeDestinationDirectory</YourDestinationDirectory>
        <YourSourceDirectory>..\SomeSourceDirectory</YourSourceDirectory>
    </PropertyGroup>

    <Target Name="BeforeBuild">
        <CreateItem Include="$(YourSourceDirectory)\**\*.*">
            <Output TaskParameter="Include" ItemName="YourFilesToCopy" />
        </CreateItem>

        <Copy SourceFiles="@(YourFilesToCopy)"
                DestinationFiles="$(YourFilesToCopy)\%(RecursiveDir)" />
    </Target>
</Project>

\**\*.*pomóc pobrać pliki z całego folderu. RecursiveDir pomaga umieścić cały plik w odpowiednim folderze ...

amit thakur
źródło
2
Pliki docelowe odnoszą się do 1 pozycji, a pliki źródłowe do 33 pozycji. Muszą mieć taką samą liczbę elementów. Ugh… msbuild może być niesamowity, ale czasami taki słabo udokumentowany kawałek śmieci.
The Muffin Man
CreateItemzadanie jest przestarzałe. regex ma alternatywę. msdn.microsoft.com/en-us/library/s2y3e43x.aspx
Ray Cheng
4

Czy próbowałeś określić konkretny katalog docelowy zamiast

DestinationFolder="@(Viewer->'$(OutputPath)\\Tools')" ? 

Nie jestem biegły w zaawansowanej składni MSBuild, ale

@(Viewer->'$(OutputPath)\\Tools') 

wygląda dla mnie dziwnie. Skrypt wygląda dobrze, więc problem może dotyczyć wartości $(ApplicationDirectory)i$(OutputPath)

EDYTOWAĆ:

Oto post na blogu, który może być przydatny:

Instrukcje: rekurencyjne kopiowanie plików za pomocą zadania

aku
źródło
1
+1 dla linku, który jest bardziej zwięzły niż zaakceptowana odpowiedź od zXen.
bernd_k
3

Oto przykład, który zadziałał:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

   <ItemGroup>
      <MySourceFiles Include="c:\MySourceTree\**\*.*"/>
   </ItemGroup>

   <Target Name="CopyFiles">
      <Copy
        SourceFiles="@(MySourceFiles)"
        DestinationFiles="@(MySourceFiles->'c:\MyDestinationTree\%(RecursiveDir)%(Filename)%(Extension)')"
       />
    </Target>

</Project>

źródło: https://msdn.microsoft.com/en-us/library/3e54c37h.aspx

PBo
źródło
2

To jest zadanie kopiowania, którego użyłem w moim własnym projekcie, działało idealnie dla mnie, pomyślnie kopiując folder z podfolderami do miejsca docelowego:

<ItemGroup >
<MyProjectSource Include="$(OutputRoot)/MySource/**/*.*" />
</ItemGroup>

<Target Name="AfterCopy" AfterTargets="WebPublish">
<Copy SourceFiles="@(MyProjectSource)" 
 OverwriteReadOnlyFiles="true" DestinationFolder="$(PublishFolder)api/% (RecursiveDir)"/>

W moim przypadku skopiowałem folder publikacji projektu do innego folderu docelowego, myślę, że jest to podobne do twojego przypadku.

nzrytmn
źródło
0

Najlepszym sposobem rekurencyjnego kopiowania plików z jednego katalogu do innego przy użyciu programu MSBuild jest użycie zadania kopiowania z parametrami SourceFiles i DestinationFiles. Na przykład - Aby skopiować wszystkie pliki z katalogu kompilacji do katalogu kopii zapasowej, będzie

<PropertyGroup>
<BuildDirectory Condition="'$(BuildDirectory)' == ''">Build</BuildDirectory>
<BackupDirectory Condition="'$(BackupDiretory)' == ''">Backup</BackupDirectory>
</PropertyGroup>

<ItemGroup>
<AllFiles Include="$(MSBuildProjectDirectory)/$(BuildDirectory)/**/*.*" />
</ItemGroup>

<Target Name="Backup">
<Exec Command="if not exist $(BackupDirectory) md $(BackupDirectory)" />
<Copy SourceFiles="@(AllFiles)" DestinationFiles="@(AllFiles-> 
'$(MSBuildProjectDirectory)/$(BackupDirectory)/%(RecursiveDir)/%(Filename)% 
(Extension)')" />
</Target>

Teraz w powyższym poleceniu Kopiuj przechodzą wszystkie katalogi źródłowe, a pliki są kopiowane do katalogu docelowego.

shivinder thakur
źródło
0

Jeśli pracujesz z typowym łańcuchem narzędzi C ++, innym sposobem jest dodanie plików do standardowej listy CopyFileToFolders

<ItemGroup>
  <CopyFileToFolders Include="materials\**\*">
    <DestinationFolders>$(MainOutputDirectory)\Resources\materials\%(RecursiveDir)</DestinationFolders>
  </CopyFileToFolders>
</ItemGroup>

Poza tym, że jest to proste, jest to przyjemny sposób, ponieważ zadanie CopyFilesToFolders wygeneruje odpowiednie dane wejściowe, wyjściowe, a nawet pliki TLog, dzięki czemu operacje kopiowania będą wykonywane tylko wtedy, gdy jeden z plików wejściowych ulegnie zmianie lub brakuje jednego z plików wyjściowych. Z TLog, Visual Studio również poprawnie rozpozna projekt jako „aktualny” lub nie (używa do tego oddzielnego mechanizmu U2DCheck).

Siergiej Ozerow
źródło