前言

javac有很多选项,在jdk1.8中,通过javac -help 可以看到如下信息的输出:

java 选择选项 javac选项_javac

关于这个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下,如图:

java 选择选项 javac选项_java 选择选项_02

当我们输入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