54 votes

Incorporation automatique des informations de révision mercurial dans les projets Visual Studio c #

Problème D'Origine

Dans la construction de nos projets, je veux mercurial id de chaque référentiel pour être incorporé dans le produit(s) de ce référentiel (la bibliothèque, de l'application ou de l'application de test).

Je trouve cela rend les choses beaucoup plus facile à déboguer une application en cours d'exécution par les clients 8 fuseaux horaires de là, si vous savez précisément ce qui s'est passé dans la construction de la version de l'application qu'ils utilisent. En tant que tel, chaque projet (application ou de la bibliothèque) dans nos systèmes met en œuvre un moyen de mise à la modification de l'information.

Je trouve aussi qu'il est très utile d'être en mesure de voir si une application a été compilée avec propre (non modifié) les modifications du référentiel. Hg id' utilement ajoute un + à l'id de révision lorsqu'il y a des modifications non validées dans un dépôt, donc cela nous permet de voir facilement si les gens sont en cours d'exécution d'un chiffon propre ou une version modifiée du code.

Ma solution actuelle est détaillée ci-dessous, et remplit les exigences de base, mais il y a un certain nombre de problèmes.

Solution Actuelle

À l'heure actuelle, pour chaque solution Visual Studio, j'ajoute à la suite de "Pré-événement de construction de la ligne de commande" des commandes:

cd $(ProjectDir)
HgID

J'ai aussi ajouter un HgID.fichier bat dans le répertoire de Projet:

@echo off
type HgId.pre > HgId.cs

For /F "delims=" %%a in ('hg id') Do <nul >>HgID.cs set /p =            @"%%a"

echo ;                  >> HgId.cs
echo     }              >> HgId.cs
echo }                  >> HgId.cs

avec un HgId.pré fichier, qui est définie comme:

namespace My.Namespace {
/// <summary> Auto generated Mercurial ID class. </summary>
internal class HgID {
    /// <summary> Mercurial version ID [+ is modified] [Named branch]</summary>
    public const string Version =

Quand j'ai créer mon application, la pré-construction de l'événement est déclenché sur toutes les bibliothèques, la création d'un nouveau HgId.cs fichier (qui n'est pas sous le contrôle de révision) et provoquant la bibliothèque pour être re-compilé avec la nouvelle hg id' chaîne 'Version'.

Problèmes avec la solution actuelle

Le principal problème est que, depuis la HgId.cs est recréé à chaque pré-construction, donc à chaque fois que nous avons besoin de compiler quoi que ce soit, tous les projets de la solution en cours de re-compilé. Puisque nous voulons être en mesure de résoudre facilement dans nos bibliothèques, nous avons l'habitude de garder de nombreuses bibliothèques référencé dans notre application principale solution. Cela peut entraîner un temps de construire qui sont significativement plus longtemps que je le voudrais.

Idéalement, j'aimerais que les bibliothèques de compiler uniquement si le contenu de la HgId.cs fichier ont effectivement changé, plutôt que d'avoir été re-créé avec exactement le même contenu.

Le deuxième problème avec cette méthode c'est que c'est la dépendance sur le comportement spécifique de l'environnement windows. J'ai déjà eu à modifier le fichier de commandes à plusieurs reprises, depuis l'origine a travaillé sous XP, mais pas Vista, la prochaine version travaillé sous Vista, mais pas XP et j'ai enfin réussi à le faire fonctionner avec les deux. Si cela va fonctionner avec Windows 7, cependant, est anyones deviner et à mesure que le temps passe, je vois qu'il est plus probable que les entrepreneurs s'attendent à être en mesure de construire nos applications sur Windows 7 boxen.

Enfin, j'ai un problème esthétique avec cette solution, les fichiers de commandes et bodged ensemble des fichiers de modèle se sentir comme de la mauvaise façon de le faire.

Mes questions

Comment résoudriez-vous/comment allez-vous résoudre le problème, je vais essayer de résoudre?

Ce que de meilleures options là-bas que ce que je suis en train de faire?

Rejeté les Solutions à ces problèmes

Avant j'ai mis en place la solution actuelle, j'ai regardé la Mercuriels mot-Clé de l'extension, car il semblait être la solution la plus évidente. Cependant, plus je l'ai regardé et lu les peuples opinion, d'autant plus que je suis venu à la conclusion que ce n'était pas la bonne chose à faire.

Je me souviens aussi des problèmes de substitution de mot clé m'a causé dans les projets précédents entreprises (juste la pensée de ne jamais avoir à utiliser de Source Sécuritaire à nouveau me remplit d'un sentiment d'effroi *8').

Aussi, je n'ai pas particulièrement envie d'activer Mercurial extensions pour obtenir la génération complète. Je veux la solution pour être autonome, de sorte qu'il n'est pas facile pour que la demande soit accidentellement compilé sans la version embarquée de l'information tout simplement parce qu'une extension n'est pas activée ou le droit helper logiciel n'a pas été installé.

J'ai aussi pensé à écrit ceci dans un meilleur langage de script, celui où je tiens seulement à écrire HgId.cs fichier si le contenu avait réellement changé, mais toutes les options que je pouvais penser, aurait besoin de mes co-travailleurs, les entrepreneurs et éventuellement les clients à avoir à installer de logiciel qu'ils pourraient autrement ne pas vouloir (par exemple cygwin).

Toutes les autres options de gens peuvent penser de serait appréciée.


Mise à jour

Solution partielle

Après avoir joué avec lui pendant un certain temps, j'ai réussi à obtenir le HgId.le fichier bat pour n'écraser la HgId.cs fichier en cas de changement:

@echo off

type HgId.pre > HgId.cst

For /F "delims=" %%a in ('hg id') Do <nul >>HgId.cst set /p =            @"%%a"

echo ;                  >> HgId.cst
echo     }              >> HgId.cst
echo }                  >> HgId.cst

fc HgId.cs HgId.cst >NUL
if %errorlevel%==0 goto :ok
copy HgId.cst HgId.cs
:ok
del HgId.cst

Des problèmes avec cette solution

Même si HgId.cs n'est plus re-créé à chaque fois, Visual Studio insiste encore sur la compilation de tout ce à chaque fois. J'ai essayé de chercher des solutions et essayé de vérifier "Seulement de construire des projets de démarrage et dépendances sur Exécuter" dans Outils|Options|Projets et Solutions|Construire et Exécuter, mais il ne fait aucune différence.

Le deuxième problème reste aussi, et maintenant je n'ai aucun moyen de tester si cela fonctionne avec Vista, depuis que l'entrepreneur n'est plus avec nous.

  • Si quelqu'un peut tester ce fichier de commandes sur un ordinateur Windows 7 et/ou Vista boîte, j'aimerais connaître comment ça s'est passé.

Enfin, mon problème esthétique avec cette solution, est d'autant plus fort qu'il ne l'était avant, depuis le fichier de commandes est plus complexe et il est maintenant plus à aller mal.

Si vous pouvez penser à une meilleure des solutions, j'aimerais entendre parler d'eux.

25voto

Joe Daley Points 9352

J'ai juste sorti un petit open-source tâche MSBuild pour faire exactement ce dont vous avez besoin:

  • Il met à votre Mercurial numéro de révision dans votre .NET version de l'assembly
  • Vous pouvez dire à partir de la version si une assemblée a été compilé avec les modifications non validées
  • À ne pas causer de construit si la révision n'a pas changé
  • Ne dépend pas de script Windows
  • Rien à installer - il suffit d'ajouter une petite DLL à votre solution, et de modifier certains fichiers de votre projet

http://versioning.codeplex.com

16voto

Audie Points 1259

Je pense que j'ai une réponse pour vous. Ce sera un peu impliquées, mais il vous permet de vous éloigner de fichiers batch. Vous pouvez compter sur MSBuild et de Tâches Personnalisés pour le faire pour vous. J'ai utilisé le pack d'extension pour MSBuild (Disponible sur CodePlex) - mais la deuxième tâche que vous avez besoin est quelque chose qu'on pourrait tout aussi bien écrire vous-même.

Avec cette solution, vous pouvez faire un clic droit sur le fichier DLL et voir dans les propriétés du fichier qui Mercurial la Version de la DLL ou EXE) sont venus.

Voici les étapes:

  1. Obtenez le Pack MBBuildExtension OU Écrire Tâche Personnalisée pour remplacer AssemblyInfo.cs
  2. Créer un Personnalisé Construire Tâche dans son propre projet afin d'obtenir des l'Mercurial Id(code ci-dessous).
  3. Modifier les fichiers de projet qui ont besoin de Mercurial Id à utiliser Tâche Personnalisée (code ci-dessous).

Tâche personnalisée pour Obtenir mercurial id: (Cela aurait besoin d'être testé et peut-être mieux généralisée...)

using System;
using System.Diagnostics;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;


namespace BuildTasks
{
    public class GetMercurialVersionNumber : Task
    {
        public override bool Execute()
        {
            bool bSuccess = true;
            try
            {
                GetMercurialVersion();
                Log.LogMessage(MessageImportance.High, "Build's Mercurial Id is {0}", MercurialId);
            }
            catch (Exception ex)
            {
                Log.LogMessage(MessageImportance.High, "Could not retrieve or convert Mercurial Id. {0}\n{1}", ex.Message, ex.StackTrace);
                Log.LogErrorFromException(ex);
                bSuccess = false;
            }
            return bSuccess;
        }

        [Output]
        public string MercurialId { get; set; }

        [Required]
        public string DirectoryPath { get; set; }

        private void GetMercurialVersion()
        {
            Process p = new Process();
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.WorkingDirectory = DirectoryPath;
            p.StartInfo.FileName = "hg";
            p.StartInfo.Arguments = "id";
            p.Start();

            string output = p.StandardOutput.ReadToEnd().Trim();
            Log.LogMessage(MessageImportance.Normal, "Standard Output: " + output);

            string error = p.StandardError.ReadToEnd().Trim();
            Log.LogMessage(MessageImportance.Normal, "Standard Error: " + error);

            p.WaitForExit();

            Log.LogMessage(MessageImportance.Normal, "Retrieving Mercurial Version Number");
            Log.LogMessage(MessageImportance.Normal, output);

            Log.LogMessage(MessageImportance.Normal, "DirectoryPath is {0}", DirectoryPath);
            MercurialId = output;

        }
    }

Et le Fichier-Projet modifié: (Les commentaires peut aider)

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!--this is the import tag for the MSBuild Extension pack. See their documentation for installation instructions.-->
  <Import Project="C:\Program Files (x86)\MSBuild\ExtensionPack\MSBuild.ExtensionPack.tasks" />
  <!--Below is the required UsingTask tag that brings in our custom task.-->
  <UsingTask TaskName="BuildTasks.GetMercurialVersionNumber" 
             AssemblyFile="C:\Users\mpld81\Documents\Visual Studio 2008\Projects\LambaCrashCourseProject\BuildTasks\bin\Debug\BuildTasks.dll" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>9.0.30729</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{D4BA6C24-EA27-474A-8444-4869D33C22A9}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>LibraryUnderHg</RootNamespace>
    <AssemblyName>LibraryUnderHg</AssemblyName>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Xml.Linq">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data.DataSetExtensions">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Class1.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

  <Target Name="Build" DependsOnTargets="BeforeBuild">
    <!--This Item group is a list of configuration files to affect with the change. In this case, just this project's.-->
    <ItemGroup>
      <AssemblyInfoFiles Include="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs" />
    </ItemGroup>
    <!--Need the extension pack to do this. I've put the Mercurial Id in the Product Name Attribute on the Assembly.-->
    <MSBuild.ExtensionPack.Framework.AssemblyInfo AssemblyInfoFiles="@(AssemblyInfoFiles)"
                                                  AssemblyProduct="Hg: $(MercurialId)"
                                                  />
    <!--This is here as an example of messaging you can use to debug while you are setting yours up.-->
    <Message Text="In Default Target, File Path is: @(AssemblyInfoFiles)" Importance="normal" />
  </Target>  

  <Target Name="BeforeBuild">
    <!--This is the custom build task. The Required Property in the task is set using the property name (DirectoryPath)-->
    <BuildTasks.GetMercurialVersionNumber DirectoryPath="$(MSBuildProjectDirectory)">
      <!--This captures the output by reading the task's MercurialId Property and assigning it to a local
          MSBuild Property called MercurialId - this is reference in the Build Target above.-->
      <Output TaskParameter="MercurialId" PropertyName="MercurialId" />
    </BuildTasks.GetMercurialVersionNumber>
  </Target>
  <!--<Target Name="AfterBuild">
  </Target>-->

</Project>

Dernière Remarque: L'accumulation des tâches de projet ne doit être construit qu'une seule fois. N'essayez pas de le construire chaque fois que vous faites le reste de votre solution. Si vous le faites, vous verrez que VS2008 a la dll verrouillé. N'ont pas compris que l'on pas encore sorti, mais je pense que la meilleure chose à faire est de créer la dll que vous le souhaitez, puis de distribuer UNIQUEMENT les dll avec votre code, à s'assurer que la dll est dans une position fixe par rapport à chaque projet, vous avez besoin de l'utiliser. De cette façon, on n'a pas à installer quoi que ce soit.

Bonne chance, et j'espère que cela aide!

Audie

5voto

dthorpe Points 23314

Avez-vous envisagé d'utiliser une ressource de chaîne au lieu d'un langage C# string constant? Ressources de chaîne peuvent être modifiés ou remplacés dans la sortie binaires de post-construction à l'aide d'outils destinés à la localisation.

Vous émettre vos mercurial numéro de version d'un fichier texte qui n'est pas utilisée par le C# construire, puis à l'aide d'un post-opération de construction de remplacer la version de la ressource avec la valeur réelle de l'émise fichier texte. Si vous fort-nom de signer vos assemblées, la chaîne de ressource de remplacement devra arriver avant la signature.

C'est la façon dont nous avons traité de cette question lors de Borland ans pour les produits Windows. Le monde est devenu plus compliqué depuis, mais le principe s'applique toujours.

2voto

kovica Points 753

Je suis mon .hgrc j'ai ceci:

 [extensions]                                                                                                                                                               
hgext.keyword =                                                                                                                                                            
hgext.hgk =                                                                                                                                                                

[keyword]                                                                                                                                                                  
** =                                                                                                                                                                       

[keywordmaps]                                                                                                                                                              
Id = {file|basename},v {node|short} {date|utcdate} {author|user} 
 

Et dans mon fichier source (Java) je fais:

 public static final String _rev = "$Id$";
 

Après validation, $ Id $ est étendu à quelque chose comme:

 public static final String _rev = "$Id: Communication.java,v 96b741d87d07 2010/01/25 10:25:30 mig $";
 

2voto

krystan honour Points 3183

Nous avons résolu ce problème avec un autre système de contrôle de source, de subversion.

Ce que nous faisons est que nous avons un commonassemblyinfo.cs de fichiers et nous fiche la révision svn nombre en que l'utilisation d'un script msbuild.

Nous avons littéralement appel svninfo et gratter la sortie de la révision: la première partie, puis le pousse dans le CommonAssemblyInfo.cs fichier.

Chaque projet dans notre solution a un lien vers le fichier commun et est ensuite compilé, ce qui signifie que les assemblées sont versionnées sur la compilation avec le numéro de révision svn, cela signifie aussi que toutes les bibliothèques dépendantes, nous avons écrit sont également versionnées.

Nous avons obtenu assez facilement à l'aide de cruisecontrol .net et msbuild fichiers.

Je n'ai pas utilisé mercurial mais je crois que vous pourriez faire quelque chose de similaire avec l'id de commande ? mais parce que le format de sortie que vous auriez besoin à l'approche de la façon dont vous l'avez utilisé un peu différemment.

Je voudrais avoir un fichier xml standard qui est lue sur l'application de démarrage et vous pouvais pousser l'information vers qui (fonctionne pour les fichiers resx aussi). Vous pouvez alors lire la valeur de retour dans le code. Un peu dégueulasse, mais il fonctionne.

Ma solution préférée est la façon dont nous le faisons, mais vous ne pouvez pas le faire dans mercurial comme l'ensemble de modifications info n'est pas purement numérique, comme le numéro de révision svn est.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by: