Laden...

VS2005 Add-In: Incremental Build

Erstellt von norman_timo vor 15 Jahren Letzter Beitrag vor 12 Jahren 14.299 Views
norman_timo Themenstarter:in
4.506 Beiträge seit 2004
vor 15 Jahren
VS2005 Add-In: Incremental Build

Hallo liebe Community,

ich habe ein VS Add-In entwickelt, das ich Euch nicht vorenthalten möchte.

IncrementalBuild: Es ist ein VS2005 Add-In, das bei jedem Release-Build die Revisionsnummer um eins erhöht. Das funktioniert innerhalb einer Solution für jedes Projekt, auch VB.NET und C# gemischt.

Folgende Vorgaben sind zu machen:

  • Das Add-In inklusive der Dll muss in das allgemeine VS-Add-In Verzeichnis kopiert werden (normalerweise: "Eigene Dateien\VisualStudio2005\AddIn")
  • Die Projekte sollten auch eine Revisionsnummer haben, damit das Add-In korrekt arbeitet (also z.B. 1.0.0.24)
  • Es erhöht die Revisionsnummer VOR einem Release-Build um genau eine Revisionsnummer (also z.B. von 1.0.0.0 -> 1.0.0.1)
  • Es erhöht keine Revisionsnummer bei einem Debug-Build

Folgendes müsste noch evaluiert werden:

  • Ich weiß nicht wie es sich bei den Express Editionen verhält (und ob Add-Ins in Express Versionen überhupt unterstützt werden)
  • Ich weiß nicht, ob es in VS2003 und/oder in VS2008 läuft

Persönliche Einrichtung:

  • Ich möchte unbedingt, dass VOR dem kompilieren die Revisionsnummer erhöht wird. Es ist aber denkbar, dass man das auch NACH dem Kompilieren tun möchte, hierfür müsste lediglich das Event "OnBuildBegin" mit dem Event "OnBuildDone" ersetzt werden.

Hier der relevante Quellcode aus der connect.cs:



using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using System.Text.RegularExpressions;

namespace IncrementalBuild
{
    /// <summary>Das Objekt für die Implementierung eines Add-Ins.</summary>
    /// <seealso class='IDTExtensibility2' />
    public class Connect : IDTExtensibility2
    {
        private DTE2 _applicationObject;
        private AddIn _addInInstance;
        private BuildEvents _BuildEvents;

        /// <summary>Implementiert den Konstruktor für das Add-In-Objekt. Platzieren Sie Ihren Initialisierungscode innerhalb dieser Methode.</summary>
        public Connect()
        {
        }

        /// <summary>Implementiert die OnDisconnection-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn das Add-In entladen wird.</summary>
        /// <param term='disconnectMode'>Beschreibt, wie das Add-In entladen wird.</param>
        /// <param term='custom'>Array von spezifischen Parametern für die Hostanwendung.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
        }

        /// <summary>Implementiert die OnAddInsUpdate-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn die Auflistung von Add-Ins geändert wurde.</summary>
        /// <param term='custom'>Array von spezifischen Parametern für die Hostanwendung.</param>
        /// <seealso class='IDTExtensibility2' />		
        public void OnAddInsUpdate(ref Array custom)
        {
        }

        /// <summary>Implementiert die OnStartupComplete-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn der Ladevorgang der Hostanwendung abgeschlossen ist.</summary>
        /// <param term='custom'>Array von spezifischen Parametern für die Hostanwendung.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnStartupComplete(ref Array custom)
        {
        }

        /// <summary>Implementiert die OnBeginShutdown-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn die Hostanwendung entladen wird.</summary>
        /// <param term='custom'>Array von spezifischen Parametern für die Hostanwendung.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnBeginShutdown(ref Array custom)
        {
        }

