
Lors du développement de plugins pour des applications CAO ( , c'est AutoCAD, Revit et Renga), un problÚme surgit souvent avec le temps : de nouvelles versions des logiciels sortent, leur API change et il faut créer de nouvelles versions des plugins.
Quand vous n'avez qu'un seul plugin ou si vous ĂȘtes encore un novice autodidacte dans ce domaine, il vous suffit de faire une copie du projet, de modifier les parties nĂ©cessaires et de compiler une nouvelle version du plugin. Par consĂ©quent, toute modification ultĂ©rieure dans le code entraĂźnera une augmentation exponentielle du temps de travail.
Au fur et Ă mesure que vous accumulez de l'expĂ©rience et des connaissances, vous trouverez plusieurs façons d'automatiser ce processus. J'ai parcouru ce chemin et je veux vous montrer oĂč j'en suis arrivĂ© et Ă quel point c'est pratique.
Pour commencer, examinons une méthode qui est évidente et que j'ai utilisée pendant longtemps.
Liens vers les fichiers du projet
Et pour que tout soit simple, clair et compréhensible, je vais tout décrire à partir d'un exemple abstrait de développement de plugin.
Ouvrons Visual Studio (j'ai la version Community 2019. Et oui â en français) et crĂ©ons une nouvelle solution. Appelons-la MySuperPluginForRevit

Nous allons créer un plugin pour Revit pour les versions 2015-2020. Donc, je vais créer un nouveau projet dans la solution (BibliothÚque de classes .Net Framework) et l'appeler MySuperPluginForRevit_2015

Nous devons ajouter des références à l'API Revit. Bien sûr, nous pouvons ajouter des références à des fichiers locaux (il faudra installer tous les SDK nécessaires ou toutes les versions de Revit), mais nous allons directement suivre le bon chemin et connecter le paquet NuGet. Vous pouvez trouver un nombre non négligeable de paquets, mais je vais utiliser les miens.
AprĂšs avoir connectĂ© le paquet, cliquez avec le bouton droit de la souris sur l'Ă©lĂ©ment «Liens» et choisissez dans le menu l'option «DĂ©placer packages.config vers PackageReferenceâŠÂ»

Si jamais Ă ce moment-lĂ vous ressentez de la panique, car dans la fenĂȘtre des propriĂ©tĂ©s du paquet il n'y a pas de point important «Copier localement», que nous devons absolument dĂ©finir sur faux, ne paniquez pas â allez dans le dossier du projet, ouvrez le fichier avec l'extension .csproj dans l'Ă©diteur de votre choix (j'utilise Notepad++) et trouvez l'entrĂ©e concernant notre paquet. Cela ressemble actuellement Ă ceci :
1.0.0Ajoutons-lui la propriété runtime. Cela donnera :
1.0.0
runtimeDésormais, lors de la création du projet, les fichiers du paquet ne seront pas copiés dans le dossier de sortie.
Continuons â imaginons d'emblĂ©e que notre plugin utilisera quelque chose de l'API Revit, qui a Ă©voluĂ© au fil des versions. Ou simplement, nous devons modifier quelque chose dans le code en fonction de la version de Revit pour laquelle nous crĂ©ons le plugin. Pour gĂ©rer ces diffĂ©rences de code, nous utiliserons des symboles de compilation conditionnelle. Ouvrons les propriĂ©tĂ©s du projet, allons Ă l'onglet «Assemblage» et dans le champ «Symboles de compilation conditionnelle» nous Ă©crirons R2015.

Notez que le symbole doit ĂȘtre ajoutĂ© Ă la fois pour la configuration Debug et pour la configuration Release.
Et pendant que nous sommes dans la fenĂȘtre des propriĂ©tĂ©s, passons immĂ©diatement Ă l'onglet «L'application» et dans le champ «Espace de noms par dĂ©faut» supprimons le suffixe _2015, afin que notre espace de noms soit universel et indĂ©pendant du nom de l'assemblage :

Dans mon cas, tous les plugins des diffĂ©rentes versions sont regroupĂ©s dans un mĂȘme dossier, donc mes noms d'assemblage conservent un suffixe de type _20xx. Mais vous pouvez aussi supprimer le suffixe du nom de l'assemblage si vous envisagez de placer les fichiers dans des dossiers diffĂ©rents.
Passons au code du fichier Class1.cs et simulons un certain 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", "Bonjour Revit 2015");
#elif R2016
TaskDialog.Show("ModPlus", "Bonjour Revit 2016");
#elif R2017
TaskDialog.Show("ModPlus", "Bonjour Revit 2017");
#elif R2018
TaskDialog.Show("ModPlus", "Bonjour Revit 2018");
#elif R2019
TaskDialog.Show("ModPlus", "Bonjour Revit 2019");
#elif R2020
TaskDialog.Show("ModPlus", "Bonjour Revit 2020");
#endif
return Result.Succeeded;
}
}
}J'ai immĂ©diatement pris en compte toutes les versions de Revit Ă partir de la version 2015 (qui Ă©taient disponibles au moment de la rĂ©daction de l'article) et j'ai immĂ©diatement considĂ©rĂ© la prĂ©sence de symboles de compilation conditionnelle, qui sont créés selon le mĂȘme modĂšle.
Passons à la piÚce maßtresse. Créons un nouveau projet dans notre solution, mais cette fois pour la version du plugin pour Revit 2016. Nous répétons toutes les actions décrites ci-dessus, en remplaçant le nombre 2015 par 2016. Mais nous supprimons le fichier Class1.cs du nouveau projet.

Le fichier avec le code nĂ©cessaire â Class1.cs â est dĂ©jĂ disponible et nous devons simplement y faire rĂ©fĂ©rence dans le nouveau projet. Il y a deux mĂ©thodes pour insĂ©rer des rĂ©fĂ©rences :
- Longue â clic droit sur le projet, sĂ©lectionnez "Ajouter" -> "ĂlĂ©ment existant". Dans la fenĂȘtre qui s'ouvre, trouvez le fichier souhaitĂ© et au lieu de "Ajouter", choisissez "Ajouter comme lien»

- Court â directement dans l'explorateur de solutions, sĂ©lectionnez le fichier (ou mĂȘme les fichiers. Vous pouvez mĂȘme prendre des dossiers entiers) et faites-les glisser dans le nouveau projet tout en maintenant la touche Alt enfoncĂ©e. Lorsque vous faites glisser, vous remarquerez que lorsque vous maintenez la touche Alt, le curseur changera d'un signe plus Ă une flĂšche.
Mise Ă jour : J'ai semĂ© un peu de confusion dans ce paragraphe â pour dĂ©placer plusieurs fichiers, il faut maintenir enfoncĂ© Maj+Alt!
AprÚs avoir effectué cette procédure, nous aurons dans le deuxiÚme projet un fichier Class1.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 le contexte de quel projet afficher le code, ce qui vous permettra de voir et d'Ă©diter le code avec diffĂ©rents symboles de compilation conditionnelle :

Avec ce schĂ©ma, nous crĂ©ons tous les autres projets (2017-2020). Astuce â si vous faites glisser des fichiers dans l'explorateur de solutions, pas Ă partir du projet de base, mais Ă partir du projet oĂč ils sont dĂ©jĂ insĂ©rĂ©s comme liens, alors vous n'avez pas besoin de maintenir la touche Alt !
La mĂ©thode dĂ©crite est assez bonne jusqu'Ă ce que l'on ajoute une nouvelle version du plugin ou qu'on ajoute de nouveaux fichiers au projet â tout cela devient trĂšs fastidieux. Et rĂ©cemment, j'ai soudainement rĂ©alisĂ© comment gĂ©rer tout cela avec un seul projet, et nous passons Ă la deuxiĂšme mĂ©thode.
Magie des configurations
En lisant jusqu'ici, vous pourriez s'exclamer "Et pourquoi as-tu décrit la premiÚre méthode, si l'article parle directement de la seconde ?". Je l'ai fait pour clarifier pourquoi nous avons besoin de symboles de compilation conditionnelle et dans quels endroits nos projets diffÚrent. Et maintenant, il nous devient plus clair quelles différences de projets nous devons réaliser, en gardant un seul projet.
Et pour que tout soit plus évident, nous ne créerons pas un nouveau projet, mais apporterons des modifications à notre projet actuel, créé de la premiÚre maniÚre.
Tout d'abord, supprimons tous les projets de la solution, sauf le principal (qui contient directement les fichiers). C'est-Ă -dire les projets pour les versions 2016-2020. Ouvrons le dossier de la solution et supprimons lĂ -bas les dossiers de ces projets.
Il ne reste qu'un projet dans la solution â MySuperPluginForRevit_2015. Ouvrons ses propriĂ©tĂ©s et :
- Dans l'onglet "L'application", supprimons le suffixe du nom de l'assemblage _2015 (il deviendra clair par la suite pourquoi)
- Dans l'onglet "Assemblage» supprimons le symbole de compilation conditionnelle R2015 du champ correspondant
Remarque : dans la derniĂšre version de Visual Studio, il y a un bug â les symboles de compilation conditionnelle ne s'affichent pas dans la fenĂȘtre des propriĂ©tĂ©s du projet, bien qu'ils soient prĂ©sents. Si vous constatez ce bug, vous devrez les supprimer manuellement du fichier .csproj. Cependant, nous devons quand mĂȘme travailler dedans, alors continuons.
Nous renommons le projet dans l'explorateur de solutions, en supprimant le suffixe _2015 et ensuite, nous supprimons le projet de la solution. Cela est nĂ©cessaire pour maintenir l'ordre et satisfaire les perfectionnistes ! Ouvrons le dossier de notre solution, renommons de la mĂȘme maniĂšre le dossier du projet et rechargeons le projet dans la solution.
Ouvrons le gestionnaire de configurations. Nous n'aurons pas vraiment besoin de la configuration Publication donc nous la supprimons. CrĂ©ons de nouvelles configurations avec les noms que nous connaissons dĂ©jĂ R2015, R2016, âŠ, R2020. Notez qu'il n'est pas nĂ©cessaire de copier les paramĂštres d'autres configurations et qu'il n'est pas nĂ©cessaire de crĂ©er des configurations de projet :

