Compilar código Java

Entre los conectores de JDT se incluye un compilador Java incremental y un compilador Java por lotes para construir archivos .class Java a partir del código fuente. El compilador no ofrece ninguna API directa. Se instala como constructor en los proyectos Java. La compilación se desencadena mediante los mecanismos de construcción estándar de la plataforma.

Los mecanismos de construcción de la plataforma se describen con detalle en el tema Constructores de proyectos incrementales.

Compilar el código

Los archivos fuente Java de un proyecto se pueden compilar programáticamente utilizando la API de construcción.

   IProject myProject;
IProgressMonitor myProgressMonitor;
myProject.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, myProgressMonitor);

Para un proyecto Java, esto invoca el constructor incremental de proyectos Java (junto con los otros constructores incrementales de proyectos que se hayan añadido a la especificación de construcción del proyecto). Los archivos .class generados se escriben en la carpeta de salida designada. Los archivos de recursos adicionales también se copian en la carpeta de salida. 

En el caso de una construcción por lotes completa, todos los archivos .class de la carpeta de salida pueden quedar eliminados para asegurar que no se encuentran archivos obsoletos. Esto se controla mediante una opción del constructor núcleo de JDT (CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER). El valor predeterminado de esta opción es hacer limpieza de las carpetas de salida. A menos que esta opción esté restablecida, debe asegurarse de que todos los archivos .class de los que no tenga sus correspondientes archivos fuente se colocan en una carpeta de archivo de clase aparte en la vía de acceso de clases, en vez de colocarlos en la carpeta de salida.

Los constructores incremental y por lotes se pueden configurar con otras opciones que controlan qué recursos se copian en la carpeta de salida. El siguiente ejemplo muestra cómo configurar un filtro de recursos para que los archivos que acaban en '.ignore' y las carpetas que se llaman 'META-INF' no se copien en la carpeta de salida: 

      Hashtable options = JavaCore.getOptions();
   options.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, "*.ignore,META-INF/");
   JavaCore.setOptions(options);

Los nombres de archivo se filtran si coinciden con uno de los patrones suministrados. Las carpetas enteras se filtran si su nombre coincide con uno de los nombres de carpeta suministrados que terminan en un separador de vía de acceso.

También pueden configurarse los constructores por lotes e incrementales para que solo generen un único error si el archivo .classpath contiene errores. Esta opción está establecida por omisión y elimina numerosos errores. En el tema que trata de las opciones de constructor núcleo de JDT encontrará una lista completa de las opciones relacionadas con el constructor y sus valores predeterminados.

El compilador también se puede configurar mediante opciones de JavaCore. Por ejemplo, puede definir la gravedad que debe utilizarse para los distintos tipos de problemas que se producen durante la compilación. En el tema que trata de las opciones de compilador núcleo de JDT encontrará una lista completa de las opciones relacionadas con el compilador y sus valores predeterminados.

Si configura programáticamente opciones para el constructor o el compilador, debe determinar el ámbito de la opción. Por ejemplo, la configuración de un filtro de recursos puede atañer solamente a un determinado proyecto. En el siguiente ejemplo se configura el mismo filtro de recursos que se mostraba anteriormente, pero tan solo atañe al proyecto individual.

   
   Hashtable options = myProject.getOptions(false);
// obtener tan solo las opciones configuradas en este proyecto
   options.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, "*.ignore,META-INF/");
   myProject.setOptions(options);

Utilizar el compilador por lotes

Localizar el compilador por lotes

El compilador por lotes se encuentra en las clases internas del conector JDT/Core. Por lo tanto, está en el archivo jdtcore.jar, en el directorio plugins/org.eclipse.jdt.core. El nombre de la clase es org.eclipse.jdt.internal.compiler.batch.Main.

Ejecutar el compilador por lotes

Qué opciones están disponibles

las opciones sugeridas son las que se indican con el color de fondo naranja.

