Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Ved udvikling af plugins til CAD-applikationer (i mit tilfælde det er AutoCAD, Revit og Renga) opstår der over tid et problem - nye versioner af programmerne udgives, deres API ændres, og der skal laves nye versioner af plugins.

Når du kun har ét plugin eller hvis du stadig er en nybegynder, kan du simpelthen lave en kopi af projektet, ændre de nødvendige steder og samle en ny version af pluginet. Følgelig vil efterfølgende ændringer i koden medføre en betydelig stigning i arbejdsbyrden.

Efterhånden som du opnår erfaring og viden, vil du finde flere metoder til at automatisere denne proces. Jeg har gennemgået denne vej og vil gerne fortælle dig, hvad jeg er nået frem til og hvor praktisk det er.

Lad os først se på den metode, der er åbenlys og som jeg har brugt i lang tid

Links til projektfiler

Og for at alt skal være simpelt, overskueligt og klart, vil jeg beskrive alt ved hjælp af et abstrakt eksempel på plugin-udvikling.

Lad os åbne Visual Studio (jeg har version Community 2019. Og ja - på russisk) og oprette en ny løsning. Vi kalder det MySuperPluginForRevit

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Vi vil lave et plugin til Revit for versionerne 2015-2020. Derfor vil jeg oprette et nyt projekt i løsningen (Klassebibliotek Net Framework) og kalde det MySuperPluginForRevit_2015

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Vi skal tilføje links til Revit API. Selvfølgelig kan vi tilføje links til lokale filer (man skal installere alle nødvendige SDK'er eller alle versioner af Revit), men vi vil straks tage den rigtige vej og tilslutte NuGet-pakken. Du kan finde et væld af pakker, men jeg vil bruge mine egne.

Når pakken er tilsluttet, højreklikker vi på punktet «Links» og vælger i menuen punktet «Flyt packages.config til PackageReference…»

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Hvis du pludselig begynder at panikere på dette punkt, fordi der i pakken's egenskaber ikke er et vigtigt punkt «Kopier lokalt», som vi absolut skal have indstillet til værdien false, så skal du ikke panikere – gå til mappen med projektet, åbn filen med filtypenavnet .csproj i en editor, du finder bekvem (jeg bruger Notepad++) og find optegnelsen om vores pakke. Den ser nu således ud:

1.0.0

Tilføj denne egenskab til den runtime. Det vil se således ud:

1.0.0
  runtime

Nu vil filer fra pakken ikke blive kopieret til output-mappen, når projektet bygges.
Lad os gå videre – lad os først forestille os, at vores plugin vil bruge noget fra Revit API, som har ændret sig med tiden, efterhånden som nye versioner er blevet udgivet. Eller måske skal vi bare ændre noget i koden afhængigt af den version af Revit, som vi laver plugin til. For at håndtere sådanne forskelle i koden vil vi bruge præprocessor direktiver. Vi åbner projektindstillingerne og går til fanen “Kompilering” og i feltet “Præprocessor direktiver” skriver vi R2015.

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Vær opmærksom på, at symbolet også skal tilføjes både til Debug-konfigurationen og Release-konfigurationen.

Mens vi er i indstillingsvinduet, går vi straks til fanen “Applikation” og i feltet “Standard-navnerum” og fjerner suffikset _2015, så vores navnerum bliver universelt og uafhængigt af samlingens navn:

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

I mit tilfælde samles plugins for alle versioner i en enkelt mappe, så mine samlingsnavne forbliver med suffikset i stil med _20xx. Men du kan også fjerne suffikset fra samlingsnavnet, hvis filerne forventes at være placeret i forskellige mapper.

Lad os gå videre til koden i filen Class1.cs og simulere en form for kode under hensyntagen til forskellige versioner af Revit:

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", "Hello Revit 2015");
#elif R2016
            TaskDialog.Show("ModPlus", "Hello Revit 2016");
#elif R2017
            TaskDialog.Show("ModPlus", "Hello Revit 2017");
#elif R2018
            TaskDialog.Show("ModPlus", "Hello Revit 2018");
#elif R2019
            TaskDialog.Show("ModPlus", "Hello Revit 2019");
#elif R2020
            TaskDialog.Show("ModPlus", "Hello Revit 2020");
#endif
            return Result.Succeeded;
        }
    }
}