        /// <summary>Implementiert die OnConnection-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn das Add-In geladen wird.</summary>
        /// <param term='application'>Stammobjekt der Hostanwendung.</param>
        /// <param term='connectMode'>Beschreibt, wie das Add-In geladen wird.</param>
        /// <param term='addInInst'>Objekt, das dieses Add-In darstellt.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            _applicationObject = (DTE2)application;
            _addInInstance = (AddIn)addInInst;
            _BuildEvents = ((DTE2)application).Events.BuildEvents;
            _BuildEvents.OnBuildBegin += new _dispBuildEvents_OnBuildBeginEventHandler(BuildEvents_OnBuildBegin);
        }

        /// <summary>
        /// Behandelt das Ereignis <see cref="E:thumbsup:nBuildBegin"/>.
        /// </summary>
        /// <param name="Scope">Definiert den aktuellen, kontextbezogenen Rahmen.</param>
        /// <param name="Action">Definiert die aktuelle Aktion.</param>
        private void BuildEvents_OnBuildBegin(vsBuildScope Scope, vsBuildAction Action)
        {
            System.Diagnostics.Debug.WriteLine("BuildEvents_OnBuildDone");

            // Untersuchung, ob im Debug-Modus oder im Release Modus
            if (_applicationObject.Solution.SolutionBuild.ActiveConfiguration.Name == "Release")
            {
                // Erhöhe für jedes Projekt in der Solution die Versionsnummer
                foreach (Project p in _applicationObject.Solution.Projects)
                {
                    IncrementVersionRevision(p);
                }
            }
        }

        /// <summary>
        /// Erhöht die Revisionsnummer automatisch, falls ein neuer Build-Vorgang vorgenommen wurde.
        /// </summary>
        /// <param name="project">Das Projekt, dessen Versionsnummer erhöht werden soll.</param>
        private void IncrementVersionRevision(Project project)
        {
            // lokale Variablen
            FileCodeModel fcm = null;
            CodeAttribute2 assemblyVersionAttribute = null;
            CodeAttribute2 fileVersionAttribute = null;

            // Assembly Info Datei herausziehen
            fcm = GetAssemblyInfo(project);

            // Ausnhame generieren, wenn keine AssemblyInfo gefunden wurde
            if (fcm == null)
            {
                System.Diagnostics.Debug.WriteLine("Konnte keine AssemblyInfo Datei zum Projekt '" + project.Name + "' finden!");
                throw new Exception("Konnte keine AssemblyInfo Datei zum Projekt '" + project.Name + "' finden!");
            }

            // Assembly Version Teil herausfiltern
            assemblyVersionAttribute = (CodeAttribute2)fcm.CodeElements.Item("AssemblyVersion");

            // Dateiversion ist nur bei VB.NET Projekten relevant, bei C# Projekten verursacht das eine Laufzeitausnahme
            if (project.FileName.EndsWith("vbproj"))
            {
                fileVersionAttribute = (CodeAttribute2)fcm.CodeElements.Item("AssemblyFileVersion");
            }

            // Regex auf die Versionsnummer
            Regex re = new Regex(@"([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)");

            // Überprüfung, ob Regex eine Versionsnummer findet
            Match M = re.Match(assemblyVersionAttribute.Value);

            // Falls eine Versionsnummer gefunden wurde
            if (M.Success)
            {
                System.Diagnostics.Debug.WriteLine(project.FileName + " + increment revision: " + Convert.ToInt16(M.Groups[4].Value));

                // erhöhe Versionsnummer
                int revision = Convert.ToInt16(M.Groups[4].Value) + 1;
                string newAssemblyVersion = string.Format(@"""{0}.{1}.{2}.{3}"""
                , M.Groups[1].Value, M.Groups[2].Value, M.Groups[3].Value, revision);

                // Versionsnummer (und Dateiversionsnummer) im Projekt ändern
                assemblyVersionAttribute.Value = newAssemblyVersion;
                if (fileVersionAttribute != null)
                {
                    fileVersionAttribute.Value = newAssemblyVersion;
                }
            }
        }

        /// <summary>
        /// Findet die 'AssemblyInfo' Datei aus übergebenem Projekt und liefert die
        /// Datei Codestruktur zurück.
        /// </summary>
        /// <param name="project">Projekt, aus dem die AssemblyInfo ermittelt werden soll.</param>
        /// <returns>Die Datei Codestruktur aus übergebenem Projekt.</returns>
        private FileCodeModel GetAssemblyInfo(Project project)
        {
            // lokale Variablen
            FileCodeModel retVal = null;
            ProjectItem fallBack = null;
            String assemblyName = String.Empty;
            String fallBackName = String.Empty;

            // Projekttypenspezifische Namensgebung festlegen
            if (project.FileName.EndsWith("vbproj"))
            {
                assemblyName = "AssemblyInfo.vb";
                fallBackName = "My Project";
            }
            else
            {
                assemblyName = "AssemblyInfo.cs";
                fallBackName = "Properties";
            }

            // untersuchen, ob AssemblyInfo im "root" zu finden ist
            foreach (ProjectItem pi in project.ProjectItems)
            {
                if (pi.Name == assemblyName)
                {
                    retVal = pi.FileCodeModel;
                    break;
                }
                else if (pi.Name == fallBackName)
                {
                    fallBack = pi;
                }
            }

            // wenn kein Rückgabewert definiert, probiere Fallback
            if ((retVal == null) && (fallBack != null))
            {
                foreach (ProjectItem pi in project.ProjectItems.Item(fallBack.Name).ProjectItems)
                {
                    if (pi.Name == assemblyName)
                    {
                        retVal = pi.FileCodeModel;
                        break;
                    }
                }
            }

            // Rückgabewert liefern
            return retVal;
        }
    }
}