Nombre Utilización
Opciones de vía de acceso de clases
-bootclasspath <dir 1>;<dir 2>;...;<dir P> Lista de los directorios o archivos jar que sirven como rutina de carga de los archivos de clase utilizados por el compilador. Por omisión, se utilizan las bibliotecas de la VM en ejecución. Las entradas se separan con el separador de vías de acceso de la plataforma.
-cp
-classpath <dir 1>;<dir 2>;...;<dir P>
Lista de los directorios o archivos jar que sirven para compilar los archivos fuente. El valor predeterminado es el valor de la propiedad "java.class.path". Las entradas se separan con el separador de vías de acceso de la plataforma.
-extdirs <dir 1>;<dir 2>;...;<dir P> Lista de los directorios que sirven para especificar la ubicación de los archivos zip/jar de las extensiones. Las entradas se separan con el separador de vías de acceso de la plataforma.
-sourcepath <dir 1>;<dir 2>;...;<dir P> Lista de los directorios que sirven para especificar los archivos fuente. Las entradas se separan con el separador de vías de acceso de la plataforma.
-d <dir 1>|none Sirve para especificar en qué directorio hay que volcar los archivos .class generados. Si se omite, no se crea ninguna estructura de directorio de paquetes.
Si no desea generar archivos .class, utilice -d none.
-encoding <nombre codificación> Especifique el formato de codificación de fuente predeterminado (también se puede especificar una codificación personalizada para cada archivo, añadiendo a cada nombre de archivo/carpeta fuente de entrada el sufijo [encoding <nombre codificación>]).
Opciones de conformidad
-target 1.1|1.2|1.3|1.4|1.5|5|5.0 Especifica el valor del destino del archivo .class. Los valores posibles son:
  • 1.1 (versión principal: 45 secundaria: 3)
  • 1.2 (versión principal: 46 secundaria: 0)
  • 1.3 (versión principal: 47 secundaria: 0)
  • 1.4 (versión principal: 48 secundaria: 0)
  • 1.5, 5 o 5.0 (versión principal: 49 secundaria: 0)
Los valores predeterminados son:
  • 1.1 en la modalidad -1.3
  • 1.2 en la modalidad -1.4
  • 1.5 en la modalidad -1.5
-1.3 Establecer que el nivel de conformidad sea 1.3. Implícito -source 1.3 -target 1.1.
-1.4 Establecer que el nivel de conformidad sea 1.4 (predeterminado). Implícito -source 1.3 -target 1.2.
-1.5 Establecer que el nivel de conformidad sea 1.5. Implícito -source 1.5 -target 1.5.
-source 1.3|1.4|1.5|5|5.0 Sirve para habilitar el nivel del fuente del compilador.
Los valores posibles son:
  • 1.3
  • 1.4
  • 1.5, 5 o 5.0
Los valores predeterminados son:
  • 1.3 en la modalidad -1.3
  • 1.4 en la modalidad -1.4
  • 1.5 en la modalidad -1.5
En 1.4, assert se trata como palabra clave. En 1.5, enum y assert se tratan como palabras clave.
Opciones de aviso
-warn:
allDeprecation
allJavadoc
assertIdentifier
boxing
charConcat
conditionAssign
constructorName
dep-ann
deprecation
emptyBlock
enumSwitch
fieldHiding
finalBound
finally
hiding
incomplete-switch
indirectStatic
intfAnnotation
intfNonInherited
javadoc
localHiding
maskedCatchBlocks
nls
noEffectAssign
null
over-ann
pkgDefaultMethod
semicolon
serial
specialParamHiding
static-access
staticReceiver
suppress
synthetic-access
syntheticAccess
tasks(<task1>|...|<taskN>)
typeHiding
unchecked
unnecessaryElse
unqualified-field-access
unqualifiedField
uselessTypeCheck
unused
unusedArgument
unusedImport
unusedLocal
unusedPrivate
unusedThrown
varargsCast
warningToken
Establecer el nivel de aviso.
Por ejemplo, -warn:unusedLocals,deprecation

Los valores predeterminados son los de color rojo.

    -warn:<avisos separados por ,>    habilitar exactamente los avisos de la lista
    -warn:+<avisos separados por ,>   habilitar avisos adicionales
    -warn:-<avisos separados por ,>   inhabilitar avisos específicos
