2010-06-02 29 views
26

Desidero che i file generati dal mio strumento personalizzato siano nascosti, ma non riesco a trovare alcuna documentazione su come è fatto.Come nascondere i file generati dallo strumento personalizzato in Visual Studio

Un esempio di ciò che sto cercando è il codice WPF dietro i file. Questi file non sono visualizzati nella vista del progetto Visual Studio, ma sono compilati con il progetto e sono disponibili in IntelliSense. Il codice WPF dietro i file (Window1.g.i.cs, ad esempio), viene generato da uno strumento personalizzato.

+0

dove (relativa ai file sorgente) stai salvando i file generati? – luke

+0

La directory di output è la stessa della directory di input. – jaws

+0

Cosa intendi quando dici che i file code-behind di WPF sono nascosti? Se creo un'applicazione WPF, ottengo un file denominato MainWindow.xaml, che può essere espanso per mostrare quello che credo sia il file code-behind, MainWindow.xaml.cs. – ErikHeemskerk

risposta

56

La soluzione è creare un Target che aggiunge i file al Compile ItemGroup anziché aggiungerli esplicitamente nel file .csproj. In questo modo Intellisense li vedrà e saranno compilati nel tuo eseguibile, ma non verranno visualizzati in Visual Studio.

semplice esempio

È inoltre necessario assicurarsi che il vostro obiettivo è aggiunto alla proprietà CoreCompileDependsOn in modo che eseguirà prima dell'esecuzione del compilatore.

Ecco un esempio estremamente semplice:

<PropertyGroup> 
    <CoreCompileDependsOn>$(CoreCompileDependsOn);AddToolOutput</CoreCompileDependsOn> 
</PropertyGroup> 

<Target Name="AddToolOutput"> 
    <ItemGroup> 
    <Compile Include="HiddenFile.cs" /> 
    </ItemGroup> 
</Target> 

Se si aggiunge questo alla fine del file Csproj (poco prima </Project>), i vostri "HiddenFile.cs" saranno inclusi nella compilation, anche se non appare in Visual Studio. il file

Invece di mettere questo direttamente nel file Csproj

utilizzando un .targets separati, si sarebbe generalmente collocato in un file separato .targets circondato da:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    ... 
</Project> 

e l'importazione nel tuo .csproj con <Import Project="MyTool.targets">. Un file .targets è consigliato anche per casi singoli perché separa il tuo codice personalizzato da quello in .csproj gestito da Visual Studio.

Costruire il nome del file generato (s)

Se si sta creando uno strumento generalizzato e/o utilizzando un file .targets separata, probabilmente non si desidera elencare esplicitamente ogni file nascosto. Invece si desidera generare i nomi di file nascosti da altre impostazioni nel progetto.Per esempio, se si desidera che tutti i file di risorse di avere corrispondenti file di utensili generati nella directory "obj", il vostro obiettivo sarebbe:

<Target Name="AddToolOutput"> 
    <ItemGroup> 
    <Compile Include="@(Resource->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" /> 
    </ItemGroup> 
</Target> 

La proprietà "IntermediateOutputPath" è ciò che tutti sappiamo come directory "obj" , ma se l'utente finale dei tuoi .target lo ha personalizzato, i tuoi file intermedi verranno trovati nello stesso posto. Se preferisci che i tuoi file generati siano nella directory principale del progetto e non nella directory "obj", puoi lasciarlo spento.

Se si desidera che lo del dei file di un tipo di articolo esistente venga elaborato dal proprio strumento personalizzato? Ad esempio, potresti voler generare file per tutti i file di pagina e di risorse con estensione ".xyz".

<Target Name="AddToolOutput"> 
    <ItemGroup> 
    <MyToolFiles Include="@(Page);@(Resource)" Condition="'%(Extension)'=='.xyz' /> 
    <Compile Include="@(MyToolFiles->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')"/> 
    </ItemGroup> 
</Target> 

Si noti che non è possibile utilizzare la sintassi dei metadati come% (Extension) in un'ItemGroup di livello superiore, ma si può farlo entro un bersaglio.

Utilizzo di un tipo di elemento personalizzato (aka costruire azione)

I file processi di cui sopra che hanno un tipo di elemento esistente, ad esempio pagine, risorse, o compilare (Visual Studio chiama questo il "Build Azione"). Se i tuoi articoli sono un nuovo tipo di file puoi usare il tuo tipo di oggetto personalizzato. Per esempio, se i file di input sono chiamati file "XYZ", il file di progetto possono definire "XYZ" come un tipo di elemento valido:

<ItemGroup> 
    <AvailableItemName Include="Xyz" /> 
</ItemGroup> 

dopo di che Visual Studio vi permetterà di selezionare "XYZ" in azione Costruire nelle proprietà del file, con conseguente questo viene aggiunto al tuo .csproj:

