web-dev-qa-db-fra.com

Événement de pré-construction à l'échelle de la solution?

J'ai une solution dans Visual Studio qui contient plusieurs projets. Je voudrais exécuter une commande au tout début de chaque build - peu importe les projets impliqués et s'ils sont à jour ou non.

Essentiellement, j'ai besoin de quelque chose de similaire à un événement de pré-génération à l'échelle de la solution, mais malheureusement, VS ne semble pas les prendre en charge. Quelqu'un connaît-il une autre façon de réaliser ce dont j'ai besoin?

64
user200783

Exigence inhabituelle. Mais cela peut être fait. Ajoutez un nouveau projet à votre solution, utilisez le modèle Visual C++> Général> Makefile Project. Définissez son paramètre NMake> Build Command Line sur les commandes que vous souhaitez exécuter. Utilisez Projet> Dépendances du projet pour que tous les autres projets en dépendent.

41
Hans Passant

Bref aperçu de mes variantes ci-dessous

juste une note: c'est une liste incomplète de tous les existants (voir aussi les autres réponses etc.), je ne supporte que mes trucs originaux en l'état actuel ...

summary

Remarques:

  • 1 - Ne nécessite aucune extension supplémentaire. Mais cela peut ne fonctionner qu'au niveau des projets, nous l'utilisons donc pour émuler le niveau de notre solution ... C'est difficile et peu pratique pour une solution commune, mais c'est une variante. Voir ci-dessous.
  • 2 - Le moteur d'origine de vsSolutionBuildEvent fournit quelques moyens de prise en charge unifiée des VS et msbuild.exe. De manière simple, le targets mode pour appeler le after.<name>.sln.targets disponible uniquement pour msbuild.exe (cela ne nécessite pas d'étapes supplémentaires, simplement une action). Mais seul le moteur d'origine (inc. VsCommandEvent) peut permettre des scripts supplémentaires qui prennent en charge par exemple (archiveur 7Zip, emballage du paquet nuget sans nuget.exe, serveurs distants, etc.). Cependant, ce n'est pas important pour notre question/problème et vous pouvez utiliser n'importe quelle option disponible pour prendre en charge le niveau de la solution si vous voyez + au dessus.

Variante 1: Microsoft.VisualStudio.Shell.Interop

Cette variante n'est pas destinée aux simples utilisateurs de VS. Cependant, cela peut être utile pour votre solution complète, etc.

Vous devez implémenter, par exemple:

par exemple:

public sealed class YourPackage: Package, IVsSolutionEvents, IVsUpdateSolutionEvents2
{
...
    public int UpdateSolution_Begin(ref int pfCancelUpdate)
    {
        //TODO:
    }
}

Ensuite, enregistrez le gestionnaire avec les méthodes 'Advise' comme écouteur de priorité, c'est-à-dire pour IVsUpdateSolutionEvents2 vous devez utiliser AdviseUpdateSolutionEvents

C'est important , car le BuildEvents (voir EnvDTE ) - n'aidera probablement pas et pourrait fonctionner trop tard - Exemple

Exemple avec AdviseUpdateSolutionEvents:

// http://msdn.Microsoft.com/en-us/library/Microsoft.visualstudio.Shell.interop.ivssolutionbuildmanager2.aspx
private IVsSolutionBuildManager2 sbm;

// http://msdn.Microsoft.com/en-us/library/bb141335.aspx
private uint _sbmCookie;
...

sbm = (IVsSolutionBuildManager2)ServiceProvider.GlobalProvider.GetService(typeof(SVsSolutionBuildManager));
sbm.AdviseUpdateSolutionEvents(this, out _sbmCookie);

Où:

  • le champ sbm doit faire partie de la classe de protection contre GC.
  • pour obtenir le service SVsSolutionBuildManager est utilisé le ServiceProvider mais cela peut être comme vous en avez besoin. Voir msdn

Maintenant, nous pouvons travailler avec tous les projets à la fois - au niveau de la solution.

Variante 2: Objectifs et carte des projets.

ok, vous aimez quelque chose comme ça - MSBuild: Extension de la construction de la solution , mais cette variante peut fonctionner avec les processus de construction de msbuild.exe et non de VS IDE ...

Mais le VS utilise également des cibles (Build, Rebuild, Clean, ..) dans les fichiers de projet (* .csproj, * .vcxproj, ..) lorsque les opérations de build sont démarrées. Nous pouvons donc également essayer ceci, mais rappelez-vous:

  • Le VS ignore également le fichier .sln étonnant. Il forme toutes les cibles finales de l'environnement chargé avec EnvDTE etc.
  • Le fichier .sln doit être traité par msbuild.exe uniquement en tant que: génère automatiquement le .metaproj (en mémoire par défaut), qui contient "quoi et quand" sera construit. Inclure des objectifs communs pour tous les projets, s'il existe, par exemple:
