
Kada razvijate dodatke za CAD aplikacije ( to su AutoCAD, Revit i Renga), s vremenom se javlja jedan problem – izlaze nove verzije programa, mijenja se njihov API i potrebno je raditi nove verzije dodataka.
Kada imate samo jedan dodatak ili ste još početnik u ovom poslu, možete jednostavno napraviti kopiju projekta, promijeniti potrebna mjesta u njemu i sastaviti novu verziju dodatka. Posljedično, daljnje promjene u kodu rezultirat će višekratnim povećanjem radnih napora.
Kao što stječete iskustvo i znanje, pronaći ćete nekoliko načina za automatizaciju ovog procesa. Prošao sam taj put i želim vam reći do čega sam došao na kraju i koliko je to praktično.
Za početak, razmotrit ćemo način koji je očit i kojeg sam dugo vremena koristio.
Linkovi na datoteke projekta
I kako bi sve bilo jednostavno, pregledno i jasno, opisivat ću sve na apstraktnom primjeru razvoja dodatka.
Otvorimo Visual Studio (imam verziju Community 2019. I da – na hrvatskom jeziku) i kreirajmo novo rješenje. Nazovimo ga MySuperPluginForRevit

Radit ćemo dodatak za Revit za verzije od 2015. do 2020. Zato ću u rješenju stvoriti novi projekt (Biblioteka klasa Net Framework) i nazvati ga MySuperPluginForRevit_2015

Trebamo dodati reference na Revit API. Naravno, možemo dodati reference na lokalne datoteke (trebat će instalirati sve potrebne SDK-ove ili sve verzije Revita), ali krenut ćemo pravim putem i povezati NuGet paket. Možete pronaći značajan broj paketa, ali ja ću koristiti svoje vlastite.
Nakon povezivanja paketa kliknemo desnom tipkom miša na stavku «Linkovi» i odabiremo u izborniku stavku «Premjesti packages.config u PackageReference…»

Ako slučajno u ovoj točki dođe do panike jer u prozoru s svojstvima paketa neće biti važnog stavka «Kopirati lokalno», koji moramo postaviti na vrijednost false, nema potrebe za panikom – idemo u mapu s projektom, otvaramo datoteku s ekstenzijom .csproj u nekom uređivaču (ja koristim Notepad++) i nalazimo zapis o našem paketu. Izgleda ovako:
1.0.0Dodajemo mu svojstvo runtime. Ispast će ovako:
1.0.0
runtimeSada pri izradi projekta, datoteke iz paketa neće biti kopirane u izlaznu mapu.
Iđemo dalje – odmah pretpostavimo da naš plugin koristi nešto iz Revit API-ja, što se mijenjalo s vremenom izlaska novih verzija. Ili jednostavno trebamo nešto promijeniti u kodu ovisno o verziji Revit-a za koju radimo plugin. Za rješavanje takvih razlika u kodu, koristit ćemo simbole uvjetne kompilacije. Otvorite svojstva projekta, idite na karticu «Izgradnja» i u polje «Simboli uvjetne kompilacije» upišite R2015.

Obratite pažnju da simbol trebate dodati i za Debug konfiguraciju i za Release konfiguraciju.
Dok smo već u prozoru svojstava, odmah se prebacujemo na karticu «Aplikacija» i u polje «Zadani prostor imena» i brišemo sufiks _2015, kako bismo imali univerzalni prostor imena neovisno o imenu skupa:

U mom slučaju, u konačnom proizvodu, pluginovi svih verzija skladište se u jednu mapu, tako da imena skupova ostaju s sufiksom oblika _20xx. No, možete ukloniti sufiks i iz imena skupa, ako je predviđeno da se datoteke nalaze u različitim mapama.
Prelazimo na kod datoteke Class1.cs i imitiramo neko kodiranje uzimajući u obzir različite verzije Revit-a:
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", "Pozdrav Revit 2015");
#elif R2016
TaskDialog.Show("ModPlus", "Pozdrav Revit 2016");
#elif R2017
TaskDialog.Show("ModPlus", "Pozdrav Revit 2017");
#elif R2018
TaskDialog.Show("ModPlus", "Pozdrav Revit 2018");
#elif R2019
TaskDialog.Show("ModPlus", "Pozdrav Revit 2019");
#elif R2020
TaskDialog.Show("ModPlus", "Pozdrav Revit 2020");
#endif
return Result.Succeeded;
}
}
}Odmah sam uzeo u obzir sve verzije Revit-a iznad 2015. godine (koje su bile u trenutku pisanja članka) i odmah sam uzeo u obzir prisutnost simbola uvjetne kompilacije koji se stvaraju prema istom obrascu.
Prelazimo na glavni fokus. Kreiramo novi projekt u našem rješenju, ali za verziju plugina za Revit 2016. Ponovno ponavljamo sve opisane prethodne korake, zamjenjujući broj 2015 s brojem 2016. No, datoteku Class1.cs iz novog projekta brišemo.

Datoteka s potrebnim kodom – Class1.cs – već imamo i trebamo samo umetnuti poveznicu na nju u novom projektu. Postoje dva načina umetanja poveznica:
- Dug – desnom klikom na projektu biramo opciju «Dodaj» -> «Postojeći element», u otvorenom prozoru nalazimo potrebnu datoteku i umjesto opcije «Dodaj» biramo opciju «Dodaj kao poveznicu»

- Kratko – direktno u pregledniku rješenja biramo potrebnu datoteku (ili čak datoteke. Moguće je čak i cijele mape) i povlačimo u novi projekt držeći tipku Alt. Kada povučete, vidjet ćete da će se pokazivač miša promijeniti iz plusa u strelicu kada držite tipku Alt.
UPD: Uveo sam malo zbrke u ovaj odlomak – kako biste povukli više datoteka, potrebno je držati Shift+Alt!
Nakon izvođenja postupka, u drugom projektu dobit ćemo datoteku Class1.cs s odgovarajućom ikonom (plava strelica):

Prilikom uređivanja koda u prozoru urednika također možete birati u kontekstu kojeg projekta prikazati kod, što će vam omogućiti da vidite i uređujete kod uz različite simbole uvjetne kompilacije:

Po ovoj shemi stvaramo sve ostale projekte (2017-2020). Life hack – ako povučete datoteke u pregledniku rješenja ne iz osnovnog projekta, već iz projekta u kojem su već umetnute kao poveznice, ne morate držati tipku Alt!
Opisani pristup je sasvim dobar do trenutka dodavanja nove verzije plugina ili do trenutka dodavanja novih datoteka u projekt – sve to postaje vrlo zamorno. A nedavno sam iznenada shvatio kako sve to pojednostaviti s jednim projektom i prelazimo na drugi način
Magija konfiguracija
Pročitali ste do ovdje, možete povikati «Zašto si opisivao prvi način, ako je članak odmah o drugom?!». Opisao sam sve kako bi bilo jasnije zašto su nam potrebni simboli uvjetne kompilacije i gdje se projekti razlikuju. I sada nam postaje jasnije koje razlike projekata trebamo implementirati, ostavljajući samo jedan projekt.
I kako bi sve bilo jasnije, nećemo stvarati novi projekt, već ćemo unijeti promjene u naš trenutni projekt, stvoren prvom metodom.
Dakle, prvo brišemo sve projekte iz rješenja, osim glavnog (koji sadrži izravno datoteke). To jest, projekte za verzije 2016-2020. Otvorite mapu s rješenjem i izbrisite mape ovih projekata.
U našem rješenju ostao je samo jedan projekt – MySuperPluginForRevit_2015. Otvorimo njegove svojstva i:
- Na kartici «Aplikacija» iz imena gradnje uklonite sufiks _2015 (kasnije će biti jasno zašto)
- Na kartici «Izgradnja» uklanjamo simbol uvjetne kompilacije R2015 iz odgovarajućeg polja
Napomena: u posljednjoj verziji Visual Studija postoji greška – simboli uvjetne kompilacije se ne prikazuju u prozoru svojstava projekta, iako su prisutni. Ako primijetite ovu grešku, trebate ih ručno ukloniti iz datoteke .csproj. Ipak, nastavljamo raditi s njom, pa čitamo dalje.
Preimenujemo projekt u prozoru preglednika rješenja, uklonivši sufiks _2015 i zatim uklanjamo projekt iz rješenja. To je potrebno za održavanje reda i osjećaja perfekcionista! Otvorite mapu našeg rješenja, preimenujte i mapu projekta na isti način, a zatim učitajte projekt natrag u rješenje.
Otvorite upravitelj konfiguracija. Ta konfiguracija Objava uglavnom nam neće biti potrebna, pa je uklanjamo. Kreiramo nove konfiguracije s već poznatim imenima R2015, R2016, …, R2020. Imajte na umu da ne treba kopirati parametre iz drugih konfiguracija i ne treba stvarati konfiguracije projekta:

