1 /*
   2  * Copyright 2004-2008 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package com.sun.tools.apt.main;
  27 
  28 import java.io.File;
  29 import java.io.FileWriter;
  30 import java.io.IOException;
  31 import java.io.PrintWriter;
  32 import java.text.MessageFormat;
  33 import java.util.ResourceBundle;
  34 import java.util.MissingResourceException;
  35 import java.util.StringTokenizer;
  36 import java.util.Map;
  37 import java.util.HashMap;
  38 import java.util.Collections;
  39 
  40 import java.net.URLClassLoader;
  41 import java.net.URL;
  42 import java.net.MalformedURLException;
  43 
  44 import javax.tools.JavaFileManager;
  45 import javax.tools.StandardLocation;
  46 
  47 import com.sun.tools.javac.file.JavacFileManager;
  48 import com.sun.tools.javac.code.Source;
  49 import com.sun.tools.javac.code.Symbol;
  50 import com.sun.tools.javac.code.Type;
  51 import com.sun.tools.javac.jvm.Target;
  52 import com.sun.tools.javac.util.*;
  53 
  54 import com.sun.tools.apt.comp.AnnotationProcessingError;
  55 import com.sun.tools.apt.comp.UsageMessageNeededException;
  56 import com.sun.tools.apt.util.Bark;
  57 import com.sun.mirror.apt.AnnotationProcessorFactory;
  58 
  59 import static com.sun.tools.javac.file.Paths.pathToURLs;
  60 
  61 /** This class provides a commandline interface to the apt build-time
  62  *  tool.
  63  *
  64  *  <p><b>This is NOT part of any API supported by Sun Microsystems.
  65  *  If you write code that depends on this, you do so at your own
  66  *  risk.  This code and its internal interfaces are subject to change
  67  *  or deletion without notice.</b>
  68  */
  69 @SuppressWarnings("deprecation")
  70 public class Main {
  71 
  72     /** For testing: enter any options you want to be set implicitly
  73      *  here.
  74      */
  75     static String[] forcedOpts = {
  76         // Preserve parameter names from class files if the class was
  77         // compiled with debug enabled
  78         "-XDsave-parameter-names"
  79     };
  80 
  81     /** The name of the compiler, for use in diagnostics.
  82      */
  83     String ownName;
  84 
  85     /** The writer to use for diagnostic output.
  86      */
  87     PrintWriter out;
  88 
  89 
  90     /** Instantiated factory to use in lieu of discovery process.
  91      */
  92     AnnotationProcessorFactory providedFactory = null;
  93 
  94     /** Map representing original command-line arguments.
  95      */
  96     Map<String,String> origOptions = new HashMap<String, String>();
  97 
  98     /** Classloader to use for finding factories.
  99      */
 100     ClassLoader aptCL = null;
 101 
 102     /** Result codes.
 103      */
 104     static final int
 105         EXIT_OK = 0,        // Compilation completed with no errors.
 106         EXIT_ERROR = 1,     // Completed but reported errors.
 107         EXIT_CMDERR = 2,    // Bad command-line arguments
 108         EXIT_SYSERR = 3,    // System error or resource exhaustion.
 109         EXIT_ABNORMAL = 4;  // Compiler terminated abnormally
 110 
 111     /** This class represents an option recognized by the main program
 112      */
 113     private class Option {
 114         /** Whether or not the option is used only aptOnly.
 115          */
 116         boolean aptOnly = false;
 117 
 118         /** Option string.
 119          */
 120         String name;
 121 
 122         /** Documentation key for arguments.
 123          */
 124         String argsNameKey;
 125 
 126         /** Documentation key for description.
 127          */
 128         String descrKey;
 129 
 130         /** Suffix option (-foo=bar or -foo:bar)
 131          */
 132         boolean hasSuffix;
 133 
 134         Option(String name, String argsNameKey, String descrKey) {
 135             this.name = name;
 136             this.argsNameKey = argsNameKey;
 137             this.descrKey = descrKey;
 138             char lastChar = name.charAt(name.length()-1);
 139             hasSuffix = lastChar == ':' || lastChar == '=';
 140         }
 141         Option(String name, String descrKey) {
 142             this(name, null, descrKey);
 143         }
 144 
 145         public String toString() {
 146             return name;
 147         }
 148 
 149         /** Does this option take a (separate) operand?
 150          */
 151         boolean hasArg() {
 152             return argsNameKey != null && !hasSuffix;
 153         }
 154 
 155         /** Does argument string match option pattern?
 156          *  @param arg        The command line argument string.
 157          */
 158         boolean matches(String arg) {
 159             return hasSuffix ? arg.startsWith(name) : arg.equals(name);
 160         }
 161 
 162         /** For javac-only options, print nothing.
 163          */
 164         void help() {
 165         }
 166 
 167         String helpSynopsis() {
 168             return name +
 169                 (argsNameKey == null ? "" :
 170                  ((hasSuffix ? "" : " ") +
 171                   getLocalizedString(argsNameKey)));
 172         }
 173 
 174         /** Print a line of documentation describing this option, if non-standard.
 175          */
 176         void xhelp() {}
 177 
 178         /** Process the option (with arg). Return true if error detected.
 179          */
 180         boolean process(String option, String arg) {
 181             options.put(option, arg);
 182             return false;
 183         }
 184 
 185         /** Process the option (without arg). Return true if error detected.
 186          */
 187         boolean process(String option) {
 188             if (hasSuffix)
 189                 return process(name, option.substring(name.length()));
 190             else
 191                 return process(option, option);
 192         }
 193     };
 194 
 195     private class SharedOption extends Option {
 196         SharedOption(String name, String argsNameKey, String descrKey) {
 197             super(name, argsNameKey, descrKey);
 198         }
 199 
 200         SharedOption(String name, String descrKey) {
 201             super(name, descrKey);
 202         }
 203 
 204         void help() {
 205             String s = "  " + helpSynopsis();
 206             out.print(s);
 207             for (int j = s.length(); j < 29; j++) out.print(" ");
 208             Bark.printLines(out, getLocalizedString(descrKey));
 209         }
 210 
 211     }
 212 
 213     private class AptOption extends Option {
 214         AptOption(String name, String argsNameKey, String descrKey) {
 215             super(name, argsNameKey, descrKey);
 216             aptOnly = true;
 217         }
 218 
 219         AptOption(String name, String descrKey) {
 220             super(name, descrKey);
 221             aptOnly = true;
 222         }
 223 
 224         /** Print a line of documentation describing this option, if standard.
 225          */
 226         void help() {
 227             String s = "  " + helpSynopsis();
 228             out.print(s);
 229             for (int j = s.length(); j < 29; j++) out.print(" ");
 230             Bark.printLines(out, getLocalizedString(descrKey));
 231         }
 232 
 233     }
 234 
 235     /** A nonstandard or extended (-X) option
 236      */
 237     private class XOption extends Option {
 238         XOption(String name, String argsNameKey, String descrKey) {
 239             super(name, argsNameKey, descrKey);
 240         }
 241         XOption(String name, String descrKey) {
 242             this(name, null, descrKey);
 243         }
 244         void help() {}
 245         void xhelp() {}
 246     };
 247 
 248     /** A nonstandard or extended (-X) option
 249      */
 250     private class AptXOption extends Option {
 251         AptXOption(String name, String argsNameKey, String descrKey) {
 252             super(name, argsNameKey, descrKey);
 253             aptOnly = true;
 254         }
 255         AptXOption(String name, String descrKey) {
 256             this(name, null, descrKey);
 257         }
 258         void xhelp() {
 259             String s = "  " + helpSynopsis();
 260             out.print(s);
 261             for (int j = s.length(); j < 29; j++) out.print(" ");
 262             Log.printLines(out, getLocalizedString(descrKey));
 263         }
 264     };
 265 
 266     /** A hidden (implementor) option
 267      */
 268     private class HiddenOption extends Option {
 269         HiddenOption(String name) {
 270             super(name, null, null);
 271         }
 272         HiddenOption(String name, String argsNameKey) {
 273             super(name, argsNameKey, null);
 274         }
 275         void help() {}
 276         void xhelp() {}
 277     };
 278 
 279     private class AptHiddenOption extends HiddenOption {
 280         AptHiddenOption(String name) {
 281             super(name);
 282             aptOnly = true;
 283         }
 284         AptHiddenOption(String name, String argsNameKey) {
 285             super(name, argsNameKey);
 286             aptOnly = true;
 287         }
 288     }
 289 
 290     private Option[] recognizedOptions = {
 291         new Option("-g",                                        "opt.g"),
 292         new Option("-g:none",                                   "opt.g.none") {
 293             boolean process(String option) {
 294                 options.put("-g:", "none");
 295                 return false;
 296             }
 297         },
 298 
 299         new Option("-g:{lines,vars,source}",                    "opt.g.lines.vars.source") {
 300             boolean matches(String s) {
 301                 return s.startsWith("-g:");
 302             }
 303             boolean process(String option) {
 304                 String suboptions = option.substring(3);
 305                 options.put("-g:", suboptions);
 306                 // enter all the -g suboptions as "-g:suboption"
 307                 for (StringTokenizer t = new StringTokenizer(suboptions, ","); t.hasMoreTokens(); ) {
 308                     String tok = t.nextToken();
 309                     String opt = "-g:" + tok;
 310                     options.put(opt, opt);
 311                 }
 312                 return false;
 313             }
 314         },
 315 
 316         new XOption("-Xlint",                                   "opt.Xlint"),
 317         new XOption("-Xlint:{"
 318                     + "all,"
 319                     + "cast,deprecation,divzero,empty,unchecked,fallthrough,path,serial,finally,overrides,"
 320                     + "-cast,-deprecation,-divzero,-empty,-unchecked,-fallthrough,-path,-serial,-finally,-overrides,"
 321                     + "none}",
 322                                                                 "opt.Xlint.suboptlist") {
 323             boolean matches(String s) {
 324                 return s.startsWith("-Xlint:");
 325             }
 326             boolean process(String option) {
 327                 String suboptions = option.substring(7);
 328                 options.put("-Xlint:", suboptions);
 329                 // enter all the -Xlint suboptions as "-Xlint:suboption"
 330                 for (StringTokenizer t = new StringTokenizer(suboptions, ","); t.hasMoreTokens(); ) {
 331                     String tok = t.nextToken();
 332                     String opt = "-Xlint:" + tok;
 333                     options.put(opt, opt);
 334                 }
 335                 return false;
 336             }
 337         },
 338 
 339         new Option("-nowarn",                                   "opt.nowarn"),
 340         new Option("-verbose",                                  "opt.verbose"),
 341 
 342         // -deprecation is retained for command-line backward compatibility
 343         new Option("-deprecation",                              "opt.deprecation") {
 344                 boolean process(String option) {
 345                     options.put("-Xlint:deprecation", option);
 346                     return false;
 347                 }
 348             },
 349 
 350         new SharedOption("-classpath",     "opt.arg.path",      "opt.classpath"),
 351         new SharedOption("-cp",            "opt.arg.path",      "opt.classpath") {
 352             boolean process(String option, String arg) {
 353                 return super.process("-classpath", arg);
 354             }
 355         },
 356         new Option("-sourcepath",          "opt.arg.path",      "opt.sourcepath"),
 357         new Option("-bootclasspath",       "opt.arg.path",      "opt.bootclasspath") {
 358             boolean process(String option, String arg) {
 359                 options.remove("-Xbootclasspath/p:");
 360                 options.remove("-Xbootclasspath/a:");
 361                 return super.process(option, arg);
 362             }
 363         },
 364         new XOption("-Xbootclasspath/p:",  "opt.arg.path", "opt.Xbootclasspath.p"),
 365         new XOption("-Xbootclasspath/a:",  "opt.arg.path", "opt.Xbootclasspath.a"),
 366         new XOption("-Xbootclasspath:",    "opt.arg.path", "opt.bootclasspath") {
 367             boolean process(String option, String arg) {
 368                 options.remove("-Xbootclasspath/p:");
 369                 options.remove("-Xbootclasspath/a:");
 370                 return super.process("-bootclasspath", arg);
 371             }
 372         },
 373         new Option("-extdirs",             "opt.arg.dirs",      "opt.extdirs"),
 374         new XOption("-Djava.ext.dirs=",    "opt.arg.dirs",      "opt.extdirs") {
 375             boolean process(String option, String arg) {
 376                 return super.process("-extdirs", arg);
 377             }
 378         },
 379         new Option("-endorseddirs",        "opt.arg.dirs",      "opt.endorseddirs"),
 380         new XOption("-Djava.endorsed.dirs=","opt.arg.dirs",     "opt.endorseddirs") {
 381             boolean process(String option, String arg) {
 382                 return super.process("-endorseddirs", arg);
 383             }
 384         },
 385         new Option("-proc:{none, only}",                        "opt.proc.none.only") {
 386             public boolean matches(String s) {
 387                 return s.equals("-proc:none") || s.equals("-proc:only");
 388             }
 389         },
 390         new Option("-processor",        "opt.arg.class",        "opt.processor"),
 391         new Option("-processorpath",    "opt.arg.path",         "opt.processorpath"),
 392 
 393         new SharedOption("-d",          "opt.arg.path", "opt.d"),
 394         new SharedOption("-s",          "opt.arg.path", "opt.s"),
 395         new Option("-encoding",         "opt.arg.encoding",     "opt.encoding"),
 396         new SharedOption("-source",             "opt.arg.release",      "opt.source") {
 397             boolean process(String option, String operand) {
 398                 Source source = Source.lookup(operand);
 399                 if (source == null) {
 400                     error("err.invalid.source", operand);
 401                     return true;
 402                 } else if (source.compareTo(Source.JDK1_5) > 0) {
 403                     error("err.unsupported.source.version", operand);
 404                     return true;
 405                 }
 406                 return super.process(option, operand);
 407             }
 408         },
 409         new Option("-target",           "opt.arg.release",      "opt.target") {
 410             boolean process(String option, String operand) {
 411                 Target target = Target.lookup(operand);
 412                 if (target == null) {
 413                     error("err.invalid.target", operand);
 414                     return true;
 415                 } else if (target.compareTo(Target.JDK1_5) > 0) {
 416                     error("err.unsupported.target.version", operand);
 417                     return true;
 418                 }
 419                 return super.process(option, operand);
 420             }
 421         },
 422         new AptOption("-version",               "opt.version") {
 423             boolean process(String option) {
 424                 Bark.printLines(out, ownName + " " + JavaCompiler.version());
 425                 return super.process(option);
 426             }
 427         },
 428         new HiddenOption("-fullversion"),
 429         new AptOption("-help",                                  "opt.help") {
 430             boolean process(String option) {
 431                 Main.this.help();
 432                 return super.process(option);
 433             }
 434         },
 435         new SharedOption("-X",                                  "opt.X") {
 436             boolean process(String option) {
 437                 Main.this.xhelp();
 438                 return super.process(option);
 439             }
 440         },
 441 
 442         // This option exists only for the purpose of documenting itself.
 443         // It's actually implemented by the launcher.
 444         new AptOption("-J",             "opt.arg.flag",         "opt.J") {
 445             String helpSynopsis() {
 446                 hasSuffix = true;
 447                 return super.helpSynopsis();
 448             }
 449             boolean process(String option) {
 450                 throw new AssertionError
 451                     ("the -J flag should be caught by the launcher.");
 452             }
 453         },
 454 
 455 
 456         new SharedOption("-A",          "opt.proc.flag",        "opt.A") {
 457                 String helpSynopsis() {
 458                     hasSuffix = true;
 459                     return super.helpSynopsis();
 460                 }
 461 
 462                 boolean matches(String arg) {
 463                     return arg.startsWith("-A");
 464                 }
 465 
 466                 boolean hasArg() {
 467                     return false;
 468                 }
 469 
 470                 boolean process(String option) {
 471                     return process(option, option);
 472                 }
 473             },
 474 
 475         new AptOption("-nocompile",     "opt.nocompile"),
 476 
 477         new AptOption("-print",         "opt.print"),
 478 
 479         new AptOption("-factorypath", "opt.arg.path", "opt.factorypath"),
 480 
 481         new AptOption("-factory",     "opt.arg.class", "opt.factory"),
 482 
 483         new AptXOption("-XListAnnotationTypes", "opt.XListAnnotationTypes"),
 484 
 485         new AptXOption("-XListDeclarations",    "opt.XListDeclarations"),
 486 
 487         new AptXOption("-XPrintAptRounds",      "opt.XPrintAptRounds"),
 488 
 489         new AptXOption("-XPrintFactoryInfo",    "opt.XPrintFactoryInfo"),
 490 
 491         /*
 492          * Option to treat both classes and source files as
 493          * declarations that can be given on the command line and
 494          * processed as the result of an apt round.
 495          */
 496         new AptXOption("-XclassesAsDecls", "opt.XClassesAsDecls"),
 497 
 498         // new Option("-moreinfo",                                      "opt.moreinfo") {
 499         new HiddenOption("-moreinfo") {
 500             boolean process(String option) {
 501                 Type.moreInfo = true;
 502                 return super.process(option);
 503             }
 504         },
 505 
 506         // treat warnings as errors
 507         new HiddenOption("-Werror"),
 508 
 509         // use complex inference from context in the position of a method call argument
 510         new HiddenOption("-complexinference"),
 511 
 512         // prompt after each error
 513         // new Option("-prompt",                                        "opt.prompt"),
 514         new HiddenOption("-prompt"),
 515 
 516         // dump stack on error
 517         new HiddenOption("-doe"),
 518 
 519         // display warnings for generic unchecked and unsafe operations
 520         new HiddenOption("-warnunchecked") {
 521             boolean process(String option) {
 522                 options.put("-Xlint:unchecked", option);
 523                 return false;
 524             }
 525         },
 526 
 527         new HiddenOption("-Xswitchcheck") {
 528             boolean process(String option) {
 529                 options.put("-Xlint:switchcheck", option);
 530                 return false;
 531             }
 532         },
 533 
 534         // generate trace output for subtyping operations
 535         new HiddenOption("-debugsubtyping"),
 536 
 537         new XOption("-Xmaxerrs",        "opt.arg.number",       "opt.maxerrs"),
 538         new XOption("-Xmaxwarns",       "opt.arg.number",       "opt.maxwarns"),
 539         new XOption("-Xstdout",         "opt.arg.file",         "opt.Xstdout") {
 540             boolean process(String option, String arg) {
 541                 try {
 542                     out = new PrintWriter(new FileWriter(arg), true);
 543                 } catch (java.io.IOException e) {
 544                     error("err.error.writing.file", arg, e);
 545                     return true;
 546                 }
 547                 return super.process(option, arg);
 548             }
 549         },
 550 
 551         new XOption("-Xprint",                                  "opt.print"),
 552 
 553         new XOption("-XprintRounds",                            "opt.printRounds"),
 554 
 555         new XOption("-XprintProcessorInfo",                     "opt.printProcessorInfo"),
 556 
 557 
 558         /* -O is a no-op, accepted for backward compatibility. */
 559         new HiddenOption("-O"),
 560 
 561         /* -Xjcov produces tables to support the code coverage tool jcov. */
 562         new HiddenOption("-Xjcov"),
 563 
 564         /* This is a back door to the compiler's option table.
 565          * -Dx=y sets the option x to the value y.
 566          * -Dx sets the option x to the value x.
 567          */
 568         new HiddenOption("-XD") {
 569             String s;
 570             boolean matches(String s) {
 571                 this.s = s;
 572                 return s.startsWith(name);
 573             }
 574             boolean process(String option) {
 575                 s = s.substring(name.length());
 576                 int eq = s.indexOf('=');
 577                 String key = (eq < 0) ? s : s.substring(0, eq);
 578                 String value = (eq < 0) ? s : s.substring(eq+1);
 579                 options.put(key, value);
 580                 return false;
 581             }
 582         },
 583 
 584         new HiddenOption("sourcefile") {
 585                 String s;
 586                 boolean matches(String s) {
 587                     this.s = s;
 588                     return s.endsWith(".java") ||
 589                         (options.get("-XclassesAsDecls") != null);
 590                 }
 591                 boolean process(String option) {
 592                     if (s.endsWith(".java")) {
 593                         if (!sourceFileNames.contains(s))
 594                             sourceFileNames.add(s);
 595                     } else if (options.get("-XclassesAsDecls") != null) {
 596                         classFileNames.add(s);
 597                     }
 598                     return false;
 599                 }
 600             },
 601     };
 602 
 603     /**
 604      * Construct a compiler instance.
 605      */
 606     public Main(String name) {
 607         this(name, new PrintWriter(System.err, true));
 608     }
 609 
 610     /**
 611      * Construct a compiler instance.
 612      */
 613     public Main(String name, PrintWriter out) {
 614         this.ownName = name;
 615         this.out = out;
 616     }
 617 
 618     /** A table of all options that's passed to the JavaCompiler constructor.  */
 619     private Options options = null;
 620 
 621     /** The list of source files to process
 622      */
 623     java.util.List<String> sourceFileNames = new java.util.LinkedList<String>();
 624 
 625     /** The list of class files to process
 626      */
 627     java.util.List<String> classFileNames = new java.util.LinkedList<String>();
 628 
 629     /** List of top level names of generated source files from most recent apt round.
 630      */
 631     java.util.Set<String> genSourceFileNames = new java.util.LinkedHashSet<String>();
 632 
 633     /** List of names of generated class files from most recent apt round.
 634      */
 635     java.util.Set<String> genClassFileNames  = new java.util.LinkedHashSet<String>();
 636 
 637     /**
 638      * List of all the generated source file names across all apt rounds.
 639      */
 640     java.util.Set<String> aggregateGenSourceFileNames = new java.util.LinkedHashSet<String>();
 641 
 642     /**
 643      * List of all the generated class file names across all apt rounds.
 644      */
 645     java.util.Set<String> aggregateGenClassFileNames  = new java.util.LinkedHashSet<String>();
 646 
 647     /**
 648      * List of all the generated file names across all apt rounds.
 649      */
 650     java.util.Set<java.io.File> aggregateGenFiles = new java.util.LinkedHashSet<java.io.File>();
 651 
 652     /**
 653      * Set of all factories that have provided a processor on some apt round.
 654      */
 655     java.util.Set<Class<? extends AnnotationProcessorFactory> > productiveFactories  =
 656         new java.util.LinkedHashSet<Class<? extends AnnotationProcessorFactory> >();
 657 
 658 
 659 
 660     /** Print a string that explains usage.
 661      */
 662     void help() {
 663         Bark.printLines(out, getLocalizedString("msg.usage.header", ownName));
 664         for (int i=0; i < recognizedOptions.length; i++) {
 665             recognizedOptions[i].help();
 666         }
 667         Bark.printLines(out, getLocalizedString("msg.usage.footer"));
 668         out.println();
 669     }
 670 
 671     /** Print a string that explains usage for X options.
 672      */
 673     void xhelp() {
 674         for (int i=0; i<recognizedOptions.length; i++) {
 675             recognizedOptions[i].xhelp();
 676         }
 677         out.println();
 678         Bark.printLines(out, getLocalizedString("msg.usage.nonstandard.footer"));
 679     }
 680 
 681     /** Report a usage error.
 682      */
 683     void error(String key, Object... args) {
 684         warning(key, args);
 685         help();
 686     }
 687 
 688     /** Report a warning.
 689      */
 690     void warning(String key, Object... args) {
 691         Bark.printLines(out, ownName + ": "
 692                        + getLocalizedString(key, args));
 693     }
 694 
 695     /** Process command line arguments: store all command line options
 696      *  in `options' table and return all source filenames.
 697      *  @param args    The array of command line arguments.
 698      */
 699     protected java.util.List<String> processArgs(String[] flags) {
 700         int ac = 0;
 701         while (ac < flags.length) {
 702             String flag = flags[ac];
 703             ac++;
 704 
 705             int j;
 706             for (j=0; j < recognizedOptions.length; j++)
 707                 if (recognizedOptions[j].matches(flag))
 708                     break;
 709 
 710             if (j == recognizedOptions.length) {
 711                 error("err.invalid.flag", flag);
 712                 return null;
 713             }
 714 
 715             Option option = recognizedOptions[j];
 716             if (option.hasArg()) {
 717                 if (ac == flags.length) {
 718                     error("err.req.arg", flag);
 719                     return null;
 720                 }
 721                 String operand = flags[ac];
 722                 ac++;
 723                 if (option.process(flag, operand))
 724                     return null;
 725             } else {
 726                 if (option.process(flag))
 727                     return null;
 728             }
 729         }
 730 
 731         String sourceString = options.get("-source");
 732         Source source = (sourceString != null)
 733             ? Source.lookup(sourceString)
 734             : Source.JDK1_5; // JDK 5 is the latest supported source version
 735         String targetString = options.get("-target");
 736         Target target = (targetString != null)
 737             ? Target.lookup(targetString)
 738             : Target.JDK1_5; // JDK 5 is the latest supported source version
 739         // We don't check source/target consistency for CLDC, as J2ME
 740         // profiles are not aligned with J2SE targets; moreover, a
 741         // single CLDC target may have many profiles.  In addition,
 742         // this is needed for the continued functioning of the JSR14
 743         // prototype.
 744         if (Character.isDigit(target.name.charAt(0)) &&
 745             target.compareTo(source.requiredTarget()) < 0) {
 746             if (targetString != null) {
 747                 if (sourceString == null) {
 748                     warning("warn.target.default.source.conflict",
 749                             targetString,
 750                             source.requiredTarget().name);
 751                 } else {
 752                     warning("warn.source.target.conflict",
 753                             sourceString,
 754                             source.requiredTarget().name);
 755                 }
 756                 return null;
 757             } else {
 758                 options.put("-target", source.requiredTarget().name);
 759             }
 760         }
 761         return sourceFileNames;
 762     }
 763 
 764     /** Programmatic interface for main function.
 765      * @param args    The command line parameters.
 766      */
 767     public int compile(String[] args, AnnotationProcessorFactory factory) {
 768         int returnCode = 0;
 769         providedFactory = factory;
 770 
 771         Context context = new Context();
 772         JavacFileManager.preRegister(context);
 773         options = Options.instance(context);
 774         Bark bark;
 775 
 776         /*
 777          * Process the command line options to create the intial
 778          * options data.  This processing is at least partially reused
 779          * by any recursive apt calls.
 780          */
 781 
 782         // For testing: assume all arguments in forcedOpts are
 783         // prefixed to command line arguments.
 784         processArgs(forcedOpts);
 785 
 786         /*
 787          * A run of apt only gets passed the most recently generated
 788          * files; the initial run of apt gets passed the files from
 789          * the command line.
 790          */
 791 
 792         java.util.List<String> origFilenames;
 793         try {
 794             // assign args the result of parse to capture results of
 795             // '@file' expansion
 796             origFilenames = processArgs((args=CommandLine.parse(args)));
 797 
 798             if (options.get("suppress-tool-api-removal-message") == null) {
 799                 Bark.printLines(out, getLocalizedString("misc.Deprecation"));
 800             }
 801 
 802             if (origFilenames == null) {
 803                 return EXIT_CMDERR;
 804             } else if (origFilenames.size() == 0) {
 805                 // it is allowed to compile nothing if just asking for help
 806                 if (options.get("-help") != null ||
 807                     options.get("-X") != null)
 808                     return EXIT_OK;
 809             }
 810         } catch (java.io.FileNotFoundException e) {
 811             Bark.printLines(out, ownName + ": " +
 812                            getLocalizedString("err.file.not.found",
 813                                               e.getMessage()));
 814             return EXIT_SYSERR;
 815         } catch (IOException ex) {
 816             ioMessage(ex);
 817             return EXIT_SYSERR;
 818         } catch (OutOfMemoryError ex) {
 819             resourceMessage(ex);
 820             return EXIT_SYSERR;
 821         } catch (StackOverflowError ex) {
 822             resourceMessage(ex);
 823             return EXIT_SYSERR;
 824         } catch (FatalError ex) {
 825             feMessage(ex);
 826             return EXIT_SYSERR;
 827         } catch (sun.misc.ServiceConfigurationError sce) {
 828             sceMessage(sce);
 829             return EXIT_ABNORMAL;
 830         } catch (Throwable ex) {
 831             bugMessage(ex);
 832             return EXIT_ABNORMAL;
 833         }
 834 
 835 
 836         boolean firstRound = true;
 837         boolean needSourcePath = false;
 838         boolean needClassPath  = false;
 839         boolean classesAsDecls = options.get("-XclassesAsDecls") != null;
 840 
 841         /*
 842          * Create augumented classpath and sourcepath values.
 843          *
 844          * If any of the prior apt rounds generated any new source
 845          * files, the n'th apt round (and any javac invocation) has the
 846          * source destination path ("-s path") as the last element of
 847          * the "-sourcepath" to the n'th call.
 848          *
 849          * If any of the prior apt rounds generated any new class files,
 850          * the n'th apt round (and any javac invocation) has the class
 851          * destination path ("-d path") as the last element of the
 852          * "-classpath" to the n'th call.
 853          */
 854         String augmentedSourcePath = "";
 855         String augmentedClassPath = "";
 856         String baseClassPath = "";
 857 
 858         try {
 859             /*
 860              * Record original options for future annotation processor
 861              * invocations.
 862              */
 863             origOptions = new HashMap<String, String>(options.size());
 864             for(String s: options.keySet()) {
 865                 String value;
 866                 if (s.equals(value = options.get(s)))
 867                     origOptions.put(s, (String)null);
 868                 else
 869                     origOptions.put(s, value);
 870             }
 871             origOptions = Collections.unmodifiableMap(origOptions);
 872 
 873             JavacFileManager fm = (JavacFileManager) context.get(JavaFileManager.class);
 874             {
 875                 // Note: it might be necessary to check for an empty
 876                 // component ("") of the source path or class path
 877 
 878                 String sourceDest = options.get("-s");
 879                 if (fm.hasLocation(StandardLocation.SOURCE_PATH)) {
 880                     for(File f: fm.getLocation(StandardLocation.SOURCE_PATH))
 881                         augmentedSourcePath += (f + File.pathSeparator);
 882                     augmentedSourcePath += (sourceDest == null)?".":sourceDest;
 883                 } else {
 884                     augmentedSourcePath = ".";
 885 
 886                     if (sourceDest != null)
 887                         augmentedSourcePath += (File.pathSeparator + sourceDest);
 888                 }
 889 
 890                 String classDest = options.get("-d");
 891                 if (fm.hasLocation(StandardLocation.CLASS_PATH)) {
 892                     for(File f: fm.getLocation(StandardLocation.CLASS_PATH))
 893                         baseClassPath += (f + File.pathSeparator);
 894                     // put baseClassPath into map to handle any
 895                     // value needed for the classloader
 896                     options.put("-classpath", baseClassPath);
 897 
 898                     augmentedClassPath = baseClassPath + ((classDest == null)?".":classDest);
 899                 } else {
 900                     baseClassPath = ".";
 901                     if (classDest != null)
 902                         augmentedClassPath = baseClassPath + (File.pathSeparator + classDest);
 903                 }
 904                 assert options.get("-classpath") != null;
 905             }
 906 
 907             /*
 908              * Create base and augmented class loaders
 909              */
 910             ClassLoader augmentedAptCL = null;
 911             {
 912             /*
 913              * Use a url class loader to look for classes on the
 914              * user-specified class path. Prepend computed bootclass
 915              * path, which includes extdirs, to the URLClassLoader apt
 916              * uses.
 917              */
 918                 String aptclasspath = "";
 919                 String bcp = "";
 920                 Iterable<? extends File> bootclasspath = fm.getLocation(StandardLocation.PLATFORM_CLASS_PATH);
 921 
 922                 if (bootclasspath != null) {
 923                     for(File f: bootclasspath)
 924                         bcp += (f + File.pathSeparator);
 925                 }
 926 
 927                 // If the factory path is set, use that path
 928                 if (providedFactory == null)
 929                     aptclasspath = options.get("-factorypath");
 930                 if (aptclasspath == null)
 931                     aptclasspath = options.get("-classpath");
 932 
 933                 assert aptclasspath != null;
 934                 aptclasspath = (bcp + aptclasspath);
 935                 aptCL = new URLClassLoader(pathToURLs(aptclasspath));
 936 
 937                 if (providedFactory == null &&
 938                     options.get("-factorypath") != null) // same CL even if new class files written
 939                     augmentedAptCL = aptCL;
 940                 else {
 941                     // Create class loader in case new class files are
 942                     // written
 943                     augmentedAptCL = new URLClassLoader(pathToURLs(augmentedClassPath.
 944                                                                    substring(baseClassPath.length())),
 945                                                         aptCL);
 946                 }
 947             }
 948 
 949             int round = 0; // For -XPrintAptRounds
 950             do {
 951                 round++;
 952 
 953                 Context newContext = new Context();
 954                 Options newOptions = Options.instance(newContext); // creates a new context
 955                 newOptions.putAll(options);
 956 
 957                 // populate with old options... don't bother reparsing command line, etc.
 958 
 959                 // if genSource files, must add destination to source path
 960                 if (genSourceFileNames.size() > 0 && !firstRound) {
 961                     newOptions.put("-sourcepath", augmentedSourcePath);
 962                     needSourcePath = true;
 963                 }
 964                 aggregateGenSourceFileNames.addAll(genSourceFileNames);
 965                 sourceFileNames.addAll(genSourceFileNames);
 966                 genSourceFileNames.clear();
 967 
 968                 // Don't really need to track this; just have to add -d
 969                 // "foo" to class path if any class files are generated
 970                 if (genClassFileNames.size() > 0) {
 971                     newOptions.put("-classpath", augmentedClassPath);
 972                     aptCL = augmentedAptCL;
 973                     needClassPath = true;
 974                 }
 975                 aggregateGenClassFileNames.addAll(genClassFileNames);
 976                 classFileNames.addAll(genClassFileNames);
 977                 genClassFileNames.clear();
 978 
 979                 options = newOptions;
 980 
 981                 if (options.get("-XPrintAptRounds") != null) {
 982                     out.println("apt Round : " + round);
 983                     out.println("filenames: " + sourceFileNames);
 984                     if (classesAsDecls)
 985                         out.println("classnames: " + classFileNames);
 986                     out.println("options: " + options);
 987                 }
 988 
 989                 returnCode = compile(args, newContext);
 990                 firstRound = false;
 991 
 992                 // Check for reported errors before continuing
 993                 bark = Bark.instance(newContext);
 994             } while(((genSourceFileNames.size() != 0 ) ||
 995                      (classesAsDecls && genClassFileNames.size() != 0)) &&
 996                     bark.nerrors == 0);
 997         } catch (UsageMessageNeededException umne) {
 998             help();
 999             return EXIT_CMDERR; // will cause usage message to be printed
1000         }
1001 
1002         /*
1003          * Do not compile if a processor has reported an error or if
1004          * there are no source files to process.  A more sophisticated
1005          * test would also fail for syntax errors caught by javac.
1006          */
1007         if (options.get("-nocompile") == null &&
1008             options.get("-print")     == null &&
1009             bark.nerrors == 0 &&
1010             (origFilenames.size() > 0 || aggregateGenSourceFileNames.size() > 0 )) {
1011             /*
1012              * Need to create new argument string for calling javac:
1013              * 1. apt specific arguments (e.g. -factory) must be stripped out
1014              * 2. proper settings for sourcepath and classpath must be used
1015              * 3. generated class names must be added
1016              * 4. class file names as declarations must be removed
1017              */
1018 
1019             int newArgsLength = args.length +
1020                 (needSourcePath?1:0) +
1021                 (needClassPath?1:0) +
1022                 aggregateGenSourceFileNames.size();
1023 
1024             // Null out apt-specific options and don't copy over into
1025             // newArgs. This loop should be a lot faster; the options
1026             // array should be replaced with a better data structure
1027             // which includes a map from strings to options.
1028             //
1029             // If treating classes as declarations, must strip out
1030             // class names from the javac argument list
1031             argLoop:
1032             for(int i = 0; i < args.length; i++) {
1033                 int matchPosition = -1;
1034 
1035                 // "-A" by itself is recognized by apt but not javac
1036                 if (args[i] != null && args[i].equals("-A")) {
1037                     newArgsLength--;
1038                     args[i] = null;
1039                     continue argLoop;
1040                 } else {
1041                     optionLoop:
1042                     for(int j = 0; j < recognizedOptions.length; j++) {
1043                         if (args[i] != null && recognizedOptions[j].matches(args[i])) {
1044                             matchPosition = j;
1045                             break optionLoop;
1046                         }
1047                     }
1048 
1049                     if (matchPosition != -1) {
1050                         Option op = recognizedOptions[matchPosition];
1051                         if (op.aptOnly) {
1052                             newArgsLength--;
1053                             args[i] = null;
1054                             if (op.hasArg()) {
1055                                 newArgsLength--;
1056                                 args[i+1] = null;
1057                             }
1058                         } else {
1059                             if (op.hasArg()) { // skip over next string
1060                                 i++;
1061                                 continue argLoop;
1062                             }
1063 
1064                             if ((options.get("-XclassesAsDecls") != null) &&
1065                                 (matchPosition == (recognizedOptions.length-1)) ){
1066                                 // Remove class file names from
1067                                 // consideration by javac.
1068                                 if (! args[i].endsWith(".java")) {
1069                                     newArgsLength--;
1070                                     args[i] = null;
1071                                 }
1072                             }
1073                         }
1074                     }
1075                 }
1076             }
1077 
1078             String newArgs[] = new String[newArgsLength];
1079 
1080             int j = 0;
1081             for(int i=0; i < args.length; i++) {
1082                 if (args[i] != null)
1083                     newArgs[j++] = args[i];
1084             }
1085 
1086             if (needClassPath)
1087                 newArgs[j++] = "-XD-classpath=" + augmentedClassPath;
1088 
1089             if (needSourcePath) {
1090                 newArgs[j++] = "-XD-sourcepath=" + augmentedSourcePath;
1091 
1092                 for(String s: aggregateGenSourceFileNames)
1093                     newArgs[j++] = s;
1094             }
1095 
1096             returnCode = com.sun.tools.javac.Main.compile(newArgs);
1097         }
1098 
1099         return returnCode;
1100     }
1101 
1102     /** Programmatic interface for main function.
1103      * @param args    The command line parameters.
1104      */
1105     int compile(String[] args, Context context) {
1106         boolean assertionsEnabled = false;
1107         assert assertionsEnabled = true;
1108         if (!assertionsEnabled) {
1109             // Bark.printLines(out, "fatal error: assertions must be enabled when running javac");
1110             // return EXIT_ABNORMAL;
1111         }
1112         int exitCode = EXIT_OK;
1113 
1114         JavaCompiler comp = null;
1115         try {
1116             context.put(Bark.outKey, out);
1117 
1118             comp = JavaCompiler.instance(context);
1119             if (comp == null)
1120                 return EXIT_SYSERR;
1121 
1122             java.util.List<String> nameList = new java.util.LinkedList<String>();
1123             nameList.addAll(sourceFileNames);
1124             if (options.get("-XclassesAsDecls") != null)
1125                 nameList.addAll(classFileNames);
1126 
1127             List<Symbol.ClassSymbol> cs
1128                 = comp.compile(List.from(nameList.toArray(new String[0])),
1129                                origOptions,
1130                                aptCL,
1131                                providedFactory,
1132                                productiveFactories,
1133                                aggregateGenFiles);
1134 
1135             /*
1136              * If there aren't new source files, we shouldn't bother
1137              *  running javac if there were errors.
1138              *
1139              * If there are new files, we should try running javac in
1140              * case there were typing errors.
1141              *
1142              */
1143 
1144             if (comp.errorCount() != 0 ||
1145                 options.get("-Werror") != null && comp.warningCount() != 0)
1146                 return EXIT_ERROR;
1147         } catch (IOException ex) {
1148             ioMessage(ex);
1149             return EXIT_SYSERR;
1150         } catch (OutOfMemoryError ex) {
1151             resourceMessage(ex);
1152             return EXIT_SYSERR;
1153         } catch (StackOverflowError ex) {
1154             resourceMessage(ex);
1155             return EXIT_SYSERR;
1156         } catch (FatalError ex) {
1157             feMessage(ex);
1158             return EXIT_SYSERR;
1159         } catch (UsageMessageNeededException umne) {
1160             help();
1161             return EXIT_CMDERR; // will cause usage message to be printed
1162         } catch (AnnotationProcessingError ex) {
1163             apMessage(ex);
1164             return EXIT_ABNORMAL;
1165         } catch (sun.misc.ServiceConfigurationError sce) {
1166             sceMessage(sce);
1167             return EXIT_ABNORMAL;
1168         } catch (Throwable ex) {
1169             bugMessage(ex);
1170             return EXIT_ABNORMAL;
1171         } finally {
1172             if (comp != null) {
1173                 comp.close();
1174                 genSourceFileNames.addAll(comp.getSourceFileNames());
1175                 genClassFileNames.addAll(comp.getClassFileNames());
1176             }
1177             sourceFileNames = new java.util.LinkedList<String>();
1178             classFileNames  = new java.util.LinkedList<String>();
1179         }
1180         return exitCode;
1181     }
1182 
1183     /** Print a message reporting an internal error.
1184      */
1185     void bugMessage(Throwable ex) {
1186         Bark.printLines(out, getLocalizedString("msg.bug",
1187                                                JavaCompiler.version()));
1188         ex.printStackTrace(out);
1189     }
1190 
1191     /** Print a message reporting an fatal error.
1192      */
1193     void apMessage(AnnotationProcessingError ex) {
1194         Bark.printLines(out, getLocalizedString("misc.Problem"));
1195         ex.getCause().printStackTrace(out);
1196     }
1197 
1198     /** Print a message about sun.misc.Service problem.
1199      */
1200     void sceMessage(sun.misc.ServiceConfigurationError ex) {
1201         Bark.printLines(out, getLocalizedString("misc.SunMiscService"));
1202         ex.printStackTrace(out);
1203     }
1204 
1205     /** Print a message reporting an fatal error.
1206      */
1207     void feMessage(Throwable ex) {
1208         Bark.printLines(out, ex.toString());
1209     }
1210 
1211     /** Print a message reporting an input/output error.
1212      */
1213     void ioMessage(Throwable ex) {
1214         Bark.printLines(out, getLocalizedString("msg.io"));
1215         ex.printStackTrace(out);
1216     }
1217 
1218     /** Print a message reporting an out-of-resources error.
1219      */
1220     void resourceMessage(Throwable ex) {
1221         Bark.printLines(out, getLocalizedString("msg.resource"));
1222         ex.printStackTrace(out);
1223     }
1224 
1225     /* ************************************************************************
1226      * Internationalization
1227      *************************************************************************/
1228 
1229     /** Find a localized string in the resource bundle.
1230      *  @param key     The key for the localized string.
1231      */
1232     private static String getLocalizedString(String key, Object... args) {
1233         return getText(key, args);
1234     }
1235 
1236     private static final String javacRB =
1237         "com.sun.tools.javac.resources.javac";
1238 
1239     private static final String aptRB =
1240         "com.sun.tools.apt.resources.apt";
1241 
1242     private static ResourceBundle messageRBjavac;
1243     private static ResourceBundle messageRBapt;
1244 
1245     /** Initialize ResourceBundle.
1246      */
1247     private static void initResource() {
1248         try {
1249             messageRBapt   = ResourceBundle.getBundle(aptRB);
1250             messageRBjavac = ResourceBundle.getBundle(javacRB);
1251         } catch (MissingResourceException e) {
1252             Error x = new FatalError("Fatal Error: Resource for apt or javac is missing");
1253             x.initCause(e);
1254             throw x;
1255         }
1256     }
1257 
1258     /** Get and format message string from resource.
1259      */
1260     private static String getText(String key, Object... _args) {
1261         String[] args = new String[_args.length];
1262         for (int i=0; i<_args.length; i++) {
1263             args[i] = "" + _args[i];
1264         }
1265         if (messageRBapt == null || messageRBjavac == null )
1266             initResource();
1267         try {
1268             return MessageFormat.format(messageRBapt.getString("apt." + key),
1269                                         (Object[]) args);
1270         } catch (MissingResourceException e) {
1271             try {
1272                 return MessageFormat.format(messageRBjavac.getString("javac." + key),
1273                                             (Object[]) args);
1274             } catch (MissingResourceException f) {
1275                 String msg = "apt or javac message file broken: key={0} "
1276                     + "arguments={1}, {2}";
1277                 return MessageFormat.format(msg, (Object[]) args);
1278             }
1279         }
1280     }
1281 }