Een incrementele projectbuilder is een object dat de resources in een project op een bepaalde manier bewerkt. Incrementele projectbuilders worden vaak gebruikt om een omzetting van een resource toe te passen en zo een resource of artefact van een ander soort te maken. Resources die door een builder zijn gemaakt, worden doorgaans gemarkeerd als afgeleide resources.
Plugins leveren incrementele projectbuilders aan het platform om speciale resourceomzettingen te kunnen implementeren. JDT (Java Development Tools) definieert bijvoorbeeld een incrementele projectbuilder die een Java-bronbestand naar een klassenbestand compileert zodra een bestand wordt toegevoegd of aangepast in een Java-project. JDT houdt ook afhankelijke bestanden bij en compileert ze opnieuw als dat nodig is.
Vanuit de API gezien definieert het platform twee basistypen voor builds:
Incrementele builds krijgen beginwaarden aan de hand van een resourcewijzigingsdelta. De delta geeft het netto-effect aan van alle resourcewijzigingen sinds de builder het project voor de laatste keer heeft opgebouwd. De delta komt overeen met degene die wordt gebruikt in resourcewijzigingsevents.
Projecten kunnen periodiek door de gebruiker worden opgeschoond om zo een nieuwe build te laten maken van een volledig project bij de volgende gelegenheid dat een incrementele build van het project wordt uitgevoerd. Bij het opschonen van een project wordt buildinformatie verwijderd, bijvoorbeeld probleemmerktekens en klassenbestanden.
Builders zijn het best te begrijpen aan de hand van voorbeelden. Het JDT Java-compileerprogramma wordt aangestuurd door een incrementele Java-projectbuilder die de bestanden in een project opnieuw compileert als er wijzigingen op hen van toepassing zijn. Als een volledige build wordt geïnitieerd (of een incrementele build na een opschoningsactie), worden alle .java-bestanden in het project gecompileerd. Eventuele compileerproblemen worden als merkteken toegevoegd in de betreffende .java-bestanden. Als een incrementele build wordt gestart, compileert de builder selectief de toegevoegde, gewijzigde of op een andere manier betrokken .java-bestanden die zijn beschreven in de resourcedelta, en werkt de probleemmerktekens bij. .class-bestanden of merktekens die niet meer van toepassing zijn, worden verwijderd.
Het maken van incrementele builds biedt prestatievoordelen voor projecten met honderden of duizenden resources, waarvan de meeste binnen een bepaalde tijd niet gewijzigd worden.
De technische uitdaging van incrementele builds is om precies te bepalen welke onderdelen opnieuw gebouwd moeten worden. De interne status die de Java-builder bijhoudt, bevat bijvoorbeeld een dependencygrafiek en een lijst met gerapporteerde compileerproblemen. Deze informatie wordt tijdens een incrementele build gebruikt om te bepalen welke klassen opnieuw gebouwd moeten worden als gevolg van een wijziging in een Java-resource.
Hoewel de basisstructuur voor het bouwen in het platform is gedefinieerd, wordt het echte werk in de buildercode gedaan. We willen hier niet te diep ingaan op het implementeren van complexe incrementele builders, omdat de implementatie afhankelijk is van het specifieke builderontwerp.
U kunt een builder expliciet oproepen op een van de volgende manieren:
In de praktijk start de workbenchgebruiker een build door de betreffende opdrachten te kiezen in het resourcenavigatormenu.
Incrementele projectbuilders worden ook impliciet door het platform aangeroepen tijdens het automatisch bouwen. Als automatisch bouwen is ingeschakeld, wordt de optie altijd uitgevoerd als het werkgebied wordt gewijzigd.
Het extensiepunt org.eclipse.core.resources.builders wordt gebruikt voor het aanleveren van een incrementele projectbuilder aan het platform. In de volgende markup ziet u hoe de hypothetische plugin com.example.builders een incrementele projectbuilder zou kunnen aanleveren.
<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>
De klasse die in het extensiepunt wordt geïdentificeerd, moet de platformklasse IncrementalProjectBuilder uitbreiden.
public class BuilderExample extends IncrementalProjectBuilder { IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { // voeg hier uw buildlogica toe return null; } protected void startupOnInitialize() { // voeg hier uw initialisatielogica toe } protected void clean(IProgressMonitor monitor) { // voeg hier uw opschoningslogica toe } }
Het bouwproces begint met de methode build(), die informatie bevat over het soort build dat aangevraagd is. De build heeft een van de volgende waarden:
Als een incrementele build is aangevraagd, wordt een resourcedelta ter beschikking gesteld. Hierin worden de wijzigingen in de resources sinds de laatste build vermeld. In het volgende codefragment gaan we verder in op de methode 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; }
Het kan bij het bouwen van project "X," voorkomen dat een builder informatie nodig heeft over wijzigingen in een ander project "Y." (bijvoorbeeld als een Java-klasse in X een interface implementeert die door Y wordt aangeboden). Tijdens het bouwen van X, wordt een delta voor Y gemaakt door getDelta(Y) aan te roepen. Om te zorgen dat het platform zulke delta's kan produceren, moet de builder van X een dependency tussen X en Y hebben gedeclareerd door een array te retourneren met daarin Y uit een vorige build()-aanroep. Als een builder geen dependency's heeft, kan deze gewoon null retourneren. Zie Incrementele projectbuilder voor verdere informatie.
De vereiste logica om een volledige-buildaanvraag te verwerken is specifiek voor de plugin. Het kan nodig zijn om alle resources in het project te inspecteren of zelfs resources in andere projecten als er dependency's tussen de projecten bestaan. In het volgende codefragment ziet u hoe u een volledige build kunt implementeren.
protected void fullBuild(final IProgressMonitor monitor) throws CoreException { try { getProject().accept(new MyBuildVisitor()); } catch (CoreException e) { } }
De buildbezoeker voert de build uit voor een bepaalde resource (en geeft met 'true' aan door te gaan met de inspectie van alle onderliggende resources).
class MyBuildVisitor implements IResourceVisitor { public boolean visit(IResource res) { // de opgegeven resource bouwen. // true retourneren om door te gaan naar de onderliggende resources. return true; } }
Het inspectieproces gaat door tot de volledige resourcestructuur is bezocht.
Bij het uitvoeren van een incrementele build werkt de builder met een resourcewijzigingsdelta in plaats van met een volledige resourcestructuur.
protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException { // de bezoeker doet het werk. delta.accept(new MyBuildDeltaVisitor()); }
Het inspectieproces gaat door tot de volledige resourcedeltastructuur is bezocht. Het specifieke soort van de wijzigingen is gelijk aan de beschrijving in Een resourcewijzigingslistener implementeren. Een belangrijk verschil is dat u bij incrementele projectbuilders werkt met een resourcedelta op basis van een bepaald project, niet het gehele werkgebied.
De workbench biedt gebruikers de mogelijkheid om projecten op te schonen voor het initiëren van een build. Dankzij deze mogelijkheid kan de gebruiker een nieuwe build uitvoeren voor uitsluitend bepaalde projecten. Builders moeten deze methode implementeren om eventuele probleemmarkeringen en afgeleide resources in het project op te schonen.
Om een builder voor een bepaald project beschikbaar te maken, moet deze worden opgenomen in de buildspecificaties van het project. De buildspecificaties van een project bestaan uit een lijst met uit te voeren opdrachten (in volgorde) als het project wordt gebouwd. Iedere opdracht benoemt één incrementele projectbuilder.
Opmerking: De naam van de builder in de buildopdracht is het volledig gekwalificeerde ID van de builderextensie. Het volledig gekwalificeerde ID van een extensie wordt gemaakt door het plugin-ID te combineren met het enkelvoudige extensie-ID in het bestand plugin.xml. Een builder met het eenvoudige extensie-ID "mybuilder" in de plugin "com.example.builders" krijgt de naam "com.example.builders.mybuilder"
Het volgende codefragment voegt een nieuwe builder toe als eerste builder in de lijst met bestaande builders.
final String BUILDER_ID = "com.example.builders.mybuilder"; 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) { // builder toevoegen aan project ICommand command = desc.newCommand(); command.setBuilderName(BUILDER_ID); ICommand[] newCommands = new ICommand[commands.length + 1]; // builder toevoegen vóór andere builders System.arraycopy(commands, 0, newCommands, 1, commands.length); newCommands[0] = command; desc.setBuildSpec(newCommands); project.setDescription(desc, null); }
Het configureren van een projectbuilder gebeurt slechts één keer, meestal bij het maken van het project. Een veelgebruikte manier om een builder aan een project te koppelen is door een projectsoort te configureren.