.NET:copie archivos a un directorio específico después de la compilación

 C Programming >> Programación C >  >> Tags >> .NET
.NET:copie archivos a un directorio específico después de la compilación

La forma más sencilla de copiar archivos después de la compilación en un proyecto .NET es usar la tarea de copia de MSBuild en el archivo .csproj, así:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <Target Name="CopyDLLs" AfterTargets="Build">
    <Message Text="Executing CopyDLLs task" Importance="High" />

    <Copy
      SourceFiles="$(TargetDir)$(ProjectName).dll;$(TargetDir)$(ProjectName).pdb"
      DestinationFolder="C:\Builds$(ProjectName)" />

    <Message Text="Copied build files" Importance="High" />
  </Target>

</Project>
Code language: HTML, XML (xml)

Nota:estoy usando VS2019.

Mi proyecto se llama NotesAPI. Cuando construyo, registra los siguientes mensajes:

1>------ Build started: Project: NotesAPI, Configuration: Debug Any CPU ------
1>NotesAPI -> C:\NotesAPI\bin\Debug\netcoreapp3.1\NotesAPI.dll
1>Executing CopyDLLs task
1>Copied build files
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========Code language: plaintext (plaintext)

Copió los siguientes archivos de compilación en C:\Build\NotesAPI:

  • NotesAPI.dll
  • NotasAPI.pdb

En este artículo, explicaré la sintaxis de la tarea de copia utilizada en el ejemplo anterior. Luego, mostraré cómo colocar una marca de tiempo en el nombre del directorio y, finalmente, mostraré cómo comprimir el directorio copiado.

Desglose de la sintaxis de la tarea de copia

Anteriormente, la forma en que copiaba los archivos de compilación era colocando argumentos de línea de comando en un evento posterior a la compilación. Ahora tenemos la tarea Copiar, que simplifica un poco las cosas una vez que aprende la sintaxis.

Echemos un vistazo a la sintaxis de Copiar tarea escribiéndola desde cero.

Agregue el elemento de destino

Primero, necesitamos un elemento de destino para contener la tarea de copia:

<Target Name="CopyDLLs" AfterTargets="Build">

</Target>
Code language: HTML, XML (xml)

Este objetivo tiene dos propiedades:

  • Nombre:un nombre único para el objetivo. Mi único consejo aquí es asegurarse de que el nombre sea descriptivo.
  • AfterTargets=”Build”:dado que queremos copiar los archivos de compilación, tendremos que hacerlo después de la compilación, por lo tanto, AfterTargets=”Build”.

Los CopyDLL Target se ejecutará después de que se haya construido el proyecto.

Agregar la tarea de copia

Al agregar una tarea de copia, como mínimo, debe especificar qué archivos copiar y dónde copiarlos, así:

<Target Name="CopyDLLs" AfterTargets="Build">

	<Copy
	  SourceFiles="$(TargetDir)$(ProjectName).dll;$(TargetDir)$(ProjectName).pdb"
	  DestinationFolder="C:\Builds$(ProjectName)" />

</Target>

Code language: HTML, XML (xml)

Esta tarea de copia especifica dos propiedades:

  • SourceFiles:uno o más archivos (separados por un punto y coma). También puede utilizar el carácter comodín (*).
  • Carpeta de destino:dónde copiar los archivos.

Ambas propiedades usan macros de MSBuild (en lugar de valores codificados):

  • $(TargetDir):el directorio de salida de compilación. Ejemplo:C:\NotesAPI\bin\Debug\netcoreapp3.1\
  • $(ProjectName):el nombre del archivo del proyecto. Ej.:NotesAPI.

Agregue tareas de mensajes para registrar lo que sucede durante la compilación

Las tareas de mensajes son básicamente como mensajes de registro en el proceso de compilación. Facilitan la resolución de problemas.

A continuación se explica cómo agregar tareas de mensajes al destino contenedor:

<Target Name="CopyDLLs" AfterTargets="Build">
	<Message Text="Executing CopyDLLs task" Importance="High" />

	<Copy
	  SourceFiles="$(TargetDir)$(ProjectName).dll;$(TargetDir)$(ProjectName).pdb"
	  DestinationFolder="C:\Builds$(ProjectName)" />

	<Message Text="Copied build files" Importance="High" />
</Target>
Code language: HTML, XML (xml)

Digamos que hay un problema durante la tarea de copia. La tarea de mensaje registra "Ejecutando la tarea CopyDLLs" justo antes del mensaje de error, lo que nos ayuda a saber de inmediato que ocurrió el problema en CopyDLLs tarea:

