/** * Process command line arguments: store all command line options in `options' table and return * all source filenames. * * @param args The array of command line arguments. */ protected java.util.List<String> processArgs(String[] flags) { int ac = 0; while (ac < flags.length) { String flag = flags[ac]; ac++; int j; for (j = 0; j < recognizedOptions.length; j++) if (recognizedOptions[j].matches(flag)) break; if (j == recognizedOptions.length) { error("err.invalid.flag", flag); return null; } Option option = recognizedOptions[j]; if (option.hasArg()) { if (ac == flags.length) { error("err.req.arg", flag); return null; } String operand = flags[ac]; ac++; if (option.process(flag, operand)) return null; } else { if (option.process(flag)) return null; } } String sourceString = options.get("-source"); Source source = (sourceString != null) ? Source.lookup(sourceString) : Source.JDK1_5; // JDK 5 is the latest supported source version String targetString = options.get("-target"); Target target = (targetString != null) ? Target.lookup(targetString) : Target.JDK1_5; // JDK 5 is the latest supported source version // We don't check source/target consistency for CLDC, as J2ME // profiles are not aligned with J2SE targets; moreover, a // single CLDC target may have many profiles. In addition, // this is needed for the continued functioning of the JSR14 // prototype. if (Character.isDigit(target.name.charAt(0)) && target.compareTo(source.requiredTarget()) < 0) { if (targetString != null) { if (sourceString == null) { warning( "warn.target.default.source.conflict", targetString, source.requiredTarget().name); } else { warning("warn.source.target.conflict", sourceString, source.requiredTarget().name); } return null; } else { options.put("-target", source.requiredTarget().name); } } return sourceFileNames; }
/** * Programmatic interface for main function. * * @param args The command line parameters. */ public int compile(String[] args, AnnotationProcessorFactory factory) { int returnCode = 0; providedFactory = factory; Context context = new Context(); options = Options.instance(context); Bark bark; /* * Process the command line options to create the intial * options data. This processing is at least partially reused * by any recursive apt calls. */ // For testing: assume all arguments in forcedOpts are // prefixed to command line arguments. processArgs(forcedOpts); /* * A run of apt only gets passed the most recently generated * files; the initial run of apt gets passed the files from * the command line. */ java.util.List<String> origFilenames; try { // assign args the result of parse to capture results of // '@file' expansion origFilenames = processArgs((args = CommandLine.parse(args))); if (origFilenames == null) { return EXIT_CMDERR; } else if (origFilenames.size() == 0) { // it is allowed to compile nothing if just asking for help if (options.get("-help") != null || options.get("-X") != null) return EXIT_OK; } } catch (java.io.FileNotFoundException e) { Bark.printLines( out, ownName + ": " + getLocalizedString("err.file.not.found", e.getMessage())); return EXIT_SYSERR; } catch (IOException ex) { ioMessage(ex); return EXIT_SYSERR; } catch (OutOfMemoryError ex) { resourceMessage(ex); return EXIT_SYSERR; } catch (StackOverflowError ex) { resourceMessage(ex); return EXIT_SYSERR; } catch (FatalError ex) { feMessage(ex); return EXIT_SYSERR; } catch (sun.misc.ServiceConfigurationError sce) { sceMessage(sce); return EXIT_ABNORMAL; } catch (Throwable ex) { bugMessage(ex); return EXIT_ABNORMAL; } boolean firstRound = true; boolean needSourcePath = false; boolean needClassPath = false; boolean classesAsDecls = options.get("-XclassesAsDecls") != null; /* * Create augumented classpath and sourcepath values. * * If any of the prior apt rounds generated any new source * files, the n'th apt round (and any javac invocation) has the * source destination path ("-s path") as the last element of * the "-sourcepath" to the n'th call. * * If any of the prior apt rounds generated any new class files, * the n'th apt round (and any javac invocation) has the class * destination path ("-d path") as the last element of the * "-classpath" to the n'th call. */ String augmentedSourcePath = ""; String augmentedClassPath = ""; String baseClassPath = ""; try { /* * Record original options for future annotation processor * invocations. */ origOptions = new HashMap<String, String>(options.size()); for (String s : options.keySet()) { String value; if (s.equals(value = options.get(s))) origOptions.put(s, (String) null); else origOptions.put(s, value); } origOptions = Collections.unmodifiableMap(origOptions); { // Note: it might be necessary to check for an empty // component ("") of the source path or class path Paths paths = Paths.instance(context); String sourceDest = options.get("-s"); if (paths.sourcePath() != null) { for (File f : paths.sourcePath()) augmentedSourcePath += (f + File.pathSeparator); augmentedSourcePath += (sourceDest == null) ? "." : sourceDest; } else { augmentedSourcePath = "."; if (sourceDest != null) augmentedSourcePath += (File.pathSeparator + sourceDest); } String classDest = options.get("-d"); if (paths.userClassPath() != null) { for (File f : paths.userClassPath()) baseClassPath += (f + File.pathSeparator); // put baseClassPath into map to handle any // value needed for the classloader options.put("-classpath", baseClassPath); augmentedClassPath = baseClassPath + ((classDest == null) ? "." : classDest); } else { baseClassPath = "."; if (classDest != null) augmentedClassPath = baseClassPath + (File.pathSeparator + classDest); } assert options.get("-classpath") != null; } /* * Create base and augmented class loaders */ ClassLoader augmentedAptCL = null; { /* * Use a url class loader to look for classes on the * user-specified class path. Prepend computed bootclass * path, which includes extdirs, to the URLClassLoader apt * uses. */ String aptclasspath = ""; Paths paths = Paths.instance(context); String bcp = ""; Collection<File> bootclasspath = paths.bootClassPath(); if (bootclasspath != null) { for (File f : bootclasspath) bcp += (f + File.pathSeparator); } // If the factory path is set, use that path if (providedFactory == null) aptclasspath = options.get("-factorypath"); if (aptclasspath == null) aptclasspath = options.get("-classpath"); assert aptclasspath != null; aptclasspath = (bcp + aptclasspath); aptCL = new URLClassLoader(pathToURLs(aptclasspath)); if (providedFactory == null && options.get("-factorypath") != null) // same CL even if new class files written augmentedAptCL = aptCL; else { // Create class loader in case new class files are // written augmentedAptCL = new URLClassLoader( pathToURLs(augmentedClassPath.substring(baseClassPath.length())), aptCL); } } int round = 0; // For -XPrintAptRounds do { round++; Context newContext = new Context(); Options newOptions = Options.instance(newContext); // creates a new context newOptions.putAll(options); // populate with old options... don't bother reparsing command line, etc. // if genSource files, must add destination to source path if (genSourceFileNames.size() > 0 && !firstRound) { newOptions.put("-sourcepath", augmentedSourcePath); needSourcePath = true; } aggregateGenSourceFileNames.addAll(genSourceFileNames); sourceFileNames.addAll(genSourceFileNames); genSourceFileNames.clear(); // Don't really need to track this; just have to add -d // "foo" to class path if any class files are generated if (genClassFileNames.size() > 0) { newOptions.put("-classpath", augmentedClassPath); aptCL = augmentedAptCL; needClassPath = true; } aggregateGenClassFileNames.addAll(genClassFileNames); classFileNames.addAll(genClassFileNames); genClassFileNames.clear(); options = newOptions; if (options.get("-XPrintAptRounds") != null) { out.println("apt Round : " + round); out.println("filenames: " + sourceFileNames); if (classesAsDecls) out.println("classnames: " + classFileNames); out.println("options: " + options); } returnCode = compile(args, newContext); firstRound = false; // Check for reported errors before continuing bark = Bark.instance(newContext); } while (((genSourceFileNames.size() != 0) || (classesAsDecls && genClassFileNames.size() != 0)) && bark.nerrors == 0); } catch (UsageMessageNeededException umne) { help(); return EXIT_CMDERR; // will cause usage message to be printed } /* * Do not compile if a processor has reported an error or if * there are no source files to process. A more sophisticated * test would also fail for syntax errors caught by javac. */ if (options.get("-nocompile") == null && options.get("-print") == null && bark.nerrors == 0 && (origFilenames.size() > 0 || aggregateGenSourceFileNames.size() > 0)) { /* * Need to create new argument string for calling javac: * 1. apt specific arguments (e.g. -factory) must be stripped out * 2. proper settings for sourcepath and classpath must be used * 3. generated class names must be added * 4. class file names as declarations must be removed */ int newArgsLength = args.length + (needSourcePath ? 1 : 0) + (needClassPath ? 1 : 0) + aggregateGenSourceFileNames.size(); // Null out apt-specific options and don't copy over into // newArgs. This loop should be a lot faster; the options // array should be replaced with a better data structure // which includes a map from strings to options. // // If treating classes as declarations, must strip out // class names from the javac argument list argLoop: for (int i = 0; i < args.length; i++) { int matchPosition = -1; // "-A" by itself is recognized by apt but not javac if (args[i] != null && args[i].equals("-A")) { newArgsLength--; args[i] = null; continue argLoop; } else { optionLoop: for (int j = 0; j < recognizedOptions.length; j++) { if (args[i] != null && recognizedOptions[j].matches(args[i])) { matchPosition = j; break optionLoop; } } if (matchPosition != -1) { Option op = recognizedOptions[matchPosition]; if (op.aptOnly) { newArgsLength--; args[i] = null; if (op.hasArg()) { newArgsLength--; args[i + 1] = null; } } else { if (op.hasArg()) { // skip over next string i++; continue argLoop; } if ((options.get("-XclassesAsDecls") != null) && (matchPosition == (recognizedOptions.length - 1))) { // Remove class file names from // consideration by javac. if (!args[i].endsWith(".java")) { newArgsLength--; args[i] = null; } } } } } } String newArgs[] = new String[newArgsLength]; int j = 0; for (int i = 0; i < args.length; i++) { if (args[i] != null) newArgs[j++] = args[i]; } if (needClassPath) newArgs[j++] = "-XD-classpath=" + augmentedClassPath; if (needSourcePath) { newArgs[j++] = "-XD-sourcepath=" + augmentedSourcePath; for (String s : aggregateGenSourceFileNames) newArgs[j++] = s; } returnCode = com.sun.tools.javac.Main.compile(newArgs); } return returnCode; }