Jeg har straks taget højde for alle Revit-versioner over 2015 (som var tilgængelige på tidspunktet for at skrive artiklen) og har straks taget højde for tilstedeværelsen af præprocessor direktiver, som jeg opretter efter et ensartet mønster.

Lad os gå videre til den største nyhed. Opret et nyt projekt i vores løsning, men denne gang til plugin-versionen til Revit 2016. Gentag alle de førnævnte trin, men udskift 2015 med 2016. Men filen Class1.cs fra det nye projekt sletter vi.

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Filen med den ønskede kode – Class1.cs – vi har allerede det, og vi skal bare indsætte et link til det i det nye projekt. Der er to måder at indsætte links på:

  1. Lang – højreklik på projektet, vælg punktet «Tilføj» -> «Eksisterende element», i det åbne vindue finder vi den nødvendige fil, og i stedet for indstillingen «Tilføj» vælger vi indstillingen «Tilføj som forbindelse»

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

  1. Kort – i listerne over løsninger vælger vi den nødvendige fil (eller endda filer. Man kan endda tage hele mapper) og trækker den ind i det nye projekt, mens man holder Alt-tasten nede. Under trækningen vil du se, at musemarkøren, når du holder Alt-tasten nede, skifter fra et plus til en pil.
    OPD: Jeg forårsagede lidt forvirring i dette afsnit — for at flytte flere filer skal man holde Shift+Alt!

Efter udførelsen af proceduren vil vi have en fil i det andet projekt Class1.cs med det tilsvarende ikon (blå pil):

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Når du redigerer koden i editorvinduet, kan du også vælge, i hvilken kontekst projektenes kode skal vises, hvilket gør det muligt at se og redigere koden under forskellige betingede kompilationssymboler:

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Ved denne metode opretter vi alle de andre projekter (2017-2020). Life hack – hvis man trækker filer i listen over løsninger fra et projekt, der ikke er grundprojektet, men et projekt, hvor de allerede er indsæt som forbindelse, så behøver man ikke at holde Alt-tasten nede!

Den beskrevne metode er helt finsk indtil man tilføjer en ny version af pluginet eller tilføjer nye filer til projektet – alt dette bliver meget besværgeligt. Og for nylig indså jeg pludselig, hvordan man kan styre det hele med ét projekt, og vi går videre til den anden metode.

Magien i konfigurationer

Når du har læst hertil, kan du udbringe «Hvorfor i alverden beskrev du den første metode, hvis artiklen straks handler om den anden?!». Og jeg beskrev det hele for at gøre det klarere, hvad vi har brug for betingede kompilationssymboler til, og hvor vores projekter adskiller sig. Og nu bliver det klarere, hvilke præcise forskelle mellem projekterne vi skal implementere, mens vi kun lader ét projekt være tilbage.

Og for at alt bliver mere åbenlyst, vil vi ikke oprette et nyt projekt, men i stedet foretage ændringer i vores nuværende projekt, der blev oprettet ved den første metode.

Så vi fjerner først alle projekter fra løsningen, bortset fra hovedprojektet (som indeholder de egentlige filer). Det vil sige projekterne til versionerne 2016-2020. Vi åbner mappen med løsningen og sletter mapperne for disse projekter der.

Vi har et projekt tilbage i løsningen — MySuperPluginForRevit_2015. Åbner dets egenskaber og:

  1. På fanen „Applikation“ fjerner vi suffikset fra samlingsnavnet _2015 (det bliver klart senere)
  2. På fanen „Kompilering“ fjerner vi betingelsessymbolet R2015 fra det tilsvarende felt

