Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Bei der Entwicklung von Plugins für CAD-Anwendungen (in meinem Fall sind das AutoCAD, Revit und Renga) stellt sich mit der Zeit ein Problem – neue Programmversionen erscheinen, ihre APIs ändern sich, und es müssen neue Plugin-Versionen erstellt werden.

Wenn Sie nur ein Plugin haben oder noch ein Anfänger sind, können Sie einfach eine Kopie des Projekts anfertigen, die notwendigen Stellen anpassen und eine neue Version des Plugins erstellen. Dies führt allerdings zu einem deutlichen Anstieg des Arbeitsaufwands bei späteren Änderungen am Code.

Mit zunehmender Erfahrung und Wissen werden Sie verschiedene Möglichkeiten zur Automatisierung dieses Prozesses finden. Ich habe diesen Weg zurückgelegt und möchte Ihnen erzählen, zu welchem Ergebnis ich gekommen bin und wie bequem das ist.

Beginnen wir mit einer Methode, die offensichtlich ist und die ich lange Zeit verwendet habe.

Links zu den Projektdateien

Um alles einfach, anschaulich und verständlich zu halten, werde ich alles an einem abstrakten Beispiel der Plugin-Entwicklung beschreiben.

Lassen Sie uns Visual Studio öffnen (ich benutze die Community-Version 2019. Und ja – auf Deutsch) und eine neue Lösung erstellen. Wir nennen sie MySuperPluginForRevit

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Wir werden ein Plugin für Revit für die Versionen 2015-2020 entwickeln. Daher erstelle ich ein neues Projekt im Lösung (Bibliothek der Klassen .NET Framework) und nenne es MySuperPluginForRevit_2015

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Wir müssen Verweise auf die Revit API hinzufügen. Natürlich könnten wir Verweise auf lokale Dateien hinzufügen (dazu müssten wir alle erforderlichen SDKs oder alle Versionen von Revit installieren), aber wir werden gleich den richtigen Weg einschlagen und das NuGet-Paket einbinden. Es gibt eine Vielzahl von Paketen, aber ich werde meine eigenen verwenden.

Nach dem Hinzufügen des Pakets klicken wir mit der rechten Maustaste auf den Punkt „Links“ und wählen im Menü den Punkt „packages.config zu PackageReference verschieben…»

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Falls an dieser Stelle Panik ausbricht, weil im Eigenschaftenfenster des Pakets der wichtige Punkt „Lokal kopieren“ nicht auf den Wert gesetzt ist, den wir unbedingt benötigen false, dann ist es nicht nötig in Panik zu geraten – gehen Sie in den Projektordner, öffnen Sie die Datei mit der Endung .csproj in einem Ihnen angenehmen Editor (ich verwende Notepad++) und suchen Sie dort den Eintrag zu unserem Paket. Er sieht momentan so aus:

1.0.0

Fügen Sie ihm die Eigenschaft hinzu runtime. Das ergibt dann Folgendes:

1.0.0
  runtime

Jetzt werden bei der Projektstruktur die Dateien aus dem Paket nicht mehr in den Ausgabepfad kopiert.
Gehen wir weiter – stellen wir uns gleich vor, dass unser Plugin etwas aus der Revit API verwenden wird, das sich im Laufe der Zeit mit neuen Versionen verändert hat. Oder wir müssen einfach etwas im Code ändern, abhängig von der Revit-Version, für die wir das Plugin erstellen. Um mit solchen Unterschieden im Code umzugehen, verwenden wir bedingte Kompilierungssymbole. Öffnen Sie die Projekteigenschaften und wechseln Sie zur Registerkarte „Zusammenbau“ und geben Sie im Feld „Bedingte Kompilierungssymbole“ ein: R2015.

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Bitte beachten Sie, dass das Symbol sowohl für die Debug- als auch für die Release-Konfiguration hinzugefügt werden muss.

Während wir uns im Eigenschaftenfenster befinden, gehen wir gleich zur Registerkarte „generierten Wortliste wurde sichergestellt.“ und geben Sie im Feld „Standardnamespace“ und entfernen das Suffix, _2015damit unser Namespace universell und unabhängig vom Namen der Assembly ist:

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

In meinem Fall werden im Endprodukt die Plugins aller Versionen in einen Ordner gelegt, daher bleiben meine Assembly-Namen mit dem Suffix wie folgt: _20xx. Aber Sie können das Suffix auch im Namen der Assembly entfernen, wenn die Dateien in verschiedenen Ordnern abgelegt werden sollen.