allDeprecation en desuso incluso dentro de código en desuso
allJavadoc javadoc no válido o que no se encuentra
assertIdentifier aparición de assert usado como identificador
boxing conversión de autoencaje
charConcat cuando una matriz de caracteres se emplea en una concatenación de series sin haber sido convertida explícitamente a una serie
conditionAssign posible asignación booleana accidental
constructorName método con nombre de constructor
dep-ann anotación @Deprecated ausente
deprecation uso de un tipo o miembro en desuso fuera del código en desuso
emptyBlock bloque vacío no documentado
enumSwitch,
incomplete-switch
conmutación de enumeración incompleta
fieldHiding campo que oculta otra variable
finalBound parámetro de tipo con límite final
finally bloque finally que no se completa con normalidad
hiding macro para fieldHiding, localHiding, typeHiding y maskedCatchBlock
indirectStatic referencia indirecta a miembro estático
intfAnnotation tipo de anotación utilizada como superinterfaz
intfNonInherited compatibilidad de método no heredada de interfaz
javadoc javadoc no válido
localHiding variable local que oculta otra variable
maskedCatchBlocks bloque catch oculto
nls literales de tipo serie no nls (que no tienen códigos //$NON-NLS-<n>)
noEffectAssign para asignación sin efecto
null comprobación de nulo ausente o redundante
over-ann anotación @Override ausente
pkgDefaultMethod intento de alterar temporalmente el método package-default
serial serialVersionUID ausente
semicolon punto y coma innecesario o sentencia vacía
specialParamHiding parámetro de constructor o de método set que oculta otro campo
static-access macro para indirectStatic y staticReceiver
staticReceiver si se utiliza un receptor no estático para obtener un campo estático o para llamar a un método estático
suppress habilitar @SuppressWarnings
syntheticAccess,
synthetic-access
cuando se lleva a cabo acceso sintético para clase interior
tasks habilitar soporte para códigos de tareas en el código fuente
typeHiding parámetro de tipo que oculta otro tipo
unchecked operación de tipo no comprobada
unnecessaryElse cláusula else innecesaria
unqualified-field-access,
unqualifiedField
referencia no calificada a campo
unused macro para unusedArgument, unusedImport, unusedLocal, unusedPrivate y unusedThrown
unusedArgument argumento de método no utilizado
unusedImport referencia a importación no utilizada
unusedLocal variable local no utilizada
unusedPrivate declaración de miembro privado no utilizado
unusedThrown excepción lanzada declarada, pero no utilizada
uselessTypeCheck operación cast/instanceof innecesaria
varargsCast argumento varargs necesita conversión de tipo explícita
warningToken símbolo de aviso de no manejado en @SuppressWarningsb

-nowarn No hay ningún aviso (equivale a -warn:none)
-deprecation Equivale a -warn:deprecation.
Opciones de depuración
-g[:none|:lines,vars,source] Establecer el nivel de atributos de depuración
-g Toda la información de depuración (equivale a -g:lines,vars,source)
-g:none No hay información de depuración
-g:[lines,vars,source] Información de depuración selectiva
-preserveAllLocals Pedir explícitamente al compilador que conserve todas las variables locales (a efectos de depuración). Si se omite, el compilador eliminará las variables locales que no se utilicen.
Opciones avanzadas
@<archivo> Leer argumentos de línea de mandatos a partir del archivo
-maxProblems <n> Número máximo de problemas por unidad de compilación (el valor predeterminada es 100)
-log <nombre de archivo> Especifique un archivo de anotaciones en el que volcar todos los datos de salida del compilador. Es verdaderamente útil si se quiere depurar el compilador por lotes u obtener un archivo que contenga todos los errores y avisos de una construcción por lotes. Si la extensión es .xml, el archivo de anotaciones generado será xml.
-proceedOnError Seguir compilando cuando se encuentra un error, volcar archivos de clase con métodos de problema o tipos de problema. Solo se lo recomendamos si desea poder ejecutar la aplicación aunque queden errores.
-verbose Imprimir unidades de compilación accedidas/procesadas en la consola o en el archivo de anotaciones, si se ha especificado.
-referenceInfo Calcular información de referencia. Solo es útil si se está conectado al constructor. De lo contrario, las informaciones de referencia son inútiles.
-progress Mostrar progreso (solo en modalidad -log)
-time Visualizar información de velocidad
-noExit No llamar a System.exit(n) al final de la compilación (n=0 si no hay error)
-repeat <n> Repetir el proceso de compilación <n> veces (análisis de rendimiento)
-inlineJSR Incorporar bytecode JSR (implícito si destino >= 1.5)
-enableJavadoc Considerar referencias dentro de javadoc
Opciones de ayuda
-? -help Visualizar el mensaje de ayuda
-v -version Visualizar el número de construcción del compilador. Es muy útil a la hora de notificar un error.
-showversion Visualizar el número de construcción del compilador y continuar. Es muy útil a la hora de notificar un error.

Ejemplos

d:\temp -classpath rt.jar -time -g -d d:/tmp Compila todos los archivos fuente que hay en d:\temp y en sus subcarpetas. La vía de acceso de clases es simplemente rt.jar. Genera todos los atributos de depuración, y todos los archivos .class generados se vuelcan en d:\tmp. La velocidad del compilador se visualizará cuando el proceso por lotes haya llegado al final.
d:\temp\Test.java -classpath d:\temp;rt.jar -g:none Solo compila Test.java y recuperará los posibles archivos dependientes de d:\temp. La vía de acceso de clases es rt.jar y d:\temp, lo que significa que todas las clases necesarias se buscan primero en d:\temp y, después, en rt.jar. No genera atributos de depuración, y los archivos .class generados se vuelcan en d:\tmp.

Utilizar el adaptador javac de Ant

El compilador Eclipse se puede utilizar dentro de un script Ant mediante el adaptador javac. Para poder utilizar el compilador Eclipse, basta con que solo defina la propiedad build.compiler en el script. A continuación se ofrece un pequeño ejemplo.
<?xml version="1.0" encoding="UTF-8" ?>
<project name="compile" default="main" basedir="../.">

	<property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/>

	<property name="root" value="${basedir}/src"/>

	<property name="destdir" value="d:/temp/bin" />

	<target name="main">
		<javac srcdir="${root}" destdir="${destdir}" debug="on" nowarn="on" extdirs="d:/extdirs" source="1.4">
		    <classpath>
		      <pathelement location="${basedir}/../org.eclipse.jdt.core/bin"/>
		    </classpath>
		</javac>		
	</target>
</project>
La sintaxis utilizada para la tarea javac de Ant se encuentra en la documentación de las tareas javac de Ant. El adaptador actual soporta las tareas Javac Ant de las versiones 1.4.1 hasta 1.6.5.

Si se propone utilizar una versión posterior a 1.5.0, puede usar el elemento de argumento de compilador anidado para especificar opciones específicas del compilador.

...
		<javac srcdir="${root}" destdir="${destdir}" debug="on" nowarn="on" extdirs="d:/extdirs" source="1.4">
		    <classpath>
		      <pathelement location="${basedir}/../org.eclipse.jdt.core/bin"/>
		    </classpath>
    <compilerarg compiler="org.eclipse.jdt.core.JDTCompilerAdapter" line="-1.5 -warn:+boxing"/>
</javac>		
...

Para impedir que pueda obtener scripts dependientes del compilador, le aconsejamos que utilice el conjunto de argumentos del compilador para org.eclipse.jdt.core.JDTCompilerAdapter. Si no se establece así, el script solo se puede usar con el compilador Eclipse. Si se establece, el argumento de compilador anidado no se tiene en cuenta si el nombre es distinto del nombre de compilador especificado por la propiedad build.compiler.

Determinación de problemas

El núcleo de JDT define un marcador especializado (marcador de tipo "org.eclipse.jdt.core.problem") para señalar los problemas de compilación. Para descubrir programáticamente los problemas detectados por el compilador, debe utilizarse el protocolo estándar de marcadores de la plataforma. En el tema Marcadores de recursos hallará una visión general sobre cómo utilizar los marcadores.

El fragmento de código siguiente busca todos los marcadores de problemas Java de una unidad de compilación.

   public IMarker[] findJavaProblemMarkers(ICompilationUnit cu) 
         throws CoreException {
      IResource javaSourceFile = cu.getUnderlyingResource();
      IMarker[] markers = 
         javaSourceFile.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
            true, IResource.DEPTH_INFINITE);
   }

