前言
javac有很多选项,在jdk1.8中,通过javac -help 可以看到如下信息的输出:
关于这个option所对应的类就是Option.接下来我们就来看一下这个类
解析
Option类是一个枚举,代表javac的选项.处理命令行选项的特定选项是通过按顺序搜索此枚举的成员来标识的,找到第一个匹配的.
其中,Option又分为OptionKind,OptionGroup,ChoiceKind不同的类型.这三种也是枚举,定义如下:
OptionKind
该类代表命令行参数的类型,通过-help 和 -X来使用的.
public enum OptionKind {
// 标准的选项,被-help所文档化
STANDARD,
// 扩展的选项,被-X所文档化
EXTENDED,
// 隐藏的选项,没有被文档化
HIDDEN,
}
OptionGroup
OptionGroup --> option的组别,这决定了该option在什么情况下是可被接受的.
enum OptionGroup {
// 基本的option,可以通过命令行或者编译器api来使用
BASIC,
// 一个javac 标准的JavaFileManager的选项,其他的file manager可能不支持这个选项
FILEMANAGER,
// 一个请求信息的命令行选项,比如 -help
INFO,
// 一个代表文件或者类名的命令行选项
OPERAND
}
ChoiceKind
ChoiceKind --> 关于 选择的类型.
enum ChoiceKind {
// 在可供选择的选项中有确定的值
ONEOF,
// 选项值需要都存在
ANYOF
}
举例说明如下:
-Xpkginfo: 这个编译选项是处理package-info 文件的,该选项的值是{always,legacy,nonempty} 中的任意一个,此时ChoiceKind为ONEOF.
-g: 这个编译选项是生成某些调试信息(lines,vars,source). 这个选项只能是(lines,vars,source),不能是其他的值.如果传入的参数值为 -g:vars,-g:xx,则最终不会进行编译
关于这两个选项的区别,可以通过查看源码的方式来观察:
// com.sun.tools.javac.main.Option.matches(String)
public boolean matches(String option) {
if (!hasSuffix)
return option.equals(text);
if (!option.startsWith(text))
return false;
if (choices != null) {
String arg = option.substring(text.length());
if (choiceKind == ChoiceKind.ONEOF)
return choices.keySet().contains(arg);
else {
for (String a: arg.split(",+")) {
if (!choices.keySet().contains(a))
return false;
}
}
}
return true;
}
可以看到,如果choiceKind为ONEOF,则只要在选项集中有对应的值即可,而ANYOF,则要求选项值需要在选项集中都存在.
–
Option的字段,构造函数如下:
public final String text; // 选项所对应的文本
final OptionKind kind; // 选项的类型
final OptionGroup group; // 选项的组别
final String argsNameKey; // 有关参数的文档key
final String descrKey; // 该选项的说明所对应的key
final boolean hasSuffix; // 该选项是否有后缀. 当选择是=或者:结尾的,则认为该选项是有后缀的,如: (-foo=bar or -foo:bar)
final ChoiceKind choiceKind; // 选择的类型
final Map<String,Boolean> choices; // 选项的值集合
Option(String text, String descrKey,
OptionKind kind, OptionGroup group) {
this(text, null, descrKey, kind, group, null, null, false);
}
Option(String text, String argsNameKey, String descrKey,
OptionKind kind, OptionGroup group) {
this(text, argsNameKey, descrKey, kind, group, null, null, false);
}
Option(String text, String argsNameKey, String descrKey,
OptionKind kind, OptionGroup group, boolean doHasSuffix) {
this(text, argsNameKey, descrKey, kind, group, null, null, doHasSuffix);
}
Option(String text, String descrKey,
OptionKind kind, OptionGroup group,
ChoiceKind choiceKind, Map<String,Boolean> choices) {
this(text, null, descrKey, kind, group, choiceKind, choices, false);
}
Option(String text, String descrKey,
OptionKind kind, OptionGroup group,
ChoiceKind choiceKind, String... choices) {
this(text, null, descrKey, kind, group, choiceKind,
createChoices(choices), false);
}
// where
private static Map<String,Boolean> createChoices(String... choices) {
Map<String,Boolean> map = new LinkedHashMap<String,Boolean>();
for (String c: choices)
map.put(c, false);
return map;
}
private Option(String text, String argsNameKey, String descrKey,
OptionKind kind, OptionGroup group,
ChoiceKind choiceKind, Map<String,Boolean> choices,
boolean doHasSuffix) {
this.text = text;
this.argsNameKey = argsNameKey;
this.descrKey = descrKey;
this.kind = kind;
this.group = group;
this.choiceKind = choiceKind;
this.choices = choices;
char lastChar = text.charAt(text.length()-1);
this.hasSuffix = doHasSuffix || lastChar == ':' || lastChar == '=';
}
下面举例来说明 text,argsNameKey,descrKey的作用,以-cp选项为例:
CP("-cp", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER) {
@Override
public boolean process(OptionHelper helper, String option, String arg) {
return super.process(helper, "-classpath", arg);
}
}
可以看到,-cp对应的是CP,其text = -cp, argsNameKey = opt.arg.path, descrKey = opt.classpath.
这里补充一点,javac中的任何信息的输出都是本地化了的,其对应的资源文件在com/sun/tools/javac/resources下,如图:
当我们输入javac -help时,对于-cp,输出的内容如下:
-cp <路径> 指定查找用户类文件和注释处理程序的位置
那么这是如何实现的呢?
首先是输出CP的text,然后在javac_zh_CN.properties 中获得javac.opt.arg.path 对应的value,当前为 javac.opt.arg.path=<路径>, 然后再获得javac.opt.classpath的value,当前为 javac.opt.classpath=指定查找用户类文件和注释处理程序的位置.
这部分所对应的源码如下:
// com.sun.tools.javac.main.Option.helpSynopsis(Log)
private String helpSynopsis(Log log) {
StringBuilder sb = new StringBuilder();
sb.append(text);
if (argsNameKey == null) {
if (choices != null) {
String sep = "{";
for (Map.Entry<String,Boolean> e: choices.entrySet()) {
if (!e.getValue()) {
sb.append(sep);
sb.append(e.getKey());
sep = ",";
}
}
sb.append("}");
}
} else {
if (!hasSuffix)
sb.append(" ");
sb.append(log.localize(PrefixKind.JAVAC, argsNameKey));
}
return sb.toString();
}
在Option中还定义了一个process函数,是用来处理编译器选项的.如下:
public boolean process(OptionHelper helper, String option) {
if (hasSuffix)
return process(helper, text, option.substring(text.length()));
else
return process(helper, option, option);
}
public boolean process(OptionHelper helper, String option, String arg) {
if (choices != null) {
if (choiceKind == ChoiceKind.ONEOF) {
// some clients like to see just one of option+choice set
for (String s: choices.keySet())
helper.remove(option + s);
String opt = option + arg;
helper.put(opt, opt);
// some clients like to see option (without trailing ":")
// set to arg
String nm = option.substring(0, option.length() - 1);
helper.put(nm, arg);
} else {
// set option+word for each word in arg
for (String a: arg.split(",+")) {
String opt = option + a;
helper.put(opt, opt);
}
}
}
helper.put(option, arg);
return false;
}
经过这个函数的处理,就将选项值加入到了Main中的OptionHelper.关于这部分的代码我们后续在讲解.
总结
现通过表格的方式,将Option的所有选项列出:
选项值 | argsNameKey | descrKey | 类型 | 组别 | 选择类型 | 选项值集 | 是否有后缀 | 作用 |
-g | null | javac.opt.g | STANDARD | BASIC | null | null | false | 生成所有调试信息 |
-g:none | null | javac.opt.g.none | STANDARD | BASIC | null | null | false | 不生成任何调试信息 |
-g: | null | javac.opt.g.lines.vars.source | STANDARD | BASIC | ANYOF | {lines,vars,source} | true | 只生成某些调试信息 |
-Xlint: | null | javac.opt.Xlint.suboptlist | EXTENDED | BASIC | ANYOF | LintCategory对应的枚举值 | true | 启用或禁用特定的警告 |
-Xdoclint: | null | javac.opt.Xdoclint | EXTENDED | BASIC | null | null | false | 为javadoc注释中的问题启用建议的检查 |
-Xdoclint: | javac.opt.Xdoclint.subopts | javac.opt.Xdoclint.custom | EXTENDED | BASIC | null | null | false | 为 javadoc 注释中的问题启用或禁用特定检查 |
-nowarn | null | javac.opt.nowarn | STANDARD | BASIC | null | null | false | 不生成任何警告(保留命令行向后兼容性) |
-verbose | null | javac.opt.verbose | STANDARD | BASIC | null | null | false | 输出有关编译器正在执行的操作的消息 |
-deprecation | null | javac.opt.deprecation | STANDARD | BASIC | null | null | false | 输出使用已过时的 API 的源位置(保留命令行向后兼容性) |
-classpath | javac.opt.arg.path | javac.opt.classpath | STANDARD | FILEMANAGER | null | null | false | 指定查找用户类文件和注释处理程序的位置 |
-cp | javac.opt.arg.path | javac.opt.classpath | STANDARD | FILEMANAGER | null | null | false | 指定查找用户类文件和注释处理程序的位置(这个选项是-classpath的简写) |
-sourcepath | javac.opt.arg.path | javac.opt.sourcepath | STANDARD | FILEMANAGER | null | null | false | 指定查找输入源文件的位置 |
-bootclasspath | javac.opt.arg.path | javac.opt.bootclasspath | STANDARD | FILEMANAGER | null | null | false | 覆盖引导类文件的位置 |
-Xbootclasspath/p: | javac.opt.arg.path | javac.opt.Xbootclasspath.p | EXTENDED | FILEMANAGER | null | null | false | 置于引导类路径之前 |
-Xbootclasspath/a: | javac.opt.arg.path | javac.opt.Xbootclasspath.a | EXTENDED | FILEMANAGER | null | null | false | 置于引导类路径之后 |
-Xbootclasspath: | javac.opt.arg.path | javac.opt.bootclasspath | EXTENDED | FILEMANAGER | null | null | false | 覆盖引导类文件的位置(这个选项是和-bootclasspath一样的) |
-extdirs | javac.opt.arg.dirs | javac.opt.extdirs | STANDARD | FILEMANAGER | null | null | false | 覆盖所安装扩展的位置 |
-Djava.ext.dirs= | javac.opt.arg.dirs | javac.opt.extdirs | EXTENDED | FILEMANAGER | null | null | false | 覆盖所安装扩展的位置(和-extdirs一样) |
-proc: | null | javac.opt.proc.none.only | STANDARD | BASIC | ONEOF | none,only | false | 控制是否执行注释处理和/或编译 |
-processor | javac.opt.arg.class.list | javac.opt.processor | STANDARD | BASIC | null | null | false | 要运行的注释处理程序的名称; 绕过默认的搜索进程 |
-processorpath | javac.opt.arg.path | javac.opt.processorpath | STANDARD | FILEMANAGER | null | null | false | 指定查找注释处理程序的位置 |
-parameters | null | javac.opt.parameters | STANDARD | BASIC | null | null | false | 生成元数据以用于方法参数的反射 |
-d | javac.opt.arg.directory | javac.opt.d | STANDARD | FILEMANAGER | null | null | false | 指定放置生成的类文件的位置 |
-s | javac.opt.arg.directory | javac.opt.sourceDest | STANDARD | FILEMANAGER | null | null | false | 指定放置生成的源文件的位置 |
-h | javac.opt.arg.directory | javac.opt.headerDest | STANDARD | FILEMANAGER | null | null | false | 指定放置生成的本机标头文件的位置 |
-implicit: | null | javac.opt.implicit | STANDARD | BASIC | ONEOF | none,class | false | 指定是否为隐式引用文件生成类文件 |
-encoding | javac.opt.arg.encoding | javac.opt.encoding | STANDARD | FILEMANAGER | null | null | false | 指定源文件使用的字符编码 |
-source | javac.opt.arg.release | javac.opt.source | STANDARD | BASIC | null | null | false | 提供与指定发行版的源兼容性 |
-target | javac.opt.arg.release | javac.opt.target | STANDARD | BASIC | null | null | false | 生成特定 VM 版本的类文件 |
-profile | javac.opt.arg.profile | javac.opt.profile | STANDARD | BASIC | null | null | false | 确保使用的 API 在指定的配置文件中可用 |
-version | null | javac.opt.version | STANDARD | INFO | null | null | false | 输出版本信息 |
-fullversion | null | null | HIDDEN | INFO | null | null | false | 输出版本信息 |
-XDdiags= | null | null | HIDDEN | INFO | null | null | false | 这是编译器选项的后门 |
-help | null | javac.opt.help | STANDARD | INFO | null | null | false | 输出标准选项的提要 |
-A | javac.opt.arg.key.equals.value | javac.opt.A | STANDARD | BASIC | null | null | true | 传递给注释处理器的选项 |
-X | null | javac.opt.X | STANDARD | BASIC | null | null | false | 输出非标准选项的提要 |
-J | javac.opt.arg.flag | javac.opt.J | STANDARD | BASIC | null | null | true | 直接将 <标记> 传递给运行时系统,如果在命令行中使用,则抛出异常 |
-moreinfo | null | null | HIDDEN | BASIC | null | null | false | 输出更多的信息 |
-Werror | null | javac.opt.Werror | STANDARD | BASIC | null | null | false | 出现警告时终止编译 |
-prompt | null | null | HIDDEN | BASIC | null | null | false | |
-prompt | null | null | HIDDEN | BASIC | null | null | false | 每次错误后提示 |
-doe | null | null | HIDDEN | BASIC | null | null | false | 在发生错误时dump堆栈信息 |
-printsource | null | null | HIDDEN | BASIC | null | null | false | 在类型擦除后输出source |
-warnunchecked | null | null | HIDDEN | BASIC | null | null | false | 在对泛型进行未检查的操作时发出警告信息 |
-Xmaxerrs | javac.opt.arg.number | javac.opt.maxerrs | EXTENDED | BASIC | null | null | false | 设置要输出的错误的最大数目 |
-Xmaxwarns | javac.opt.arg.number | javac.opt.maxwarns | EXTENDED | BASIC | null | null | false | 设置要输出的警告的最大数目 |
-Xstdout | javac.opt.arg.file | javac.opt.Xstdout | EXTENDED | INFO | null | null | false | 重定向标准输出 |
-Xprint | null | javac.opt.print | EXTENDED | BASIC | null | null | false | 输出指定类型的文本表示 |
-XprintRounds | null | javac.opt.printRounds | EXTENDED | BASIC | null | null | false | 输出有关注释处理循环的信息 |
-XprintProcessorInfo | null | javac.opt.printProcessorInfo | EXTENDED | BASIC | null | null | false | 输出有关请求处理程序处理哪些注释的信息 |
-Xprefer | null | javac.opt.prefer | EXTENDED | BASIC | ONEOF | source,newer | false | 指定读取文件, 当同时找到隐式编译类的源文件和类文件时 |
-Xpkginfo: | null | javac.opt.pkginfo | EXTENDED | BASIC | ONEOF | always,legacy,nonempty | false | 指定读取文件, 当同时找到隐式编译类的源文件和类文件时 |
-O | null | null | HIDDEN | BASIC | null | null | false | 不进行任何操作,后向兼容性 |
-Xjcov | null | null | HIDDEN | BASIC | null | null | false | 生成jcov(代码覆盖)所需要的table |
-Xplugin: | javac.opt.arg.plugin | javac.opt.plugin | EXTENDED | BASIC | null | null | false | 要运行的插件的名称和可选参数 |
-Xdiags: | null | javac.opt.diags | EXTENDED | BASIC | ONEOF | compact,verbose | false | 选择诊断模式 |
-XD | null | null | HIDDEN | BASIC | null | null | false | 这是编译器选项的后门,-XDx=y 将选项x的值设置为y |
@ | javac.opt.arg.file | javac.opt.arg.file | STANDARD | INFO | null | null | true | 从文件读取选项和文件名,由CommandLine实现 |
sourcefile | null | null | HIDDEN | INFO | null | null | false |