Copie todos os arquivos e pastas usando msbuild

93

Gostaria de saber se alguém poderia me ajudar com alguns scripts msbuild que estou tentando escrever. O que eu gostaria de fazer é copiar todos os arquivos e subpastas de uma pasta para outra pasta usando msbuild.

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

O que preciso fazer é copiar todos os arquivos e subpastas da pasta de ferramentas para a pasta de depuração do aplicativo. Este é o código que tenho até agora.

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

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

O script de construção é executado, mas não copia nenhum dos arquivos ou pastas.

obrigado

Nathan W
fonte

Respostas:

133

Eu estava procurando ajuda nisso também. Demorei um pouco, mas aqui está o que fiz que funcionou muito bem.

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

Isso copiava recursivamente o conteúdo da pasta nomeada antlrcspara o $(TargetDir).

Rodolfo Neuber
fonte
4
Sim, esta é a melhor resposta. O mesmo recomendado aqui no blog do msdn
Karsten
2
Funciona bem - obrigado! Eu me pergunto por que outras respostas mais complicadas têm mais votos positivos ?!
Ivan,
17
O truque parece ser que adicionar %(RecursiveDir)à pasta de destino recriará a estrutura do diretório. Caso contrário, a saída é plana. Esta é a melhor resposta.
JB. Com a Monica.
1
Precisa ser colocado no final do arquivo .fsproj, ou não leva.
Henrik
O momento chave aqui que funcionou para mim é mover a declaração da variável ( <ANTLR Include = ".. \ Data \ antlrcs ***. *" /> ) Para o destino AfterBuild. No meu caso, foi declarado no escopo externo e não funcionou.
Envio em
71

Acho que o problema pode estar em como você está criando seu ItemGroup e chamando a tarefa de cópia. Veja se isso faz sentido:

<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
fonte
CreateItemtarefa está obsoleta. regex tem a alternativa. msdn.microsoft.com/en-us/library/s2y3e43x.aspx
Ray Cheng,
35

Eu sou meio novo no MSBuild, mas acho a Tarefa EXEC útil para situações como essas. Encontrei o mesmo desafio em meu projeto e funcionou para mim e foi muito mais simples. Alguém me avise se não for uma boa prática.

<Target Name="CopyToDeployFolder" DependsOnTargets="CompileWebSite">
    <Exec Command="xcopy.exe  $(OutputDirectory) $(DeploymentDirectory) /e" WorkingDirectory="C:\Windows\" />
</Target>
Denzil Brown
fonte
9
Atrevo-me a fazer a pergunta ao contrário. Existe alguma razão para usar a tarefa de cópia msbuild de preenchimento de log?
bernd_k
4
Potencialmente. Se você tiver um build farm (Jenkins, TeamCity etc), o serviço do agente pode ser executado em uma conta diferente que não tem xcopy no caminho. Você pode tentar coisas como% windir% \ system32 no caminho, mas nem isso funciona algumas vezes.
Andrew dh
Essa é a solução que funcionou para mim. Também não precisei definir o WorkingDirectory.
Aebsubis
Para sua informação, preciso adicionar / Y para suprimir o prompt de substituição de arquivo / pasta. Além disso, se $ (DeploymentDirectory) for uma pasta, deixar um "\" após o caminho removerá o prompt: "o destino é a pasta ou o arquivo?"
Hoàng Long,
6
Sei que esse problema não surge com frequência, mas meu principal motivo para usar a Copytarefa em vez de um comando é a compatibilidade. Eu construí no Linux usando Mono antes e obviamente xcopynão funciona lá.
GregRos
12
<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>

\**\*.*ajuda a obter arquivos de todas as pastas. RecursiveDir ajuda para colocar todos os arquivos na respectiva pasta ...

amit thakur
fonte
2
Os arquivos de destino se referem a 1 item e os arquivos de origem se referem a 33 itens. Eles devem ter o mesmo número de itens. Ugh ... msbuild pode ser incrível, mas às vezes é um lixo tão mal documentado.
The Muffin Man
CreateItemtarefa está obsoleta. regex tem a alternativa. msdn.microsoft.com/en-us/library/s2y3e43x.aspx
Ray Cheng,
4

Você tentou especificar o diretório de destino concreto em vez de

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

Não sou muito proficiente com a sintaxe MSBuild avançada, mas

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

parece estranho para mim. O script parece bom, então o problema pode estar nos valores de$(ApplicationDirectory) e$(OutputPath)

EDITAR:

Aqui está uma postagem do blog que pode ser útil:

Como: copiar arquivos recursivamente usando a tarefa

aku
fonte
1
+1 para o link, que é mais conciso do que a resposta aceita do zXen.
bernd_k
3

Este é o exemplo que funcionou:

<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>

fonte: https://msdn.microsoft.com/en-us/library/3e54c37h.aspx

PBo
fonte
2

Esta é uma tarefa de cópia que usei em meu próprio projeto, funcionou perfeitamente para mim que copia a pasta com subpastas para o destino com sucesso:

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

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

No meu caso, copiei a pasta de publicação de um projeto para outra pasta de destino, acho que é semelhante ao seu caso.

nzrytmn
fonte
1

Pessoalmente, usei CopyFolder, que faz parte da Biblioteca de Tarefas do SDC.

http://sdctasks.codeplex.com/

João
fonte
0

A melhor maneira de copiar arquivos recursivamente de um diretório para outro usando o MSBuild é usando a tarefa Copiar com SourceFiles e DestinationFiles como parâmetros. Por exemplo - para copiar todos os arquivos do diretório de construção para o diretório de backup será

<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>

Agora, no comando Copiar acima, todos os diretórios de origem são percorridos e os arquivos são copiados para o diretório de destino.

shivinder thakur
fonte
0

Se você estiver trabalhando com um conjunto de ferramentas C ++ típico, outra maneira é adicionar seus arquivos à lista CopyFileToFolders padrão

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

Além de ser simples, este é um bom caminho a seguir porque a tarefa CopyFilesToFolders irá gerar entradas, saídas e até arquivos TLog apropriados, portanto, certificando-se de que as operações de cópia serão executadas apenas quando um dos arquivos de entrada for alterado ou um dos arquivos de saída estiver faltando. Com o TLog, o Visual Studio também reconhecerá corretamente o projeto como "atualizado" ou não (ele usa um mecanismo U2DCheck separado para isso).

Sergei Ozerov
fonte