El constructor de proyectos Java es el encargado del mantenimiento de los marcadores de problemas Java, y estos quedarán automáticamente eliminados en cuanto se solucionen los problemas y se recompile el código Java.

Una de las constantes de IProblem establece el valor del ID del problema. El ID del problema es fiable, pero el mensaje se traduce y, por lo tanto, puede cambiar según el entorno local predeterminado. Las constantes definidas en IProblem son autodescriptivas.

Debe definirse una implementación de IProblemRequestor para recoger los problemas descubiertos durante una operación Java. Las copias de trabajo se pueden reconciliar con la detección de problemas si se ha suministrado una interfaz IProblemRequestor para la creación de copias de trabajo. Para ello, puede utilizar el método reconcile. A continuación se ofrece un ejemplo:

  ICompilationUnit unit = ..; // obtener una unidad de compilación
			
  // crear peticionario para acumular problemas descubiertos
  IProblemRequestor problemRequestor = new IProblemRequestor() {
    public void acceptProblem(IProblem problem) {
      System.out.println(problem.getID() + ": " + problem.getMessage());
    }
    public void beginReporting() {}
    public void endReporting() {}
    public boolean isActive() {	return true; } // detectará problemas si está activo
  };
    
  // utilizar copia de trabajo para guardar el fuente con error
  ICompilationUnit workingCopy = unit.getWorkingCopy(new WorkingCopyOwner() {}, problemRequestor, null);
  ((IOpenable)workingCopy).getBuffer().setContents("public class X extends Zork {}");

  // desencadenar la reconciliación
  workingCopy.reconcile(NO_AST, true, null, null);
En el método acceptProblem(IProblem), puede añadir una acción en los problemas notificados. En este ejemplo, el problema notificado será que Zork no puede resolverse o no es una superclase válida y su ID es IProblem.SuperclassNotFound.