Allgemein Für Administratoren Für Architekten Für Entwickler Für Projektleiter Für Tester News Produkte Publikationen
X
Sven Hubert

Sven Hubert

Erweitertes Dependency Management mit Team Foundation Server

Sonntag, 07. November 2010

[Read the English version]

Abhängigkeiten gibt es viele in Software-Entwicklungsprojekten. Von besonderem Interesse sind die Build-Abhängigkeiten der Komponenten, die beim Bauen, Ausliefern und Warten des Produktes beachtet und verwaltet werden müssen. Mit Visual Studio Ultimate können Build-Abhängigkeiten einer Assembly oder einer Solution mit Hilfe des Architecture Explorer ausgewertet und dargestellt werden. Nicht wenige größere Projekte müssen mit einer Vielzahl von Solutions und Visual Studio-Projekten umgehen. Dieser Beitrag zeigt Ihnen, wie Sie die Erweiterungschnittstellen von Visual Studio und MSBuild nutzen können, um das Dependency Management zu einem “First-Class Citizen” zu machen.

Update

Wir haben eine Visual Studio Extension – den "AIT Dependency Manager" – veröffentlicht. Damit wird es in Zukunft möglich sein, Abhängigkeiten in Team Explorer und Visual Studio zu verwalten und zu nutzen, ohne selbst Anpassungen an Visual Studio vornehmen zu müssen. Sie finden den kostenfreien AIT Dependency Manager unter http://www.aitgmbh.de/DependencyManager.

Wo ist das Problem?

Doch zurück zur Solution. Eine Solution besteht vereinfacht aus Projekten, die untereinander und nach außen Abhängigkeiten besitzen. Die folgende Abbildung verdeutlicht das.

SolutionTree

Abbildung 1: Solution-Struktur

Bei den äußeren Abhängigkeiten handelt es sich neben Referenzen auf das .NET Framework und Komponenten von Drittanbietern auch um In-House gebaute Komponenten – also Zulieferungen anderer Teams oder Abteilungen. Dies geschieht aus Gründen der Wiederverwendung oder aber auch der besseren Arbeitsteilung wegen. Christian Binder von Microsoft Deutschland arbeitet zusammen mit uns an einem Whitepaper welches die verschiedenen Optionen zur Lösung von internen Abhängigkeiten aufzeigen soll.

Ein Branch-zentrischer Ansatz

Eine ganz konkrete Lösungsmöglichkeiten soll nun im folgenden anhand eines Beispiels erarbeitet werden.

Dabei gehen wir von einem Branch-zentrischen Ansatz aus. Das Projekt ist in verschiedene Teilbereiche (wie z.B. Plattform und Applikation) aufgeteilt. Jeder Teilbereich agiert als Zulieferer für konsumierende Teilbereiche – besitzt also ein eigenes Release-Management und wird binär über Build-Ergebnisse an die Konsumenten übergeben.

Die folgende Grafik zeigt diese Abstraktionsebene:

BranchTree

Abbildung 2: Branch-Struktur im Kontext der Teamprojekte

Mit den Boardmitteln von .NET Framework und TFS kommt man nun ncht mehr aus, wenn Abhängigkeiten zwischen Solutions verschiedener Teamprojekte und Branches komfortabel verwaltet werden sollen. Einige Anpassungen und Erweiterungen sind nötig, um den entstehenden Abhängigkeitsbaum verwalten zu können.

ComponentDefinition.targets

Zum einen müssen die Abhängigkeiten modelliert und irgendwo gespeichert werden. Da die Abhängigkeiten einer Solution beschrieben werden sollen, eignet sich die Solution auch als Ablageort. Dafür definieren wir eine Datei namens ComponentDefinition.targets innerhalb der Solutions Items:

image

Abbildung 3: Die Datei ComponentDefinition.targets als Solution Item

Diese Datei enthält nun 2 wesentlich Beschreibungen.

  • Die Liste der zu bauenden Solutions in ihrer Reihenfolge, wenn Abhänigkeiten als zu bauender Quellcode vorliegen – vor allem bei aufgeteilten Solutions der Fall.
  • Die Abhängigkeiten die entsprechend noch geladen werden müssen – entsprechend ihrem Typ entweder aus der Versionskontrolle, vom Build-Share oder 3rdParty-Share.

Die unterschiedlichen Abhängigkeitstypen bedingen auch verschiedene Identifizierungsarten.

  • Binäre Abhängigkeiten zu Drittanbieterbibliotheken (3rdParty)
    • Werden über einen Pfad auf das entsprechende Netzwerkverzeichnis identifiziert.
    • Alle Dateien müssen ins lokale Build-Ausgabeverzeichnis kopiert werden.
  • Binäre Abhängigkeiten zu eigens erstellten Bibliotheken und Komponenten (BuildResult)
    • Werden über das Teamprojekt und die Buildnummer identifiziert
    • Alle Dateien aus dem Build-Drop müssen ins lokale Build-Ausgabeverzeichnis kopiert werden.
  • Quellcodeabhängigkeiten zu anderen Solutions (Source)
    • Werden über den Pfad in der Versionskontrolle und der Version identifiziert
    • Das Verzeichnis muss lokal im Workspace gemappt und die entsprechende Version aus dem Repository abgerufen werden

