web-dev-qa-db-fra.com

Déterminer la version de l'assembly lors d'un événement post-build

Disons que je voulais créer un fichier texte statique qui est livré avec chaque version. Je souhaite que le fichier soit mis à jour avec le numéro de version de la version (comme spécifié dans AssemblyInfo.cs), mais je ne veux pas avoir à le faire manuellement.

J'espérais pouvoir utiliser un événement post-build et alimenter le numéro de version dans un fichier batch comme celui-ci:

call foo.bat $(AssemblyVersion)

Cependant, je ne trouve aucune variable ou macro appropriée à utiliser.

Existe-t-il un moyen d'y parvenir que j'ai manqué?

55
Winston Smith

Si vous préférez les scripts, ces méthodes peuvent également fonctionner pour vous:

Si vous utilisez l'événement post-build, vous pouvez utiliser l'outil filever.exe pour l'extraire de l'assembly déjà construit:

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

Obtenez filever.exe à partir d'ici: http://support.Microsoft.com/kb/913111

Si vous utilisez l'événement de pré-génération, vous pouvez le supprimer du fichier AssemblyInfo.cs comme suit:

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%

Cela utilise l'outil de ligne de commande unix sed, que vous pouvez télécharger à partir de nombreux endroits, comme ici: http://unxutils.sourceforge.net/ - iirc que l'on fonctionne bien.

16
Tuinstoelen

Si (1) vous ne voulez pas télécharger ou créer un exécutable personnalisé qui récupère la version Assembly et (2) cela ne vous dérange pas de modifier le fichier de projet Visual Studio, alors il existe une solution simple qui vous permet d'utiliser une macro qui ressemble à ceci:

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

@(VersionNumber)

Pour ce faire, déchargez votre projet. Si le projet définit quelque part une propriété <PostBuildEvent>, coupez-la du projet et enregistrez-la ailleurs temporairement (bloc-notes?). Ensuite, à la toute fin du projet, juste avant la balise de fin, placez ceci:

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

Cet extrait contient déjà un exemple <PostBuildEvent>. Pas de soucis, vous pouvez le réinitialiser à votre véritable événement post-build après avoir rechargé le projet.

Maintenant, comme promis, la version Assembly est disponible pour votre événement post-build avec cette macro:

@(VersionNumber)

Terminé!

86
Brent Arias

Comme solution de contournement, j'ai écrit une application de console managée qui prend la cible comme paramètre et renvoie le numéro de version.

Je suis toujours intéressé à entendre une solution plus simple - mais je poste ceci au cas où quelqu'un d'autre le trouverait utile.

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

Cette réponse est une modification mineure de la réponse de Brent Arias. Son PostBuildMacro a très bien fonctionné pour moi jusqu'à une mise à jour de la version de Nuget.exe.

Dans les versions récentes, Nuget coupe des parties non significatives du numéro de version du package afin d'obtenir une version sémantique comme "1.2.3". Par exemple, la version Assembly "1.2.3.0" est formatée par Nuget.exe "1.2.3". Et "1.2.3.1" est formaté "1.2.3.1" comme prévu.