Kommen wir zum Dateicode Class1.cs und simulieren dort einen Code unter Berücksichtigung der verschiedenen Revit-Versionen:

namespace MySuperPluginForRevit
{
    using Autodesk.Revit.Attributes;
    using Autodesk.Revit.DB;
    using Autodesk.Revit.UI;

    [Regeneration(RegenerationOption.Manual)]
    [Transaction(TransactionMode.Manual)]
    public class Class1 : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
#if R2015
            TaskDialog.Show("ModPlus", "Hallo Revit 2015");
#elif R2016
            TaskDialog.Show("ModPlus", "Hallo Revit 2016");
#elif R2017
            TaskDialog.Show("ModPlus", "Hallo Revit 2017");
#elif R2018
            TaskDialog.Show("ModPlus", "Hallo Revit 2018");
#elif R2019
            TaskDialog.Show("ModPlus", "Hallo Revit 2019");
#elif R2020
            TaskDialog.Show("ModPlus", "Hallo Revit 2020");
#endif
            return Result.Succeeded;
        }
    }
}

Ich habe sofort alle Versionen von Revit über 2015 (die zum Zeitpunkt des Schreibens des Artikels verfügbar waren) berücksichtigt und die Bedingungs-Compilation-Symbole verwendet, die nach demselben Muster erstellt werden.

Kommen wir nun zum Hauptpunkt. Erstellen Sie ein neues Projekt in unserer Lösung, diesmal für die Plugin-Version für Revit 2016. Wiederholen Sie alle oben beschriebenen Schritte, indem Sie die Zahl 2015 durch die Zahl 2016 ersetzen. Die Datei Class1.cs aus dem neuen Projekt löschen wir.

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Die Datei mit dem benötigten Code ist bereits vorhanden – Class1.cs und wir müssen lediglich einen Link in das neue Projekt einfügen. Es gibt zwei Möglichkeiten, Links einzufügen:

  1. Lange – Klicken Sie mit der rechten Maustaste auf das Projekt und wählen Sie den Punkt „Hinzufügen“ -> „Existierendes Element“, im sich öffnenden Fenster suchen Sie die benötigte Datei und wählen anstelle der Option „Hinzufügen“ die Option „Als Verknüpfung hinzufügen»

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

  1. Kurze – wählen Sie direkt im Projektmappen-Explorer die benötigte Datei (oder sogar Dateien oder ganze Ordner) aus und ziehen Sie sie mit gedrückter Alt-Taste in das neue Projekt. Während Sie ziehen, werden Sie sehen, dass der Mauszeiger beim Drücken der Alt-Taste von einem Pluszeichen zu einem Pfeil wechselt.
    UPD: Ich habe in diesem Absatz ein wenig Verwirrung gestiftet – um mehrere Dateien zu verschieben, sollten Sie die Tasten Shift+Alt!

gedrückt halten. Nach dem Durchführen des Vorgangs wird im zweiten Projekt eine Datei Class1.cs mit dem entsprechenden Symbol (blauer Pfeil) erscheinen:

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Beim Bearbeiten des Codes im Editor-Fenster können Sie auch wählen, in welchem Kontext des Projekts der Code angezeigt wird, was es Ihnen ermöglicht, den Code mit verschiedenen Bedingungen zur bedingten Kompilierung zu sehen und zu bearbeiten:

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Nach diesem Schema erstellen wir alle anderen Projekte (2017-2020). Ein kleiner Tipp – wenn Sie Dateien im Solution Explorer nicht aus dem Basisprojekt, sondern aus einem Projekt ziehen, in dem sie als Verknüpfung bereits eingefügt sind, müssen Sie die Alt-Taste nicht gedrückt halten!

Die beschriebene Variante ist bis zur Hinzufügung einer neuen Version des Plugins oder neuer Dateien im Projekt durchaus gut – alles wird dann jedoch sehr mühsam. Vor kurzem wurde mir plötzlich klar, wie ich das alles mit einem Projekt lösen kann, und wir kommen zur zweiten Methode.

Die Magie der Konfigurationen