<ItemGroup> 
    <Xyz Include="Something.xyz" /> 
</ItemGroup> 

Ora è possibile utilizzare il "XYZ" tipo di elemento per creare i nomi dei file per l'uscita strumento, proprio come abbiamo fatto in precedenza con la "risorsa" tipo di articolo:

<Target Name="AddToolOutput"> 
    <ItemGroup> 
    <Compile Include="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" /> 
    </ItemGroup> 
</Target> 

Quando si utilizza un tipo di oggetto personalizzato, è possibile far sì che anche i propri elementi vengano gestiti mediante meccanismi incorporati mappandoli su un altro tipo di oggetto (noto anche come Azione build). Ciò è utile se i file "Xyz" sono in realtà file .cs o .xaml o se devono essere creati

EmbeddedResources. Per esempio è possibile causare tutti i file con "Build Azione" di Xyz a anche essere compilato:

<ItemGroup> 
    <Compile Include="@(Xyz)" /> 
</ItemGroup> 

O se i file di origine "XYZ" devono essere conservati come risorse incorporate, è possibile esprimere in questo modo:

<ItemGroup> 
    <EmbeddedResource Include="@(Xyz)" /> 
</ItemGroup> 

Si noti che il secondo esempio non funzionerà se lo si inserisce all'interno del Target, poiché il target non viene valutato fino a poco prima della compilazione del core. Per fare in modo che questo funzioni all'interno di un Target devi elencare il nome del target nella proprietà PrepareForBuildDependsOn invece di CoreCompileDependsOn.

Invocare il generatore di codice personalizzato da MSBuild

essere andati fino a creare un file .targets, si potrebbe considerare invocando il vostro strumento direttamente da MSBuild piuttosto che utilizzare un evento di pre-compilazione separata o Visual Studio di imperfetto Meccanismo "Strumento personalizzato".

Per fare questo:

  1. Creare un progetto Libreria di classi con un riferimento a Microsoft.Build.Framework
  2. Aggiungere il codice per implementare il generatore di codice personalizzato
  3. aggiungere una classe che implementa ITask, e nel metodo Execute chiama il tuo generatore di codice personalizzato
  4. Aggiungi un elemento UsingTask al tuo file .targets e nel tuo target aggiungi una chiamata alla tua nuova attività

Qui è tutto ciò che serve per implementare 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 
    } 
} 

E qui è l'elemento UsingTask e un obiettivo che lo chiama:

<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> 

noti che elemento di uscita di questo obiettivo pone la lista di uscita file direttamente in Compile, quindi non è necessario utilizzare un ItemGroup separato per farlo.

non come il vecchio meccanismo di "strumento personalizzato" è imperfetto e perché usarlo

Una nota riguardo il meccanismo di "strumento personalizzato" di Visual Studio: In .NET Framework 1.x non abbiamo avuto MSBuild, quindi abbiamo dovuto affidarci a Visual Studio per costruire i nostri progetti. Per ottenere Intellisense sul codice generato, Visual Studio disponeva di un meccanismo denominato "Strumento personalizzato" che può essere impostato nella finestra Proprietà su un file. Il meccanismo era fondamentalmente difettoso in vari modi, motivo per cui è stato sostituito con gli obiettivi di MSBuild. Alcuni dei problemi con la funzionalità "Strumento personalizzato" erano:

  1. Uno "Strumento personalizzato" costruisce il file generato ogni volta che il file viene modificato e salvato, non quando il progetto è compilato. Ciò significa che qualsiasi cosa che modifica il file esternamente (come un sistema di controllo di revisione) non aggiorna il file generato e spesso ottieni codice stantio nell'eseguibile.
  2. L'output di uno "Strumento personalizzato" doveva essere fornito con l'albero di origine a meno che il destinatario non avesse sia Visual Studio sia il tuo "Strumento personalizzato".
  3. Lo "Strumento personalizzato" doveva essere installato nel registro e non poteva semplicemente essere referenziato dal file di progetto.
  4. L'output dello "Strumento personalizzato" non è memorizzato nella directory "obj".

Se si utilizza la vecchia funzione "Strumento personalizzato", si consiglia vivamente di passare all'utilizzo di un'attività MSBuild. Funziona bene con Intellisense e ti consente di creare il tuo progetto senza nemmeno installare Visual Studio (tutto ciò che ti serve è NET Framework).

Quando verrà eseguita l'attività di creazione personalizzata?

In generale il vostro compito di generazione personalizzata verrà eseguito:

  • In sottofondo quando Visual Studio apre la soluzione, se il file generato non è aggiornato
  • Sullo sfondo ogni volta che si salva un i file di input in Visual Studio
  • Ogni volta che si genera, se il file generato non è aggiornato
  • Ogni volta che si ricostruisce

