1 /*
   2  * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.javac.main;
  27 
  28 import com.sun.tools.javac.code.Lint;
  29 import com.sun.tools.javac.code.Source;
  30 import com.sun.tools.javac.code.Type;
  31 import com.sun.tools.javac.jvm.Target;
  32 import com.sun.tools.javac.main.JavacOption.HiddenOption;
  33 import com.sun.tools.javac.main.JavacOption.Option;
  34 import com.sun.tools.javac.main.JavacOption.XOption;
  35 import com.sun.tools.javac.util.ListBuffer;
  36 import com.sun.tools.javac.util.Options;
  37 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
  38 import java.io.File;
  39 import java.io.FileWriter;
  40 import java.io.PrintWriter;
  41 import java.util.EnumSet;
  42 import java.util.LinkedHashMap;
  43 import java.util.Map;
  44 import java.util.Set;
  45 import javax.lang.model.SourceVersion;
  46 
  47 import static com.sun.tools.javac.main.OptionName.*;
  48 
  49 /**
  50  * TODO: describe com.sun.tools.javac.main.RecognizedOptions
  51  *
  52  * <p><b>This is NOT part of any supported API.
  53  * If you write code that depends on this, you do so at your own
  54  * risk.  This code and its internal interfaces are subject to change
  55  * or deletion without notice.</b></p>
  56  */
  57 public class RecognizedOptions {
  58 
  59     private RecognizedOptions() {}
  60 
  61     public interface OptionHelper {
  62 
  63         void setOut(PrintWriter out);
  64 
  65         void error(String key, Object... args);
  66 
  67         void printVersion();
  68 
  69         void printFullVersion();
  70 
  71         void printHelp();
  72 
  73         void printXhelp();
  74 
  75         void addFile(File f);
  76 
  77         void addClassName(String s);
  78 
  79     }
  80 
  81     public static class GrumpyHelper implements OptionHelper {
  82 
  83         public void setOut(PrintWriter out) {
  84             throw new IllegalArgumentException();
  85         }
  86 
  87         public void error(String key, Object... args) {
  88             throw new IllegalArgumentException(Main.getLocalizedString(key, args));
  89         }
  90 
  91         public void printVersion() {
  92             throw new IllegalArgumentException();
  93         }
  94 
  95         public void printFullVersion() {
  96             throw new IllegalArgumentException();
  97         }
  98 
  99         public void printHelp() {
 100             throw new IllegalArgumentException();
 101         }
 102 
 103         public void printXhelp() {
 104             throw new IllegalArgumentException();
 105         }
 106 
 107         public void addFile(File f) {
 108             throw new IllegalArgumentException(f.getPath());
 109         }
 110 
 111         public void addClassName(String s) {
 112             throw new IllegalArgumentException(s);
 113         }
 114 
 115     }
 116 
 117     static Set<OptionName> javacOptions = EnumSet.of(
 118         G,
 119         G_NONE,
 120         G_CUSTOM,
 121         XLINT,
 122         XLINT_CUSTOM,
 123         NOWARN,
 124         VERBOSE,
 125         DEPRECATION,
 126         CLASSPATH,
 127         CP,
 128         SOURCEPATH,
 129         BOOTCLASSPATH,
 130         XBOOTCLASSPATH_PREPEND,
 131         XBOOTCLASSPATH_APPEND,
 132         XBOOTCLASSPATH,
 133         EXTDIRS,
 134         DJAVA_EXT_DIRS,
 135         ENDORSEDDIRS,
 136         DJAVA_ENDORSED_DIRS,
 137         PROC,
 138         PROCESSOR,
 139         PROCESSORPATH,
 140         D,
 141         S,
 142         IMPLICIT,
 143         ENCODING,
 144         SOURCE,
 145         TARGET,
 146         VERSION,
 147         FULLVERSION,
 148         DIAGS,
 149         HELP,
 150         A,
 151         X,
 152         J,
 153         MOREINFO,
 154         WERROR,
 155         // COMPLEXINFERENCE,
 156         PROMPT,
 157         DOE,
 158         PRINTSOURCE,
 159         WARNUNCHECKED,
 160         XMAXERRS,
 161         XMAXWARNS,
 162         XSTDOUT,
 163         XPKGINFO,
 164         XPRINT,
 165         XPRINTROUNDS,
 166         XPRINTPROCESSORINFO,
 167         XPREFER,
 168         O,
 169         XJCOV,
 170         XD,
 171         AT,
 172         SOURCEFILE);
 173 
 174     static Set<OptionName> javacFileManagerOptions = EnumSet.of(
 175         CLASSPATH,
 176         CP,
 177         SOURCEPATH,
 178         BOOTCLASSPATH,
 179         XBOOTCLASSPATH_PREPEND,
 180         XBOOTCLASSPATH_APPEND,
 181         XBOOTCLASSPATH,
 182         EXTDIRS,
 183         DJAVA_EXT_DIRS,
 184         ENDORSEDDIRS,
 185         DJAVA_ENDORSED_DIRS,
 186         PROCESSORPATH,
 187         D,
 188         S,
 189         ENCODING,
 190         SOURCE);
 191 
 192     static Set<OptionName> javacToolOptions = EnumSet.of(
 193         G,
 194         G_NONE,
 195         G_CUSTOM,
 196         XLINT,
 197         XLINT_CUSTOM,
 198         NOWARN,
 199         VERBOSE,
 200         DEPRECATION,
 201         PROC,
 202         PROCESSOR,
 203         IMPLICIT,
 204         SOURCE,
 205         TARGET,
 206         // VERSION,
 207         // FULLVERSION,
 208         // HELP,
 209         A,
 210         // X,
 211         // J,
 212         MOREINFO,
 213         WERROR,
 214         // COMPLEXINFERENCE,
 215         PROMPT,
 216         DOE,
 217         PRINTSOURCE,
 218         WARNUNCHECKED,
 219         XMAXERRS,
 220         XMAXWARNS,
 221         // XSTDOUT,
 222         XPKGINFO,
 223         XPRINT,
 224         XPRINTROUNDS,
 225         XPRINTPROCESSORINFO,
 226         XPREFER,
 227         O,
 228         XJCOV,
 229         XD);
 230 
 231     static Option[] getJavaCompilerOptions(OptionHelper helper) {
 232         return getOptions(helper, javacOptions);
 233     }
 234 
 235     public static Option[] getJavacFileManagerOptions(OptionHelper helper) {
 236         return getOptions(helper, javacFileManagerOptions);
 237     }
 238 
 239     public static Option[] getJavacToolOptions(OptionHelper helper) {
 240         return getOptions(helper, javacToolOptions);
 241     }
 242 
 243     static Option[] getOptions(OptionHelper helper, Set<OptionName> desired) {
 244         ListBuffer<Option> options = new ListBuffer<Option>();
 245         for (Option option : getAll(helper))
 246             if (desired.contains(option.getName()))
 247                 options.append(option);
 248         return options.toArray(new Option[options.length()]);
 249     }
 250 
 251     /**
 252      * Get all the recognized options.
 253      * @param helper an {@code OptionHelper} to help when processing options
 254      * @return an array of options
 255      */
 256     public static Option[] getAll(final OptionHelper helper) {
 257         return new Option[] {
 258         new Option(G,                                           "opt.g"),
 259         new Option(G_NONE,                                      "opt.g.none") {
 260             @Override
 261             public boolean process(Options options, String option) {
 262                 options.put("-g:", "none");
 263                 return false;
 264             }
 265         },
 266 
 267         new Option(G_CUSTOM,                                    "opt.g.lines.vars.source",
 268                 Option.ChoiceKind.ANYOF, "lines", "vars", "source"),
 269 
 270         new XOption(XLINT,                                      "opt.Xlint"),
 271         new XOption(XLINT_CUSTOM,                               "opt.Xlint.suboptlist",
 272                 Option.ChoiceKind.ANYOF, getXLintChoices()),
 273 
 274         // -nowarn is retained for command-line backward compatibility
 275         new Option(NOWARN,                                      "opt.nowarn") {
 276             @Override
 277             public boolean process(Options options, String option) {
 278                 options.put("-Xlint:none", option);
 279                 return false;
 280             }
 281         },
 282 
 283         new Option(VERBOSE,                                     "opt.verbose"),
 284 
 285         // -deprecation is retained for command-line backward compatibility
 286         new Option(DEPRECATION,                                 "opt.deprecation") {
 287             @Override
 288             public boolean process(Options options, String option) {
 289                 options.put("-Xlint:deprecation", option);
 290                 return false;
 291             }
 292         },
 293 
 294         new Option(CLASSPATH,              "opt.arg.path",      "opt.classpath"),
 295         new Option(CP,                     "opt.arg.path",      "opt.classpath") {
 296             @Override
 297             public boolean process(Options options, String option, String arg) {
 298                 return super.process(options, "-classpath", arg);
 299             }
 300         },
 301         new Option(SOURCEPATH,             "opt.arg.path",      "opt.sourcepath"),
 302         new Option(BOOTCLASSPATH,          "opt.arg.path",      "opt.bootclasspath") {
 303             @Override
 304             public boolean process(Options options, String option, String arg) {
 305                 options.remove("-Xbootclasspath/p:");
 306                 options.remove("-Xbootclasspath/a:");
 307                 return super.process(options, option, arg);
 308             }
 309         },
 310         new XOption(XBOOTCLASSPATH_PREPEND,"opt.arg.path", "opt.Xbootclasspath.p"),
 311         new XOption(XBOOTCLASSPATH_APPEND, "opt.arg.path", "opt.Xbootclasspath.a"),
 312         new XOption(XBOOTCLASSPATH,        "opt.arg.path", "opt.bootclasspath") {
 313             @Override
 314             public boolean process(Options options, String option, String arg) {
 315                 options.remove("-Xbootclasspath/p:");
 316                 options.remove("-Xbootclasspath/a:");
 317                 return super.process(options, "-bootclasspath", arg);
 318             }
 319         },
 320         new Option(EXTDIRS,                "opt.arg.dirs",      "opt.extdirs"),
 321         new XOption(DJAVA_EXT_DIRS,        "opt.arg.dirs",      "opt.extdirs") {
 322             @Override
 323             public boolean process(Options options, String option, String arg) {
 324                 return super.process(options, "-extdirs", arg);
 325             }
 326         },
 327         new Option(ENDORSEDDIRS,            "opt.arg.dirs",     "opt.endorseddirs"),
 328         new XOption(DJAVA_ENDORSED_DIRS,    "opt.arg.dirs",     "opt.endorseddirs") {
 329             @Override
 330             public boolean process(Options options, String option, String arg) {
 331                 return super.process(options, "-endorseddirs", arg);
 332             }
 333         },
 334         new Option(PROC,                                 "opt.proc.none.only",
 335                 Option.ChoiceKind.ONEOF, "none", "only"),
 336         new Option(PROCESSOR,           "opt.arg.class.list",   "opt.processor"),
 337         new Option(PROCESSORPATH,       "opt.arg.path",         "opt.processorpath"),
 338         new Option(D,                   "opt.arg.directory",    "opt.d"),
 339         new Option(S,                   "opt.arg.directory",    "opt.sourceDest"),
 340         new Option(IMPLICIT,                                    "opt.implicit",
 341                 Option.ChoiceKind.ONEOF, "none", "class"),
 342         new Option(ENCODING,            "opt.arg.encoding",     "opt.encoding"),
 343         new Option(SOURCE,              "opt.arg.release",      "opt.source") {
 344             @Override
 345             public boolean process(Options options, String option, String operand) {
 346                 Source source = Source.lookup(operand);
 347                 if (source == null) {
 348                     helper.error("err.invalid.source", operand);
 349                     return true;
 350                 }
 351                 return super.process(options, option, operand);
 352             }
 353         },
 354         new Option(TARGET,              "opt.arg.release",      "opt.target") {
 355             @Override
 356             public boolean process(Options options, String option, String operand) {
 357                 Target target = Target.lookup(operand);
 358                 if (target == null) {
 359                     helper.error("err.invalid.target", operand);
 360                     return true;
 361                 }
 362                 return super.process(options, option, operand);
 363             }
 364         },
 365         new Option(VERSION,                                     "opt.version") {
 366             @Override
 367             public boolean process(Options options, String option) {
 368                 helper.printVersion();
 369                 return super.process(options, option);
 370             }
 371         },
 372         new HiddenOption(FULLVERSION) {
 373             @Override
 374             public boolean process(Options options, String option) {
 375                 helper.printFullVersion();
 376                 return super.process(options, option);
 377             }
 378         },
 379         new HiddenOption(DIAGS) {
 380             @Override
 381             public boolean process(Options options, String option) {
 382                 Option xd = getOptions(helper, EnumSet.of(XD))[0];
 383                 option = option.substring(option.indexOf('=') + 1);
 384                 String diagsOption = option.contains("%") ?
 385                     "-XDdiagsFormat=" :
 386                     "-XDdiags=";
 387                 diagsOption += option;
 388                 if (xd.matches(diagsOption))
 389                     return xd.process(options, diagsOption);
 390                 else
 391                     return false;
 392             }
 393         },
 394         new Option(HELP,                                        "opt.help") {
 395             @Override
 396             public boolean process(Options options, String option) {
 397                 helper.printHelp();
 398                 return super.process(options, option);
 399             }
 400         },
 401         new Option(A,                "opt.arg.key.equals.value","opt.A") {
 402             @Override
 403             String helpSynopsis() {
 404                 hasSuffix = true;
 405                 return super.helpSynopsis();
 406             }
 407 
 408             @Override
 409             public boolean matches(String arg) {
 410                 return arg.startsWith("-A");
 411             }
 412 
 413             @Override
 414             public boolean hasArg() {
 415                 return false;
 416             }
 417             // Mapping for processor options created in
 418             // JavacProcessingEnvironment
 419             @Override
 420             public boolean process(Options options, String option) {
 421                 int argLength = option.length();
 422                 if (argLength == 2) {
 423                     helper.error("err.empty.A.argument");
 424                     return true;
 425                 }
 426                 int sepIndex = option.indexOf('=');
 427                 String key = option.substring(2, (sepIndex != -1 ? sepIndex : argLength) );
 428                 if (!JavacProcessingEnvironment.isValidOptionName(key)) {
 429                     helper.error("err.invalid.A.key", option);
 430                     return true;
 431                 }
 432                 return process(options, option, option);
 433             }
 434         },
 435         new Option(X,                                           "opt.X") {
 436             @Override
 437             public boolean process(Options options, String option) {
 438                 helper.printXhelp();
 439                 return super.process(options, option);
 440             }
 441         },
 442 
 443         // This option exists only for the purpose of documenting itself.
 444         // It's actually implemented by the launcher.
 445         new Option(J,                   "opt.arg.flag",         "opt.J") {
 446             @Override
 447             String helpSynopsis() {
 448                 hasSuffix = true;
 449                 return super.helpSynopsis();
 450             }
 451             @Override
 452             public boolean process(Options options, String option) {
 453                 throw new AssertionError
 454                     ("the -J flag should be caught by the launcher.");
 455             }
 456         },
 457 
 458         // stop after parsing and attributing.
 459         // new HiddenOption("-attrparseonly"),
 460 
 461         // new Option("-moreinfo",                                      "opt.moreinfo") {
 462         new HiddenOption(MOREINFO) {
 463             @Override
 464             public boolean process(Options options, String option) {
 465                 Type.moreInfo = true;
 466                 return super.process(options, option);
 467             }
 468         },
 469 
 470         // treat warnings as errors
 471         new Option(WERROR,                                      "opt.Werror"),
 472 
 473         // use complex inference from context in the position of a method call argument
 474         new HiddenOption(COMPLEXINFERENCE),
 475 
 476         // generare source stubs
 477         // new HiddenOption("-stubs"),
 478 
 479         // relax some constraints to allow compiling from stubs
 480         // new HiddenOption("-relax"),
 481 
 482         // output source after translating away inner classes
 483         // new Option("-printflat",                             "opt.printflat"),
 484         // new HiddenOption("-printflat"),
 485 
 486         // display scope search details
 487         // new Option("-printsearch",                           "opt.printsearch"),
 488         // new HiddenOption("-printsearch"),
 489 
 490         // prompt after each error
 491         // new Option("-prompt",                                        "opt.prompt"),
 492         new HiddenOption(PROMPT),
 493 
 494         // dump stack on error
 495         new HiddenOption(DOE),
 496 
 497         // output source after type erasure
 498         // new Option("-s",                                     "opt.s"),
 499         new HiddenOption(PRINTSOURCE),
 500 
 501         // output shrouded class files
 502         // new Option("-scramble",                              "opt.scramble"),
 503         // new Option("-scrambleall",                           "opt.scrambleall"),
 504 
 505         // display warnings for generic unchecked operations
 506         new HiddenOption(WARNUNCHECKED) {
 507             @Override
 508             public boolean process(Options options, String option) {
 509                 options.put("-Xlint:unchecked", option);
 510                 return false;
 511             }
 512         },
 513 
 514         new XOption(XMAXERRS,           "opt.arg.number",       "opt.maxerrs"),
 515         new XOption(XMAXWARNS,          "opt.arg.number",       "opt.maxwarns"),
 516         new XOption(XSTDOUT,            "opt.arg.file",         "opt.Xstdout") {
 517             @Override
 518             public boolean process(Options options, String option, String arg) {
 519                 try {
 520                     helper.setOut(new PrintWriter(new FileWriter(arg), true));
 521                 } catch (java.io.IOException e) {
 522                     helper.error("err.error.writing.file", arg, e);
 523                     return true;
 524                 }
 525                 return super.process(options, option, arg);
 526             }
 527         },
 528 
 529         new XOption(XPRINT,                                     "opt.print"),
 530 
 531         new XOption(XPRINTROUNDS,                               "opt.printRounds"),
 532 
 533         new XOption(XPRINTPROCESSORINFO,                        "opt.printProcessorInfo"),
 534 
 535         new XOption(XPREFER,                                    "opt.prefer",
 536                 Option.ChoiceKind.ONEOF, "source", "newer"),
 537 
 538         new XOption(XPKGINFO,                                   "opt.pkginfo",
 539                 Option.ChoiceKind.ONEOF, "always", "legacy", "nonempty"),
 540 
 541         /* -O is a no-op, accepted for backward compatibility. */
 542         new HiddenOption(O),
 543 
 544         /* -Xjcov produces tables to support the code coverage tool jcov. */
 545         new HiddenOption(XJCOV),
 546 
 547         /* This is a back door to the compiler's option table.
 548          * -XDx=y sets the option x to the value y.
 549          * -XDx sets the option x to the value x.
 550          */
 551         new HiddenOption(XD) {
 552             String s;
 553             @Override
 554             public boolean matches(String s) {
 555                 this.s = s;
 556                 return s.startsWith(name.optionName);
 557             }
 558             @Override
 559             public boolean process(Options options, String option) {
 560                 s = s.substring(name.optionName.length());
 561                 int eq = s.indexOf('=');
 562                 String key = (eq < 0) ? s : s.substring(0, eq);
 563                 String value = (eq < 0) ? s : s.substring(eq+1);
 564                 options.put(key, value);
 565                 return false;
 566             }
 567         },
 568 
 569         // This option exists only for the purpose of documenting itself.
 570         // It's actually implemented by the CommandLine class.
 571         new Option(AT,                   "opt.arg.file",         "opt.AT") {
 572             @Override
 573             String helpSynopsis() {
 574                 hasSuffix = true;
 575                 return super.helpSynopsis();
 576             }
 577             @Override
 578             public boolean process(Options options, String option) {
 579                 throw new AssertionError
 580                     ("the @ flag should be caught by CommandLine.");
 581             }
 582         },
 583 
 584         /*
 585          * TODO: With apt, the matches method accepts anything if
 586          * -XclassAsDecls is used; code elsewhere does the lookup to
 587          * see if the class name is both legal and found.
 588          *
 589          * In apt, the process method adds the candidate class file
 590          * name to a separate list.
 591          */
 592         new HiddenOption(SOURCEFILE) {
 593             String s;
 594             @Override
 595             public boolean matches(String s) {
 596                 this.s = s;
 597                 return s.endsWith(".java")  // Java source file
 598                     || SourceVersion.isName(s);   // Legal type name
 599             }
 600             @Override
 601             public boolean process(Options options, String option) {
 602                 if (s.endsWith(".java") ) {
 603                     File f = new File(s);
 604                     if (!f.exists()) {
 605                         helper.error("err.file.not.found", f);
 606                         return true;
 607                     }
 608                     if (!f.isFile()) {
 609                         helper.error("err.file.not.file", f);
 610                         return true;
 611                     }
 612                     helper.addFile(f);
 613                 }
 614                 else
 615                     helper.addClassName(s);
 616                 return false;
 617             }
 618         },
 619     };
 620     }
 621 
 622     public enum PkgInfo {
 623         ALWAYS, LEGACY, NONEMPTY;
 624         public static PkgInfo get(Options options) {
 625             String v = options.get(XPKGINFO);
 626             return (v == null
 627                     ? PkgInfo.LEGACY
 628                     : PkgInfo.valueOf(v.toUpperCase()));
 629         }
 630     }
 631 
 632     private static Map<String,Boolean> getXLintChoices() {
 633         Map<String,Boolean> choices = new LinkedHashMap<String,Boolean>();
 634         choices.put("all", false);
 635         for (Lint.LintCategory c : Lint.LintCategory.values())
 636             choices.put(c.option, c.hidden);
 637         for (Lint.LintCategory c : Lint.LintCategory.values())
 638             choices.put("-" + c.option, c.hidden);
 639         choices.put("none", false);
 640         return choices;
 641     }
 642 
 643 }