Bemærk: I den seneste version af Visual Studio er der en fejl – betingelsessymbolet vises ikke i projektets egenskaber, selvom de er der. Hvis du oplever denne fejl, skal du fjerne dem manuelt fra .csproj-filen. Men vi skal stadig arbejde i den, så lad os fortsætte med at læse.

Omnavngiv projektet i løsningerudforskeren ved at fjerne suffikset _2015 og fjern derefter projektet fra løsningen. Dette er nødvendigt for at opretholde orden og tilfredsstille perfektionister! Åbn mappen for vores løsning, omdøb mappen der på samme måde, og upload projektet tilbage til løsningen.

Åbn konfigurationshåndteringen. Vi får brug for konfigurationen Release vil principielt ikke være nødvendigt, så vi sletter den. Opretter nye konfigurationer med de allerede velkendte navne R2015, R2016, …, R2020. Bemærk, at det ikke er nødvendigt at kopiere parametrene fra andre konfigurationer, og der er ikke behov for at oprette projektkonfigurationer:

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Gå til projektmappen og åbne filen med filtypen .csproj i den editor, du foretrækker. For resten kan den også åbnes i Visual Studio – du skal først indlæse projektet og derefter vil der være den nødvendige punkt i kontekstmenuen:

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

At redigere i Visual Studio er endda at foretrække, da editoren både justerer og giver forslag.

I filen vil vi se elementerne PropertyGroup – øverst er der en generel, og derefter følger de betingede. Disse elementer bestemmer projektets egenskaber ved kompilering. Det første element, der ikke har betingelser, angiver generelle egenskaber, mens elementer med betingelser ændrer nogle egenskaber afhængigt af konfigurationerne.

Vi går til det generelle (første) element PropertyGroup og ser på egenskaben AssemblyName – dette er samlingsnavnet, og det skal være uden suffiks _2015. Hvis der er et suffiks, så fjern det.

Find elementet med betingelsen

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

Det er ikke nødvendigt – slet det.

Elementet med betingelsen

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

vil være nødvendigt for at arbejde på udviklings- og fejlfindingsstadiet. Du kan ændre dets egenskaber efter dine behov – angive forskellige output-stier, ændre betingelsessymboller osv.

Nu opretter vi nye elementer PropertyGroup for vores konfigurationer. For disse elementer er det nok at angive fire egenskaber:

  • OutputPath – output-mappe. Jeg angiver standardværdien binR20xx
  • DefineConstants – betingede kompilationssymboler. Værdien skal angives som TRACE;R20хх
  • TargetFrameworkVersion – platformversion. For forskellige versioner af Revit API skal der angives forskellige platforme.
  • AssemblyName – navnet på samlingen (dvs. navnet på filen). Du kan indtaste det ønskede navn på samlingen direkte, men for universets skyld anbefaler jeg at skrive værdien $(AssemblyName)_20хх. For dette fjernede vi tidligere suffikset fra samlingens navn

Den vigtigste funktion ved alle disse elementer er, at de kan kopieres til andre projekter uden ændringer. I artiklen vil jeg vedhæfte alt indholdet fra .csproj-filen.

Godt, vi har håndteret projekternes egenskaber - det er ikke kompliceret. Men hvad med de tilsluttede biblioteker (NuGet-pakker). Hvis man ser nærmere på det, vil vi se, at de tilsluttede biblioteker angives i elementerne ItemGroup. Men her er problemet – dette element behandler betingelserne forkert, som elementet PropertyGroup. Det kan endda være et glitch i Visual Studio, men hvis man angiver flere elementer ItemGroup med betingelser for konfigurationer og inden i indsætter forskellige referencer til NuGet-pakker, så tilføjes alle angivne pakker til projektet, når konfigurationen ændres.

Her hjælper elementet Choose, som fungerer efter den logik, vi kender if-then-else.

