Un constructor de proyectos incremental es un objeto que manipula los recursos de un proyecto siguiendo un procedimiento definido por el constructor. Los constructores de proyectos incrementales se utilizan a menudo para aplicar una transformación en un recurso a fin de producir un recurso o componente de otro tipo.
Los conectores contribuyen con constructores de proyectos incrementales a la plataforma con la finalidad de implementar transformaciones específicas en recursos. Por ejemplo, el conector de Herramientas de desarrollo Java (JDT) que se proporciona con el SDK de la plataforma define un constructor de proyectos incremental que compila un archivo fuente Java en un archivo de clase cada vez que se añade o modifica un archivo en un proyecto Java y recompila cualquier otro archivo afectado por el cambio.
La plataforma define dos tipos de construcciones:
Construcción completa, que efectúa una construcción desde el principio. Trata todos los recursos de los proyectos como si el constructor nunca los hubiera visto.
Construcción incremental, que utiliza el "último estado de la construcción", mantenido internamente por el constructor, para efectuar una construcción optimizada basada en los cambios efectuados desde la última construcción.
Las construcciones incrementales se generan con un valor delta de modificación de recurso. El delta refleja el efecto final de todas las modificaciones en los recursos desde la última vez que el constructor construyó el proyecto. Este delta es similar al utilizado en los eventos de modificación de recurso.
La mejor manera de explicar los constructores es mediante ejemplos. Las Herramientas de desarrollo Java (JDT) proporcionan un compilador Java controlado por un constructor de proyectos incremental Java para recompilar archivos que han sufrido modificaciones. Cuando se activa una construcción completa, se compilan todos los archivos .java del proyecto. Todos los problemas de compilación encontrados se añaden como marcadores de problemas en los archivos .java afectados. Cuando se activa una construcción incremental, el constructor recompila de manera selectiva los archivos .java añadidos, modificados o que hayan sufrido otro tipo de alteración que se describen en el delta de recursos y, en caso necesario, actualiza los marcadores de problemas. Se eliminan todos los archivos o marcadores .class que ya no son adecuados.
La construcción incremental proporciona ventajas de rendimiento obvias a objetos con una gran cantidad de recursos, la mayoría de los cuales permanecen sin modificar en un momento determinado.
El reto técnico para la construcción incremental es determinar de manera exacta qué se debe reconstruir. Por ejemplo, el estado interno mantenido por el compilador Java incluye aspectos, tales como un gráfico de dependencia y una lista de los problemas de compilación que se han producido. Esta información se utiliza durante una construcción incremental para identificar qué clases deben recompilarse como respuesta a un cambio en un recurso Java.
Aunque la estructura básica para la construcción se define en la plataforma, el trabajo real lo efectúa el conector. Los patrones para implementar constructores incrementales complejos no están dentro del ámbito de lo que se trata aquí, ya que la implementación depende del diseño específico del constructor.
Un constructor puede invocarse explícitamente de una de las siguientes maneras:
En la práctica, el usuario del entorno de trabajo activa un constructor seleccionando los mandatos correspondientes en el menú del navegador de recursos.
La plataforma también invoca los constructores de proyectos incrementales durante una construcción automática. Si están habilitadas, las construcciones automáticas se ejecutan siempre que se modifica el área de trabajo.
El punto de extensión org.eclipse.core.resources.builders se utiliza para contribuir con un constructor de proyectos incremental en la plataforma. La siguiente marcación muestra cómo el conector com.example.builders ficticio podría contribuir con un constructor de proyectos incremental.
<extension
point="org.eclipse.core.resources.builders">
<builder
id="com.example.builders"
name="MyBuilder">
<run
class="com.example.builders.BuilderExample">
<parameter
name="optimize" value="true" />
<parameter
name="comment" value="Builder comment" />
</run>
</builder>
</extension>
La clase identificada en el punto de extensión debe ampliar la clase IncrementalProjectBuilder de la plataforma.
public class BuilderExample extends IncrementalProjectBuilder {
IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException {
// añadir la lógica de construcción aquí
return null;
}
protected void startupOnInitialize() {
// añadir la lógica de inicialización del constructor aquí
}
}
El proceso de construcción empieza con el método build(), que incluye información sobre el tipo de construcción que se ha solicitado: FULL_BUILD, INCREMENTAL_BUILD o AUTO_BUILD. Si se ha solicitado una construcción incremental, se proporciona un delta de recurso para describir los cambios en los recursos del proyecto desde la última construcción. El segmento siguiente especifica de manera más detallada el 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;
}
A veces, al construir el proyecto "X", el constructor necesita información sobre cambios en algún otro proyecto "Y." (Por ejemplo, cuando una clase Java en X implementa una interfaz proporcionada en Y.) Al construir X, se puede obtener una delta para Y llamando a getDelta(Y). Para asegurarse de que la plataforma puede proporcionar deltas, el constructor de X debe haber declarado la dependencia entre X e Y devolviendo una matriz que contenga Y desde una llamada build() anterior. Si un constructor no tiene dependencias, simplemente puede devolver nulo. Consulte IncrementalProjectBuilder para obtener más información.
La lógica necesaria para procesar una petición de construcción completa es específica del conector. Esto puede implicar que se tengan que visitar todos los recursos del proyecto (si se activó la construcción para un proyecto) o examinar otros proyectos si existen dependencias entre proyectos. En el segmento siguiente se sugiere cómo podría implementarse una construcción completa.
protected void fullBuild(final IProgressMonitor monitor) throws CoreException {
try {
getProject().accept(new
MyBuildVisitor());
} catch (CoreException e) { }
}
El visitante de la construcción debería efectuar la construcción para el recurso específico (y responder verdadero para continuar visitando todos los recursos hijo).
class MyBuildVisitor implements IResourceVisitor {
public boolean visit(IResource res) {
//construir el recurso especificado.
//devolver verdadero para continuar visitando los hijos.
return true;
}
}
El proceso de visita continúa hasta que se ha visitado todo el árbol del recurso.
Al efectuar una construcción incremental, se utiliza un delta de modificación de recurso en lugar del proyecto entero.
protected void incrementalBuild(IResourceDelta delta,
IProgressMonitor monitor) throws CoreException {
// el visitante realiza el trabajo.
delta.accept(new MyBuildDeltaVisitor());
}
Tenga en cuenta que en una construcción incremental, el visitante de la construcción utiliza un árbol de delta de recursos en lugar de un árbol de recurso completo.
El proceso de visita continúa hasta que se ha visitado todo el árbol del delta de recursos. Las características específicas de los cambios son similares a las que se describen en Implementar un escuchador de cambios de recursos. Una diferencia importante es que con los constructores de proyectos incrementales, se suele utilizar un delta de recursos basado en un proyecto concreto, no en el área de trabajo entera.
Para que un constructor esté disponible para un proyecto determinado, debe incluirse en la especificación de construcción del proyecto. La especificación de construcción de un proyecto es una lista de mandatos que se han de ejecutar secuencialmente cuando se construye el proyecto. Cada mandato identifica un solo constructor de proyectos incremental.
El segmento siguiente añade un nuevo constructor como el primer constructor en la lista de constructores existente.
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];
// Añadirlo antes de otros constructores.
System.arraycopy(commands, 0, newCommands, 1, commands.length);
newCommands[0] = command;
desc.setBuildSpec(newCommands);
project.setDescription(desc, null);
}
La configuración del constructor de un proyecto sólo se realiza una vez; generalmente, a medida que el proyecto se está creando.