1. 程式人生 > >javac編譯選項-Option

javac編譯選項-Option

前言

javac有很多選項,在jdk1.8中,通過javac -help 可以看到如下資訊的輸出:

1

關於這個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下,如圖:

2

當我們輸入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的所有選項列出:

選項值argsNameKeydescrKey型別組別選擇型別選項值集是否有後綴作用
-gnulljavac.opt.gSTANDARDBASICnullnullfalse生成所有除錯資訊
-g:nonenulljavac.opt.g.noneSTANDARDBASICnullnullfalse不生成任何除錯資訊
-g:nulljavac.opt.g.lines.vars.sourceSTANDARDBASICANYOF{lines,vars,source}true只生成某些除錯資訊
-Xlint:nulljavac.opt.Xlint.suboptlistEXTENDEDBASICANYOFLintCategory對應的列舉值true啟用或禁用特定的警告
-Xdoclint:nulljavac.opt.XdoclintEXTENDEDBASICnullnullfalse為javadoc註釋中的問題啟用建議的檢查
-Xdoclint:javac.opt.Xdoclint.suboptsjavac.opt.Xdoclint.customEXTENDEDBASICnullnullfalse為 javadoc 註釋中的問題啟用或禁用特定檢查
-nowarnnulljavac.opt.nowarnSTANDARDBASICnullnullfalse不生成任何警告(保留命令列向後相容性)
-verbosenulljavac.opt.verboseSTANDARDBASICnullnullfalse輸出有關編譯器正在執行的操作的訊息
-deprecationnulljavac.opt.deprecationSTANDARDBASICnullnullfalse輸出使用已過時的 API 的源位置(保留命令列向後相容性)
-classpathjavac.opt.arg.pathjavac.opt.classpathSTANDARDFILEMANAGERnullnullfalse指定查詢使用者類檔案和註釋處理程式的位置
-cpjavac.opt.arg.pathjavac.opt.classpathSTANDARDFILEMANAGERnullnullfalse指定查詢使用者類檔案和註釋處理程式的位置(這個選項是-classpath的簡寫)
-sourcepathjavac.opt.arg.pathjavac.opt.sourcepathSTANDARDFILEMANAGERnullnullfalse指定查詢輸入原始檔的位置
-bootclasspathjavac.opt.arg.pathjavac.opt.bootclasspathSTANDARDFILEMANAGERnullnullfalse覆蓋引導類檔案的位置
-Xbootclasspath/p:javac.opt.arg.pathjavac.opt.Xbootclasspath.pEXTENDEDFILEMANAGERnullnullfalse置於引導類路徑之前
-Xbootclasspath/a:javac.opt.arg.pathjavac.opt.Xbootclasspath.aEXTENDEDFILEMANAGERnullnullfalse置於引導類路徑之後
-Xbootclasspath:javac.opt.arg.pathjavac.opt.bootclasspathEXTENDEDFILEMANAGERnullnullfalse覆蓋引導類檔案的位置(這個選項是和-bootclasspath一樣的)
-extdirsjavac.opt.arg.dirsjavac.opt.extdirsSTANDARDFILEMANAGERnullnullfalse覆蓋所安裝擴充套件的位置
-Djava.ext.dirs=javac.opt.arg.dirsjavac.opt.extdirsEXTENDEDFILEMANAGERnullnullfalse覆蓋所安裝擴充套件的位置(和-extdirs一樣)
-proc:nulljavac.opt.proc.none.onlySTANDARDBASICONEOFnone,onlyfalse控制是否執行註釋處理和/或編譯
-processorjavac.opt.arg.class.listjavac.opt.processorSTANDARDBASICnullnullfalse要執行的註釋處理程式的名稱; 繞過預設的搜尋程序
-processorpathjavac.opt.arg.pathjavac.opt.processorpathSTANDARDFILEMANAGERnullnullfalse指定查詢註釋處理程式的位置
-parametersnulljavac.opt.parametersSTANDARDBASICnullnullfalse生成元資料以用於方法引數的反射
-djavac.opt.arg.directoryjavac.opt.dSTANDARDFILEMANAGERnullnullfalse指定放置生成的類檔案的位置
-sjavac.opt.arg.directoryjavac.opt.sourceDestSTANDARDFILEMANAGERnullnullfalse指定放置生成的原始檔的位置
-hjavac.opt.arg.directoryjavac.opt.headerDestSTANDARDFILEMANAGERnullnullfalse指定放置生成的本機標標頭檔案的位置
-implicit:nulljavac.opt.implicitSTANDARDBASICONEOFnone,classfalse指定是否為隱式引用檔案生成類檔案
-encodingjavac.opt.arg.encodingjavac.opt.encodingSTANDARDFILEMANAGERnullnullfalse指定原始檔使用的字元編碼
-sourcejavac.opt.arg.releasejavac.opt.sourceSTANDARDBASICnullnullfalse提供與指定發行版的源相容性
-targetjavac.opt.arg.releasejavac.opt.targetSTANDARDBASICnullnullfalse生成特定 VM 版本的類檔案
-profilejavac.opt.arg.profilejavac.opt.profileSTANDARDBASICnullnullfalse確保使用的 API 在指定的配置檔案中可用
-versionnulljavac.opt.versionSTANDARDINFOnullnullfalse輸出版本資訊
-fullversionnullnullHIDDENINFOnullnullfalse輸出版本資訊
-XDdiags=nullnullHIDDENINFOnullnullfalse這是編譯器選項的後門
-helpnulljavac.opt.helpSTANDARDINFOnullnullfalse輸出標準選項的提要
-Ajavac.opt.arg.key.equals.valuejavac.opt.ASTANDARDBASICnullnulltrue傳遞給註釋處理器的選項
-Xnulljavac.opt.XSTANDARDBASICnullnullfalse輸出非標準選項的提要
-Jjavac.opt.arg.flagjavac.opt.JSTANDARDBASICnullnulltrue直接將 <標記> 傳遞給執行時系統,如果在命令列中使用,則丟擲異常
-moreinfonullnullHIDDENBASICnullnull false 輸出更多的資訊
-Werrornulljavac.opt.WerrorSTANDARDBASICnullnull false 出現警告時終止編譯
-promptnullnullHIDDENBASICnullnull false
-promptnullnullHIDDENBASICnullnull false 每次錯誤後提示
-doenullnullHIDDENBASICnullnull false 在發生錯誤時dump堆疊資訊
-printsourcenullnullHIDDENBASICnullnull false 在型別擦除後輸出source
-warnuncheckednullnullHIDDENBASICnullnull false 在對泛型進行未檢查的操作時發出警告資訊
-Xmaxerrsjavac.opt.arg.numberjavac.opt.maxerrsEXTENDEDBASICnullnull false 設定要輸出的錯誤的最大數目
-Xmaxwarnsjavac.opt.arg.numberjavac.opt.maxwarnsEXTENDEDBASICnullnull false 設定要輸出的警告的最大數目
-Xstdoutjavac.opt.arg.filejavac.opt.XstdoutEXTENDEDINFOnullnull false 重定向標準輸出
-Xprintnulljavac.opt.printEXTENDEDBASICnullnullfalse輸出指定型別的文字表示
-XprintRoundsnulljavac.opt.printRoundsEXTENDEDBASICnullnullfalse輸出有關注釋處理迴圈的資訊
-XprintProcessorInfonulljavac.opt.printProcessorInfoEXTENDEDBASICnullnullfalse輸出有關請求處理程式處理哪些註釋的資訊
-Xprefernulljavac.opt.preferEXTENDEDBASICONEOFsource,newerfalse指定讀取檔案, 當同時找到隱式編譯類的原始檔和類檔案時
-Xpkginfo:nulljavac.opt.pkginfoEXTENDEDBASICONEOFalways,legacy,nonemptyfalse指定讀取檔案, 當同時找到隱式編譯類的原始檔和類檔案時
-OnullnullHIDDENBASICnull null false不進行任何操作,後向相容性
-XjcovnullnullHIDDENBASICnull null false生成jcov(程式碼覆蓋)所需要的table
-Xplugin:javac.opt.arg.pluginjavac.opt.pluginEXTENDEDBASICnull null false要執行的外掛的名稱和可選引數
-Xdiags:nulljavac.opt.diagsEXTENDEDBASICONEOF compact,verbose false選擇診斷模式
-XDnull null HIDDENBASICnull nullfalse這是編譯器選項的後門,-XDx=y 將選項x的值設定為y
@javac.opt.arg.file javac.opt.arg.file STANDARDINFOnull nulltrue從檔案讀取選項和檔名,由CommandLine實現
sourcefilenull null HIDDENINFOnull nullfalse