...
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportAfter\*" Condition="'$(ImportByWildcardBeforeSolution)' != 'false' and exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\SolutionFile\ImportAfter')" />
<Import Project="D:\tmp\p\after.name.sln.targets" Condition="exists('D:\tmp\p\after.name.sln.targets')" />
<Target Name="Build" />
<Target Name="Rebuild" />
<Target Name="Clean" />
<Target Name="Publish" />
  • Et oui, le .metaproj n'est pas non plus visible par VS IDE.

Par conséquent, pour travailler avec des cibles communes de VS IDE vous pouvez utiliser uniquement des fichiers de projet avec certaines limitations (sans modification/extension de VS, cela signifie)).

Et donc, si vous avez besoin de la solution commune (c'est-à-dire que vous ne connaissez peut-être pas les projets, etc. - cela peut être, par exemple, pour certaines solutions de boîtes et similaires):

  • Ajoutez votre fichier .targets commun à tous vos projets (il peut être automatiquement avec n'importe quel outil, y compris les événements NuGet etc.), par exemple: <Import Project="..\<SolutionFile>.targets" />
  • Ensuite, vous devez utiliser une limitation pour:
    • "seulement - avant tous les projets"
    • "seulement - après tous les projets"

Et par exemple, oui, ça peut être la 'Carte des projets':

  • 'Map of projects' illustre les 'événements' PRE/POST à ​​l'échelle de la solution pour les opérations de build de Visual Studio IDE (c'est-à-dire principal de VS IDE)
...
<Target Name="_Build" BeforeTargets="Build" DependsOnTargets="ProjectsMap">
    <CallTarget Targets="_BuildPRE" Condition="$(ScopeDetectFirst)" />
    <CallTarget Targets="_BuildPOST" Condition="$(ScopeDetectLast)" />
</Target>
<Target Name="_BuildPRE">
    <!-- ... -->
</Target>
<Target Name="_BuildPOST">
    <!-- ... -->
</Target>
...

En général, nous allons utiliser la carte des projets et maintenant nous savons "quoi et quand" devrait se produire. Il est sûr pour tous ou la plupart des cas (modifications de l'ordre de construction ou suppression des projets de la solution). Pourtant! vous devez gérer <Import> section pour les nouveaux projets en première init. C'est vraiment gênant, mais c'est aussi une variante ...

Variante 3: Plugin vsSolutionBuildEvent

Aujourd'hui, c'est la solution la plus complète pour travailler avec de nombreux événements comme Events-Catcher avec une variété d'actions avancées pour la maintenance de vos projets et bibliothèques, la construction de processus et de processus au moment de l'exécution à partir de votre Visual Studio et MSBuild Tool.

Différents types d'actions pour tous les sous-projets à la fois en solution sous forme d'événements de solution ou individuellement pour chacun.

https://visualstudiogallery.msdn.Microsoft.com/0d1dbfd7-ed8a-40af-ae39-281bfeca2334/

plugin - vsSolutionBuildEvent

Comment ça marche à l'intérieur

Si vous souhaitez utiliser Variante 1 ci-dessus ou si vous voulez voir comment travailler avec Shell.Interop, EnvDTE, IVsUpdateSolutionEvents2, MSBuild Engine etc., voir ici :

scheme

Variante 4. EnvDTE.CommandEvents

Cette variante n'est pas non plus pour les simples utilisateurs de VS. Cependant, comme pour Variante 1 il peut être utile pour votre box-solution etc.

Ce n'est pas pareil, mais oui, c'est aussi possible avec EnvDTE.CommandEvents comme dans Variante 1 ci-dessus.

Vous devriez déjà savoir (voir ci-dessus) cette solution pour un travail prioritaire avec le type actuel de l'action de construction ... Alors pourquoi ne pas l'utiliser comme solution principale pour le problème actuel?

_cmdEvents.BeforeExecute += (string guid, int id, object customIn, object customOut, ref bool cancelDefault) => {

    if(UnifiedTypes.Build.VSCommand.existsById(id)) {
        // ... your action
    }

};

Où: Description | guid | id |In |Out| --------------------------|---------------------------------------|-----|---|---| Started: Build Solution |{5EFC7975-14BC-11CF-9B2B-00AA00573819} | 882 | | | Started: Rebuild Solution |{5EFC7975-14BC-11CF-9B2B-00AA00573819} | 883 | | | Started: Clean Solution |{5EFC7975-14BC-11CF-9B2B-00AA00573819} | 885 | | |

http://vsce.r-eg.net/doc/Features/Solution-wide/

De plus, en option, vous pouvez supprimer ces commandes si vous en avez besoin. Dans la variante ci-dessous, vous verrez la solution complète pour cette façon.

Variante 5. Plugin vsCommandEvent

https://visualstudiogallery.msdn.Microsoft.com/ad9f19b2-04c0-46fe-9637-9a52ce4ca661/

Il présente également un gestionnaire avancé de la plupart des événements, mais contrairement au premier, il était spécialisé pour MS Visual Studio pour un travail avancé avec toutes les commandes et les données de sortie en tant que gestionnaire de cela. Non seulement pour les projets et les solutions, mais aussi pour l'ensemble de l'IDE de Visual studio.

En général, c'est la solution courante de Variante 4 et vous pouvez simplement remplacer toutes les commandes ci-dessus pour résoudre ce problème.

Et pour le même modèle Event-Actions comme dans vsSolutionBuildEvent, il peut être utile dans la plupart des cas.

scheme

"Aidez-moi avec les variantes"

Il existe une implémentation ouverte pour toutes ces variantes. Voir ici et sourire:

41
Denis Kuzmin

Vous pouvez jeter un œil à cet article: MSBuild: extension de la version de la solution.

Semble être exactement ce dont vous avez besoin.

18
Andriy K

Pour ce faire, nous ajoutons un projet vide et définissons des événements de génération pour ce projet. Ensuite, vous devez donner à chaque projet la dépendance de ce projet vide pour vous assurer qu'il est généré à chaque fois.

9
erelender

Cela fait un moment, et certaines choses dans l'infrastructure .Net ont changé depuis, offrant de nouvelles options. Maintenant, mon choix pour résoudre ce roi de problème sont des paquets de pépites. J'ai mis mes étapes de construction dans un package qui a ensuite été inclus dans chaque projet. Le gestionnaire de packages Visual Studio donne une vue d'ensemble des packages au niveau de la solution.Il est donc assez facile de vérifier cette règle.

0
Andriy K

Un autre ancien poste mais inspiré par la solution @reg, je voulais exécuter un simple temporisateur de génération qui enregistrerait le temps écoulé pour une génération de solution. J'ai fait fonctionner les événements de génération à l'aide d'un module PowerShell que je charge via la console du gestionnaire de packages lorsque Visual Studio IDE démarre.

Créez donc un module PowerShell comme BuildEvents.psm1:

<#
.SYNOPSIS
    Register solution build events

.DESCRIPTION
    Registers the OnBuildBegin and OnBuildDone events for the entire solution
    De-registers the events if called multiple times.

.EXAMPLE
    RegisterBuildEvents
#>
function RegisterBuildEvents{
  try {
    Unregister-Event -SourceIdentifier "OnBuildBegin" -Force
  } catch {
    #we don't care if this doesn't work
  }
  try {
    Unregister-Event -SourceIdentifier "OnBuildDone" -Force
  } catch {
    #we don't care if this doesn't work
  }
  $obj = [System.Runtime.InteropServices.Marshal]::CreateWrapperOfType($dte.Application.Events.BuildEvents, [EnvDTE.BuildEventsClass])
  Register-ObjectEvent -InputObject $obj -EventName OnBuildBegin -Action {
    # do stuff here on build begin
    Write-Host "Solution build started!"
  } -SourceIdentifier "OnBuildBegin"
  Register-ObjectEvent -InputObject $obj -EventName OnBuildDone -Action {
    # do stuff here on build done
    Write-Host "Solution build done!" 
  } -SourceIdentifier "OnBuildDone"
}

# export the functions from the module
export-modulemember -function RegisterBuildEvents

Importez le module lorsque l'hôte du gestionnaire de packages s'initialise:

  1. Dans la console du gestionnaire de packages, tapez $ profile pour obtenir l'emplacement de votre profil PowerShell
  2. Accédez à ce répertoire sur le disque, s'il n'y a pas de fichier, créez-en un avec le nom renvoyé par la commande ci-dessus (par exemple NuGet_profile.ps1)
  3. Ouvrez le fichier dans le bloc-notes et ajoutez les lignes suivantes

    Import-Module -Name <Path to your ps module>\BuildEvents -Force
    RegisterBuildEvents
    
0
James Close