
Při vývoji pluginů pro CAD aplikace ( 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

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

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…»

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
runtimePř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.

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:

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.

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ů:
- 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»

- 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):

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:

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:
- Na kartě „Aplikace“ odstraníme ze jména sestavení příponu _2015 (dále bude jasné proč)
- 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:

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:

Úprava ve Visual Studio je dokonce výhodnější, protože editor zarovná a naznačuje.
V souboru uvidíme prvky – 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 . 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 , 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
runtimeVezmě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.

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 . 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».

Pokud nemáte nainstalováno nuke, nejprve napište příkaz
dotnet tool install Nuke.GlobalTool –globalNyní 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:

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

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
