Beispiel #1
0
  /**
   * 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;
  }
Beispiel #2
0
  /**
   * 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;
  }