Pri vývoji pluginov pre CAD aplikácie (
Keď máte iba jeden plugin alebo ste v tejto veci ešte len samouk, môžete si jednoducho vytvoriť kópiu projektu, zmeniť v ňom potrebné miesta a zostaviť novú verziu pluginu. V súlade s tým budú následné zmeny kódexu viesť k viacnásobnému zvýšeniu nákladov práce.
Keď získate skúsenosti a znalosti, nájdete niekoľko spôsobov, ako tento proces zautomatizovať. Kráčal som touto cestou a chcem vám povedať, k čomu som dospel a aké je to pohodlné.
Najprv sa pozrime na metódu, ktorá je zrejmá a ktorú používam už dlho.
Odkazy na súbory projektu
A aby bolo všetko jednoduché, vizuálne a zrozumiteľné, všetko popíšem na abstraktnom príklade vývoja pluginu.
Otvorme Visual Studio (mám verziu Community 2019. A áno – v ruštine) a vytvoríme nové riešenie. Zavolajme mu MySuperPluginForRevit
Budeme robiť plugin pre Revit pre verzie 2015-2020. Vytvorme si preto nový projekt v riešení (Net Framework Class Library) a nazvime ho MySuperPluginForRevit_2015
Potrebujeme pridať odkazy na Revit API. Samozrejme, môžeme pridať odkazy na lokálne súbory (budeme musieť nainštalovať všetky potrebné SDK alebo všetky verzie Revitu), ale hneď pôjdeme po správnej ceste a pripojíme balíček NuGet. Balíčkov nájdete pomerne dosť, ale ja použijem svoje.
Po pripojení balíka kliknite pravým tlačidlom myši na položku „referencie"a vyberte položku"Presunúť packages.config do PackageReference...»
Ak zrazu v tomto bode začnete panikáriť, pretože v okne vlastností balíka nebude žiadna dôležitá položka “Skopírujte lokálne“, ktorú určite musíme nastaviť na hodnotu nepravdivý, potom neprepadajte panike - prejdite do priečinka s projektom, otvorte súbor s príponou .csproj v editore, ktorý vám vyhovuje (ja používam Notepad++) a nájdite tam záznam o našom balíku. Teraz vyzerá takto:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
</PackageReference>
Pridajte k nemu vlastnosť beh programu. Dopadne to takto:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
Teraz, keď vytvárate projekt, súbory z balíka sa neskopírujú do výstupného priečinka.
Poďme ďalej – hneď si predstavme, že náš plugin bude využívať niečo z Revit API, čo sa časom zmenilo, keď vyšli nové verzie. Alebo len potrebujeme niečo zmeniť v kóde v závislosti od verzie Revitu, pre ktorú robíme plugin. Na vyriešenie takýchto rozdielov v kóde použijeme symboly podmienenej kompilácie. Otvorte vlastnosti projektu, prejdite na kartu „zhromaždenia"a v teréne"Zápis podmienenej kompilácie"píšme R2015.
Všimnite si, že symbol musí byť pridaný pre konfigurácie Debug aj Release.
Keď sme v okne vlastností, okamžite prejdeme na kartu „Aplikácia"a v teréne"Predvolený priestor názvov» odstráňte príponu _2015takže náš menný priestor je univerzálny a nezávislý od názvu zostavy:
V mojom prípade sú v konečnom produkte pluginy všetkých verzií vložené do jedného priečinka, takže moje názvy zostáv ostávajú s príponou formulára _20хх. Môžete však tiež odstrániť príponu z názvu zostavy, ak sa majú súbory nachádzať v rôznych priečinkoch.
Poďme na kód súboru Trieda1.cs a simulovať tam nejaký kód, berúc do úvahy rôzne verzie Revitu:
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;
}
}
}
Okamžite som vzal do úvahy všetky verzie Revitu nad verziou 2015 (ktoré boli k dispozícii v čase písania) a okamžite som vzal do úvahy prítomnosť symbolov podmienenej kompilácie, ktoré sú vytvorené pomocou rovnakej šablóny.
Prejdime k hlavnému highlightu. V našom riešení vytvoríme nový projekt, len pre verziu pluginu pre Revit 2016. Zopakujeme všetky kroky popísané vyššie, respektíve nahradíme číslo 2015 číslom 2016. Súbor však Trieda1.cs vymazať z nového projektu.
Súbor s požadovaným kódom - Trieda1.cs – už ho máme a stačí nám naň vložiť odkaz do nového projektu. Existujú dva spôsoby vloženia odkazov:
- Dlhé – kliknite pravým tlačidlom myši na projekt a vyberte „Pridať»->«Existujúci prvok", v okne, ktoré sa otvorí, nájdite požadovaný súbor a namiesto možnosti "Pridať"vyberte možnosť"Pridať ako pripojenie»
- krátky – priamo v prieskumníkovi riešení vyberte požadovaný súbor (alebo aj súbory, alebo dokonca celé priečinky) a pretiahnite ho do nového projektu so stlačeným klávesom Alt. Počas ťahania uvidíte, že keď stlačíte kláves Alt, kurzor myši sa zmení zo znamienka plus na šípku.
UPS: V tomto odseku som urobil malý zmätok - ak chcete preniesť niekoľko súborov, mali by ste ho podržať Shift + Alt!
Po vykonaní postupu budeme mať súbor v druhom projekte Trieda1.cs s príslušnou ikonou (modrá šípka):
Pri úprave kódu v okne editora si tiež môžete vybrať, v ktorom kontexte projektu sa má kód zobraziť, čo vám umožní vidieť upravovaný kód pod rôznymi symbolmi podmienenej kompilácie:
Všetky ostatné projekty (2017-2020) vytvárame pomocou tejto schémy. Life hack - ak pretiahnete súbory v Prieskumníkovi riešení nie zo základného projektu, ale z projektu, kde sú už vložené ako odkaz, potom nemusíte držať kláves Alt!
Opísaná možnosť je celkom dobrá až do okamihu pridania novej verzie pluginu alebo do okamihu pridania nových súborov do projektu - to všetko sa stáva veľmi únavným. A nedávno som si zrazu uvedomil, ako to všetko vyriešiť jedným projektom a prechádzame na druhý spôsob
Kúzlo konfigurácií
Po dočítaní tohto textu môžete zvolať: „Prečo ste opísali prvú metódu, ak je článok hneď o druhej? A všetko som opísal, aby bolo jasnejšie, prečo potrebujeme symboly podmienenej kompilácie a na ktorých miestach sa naše projekty líšia. A teraz je nám presnejšie, aké rozdiely v projektoch musíme realizovať, pričom zostáva len jeden projekt.
A aby bolo všetko jasnejšie, nebudeme vytvárať nový projekt, ale urobíme zmeny v našom aktuálnom projekte, ktorý bol vytvorený prvým spôsobom.
Takže v prvom rade odstránime všetky projekty z riešenia okrem hlavného (obsahujúceho priamo súbory). Tie. projekty pre verzie 2016-2020. Otvorte priečinok s riešením a vymažte tam priečinky týchto projektov.
V našom rozhodnutí nám zostáva jeden projekt - MySuperPluginForRevit_2015. Otvorte jeho vlastnosti a:
- Na karte „Aplikácia"odstráňte príponu z názvu zostavy _2015 (neskôr sa ukáže prečo)
- Na karte „zhromaždenia» odstráňte symbol podmienenej kompilácie R2015 z príslušného poľa
Poznámka: Najnovšia verzia Visual Studio má chybu - symboly podmienenej kompilácie sa nezobrazujú v okne vlastností projektu, hoci sú k dispozícii. Ak sa stretnete s touto chybou, musíte ich manuálne odstrániť zo súboru .csproj. Musíme v ňom však ešte popracovať, tak čítajte ďalej.
Premenujte projekt v okne Solution Explorer odstránením prípony _2015 a potom odstráňte projekt z riešenia. To je potrebné na udržanie poriadku a pocitov perfekcionistov! Otvoríme priečinok nášho riešenia, premenujeme tam priečinok projektu rovnakým spôsobom a načítame projekt späť do riešenia.
Otvorte správcu konfigurácie. US konfigurácia Verzia v zásade to nebude potrebné, preto ho vymažeme. Vytvárame nové konfigurácie s názvami, ktoré sú nám už známe R2015, R2016, ..., R2020. Upozorňujeme, že nemusíte kopírovať nastavenia z iných konfigurácií a nemusíte vytvárať konfigurácie projektu:
Prejdite do priečinka s projektom a otvorte súbor s príponou .csproj v editore, ktorý vám vyhovuje. Mimochodom, môžete ho otvoriť aj vo Visual Studio - musíte projekt vyložiť a potom bude požadovaná položka v kontextovej ponuke:
Úpravy vo Visual Studiu sú ešte výhodnejšie, pretože editor zarovnáva aj výzvy.
V súbore uvidíme prvky
Prejdite na spoločný (prvý) prvok PropertyGroup a pozrite sa na nehnuteľnosť AssemblyName – to je názov zhromaždenia a mali by sme ho mať bez prípony _2015. Ak existuje prípona, odstráňte ju.
Nájdenie prvku s podmienkou
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
Nepotrebujeme to - vymažeme to.
Prvok s podmienkou
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
budú potrebné na prácu vo fáze vývoja kódu a ladenia. Jeho vlastnosti môžete zmeniť podľa svojich potrieb – nastaviť rôzne výstupné cesty, zmeniť symboly podmienenej kompilácie atď.
Teraz vytvoríme nové prvky PropertyGroup pre naše konfigurácie. V týchto prvkoch stačí nastaviť štyri vlastnosti:
- Výstupná cesta – výstupný priečinok. Nastavil som predvolenú hodnotu binR20xx
- DefineConstants – symboly podmienenej kompilácie. Hodnota by mala byť špecifikovaná TRACE;R20хх
- TargetFrameworkVersion – platformová verzia. Rôzne verzie rozhrania Revit API vyžadujú špecifikáciu rôznych platforiem.
- AssemblyName – názov zostavy (t. j. názov súboru). Môžete napísať presný názov zostavy, ale pre univerzálnosť odporúčam napísať hodnotu $(AssemblyName)_20хх. Aby sme to urobili, predtým sme odstránili príponu z názvu zostavy
Najdôležitejšou vlastnosťou všetkých týchto prvkov je, že ich možno jednoducho skopírovať do iných projektov bez toho, aby sa vôbec zmenili. Neskôr v článku pripojím celý obsah súboru .csproj.
Dobre, zistili sme vlastnosti projektu - nie je to ťažké. Čo však robiť s knižnicami doplnkov (balíčky NuGet). Ak sa pozrieme ďalej, uvidíme, že zahrnuté knižnice sú špecifikované v prvkoch
Živel nám prichádza na pomoc
Použitie prvku Vybrať, nastavili sme rôzne balíčky NuGet pre rôzne konfigurácie:
Celý obsah csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5AD738D6-4122-4E76-B865-BE7CE0F6B3EB}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MySuperPluginForRevit</RootNamespace>
<AssemblyName>MySuperPluginForRevit</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>binDebug</OutputPath>
<DefineConstants>DEBUG;R2015</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2015|AnyCPU' ">
<OutputPath>binR2015</OutputPath>
<DefineConstants>TRACE;R2015</DefineConstants>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2015</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2016|AnyCPU' ">
<OutputPath>binR2016</OutputPath>
<DefineConstants>TRACE;R2016</DefineConstants>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2016</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2017|AnyCPU' ">
<OutputPath>binR2017</OutputPath>
<DefineConstants>TRACE;R2017</DefineConstants>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2017</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2018|AnyCPU' ">
<OutputPath>binR2018</OutputPath>
<DefineConstants>TRACE;R2018</DefineConstants>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2018</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2019|AnyCPU' ">
<OutputPath>binR2019</OutputPath>
<DefineConstants>TRACE;R2019</DefineConstants>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2019</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'R2020|AnyCPU' ">
<OutputPath>binR2020</OutputPath>
<DefineConstants>TRACE;R2020</DefineConstants>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<AssemblyName>$(AssemblyName)_2020</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Class1.cs" />
<Compile Include="PropertiesAssemblyInfo.cs" />
</ItemGroup>
<Choose>
<When Condition=" '$(Configuration)'=='R2015' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2016' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2016">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2017' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2017">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2018' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2018">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2019' ">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2019">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
<When Condition=" '$(Configuration)'=='R2020' or '$(Configuration)'=='Debug'">
<ItemGroup>
<PackageReference Include="ModPlus.Revit.API.2020">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(MSBuildToolsPath)Microsoft.CSharp.targets" />
</Project>
Upozorňujeme, že v jednej z podmienok som špecifikoval dve konfigurácie cez ALEBO. Týmto spôsobom sa počas konfigurácie pripojí požadovaný balík Ladiť.
A tu máme takmer všetko dokonalé. Načítame projekt späť, povolíme konfiguráciu, ktorú potrebujeme, zavoláme položku “ v kontextovom menu riešenia (nie projektu)Obnovte všetky balíčky NuGet"a vidíme, ako sa naše balíčky menia.
A v tejto fáze som sa dostal do slepej uličky - aby sme mohli zhromaždiť všetky konfigurácie naraz, mohli by sme použiť dávkovú montáž (menu "zhromaždenia»->«Dávkové zostavenie"), ale pri zmene konfigurácií sa balíky automaticky neobnovia. A pri zostavovaní projektu k tomu tiež nedochádza, hoci by sa teoreticky malo. Nenašiel som riešenie tohto problému pomocou štandardných prostriedkov. A s najväčšou pravdepodobnosťou je to tiež chyba Visual Studio.
Preto sa pre dávkovú montáž rozhodlo použiť špeciálny automatizovaný montážny systém
Prejdite teda do priečinka nášho riešenia (nie projektu), podržte kláves smena a kliknite pravým tlačidlom myši na prázdne miesto v priečinku - v kontextovej ponuke vyberte položku „Tu otvorte okno PowerShell".
Ak ho nemáte nainštalovaný Nuke, potom najprv napíšte príkaz
dotnet tool install Nuke.GlobalTool –global
Teraz napíšte príkaz Nuke a budete vyzvaní na konfiguráciu Nuke pre aktuálny projekt. Neviem, ako to správne napísať v ruštine - v angličtine to bude napísané Nepodarilo sa nájsť súbor .nuke. Chcete nastaviť zostavu? [y/n]
Stlačte kláves Y a potom budú priame položky nastavení. Potrebujeme najjednoduchšiu možnosť použitia MSBuild, takže odpovieme ako na snímke obrazovky:
Poďme do Visual Studia, ktoré nás vyzve na opätovné načítanie riešenia, keďže doň pribudol nový projekt. Znovu načítame riešenie a vidíme, že máme projekt vybudovať v ktorom nás zaujíma iba jeden súbor - Build.cs
Otvorte tento súbor a napíšte skript na zostavenie projektu pre všetky konfigurácie. Alebo použite môj skript, ktorý si môžete upraviť podľa svojich potrieb:
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;
// If the solution name and the project (plugin) name are different, then indicate the project (plugin) name here
string PluginName => Solution.Name;
Target Compile => _ => _
.Executes(() =>
{
var project = Solution.GetProject(PluginName);
if (project == null)
throw new FileNotFoundException("Not found!");
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($"Configuration: {configuration}");
build.Add(configuration);
MSBuild(_ => _
.SetProjectFile(project.Path)
.SetConfiguration(configuration)
.SetTargets("Restore"));
MSBuild(_ => _
.SetProjectFile(project.Path)
.SetConfiguration(configuration)
.SetTargets("Rebuild"));
}
});
}
Vrátime sa do okna PowerShell a príkaz napíšeme znova Nuke (môžete napísať príkaz Nuke s uvedením požadovaného Cieľ. Ale máme jeden Cieľ, ktorý sa štandardne spúšťa). Po stlačení klávesy Enter sa budeme cítiť ako skutoční hackeri, pretože ako vo filme sa náš projekt automaticky zostaví pre rôzne konfigurácie.
Mimochodom, PowerShell môžete použiť priamo z Visual Studia (ponuka "Pohľad»->«Ostatné okná»->«Konzola správcu balíkov“), ale všetko bude čiernobiele, čo nie je príliš výhodné.
Týmto končím môj článok. Som si istý, že môžete prísť na možnosť pre AutoCAD sami. Dúfam, že si tu prezentovaný materiál nájde svojich „klientov“.
Ďakujem vám za pozornosť!
Zdroj: hab.com