Wenn Sie bis hierher gelesen haben, könnten Sie ausrufen: „Warum hast du die erste Methode beschrieben, wenn der Artikel sofort über die zweite geht?!“. Ich habe alles beschrieben, um klarer zu machen, wofür wir die Symbole der bedingten Kompilierung benötigen und an welchen Stellen sich unsere Projekte unterscheiden. Jetzt wird uns klarer, welche Unterschiede wir umsetzen müssen, indem wir nur ein Projekt beibehalten.

Um alles deutlicher zu machen, werden wir kein neues Projekt erstellen, sondern Änderungen an unserem aktuellen Projekt vornehmen, das mit der ersten Methode erstellt wurde.

Zuerst entfernen wir alle Projekte aus der Lösung, außer dem Hauptprojekt (das die tatsächlichen Dateien enthält), also die Projekte für die Versionen 2016-2020. Öffnen Sie den Ordner mit der Lösung und löschen Sie dort die Ordner dieser Projekte.

In unserer Lösung bleibt ein Projekt übrig — MySuperPluginForRevit_2015. Öffnen Sie die Eigenschaften davon und:

  1. Im Tab «generierten Wortliste wurde sichergestellt.» entfernen wir das Suffix aus dem Namen der Assembly _2015 (das wird gleich klar werden)
  2. Im Tab «Zusammenbau» entfernen wir das Symbol für bedingte Kompilierung R2015 aus dem entsprechenden Feld

Hinweis: In der letzten Version von Visual Studio gibt es einen Fehler – die Symbole für bedingte Kompilierung werden im Eigenschaftenfenster des Projekts nicht angezeigt, obwohl sie vorhanden sind. Wenn Sie diesen Fehler bemerken, müssen Sie sie manuell aus der .csproj-Datei entfernen. Wir werden jedoch trotzdem damit arbeiten, also lesen Sie weiter.

Benennen Sie das Projekt im Fenster des Projektmappen-Explorers um, indem Sie das Suffix entfernen _2015 und löschen Sie dann das Projekt aus der Lösung. Das ist notwendig, um Ordnung zu halten und das Gefühl der Perfektionisten zu wahren! Öffnen Sie den Ordner unserer Lösung, benennen Sie dort den Projektordner ebenfalls um und laden Sie das Projekt zurück in die Lösung.

Öffnen Sie den Konfigurations-Manager. Unsere Konfiguration Release Im Grunde genommen benötigen wir ihn nicht, also entfernen wir ihn. Wir erstellen neue Konfigurationen mit den uns bereits vertrauten Namen. R2015, R2016, …, R2020. Beachten Sie, dass Sie die Parameter aus anderen Konfigurationen nicht kopieren und keine Projektkonfigurationen erstellen müssen:

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Gehen Sie in den Projektordner und öffnen Sie die Datei mit der Endung .csproj in einem Ihnen angenehmen Editor. Übrigens kann er auch in Visual Studio geöffnet werden – man muss das Projekt entladen und dann gibt es im Kontextmenü den benötigten Punkt:

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Die Bearbeitung in Visual Studio ist sogar vorzuziehen, da der Editor sowohl formatiert als auch Hinweise gibt.

In der Datei sehen wir die Elemente PropertyGroup – ganz oben befindet sich die allgemeine, gefolgt von den mit Bedingungen. Diese Elemente legen die Eigenschaften des Projekts beim Build fest. Das erste Element ohne Bedingungen legt die allgemeinen Eigenschaften fest, während die Elemente mit Bedingungen entsprechend einige Eigenschaften in Abhängigkeit von den Konfigurationen ändern.

Wir gehen zum allgemeinen (ersten) Element PropertyGroup und schauen uns die Eigenschaft AssemblyName an – das ist der Name der Assembly und sollte ohne Suffix sein. _2015Wenn ein Suffix vorhanden ist, entfernen wir es.

Wir finden das Element mit der Bedingung

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">

Wir brauchen es nicht – also entfernen wir es.

Element mit Bedingung

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

wird für die Entwicklung und das Debugging des Codes benötigt. Sie können dessen Eigenschaften an Ihre Bedürfnisse anpassen – unterschiedliche Ausgabe-pfade festlegen, bedingte Kompilierungszeichen ändern usw.

Jetzt erstellen wir neue Elemente PropertyGroup für unsere Konfigurationen. In diesen Elementen müssen wir lediglich vier Eigenschaften festlegen:

  • OutputPath – Ausgabeverzeichnis. Ich lege einen Standardwert fest binR20xx
  • DefineConstants – bedingte Kompilierungszeichen. Hier sollte der Wert festgelegt werden TRACE;R20хх
  • TargetFrameworkVersion – Plattformversion. Für verschiedene Versionen des Revit API müssen unterschiedliche Plattformen festgelegt werden.
  • AssemblyName – Name der Assembly (d.h. der Dateiname). Sie können den gewünschten Namen direkt eingeben, aber aus Gründen der Universalität empfehle ich, den Wert $(AssemblyName)_20хх. Dafür haben wir zuvor das Suffix aus dem Namen der Assembly entfernt.

Das Wichtigste an all diesen Elementen ist – sie können ganz einfach in andere Projekte kopiert werden, ohne dass Änderungen erforderlich sind. Im weiteren Verlauf des Artikels werde ich den gesamten Inhalt der .csproj-Datei beifügen.

Wir haben die Projekteigenschaften geklärt – das ist nicht kompliziert. Aber was ist mit den abgerufenen Bibliotheken (NuGet-Paketen)? Wenn wir weiter schauen, sehen wir, dass die abgerufenen Bibliotheken in den Elementen definiert werden ItemGroup. Aber hier ist das Problem – dieses Element verarbeitet die Bedingungen nicht korrekt, wie das Element PropertyGroup. Es könnte sogar ein Fehler in Visual Studio sein, aber wenn man mehrere Elemente ItemGroup mit Konfigurationsbedingungen angibt und verschiedene Links zu NuGet-Paketen einfügt, dann werden alle angegebenen Pakete beim Wechsel der Konfiguration zum Projekt hinzugefügt.

Hierbei hilft uns das Element Wählen, das nach einer vertrauten Logik funktioniert. if-then-else.

Durch die Verwendung des Elements Wählen, definieren wir verschiedene NuGet-Pakete für unterschiedliche Konfigurationen:

Der gesamte Inhalt csproj

<Project ToolsVersion="15.0"  Debug
    AnyCPU
    {5AD738D6-4122-4E76-B865-BE7CE0F6B3EB}
    Library
    Properties
    MySuperPluginForRevit
    MySuperPluginForRevit
    v4.5
    512
    true
  
  
    true
    full
    false
    binDebug
    DEBUG;R2015
    prompt
    4
  
  
    binR2015
    TRACE;R2015
    v4.5
    $(AssemblyName)_2015
  
  
    binR2016
    TRACE;R2016
    v4.5
    $(AssemblyName)_2016
  
  
    binR2017
    TRACE;R2017
    v4.5.2
    $(AssemblyName)_2017
  
  
    binR2018
    TRACE;R2018
    v4.5.2
    $(AssemblyName)_2018
  
  
    binR2019
    TRACE;R2019
    v4.7
    $(AssemblyName)_2019
  
  
    binR2020
    TRACE;R2020
    v4.7
    $(AssemblyName)_2020
  
  
    
    
    
    
    
    
    
    
  
  
    
    
  
  
    
      
        
          1.0.0
          runtime
        
      
    
    
      
        
          1.0.0
          runtime
        
      
    
    
      
        
          1.0.0
          runtime
        
      
    
    
      
        
          1.0.0
          runtime
        
      
    
    
      
        
          1.0.0
          runtime
        
      
    
    
      
        
          1.0.0
          runtime

Bitte beachten Sie, dass ich in einer der Bedingungen zwei Konfigurationen über ODER (Or). So wird das benötigte Paket bei der Konfiguration verbunden. Debug.

Und jetzt haben wir fast alles perfekt. Wir laden das Projekt zurück, aktivieren die benötigte Konfiguration und wählen im Kontextmenü der Lösung (nicht des Projekts) den Punkt „Alle NuGet-Pakete wiederherstellen“ und sehen, wie sich unsere Pakete ändern.

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

An diesem Punkt bin ich jedoch auf ein Problem gestoßen – um alle Konfigurationen gleichzeitig zu erstellen, könnten wir die Batch-Build-Funktion (im Menü „Zusammenbau“ -> „Batch-Build“) nutzen, aber beim Wechsel der Konfigurationen erfolgt keine automatische Wiederherstellung der Pakete. Auch beim Erstellen des Projekts passiert das nicht, obwohl es eigentlich so sein sollte. Eine Lösung für dieses Problem mit Standardmitteln habe ich nicht gefunden, und es ist wahrscheinlich auch ein Bug in Visual Studio.