Vielen Dank auch an Xqgene, der mir bei meinem Problem geholfen hat:
[gelöst] VS2005 Add-In selbst erstellen

Im Anhang befindet sich auch die komplette Solution.

VS2005, Add-In, Incremental, Build, inkrementelles, Erstellen

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

norman_timo Themenstarter:in
4.506 Beiträge seit 2004
vor 15 Jahren

Und hier im Anhang befindet sich die Dll und das Add-In ohne Quellcode.

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

F
101 Beiträge seit 2007
vor 15 Jahren

schöne sache 😁 aber ich vermute gerade das du die Falsche Zahl erhöhst.

major, minor, build, revision

major = Version (wie z.B. bei nen Release mit großer Änderung)
minor = die Anzahl der veröffentlichten Version seit der Major Version.
build = Das ist die Zahl die erhöht werden sollte.
revision = Die kommt von Sourcecontrol programmen (Halt die Revision vom dem aktuellen Tag/Trunk/Branch)

Hab die infos mal in der msdn/Forum gelesen... Jahre her

norman_timo Themenstarter:in
4.506 Beiträge seit 2004
vor 15 Jahren

Hallo fadass,

naja, ich schreibe ja direkt, dass ich die Revisionsnummer um eins erhöhe. Ich habe hier für uns das gleich der Build-Nummer gesetzt.

Es ist ja aber kein Problem anhand von meinem Quellcode das nach eigenen Wünschen anzupassen.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

5.742 Beiträge seit 2007
vor 15 Jahren

Hallo norman_timo,

noch einmal danke für dieses kleine, aber feine Addin!!!

Eine Verbesserung musste ich aber dennoch hinzufügen: Automatische Speicherung der geänderten Datei. Andernfalls gibt es Probleme beim Build in Verbindung mit WPF Anwendungen.
Um das umzusetzen, muss man lediglich ein paar Zeilen abändern:


private void IncrementVersionRevision(Project project)
{
   //...
   ProjectItem pi = GetAssemblyInfo(project);
   fcm = pi.FileCodeModel;
   //...
      pi.Save(pi.Name); //Als letzte Anweisung im if-Block "if (M.Success)"
   }
}

private ProjectItem GetAssemblyInfo(Project project)
{
   //...
   ProjectItem retVal = null;
   //...
         retVal = pi;
   //...
            retVal = pi;
   //...
}

888 Beiträge seit 2007
vor 15 Jahren

Hallo norman_timo,

vielen Dank, für das feine AddIn.
Habs grad bei mir zum Laufen gebracht und kann somit bestätigen es läuft unter
VisualStudio-2008-Pro ohne Probleme.

Anmerkungen für VS2008:

1.
Das VS-Add-In Verzeichnis ist default:
"...\Eigene Dateien\Visual Studio 2008\Addins&quot;

2.
In der Datei "IncrementalBuild.AddIn" muss die VS Version auf 9.0 gesetzt werden.

Grüße

888 Beiträge seit 2007
vor 15 Jahren

Und eine kleine Ergänzung hab ich auch noch:

Das Projekt wird nach der Revision-Erhöhung (AssemblyInfo.cs) nicht gepeichert, daher kommt vor dem Schließen von VisualStudio immer eine nervige Bestätigungsmeldung.

Um das zu ändern muss in der Methode: IncrementVersionRevision() als letzte Anweisung in der Bedingung if (M.Success) folgendes hinzugefügt werden:

project.Save(project.FileName);

Grüße

699 Beiträge seit 2007
vor 14 Jahren

Hallo zusammen,

ich hab mir das AddIn auch geladen und angepasst an meine Ideen.
Nur eine Idee bekomme ich im moment nicht umgesetzt:

Kann man auch ein Projekt in der Solution von dem Inkremental Build ausschließen, wenn darin keine änderungen gemacht wurden?
Irgendwo merkt sich das Visual Studio das ja auch.

So das die Versionsnummer nicht erhöht wird.

Grüße Stephan

PS: Ansonsten ein super AddIn 🙂

888 Beiträge seit 2007
vor 12 Jahren

Für's 2010er Studio hab ich es leider nicht mehr hinbekommen.
Hab aber eine, für mich ausreichende, Alternative gefunden:

1.Open the file AssemblyInfo.cs
2.Change [assembly: AssemblyVersion("1.0.0.0")] to [assembly:
AssemblyVersion("1.0.*")]
3.Remove [assembly: AssemblyFileVersion("1.0.0.0")]
4.Rebuild the project and verify that the version is incremented properly by viewing the properties of the newly compiled assembly