Das bedingt dann den folgenden Aufbau der ComponentDefinition.targets-Datei:

image

Listing 1: ComponentDefinition.targets Inhalt

Es handelt sich dabei um eine Datei im MSBuild-Format. Damit lässt sich die Liste der zu bauenden Solutions “nativ” in Form einer sogenannten ItemGroup namens ItemsToBuild (Zeilen 6-9) abbilden. Die Solution Project2.sln ist dabei die eigentliche Solution der betrachteten Komponente. Vorher muss aber noch Project1.sln aus der Komponente Platform.Common gebaut werden (Zeile 7).

Um nun Bauen zu können muss die Komponente Platform.Common aber zunächst einmal da sein. Zudem werden Dlls der Komponente Platform.Compression benötigt  Dafür sorgt die ItemGroup Dependencies (Zeilen 11-25). Platform.Common wird als Source (Zeile 13) in den lokalen Workspace gemappt und in einer bestimmten Version – hier einem Build-Label (Zeile 16) – abgerufen.

Platform.Compression liegt als Build-Ergebnis vor und wird über das Teamprojekt, die Build-Definition und Nummer “geortet” und in das lokale Ausgabeverzeichnis kopiert.

Für die beschriebenen Mapping und Kopieraktionen sorgt dabei ein eigens entwickelter Build-Tasks GetComponents (Zeile 4 in Listing 2). In Zeile 4 im Listing 1 wird diese Logik importiert – sie muss also auf den Entwicklungs- und Build-Rechnern ausgerollt sein.

image

Listing 2: Auszug aus der zentralen Logik

Die ItemsToBuild Liste wird in Listing 2 Zeile 12 verwendet, um direkt MSBuild aufzurufen. Dieses sorgt dafür, dass die Solutions der Reihe nach gebaut werden. Das Skript ist hier vereinfacht. Normalerweise müssten noch entsprechende Parameter wie z.B. zum Ausgabeverzeichnis und der Plattform übergeben werden.

Abbildung 3 zeigte, dass die Datei ComponentDefinition.targets zur Solution hinzugefügt wurde. Doch wo liegt diese im Gesamtkontext Teamprojekt und Branches? Nun, am Ort der Solution unterhalb eines Branches. Nur so kann gewährleistet werden, dass für verschiedene Varianten oder Entwicklungszweige unabhängig die Liste der Abhängigkeiten gepflegt aber dennoch gemergt werden kann. Abbildung 4 zeigt einen Ausschnitt aus der Versionskontrolle des Beispiels.

image

Abbildung 4: Ort der ComponentDefinition.targets in der Versionskontrolle

Der Arbeitsablauf für den Anwender gestaltet sich dann wie folgt:

  1. Workspace im TFS mappen (auf die Komponente im Unterverzeichnis eines Branches – hier: Platform.Base unterhalb von Branch Team Stuttgart)
  2. Solution öffnen – hier: Project2.sln
  3. Abhängigkeiten über ComponentDefinition.targets abrufen (starten mit MSBuild)

Das Starten der ComponentDefinition.targets über MSBuild ist zwar von Kommandozeile aus möglich. Geschickter ist aber der Aufruf direkt aus dem Solution Explorer über das Kontextmenü. Dazu gehen Sie wie folgt vor.

Legen Sie zwei neue External Tools im Visual Studio an:

  1. Menü Tools > External Tools
  2. Legen Sie das External Tool “Get Dependencies” an:
    image
  3. Legen Sie das External Tool “Build All” an – beachten Sie die Arguments-Angaben:
    image
  4. Um die Tools aus dem Kontextmenü heraus aufzurufen, gehen Sie in das Menü Tools > Customize… und dort in den Reiter Commands
    Dort selektieren Sie den Radio Button “Context Menus” und wählen aus der Liste “Project and Solution Context Menu | Item” aus
    Nun fügen Sie die Kommandos “External Command X” und “External Command Y” hinzu, wobei X und Y den Indizes der angelegten Tools in der obigen Liste (beginnend bei 1) entspricht.
    image

Das Kontextmenü im Solution Explorer sollte nun folgendes Anzeigen:

image

Abbildung 8: Kontextmenü im Solution Explorer

Somit lassen sich die Abhängigkeiten elegant abrufen und im Anschluss bauen. Die Ausgabe der Tools (also MSBuild) erfolgt dabei komfortabel im Ausgabefenster von Visual Studio.

image

Abbildung 9: Ergebnisse im Ausgabefenster

Damit befinden Sie alle Abhängigkeiten auf dem lokalen Arbeitsplatz. Mühevolles Zusammensuchen der Referenzen ist damit passé.

Zentrale Build-Prozesse

Die lokale Anlage des Arbeitsplatzes ist aber nicht alles, was während des Entwicklungsprozesses benötigt wird. Im zentralen Build müssen ebenfalls Abhängigkeiten vorhanden sein. Mit Team Foundation Build können die oben beschriebenen Arbeitsabläufe auf dem Build-Rechner autmatisiert ablaufen.

Das Mapping auf die Komponente wird im Workspace-Template der Build-Definition angegeben:

image

Abbildung 10: Mappen der Komponente im Build-Workspace

Die ComponentDefinition.targets-Datei wird dabei als zentrales Item to Build in der Build-Definition definiert (siehe Abbildung 11). Mit Hilfe der AIT Build Suite lässt sich zudem nach dem Abrufen der Sourcen das Build-Skript starten, um vorab die Abhängigkeiten abzurufen.

image

Abbildung 11: Definition der Build-Prozessparameter mit der AIT Build Suite

Visualisierung der Abhängigkeiten

Bleibt die Verwaltung der Abhängigkeiten. Die dezentralen Definitionsdateien müssen zentral überwacht werden können. Dazu dient in unserem Fall der Architecture Explorer und DGML – eine Möglichkeit, Graphen zu visualisieren und zu analysieren.

Vorab noch ein Blick auf die eingebauten Möglichkeiten der Tools. Der Architecture Explorer ermöglicht zum Beispiel über den sogenannten Solution View, eine Solution auszuwerten.

image

Abbildung 12: Der Solution View im Architecture Explorer

Die erfragten Informaitonen lassen sich per Drag’n’Drop auf eine DGML-Datei ziehen und werden dann visualisiert wie in Abbildung 13.

image

Abbildung 13: Visualisierung der Abhängigkeiten einer Solution

Eine Information zu Build-Ergebnisabhängigkeiten oder zum Source-Code gibt es nicht.

Informationen aus der Versionskontrolle sind in der Branch-Visualisierung des Team Explorers dargestellt. Allerdings nur die Branch- und Merge-Beziehungen zwischen den Zweigen im Repository und keine Abhängigkeiten.

image

Abbildung 14: Der Branch-Baum im Team Exporer

Diese Lücke gilt es mit einem eignen Tooling zu schließen. Wie bereits angedeutet, eignet sich dafür die erweiterbare Schnittstelle des Architecture Explorers – die Progression API. Der Architecture Explorer bietet ein flexibles Provider-Modell, bei dem lediglich eine Dll mit einer Implementierung des Interface Microsoft.VisualStudio.Progression.IProvider in das Verzeichnis "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Providers” abgelegt werden muss.

Über eigene Abfrage-Typen kann nun ein mächtiges Werkzeug zur Analyse der Abhängigkeiten geschaffen werden (siehe Abbildung 15). Dabei kommuniziert das Visual Studio direkt mit der Versionskontrolle des TFS – lädt zum Beispiel die ComponentDefinition.targets-Dateien direkt in den Speicher, um diese weiter zu analysieren.

image

Abbildung 15: Die eigene Dependency View im Architecture Explorer

Die einzelnen Komponenten lassen sich dann via DGML darstellen. Doch auch die Verbindungen – die Links – bieten über zusätzliche Eigenschaften nützliche Informationen.

image

Abbildung 16: Visualisierte Abhängigkeit mit referenzierter Version

So können nicht nur die Visualisierungsfunktionen von Visual Studio via DGML genutzt werden. Auch die Analysefunktionen des Architecture Explorer zum Beispiel für das Aufsuchen von zirkulären Referenzen im Graphen lassen sich verwenden wie Abbildung 17 abschließend zeigt.

image

Abbildung 17: Analyse auf zirkuläre Abhängigkeiten und die markierten Treffer

Weitere Informationen

Aufzeichnung des TechEd 2010 Vortrags: Extended Dependency Management using TFS 2010

Fazit

Das beschriebene Beispiel gibt einen kleinen Einblick in die Möglichkeiten zur Verwaltung von Abhängigkeiten sozusagen über Zweig- un Baumgrenzen hinweg. Die Erweiterbarkeit von Visual Studio, MSBuild und Team Foundation Server lässt viele weitere Möglichkeiten offen. Die hier dargestellte Lösung kommt bereits in ähnlicher Form bei einigen unserer Kunden zum Einsatz.

Wenn Sie Details und Code-Snippets benötigen sollten, können Sie gerne auf uns zurückkommen. Schicken Sie uns doch eine Nachricht oder rufen Sie uns an.

Über den Autor

Portrait_100px Sven Hubert ist für die AIT als Berater im AIT Team SystemPro Team tätig und Most Valuable Professional (MVP) für Visual Studio ALM. Zu seinen Schwerpunkten gehört neben der Projektleitung das Application-Lifecylce-Management.
Er berät Unternehmen bei der Einführung und Anpassung des Visual Studio Team Foundation Server.
Schreiben Sie ihm an Sven.Hubert (at) aitgmbh.de oder hinterlassen Sie einfach einen Kommentar.

Verwandte Artikel:

Benötigen Sie Unterstützung bei der Software-Entwicklung und Architektur von .NET basierten Lösungen oder bei Einführung und Anpassung von Visual Studio / Microsoft Test Manager / Team Foundation Server?

Wir stehen Ihnen unter info(at)aitgmbh.de gerne zur Verfügung.

Tags: , , ,

Hinterlasse eine Antwort