Idemo u mapu s projektom i otvaramo datoteku s ekstenzijom .csproj u editoru po vašoj izboru. Usput, može se otvoriti i u Visual Studiju – potrebno je isključiti projekt, a zatim u kontekstnom izborniku će biti potrebna opcija:

Uređivanje u Visual Studiju je čak i poželjnije, jer editor usklađuje i daje savjete.
U datoteci ćemo vidjeti elemente – na vrhu se nalazi opći, a zatim slijede uvjetni. Ovi elementi postavljaju svojstva projekta tijekom njegove izgradnje. Prvi element, koji je bez uvjeta, postavlja opća svojstva, a elementi s uvjetima, odgovarajuće, mijenjaju neka svojstva ovisno o konfiguracijama.
Prelazimo na opći (prvi) element PropertyGroup i gledamo svojstvo AssemblyName – to je ime sastava i treba biti bez sufiksa _2015. Ako sufiks postoji, uklanjamo ga.
Pronalazimo element s uvjetom
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">Nije nam potreban – uklanjamo ga.
Element s uvjetom
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">trebat će nam za rad u fazi razvoja i ispravke koda. Možete mijenjati njegova svojstva prema vlastitim potrebama – postaviti različite izlazne putanje, mijenjati simbole uvjetne kompilacije i slično.
Sada kreiramo nove elemente PropertyGroup za naše konfiguracije. U ovim elementima potrebno je postaviti četiri svojstva:
- OutputPath – izlazna mapa. Ja postavljam standardnu vrijednost binR20xx
- DefineConstants – simboli uvjetne kompilacije. Vrijednost treba postaviti TRACE;R20xx
- TargetFrameworkVersion – verzija platforme. Za različite verzije Revit API potrebno je postaviti različite platforme.
- AssemblyName – ime skupa (tj. ime datoteke). Možete napisati točno ime skupa, ali za univerzalnost savjetujem da napišete vrijednost $(AssemblyName)_20xx. Za to smo prije uklonili sufiks iz imena skupa
Najvažnija značajka svih ovih elemenata je da ih možete jednostavno kopirati u druge projekte bez ikakvih izmjena. U nastavku ću priložiti sav sadržaj datoteke .csproj.
U redu, razjasnili smo svojstva projekta – to nije teško. Ali što učiniti s povezanim bibliotekama (NuGet paketima). Ako pogledamo dalje, vidjet ćemo da se povezane biblioteke postavljaju u elementima . No, problem je u tome što ovaj element pogrešno obrađuje uvjete, poput elementa PropertyGroup. Možda je to i greška Visual Studija, ali ako postavite više elemenata ItemGroup s uvjetima konfiguracija, a unutar njih umetnete različite reference na NuGet pakete, tada se pri promjeni konfiguracije svim navedenim paketima pridružuje projekt.
U pomoć nam dolazi element , koji radi prema poznatoj logici if-then-else.
Koristeći element Odaberi, postavljamo različite NuGet pakete za različite konfiguracije:
Sav sadržaj 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
runtimeObratite pažnju da sam u jednom od uvjeta naveo dvije konfiguracije kroz ILI (Or). Na taj način će se povezati potrebni paket prilikom konfiguracije Debug.
. I evo, gotovo je savršeno. Učitajte projekt natrag, uključite potrebnu konfiguraciju, pozovite u kontekstnom izborniku rješenja (ne projekta) stavku «Vrati sve NuGet pakete» i vidimo kako se paketi mijenjaju.

I tu sam zapao u slijepu ulicu – da bismo prikupili sve konfiguracije, mogli bismo iskoristiti paketnu izgradnju (izbornik «Izgradnja» -> «Paketna izgradnja»), ali pri prebacivanju konfiguracija ne dolazi do automatskog vraćanja paketa. Također se ne događa ni prilikom izgradnje projekta, iako bi, po logici, trebalo. Rješenje ovog problema standardnim sredstvima nisam uspio pronaći. I vjerojatno je to također bag Visual Studija.
Zato je za paketnu izgradnju odlučeno koristiti poseban sustav automatizirane izgradnje . U stvari, to nisam želio, jer smatram da je to suvišno u okviru razvoja dodataka, ali trenutno ne vidim drugo rješenje. A na pitanje «Zašto upravo Nuke?» odgovor je jednostavan – koristimo ga na poslu.
Dakle, prelazimo u mapu našeg rješenja (ne projekta), držimo pritisnutu tipku Shift i desnom tipkom miša kliknemo na prazno mjesto u mapi – u kontekstnom izborniku odaberemo stavku «Otvori PowerShell prozor ovdje».

Ako nemate instaliran nuke, prvo upišite naredbu
dotnet tool install Nuke.GlobalTool –globalSada upišite naredbu nuke i bit će vam ponuđeno da postavite nuke za trenutni projekt. Ne znam kako bi se to pravilnije napisalo na hrvatskom jeziku – na engleskom bi pisalo Could not find .nuke file. Do you want to setup a build? [y/n]
Pritisnemo tipku Y i slijedi niz postavki. Treba nam najjednostavnija opcija s korištenjem MSBuild, pa odgovaramo kao na slici:

Prelazimo u Visual Studio, koja će nam ponuditi da ponovno učitamo rješenje, jer je dodan novi projekt. Ponovno učitavamo rješenje i vidimo da imamo projekt build u kojem nas zanima samo jedna datoteka – Build.cs

Otvorimo tu datoteku i napišemo skriptu za izgradnju projekta pod sve konfiguracije. Ili koristimo moju skriptu, koju možete urediti prema svojim potrebama:
koristeći System.IO;
koristeći Nuke.Common;
koristeći Nuke.Common.Execution;
koristeći Nuke.Common.ProjectModel;
koristeći Nuke.Common.Tools.MSBuild;
koristeći static Nuke.Common.Tools.MSBuild.MSBuildTasks;
[CheckBuildProjectConfigurations]
[UnsetVisualStudioEnvironmentVariables]
klasa Build : NukeBuild
{
javna statička int Main () => Execute<Build>(x => x.Compile);
[Solution] u odnosu Solution;
\/\/ Ako se naziv rešenja i naziv projekta (plugin) razlikuju, ovde navedite naziv projekta (plugina)
string PluginName => Solution.Name;
Cilj Compile => _ => _
.Executes(() =>
{
var project = Solution.GetProject(PluginName);
if (project == null)
throw new FileNotFoundException("Nije pronađeno!");
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($"Konfiguracija: {configuration}");
build.Add(configuration);
MSBuild(_ => _
.SetProjectFile(project.Path)
.SetConfiguration(configuration)
.SetTargets("Restore"));
MSBuild(_ => _
.SetProjectFile(project.Path)
.SetConfiguration(configuration)
.SetTargets("Rebuild"));
}
});
}Vraćamo se u PowerShell prozor i ponovo kucamo komandu nuke (možete kucati komandu nuke sa potrebnim Ciljem. Ali imamo jedan Ciljem, koji se pokreće po defaultu). Nakon pritiska na taster Enter, osećaćemo se kao pravi hakeri, jer će se kao u filmu automatski graditi naš projekat pod različitim konfiguracijama.
Inače, možete koristiti PowerShell direktno iz Visual Studija (menij "Prikaz» -> «Druge prozore» -> «Konzola upravitelja paketa»), ali će sve biti crno-bele, što nije baš praktično.
Time se završava moj članak. Siguran sam da ćete se sami snaći s opcijom za AutoCAD. Nadam se da će ovde izloženi materijal naći svoje 'klijente'.
Hvala na pažnji!
Izvor: habr.com
