编译 Java 代码

JDT 插件包含增量和批处理 Java 编译器,用于根据源代码构建 Java .class 文件。编译器未提供直接 API。它是作为 Java 项目的构建器安装的。编译是使用标准平台构建机制触发的。

增量项目构建器详细描述了平台构建机制。

编译代码

可以使用构建 API 来通过程序编译项目中的 Java 源文件。

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

对于 Java 项目,这将调用 Java 增量项目构建器(以及已经添加到项目构建规范中的任何其它增量项目构建器)。生成的 .class 文件将被写入指定的输出文件夹。其它资源文件也被复制到输出文件夹中。 

对于完全批处理构建来说,输出文件夹中的所有 .class 文件都可能“被清理”以确保找不到任何旧文件。此操作是使用“JDT 核心构建器选项”(CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER)控制的。此选项的缺省值将清除输出文件夹。除非复位此选项,否则您必须确保将所有不具有相应源文件的 .class 文件放在类路径中的单独类文件文件夹中,而不是放在输出文件夹中。

可以使用其它用于控制将哪些资源复制到输出文件夹的选项来配置增量和批处理构建器。以下样本显示如何设置资源过滤器,以便不将以“.ignore”结尾的文件和名为“META-INF”的文件夹复制到输出文件夹: 

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

如果文件名与提供的其中一个模式相匹配,这些文件名就会被过滤掉。如果整个文件夹的名称与提供的其中一个文件夹名(以路径分隔符结尾)相匹配,这些文件夹就会被过滤掉。

还可以将增量和批处理构建器配置为在 .classpath 文件有错误时只生成单个错误。缺省情况下将设置此选项,这将消除大量的错误。请参阅 JDT 核心构建器选项以获取与构建器相关的选项及其缺省值的完整列表。

也可以使用 JavaCore 选项来配置编译器。例如,可以定义应当用于在编译期间发现的不同类型问题的严重性。请参阅 JDT 核心编译器选项以获取与编译器相关的选项及其缺省值的完整列表。

当使用程序来为构建器或编译器配置选项时,应确定选项的作用域。例如,设置资源过滤器时,所作的设置可能只应用于特定的项目。以下示例设置前面显示的那个资源过滤器,但是仅对单独的项目设置它。

   
   Hashtable options = myProject.getOptions(false);  // get only the options set up in this project
   options.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, "*.ignore,META-INF/");
   myProject.setOptions(options);

使用批处理编译器

查找批处理编译器

批处理编译器类位于 JDT/Core 插件的内部类中。因此,它在 plugins/org.eclipse.jdt.core 目录中的 jdtcore.jar 文件中。类名是 org.eclipse.jdt.internal.compiler.batch.Main

运行批处理编译器

可以使用哪些选项?

带橙色背景的是建议的选项。

名称 用法
类路径选项
-bootclasspath <dir 1>;<dir 2>;...;<dir P> 这是目录或 jar 文件的列表,用来引导编译器使用的类文件。缺省情况下,将使用正在运行的 VM 的库。这些条目是由平台路径分隔符分隔的。
-cp
-classpath <dir 1>;<dir 2>;...;<dir P>
用来编译源文件的目录或 jar 文件的列表。缺省值是“java.class.path”属性的值。这些条目是由平台路径分隔符分隔的。
-extdirs <dir 1>;<dir 2>;...;<dir P> 这是用来指定扩展 zip/jar 文件位置的目录列表。这些条目是由平台路径分隔符分隔的。
-sourcepath <dir 1>;<dir 2>;...;<dir P> 这是用来指定源文件的目录列表。这些条目是由平台路径分隔符分隔的。
-d <dir 1>|none 此选项用来指定生成的 .class 文件应该被转储到的目录。如果省略此选项,则不会创建任何包目录结构。
如果您不想生成 .class 文件,请使用 -d none
-encoding <encoding name> 指定缺省的源代码编码格式(也可以通过对每个输入源文件名/文件夹名追加后缀 [encoding <encoding name>] 来逐个文件地指定定制编码)。
一致性选项
-target 1.1|1.2|1.3|1.4|1.5|5|5.0 此选项指定 .class 文件目标设置。可能的值是:
  • 1.1(主版本:45,次版本:3)
  • 1.2(主版本:46,次版本:0)
  • 1.3(主版本:47,次版本:0)
  • 1.4(主版本:48,次版本:0)
  • 1.555.0(主版本:49,次版本:0)
缺省值是:
  • 1.1(在 -1.3 方式下)
  • 1.2(在 -1.4 方式下)
  • 1.5(在 -1.5 方式下)
-1.3 将一致性级别设置为 1.3。实际上是指 -source 1.3 -target 1.1。
-1.4 将一致性级别设置为 1.4(缺省值)。实际上是指 -source 1.3 -target 1.2。
-1.5 将一致性级别设置为 1.5。实际上是指 -source 1.5 -target 1.5。
-source 1.3|1.4|1.5|5|5.0 此选项用来启用编译器的源代码级别。
可能的值是:
  • 1.3
  • 1.4
  • 1.555.0
缺省值是:
  • 1.3(在 -1.3 方式下)
  • 1.4(在 -1.4 方式下)
  • 1.5(在 -1.5 方式下)
1.4 中,assert 被视为关键字。在 1.5 中,enumassert 都被视为关键字。
警告选项
-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
设置警告级别。
例如:-warn:unusedLocals,deprecation

红色的是缺省设置。

    -warn:<用 , 分隔的警告>    精确地启用列示的警告
    -warn:+<用 , 分隔的警告>   启用其它警告
    -w rn:-<用 , 分隔的警告>   禁用特定的警告
allDeprecation 建议不要使用的内容,即使在建议不要使用的代码中亦如此
allJavadoc javadoc 无效或缺少 javadoc
assertIdentifier 出现了用作标识的 assert
boxing 自动装箱转换
charConcat 在字符串并置中使用了字符型数组,但未显式地将其转换为字符串
conditionAssign 可能的意外布尔赋值
constructorName 具有构造函数名的方法
dep-ann 缺少 @Deprecated 注释
deprecation 在建议不要使用的代码外部使用了不建议的类型或成员
emptyBlock 未记录的空块
enumSwitch,
incomplete-switch
不完整的 enum 开关
fieldHiding 字段隐藏了另一个变量
finalBound 带最终边界的类型参数
finally finally 块未正常结束
hiding 用于 fieldHiding、localHiding、typeHiding 和 maskedCatchBlock 的宏
indirectStatic 对静态成员的间接引用
intfAnnotation 用作超接口的注释类型
intfNonInherited 接口非继承方法兼容性
javadoc javadoc 无效
localHiding 局部变量隐藏了另一个变量
maskedCatchBlocks 隐藏的 catch 块
nls 非 nls 字符串文字(缺少标记 //$NON-NLS-<n>)
noEffectAssign 用于无作用的赋值
null 缺少 null 检查或 null 检查冗余
over-ann 缺少 @Override 注释
pkgDefaultMethod 尝试覆盖包缺省方法
serial 缺少 serialVersionUID
semicolon 不必要的分号或空语句
specialParamHiding 构造方法或 setter 参数隐藏了另一个字段
static-access indirectStatic 和 staticReceiver 的宏
staticReceiver 如果使用了非静态接收方来获取静态字段或调用静态方法
suppress 启用 @SuppressWarnings
syntheticAccess,
synthetic-access
当对内部类执行复合访问时
tasks 启用对源代码中的任务标记的支持
typeHiding 类型参数隐藏了另一个类型
unchecked 未校验的类型操作
unnecessaryElse 不必要的 else 子句
unqualified-field-access,
unqualifiedField
对字段的未限定引用
unused unusedArgument、unusedImport、unusedLocal、unusedPrivate 和 unusedThrown 的宏
unusedArgument 未使用的方法自变量
unusedImport 未使用的导入引用
unusedLocal 未使用的局部变量
unusedPrivate 未使用的私有成员声明
unusedThrown 未使用的声明的抛出异常
uselessTypeCheck 不必要的强制类型转换/instanceof 操作
varargsCast varargs 自变量需要显式强制类型转换
warningToken @SuppressWarningsb 中未处理的警告标记

-nowarn 没有任何警告(等同于 -warn:none
-deprecation 等同于 -warn:deprecation
调试选项
-g[:none|:lines,vars,source] 设置调试属性级别
-g 所有调试信息(等同于 -g:lines,vars,source
-g:none 无调试信息
-g:[lines,vars,source] 选择性的调试信息
-preserveAllLocals 显式地请求编译器保留所有局部变量(用于调试目的)。如果省略此选项,编译器将除去未使用的局部变量。
高级选项
@<file> 从文件中读取命令行自变量
-maxProblems <n> 每个编译单元的最大问题数(缺省值为 100)
-log <filename> 指定一个日志文件,编译器的所有输出都将被转储到该文件中。如果您想要调试批处理编译器或者获取包含批处理构建所生成的所有错误和警告的文件,此选项就特别有用。如果扩展名是 .xml,则生成的日志将会是 XML 文件。
-proceedOnError 出错时继续编译,并将包含问题方法或问题类型的类文件转储。仅当您希望即使未解决全部错误也能够运行应用程序时,才建议指定此选项。
-verbose 在控制台中(或者在日志文件中,如果指定的话)打印已访问的/已处理的编译单元。
-referenceInfo 计算引用信息。仅当已连接到构建器时,此选项才有用。否则,引用信息是没有用的。
-progress 显示进度(仅在 -log 方式下)
-time 显示速度信息
-noExit 编译结束时不调用 System.exit(n)(如果没有错误,则 n=0)。
-repeat <n> 将编译过程重复 <n> 次(性能分析)。
-inlineJSR 直接插入 JSR 字节码(如果 target >= 1.5,则是隐式的)
-enableJavadoc 考虑 javadoc 内的引用
帮助选项
-? -help 显示帮助消息
-v -version 显示编译器的构建号。此选项对于报告错误来说非常有用。
-showversion 显示编译器的构建号并继续。此选项对于报告错误来说非常有用。

示例

d:\temp -classpath rt.jar -time -g -d d:/tmp 此命令编译 d:\temp 及其子文件夹中的所有源文件。类路径仅仅是 rt.jar。它生成所有调试属性,并将生成的所有 .class 文件转储到 d:\tmp。批处理过程一旦完成,就会显示编译器的速度。
d:\temp\Test.java -classpath d:\temp;rt.jar -g:none 此命令仅编译 Test.java,它将从 d:\temp 中检索任何依赖文件。类路径是 rt.jar 和 d:\temp,这表示首先在 d:\temp 中然后在 rt.jar 中搜索所有必需的类。它不生成任何调试属性,并将生成的所有 .class 文件转储到 d:\tmp。

使用 ant javac 适配器

通过使用 javac 适配器,可以在 Ant 脚本内使用 Eclipse 编译器。为了使用 Eclipse 编译器,只需在脚本中定义 build.compiler 属性。以下是一个简单示例。
<?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>
可以在 Ant javac 任务文档中找到用于 javac Ant 任务的语法。当前适配器支持 Javac Ant 任务 1.4.1 到 1.6.5 版本。

如果您正在使用 1.5.0 以上的版本,则可以使用嵌套的编译器自变量元素来指定特定于编译器的选项。

...
<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>		
...

为了防止脚本依赖于编译器,我们建议您将编译器自变量设置为 org.eclipse.jdt.core.JDTCompilerAdapter。如果未设置此自变量,则该脚本只能与 Eclipse 编译器配合使用。如果设置了该自变量,并且名称与属性 build.compiler 指定的编译器名不同,则将忽略嵌套的编译器自变量。

问题确定

JDT 核心定义了一个专用标记(标记类型为“org.eclipse.jdt.core.problem”)来指示编译问题。要使用程序来发现由编译器检测到的问题,应该使用标准的平台标记协议。有关使用标记的概述,请参阅资源标记

以下代码段查找编译单元中的所有 Java 问题标记。

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

Java 问题标记是由 Java 项目构建器维护的,在解决问题并重新编译 Java 源代码后,就会自动除去这些标记。

问题标识值是由 IProblem 中的其中一个常量设置的。问题标识是可靠的,但是消息是本地化的,因此会随缺省语言环境的不同而更改。IProblem 中定义的常量是自我描述的。

应该定义 IProblemRequestor 的实现以便收集在 Java 操作期间发现的问题。如果创建工作副本时已经提供了 IProblemRequestor,则可以使工作副本与问题检测一致。为此,可以使用 reconcile 方法。以下是一个示例:

  ICompilationUnit unit = ..; // get some compilation unit
			
  // create requestor for accumulating discovered problems
  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; } // will detect problems if active
  };
    
  // use working copy to hold source with error
  ICompilationUnit workingCopy = unit.getWorkingCopy(new WorkingCopyOwner() {}, problemRequestor, null);
  ((IOpenable)workingCopy).getBuffer().setContents("public class X extends Zork {}");

  // trigger reconciliation			
  workingCopy.reconcile(NO_AST, true, null, null);
可以在 acceptProblem(IProblem) 方法中对报告的问题添加操作。在此示例中,报告的问题将是无法解析 Zork,或者它不是有效的超类,并且它的标识为 IProblem.SuperclassNotFound