Allons dans le dossier du projet et ouvrons le fichier avec l'extension .csproj dans l'Ă©diteur de votre choix. D'ailleurs, il peut Ă©galement ĂȘtre ouvert dans Visual Studio â vous devez dĂ©charger le projet, puis le point nĂ©cessaire apparaĂźtra dans le menu contextuel :

Il est mĂȘme prĂ©fĂ©rable de modifier dans Visual Studio, car l'Ă©diteur aligne et fournit des suggestions.
Dans le fichier, nous allons voir les Ă©lĂ©ments â tout en haut se trouve le groupe gĂ©nĂ©ral, suivi de ceux avec des conditions. Ces Ă©lĂ©ments dĂ©finissent les propriĂ©tĂ©s du projet lors de sa compilation. Le premier Ă©lĂ©ment, sans conditions, dĂ©finit les propriĂ©tĂ©s gĂ©nĂ©rales, tandis que les Ă©lĂ©ments avec conditions, respectivement, modifient certaines propriĂ©tĂ©s en fonction des configurations.
Passons Ă l'Ă©lĂ©ment gĂ©nĂ©ral (le premier) PropertyGroup et regardons la propriĂ©tĂ© AssemblyName â c'est le nom de l'assembly et il ne doit pas avoir de suffixe _2015. Si un suffixe est prĂ©sent, supprimons-le.
Trouvons l'élément avec la condition
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">Il ne nous est pas nĂ©cessaire â supprimons-le.
L'élément avec la condition
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">sera nĂ©cessaire pour le travail lors de la phase de dĂ©veloppement et de dĂ©bogage du code. Vous pouvez modifier ses propriĂ©tĂ©s selon vos besoins â dĂ©finir diffĂ©rents chemins de sortie, changer les symboles de compilation conditionnelle, etc.
Nous allons maintenant créer de nouveaux éléments PropertyGroup pour nos configurations. Dans ces éléments, il nous suffit de spécifier quatre propriétés :
- OutputPath â dossier de sortie. Je dĂ©finis une valeur standard binR20xx
- DefineConstants â symboles de compilation conditionnelle. Il convient de dĂ©finir la valeur TRACE;R20xx
- VersionDuCadreCible â version de la plateforme. Pour diffĂ©rentes versions de Revit API, des plateformes diffĂ©rentes doivent ĂȘtre spĂ©cifiĂ©es.
- AssemblyName â nom de l'assembly (c'est-Ă -dire le nom du fichier). Vous pouvez Ă©crire directement le nom requis de l'assembly, mais pour plus d'universalitĂ©, je conseille d'Ă©crire la valeur $(AssemblyName)_20xx. C'est pourquoi nous avons prĂ©cĂ©demment supprimĂ© le suffixe du nom de l'assembly
La fonctionnalitĂ© la plus importante de tous ces Ă©lĂ©ments est qu'ils peuvent ĂȘtre facilement copiĂ©s dans d'autres projets sans mĂȘme ĂȘtre modifiĂ©s. Plus loin dans l'article, je fournirai tout le contenu du fichier .csproj.
Bien, nous avons compris les propriĂ©tĂ©s du projet â ce n'est pas difficile. Mais que faire avec les bibliothĂšques connectĂ©es (paquetages NuGet). Si nous regardons plus loin, nous verrons que les bibliothĂšques connectĂ©es sont spĂ©cifiĂ©es dans les Ă©lĂ©ments . Mais voici le problĂšme â cet Ă©lĂ©ment traite incorrectement les conditions, comme l'Ă©lĂ©ment PropertyGroup. Cela pourrait mĂȘme ĂȘtre un bug de Visual Studio, mais si l'on dĂ©finit plusieurs Ă©lĂ©ments ItemGroup avec des conditions de configuration, et qu'Ă l'intĂ©rieur on insĂšre diffĂ©rents liens vers des paquetages NuGet, alors lors du changement de configuration, tous les paquetages spĂ©cifiĂ©s sont ajoutĂ©s au projet.
L'élément , qui fonctionne selon la logique qui nous est familiÚre, nous aide. if-then-else.
En utilisant l'élément Choose, nous spécifions différents paquetages NuGet pour différentes configurations :
Tout le contenu de 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
runtimeVeuillez noter qu'une de mes conditions stipule que deux configurations sont indiquées par OU (Or). Ainsi, le bon paquet sera connecté lors de la configuration Débogage.
Et nous avons presque tout de parfait. Nous rechargeons le projet, activons la configuration dont nous avons besoin, puis dans le menu contextuel de la solution (pas du projet) nous sélectionnons l'option «Restaurer tous les paquets NuGet» et nous voyons comment nos paquets changent.

Et Ă ce stade, je suis arrivĂ© Ă une impasse - pour construire toutes les configurations en mĂȘme temps, nous pourrions utiliser la construction par lots (menu «Assemblage" -> "Construction par lots»), mais le changement de configurations ne dĂ©clenche pas automatiquement la restauration des paquets. Et lors de la construction du projet, cela ne se produit pas non plus, bien que cela devrait thĂ©oriquement. Je n'ai pas trouvĂ© de solution Ă ce problĂšme avec les moyens standards. Et il y a fort Ă parier que c'est aussi un bug de Visual Studio.
Donc, pour la construction par lots, il a été décidé d'utiliser un systÚme de construction automatisée . En réalité, je ne voulais pas faire cela, car je pense que c'est superflu dans le cadre du développement de plugins, mais à l'heure actuelle, je ne vois pas d'autre solution. Quant à la question «Pourquoi Nuke ?», la réponse est simple : nous l'utilisons au travail.
Donc, nous allons dans le dossier de notre solution (pas du projet), nous maintenons la touche Shift et faisons un clic droit dans un espace vide du dossier - dans le menu contextuel, nous choisissons l'option «Ouvrir PowerShell ici».

Si vous n'avez pas installé nuke, commencez par écrire la commande
dotnet tool install Nuke.GlobalTool âglobalEnsuite, Ă©crivez la commande nuke et il vous sera proposĂ© de configurer nuke pour le projet actuel. Je ne sais pas comment cela serait mieux formulĂ© en russe - en anglais, il sera Ă©crit Could not find .nuke file. Do you want to setup a build? [y/n]
Appuyez sur la touche Y et ensuite il y aura les étapes de configuration propres. Nous avons besoin de l'option la plus simple en utilisant MSBuild, donc nous répondons comme sur la capture d'écran :

Passons à Visual Studio, qui nous proposera de recharger la solution, car un nouveau projet a été ajouté. Nous rechargeons la solution et voyons qu'un projet est apparu build dans lequel nous ne sommes intéressés que par un fichier - Build.cs

Nous ouvrons ce fichier et écrivons le script de construction du projet pour toutes les configurations. Ou nous utilisons mon script, que vous pouvez modifier selon 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;
// Si le nom de la solution et le nom du projet (plugin) sont différents, indiquez le nom du projet (plugin) ici
string PluginName => Solution.Name;
Target Compile => _ => _
.Executes(() =>
{
var project = Solution.GetProject(PluginName);
if (project == null)
throw new FileNotFoundException("Non trouvé!");
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 retournons Ă la fenĂȘtre PowerShell et réécrivons la commande nuke (vous pouvez Ă©crire la commande nuke en spĂ©cifiant le nĂ©cessaire Cible. Mais nous en avons un Cible, qui s'exĂ©cute par dĂ©faut). AprĂšs avoir appuyĂ© sur la touche EntrĂ©e, nous nous sentirons comme de vĂ©ritables hackers, car, comme dans les films, la compilation automatique de notre projet pour diffĂ©rentes configurations se produira.
Au fait, vous pouvez utiliser PowerShell directement depuis Visual Studio (menu «Affichage" -> "Autres fenĂȘtres" -> "Console du gestionnaire de packages»), mais tout sera en noir et blanc, ce qui n'est pas trĂšs pratique.
Ainsi se termine mon article. Je suis sĂ»r qu'avec l'option pour AutoCAD, vous pourrez vous dĂ©brouiller vous-mĂȘmes. J'espĂšre que le matĂ©riel prĂ©sentĂ© ici trouvera ses "clients".
Merci de votre attention !
Source : habr.com