1>------ Build started: Project: NotesAPI, Configuration: Debug Any CPU ------
1>NotesAPI -> C:\NotesAPI\bin\Debug\netcoreapp3.1\NotesAPI.dll
1>Executing CopyDLLs task
1>C:\NotesAPI\NotesAPI.csproj(10,5): error MSB3030: Could not copy the file "\NotesAPI.dll" because it was not found.
1>Done building project "NotesAPI.csproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Code language: plaintext (plaintext)

Sello de tiempo del nombre del directorio de destino

Digamos que cada vez que se ejecuta la compilación, desea copiar archivos en un directorio con una marca de tiempo en el nombre.

Así es como se marca la hora del directorio de destino de una tarea de copia:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <Target Name="CopyDLLs" AfterTargets="Build">
    <Message Text="Executing CopyDLLs task" Importance="High" />
    
    <PropertyGroup>
      <CopyToDir>C:\Builds$(ProjectName)_$([System.DateTime]::UtcNow.ToString(yyyy-MM-ddThhmmss))_$(Configuration)</CopyToDir>
    </PropertyGroup>
    
    <Copy
      SourceFiles="$(TargetDir)$(ProjectName).dll;$(TargetDir)$(ProjectName).pdb"
      DestinationFolder="$(CopyToDir)" />

    <Message Text="Copied build files to $(CopyToDir)" Importance="High" />
  </Target>

</Project>
Code language: HTML, XML (xml)

Ejecutar la compilación genera lo siguiente:

1>------ Rebuild All started: Project: NotesAPI, Configuration: Debug Any CPU ------
1>NotesAPI -> C:\NotesAPI\bin\Debug\netcoreapp3.1\NotesAPI.dll
1>Executing CopyDLLs task
1>Copied build files to C:\Builds\NotesAPI_2021-05-20T121046_Debug
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========Code language: plaintext (plaintext)

Creó el directorio C:\Builds\NotesAPI_2021-05-20T121046_Debug.

Analicemos la sintaxis involucrada aquí escribiéndola desde cero.

Agregue el elemento PropertyGroup

Piense en las propiedades como variables en el código. Puede agregar el suyo propio y nombrarlo como desee y luego hacer referencia a él en otros lugares del código.

Cuando agrega su propia propiedad, debe estar contenida dentro de un elemento PropertyGroup. Así que agregue un elemento PropertyGroup y agregue una nueva propiedad llamada CopyToDir :

<Target Name="CopyDLLs" AfterTargets="Build">
	<Message Text="Executing CopyDLLs task" Importance="High" />

	<PropertyGroup>
	  <CopyToDir></CopyToDir>
	</PropertyGroup>

	<Copy
	  SourceFiles="$(TargetDir)$(ProjectName).dll;$(TargetDir)$(ProjectName).pdb"
	  DestinationFolder="C:\Builds$(ProjectName)" />

	<Message Text="Copied build files" Importance="High" />
</Target>
Code language: HTML, XML (xml)

Calcule el nombre del directorio con una marca de tiempo

Ahora tenemos la propiedad y necesitamos proporcionarle un valor. En este caso, queremos especificar un directorio con marca de tiempo.

Así es como:

<PropertyGroup>
  <CopyToDir>C:\Builds$(ProjectName)_$([System.DateTime]::UtcNow.ToString(yyyy-MM-ddThhmmss))_$(Configuration)</CopyToDir>
</PropertyGroup>
Code language: HTML, XML (xml)

Esto parece una cadena muy complicada. Utiliza una combinación de literales de cadena, macros de MSBuild e incluso llama a un método.

Vamos a desglosarlo.

  • Macros de MSBuild:

C:\Builds\$(Nombre del proyecto) _$([Sistema.DateTime]::UtcNow.ToString(yyyy-MM-ddThhmmss))_$(Configuración)

$(ProjectName) se resuelve en el nombre del proyecto. En este caso, el nombre del proyecto es NotesAPI .

$(Configuración) se resuelve en la configuración de compilación. En este caso, hice una compilación de depuración, por lo que se resuelve en Depurar.

  • Llamar a un método:

C:\Builds\$(ProjectName)_$([System.DateTime]::UtcNow.ToString(yyyy-MM-ddThhmmss)) _$(Configuración)

Esto es equivalente a llamar:

System.DateTime.UtcNow.ToString("yyyy-MM-ddThhmmss")
Code language: C# (cs)

Que genera la fecha y hora actual, por ejemplo:2021-05-20T121046 .

Poniendo todo esto junto, el valor de la propiedad se resuelve dinámicamente en:C:\Builds\NotesAPI_2021-05-20T121046_Debug .

Consulte la propiedad en las Tareas de copia y mensaje

Ahora, la parte más importante:usar la propiedad. Para utilizar CopyToDir valor de la propiedad, use $(CopyToDir), así:

<Target Name="CopyDLLs" AfterTargets="Build">
	<Message Text="Executing CopyDLLs task" Importance="High" />

	<PropertyGroup>
	  <CopyToDir>C:\Builds$(ProjectName)_$([System.DateTime]::UtcNow.ToString(yyyy-MM-ddThhmmss))_$(Configuration)</CopyToDir>
	</PropertyGroup>

	<Copy
	  SourceFiles="$(TargetDir)$(ProjectName).dll;$(TargetDir)$(ProjectName).pdb"
	  DestinationFolder="$(CopyToDir)" />

	<Message Text="Copied build files to $(CopyToDir)" Importance="High" />
</Target>
Code language: HTML, XML (xml)

Cuando se ejecutan las tareas, $(CopyToDir) se reemplazará con su valor dinámico (por ejemplo:C:\Builds\NotesAPI_2021-05-20T121046_Debug ).

Comprime el directorio de destino

Digamos que después de copiar los archivos, desea comprimir el directorio de destino. Puede usar la tarea ZipDirectory de esta manera:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <Target Name="CopyDLLs" AfterTargets="Build">
    <Message Text="Executing CopyDLLs task" Importance="High" />

    <PropertyGroup>
      <CopyToDir>C:\Builds$(ProjectName)_$([System.DateTime]::UtcNow.ToString(yyyy-MM-ddThhmmss))_$(Configuration)</CopyToDir>
    </PropertyGroup>

    <Copy
      SourceFiles="$(TargetDir)$(ProjectName).dll;$(TargetDir)$(ProjectName).pdb"
      DestinationFolder="$(CopyToDir)" />

    <Message Text="Copied build files to $(CopyToDir). Now zipping it up." Importance="High" />

    <ZipDirectory SourceDirectory="$(CopyToDir)" DestinationFile="$(CopyToDir).zip" />

    <Message Text="CopyDLLs task completed" Importance="High" />
  </Target>

</Project>
Code language: HTML, XML (xml)

Ejecutar la compilación genera lo siguiente:

1>------ Rebuild All started: Project: NotesAPI, Configuration: Debug Any CPU ------
1>NotesAPI -> C:\NotesAPI\bin\Debug\netcoreapp3.1\NotesAPI.dll
1>Executing CopyDLLs task
1>Copied build files to C:\Builds\NotesAPI_2021-05-21T120836_Debug. Now zipping it up.
1>Zipping directory "C:\Builds\NotesAPI_2021-05-21T120836_Debug" to "C:\Builds\NotesAPI_2021-05-21T120836_Debug.zip".
1>CopyDLLs task completed
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

Code language: plaintext (plaintext)

Nota:La tarea ZipDirectory en sí genera ese mensaje amigable, explicando exactamente qué comprimió y dónde colocó el archivo comprimido.

La sintaxis de la tarea ZipDirectory es relativamente simple:

  • SourceDirectory:Qué comprimir.
  • DestinationFile:dónde colocar el archivo zip.

En ambas propiedades, observe que se refiere a CopyToDir propiedad. La misma propiedad se utilizó en la tarea de copia. Es una buena idea usar su propia propiedad como esta en lugar de codificar valores duplicados.

ZipDirectory falla si hay una nueva línea en el nombre del directorio

Cuando defina sus propias propiedades, mantenga los valores en una sola línea. De lo contrario, ZipDirectory fallará con el siguiente error:

Por ejemplo, se encontraría con este error si definiera CopyToDir propiedad como esta:

<PropertyGroup>
  <CopyToDir>
	C:\Builds$(ProjectName)_$([System.DateTime]::UtcNow.ToString(yyyy-MM-ddThhmmss))_$(Configuration)
  </CopyToDir>
</PropertyGroup>
Code language: HTML, XML (xml)

Observe que el valor definido en la propiedad está en realidad en una nueva línea. Esa nueva línea es parte de la cadena y ZipDirectory no puede manejarla.

En su lugar, siempre ponga el valor de la propiedad en una sola línea, así:

<PropertyGroup>
  <CopyToDir>C:\Builds$(ProjectName)_$([System.DateTime]::UtcNow.ToString(yyyy-MM-ddThhmmss))_$(Configuration)</CopyToDir>
</PropertyGroup>
Code language: HTML, XML (xml)