Lors du développement de plugins pour des applications CAO (
Lorsque vous n'avez qu'un seul plugin ou que vous êtes encore autodidacte débutant en la matière, vous pouvez simplement faire une copie du projet, y modifier les emplacements nécessaires et assembler une nouvelle version du plugin. En conséquence, les modifications ultérieures du code entraîneront une augmentation multiple des coûts de main-d'œuvre.
Au fur et à mesure que vous acquerrez de l'expérience et des connaissances, vous découvrirez plusieurs façons d'automatiser ce processus. J'ai parcouru ce chemin et je veux vous dire avec quoi je me suis retrouvé et à quel point c'est pratique.
Voyons d’abord une méthode qui est évidente et que j’utilise depuis longtemps.
Liens vers les fichiers du projet
Et pour que tout soit simple, visuel et compréhensible, je vais tout décrire à l'aide d'un exemple abstrait de développement de plugin.
Ouvrons Visual Studio (j'ai la version Community 2019. Et oui - en russe) et créons une nouvelle solution. Appelons-le MonSuperPluginForRevit
Nous allons créer un plugin pour Revit pour les versions 2015-2020. Par conséquent, créons un nouveau projet dans la solution (Net Framework Class Library) et appelons-le MonSuperPluginForRevit_2015
Nous devons ajouter des liens vers l'API Revit. Bien sûr, nous pouvons ajouter des liens vers des fichiers locaux (nous devrons installer tous les SDK nécessaires ou toutes les versions de Revit), mais nous suivrons immédiatement le bon chemin et connecterons le package NuGet. Vous pouvez trouver de nombreux packages, mais j'utiliserai le mien.
Après avoir connecté le package, faites un clic droit sur l'élément «références" et sélectionnez l'élément "Déplacez packages.config vers PackageReference...»
Si soudainement, à ce stade, vous commencez à paniquer, car dans la fenêtre des propriétés du package, il n'y aura aucun élément important "Copier localement", que nous devons absolument définir sur la valeur non, alors pas de panique - allez dans le dossier contenant le projet, ouvrez le fichier avec l'extension .csproj dans un éditeur qui vous convient (j'utilise Notepad++) et trouvez-y une entrée sur notre package. Elle ressemble à ça maintenant :
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
</PackageReference>
Ajoutez-y une propriété Durée. Cela se passera comme ceci :
<PackageReference Include="ModPlus.Revit.API.2015">
<Version>1.0.0</Version>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
Désormais, lors de la création d'un projet, les fichiers du package ne seront pas copiés dans le dossier de sortie.
Allons plus loin - imaginons immédiatement que notre plugin utilise quelque chose de l'API Revit, qui a changé au fil du temps avec la sortie de nouvelles versions. Eh bien, ou nous devons simplement modifier quelque chose dans le code en fonction de la version de Revit pour laquelle nous créons le plugin. Pour résoudre de telles différences de code, nous utiliserons des symboles de compilation conditionnelle. Ouvrez les propriétés du projet, allez dans l'onglet "assemblage" et sur le terrain "Notation de compilation conditionnelle"écrivons R2015.
Notez que le symbole doit être ajouté pour les configurations Debug et Release.
Eh bien, pendant que nous sommes dans la fenêtre des propriétés, nous allons immédiatement dans l'onglet "Application" et sur le terrain "Espace de noms par défaut» supprimer le suffixe _2015afin que notre espace de noms soit universel et indépendant du nom de l'assembly :
Dans mon cas, dans le produit final, les plugins de toutes les versions sont placés dans un seul dossier, donc mes noms d'assembly restent avec le suffixe du formulaire _20хх. Mais vous pouvez également supprimer le suffixe du nom de l'assembly si les fichiers sont censés se trouver dans des dossiers différents.
Passons au code du fichier Classe1.cs et simulez-y du code, en tenant compte des différentes 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;
}
}
}
J'ai immédiatement pris en compte toutes les versions de Revit supérieures à la version 2015 (qui étaient disponibles au moment de la rédaction) et j'ai immédiatement pris en compte la présence de symboles de compilation conditionnelle, qui sont créés à l'aide du même modèle.
Passons au point culminant principal. Nous créons un nouveau projet dans notre solution, uniquement pour la version du plugin pour Revit 2016. Nous répétons respectivement toutes les étapes décrites ci-dessus en remplaçant le numéro 2015 par le numéro 2016. Mais le fichier Classe1.cs supprimer du nouveau projet.
Fichier avec le code requis - Classe1.cs – nous l’avons déjà et il nous suffit d’insérer un lien vers celui-ci dans un nouveau projet. Il existe deux manières d'insérer des liens :
- Long – faites un clic droit sur le projet et sélectionnez «ajouter"->"Élément existant", dans la fenêtre qui s'ouvre, recherchez le fichier souhaité et à la place de l'option "ajouter" sélectionnez l'option "Ajouter comme connexion»
- court – directement dans l'explorateur de solutions, sélectionnez le fichier souhaité (voire des fichiers, voire des dossiers entiers) et faites-le glisser dans un nouveau projet tout en maintenant la touche Alt enfoncée. Au fur et à mesure que vous faites glisser, vous verrez que lorsque vous appuyez sur la touche Alt, le curseur de la souris passe d'un signe plus à une flèche.
UPD: J'ai fait une petite confusion dans ce paragraphe - pour transférer plusieurs fichiers, vous devez maintenir enfoncé Maj + Alt!
Après avoir effectué la procédure, nous aurons un fichier dans le deuxième projet Classe1.cs avec l'icône correspondante (flèche bleue) :
Lors de l'édition du code dans la fenêtre de l'éditeur, vous pouvez également choisir dans quel contexte de projet afficher le code, ce qui vous permettra de voir le code en cours d'édition sous différents symboles de compilation conditionnelle :
Nous créons tous les autres projets (2017-2020) en utilisant ce schéma. Life hack - si vous faites glisser des fichiers dans l'Explorateur de solutions non pas depuis le projet de base, mais depuis le projet dans lequel ils sont déjà insérés sous forme de lien, vous n'avez pas besoin de maintenir la touche Alt enfoncée !
L'option décrite est assez bonne jusqu'au moment de l'ajout d'une nouvelle version du plugin ou jusqu'au moment de l'ajout de nouveaux fichiers au projet - tout cela devient très fastidieux. Et récemment, j'ai soudain réalisé comment tout régler avec un seul projet et nous passons à la deuxième méthode
La magie des configurations
Après avoir fini de lire ici, vous pourriez vous exclamer : « Pourquoi avez-vous décrit la première méthode, si l'article concerne immédiatement la seconde ?! » Et j'ai tout décrit pour expliquer plus clairement pourquoi nous avons besoin de symboles de compilation conditionnelle et en quoi nos projets diffèrent. Et maintenant, il nous devient plus clair quelles différences dans les projets nous devons mettre en œuvre, ne laissant qu'un seul projet.
Et pour que tout soit plus évident, nous ne créerons pas de nouveau projet, mais apporterons des modifications à notre projet actuel créé de la première manière.
Donc, tout d'abord, on supprime tous les projets de la solution sauf le principal (contenant directement les fichiers). Ceux. projets pour les versions 2016-2020. Ouvrez le dossier contenant la solution et supprimez-y les dossiers de ces projets.
Il nous reste un projet dans notre décision - MonSuperPluginForRevit_2015. Ouvrez ses propriétés et :
- Sur l'onglet "Application"supprimez le suffixe du nom de l'assembly _2015 (on comprendra pourquoi plus tard)
- Sur l'onglet "assemblage» supprimer le symbole de compilation conditionnelle R2015 du champ correspondant
Remarque : la dernière version de Visual Studio présente un bug : les symboles de compilation conditionnelle ne sont pas affichés dans la fenêtre des propriétés du projet, bien qu'ils soient disponibles. Si vous rencontrez ce problème, vous devez les supprimer manuellement du fichier .csproj. Cependant, nous devons encore y travailler, alors poursuivez votre lecture.
Renommez le projet dans la fenêtre Explorateur de solutions en supprimant le suffixe _2015 puis supprimez le projet de la solution. C'est nécessaire pour maintenir l'ordre et les sentiments des perfectionnistes ! Ouvrez le dossier de notre solution, renommez-y le dossier du projet de la même manière et chargez à nouveau le projet dans la solution.
Ouvrez le gestionnaire de configuration. Configuration américaine Libération en principe, il ne sera pas nécessaire, nous le supprimons donc. Nous créons de nouvelles configurations avec des noms qui nous sont déjà familiers R2015, R2016, ..., R2020. Notez que vous n'avez pas besoin de copier les paramètres d'autres configurations et que vous n'avez pas besoin de créer des configurations de projet :
Accédez au dossier contenant le projet et ouvrez le fichier avec l'extension .csproj dans un éditeur qui vous convient. À propos, vous pouvez également l'ouvrir dans Visual Studio - vous devez décharger le projet, puis l'élément souhaité sera dans le menu contextuel :
L'édition dans Visual Studio est même préférable, puisque l'éditeur aligne et invite.
Dans le fichier nous verrons les éléments
Accédez au (premier) élément commun GroupePropriété et regarde la propriété Nom de l'assemblage – c'est le nom de l'assemblée et nous devrions l'avoir sans suffixe _2015. S'il y a un suffixe, supprimez-le.
Trouver un élément avec une condition
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
Nous n'en avons pas besoin - nous le supprimons.
Élément avec condition
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
sera nécessaire pour travailler au stade du développement du code et du débogage. Vous pouvez modifier ses propriétés en fonction de vos besoins - définir différents chemins de sortie, modifier les symboles de compilation conditionnelle, etc.
Créons maintenant de nouveaux éléments GroupePropriété pour nos configurations. Dans ces éléments, il nous suffit de définir quatre propriétés :
- Chemin de sortie - dossier de sortie. J'ai défini la valeur par défaut poubelleR20xx
- Définir les constantes – symboles de compilation conditionnelle. La valeur doit être précisée TRACE;R20хх
- Version du cadre cible – version plateforme. Différentes versions de l'API Revit nécessitent la spécification de différentes plates-formes.
- Nom de l'assemblage – nom de l’assembly (c’est-à-dire le nom du fichier). Vous pouvez écrire le nom exact de l'assembly, mais pour des raisons de polyvalence, je recommande d'écrire la valeur $(Nom de l'Assemblée)_20хх. Pour ce faire, nous avons précédemment supprimé le suffixe du nom de l'assembly
La caractéristique la plus importante de tous ces éléments est qu’ils peuvent simplement être copiés dans d’autres projets sans aucune modification. Plus loin dans l'article, je joindrai tout le contenu du fichier .csproj.
D'accord, nous avons compris les propriétés du projet - ce n'est pas difficile. Mais que faire des bibliothèques de plug-ins (packages NuGet). Si nous regardons plus loin, nous verrons que les bibliothèques incluses sont spécifiées dans les éléments
L'élément nous vient en aide
Utilisation de l'élément Selectionnez, nous définissons différents packages NuGet pour différentes configurations :
Tous les contenus 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>
Veuillez noter que dans l'une des conditions j'ai spécifié deux configurations via OU. De cette façon, le package requis sera connecté lors de la configuration Déboguer.
Et ici, nous avons presque tout parfait. Nous chargeons le projet, activons la configuration dont nous avons besoin, appelons l'élément " dans le menu contextuel de la solution (pas du projet)Restaurer tous les packages NuGet"et nous voyons comment nos forfaits changent.
Et à ce stade, je suis arrivé dans une impasse - afin de collecter toutes les configurations d'un coup, nous pourrions utiliser l'assemblage par lots (menu "assemblage"->"Construction par lots"), mais lors du changement de configuration, les packages ne sont pas automatiquement restaurés. Et lors de l'assemblage du projet, cela ne se produit pas non plus, même si, en théorie, cela devrait le faire. Je n'ai pas trouvé de solution à ce problème avec des moyens standards. Et il s’agit très probablement aussi d’un bug de Visual Studio.
Par conséquent, pour l'assemblage par lots, il a été décidé d'utiliser un système d'assemblage automatisé spécial.
Alors, allez dans le dossier de notre solution (pas du projet), maintenez la touche enfoncée Shift et faites un clic droit sur un espace vide du dossier - dans le menu contextuel, sélectionnez l'élément "Ouvrez la fenêtre PowerShell ici».
Si vous ne l'avez pas installé nuke, puis écrivez d'abord la commande
dotnet tool install Nuke.GlobalTool –global
Maintenant, écrivez la commande nuke et vous serez invité à configurer nuke pour le projet en cours. Je ne sais pas comment écrire cela plus correctement en russe - en anglais, ce sera écrit Impossible de trouver le fichier .nuke. Voulez-vous configurer une version ? [o/n]
Appuyez sur la touche Y, puis il y aura des éléments de paramètres directs. Nous avons besoin de l'option la plus simple en utilisant MSBuild, nous répondons donc comme dans la capture d'écran :
Passons à Visual Studio, qui nous proposera de recharger la solution, puisqu'un nouveau projet y a été ajouté. On recharge la solution et on voit qu'on a un projet construire dans lequel nous ne nous intéressons qu'à un seul fichier - Construire.cs
Ouvrez ce fichier et écrivez un script pour créer le projet pour toutes les configurations. Eh bien, ou utilisez mon script, que vous pouvez modifier en fonction de vos besoins :
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"));
}
});
}
Nous revenons à la fenêtre PowerShell et réécrivons la commande nuke (vous pouvez écrire la commande nuke indiquant le nécessaire Target. Mais nous en avons un Target, qui s'exécute par défaut). Après avoir appuyé sur la touche Entrée, nous nous sentirons comme de vrais hackers, car, comme dans un film, notre projet sera automatiquement assemblé pour différentes configurations.
D'ailleurs, vous pouvez utiliser PowerShell directement depuis Visual Studio (menu "Voir"->"Autres fenêtres"->"Console du gestionnaire de packages"), mais tout sera en noir et blanc, ce qui n'est pas très pratique.
Ceci conclut mon article. Je suis sûr que vous pouvez découvrir vous-même l'option pour AutoCAD. J'espère que le matériel présenté ici trouvera ses « clients ».
Je vous remercie!
Source: habr.com