By it ûntwikkeljen fan plugins foar CAD-applikaasjes (
As jo mar ien plugin hawwe of jo binne noch in sels-learde begjinner yn dizze saak, kinne jo gewoan in kopy fan it projekt meitsje, de nedige plakken dêryn feroarje en in nije ferzje fan 'e plugin gearstalle. Dêrtroch sille opfolgjende wizigingen yn 'e koade in meardere ferheging fan' e arbeidskosten meibringe.
As jo ûnderfining en kennis krije, sille jo ferskate manieren fine om dit proses te automatisearjen. Ik rûn dit paad en ik wol jo fertelle wêr't ik mei kaam en hoe handich it is.
Litte wy earst nei in metoade sjen dy't fanselssprekkend is en dy't ik in lange tiid haw brûkt.
Keppelings nei projekt triemmen
En om alles ienfâldich, fisueel en begryplik te meitsjen, sil ik alles beskriuwe mei in abstrakt foarbyld fan pluginûntwikkeling.
Litte wy Visual Studio iepenje (ik haw de Community 2019-ferzje. En ja - yn it Russysk) en meitsje in nije oplossing. Litte wy him skilje MySuperPluginForRevit
Wy sille in plugin meitsje foar Revit foar ferzjes 2015-2020. Lit ús dêrom in nij projekt meitsje yn 'e oplossing (Net Framework Class Library) en neame it MySuperPluginForRevit_2015
Wy moatte keppelings tafoegje oan de Revit API. Fansels kinne wy keppelings tafoegje oan lokale bestannen (wy moatte alle nedige SDK's of alle ferzjes fan Revit ynstallearje), mar wy sille fuortendaliks it goeie paad folgje en it NuGet-pakket ferbine. Jo kinne nochal in pear pakketten fine, mar ik sil myn eigen brûke.
Nei it ferbinen fan it pakket, klikje jo mei de rechtermuisknop op it item "referinsjes"en selektearje it item"Ferpleats packages.config nei PackageReference...»
As jo ynienen op dit punt begjinne te panyk, want yn it finster fan pakketeigenskippen sil d'r gjin wichtich item wêze "Kopiearje lokaal", dy't wy perfoarst op 'e wearde moatte ynstelle falsk, dan net panyk - gean nei de map mei it projekt, iepenje de triem mei de .csproj tafoeging yn in bewurker handich foar jo (ik brûk Notepad ++) en fyn in yngong oer ús pakket dêr. Se sjocht der no sa út:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
</PackageReference>
Foegje in eigendom ta runtime. It sil sa útkomme:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
No, by it bouwen fan in projekt, wurde bestannen fan it pakket net kopiearre nei de útfiermap.
Litte wy fierder gean - lit ús ús daliks foarstelle dat ús plugin wat sil brûke fan 'e Revit API, dy't yn' e rin fan 'e tiid feroare is as nije ferzjes binne frijlitten. No, of wy moatte gewoan wat feroarje yn 'e koade ôfhinklik fan' e ferzje fan Revit wêrfoar wy de plugin meitsje. Om sokke ferskillen yn koade op te lossen, sille wy betingste kompilaasjesymboalen brûke. Iepenje de projekteigenskippen, gean nei de "ljepperAssembly"en yn it fjild"Betingst kompilaasje notaasje"litte wy skriuwe R2015.
Tink derom dat it symboal tafoege wurde moat foar sawol de Debug- en Release-konfiguraasjes.
No, wylst wy yn it eigenskippenfinster binne, geane wy fuortendaliks nei de " ljepperApplikaasje"en yn it fjild"Standert nammeromte» it efterheaksel fuortsmite _2015sadat ús nammeromte universeel is en ûnôfhinklik fan 'e gearkomstenamme:
Yn myn gefal, yn it definitive produkt, wurde plugins fan alle ferzjes yn ien map pleatst, sadat myn gearkomstenammen bliuwe mei it efterheaksel fan it formulier _20хх. Mar jo kinne it efterheaksel ek fuortsmite fan 'e gearstallingsnamme as de bestannen yn ferskate mappen sitte moatte.
Litte wy nei de triemkoade gean Class1.cs en simulearje dêr wat koade, rekken hâldend mei ferskate ferzjes fan 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;
}
}
}
Ik haw fuortendaliks rekken holden mei alle ferzjes fan Revit boppe ferzje 2015 (dy't beskikber wiene op it stuit fan skriuwen) en fuortendaliks rekken holden mei de oanwêzigens fan betingsten kompilaasje symboalen, dy't makke binne mei deselde sjabloan.
Lit ús gean nei it wichtichste hichtepunt. Wy meitsje in nij projekt yn ús oplossing, allinich foar de ferzje fan it plugin foar Revit 2016. Wy werhelje alle hjirboppe beskreaune stappen, respektivelik, it ferfangen fan it nûmer 2015 mei it nûmer 2016. Mar it bestân Class1.cs wiskje út it nije projekt.
Triem mei de fereaske koade - Class1.cs - wy hawwe it al en wy moatte gewoan in keppeling nei it yn in nij projekt ynfoegje. D'r binne twa manieren om keppelings yn te foegjen:
- Lang - Rjochts-klikke op it projekt en selektearje "Add»->«Besteande elemint"Fyn yn it finster dat iepent de fereaske triem en ynstee fan de opsje "Add"selektearje de opsje"Foegje as ferbining ta»
- Koart - direkt yn 'e oplossingsûntdekker, selektearje it winske bestân (of sels bestannen, of sels folsleine mappen) en sleep it nei in nij projekt wylst jo de Alt-toets yndrukt hâlde. As jo slepe, sille jo sjen dat as jo op 'e Alt-toets drukke, de mûsoanwizer feroaret fan in plusteken nei in pylk.
UPD: Ik makke in bytsje betizing yn dizze paragraaf - om ferskate bestannen oer te dragen moatte jo yndrukt hâlde Shift + Alt!
Nei it útfieren fan de proseduere sille wy in bestân hawwe yn it twadde projekt Class1.cs mei it oerienkommende ikoan (blauwe pylk):
By it bewurkjen fan koade yn it bewurkingsfinster kinne jo ek kieze yn hokker projektkontekst de koade werjûn wurde sil, wêrtroch jo de koade kinne sjen dy't bewurke wurdt ûnder ferskate betingste kompilaasjesymboalen:
Wy meitsje alle oare projekten (2017-2020) mei dit skema. Life hack - as jo bestannen yn 'e Solution Explorer slepe net fan it basisprojekt, mar fan it projekt wêr't se al binne ynfoege as in keppeling, dan hoege jo de Alt-toets net yn te hâlden!
De beskreaune opsje is frij goed oant it momint fan it tafoegjen fan in nije ferzje fan 'e plugin of oant it momint fan it tafoegjen fan nije bestannen oan it projekt - dit alles wurdt heul ferfeelsum. En koartlyn realisearre ik my ynienen ynienen hoe't jo it allegear mei ien projekt kinne sortearje en wy geane troch nei de twadde metoade
De magy fan konfiguraasjes
Nei it lêzen hjir, kinne jo útroppen: "Wêrom hawwe jo de earste metoade beskreaun, as it artikel daliks oer de twadde giet?!" En ik beskreau alles om dúdliker te meitsjen wêrom't wy betingsten kompilaasjesymboalen nedich binne en op hokker plakken ús projekten ferskille. En no wurdt it ús krekt dúdliker hokker ferskillen yn projekten wy moatte útfiere, en bliuwt mar ien projekt oer.
En om alles dúdliker te meitsjen, sille wy gjin nij projekt oanmeitsje, mar wizigingen meitsje oan ús hjoeddeistige projekt makke op 'e earste manier.
Dat, earst fan alles, ferwiderje wy alle projekten út 'e oplossing, útsein de wichtichste (mei de bestannen direkt). Dy. projekten foar ferzjes 2016-2020. Iepenje de map mei de oplossing en wiskje dêr de mappen fan dizze projekten.
Wy hawwe ien projekt oer yn ús beslút - MySuperPluginForRevit_2015. Iepenje syn eigenskippen en:
- Op de ljepper "Applikaasje"ferwiderje it efterheaksel fan 'e gearstallingsnamme _2015 (it sil letter dúdlik wurde wêrom)
- Op de ljepper "Assembly» fuortsmite it betingstlike kompilaasjesymboal R2015 út it oerienkommende fjild
Opmerking: de lêste ferzje fan Visual Studio hat in brek - symboalen foar betingst kompilaasje wurde net werjûn yn it finster fan projekteigenskippen, hoewol se beskikber binne. As jo dizze glitch ûnderfine, dan moatte jo se manuell fuortsmite fan it .csproj-bestân. Wy moatte der lykwols noch yn wurkje, dus lês fierder.
Omneame it projekt yn it finster fan Solution Explorer troch it efterheaksel te ferwiderjen _2015 en dan fuortsmite it projekt út de oplossing. Dit is nedich om oarder en gefoelens fan perfeksjonisten te behâlden! Wy iepenje de map fan ús oplossing, omneame de projektmap dêr op deselde manier en laden it projekt werom yn 'e oplossing.
Iepenje de konfiguraasjebehearder. Amerikaanske konfiguraasje release yn prinsipe sil it net nedich wêze, dus wy wiskje it. Wy meitsje nije konfiguraasjes mei nammen dy't ús al bekend binne R2015, R2016, ..., R2020. Tink derom dat jo gjin ynstellings hoege te kopiearjen fan oare konfiguraasjes en jo hoege gjin projektkonfiguraasjes te meitsjen:
Gean nei de map mei it projekt en iepenje it bestân mei de tafoeging .csproj yn in foar jo handich bewurker. Trouwens, jo kinne it ek iepenje yn Visual Studio - jo moatte it projekt ûntladen en dan sil it winske item yn it kontekstmenu wêze:
Bewurkje yn Visual Studio is sels de foarkar, om't de bewurker sawol rjochtet as freget.
Yn it bestân sille wy de eleminten sjen
Gean nei it mienskiplike (earste) elemint PropertyGroup en sjoch nei it eigendom AssemblyName - dit is de namme fan 'e gearkomste en wy moatte it sûnder efterheaksel hawwe _2015. As d'r in efterheaksel is, ferwiderje it dan.
It finen fan in elemint mei in betingst
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
Wy hawwe it net nedich - wy wiskje it.
Elemint mei betingst
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
sil nedich wêze om te wurkjen op it poadium fan koadeûntwikkeling en debuggen. Jo kinne de eigenskippen feroarje om oan jo behoeften te passen - ferskate útfierpaden ynstelle, betingste kompilaasjesymboalen feroarje, ensfh.
Litte wy no nije eleminten oanmeitsje PropertyGroup foar ús konfiguraasjes. Yn dizze eleminten moatte wy gewoan fjouwer eigenskippen ynstelle:
- OutputPath - útfier map. Ik set de standert wearde binR20xx
- DefineConstants - betingstlike kompilaasjesymboalen. De wearde moat wurde oantsjutte TRACE;R20хх
- TargetFrameworkVersion - platfoarm ferzje. Ferskillende ferzjes fan 'e Revit API fereaskje dat ferskate platfoarms wurde spesifisearre.
- AssemblyName - gearstallingsnamme (d.w.s. bestânsnamme). Jo kinne de krekte namme fan 'e gearkomste skriuwe, mar foar veelzijdigheid advisearje ik de wearde te skriuwen $(AssemblyName)_20хх. Om dit te dwaan, hawwe wy earder it efterheaksel fuortsmiten fan 'e gearkomstenamme
It wichtichste skaaimerk fan al dizze eleminten is dat se gewoan kinne wurde kopieare nei oare projekten sûnder se hielendal te feroarjen. Letter yn it artikel sil ik alle ynhâld fan it .csproj-bestân taheakje.
Okee, wy hawwe de eigenskippen fan it projekt útfûn - it is net dreech. Mar wat te dwaan mei plug-in biblioteken (NuGet pakketten). As wy fierder sjogge, sille wy sjen dat de opnommen bibleteken binne spesifisearre yn 'e eleminten
It elemint komt ús te helpen
It brûken fan elemint Kieze, sette wy ferskate NuGet-pakketten yn foar ferskate konfiguraasjes:
Alle ynhâld 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>
Tink derom dat ik yn ien fan 'e betingsten twa konfiguraasjes spesifisearre fia OF. Op dizze manier sil it fereaske pakket ferbûn wurde tidens konfiguraasje Debug.
En hjir hawwe wy hast alles perfekt. Wy laden it projekt werom, skeakelje de konfiguraasje yn dy't wy nedich binne, neame it item " yn it kontekstmenu fan 'e oplossing (net it projekt)Weromsette alle NuGet-pakketten"en wy sjogge hoe't ús pakketten feroarje.
En op dit stadium kaam ik oan in deade ein - om alle konfiguraasjes yn ien kear te sammeljen, koene wy batchmontage brûke (menu "Assembly»->«Batch bouwe"), mar by it wikseljen fan konfiguraasjes wurde pakketten net automatysk weromset. En by it gearstallen fan it projekt bart dit ek net, hoewol it yn teory moat. Ik haw net fûn in oplossing foar dit probleem mei help fan standert middels. En nei alle gedachten is dit ek in Visual Studio-bug.
Dêrom, foar batch gearkomste, waard besletten om te brûken in spesjale automatisearre assembly systeem
Dus, gean nei de map fan ús oplossing (net it projekt), hâld de kaai yn Shift en rjochts-klikke op in lege romte yn 'e map - selektearje yn it kontekstmenu it item "Iepenje PowerShell-finster hjir".
As jo it net ynstalleare hawwe nuke, skriuw dan earst it kommando
dotnet tool install Nuke.GlobalTool –global
Skriuw no it kommando nuke en jo wurde frege om te konfigurearjen nuke foar it hjoeddeiske projekt. Ik wit net hoe't ik dit korrekter skriuwe yn it Russysk - yn it Ingelsk sil it skreaun wurde Koe .nuke-bestân net fine. Wolle jo in build ynstelle? [y/n]
Druk op de Y-kaai en dan sille d'r direkte ynstellings items wêze. Wy moatte de ienfâldichste opsje brûke MSBuild, dus wy antwurdzje lykas yn 'e skermôfbylding:
Litte wy nei Visual Studio gean, dy't ús sil freegje de oplossing opnij te laden, om't der in nij projekt oan is tafoege. Wy laden de oplossing opnij en sjogge dat wy in projekt hawwe bouwe wêryn wy binne ynteressearre yn mar ien triem - Build.cs
Iepenje dit bestân en skriuw in skript om it projekt te bouwen foar alle konfiguraasjes. No, of brûk myn skript, dat jo kinne bewurkje om oan jo behoeften te passen:
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"));
}
});
}
Wy geane werom nei it PowerShell-finster en skriuwe it kommando opnij nuke (jo kinne it kommando skriuwe nuke oanjout de fereaske Doel. Mar wy hawwe ien Doel, dy't standert rint). Nei it drukken op de Enter-kaai sille wy fiele as echte hackers, om't, lykas yn in film, ús projekt automatysk wurdt gearstald foar ferskate konfiguraasjes.
Trouwens, jo kinne PowerShell direkt brûke fan Visual Studio (menu "view»->«Oare finsters»->«Package Manager Console"), mar alles sil yn swart en wyt wêze, wat net heul handich is.
Dit konkludearret myn artikel. Ik bin der wis fan dat jo de opsje foar AutoCAD sels kinne útfine. Ik hoopje dat it hjir presintearre materiaal syn "kliïnten" sil fine.
Спасибо за внимание!
Boarne: www.habr.com