Deshalb wurde für den Batch-Build beschlossen, ein spezielles automatisiertes Build-System zu verwenden Nuke. Tatsächlich wollte ich das nicht, da ich dies im Rahmen der Plugin-Entwicklung für überflüssig halte, aber im Moment sehe ich keine andere Lösung. Und auf die Frage „Warum gerade Nuke?“ ist die Antwort einfach – wir verwenden es bei der Arbeit.

Lassen Sie uns also in den Ordner unserer Lösung (nicht des Projekts) wechseln, die Taste Umschalttaste gedrückt halten und mit der rechten Maustaste auf einen leeren Bereich im Ordner klicken – im Kontextmenü wählen wir den Punkt „PowerShell-Fenster hier öffnen“».

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Falls Sie nicht nukeinstalliert haben, geben Sie zunächst den Befehl ein

dotnet tool install Nuke.GlobalTool –global

Jetzt geben Sie den Befehl ein nuke und es wird Ihnen angeboten, das nuke für das aktuelle Projekt einzurichten. Ich weiß nicht, wie man das besser auf Russisch schreiben kann – auf Englisch würde da stehen: Could not find .nuke file. Do you want to setup a build? [y/n]

Drücken Sie die Y-Taste und es folgen die eigentlichen Einrichtungspunkte. Wir benötigen die einfachste Variante mit MSBuild, deshalb antworten wir wie im Screenshot:

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Gehen wir zu Visual Studio, das uns anbietet, die Lösung neu zu laden, da ein neues Projekt hinzugefügt wurde. Wir laden die Lösung neu und sehen, dass wir ein Projekt haben build , wobei uns nur eine Datei interessiert – Build.cs

Wir entwickeln ein Plugin-Projekt mit Kompilierung für verschiedene Versionen von Revit/AutoCAD

Wir öffnen diese Datei und schreiben ein Skript zur Erstellung des Projekts für alle Konfigurationen. Oder wir verwenden mein Skript, das Sie anpassen können:

using System.IO;
using Nuke.Common;
using Nuke.Common.Execution;
using Nuke.Common.ProjectModel;
using Nuke.Common.Tools.MSBuild;
using static Nuke.Common.Tools.MSBuild.MSBuildTasks;

[CheckBuildProjectConfigurations]
[UnsetVisualStudioEnvironmentVariables]
class Build : NukeBuild
{
    public static int Main () => Execute<Build>(x => x.Compile);

    [Solution] readonly Solution Solution;

    // Wenn der Lösungsname und der Projekt- (Plugin-) Name unterschiedlich sind, geben Sie hier den Namen des Projekts (Plugins) an
    string PluginName => Solution.Name;

    Target Compile => _ => _
        .Executes(() =>
        {
            var project = Solution.GetProject(PluginName);
            if (project == null)
                throw new FileNotFoundException("Nicht gefunden!");

            var build = new List<string>();
            foreach (var (_, c) in project.Configurations)
            {
                var configuration = c.Split("|")[0];

                if (configuration == "Debug" || build.Contains(configuration))
                    continue;

                Logger.Normal($"Konfiguration: {configuration}");

                build.Add(configuration);

                MSBuild(_ => _
                    .SetProjectFile(project.Path)
                    .SetConfiguration(configuration)
                    .SetTargets("Restore"));
                MSBuild(_ => _
                    .SetProjectFile(project.Path)
                    .SetConfiguration(configuration)
                    .SetTargets("Rebuild"));
            }
        });
}

Kehren Sie ins PowerShell-Fenster zurück und geben Sie erneut den Befehl ein nuke (Sie können den Befehl eingeben nuke unter Angabe des erforderlichen Ziels. Aber wir haben nur einen Ziels, der standardmäßig gestartet wird). Nach Drücken der Enter-Taste fühlen wir uns wie echte Hacker, denn wie im Film wird das automatische Zusammenstellen unseres Projekts für verschiedene Konfigurationen stattfinden.

Übrigens kann man PowerShell direkt aus Visual Studio verwenden (Menü „Ansicht“ -> „Weitere Fenster“ -> „Paket-Manager-Konsole„), aber alles wird in Schwarz-Weiß angezeigt, was nicht sehr praktisch ist.

Damit endet mein Artikel. Ich bin sicher, dass Sie mit der Version für AutoCAD selbst klar kommen werden. Ich hoffe, dass das hier vermittelte Material seine „Kunden“ finden wird.

Vielen Dank für Ihre Aufmerksamkeit!

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster