Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Při vývoji pluginů pro CAD aplikace (v mém případě to je AutoCAD, Revit a Renga) se časem objevuje jeden problém – vycházejí nové verze programů, mění se jejich API a je potřeba vytvářet nové verze pluginů.

Když máte pouze jeden plugin nebo jste nováček samouk v této oblasti, můžete jednoduše udělat kopii projektu, změnit v něm potřebná místa a sestavit novou verzi pluginu. To samozřejmě vede k výraznému zvýšení pracovní zátěže při dalších změnách v kódu.

S narůstajícími zkušenostmi a znalostmi najdete několik způsobů, jak tento proces automatizovat. Prošel jsem touto cestou a chci vám říct, k čemu jsem dospěl a jak pohodlné to je.

Nejprve se podíváme na způsob, který je zřejmý a kterým jsem dlouho používal

Odkazy na soubory projektu

Aby bylo vše jednoduché, názorné a jasné, budu vše popisovat na abstraktním příkladu vývoje pluginu.

Otevřeme Visual Studio (mám verzi Community 2019. A ano – v ruštině) a vytvoříme nové řešení. Pojmenujeme ho MySuperPluginForRevit

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Budeme vyrábět plugin pro Revit pro verze 2015-2020. Proto v řešení vytvořím nový projekt (Knihovna tříd Net Framework) a pojmenuji ho MySuperPluginForRevit_2015

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Musíme přidat odkazy na API Revit. Samozřejmě, můžeme přidat odkazy na místní soubory (bude potřeba mít nainstalované všechny potřebné SDK nebo všechny verze Revit), ale půjdeme ihned správnou cestou a připojíme NuGet balíček. Můžete najít množství balíčků, ale já budu používat ty své.

Po připojení balíčku klikneme pravým tlačítkem na položku „Odkazy“ a v menu zvolíme možnost „Přesunout packages.config do PackageReference…»

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Pokud náhodou v tomto místě začnete panikařit, protože v okně vlastností balíčku nebude důležitý bod „Kopírovat lokálně“, který musíme nastavit na hodnotu false, tak nemusíte panikařit – jdeme do složky s projektem, otevřeme soubor s příponou .csproj v editoru, který vám vyhovuje (já používám Notepad++) a najdeme tam záznam o našem balíčku. Vypadá nyní takto:

<PackageReference Include="ModPlus.Revit.API.2015">
  <Version>1.0.0<\/Version>
<\/PackageReference>

Přidáváme mu vlastnost <ExcludeAssets>runtime<\/ExcludeAssets>. Vyjde to takto:

1.0.0
  runtime

Při sestavení projektu se nyní soubory z balíčku nebudou kopírovat do výstupní složky.
Pokračujeme – představme si, že náš plugin bude používat něco z Revit API, co se v průběhu vydání nových verzí měnilo. Nebo prostě potřebujeme něco změnit v kódu v závislosti na verzi Revit, pro kterou plugin vytváříme. Pro řešení těchto rozdílů v kódu budeme používat symboly podmínkového kompilace. Otevřete vlastnosti projektu, přejděte na záložku „Sestavení“ a do pole „Symboly podmínkového kompilace“ napište R2015.

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Všimněte si, že symbol je třeba přidat jak pro konfiguraci Debug, tak pro konfiguraci Release.

Zatímco jsme v okně vlastností, přejdeme ihned na záložku „Aplikace“ a do pole „Výchozí prostor názvů“ a odstraníme příponu _2015, aby měl náš prostor názvů univerzální a nezávislý název na složce:

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

V mém případě se v konečném produktu pluginy všech verzí ukládají do jedné složky, takže mají názvy DLL s příponou ve tvaru _20xx. Ale můžete příponu odstranit i z názvu DLL, pokud plánujete umístit soubory do různých složek.

Přejděme ke kódu souboru Class1.cs a simulujme tam nějaký kód s ohledem na různé verze 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", "Ahoj Revit 2015");
#elif R2016
            TaskDialog.Show("ModPlus", "Ahoj Revit 2016");
#elif R2017
            TaskDialog.Show("ModPlus", "Ahoj Revit 2017");
#elif R2018
            TaskDialog.Show("ModPlus", "Ahoj Revit 2018");
#elif R2019
            TaskDialog.Show("ModPlus", "Ahoj Revit 2019");
#elif R2020
            TaskDialog.Show("ModPlus", "Ahoj Revit 2020");
#endif
            return Result.Succeeded;
        }
    }
}

Ihned jsem zahrnul všechny verze Revit nad verzi 2015 (které byly k dispozici v době psaní článku) a vzal v potaz existenci symbolů podmínkového kompilace, které mám nastavené podle stejného vzoru.

Přejděme k hlavnímu lákadlu. Vytvořte nový projekt v našem řešení, ale tentokrát pro verzi pluginu pro Revit 2016. Opakujte všechny výše popsané kroky, odpovídajícím způsobem měňte číslo 2015 na 2016. Ale soubor Class1.cs z nového projektu odstraníme.

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Soubor s požadovaným kódem – Class1.cs – už máme a potřebujeme na něj jen vložit odkaz v novém projektu. Existují dvě možnosti vložení odkazů:

  1. Dlouhý – klikneme pravým tlačítkem na projekt, vybereme položku «Přidat» -> «Existující prvek», v otevřeném okně najdeme potřebný soubor a místo možnosti «Přidat» vybereme možnost «Přidat jako vazbu»

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

  1. Krátký – přímo v Průzkumníku řešení vybereme potřebný soubor (nebo dokonce soubory. Můžete klidně i celé složky) a přetáhneme do nového projektu se stisknutým klávesem Alt. Při přetahování uvidíte, že se kurzor myši změní z plusu na šipku při stisknutí klávesy Alt.
    UPD: Trochu jsem zanesl zmatek do tohoto odstavce – abyste mohli přetahovat více souborů, je třeba stisknout Shift+Alt!

Po provedení této procedury se nám ve druhém projektu objeví soubor Class1.cs s odpovídající ikonou (modrá šipka):

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Při úpravě kódu v editoru můžete také vybírat, v kontextu jakého projektu se má kód zobrazovat, což vám umožní vidět a upravit kód při různých symbolech podmínkové kompilace:

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Podle této schémy vytváříme všechny ostatní projekty (2017-2020). Tip – pokud přetahujete soubory v Průzkumníku řešení ne z základního projektu, ale z projektu, kde už jsou vloženy jako vazba, tak není třeba držet klávesu Alt!

Popisovaná varianta je docela dobrá do momentu, kdy přidáme novou verzi pluginu nebo do momentu přidání nových souborů do projektu – to vše se stává velmi obtížným. A nedávno jsem náhle pochopil, jak to všechno vyřešit jedním projektem, a přecházíme k druhému způsobu

Magie konfigurací

Při čtení tady můžete vykřiknout: „A proč jsi popisoval první způsob, když článek okamžitě o druhém?!“. A popsal jsem to všechno, aby bylo jasnější, k čemu nám potřebné symboly podmínkové kompilace slouží a v jakých místech se naše projekty liší. A nyní je nám jasné, jaké přesně odlišnosti projektů potřebujeme realizovat, přičemž zachováme pouze jeden projekt.

Abychom byli všichni více zřetelní, nevytváříme nový projekt, ale provedeme změny v našem aktuálním projektu, vytvořeném prvním způsobem.

Takže nejprve odstraníme ze řešení všechny projekty kromě hlavního (obsahujícího přímo soubory). Tedy projekty pro verze 2016-2020. Otevřeme složku s řešením a odstraníme tam složky těchto projektů.

V našem řešení zůstal jeden projekt — MySuperPluginForRevit_2015. Otevřeme jeho vlastnosti a:

  1. Na kartě „Aplikace“ odstraníme ze jména sestavení příponu _2015 (dále bude jasné proč)
  2. Na kartě „Sestavení“ odstraníme symbol podmínkové kompilace R2015 z odpovídajícího pole

Poznámka: V poslední verzi Visual Studio se vyskytuje chyba – symboly podmínkové kompilace se nezobrazují v okně vlastností projektu, ačkoli tam jsou. Pokud máte tuto chybu, musíte je ručně odstranit z .csproj souboru. Ale i tak v něm musíme pracovat, takže čteme dál.

Přejmenujeme projekt v okně Průzkumníka řešení, odstraníme příponu _2015 a poté projekt odstraníme z řešení. To je třeba pro udržení pořádku a pocitů perfectionistů! Otevřeme složku našeho řešení, přejmenujeme tam stejným způsobem složku projektu a načteme projekt zpět do řešení.

Otevřeme správce konfigurací. Konfigurace Release v zásadě nebude potřeba, proto ji odstraníme. Vytvoříme nové konfigurace s již známými názvy R2015, R2016, …, R2020. Všimněte si, že není třeba kopírovat parametry z jiných konfigurací a není třeba vytvářet konfigurace projektu:

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Jdeme do složky s projektem a otevřeme soubor s příponou .csproj v editoru, který vám vyhovuje. Mimochodem, lze ho otevřít i ve Visual Studio – je třeba projekt odpojit a poté v kontextovém menu bude potřebná položka:

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Úprava ve Visual Studio je dokonce výhodnější, protože editor zarovná a naznačuje.

V souboru uvidíme prvky PropertyGroup – v horní části je obecný a následují podmínkové. Tyto prvky určují vlastnosti projektu při jeho sestavování. První prvek, který nemá podmínky, určuje obecné vlastnosti, zatímco prvky s podmínkami odpovídajícím způsobem mění některé vlastnosti v závislosti na konfiguracích.

Přejdeme k obecnému (prvnímu) prvku PropertyGroup a podíváme se na vlastnost AssemblyName – to je název sestavení a mělo by být bez přípony _2015. Pokud je přípona přítomna, odstraníme ji.

Najdeme prvek s podmínkou

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

Ten nepotřebujeme – odstraníme ho.

Prvek s podmínkou

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

bude potřeba pro práci v době vývoje a ladění kódu. Můžete měnit jeho vlastnosti podle svých potřeb – nastavovat různé výstupní cesty, měnit symboly podmínkové kompilace atd.

Nyní vytvoříme nové prvky PropertyGroup pro naše konfigurace. U těchto prvků stačí definovat čtyři vlastnosti:

  • OutputPath – výstupní složka. Definuji standardní hodnotu binR20xx
  • DefineConstants – symboly podmínkového kompilování. Je třeba zadat hodnotu TRACE;R20хх
  • TargetFrameworkVersion – verze platformy. Pro různé verze Revit API je třeba zadat různé platformy.
  • AssemblyName – název sestavení (tj. název souboru). Můžete napsat přímo potřebný název sestavení, ale pro univerzálnost doporučuji napsat hodnotu $(AssemblyName)_20хх. Pro to jsme dříve odstranili příponu z názvu sestavení.

Nejdůležitější věcí na těchto prvcích je, že je lze jednoduše kopírovat do jiných projektů, aniž by se měnily. Dále v článku přiložím veškerý obsah souboru .csproj.

Dobře, vlastnosti projektu jsme si objasnili – není to obtížné. Ale co s připojenými knihovnami (NuGet balíčky)? Pokud se podíváme dále, uvidíme, že připojené knihovny jsou definovány v elementech ItemGroup. Ale tady je problém – tento element nesprávně zpracovává podmínky, jako element PropertyGroup. Možná je to dokonce chyba Visual Studia, ale pokud zadáte několik elementů ItemGroup s podmínkami konfigurací a uvnitř vložíte různé odkazy na NuGet balíčky, při změně konfigurace se k projektu připojí všechny uvedené balíčky.

Na pomoc přichází prvek Choose, který funguje podle nám známé logiky. if-then-else.

Použitím prvku Choose, definujeme různé NuGet balíčky pro různé konfigurace:

Všechen obsah 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

Vezměte na vědomí, že v jedné z podmínek jsem uvedl dvě konfigurace přes NEBO (Or). Takto se připojí potřebný balíček při konfiguraci Debug.

A nyní máme téměř vše v pořádku. Načteme zpět projekt, zapneme potřebnou konfiguraci, vybereme v kontextovém menu řešení (ne projektu) položku „Obnovit všechny balíčky NuGet“ a vidíme, jak se nám mění balíčky.

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

A na tomto bodě jsem narazil na slepou uličku – abychom sestavili všechny konfigurace najednou, mohli bychom využít dávkovou sestavu (menu „Sestavení» -> «Dávková sestava“), ale při přepínání konfigurací se automaticky balíčky neobnovují. A při sestavování projektu se to také neděje, i když by to mělo. Řešení tohoto problému pomocí standardních prostředků jsem nenašel. A s největší pravděpodobností je to také chyba Visual Studia.

Proto bylo pro dávkovou sestavu rozhodnuto použít speciální systém automatizované sestavy Nuke. Vlastně jsem to nechtěl, protože považuji za nadbytečné v rámci vývoje pluginů, ale v tuto chvíli nevidím jiné řešení. A na otázku „Proč právě Nuke?“ je odpověď jednoduchá – používáme ho v práci.

Takže přejdeme do složky našeho řešení (ne projektu), podržíme klávesu Shift a klikneme pravým tlačítkem myši na prázdné místo ve složce – v kontextovém menu vybereme položku „Otevřít okno PowerShell zde».

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Pokud nemáte nainstalováno nuke, nejprve napište příkaz

dotnet tool install Nuke.GlobalTool –global

Nyní napište příkaz nuke a budete vyzváni k nastavení nuke pro aktuální projekt. Nevím, jak to správně napsat v českém jazyce – v angličtině by to bylo napsáno Could not find .nuke file. Do you want to setup a build? [y/n]

Stiskněte klávesu Y a dále budou bezprostřední body nastavení. Potřebujeme nejjednodušší variantu s použitím MSBuild, proto odpovídáme, jak je na snímku obrazovky:

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Přejdeme do Visual Studia, které nám nabídne přeuložení řešení, protože byl přidán nový projekt. Přeuložíme řešení a vidíme, že se nám objevil projekt build ve kterém nás zajímá pouze jeden soubor – Build.cs

Vytváříme jeden projekt pluginu s kompilací pro různé verze Revit / AutoCAD

Otevřeme tento soubor a napíšeme skript pro sestavení projektu pod všechny konfigurace. Nebo použijeme můj skript, který si můžete upravit podle sebe:

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;

    // Pokud se název řešení a název projektu (pluginu) liší, uveďte zde název projektu (pluginu)
    string PluginName => Solution.Name;

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

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

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

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

                build.Add(configuration);

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

Vracíme se do okna PowerShell a znovu píšeme příkaz nuke (můžete napsat příkaz nuke s uvedením potřebného Target. Ale máme jen jeden Target, který se spouští jako výchozí). Po stisknutí klávesy Enter se budeme cítit jako skuteční hackeři, protože se jako ve filmu spustí automatická sestavení našeho projektu pod různé konfigurace.

Mimochodem, můžete používat PowerShell přímo z Visual Studia (menu „Zobrazit» -> «Ostatní okna» -> «Konzole správce balíčků“), ale tam to bude celé černobílé, což není moc pohodlné.

Na tomto místě má můj článek konec. Jsem si jistý, že s verzí pro AutoCAD si poradíte sami. Doufám, že zde prezentovaný materiál najde své „klienty”.

Děkujeme za pozornost!

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster