/** * Programmatic interface for main function. * * @param args The command line parameters. */ public int compile( String[] args, Context context, List<JavaFileObject> fileObjects, Iterable<? extends Processor> processors) { if (options == null) options = Options.instance(context); // creates a new one filenames = new ListBuffer<File>(); classnames = new ListBuffer<String>(); JavaCompiler comp = null; /* * TODO: Logic below about what is an acceptable command line * should be updated to take annotation processing semantics * into account. */ try { if (args.length == 0 && fileObjects.isEmpty()) { help(); return EXIT_CMDERR; } List<File> files; try { files = processArgs(CommandLine.parse(args)); if (files == null) { // null signals an error in options, abort return EXIT_CMDERR; } else if (files.isEmpty() && fileObjects.isEmpty() && classnames.isEmpty()) { // it is allowed to compile nothing if just asking for help or version info if (options.get("-help") != null || options.get("-X") != null || options.get("-version") != null || options.get("-fullversion") != null) return EXIT_OK; error("err.no.source.files"); return EXIT_CMDERR; } } catch (java.io.FileNotFoundException e) { Log.printLines( out, ownName + ": " + getLocalizedString("err.file.not.found", e.getMessage())); return EXIT_SYSERR; } boolean forceStdOut = options.get("stdout") != null; if (forceStdOut) { out.flush(); out = new PrintWriter(System.out, true); } context.put(Log.outKey, out); // allow System property in following line as a Mustang legacy boolean batchMode = (options.get("nonBatchMode") == null && System.getProperty("nonBatchMode") == null); if (batchMode) CacheFSInfo.preRegister(context); fileManager = context.get(JavaFileManager.class); comp = JavaCompiler.instance(context); if (comp == null) return EXIT_SYSERR; if (!files.isEmpty()) { // add filenames to fileObjects comp = JavaCompiler.instance(context); List<JavaFileObject> otherFiles = List.nil(); JavacFileManager dfm = (JavacFileManager) fileManager; for (JavaFileObject fo : dfm.getJavaFileObjectsFromFiles(files)) otherFiles = otherFiles.prepend(fo); for (JavaFileObject fo : otherFiles) fileObjects = fileObjects.prepend(fo); } comp.compile(fileObjects, classnames.toList(), processors); if (comp.errorCount() != 0) return EXIT_ERROR; } 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 (AnnotationProcessingError ex) { apMessage(ex); return EXIT_SYSERR; } catch (ClientCodeException ex) { // as specified by javax.tools.JavaCompiler#getTask // and javax.tools.JavaCompiler.CompilationTask#call throw new RuntimeException(ex.getCause()); } catch (PropagatedException ex) { throw ex.getCause(); } catch (Throwable ex) { // Nasty. If we've already reported an error, compensate // for buggy compiler error recovery by swallowing thrown // exceptions. if (comp == null || comp.errorCount() == 0 || options == null || options.get("dev") != null) bugMessage(ex); return EXIT_ABNORMAL; } finally { if (comp != null) comp.close(); filenames = null; options = null; } return EXIT_OK; }
/** * 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; }