Un generatore incrementale di progetto è un oggetto che utilizza le risorse di un progetto in un modo definito dal generatore stesso. I generatori incrementali di progetto vengono spesso utilizzati per applicare una trasformazione a una risorsa in modo da produrre una risorsa o un elemento di altro tipo.
I plug-in forniscono alla piattaforma i generatori incrementali di progetto per implementare le trasformazioni di risorse specializzate. Ad esempio, il plug-in JDT (Java Development Tooling) fornito con il SDK della piattaforma definisce un generatore incrementale di progetto che effettua la compilazione di un file di origineJava in un file di classe ogni volta che un file viene aggiunto o modificato in un progetto Java e ricompila qualsiasi altro file interessato dalla modifica.
La piattaforma definisce due tipi di generazione:
Le generazioni incrementali vengono impostate con un delta delle modifiche delle risorse. Il delta riflette l'effetto di rete prodotto da tutte le modifiche apportate alle risorse dal momento in cui il generatore ha eseguito l'ultima generazione del progetto. Questo delta è simile a quello utilizzato negli eventi di modifica delle risorse.
I generatori possono essere descritti in modo migliore attraverso esempi. Il JDT (Java Development Tooling) fornisce un compilatore Java che viene guidato da un generatore incrementale di progetto Java per effettuare la ricompilazione di file influenzati da modifiche. Quando viene attivata una generazione completa, tutti i file .java del progetto vengono compilati. Eventuali problemi riscontrati vengono aggiunti come indicatori di problema nei file .java interessati. Quando viene attivata una generazione incrementale, il generatore ricompila in modo selettivo i file .java aggiunti, modificati o altrimenti influenzati che sono descritti nel delta delle risorse e aggiorna, se necessario, gli indicatori di problema. I file .class o gli indicatori che non sono più appropriati vengono rimossi.
La generazione incrementale ha dei chiari vantaggi di prestazione per i progetti contenenti centinaia o migliaia di risorse, la maggior parte delle quali resta invariata in un momento specifico.
L'obiettivo da un punto di vista tecnico della funzione di generazione incrementale consiste nel determinare esattamente le risorse che devono essere create nuovamente. Ad esempio, lo stato interno mantenuto dal generatore Java include elementi quali un grafico delle dipendenze e un elenco dei problemi di compilazione segnalati. Queste informazioni vengono utilizzate durante una generazione incrementale per identificare le classi che devono essere ricompilate in risposta a una modifica di una risorsa Java.
Sebbene la struttura di base per la funzione di generazione sia definita nella piattaforma, il lavoro vero e proprio avviene nel plug-in dell'utente. I modelli per l'implementazione di generatori incrementali complessi vanno oltre l'obiettivo di questa discussione, poiché l'implementazione è dipendente dal design dello specifico generatore.
Un generatore può essere richiamato esplicitamente in uno dei seguenti modi:
In pratica, l'utente del workbench attiva una generazione selezionando i comandi corrispondenti nel menu di selezione delle risorse.
I generatori incrementali di progetto vengono anche richiamati esplicitamente dalla piattaforma durante una generazione automatica. Se abilitate, le generazioni automatiche vengono eseguite ogni volta che lo spazio di lavoro viene modificato.
Il punto di estensione org.eclipse.core.resources.builders viene utilizzato per fornire alla piattaforma un generatore incrementale di progetto. Il codice seguente mostra il modo in cui l'ipotetico plug-in com.example.builders è in grado di fornire un generatore incrementale di progetto.
<extension id="mybuilder" name="My Sample Builder" point="org.eclipse.core.resources.builders"> <builder <run class="com.example.builders.BuilderExample"> <parameter name="optimize" value="true" /> <parameter name="comment" value="Builder comment" /> </run> </builder> </extension>
La classe identificata nel punto di estensione deve estendere la classe della piattaforma IncrementalProjectBuilder.
public class BuilderExample extends IncrementalProjectBuilder { IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { // aggiungere in questo punto la logica della generazione return null; } protected void startupOnInitialize() { // aggiungere in questo punto la logica di inizializzazione del generatore } }
L'elaborazione della generazione inizia con il metodo build(), che comprende informazioni sul tipo di generazione richiesta, FULL_BUILD, INCREMENTAL_BUILD o AUTO_BUILD. Se è stata richiesta una generazione incrementale, viene fornito un delta delle risorse per descrivere le modifiche avvenute nella risorsa del progetto a partire dall'ultima generazione. Il frammento di codice seguente specifica ulteriormente il metodo build().
protected IProject[] build(int kind, Map args, IProgressMonitor monitor throws CoreException { if (kind == IncrementalProjectBuilder.FULL_BUILD) { fullBuild(monitor); } else { IResourceDelta delta = getDelta(getProject()); if (delta == null) { fullBuild(monitor); } else { incrementalBuild(delta, monitor); } } return null; }
A volte può accadere che, durante la creazione del progetto "X", un generatore abbia bisogno delle informazioni sulle modifiche apportate in un altro progetto "Y". Ad esempio, se una classe Java in X implementa un'interfaccia fornita in Y. Durante la generazione di X, è disponibile un delta per Y mediante la chiamata a getDelta(Y). Per essere certi che la piattaforma sia in grado di fornire questi delta, è necessario che il generatore di X abbia dichiarato la dipendenza tra X e Y restituendo una matrice contenente Y derivato da una precedente chiamata a build(). Se un generatore non ha dipendenze, può semplicemente restituire un valore nullo. Per ulteriori informazioni, vedere IncrementalProjectBuilder.
La logica necessaria per elaborare una richiesta di generazione completa è specifica per il plug-in. Può comportare una visita alle risorsa del progetto (se la generazione è stata attivata per un progetto) oppure l'esame di altri progetti (se esistono dipendenze tra progetti). Il seguente frammento di codice suggerisce la modalità di implementazione di una generazione completa.
protected void fullBuild(final IProgressMonitor monitor) throws CoreException { try { getProject().accept(new MyBuildVisitor()); } catch (CoreException e) { } }
Il visitatore della generazione potrebbe voler eseguire la generazione per la risorsa specifica (e restituire true per continuare l'esame di tutte le risorse secondarie).
class MyBuildVisitor implements IResourceVisitor { public boolean visit(IResource res) { //creare la risorsa specificata. //restituire true per continuare l'esame delle risorse secondarie. return true; } }
Il processo di visita continua fino al completamento dell'esame di tutta la struttura delle risorse.
Quando si esegue una generazione incrementale, si utilizza un delta delle modifiche delle risorse anziché l'intero progetto.
protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException { // il visitatore esegue il lavoro. delta.accept(new MyBuildDeltaVisitor()); }
Notare che, per una generazione incrementale, il visitatore della generazione utilizza una struttura di delta delle risorse anziché una struttura di risorse completa.
Il processo di visita continua fino al completamento dell'esame di tutta la struttura di delta delle risorse. La natura specifica delle modifiche è simile a quanto descritto in Implementazione di un listener di modifica delle risorse. Un'importante differenza consiste nel fatto che con i generatori incrementali di progetto si utilizza un delta delle risorse basato su un particolare progetto, non sull'intero spazio di lavoro.
Per rendere disponibile un generatore per un particolare progetto, il generatore deve essere incluso nella specifica di generazione del progetto. La specifica di generazione di un progetto è un elenco dei comandi da eseguire, in sequenza, quando il progetto viene creato. Ogni comando specifica un singolo generatore incrementale di progetto.
Il seguente frammento di codice aggiunge un nuovo generatore come primo generatore del relativo elenco.
IProjectDescription desc = project.getDescription(); ICommand[] commands = desc.getBuildSpec(); boolean found = false; for (int i = 0; i < commands.length; ++i) { if (commands[i].getBuilderName().equals(BUILDER_ID)) { found = true; break; } } if (!found) { //aggiungere il generatore al progetto ICommand command = desc.newCommand(); command.setBuilderName(BUILDER_ID); ICommand[] newCommands = new ICommand[commands.length + 1]; // Aggiungerlo prima degli altri generatori. System.arraycopy(commands, 0, newCommands, 1, commands.length); newCommands[0] = command; desc.setBuildSpec(newCommands); project.setDescription(desc, null); }
La configurazione del generatore di un progetto avviene una sola volta, generalmente quando il progetto sta per essere creato.