Ved at bruge elementet Choose, angiver vi forskellige NuGet-pakker for forskellige konfigurationer:

Alt indholdet i csproj

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

Bemærk, at jeg i et af betingelserne angav to konfigurationer via ELLER (Or). På den måde vil den nødvendige pakke blive tilsluttet ved konfiguration Debug.

Og nu er alt næsten perfekt. Vi indlæser tilbage projektet, aktiverer den konfiguration, vi har brug for, og vælger i kontekstmenuen for løsningen (ikke projektet) punktet «Gendan alle NuGet-pakker» og ser, hvordan vores pakker ændres.

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Og på dette tidspunkt er jeg gået i stå – for at samle alle konfigurationer på én gang kunne vi bruge pakkebygning (menuen «Kompilering» -> «Pakkebygning»), men når konfigurationerne skiftes, sker der ikke en automatisk genoprettelse af pakkerne. Og ved projektbygning sker det heller ikke, selvom det burde. Jeg fandt ikke en løsning på dette problem med standardmidler og det vil sandsynligvis også være en fejl i Visual Studio.

Derfor blev det besluttet at bruge et specielt automatiseret byggesystem Nuke. Faktisk ønskede jeg ikke dette, da jeg anser det for overflødigt i forbindelse med plugin-udvikling, men i øjeblikket ser jeg ikke et andet valg. Og spørgsmålet «Hvorfor netop Nuke?» har et enkelt svar – vi bruger det på arbejde.

Så lad os gå til mappen for vores løsning (ikke projektet), holde tasten Shift nede og højreklikke på et tomt sted i mappen – i kontekstmenuen vælger vi punktet «Åbn PowerShell-vindue her».

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Hvis du ikke har installeret nuke, så skal du først skrive kommandoen

dotnet tool install Nuke.GlobalTool –global

Nu skriver du kommandoen nuke og du vil blive bedt om at konfigurere nuke for det aktuelle projekt. Jeg ved ikke, hvordan man korrekt skriver dette på dansk – på engelsk vil det stå Could not find .nuke file. Do you want to setup a build? [y/n]

Tryk på tasten Y, og derefter vil der være specifikke konfigurationspunkter. Vi har brug for den simpleste mulighed med brugen af MSBuild, så derfor svarer vi som på skærmbilledet:

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Lad os gå tilbage til Visual Studio, som vil tilbyde os at genindlæse løsningen, da der er tilføjet et nyt projekt. Vi genindlæser løsningen og ser, at vi har fået et projekt build hvor vi kun er interesserede i én fil – Build.cs

Vi laver et plugin-projekt med kompilering til forskellige versioner af Revit/AutoCAD

Vi åbner denne fil og skriver et build-script til projektet for alle konfigurationer. Eller vi bruger mit script, som du kan redigere til dine behov:

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(x => x.Compile);

    [Solution] readonly Solution Solution;

    // Hvis navnet på løsningen og projektet (plugin) er forskellige, angiv da navnet på projektet (plugin) her
    string PluginName => Solution.Name;

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

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

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

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

                build.Add(configuration);

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

Vi vender tilbage til PowerShell-vinduet og skriver kommandoen igen nuke (man kan skrive kommandoen nuke med den ønskede Mål. Men vi har én Mål, som kører som standard). Efter at have trykket på Enter-tasten vil vi føle os som rigtige hackere, da der som i en film vil ske en automatisk opbygning af vores projekt under forskellige konfigurationer.

For resten, kan man bruge PowerShell direkte fra Visual Studio (menuen «Vis» -> «Andre vinduer» -> «Pakkeadministratorens konsol»), men der vil alt være sort-hvidt, hvilket ikke er særligt praktisk.

Dette afslutter min artikel. Jeg er sikker på, at du selv kan finde ud af versionen til AutoCAD. Jeg håber, at det materiale, der er præsenteret her, finder sine 'kunder'.

Tak for din opmærksomhed!

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster