Copie todos los archivos y carpetas usando msbuild

93

Me pregunto si alguien podría ayudarme con algunos scripts de msbuild que estoy tratando de escribir. Lo que me gustaría hacer es copiar todos los archivos y subcarpetas de una carpeta a otra usando msbuild.

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

Lo que necesito poder hacer es copiar todos los archivos y subcarpetas de la carpeta de herramientas a la carpeta de depuración de la aplicación. Este es el código que tengo hasta ahora.

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

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

El script de compilación se ejecuta pero no copia ninguno de los archivos o carpetas.

Gracias

Nathan W
fuente

Respuestas:

133

También estaba buscando ayuda en esto. Me tomó un tiempo, pero esto es lo que hice que funcionó muy bien.

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

Esto copió de forma recursiva el contenido de la carpeta nombrada antlrcsen $(TargetDir).

Rodolfo Neuber
fuente
4
Si, esta es la mejor respuesta. Lo mismo que se recomienda aquí en el blog de msdn
Karsten
2
Funciona bien, ¡gracias! Me pregunto por qué otras respuestas más complicadas tienen más votos a favor.
Ivan
17
El truco parece ser que agregar %(RecursiveDir)a la carpeta de destino recreará la estructura del directorio. De lo contrario, la salida es plana. Esta es la mejor respuesta.
JB. Con Monica.
1
Debe colocarse al final del archivo .fsproj, o no se necesita.
Henrik
El momento clave aquí que funcionó para mí es mover la declaración de variable ( <ANTLR Include = ".. \ Data \ antlrcs ***. *" /> ) En el objetivo de AfterBuild. En mi caso, se declaró en el ámbito externo y no funcionó.
Envío el
71

Creo que el problema podría estar en cómo está creando su ItemGroup y llamando a la tarea Copiar. Vea si esto tiene 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
fuente
CreateItemla tarea está en desuso. regex tiene la alternativa. msdn.microsoft.com/en-us/library/s2y3e43x.aspx
Ray Cheng
35

Soy un poco nuevo en MSBuild, pero encuentro que la tarea EXEC es útil para situaciones como estas. Me encontré con el mismo desafío en mi proyecto y esto funcionó para mí y fue mucho más simple. Alguien, por favor, avíseme si no es una buena práctica.

<Target Name="CopyToDeployFolder" DependsOnTargets="CompileWebSite">
    <Exec Command="xcopy.exe  $(OutputDirectory) $(DeploymentDirectory) /e" WorkingDirectory="C:\Windows\" />
</Target>
Denzil Brown
fuente
9
Me atrevo a hacer la pregunta al revés. ¿Hay alguna razón para usar la tarea de copia de msbuild de llenado de registros?
bernd_k
4
Potencialmente. Si tiene una granja de compilación (Jenkins, TeamCity, etc.), el servicio del agente puede ejecutarse con una cuenta diferente que no tenga xcopy en la ruta. Puede probar cosas como% windir% \ system32 en la ruta, pero incluso esto no funciona algunas veces.
Andrew dh
Esa es la solución que funcionó para mí. Además, no necesitaba configurar WorkingDirectory.
Aebsubis
FYI, necesito agregar / Y para suprimir el mensaje de anulación de archivo / carpeta. Además, si $ (DeploymentDirectory) es una carpeta, dejar un "\" después de la ruta eliminará el mensaje: "¿el destino es una carpeta o un archivo?"
Hoàng Long
6
Sé que este problema no surge con frecuencia, pero mi principal razón para usar la Copytarea en lugar de un comando es la compatibilidad. He construido en Linux usando Mono antes, y obviamente xcopyno funciona allí.
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>

\**\*.*ayuda a obtener archivos de toda la carpeta. RecursiveDir ayuda a poner todo el archivo en la carpeta respectiva ...

amit thakur
fuente
2
Los archivos de destino se refieren a 1 elementos y los archivos de origen se refieren a 33 elementos. Deben tener la misma cantidad de elementos. Ugh ... msbuild puede ser increíble, pero a veces es una basura tan mal documentada.
The Muffin Man
CreateItemla tarea está en desuso. regex tiene la alternativa. msdn.microsoft.com/en-us/library/s2y3e43x.aspx
Ray Cheng
4

¿Intentó especificar un directorio de destino concreto en lugar de

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

No soy muy competente con la sintaxis avanzada de MSBuild, pero

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

me parece extraño. El script se ve bien, por lo que el problema puede estar en los valores de $(ApplicationDirectory)y$(OutputPath)

EDITAR:

Aquí hay una publicación de blog que podría ser útil:

Cómo: copiar archivos de forma recursiva mediante la tarea

aku
fuente
1
+1 para el enlace, que es más conciso que la respuesta aceptada de zXen.
bernd_k
3

Este es el ejemplo que funcionó:

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

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

PBo
fuente
2

Esta es la tarea de copia que utilicé en mi propio proyecto, funcionó perfectamente para mí que copia la carpeta con subcarpetas al destino con éxito:

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

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

En mi caso, copié la carpeta de publicación de un proyecto en otra carpeta de destino, creo que es similar a su caso.

nzrytmn
fuente
0

La mejor manera de copiar archivos de forma recursiva de un directorio a otro usando MSBuild es usando la tarea Copiar con SourceFiles y DestinationFiles como parámetros. Por ejemplo: copiar todos los archivos del directorio de compilación al directorio de respaldo 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>

Ahora, en el comando Copiar anterior, todos los directorios de origen se recorren y los archivos se copian en el directorio de destino.

shivinder thakur
fuente
0

Si está trabajando con una cadena de herramientas típica de C ++, otra forma de hacerlo es agregar sus archivos a la lista estándar CopyFileToFolders

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

Además de ser simple, esta es una buena manera de hacerlo porque la tarea CopyFilesToFolders generará entradas, salidas e incluso archivos TLog adecuados, por lo que se asegurará de que las operaciones de copia se ejecuten solo cuando uno de los archivos de entrada haya cambiado o falta uno de los archivos de salida. Con TLog, Visual Studio también reconocerá correctamente el proyecto como "actualizado" o no (utiliza un mecanismo U2DCheck separado para eso).

Sergei Ozerov
fuente