Comme j'ai besoin de déduire le nom de fichier du package exact généré par Nuget.exe, j'utilise maintenant cette macro adaptée (testée dans 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>

MISE À JOUR 2017-05-24: J'ai corrigé l'expression régulière de cette manière: "1.2.0.0" sera traduit en "1.2.0" et non "1.2" comme précédemment codé.


Et pour répondre à un commentaire d'Ehryk Apr, vous pouvez adapter l'expression régulière pour ne conserver qu'une partie du numéro de version. Par exemple, pour conserver "Major.Minor", remplacez:

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

Par

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

De ce que je comprends ...

Vous avez besoin d'un générateur pour les événements post-build.

1. Étape: écrire un générateur

/*
* 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. Étape: l'utiliser comme une commande post-build dans notre IDE

Une fois que l'application s'exécute de manière satisfaisante pour vous:

Dans votre IDE de développement, utilisez la ligne de commande suivante pour les événements de post-génération.

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

Si vous avez un projet de bibliothèque, vous pouvez essayer d'utiliser l'utilitaire WMIC (disponible dans Windows). Voici un exemple. Heureusement, vous n'avez pas besoin d'utiliser d'outils externes.

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
DolceVita

J'ai commencé à ajouter un projet distinct qui se construit en dernier et à ajouter un événement post-construction à ce projet qui s'exécute lui-même. Ensuite, j'effectue simplement mes étapes de création de poste par programme.

Cela rend beaucoup plus facile de faire des choses comme ça. Ensuite, vous pouvez simplement inspecter les attributs d'assembly de l'assembly que vous souhaitez. Jusqu'à présent, cela fonctionne plutôt bien.

1
Joshua Evensen

Je pense que la meilleure chose que vous pouvez faire est de regarder MSBuild et MsBuild Extension Pack vous devriez être en mesure de modifier votre fichier de solution afin qu'un événement post-génération se produise et écrive dans votre fichier de test.

Si c'est trop compliqué, vous pouvez simplement créer un petit programme qui inspecte tous les assemblys dans votre répertoire de sortie et l'exécuter lors de la post-construction, vous pouvez passer dans le répertoire de sortie en utilisant le nom de variable ... par exemple dans l'événement post-construction. ..

AssemblyInspector.exe "$ (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;
            }
        }
    }
}

Vous pouvez également passer à l'emplacement du fichier texte ...

1
Rohan West

J'avais besoin exactement de cela pour mettre automatiquement le numéro dans le fichier Lisez-moi dans le dossier de sortie. En fin de compte, comme l'a montré Winston Smith, un petit outil externe est une très bonne solution pour cela, et il a l'avantage que vous pouvez le formater comme vous le souhaitez.

Cette application affiche la version formatée sur la console. Je l'ai utilisé dans mes événements post-génération pour créer le fichier readme en l'appelant avec >> pour rediriger sa sortie vers le fichier 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 { }
    }
}

Mes événements post-build:

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

Je mets tous les fichiers Lisez-moi générant des éléments connexes dans le dossier\Readme\de mon projet; l'application contenant le code ci-dessus et le fichier "readme-body.txt" contenant les éléments du fichier Lisez-moi.

  • Première ligne: créez le fichier "readme-header.txt" dans le dossier\Readme\de mon projet, et mettez-y le nom du programme. (Le <nul set /p dummyset= est une astuce que j'ai trouvée ici: batch Windows: écho sans nouvelle ligne ). Vous pouvez également stocker cette chaîne dans un autre fichier texte et simplement la copier dans "readme-header.txt" à la place.
  • Deuxième ligne: exécutez l'application de récupération du numéro de version avec le fichier exe fraîchement généré comme paramètre et ajoutez sa sortie au fichier d'en-tête.
  • Troisième ligne: ajoutez tout autre élément (dans ce cas, des crédits) au fichier d'en-tête. Cela ajoute également un saut de ligne à la fin.

Ces trois ensembles vous donnent un fichier "readme-header.txt" avec "My Application v1.2.3 by Nyerguds", suivi d'un saut de ligne. Ensuite, j'ajoute la date de construction et une autre ligne ouverte, et je copie le fichier d'en-tête et le fichier corps du fichier Lisez-moi ensemble dans un fichier dans le dossier de construction final. Notez que j'utilise spécifiquement la copie binaire, sinon cela donne des résultats étranges. Vous devez vous assurer que le fichier corps ne contient pas de marque d'ordre d'octets UTF-8 au début, ou vous obtenez des octets étranges dans votre fichier final.

0
Nyerguds

J'ai cherché la même fonctionnalité et j'ai trouvé la solution sur MSDN. https://social.msdn.Microsoft.com/Forums/vstudio/de-DE/e9485c92-98e7-4874-9310-720957fea677/Assembly-version-in-post-build-event?forum=msbuild

$ (ApplicationVersion) a fait le travail pour moi.

Modifier:

D'accord, je viens de voir que le problème $ (ApplicationVersion) ne vient pas de AssemblyInfo.cs, c'est la PublishVersion définie dans les propriétés du projet. Il fait toujours le travail pour moi d'une manière simple. Alors peut-être que quelqu'un en a besoin aussi.

Une autre solution:

Vous pouvez appeler un script PowerShell sur PostBuild, ici vous pouvez lire la AssemblyVersion directement depuis votre Assembly. J'appelle le script avec TargetDir comme paramètre

Commande PostBuild:

PowerShell -ExecutionPolicy Unrestricted $(ProjectDir)\somescript.ps1 -TargetDir $(TargetDir)

Script PowerShell:

param(
    [string]$TargetDir
)

$Version = (Get-Command ${TargetDir}Example.exe).FileVersionInfo.FileVersion

De cette façon, vous obtiendrez la version de AssemblyInfo.cs

0
Momo