Determinar la versión de ensamblado durante un evento posterior a la compilación


Digamos que quería crear un archivo de texto estático que se envía con cada versión. Quiero que el archivo se actualice con el número de versión de la versión (como se especifica en AssemblyInfo.cs), pero no quiero tener que hacerlo manualmente.

Esperaba poder usar un evento post-build y alimentar el número de versión a un archivo por lotes como este:

call foo.bat $(AssemblyVersion)

Sin embargo, no puedo encontrar ninguna variable o macro adecuada para usar.

¿Hay alguna manera de lograr esto que me he perdido?

Author: Peter Mortensen, 2010-02-11

9 answers

Si prefiere scripting estos métodos también podrían funcionar para usted:

Si está utilizando el evento post-build, puede usar el filever.herramienta exe para sacarlo del ensamblaje ya construido:

for /F "tokens=4" %%F in ('filever.exe /B /A /D bin\debug\myapp.exe') do (
  set VERSION=%%F
)
echo The version is %VERSION%

Obtener filever.exe de aquí: http://support.microsoft.com/kb/913111

Si está utilizando el evento pre-build, puede sacarlo del AssemblyInfo.archivo cs como sigue:

set ASMINFO=Properties\AssemblyInfo.cs
FINDSTR /C:"[assembly: AssemblyVersion(" %ASMINFO% | sed.exe "s/\[assembly: AssemblyVersion(\"/SET CURRENT_VERSION=/g;s/\")\]//g;s/\.\*//g" >SetCurrVer.cmd
CALL SetCurrVer.cmd
DEL SetCurrVer.cmd
echo Current version is %CURRENT_VERSION%

Esto utiliza la herramienta de línea de comandos de unix sed, que puede descargar de muchos lugares, como aquí: http://unxutils.sourceforge.net / - iirc que uno funciona bien.

 15
Author: Tuinstoelen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-04 18:40:21

Si (1) no desea descargar o crear un ejecutable personalizado que recupere la versión de ensamblaje y (2) no le importa editar el archivo de proyecto de Visual Studio, entonces hay una solución simple que le permite usar una macro que se vea así:

@(Objetivos - > ' %(Version)')

@(VersionNumber)

Para lograr esto, descargue su proyecto. Si el proyecto define en algún lugar una propiedad , córtela del proyecto y guárdela en otro lugar temporalmente (bloc de notas?). Luego, al final del proyecto, justo antes de la etiqueta final, coloque esto:

<Target Name="PostBuildMacros">
  <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
    <Output TaskParameter="Assemblies" ItemName="Targets" />
  </GetAssemblyIdentity>
  <ItemGroup>
    <VersionNumber Include="@(Targets->'%(Version)')"/>
  </ItemGroup>
</Target>
<PropertyGroup>
  <PostBuildEventDependsOn>
    $(PostBuildEventDependsOn);
    PostBuildMacros;
  </PostBuildEventDependsOn>    
  <PostBuildEvent>echo HELLO, THE ASSEMBLY VERSION IS: @(VersionNumber)</PostBuildEvent>
</PropertyGroup>

Este fragmento tiene un ejemplo ya en él. No se preocupe, puede restablecerlo a su evento real posterior a la compilación después de haber recargado el proyecto.

Ahora como se prometió, la versión ensamblada está disponible para su evento de compilación post con esta macro:

@(VersionNumber)

Hecho!

 77
Author: Brent Arias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-01 13:35:37

Como solución alternativa he escrito una aplicación de consola administrada que toma el destino como parámetro y devuelve el número de versión.

Todavía estoy interesado en escuchar una solución más simple, pero estoy publicando esto en caso de que alguien más lo encuentre útil.

using System;
using System.IO;
using System.Diagnostics;
using System.Reflection;

namespace Version
{
    class GetVersion
    {
        static void Main(string[] args)
        {
            if (args.Length == 0 || args.Length > 1) { ShowUsage(); return; }

            string target = args[0];

            string path = Path.IsPathRooted(target) 
                                ? target 
                                : Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + Path.DirectorySeparatorChar + target;

            Console.Write( Assembly.LoadFile(path).GetName().Version.ToString(2) );
        }

        static void ShowUsage()
        {
            Console.WriteLine("Usage: version.exe <target>");
        }
    }
}
 12
Author: Winston Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-16 17:42:45

Esta respuesta es una modificación menor de la respuesta de Brent Arias. Su PostBuildMacro funcionó bastante bien para mí hasta una actualización de la versión de Nuget.exe.

