Comment masquer les fichiers générés par l'outil personnalisé dans Visual Studio
Je voudrais que les fichiers générés par mon outil personnalisé soient cachés, mais je ne trouve aucune documentation sur la façon dont cela est fait.
Un exemple de ce que je cherche est le code WPF derrière les fichiers. Ces fichiers ne sont pas affichés dans la vue projet Visual Studio, mais sont compilés avec le projet et sont disponibles dans IntelliSense. Code WPF derrière les fichiers (Window1.G. I. cs, par exemple), sont générés par un outil personnalisé.
4 réponses
La solution consiste à créer une cible qui ajoute vos fichiers au groupe D'éléments de compilation plutôt que de les ajouter explicitement dans votre .fichier csproj. De cette façon Intellisense les voir et ils seront compilés dans votre exécutable, mais ils n'apparaîtront pas dans Visual Studio.
Exemple Simple
Vous devez également vous assurer que votre cible est ajoutée à la propriété CoreCompileDependsOn
afin qu'elle s'exécute avant l'exécution du compilateur.
Voici un très simple exemple:
<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);AddToolOutput</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="HiddenFile.cs" />
</ItemGroup>
</Target>
Si vous ajoutez cela au bas de votre .fichier csproj (juste avant </Project>
), votre " HiddenFile.cs " sera inclus dans votre compilation même si elle n'apparaît pas dans Visual Studio.
En utilisant un séparé .fichier cible
Au lieu de placer cela directement dans votre .fichier csproj, vous généralement placé dans un séparé .fichier cible entouré de:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
</Project>
Et importer dans votre .csproj avec <Import Project="MyTool.targets">
. Un .objectifs fichier est recommandé même pour les cas ponctuels, car il sépare votre code personnalisé de la substance .csproj qui est maintenu par Visual Studio.
La Construction de l'généré nom de fichier(s)
Si vous créez un outil généralisé et / ou utilisez un outil distinct .fichier cible, vous ne voulez probablement pas lister explicitement chaque fichier caché. Au lieu de cela, vous souhaitez générer les noms de fichiers cachés à partir d'autres paramètres du projet. Par exemple si vous voulez que tous les fichiers de ressources aient fichiers générés par l'outil dans le répertoire" obj", votre cible serait:
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="@(Resource->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
</ItemGroup>
</Target>
La propriété" IntermediateOutputPath "est ce que nous connaissons tous comme le répertoire" obj", mais si l'utilisateur final de votre .targets a personnalisé cela vos fichiers intermédiaires seront toujours trouvés au même endroit. Si vous préférez que vos fichiers générés se trouvent dans le répertoire principal du projet et non dans le répertoire" obj", vous pouvez laisser cela désactivé.
Si vous voulez seulement certains des fichiers d'un élément existant type à traiter par votre outil personnalisé? Par exemple, vous pouvez générer des fichiers pour tous les fichiers de pages et de ressources avec un ".XYZ " extension.
<Target Name="AddToolOutput">
<ItemGroup>
<MyToolFiles Include="@(Page);@(Resource)" Condition="'%(Extension)'=='.xyz' />
<Compile Include="@(MyToolFiles->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')"/>
</ItemGroup>
</Target>
Notez que vous ne pouvez pas utiliser la syntaxe de métadonnées comme %(Extension) dans un ItemGroup de niveau supérieur, mais vous pouvez le faire dans une cible.
À l'Aide d'un type d'élément personnalisé (aka Action de Construire)
Ce qui précède traite les fichiers qui ont un type d'élément existant tel que Page, Resource ou Compile (Visual Studio appelle cela le " Build Action"). Si vos articles sont un nouveau type de fichier, vous pouvez utiliser votre propre type d'élément personnalisé. Par exemple, si vos fichiers d'entrée sont appelés fichiers" Xyz", votre fichier de projet peut définir " Xyz " comme un type d'élément valide:
<ItemGroup>
<AvailableItemName Include="Xyz" />
</ItemGroup>
Après quoi Visual Studio vous permettra de sélectionner " Xyz " dans L'Action de construction dans les propriétés du fichier, ce qui entraîne son ajout à votre .csproj:
<ItemGroup>
<Xyz Include="Something.xyz" />
</ItemGroup>
Maintenant, vous pouvez utiliser le type d'élément " Xyz " pour créer les noms de fichiers pour la sortie de l'outil, comme nous l'avons fait précédemment avec le type d'élément "ressource":
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
</ItemGroup>
</Target>
Lorsque vous utilisez un type d'élément personnalisé, vous pouvez également gérer vos éléments par des mécanismes intégrés en les mappant à un autre type d'élément (aka Build Action). Ceci est utile si vos fichiers" Xyz " sont vraiment .fichiers cs ou .xaml ou si elles doivent être faites
EmbeddedResources. Par exemple, vous pouvez faire en sorte que tous les fichiers avec "Build Action" De Xyz soient également compilés:
<ItemGroup>
<Compile Include="@(Xyz)" />
</ItemGroup>
Ou si vos fichiers source " Xyz " doivent être stockés comme intégrés ressources, vous pouvez l'exprimer de cette façon:
<ItemGroup>
<EmbeddedResource Include="@(Xyz)" />
</ItemGroup>
Notez que le deuxième exemple ne fonctionnera pas si vous le placez dans la cible, car la cible n'est évaluée que juste avant la compilation de base. Pour que cela fonctionne dans une cible, vous devez lister le nom de la cible dans la propriété PrepareForBuildDependsOn au lieu de CoreCompileDependsOn.
Appel de votre générateur de code personnalisé à partir de MSBuild
Étant allé jusqu'à créer un .fichier cibles, vous pourriez envisager invoquer votre outil directement à partir de MSBuild plutôt que d'utiliser un événement de pré-construction séparé ou le mécanisme "outil personnalisé" défectueux de Visual Studio.
Pour ce faire:
- créez un projet de bibliothèque de classes avec une référence à Microsoft.Construire.Cadre
- ajoutez le code pour implémenter votre générateur de code personnalisé
- ajoutez une classe qui implémente ITask, et dans la méthode Execute appelez votre générateur de code personnalisé
- Ajouter un élément
UsingTask
à votre .les objectifs de fichier, et dans votre objectif ajouter un appel à votre nouvelle tâche
Voici tout ce dont vous avez besoin pour implémenter ITask:
public class GenerateCodeFromXyzFiles : ITask
{
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
public ITaskItem[] InputFiles { get; set; }
public ITaskItem[] OutputFiles { get; set; }
public bool Execute()
{
for(int i=0; i<InputFiles.Length; i++)
File.WriteAllText(OutputFiles[i].ItemSpec,
ProcessXyzFile(
File.ReadAllText(InputFiles[i].ItemSpec)));
}
private string ProcessXyzFile(string xyzFileContents)
{
// Process file and return generated code
}
}
Et voici L'élément UsingTask et une cible qui l'appelle:
<UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />
<Target Name="GenerateToolOutput">
<GenerateCodeFromXyzFiles
InputFiles="@(Xyz)"
OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">
<Output TaskParameter="OutputFiles" ItemGroup="Compile" />
</GenerateCodeFromXyzFiles>
</Target>
Notez que L'élément de sortie de cette cible place la liste des fichiers de sortie directement dans la compilation, il n'est donc pas nécessaire d'utiliser un ItemGroup distinct pour le faire.
Comment l'ancien "de l'Outil Personnalisé" mécanisme est défectueuse et pourquoi ne pas l'utiliser
Une note sur "L'outil personnalisé de Visual Studio" mécanisme: dans le cadre NET 1.x nous n'avions pas MSBuild, nous avons donc dû compter sur Visual Studio pour construire nos projets. Afin d'obtenir Intellisense sur le code généré, Visual Studio avait un mécanisme appelé "outil personnalisé" qui peut être défini dans la fenêtre des propriétés sur un fichier. Le mécanisme était fondamentalement défectueux de plusieurs façons, c'est pourquoi il a été remplacé par MSBuild targets. Certains des problèmes avec la fonctionnalité "outil personnalisé" étaient:
- un "outil personnalisé" construit le Fichier chaque fois que le fichier est édité et enregistré, pas lorsque le projet est compilé. Cela signifie que tout ce qui modifie le fichier en externe (comme un système de contrôle de révision) ne met pas à jour le fichier généré et vous obtenez souvent du code périmé dans votre exécutable.
- la sortie D'un "outil personnalisé" devait être livrée avec votre arborescence source à moins que votre destinataire n'ait également Visual Studio et votre "outil personnalisé".
- "L'Outil Personnalisé" devait être installé dans le registre et ne pouvait pas simplement être référence du fichier de projet.
- la sortie de "Custom Tool" n'est pas stockée dans le répertoire" obj".
Si vous utilisez l'ancienne fonctionnalité "outil personnalisé", je vous recommande fortement de passer à L'utilisation D'une tâche MSBuild. Il fonctionne bien avec Intellisense et vous permet de construire votre projet sans même installer Visual Studio (Tout ce dont vous avez besoin est NET Framework).
Quand votre tâche de construction personnalisée s'exécutera-t-elle?
En général, votre tâche de construction personnalisée exécuter:
- En arrière-plan lorsque Visual Studio ouvre la solution, si le fichier généré n'est pas à jour
- en arrière-plan chaque fois que vous enregistrez L'un des fichiers d'entrée dans Visual Studio
- Tout moment, vous construisez, si le fichier généré n'est pas à jour
- chaque fois que vous reconstruisez
, Pour être plus précis:
- une génération incrémentielle IntelliSense est exécutée au démarrage de Visual Studio et chaque fois qu'un fichier est enregistré dans Visual Studio. Cela exécutera votre générateur si le fichier de sortie est manquant l'un des fichiers d'entrée sont plus récents que la sortie du générateur.
- une génération incrémentielle régulière est exécutée chaque fois que vous utilisez une commande "Build" ou "Run" dans Visual Studio (y compris les options de menu et en appuyant sur F5), ou lorsque vous exécutez "MSBuild" à partir de la ligne de commande. Comme la construction incrémentale IntelliSense, elle n'exécutera également votre générateur que si le fichier généré n'est pas à jour
- une version complète régulière est exécutée chaque fois que vous utilisez l'une des commandes "reconstruire" dans Visual Studio, ou lorsque vous exécutez "MSBuild / t: reconstruire" à partir de la ligne de commande. Il fonctionnera toujours votre générateur s'il y a des entrées ou des sorties.
Vous pouvez forcer votre générateur à s'exécuter à d'autres moments, par exemple lorsque certaines variables d'environnement changent, ou le forcer à s'exécuter de manière synchrone plutôt qu'en arrière-plan.
Pour faire redémarrer le générateur même si aucun fichier d'entrée n'a changé, le meilleur moyen est généralement d'ajouter une entrée supplémentaire à votre cible qui est un fichier d'entrée factice stocké dans le répertoire" obj". Ensuite, chaque fois qu'une variable d'environnement ou des changements de paramètres externes qui devraient forcer votre outil de générateur à redémarrer, il suffit de toucher ce fichier (ie. créer ou mettre à jour sa date de modification).
Pour forcer le générateur à fonctionner de manière synchrone plutôt que D'attendre Qu'IntelliSense l'exécute en arrière-plan, utilisez simplement MSBuild pour construire votre cible particulière. Cela pourrait être aussi simple que de l'exécution de "MSBuild / t: GenerateToolOutput", ou VSIP peut fournir un moyen de construire pour appeler des cibles de construction personnalisées. Alternativement, vous pouvez simplement appeler la commande Build et attendre qu'elle se termine.
Notez que "Input files" dans cette section fait référence à tout ce qui est répertorié dans L'attribut" Inputs " de l'élément cible.
Les notes Finales
Vous pouvez obtenir des avertissements de Visual Studio qu'il ne sait pas s'il faut faire confiance à votre outil personnalisé .les objectifs de fichier. Pour résoudre ce problème, ajoutez-le à la clé de Registre HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\MSBuild \ SafeImports.
Voici un résumé de ce qu'est un réel .le fichier targets ressemblerait à toutes les pièces en place:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateToolOutput</CoreCompileDependsOn>
</PropertyGroup>
<UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />
<Target Name="GenerateToolOutput" Inputs="@(Xyz)" Outputs="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">
<GenerateCodeFromXyzFiles
InputFiles="@(Xyz)"
OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">
<Output TaskParameter="OutputFiles" ItemGroup="Compile" />
</GenerateCodeFromXyzFiles>
</Target>
</Project>
Faites-moi savoir si vous avez des questions ou s'il y a quelque chose ici que vous n'avez pas compris.
Pour masquer les éléments de Visual Studio, ajoutez une propriété de métadonnées Visible
à l'élément. Les métadonnées InProject
le font apparemment aussi.
Visibles: http://msdn.microsoft.com/en-us/library/ms171468(SV.90).aspx
InProject: http://blogs.msdn.com/b/jomo_fisher/archive/2005/01/25/360302.aspx
<ItemGroup>
<Compile Include="$(AssemblyInfoPath)">
<!-- either: -->
<InProject>false</InProject>
<!-- or: -->
<Visible>false</Visible>
</Compile>
</ItemGroup>
La seule façon de le faire est d'ajouter le fichier généré pour avoir une dépendance sur le fichier que vous voulez caché derrière-dans le fichier proj.
Par exemple:
<ItemGroup>
<Compile Include="test.cs" />
<Compile Include="test.g.i.cs">
<DependentUpon>test.cs</DependentUpon>
</Compile>
</ItemGroup>
Si vous avez supprimé L'élément DependentUpon, le fichier apparaît à côté de l'autre fichier au lieu de derrière ... comment votre générateur ajoute-t-il les fichiers? pouvez-vous nous guider à travers le cas d'utilisation et comment vous aimeriez qu'il fonctionne?
Je pense que vous voulez regarder ici: http://msdn.microsoft.com/en-us/library/ms171453.aspx.
Plus précisément, la section" Création D'éléments pendant L'exécution".