Quan es desenvolupen connectors per a aplicacions CAD (
Quan només tens un connector o encara ets un principiant autodidacte en aquesta matèria, només pots fer una còpia del projecte, canviar-hi els llocs necessaris i muntar una nova versió del connector. En conseqüència, els canvis posteriors al codi comportaran un augment múltiple dels costos laborals.
A mesura que adquireixis experiència i coneixements, trobaràs diverses maneres d'automatitzar aquest procés. He fet aquest camí i us vull explicar amb què he acabat i com de convenient és.
En primer lloc, mirem un mètode que és evident i que he utilitzat durant molt de temps.
Enllaços als fitxers del projecte
I perquè tot sigui senzill, visual i entenedor, ho descriuré tot utilitzant un exemple abstracte de desenvolupament de complements.
Obrim Visual Studio (tinc la versió Community 2019. I sí, en rus) i creem una solució nova. Anem a cridar-lo MySuperPluginForRevit
Farem un complement per a Revit per a les versions 2015-2020. Per tant, creem un nou projecte a la solució (Net Framework Class Library) i l'anomenem MySuperPluginForRevit_2015
Hem d'afegir enllaços a l'API de Revit. Per descomptat, podem afegir enllaços a fitxers locals (haurem d'instal·lar tots els SDK necessaris o totes les versions de Revit), però de seguida seguirem el camí correcte i connectarem el paquet NuGet. Podeu trobar uns quants paquets, però jo faré servir els meus.
Després de connectar el paquet, feu clic amb el botó dret a l'element "Referències"i seleccioneu l'element"Mou packages.config a PackageReference...»
Si de sobte en aquest punt comences a entrar en pànic, perquè a la finestra de propietats del paquet no hi haurà cap element important "Copia localment", que definitivament hem d'ajustar al valor false, aleshores no us espanteu: aneu a la carpeta amb el projecte, obriu el fitxer amb l'extensió .csproj en un editor que us convingui (faig servir Notepad++) i hi trobareu una entrada sobre el nostre paquet. Ara es veu així:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
</PackageReference>
Afegeix-hi una propietat temps d'execució. Sortirà així:
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
Ara, quan es construeix un projecte, els fitxers del paquet no es copiaran a la carpeta de sortida.
Anem més enllà: imaginem immediatament que el nostre connector utilitzarà alguna cosa de l'API de Revit, que ha canviat amb el temps quan s'han llançat noves versions. Bé, o només hem de canviar alguna cosa al codi en funció de la versió de Revit per a la qual estem fent el connector. Per resoldre aquestes diferències en el codi, utilitzarem símbols de compilació condicional. Obriu les propietats del projecte, aneu a la pestanya “assemblea"i al camp"Notació de compilació condicional"Escrivim R2015.
Tingueu en compte que el símbol s'ha d'afegir tant per a les configuracions de depuració com per a la versió.
Bé, mentre estem a la finestra de propietats, anem immediatament a la pestanya “Aplicació"i al camp"Espai de noms per defecte» elimina el sufix _2015de manera que el nostre espai de noms sigui universal i independent del nom de l'assemblea:
En el meu cas, al producte final, els connectors de totes les versions es posen en una carpeta, de manera que els meus noms de conjunt es mantenen amb el sufix del formulari _20хх. Però també podeu eliminar el sufix del nom del conjunt si se suposa que els fitxers es troben en carpetes diferents.
Anem al codi del fitxer Class1.cs i simular algun codi allà, tenint en compte diferents versions de 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;
}
}
}
Immediatament vaig tenir en compte totes les versions de Revit anteriors a la versió 2015 (que estaven disponibles en el moment d'escriure) i de seguida vaig tenir en compte la presència de símbols de compilació condicional, que es creen amb la mateixa plantilla.
Passem al més destacat. Creem un nou projecte a la nostra solució, només per a la versió del connector per a Revit 2016. Repetim tots els passos descrits anteriorment, respectivament, substituint el número 2015 pel número 2016. Però el fitxer Class1.cs esborrar del nou projecte.
Fitxer amb el codi necessari - Class1.cs – ja el tenim i només cal inserir-hi un enllaç en un projecte nou. Hi ha dues maneres d'inserir enllaços:
- Llarg - Feu clic amb el botó dret al projecte i seleccioneu "Afegir»->«Element existent", a la finestra que s'obre, cerqueu el fitxer necessari i en lloc de l'opció "Afegir"selecciona l'opció"Afegeix com a connexió»
- Curt - directament a l'explorador de solucions, seleccioneu el fitxer desitjat (o fins i tot fitxers, o fins i tot carpetes senceres) i arrossegueu-lo a un projecte nou mentre manteniu premuda la tecla Alt. Mentre arrossegueu, veureu que quan premeu la tecla Alt, el cursor del ratolí canviarà d'un signe més a una fletxa.
ACTUALITZACIÓ: Vaig fer una mica de confusió en aquest paràgraf: per transferir diversos fitxers, hauríeu de mantenir premut Maj + Alt!
Després de realitzar el tràmit, tindrem una fitxa en el segon projecte Class1.cs amb la icona corresponent (fletxa blava):
Quan editeu el codi a la finestra de l'editor, també podeu triar en quin context del projecte voleu mostrar el codi, cosa que us permetrà veure el codi que s'està editant sota diferents símbols de compilació condicional:
Creem tots els altres projectes (2017-2020) amb aquest esquema. Truc de vida: si arrossegueu fitxers a l'Explorador de solucions no des del projecte base, sinó des del projecte on ja s'han inserit com a enllaç, no haureu de mantenir premuda la tecla Alt!
L'opció descrita és bastant bona fins al moment d'afegir una nova versió del connector o fins al moment d'afegir nous fitxers al projecte; tot això es fa molt tediós. I recentment, de sobte, em vaig adonar de com ordenar-ho tot amb un projecte i estem passant al segon mètode
La màgia de les configuracions
Després d'haver acabat de llegir aquí, podeu exclamar: "Per què vau descriure el primer mètode, si l'article tracta immediatament del segon?!" I ho vaig descriure tot per aclarir per què necessitem símbols de compilació condicionals i en quins llocs difereixen els nostres projectes. I ara ens queda més clar quines diferències en els projectes hem d'implementar, deixant només un projecte.
I per fer-ho tot més evident, no crearem un nou projecte, sinó que farem canvis al nostre projecte actual creat de la primera manera.
Per tant, primer de tot, eliminem tots els projectes de la solució excepte el principal (que conté els fitxers directament). Aquells. projectes per a les versions 2016-2020. Obriu la carpeta amb la solució i suprimiu-hi les carpetes d'aquests projectes.
Ens queda un projecte a la nostra decisió: MySuperPluginForRevit_2015. Obriu les seves propietats i:
- A la pestanya "Aplicació"elimineu el sufix del nom del conjunt _2015 (després quedarà clar per què)
- A la pestanya "assemblea» elimina el símbol de compilació condicional R2015 del camp corresponent
Nota: la darrera versió de Visual Studio té un error: els símbols de compilació condicional no es mostren a la finestra de propietats del projecte, tot i que estan disponibles. Si experimenteu aquest error, haureu d'eliminar-los manualment del fitxer .csproj. Tanmateix, encara hem de treballar-hi, així que segueix llegint.
Canvieu el nom del projecte a la finestra de l'Explorador de solucions eliminant el sufix _2015 i després elimineu el projecte de la solució. Això és necessari per mantenir l'ordre i els sentiments de perfeccionistes! Obrim la carpeta de la nostra solució, canviem el nom de la carpeta del projecte allà de la mateixa manera i tornem a carregar el projecte a la solució.
Obriu el gestor de configuració. Configuració dels EUA Deixeu anar en principi, no serà necessari, així que l'eliminem. Creem noves configuracions amb noms que ja ens són familiars R2015, R2016, ..., R2020. Tingueu en compte que no cal que copieu la configuració d'altres configuracions i que no cal que creeu configuracions de projecte:
Aneu a la carpeta amb el projecte i obriu el fitxer amb l'extensió .csproj en un editor que us convingui. Per cert, també podeu obrir-lo a Visual Studio: heu de descarregar el projecte i, a continuació, l'element desitjat estarà al menú contextual:
L'edició a Visual Studio és fins i tot preferible, ja que l'editor s'alinea i sol·licita.
A l'arxiu veurem els elements
Aneu a l'element comú (primer). PropertyGroup i mira la propietat AssemblyName – aquest és el nom de l'assemblea i l'hauríem de tenir sense sufix _2015. Si hi ha un sufix, elimineu-lo.
Trobar un element amb una condició
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
No el necessitem, l'eliminem.
Element amb condició
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
serà necessari per treballar en l'etapa de desenvolupament de codi i depuració. Podeu canviar les seves propietats segons les vostres necessitats: establiu diferents camins de sortida, canvieu els símbols de compilació condicional, etc.
Ara anem a crear nous elements PropertyGroup per a les nostres configuracions. En aquests elements només hem d'establir quatre propietats:
- OutputPath - Carpeta de sortida. He posat el valor per defecte binR20xx
- Definir constants – símbols de compilació condicional. S'ha d'especificar el valor TRACE;R20хх
- TargetFrameworkVersion - versió de plataforma. Les diferents versions de l'API de Revit requereixen especificar diferents plataformes.
- AssemblyName – nom del conjunt (és a dir, nom del fitxer). Podeu escriure el nom exacte del conjunt, però per versatilitat us recomano escriure el valor $(AssemblyName)_20хх. Per fer-ho, prèviament hem eliminat el sufix del nom del conjunt
La característica més important de tots aquests elements és que simplement es poden copiar en altres projectes sense canviar-los en absolut. Més endavant a l'article adjuntaré tot el contingut del fitxer .csproj.
D'acord, hem descobert les propietats del projecte, no és difícil. Però què fer amb les biblioteques de connectors (paquets NuGet). Si mirem més enllà, veurem que les biblioteques incloses s'especifiquen als elements
L'element ens ajuda
Utilitzant l'element Escollir, establim diferents paquets NuGet per a diferents configuracions:
Tots els continguts 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>
Tingueu en compte que en una de les condicions he especificat dues configuracions mitjançant O. D'aquesta manera, el paquet requerit es connectarà durant la configuració Depurar.
I aquí ho tenim gairebé tot perfecte. Tornem a carregar el projecte, habilitem la configuració que necessitem, anomenem l'element " al menú contextual de la solució (no el projecte)Restaura tots els paquets NuGet"i veiem com canvien els nostres paquets.
I en aquesta etapa vaig arribar a un carreró sense sortida: per recollir totes les configuracions alhora, podríem utilitzar el muntatge per lots (menú "assemblea»->«Construcció per lots"), però en canviar de configuració, els paquets no es restauren automàticament. I a l'hora de muntar el projecte, això tampoc no passa, tot i que, en teoria, hauria de ser. No he trobat una solució a aquest problema amb mitjans estàndard. I el més probable és que també sigui un error de Visual Studio.
Per tant, per al muntatge per lots, es va decidir utilitzar un sistema especial de muntatge automatitzat
Per tant, aneu a la carpeta de la nostra solució (no al projecte), manteniu premuda la tecla Canviar i feu clic amb el botó dret a un espai buit de la carpeta; al menú contextual, seleccioneu l'element "Obriu la finestra de PowerShell aquí».
Si no el tens instal·lat nuclear, després escriviu primer l'ordre
dotnet tool install Nuke.GlobalTool –global
Ara escriviu l'ordre nuclear i se us demanarà que configureu nuclear per al projecte actual. No sé com escriure això més correctament en rus; en anglès s'escriurà No s'ha pogut trobar el fitxer .nuke. Vols configurar una compilació? [y/n]
Premeu la tecla Y i després hi haurà elements de configuració directes. Necessitem l'opció més senzilla d'utilitzar MSBuild, així que responem com a la captura de pantalla:
Anem a Visual Studio, que ens demanarà que tornem a carregar la solució, ja que s'hi ha afegit un nou projecte. Tornem a carregar la solució i veiem que tenim un projecte construir en el qual només estem interessats en un fitxer - Build.cs
Obriu aquest fitxer i escriviu un script per crear el projecte per a totes les configuracions. Bé, o utilitzeu el meu script, que podeu editar segons les vostres necessitats:
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"));
}
});
}
Tornem a la finestra de PowerShell i tornem a escriure l'ordre nuclear (podeu escriure l'ordre nuclear indicant el requerit Objectiu. Però en tenim un Objectiu, que s'executa per defecte). Després de prémer la tecla Enter, ens sentirem com uns autèntics pirates informàtics, perquè, com en una pel·lícula, el nostre projecte s'assemblarà automàticament per a diferents configuracions.
Per cert, podeu utilitzar PowerShell directament des de Visual Studio (menú "Veure»->«Altres finestres»->«Consola del gestor de paquets"), però tot serà en blanc i negre, cosa que no és gaire convenient.
Això conclou el meu article. Estic segur que podeu esbrinar l'opció per a AutoCAD vosaltres mateixos. Espero que el material aquí presentat trobi els seus "clients".
Gràcies!
Font: www.habr.com