En las versiones recientes, Nuget recorta partes no significativas del número de versión del paquete para obtener una versión semántica como "1.2.3". Por ejemplo, la versión ensamblada "1.2.3.0" está formateada por Nuget.exe "1.2.3". Y "1.2.3.1 "tiene el formato" 1.2.3.1 " como se esperaba.

Ya que necesito inferir el nombre de archivo exacto del paquete generado por Nuget.exe, ahora uso esta macro adaptada (probada en VS2015):

<Target Name="PostBuildMacros">
  <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
    <Output TaskParameter="Assemblies" ItemName="Targets" />
  </GetAssemblyIdentity>
  <ItemGroup>
    <VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" />
  </ItemGroup>
</Target>
<PropertyGroup>
  <PostBuildEventDependsOn>
    $(PostBuildEventDependsOn);
    PostBuildMacros;
  </PostBuildEventDependsOn>    
  <PostBuildEvent>echo HELLO, THE ASSEMBLY VERSION IS: @(VersionNumber)</PostBuildEvent>
</PropertyGroup>

ACTUALIZACIÓN 2017-05-24: Corregí la expresión regular de esta manera: "1.2.0.0" se traducirá a "1.2.0" y no a "1.2" como se codificó anteriormente.


Y para responder a un comentario de Ehryk Apr, puede adaptar la expresión regular para mantener solo una parte del número de versión. Como ejemplo para mantener " Mayor.Menor", sustitúyase:

<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" />

Por

<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^([^\.]+)\.([^\.]+)(.*)$&quot;, &quot;$1.$2&quot;))" />
 6
Author: Eric Boumendil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-24 16:22:57

Creo que lo mejor que puede hacer es mirar MSBuild y MSBuild Extension Pack debería poder editar su archivo de solución para que ocurra un evento de compilación posterior y escriba en su archivo de prueba.

Si esto es demasiado complicado, simplemente podría crear un pequeño programa que inspeccione todos los ensamblados en su directorio de salida y ejecutarlo en la compilación posterior, podría pasar en el directorio de salida utilizando el nombre de la variable... por ejemplo, en la compilación post evento...

AssemblyInspector.exe " Targ (TargetPath)"

class Program
{
    static void Main(string[] args)
    {
        var assemblyFilename = args.FirstOrDefault();
        if(assemblyFilename != null && File.Exists(assemblyFilename))
        {
            try
            {
                var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyFilename);
                var name = assembly.GetName();

                using(var file = File.AppendText("C:\\AssemblyInfo.txt"))
                {
                    file.WriteLine("{0} - {1}", name.FullName, name.Version);
                }
            }
            catch (Exception ex)
            {
                throw;
            }
        }
    }
}

También puede pasar la ubicación del archivo de texto...

 1
Author: Rohan West,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-11 10:39:12

He comenzado a agregar un proyecto separado que se compila al final y agregar un evento post build a ese proyecto que se ejecuta por sí mismo. Luego solo realizo mis pasos de construcción de post programáticamente allí.

Hace Que sea mucho más fácil hacer cosas como esta. A continuación, puede inspeccionar los atributos de ensamblaje de cualquier ensamblaje que desee. Hasta ahora está funcionando muy bien.

 1
Author: Joshua Evensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-08 10:42:52

De eso entiendo...

Necesitas un generador para los eventos post build.

1. Paso: Escribir un Generador

/*
* Author: Amen RA
* # Timestamp: 2013.01.24_02:08:03-UTC-ANKH
* Licence: General Public License
*/
using System;
using System.IO;

namespace AppCast
{
    class Program
    {
        public static void Main(string[] args)
        {
            // We are using two parameters.

            // The first one is the path of a build exe, i.e.: C:\pathto\nin\release\myapp.exe
            string exePath = args[0];

            // The second one is for a file we are going to generate with that information
            string castPath = args[1];

            // Now we use the methods below
            WriteAppCastFile(castPath, VersionInfo(exePath));
        }


        public static string VersionInfo(string filePath)
        {
            System.Diagnostics.FileVersionInfo myFileVersionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(filePath);
            return myFileVersionInfo.FileVersion;
        }


        public static void WriteAppCastFile(string castPath, string exeVersion)
        {
            TextWriter tw = new StreamWriter(castPath);
            tw.WriteLine(@"<?xml version=""1.0"" encoding=""utf-8""?>");
            tw.WriteLine(@"<item>");
            tw.WriteLine(@"<title>MyApp - New version! Release " + exeVersion + " is available.</title>");
            tw.WriteLine(@"<version>" + exeVersion + "</version>");
            tw.WriteLine(@"<url>http://www.example.com/pathto/updates/MyApp.exe</url>");
            tw.WriteLine(@"<changelog>http://www.example.com/pathto/updates/MyApp_release_notes.html</changelog>");
            tw.WriteLine(@"</item>");
            tw.Close();
        }
    }
}

