Um construtor de projectos incrementais é um objecto que manipula os recursos num projecto de determinada maneira. Os construtores de projectos incrementais são geralmente usados para aplicar uma transformação a um recurso a fim de produzir um recurso ou artefacto de outra espécie. Os recursos criados por um construtor são geralmente marcados como recursos derivados.
Os plug-ins contribuem com construtores de projectos incrementais para a plataforma a fim de implementar transformações de recurdsos especializados. Por exemplo, as Java development tools (JDT) definem um construtor de projectos incrementais que compila um ficheiro origem Java num ficheiro de classes em qualquer altura que seja adicionado ou modificado um ficheiro num projecto Java. Também acompanha os ficheiros dependentes e recompila-os se necessário.
Do ponto de vista de uma API, a plataforma define dois tipos de construções básicas:
As construções incrementais são semeadas com um delta de alterações de recursos. O delta reflecte o efeito real de todas as alterações de recursos desde a última vez que o construtor construiu o projecto. Este delta é semelhante àquele utilizado em eventos de alteração de recursos.
Os projectos podem ser periodicamente limpos pelo utilizador a fim de forçar uma reconstrução de um projecto completo na ocasião seguinte em que seja realizada uma construção incremental nesse projecto. Desmarcar um projecto remove informações de construção como, por exemplo, marcadores de problemas e ficheiros de classes.
Passamos a demonstrar os construtores com um exemplo. O compilador Java das JDT é conduzido por um construtor de projectos incrementais que recompila os ficheiros num projecto que sejam afectados pelas alterações. Quando se desencadeia uma construção total (ou uma construção incremental após limpeza), todos os ficheiros .java no projecto são compilados. Os eventuais problemas de compilação são adicionados como marcadores de problemas aos ficheiros .java afectados. Quando se desencadeia uma construção incremental, o construtor recompila selectivamente os ficheiros .java adicionados, alterados ou afectados de algum modo que estejam descritos no delta de recursos e actualiza os marcadores de problemas conforme o necessário. Os ficheiros .class ou os marcadores que já não forem apropriados são removidos.
A construção incremental tem vantagens óbvias no rendimento de projectos com centenas ou milhares de recursos, a maioria dos quais se encontra inalterada em dada altura.
O desafio técnico da construção incremental consiste em determinar exactamente o que necessita de ser reconstruído. Por exemplo, o estado interno mantido pelo construtor Java inclui coisas como um gráfico de dependências e uma lista de problemas de compilação reportados. Estas informações são usadas durante uma construção incremental para identificar quais as classes que necessitam de recompilação em resposta a uma alteração num recurso Java.
Embora a estrutura básica de construção seja definida na plataforma, o verdadeiro trabalho realiza-se no código do construtor. Os padrões para implementar construtores incrementais complexos estão além do âmbito desta abordagem, dado que a implementação depende da concepção específica do construtor.
Pode ser invocado um construtor explicitamente de uma das seguintes formas:
Na prática, o utilizador da área de trabalho desencadeia uma construção seleccionando comandos correspondentes no menu do navegador de recursos.
Os construtores de projectos incrementais também são invocados implicitamente pela plataforma durante uma construção automática. Se activadas, as construções automáticas executam-se sempre que o espaço de trabalho seja alterado.
O ponto de extensão org.eclipse.core.resources.builders é utilizado para contribuir com um construtor de projectos incrementais para a plataforma. A marcação seguinte mostra como o plug-in hipotético com.example.builders poderia contribuir com um construtor de projectos incrementais.
<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>
A classe identificada no ponto de extensão deve estender a classe de plataforma IncrementalProjectBuilder.
public class BuilderExample extends IncrementalProjectBuilder { IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { // add your build logic here return null; } protected void startupOnInitialize() { // add builder init logic here } protected void clean(IProgressMonitor monitor) { // add builder clean logic here } }
O processamento de construção começa com o método build(), o qual inclui informações sobre o tipo de construção que foi solicitada. A construção é um dos seguintes valores:
Se tiver sido solicitada uma construção incremental, é facultado um delta de recursos para descrever as alterações aos recursos desde a última construção. A porção de código seguinte refina ainda mais o método 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; }
Por vezes acontece que ao construir o projecto"X", um construtor precise de informação sobre alterações noutro projecto "Y". (Por exemplo, se uma classe Java em X implementar uma interface facultada em Y.) Ao construir X, fica disponível um delta para Y chamando getDelta(Y). Para garantir que a plataforma pode facultar tais deltas, o construtor de X deve ter declarado a dependência entre X e Y mediante devolução de uma matriz que contenha Y de uma chamada build() anterior. Se o construtor não tiver dependências, poderá simplesmente devolver nulo. Consulte IncrementalProjectBuilder para mais informações.
A lógica necessária para processar uma construção total é específica do plug-in. Poderá implicar visitar cada recurso no projecto ou até examinar outros projectos se houver dependências entre projectos. A porção de código seguinte sugere como se poderia implementar uma construção total.
protected void fullBuild(final IProgressMonitor monitor) throws CoreException { try { getProject().accept(new MyBuildVisitor()); } catch (CoreException e) { } }
O visitante de construção iria realizar a construção para o recurso específico (e responder verdadeiro para continuar a visitar todos os recursos descendentes).
class MyBuildVisitor implements IResourceVisitor { public boolean visit(IResource res) { //build the specified resource. //return true to continue visiting children. return true; } }
O processo de visita continua até ter sido percorrida toda a árvore de recursos.
Ao realizar uma construção incremental, o construtor funciona com um delta de alterações de recursos em vez de uma árvore de recursos completa.
protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException { // the visitor does the work. delta.accept(new MyBuildDeltaVisitor()); }
O processo de visita continua até ter sido percorrida toda a árvore delta de recursos. A natureza específica das alterações é semelhante à descrita em Implementar um ouvinte de alterações de recursos. Uma diferença importante é que com construtores de projectos incrementais, trabalha-se com um delta de recursos baseado em determinado projecto e não no espaço de trabalho inteiro.
A área de trabalho permite aos utilizadores desmarcar um projecto ou conjunto de projectos antes de iniciar uma construção. Esta função permite ao utilizador forçar uma reconstrução de raiz somente em certos projectos. Os construtores devem implementar este método para desmarcar marcadores de problemas e recursos derivados no projecto.
Para disponibilizar um construtor para determinado projecto, este deve ser incluído na especificação de construção do projecto. A especificação de construção de um projecto é uma lista de comandos a executar, em sequência, quando o projecto é construído. Cada comando chama um único construtor de projectos incrementais.
NOTA: O nome do construtor num comando de construção é o ID totalmente qualificado da extensão do construtor. O ID totalmente qualificado de uma extensão é criado combinando o ID do plug-in com o ID da extensão simples no ficheiro plugin.xml. Por exemplo, um construtor com o ID de extensão simples "omeuconstrutor" no plug-in "com.example.builders" teria o nome "com.example.builders.omeuconstrutor"
A porção de código seguinte adiciona um novo construtor como primeiro construtor à lista de construtores existente.
final String BUILDER_ID = "com.example.builders.omeuconstrutor"; 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) { //add builder to project ICommand command = desc.newCommand(); command.setBuilderName(BUILDER_ID); ICommand[] newCommands = new ICommand[commands.length + 1]; // Add it before other builders. System.arraycopy(commands, 0, newCommands, 1, commands.length); newCommands[0] = command; desc.setBuildSpec(newCommands); project.setDescription(desc, null); }
A configuração do construtor de um projecto realiza-se uma única vez, geralmente na criação do projecto. Uma forma comum de associar um construtor a um projecto é através da configuração de uma natureza de projecto.