Per essere più precisi:

  1. Una generazione incrementale IntelliSense viene eseguito quando si avvia Studio Visive e ogni volta che un file viene salvato all'interno di Visual Studio. Questo eseguirà il generatore se il file di output manca uno dei file di input più recenti rispetto all'uscita del generatore.
  2. Una build incrementale regolare viene eseguita ogni volta che si utilizza qualsiasi comando "Build" o "Esegui" in Visual Studio (incluse le opzioni di menu e premendo F5) o quando si esegue "MSBuild" dalla riga di comando. Come la build incrementale IntelliSense, eseguirà anche il generatore solo se il file generato non è aggiornato
  3. Una generazione completa normale viene eseguita ogni volta che si utilizza uno dei comandi "Ricostruisci" in Visual Studio o quando si esegue " MSBuild/t: Ricostruisci "dalla riga di comando. Avvia sempre il tuo generatore se ci sono ingressi o uscite.

È possibile forzare l'esecuzione del generatore in altri momenti, ad esempio quando alcune variabili di ambiente cambiano o forzare l'esecuzione in modo sincrono anziché in background.

  • per causare il generatore di eseguire nuovamente anche quando nessun file di input sono cambiati, il modo migliore è di solito per aggiungere un ingresso aggiuntivo per il vostro obiettivo che è un file di input fittizio memorizzato nella directory "obj". Quindi, ogni volta che si modifica una variabile di ambiente o alcune impostazioni esterne che dovrebbero forzare la rigenerazione del proprio strumento generatore, è sufficiente toccare questo file (ad esempio crearlo o aggiornare la data di modifica).

  • Per forzare il generatore a funzionare in modo sincrono anziché attendere che IntelliSense lo esegua in background, basta usare MSBuild per creare il target specifico. Questo potrebbe essere semplice come eseguire "MSBuild/t: GenerateToolOutput", oppure VSIP può fornire un modo integrato per chiamare obiettivi di compilazione personalizzati. In alternativa puoi semplicemente invocare il comando Crea e attendere che si completi.

Nota che "File di input" in questa sezione si riferisce a qualsiasi cosa sia elencata nell'attributo "Ingressi" dell'elemento Target.

Note finali

si possono ottenere gli avvertimenti da Visual Studio che non sa se fidarsi di file lo strumento personalizzato .targets. Per risolvere questo problema, aggiungilo alla chiave di registro HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ VisualStudio \ 9.0 \ MSBuild \ SafeImports.

Ecco un riepilogo di ciò che è reale.file di obiettivi sarebbe simile con tutti i pezzi a posto:

<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> 

Per favore fatemi sapere se avete domande o c'è qualcosa qui non hai capito.

+0

Grazie per la risposta dettagliata. Solo una domanda: l'approccio dello strumento personalizzato mi ha permesso di attivare in modo programmatico la generazione del file generato, all'interno del mio VSIP. Sarò in grado di fare qualcosa di simile con il tuo approccio? – jaws

+0

Sì, esistono diversi modi per controllare quando viene eseguita l'attività di creazione personalizzata. Ho aggiunto un nuovo "Quando verrà eseguita l'attività di creazione personalizzata?" sezione alla mia risposta che spiega come funziona e offre alcune opzioni. –

+0

BTW, per VS2010 usare invece 'HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ VisualStudio \ 10.0 \ MSBuild \ SafeImports'. – devstuff

0

L'unico modo che conosco è di aggiungere il file generato per avere una dipendenza dal file che si desidera nascondere dietro - nel file proj.

Ad esempio:

<ItemGroup> 
    <Compile Include="test.cs" /> 
    <Compile Include="test.g.i.cs"> 
     <DependentUpon>test.cs</DependentUpon> 
    </Compile> 
    </ItemGroup> 

Se è stato rimosso l'elemento DependentUpon allora il file compare accanto all'altro file invece di dietro di esso ... come fa il vostro generatore di aggiungere i file? puoi guidarci attraverso il caso d'uso e come vorresti che funzionasse?

+0

Sto cercando di nascondere del tutto il file generato. Per definizione, i file generati dagli strumenti personalizzati dipendono dallo strumento personalizzato e Visual Studio ne gestisce la generazione. – jaws

0

Penso che vogliate guardare qui: http://msdn.microsoft.com/en-us/library/ms171453.aspx.

In particolare, la sezione "Creazione di articoli durante l'esecuzione".

+0

Questo file deve essere presente in fase di progettazione. Ho bisogno delle classi che contiene disponibili all'utente tramite intellisense, proprio come lo è il codice WPF. – jaws

13

Per nascondere elementi da Visual Studio, aggiungere una proprietà di metadati Visible all'elemento. I metadati InProject fanno questo anche apparentemente.

Visibile: http://msdn.microsoft.com/en-us/library/ms171468(VS.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> 
Problemi correlati