2. Paso: Usándolo como un comando post build en nuestro IDE

Después de que la aplicación se esté ejecutando satisfactoriamente para usted:

En su IDE de desarrollo, use la siguiente línea de comandos para los eventos post build.

C:\Projects\pathto\bin\Release\AppCast.exe "C:\Projects\pathto\bin\Release\MyApp.exe" "c:\pathto\www.example.com\root\pathto\updates\AppCast.xml"
 1
Author: Amen Ra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-13 21:06:17

Si tiene un proyecto de biblioteca, puede intentar usar la utilidad WMIC (disponible en windows). He aquí un ejemplo. Lo bueno es que no necesita usar ninguna herramienta externa.

SET pathFile=$(TargetPath.Replace("\", "\\"))

FOR /F "delims== tokens=2" %%x IN ('WMIC DATAFILE WHERE "name='%pathFile%'" get  Version /format:Textvaluelist')  DO (SET dllVersion=%%x)
echo Found $(ProjectName) version %dllVersion%
 1
Author: DolceVita,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-07 09:40:58

Necesitaba exactamente esto para poner automáticamente el número en el archivo readme en la carpeta de salida. Al final, como demostró Winston Smith, una pequeña herramienta externa es una muy buena solución para eso, y tiene la ventaja de que puedes formatearla como quieras.

Esta aplicación envía la versión formateada a la consola. Lo utilicé en mis eventos post-build para construir el archivo readme llamándolo con >> para redirigir su salida al archivo readme.

public class GetVerNum
{
    static void Main(String[] args)
    {
        if (args.Length == 0)
            return;
        try
        {
            FileVersionInfo ver = FileVersionInfo.GetVersionInfo(args[0]);
            String version = "v" + ver.FileMajorPart.ToString() + "." + ver.FileMinorPart;
            if (ver.FileBuildPart > 0 || ver.FilePrivatePart > 0)
                version += "." + ver.FileBuildPart;
            if (ver.FilePrivatePart > 0)
                version += "." + ver.FilePrivatePart;
            Console.Write(version);
        }
        catch { }
    }
}

Mi post-build eventos:

<nul set /p dummyset=My Application > "$(ProjectDir)\Readme\readme-header.txt"
"$(ProjectDir)\Readme\GetVersionNumber.exe" "$(TargetPath)" >>"$(ProjectDir)\Readme\readme-header.txt"
echo  by Nyerguds>>"$(ProjectDir)\Readme\readme-header.txt"
echo Build date: %date% %time% >> "$(ProjectDir)\Readme\readme-header.txt"
echo.>>"$(ProjectDir)\Readme\readme-header.txt"
copy /b "$(ProjectDir)\Readme\readme-header.txt" + "$(ProjectDir)\Readme\readme-body.txt" "$(TargetDir)\$(ProjectName).txt"

Puse todo el readme generando cosas relacionadas en la carpeta \Readme\ de mi proyecto; la aplicación que contiene el código anterior, y el "readme-body.txt " que contiene el material readme real.

  • Primera línea: cree el "readme-header.txt " archivo en la carpeta\ Readme \ de mi proyecto, y poner el nombre del programa dentro de él. (El <nul set /p dummyset= es un truco que encontré aquí: Windows batch: echo sin nueva línea ). También puede almacenar esta cadena en otro archivo de texto y simplemente copie eso a " readme-header.txt " en su lugar.
  • Segunda línea: ejecute la aplicación de recuperación de número de versión con el archivo exe recién generado como parámetro y agregue su salida al archivo de encabezado.
  • Tercera línea: agregue cualquier otra cosa (en este caso, créditos) al archivo de encabezado. Esto también añade un salto de línea al final.

Estos tres juntos le dan un "readme-header.txt "archivo con" Mi aplicación v1.2. 3 por Nyerguds", seguido de un salto de línea, en él. Luego añado la fecha de construcción y otra línea abierta, y copiar el archivo de encabezado y el archivo readme cuerpo juntos a un archivo en la carpeta de compilación final. Tenga en cuenta que utilizo específicamente copia binaria, de lo contrario da resultados extraños. Usted tiene que asegurarse de que el archivo de cuerpo no contiene ninguna marca de orden de bytes UTF-8 al principio, o se obtiene bytes extraños en su archivo final.

 0
Author: Nyerguds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:18