Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(614)

Unified Diff: third_party/jmake/src/org/pantsbuild/jmake/Main.java

Issue 1373723003: Fix javac --incremental by using jmake for dependency analysis (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@aidl
Patch Set: fix license check Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/jmake/src/org/pantsbuild/jmake/Main.java
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/Main.java b/third_party/jmake/src/org/pantsbuild/jmake/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e8055489fba32becf0778f1dbd5bcbc2555f80c
--- /dev/null
+++ b/third_party/jmake/src/org/pantsbuild/jmake/Main.java
@@ -0,0 +1,899 @@
+/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
+ *
+ * This program is distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+package org.pantsbuild.jmake;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.StreamTokenizer;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The main class of the <b>jmake</b> tool.<p>
+ *
+ * Has several entrypoints: <code>main</code>, <code>mainExternal</code>, <code>mainProgrammatic</code>,
+ * <code>mainExternalControlled</code> and <code>mainProgrammaticControlled</code>.
+ * The first is not intended to be used by applications other than <b>jmake</b> itself, whereas the
+ * rest can be used to call <b>jmake</b> externally with various degrees of control over its behaviour.
+ * See method comments for more details.
+ *
+ * @author Misha Dmitriev
+ * 12 October 2004
+ */
+public class Main {
+
+ static final String DEFAULT_STORE_NAME = "jmake.pdb";
+ static final String VERSION = "1.3.8-11";
+ private String pdbFileName = null;
+ private List<String> allProjectJavaFileNamesList =
+ new ArrayList<String>(100);
+ private String allProjectJavaFileNames[];
+ private String addedJavaFileNames[], removedJavaFileNames[], updatedJavaFileNames[];
+ private String destDir = "";
+ private List<String> javacAddArgs = new ArrayList<String>();
+ private String jcPath, jcMainClass, jcMethod;
+ private String jcExecApp;
+ boolean controlledExecution = false;
+ Object externalApp = null;
+ Method externalCompileSourceFilesMethod = null;
+ private boolean failOnDependentJar = false, noWarnOnDependentJar = false;
+ private boolean pdbTextFormat = false;
+ private PCDManager pcdm = null;
+ private String dependencyFile;
+ private static final String optNames[] = {"-h", "-help", "-d", "-pdb", "-C", "-jcpath", "-jcmainclass", "-jcmethod", "-jcexec", "-Xtiming", "-version",
+ "-warnlimit", "-failondependentjar", "-nowarnondependentjar", "-classpath", "-projclasspath", "-bootclasspath", "-extdirs", "-vpath", "-pdb-text-format",
+ "-depfile"};
+ private static final int optArgs[] = {0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1};
+ private static final int OPT_H = 0;
+ private static final int OPT_HELP = 1;
+ private static final int OPT_D = 2;
+ private static final int OPT_STORE = 3;
+ private static final int OPT_JAVAC_OPT = 4;
+ private static final int OPT_JCPATH = 5;
+ private static final int OPT_JCMAINCLASS = 6;
+ private static final int OPT_JCMETHOD = 7;
+ private static final int OPT_JCEXEC = 8;
+ private static final int OPT_TIMING = 9;
+ private static final int OPT_VERSION = 10;
+ private static final int OPT_WARNLIMIT = 11;
+ private static final int OPT_FAILONDEPJAR = 12;
+ private static final int OPT_NOWARNONDEPJAR = 13;
+ private static final int OPT_CLASSPATH = 14;
+ private static final int OPT_PROJECTCLASSPATH = 15;
+ private static final int OPT_BOOTCLASSPATH = 16;
+ private static final int OPT_EXTDIRS = 17;
+ private static final int OPT_VPATH = 18;
+ private static final int OPT_PDB_TEXT_FORMAT = 19;
+ private static final int OPT_DEPENDENCY_FILE = 20;
+
+ /** Construct a new instance of Main. */
+ public Main() {
+ }
+
+ /**
+ * Checks whether the argument is a legal jmake option. Returns its number or
+ * -1 if not found.
+ */
+ private static int inOptions(String option) {
+ if (option.startsWith(optNames[OPT_JAVAC_OPT])) {
+ return OPT_JAVAC_OPT;
+ }
+ for (int i = 0; i < optNames.length; i++) {
+ if (option.equals(optNames[i])) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private void processCommandLine(String args[]) {
+ if ((args.length == 0) || (args.length == 1 && args[0].equals(optNames[OPT_HELP]))) {
+ printUsage();
+ throw new PrivateException(new PublicExceptions.NoActionRequestedException());
+ }
+
+ List<String> argsV = new ArrayList<String>();
+ for (int i = 0; i < args.length; i++) {
+ argsV.add(args[i]);
+ }
+ int argsSt = 0;
+ String arg;
+
+ while (argsSt < argsV.size()) {
+ arg = argsV.get(argsSt++);
+ if (arg.charAt(0) == '-') {
+ int argN = inOptions(arg);
+ if (argN != -1) {
+ if (argsSt + optArgs[argN] > argsV.size()) {
+ optRequiresArg("\"" + optNames[argN] + "\"");
+ }
+ } else {
+ bailOut(arg + ERR_IS_INVALID_OPTION);
+ }
+
+ switch (argN) {
+ case OPT_H:
+ case OPT_HELP:
+ printUsage();
+ throw new PrivateException(new PublicExceptions.NoActionRequestedException());
+ case OPT_D:
+ destDir = argsV.get(argsSt);
+ javacAddArgs.add("-d");
+ javacAddArgs.add(argsV.get(argsSt));
+ argsSt++;
+ break;
+ case OPT_CLASSPATH:
+ try {
+ setClassPath(argsV.get(argsSt++));
+ } catch (PublicExceptions.InvalidCmdOptionException ex) {
+ bailOut(ex.getMessage());
+ }
+ break;
+ case OPT_PROJECTCLASSPATH:
+ try {
+ setProjectClassPath(argsV.get(argsSt++));
+ } catch (PublicExceptions.InvalidCmdOptionException ex) {
+ bailOut(ex.getMessage());
+ }
+ break;
+ case OPT_STORE:
+ pdbFileName = argsV.get(argsSt++);
+ break;
+ case OPT_JAVAC_OPT:
+ String javacArg =
+ (argsV.get(argsSt - 1)).substring(2);
+ if (javacArg.equals("-d") ||
+ javacArg.equals("-classpath") ||
+ javacArg.equals("-bootclasspath") ||
+ javacArg.equals("-extdirs")) {
+ bailOut(javacArg + ERR_SHOULD_BE_EXPLICIT);
+ }
+ javacAddArgs.add(javacArg);
+ break;
+ case OPT_JCPATH:
+ if (jcExecApp != null) {
+ bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
+ }
+ jcPath = argsV.get(argsSt++);
+ break;
+ case OPT_JCMAINCLASS:
+ if (jcExecApp != null) {
+ bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
+ }
+ jcMainClass = argsV.get(argsSt++);
+ break;
+ case OPT_JCMETHOD:
+ if (jcExecApp != null) {
+ bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
+ }
+ jcMethod = argsV.get(argsSt++);
+ break;
+ case OPT_JCEXEC:
+ if (jcPath != null || jcMainClass != null || jcMethod != null) {
+ bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
+ }
+ jcExecApp = argsV.get(argsSt++);
+ break;
+ case OPT_TIMING:
+ Utils.setTimingOn();
+ break;
+ case OPT_VERSION:
+ // Utils.printInfoMessage("jmake version " + VERSION); // Printed anyway at present...
+ throw new PrivateException(new PublicExceptions.NoActionRequestedException());
+ case OPT_WARNLIMIT:
+ try {
+ Utils.warningLimit =
+ Integer.parseInt(argsV.get(argsSt++));
+ } catch (NumberFormatException e) {
+ Utils.ignore(e);
+ bailOut(argsV.get(argsSt) + ERR_IS_INVALID_OPTION);
+ }
+ break;
+ case OPT_FAILONDEPJAR:
+ if (noWarnOnDependentJar) {
+ bailOut("it is not allowed to use -nowarnondependentjar and -failondependentjar together");
+ }
+ failOnDependentJar = true;
+ break;
+ case OPT_NOWARNONDEPJAR:
+ if (failOnDependentJar) {
+ bailOut("it is not allowed to use -failondependentjar and -nowarnondependentjar together");
+ }
+ noWarnOnDependentJar = true;
+ break;
+ case OPT_BOOTCLASSPATH:
+ try {
+ setBootClassPath(argsV.get(argsSt++));
+ } catch (PublicExceptions.InvalidCmdOptionException ex) {
+ bailOut(ex.getMessage());
+ }
+ break;
+ case OPT_EXTDIRS:
+ try {
+ setExtDirs(argsV.get(argsSt++));
+ } catch (PublicExceptions.InvalidCmdOptionException ex) {
+ bailOut(ex.getMessage());
+ }
+ break;
+ case OPT_VPATH:
+ try {
+ setVirtualPath(argsV.get(argsSt++));
+ } catch (PublicExceptions.InvalidCmdOptionException ex) {
+ bailOut(ex.getMessage());
+ }
+ break;
+ case OPT_PDB_TEXT_FORMAT:
+ pdbTextFormat = true;
+ break;
+ case OPT_DEPENDENCY_FILE:
+ dependencyFile = argsV.get(argsSt++);
+ break;
+ default:
+ bailOut(arg + ERR_IS_INVALID_OPTION);
+ }
+ } else { // Not an option, at least does not start with "-". Treat it as a command line file name or source name.
+ if (arg.length() > 1 && arg.charAt(0) == '@') {
+ arg = arg.substring(1);
+ loadCmdFile(arg, argsV);
+ } else {
+ if (!arg.endsWith(".java")) {
+ bailOut(arg + ERR_IS_INVALID_OPTION);
+ }
+ allProjectJavaFileNamesList.add(arg);
+ }
+ }
+ }
+ }
+
+ /** Load @-file that can contain anything that command line can contain. */
+ private void loadCmdFile(String name, List<String> argsV) {
+ try {
+ Reader r = new BufferedReader(new FileReader(name));
+ StreamTokenizer st = new StreamTokenizer(r);
+ st.resetSyntax();
+ st.wordChars(' ', 255);
+ st.whitespaceChars(0, ' ');
+ st.commentChar('#');
+ st.quoteChar('"');
+ st.quoteChar('\'');
+ while (st.nextToken() != StreamTokenizer.TT_EOF) {
+ argsV.add(st.sval);
+ }
+ r.close();
+ } catch (IOException e) {
+ throw new PrivateException(new PublicExceptions.CommandFileReadException(name + ":\n" + e.getMessage()));
+ }
+ }
+
+ /**
+ * Main entrypoint for applications that want to call <b>jmake</b> externally and are willing
+ * to handle exceptions that it may throw.
+ *
+ * @param args command line arguments passed to <b>jmake</b>.
+ *
+ * @throws PublicExceptions.NoActionRequestedException if <b>jmake</b> was not requested to do any real work;
+ * @throws PublicExceptions.InvalidCmdOptionException if invalid command line option was detected;
+ * @throws PublicExceptions.PDBCorruptedException if project database is corrupted;
+ * @throws PublicExceptions.CommandFileReadException if there was error reading a command file;
+ * @throws PublicExceptions.CompilerInteractionException if there was a problem initializing or calling the compiler,
+ * or compilation errors were detected;
+ * @throws PublicExceptions.ClassFileParseException if there was error parsing a class file;
+ * @throws PublicExceptions.ClassNameMismatchException if there is a mismatch between the deduced and the actual class name;
+ * @throws PublicExceptions.InvalidSourceFileExtensionException if a supplied source file has an invalid extension (not .java);
+ * @throws PublicExceptions.JarDependsOnSourceException if a class in a <code>JAR</code> is found dependent on a class with the .java source;
+ * @throws PublicExceptions.DoubleEntryException if more than one entry for the same class is found in the project
+ * @throws FileNotFoundException if a <code>.java</code> or a <code>.class</code> file was not found;
+ * @throws IOException if there was an I/O problem of any kind;
+ * @throws PublicExceptions.InternalException if an internal problem that should never happen was detected.
+ */
+ public void mainProgrammatic(String args[]) throws
+ PublicExceptions.NoActionRequestedException,
+ PublicExceptions.InvalidCmdOptionException,
+ PublicExceptions.PDBCorruptedException,
+ PublicExceptions.CommandFileReadException,
+ PublicExceptions.CompilerInteractionException,
+ PublicExceptions.ClassFileParseException,
+ PublicExceptions.ClassNameMismatchException,
+ PublicExceptions.InvalidSourceFileExtensionException,
+ PublicExceptions.JarDependsOnSourceException,
+ PublicExceptions.DoubleEntryException,
+ FileNotFoundException,
+ IOException,
+ PublicExceptions.InternalException {
+ try {
+ Utils.printInfoMessage("Jmake version " + VERSION);
+ if (!controlledExecution) {
+ processCommandLine(args);
+ String[] projectJars = ClassPath.getProjectJars();
+ if (projectJars != null) {
+ for (int i = 0; i < projectJars.length; i++) {
+ allProjectJavaFileNamesList.add(projectJars[i]);
+ }
+ }
+ allProjectJavaFileNames =
+ allProjectJavaFileNamesList.toArray(new String[allProjectJavaFileNamesList.size()]);
+ } else {
+ String[] projectJars = ClassPath.getProjectJars();
+ if (projectJars != null) {
+ String newNames[] =
+ new String[allProjectJavaFileNames.length + projectJars.length];
+ System.arraycopy(allProjectJavaFileNames, 0, newNames, 0, allProjectJavaFileNames.length);
+ System.arraycopy(projectJars, 0, newNames, allProjectJavaFileNames.length, projectJars.length);
+ allProjectJavaFileNames = newNames;
+ }
+ }
+
+ Utils.startTiming(Utils.TIMING_PDBREAD);
+ PCDContainer pcdc;
+ pcdc = PCDContainer.load(pdbFileName, pdbTextFormat);
+ Utils.stopAndPrintTiming("DB read", Utils.TIMING_PDBREAD);
+
+ pcdm = new PCDManager(pcdc, allProjectJavaFileNames,
+ addedJavaFileNames, removedJavaFileNames, updatedJavaFileNames,
+ destDir, javacAddArgs, failOnDependentJar, noWarnOnDependentJar,
+ dependencyFile);
+
+ pcdm.initializeCompiler(jcExecApp, jcPath, jcMainClass, jcMethod, externalApp, externalCompileSourceFilesMethod);
+
+ pcdm.run();
+ } catch (PrivateException e) {
+ Throwable origException = e.getOriginalException();
+ if (origException instanceof PublicExceptions.NoActionRequestedException) {
+ throw (PublicExceptions.NoActionRequestedException) origException;
+ } else if (origException instanceof PublicExceptions.InvalidCmdOptionException) {
+ throw (PublicExceptions.InvalidCmdOptionException) origException;
+ } else if (origException instanceof PublicExceptions.CommandFileReadException) {
+ throw (PublicExceptions.CommandFileReadException) origException;
+ } else if (origException instanceof PublicExceptions.PDBCorruptedException) {
+ throw (PublicExceptions.PDBCorruptedException) origException;
+ } else if (origException instanceof PublicExceptions.CompilerInteractionException) {
+ throw (PublicExceptions.CompilerInteractionException) origException;
+ } else if (origException instanceof PublicExceptions.ClassFileParseException) {
+ throw (PublicExceptions.ClassFileParseException) origException;
+ } else if (origException instanceof PublicExceptions.ClassNameMismatchException) {
+ throw (PublicExceptions.ClassNameMismatchException) origException;
+ } else if (origException instanceof PublicExceptions.InvalidSourceFileExtensionException) {
+ throw (PublicExceptions.InvalidSourceFileExtensionException) origException;
+ } else if (origException instanceof PublicExceptions.JarDependsOnSourceException) {
+ throw (PublicExceptions.JarDependsOnSourceException) origException;
+ } else if (origException instanceof PublicExceptions.DoubleEntryException) {
+ throw (PublicExceptions.DoubleEntryException) origException;
+ } else if (origException instanceof FileNotFoundException) {
+ throw (FileNotFoundException) origException;
+ } else if (origException instanceof IOException) {
+ throw (IOException) origException;
+ } else if (origException instanceof PublicExceptions.InternalException) {
+ throw (PublicExceptions.InternalException) origException;
+ }
+ } finally {
+ ClassPath.resetOnFinish();
+ }
+ }
+
+ /**
+ * Main entrypoint for applications that want to call <b>jmake</b> externally and would prefer
+ * receiving an error code instead of an exception in case something goes wrong.<p>
+ *
+ * @param args command line arguments passed to <b>jmake</b>.
+ *
+ * @return <dl>
+ * <dt><code> 0</code> if everything was successful;
+ * <dt><code> -1</code> invalid command line option detected;
+ * <dt><code> -2</code> error reading command file;
+ * <dt><code> -3</code> project database corrupted;
+ * <dt><code> -4</code> error initializing or calling the compiler;
+ * <dt><code> -5</code> compilation error;
+ * <dt><code> -6</code> error parsing a class file;
+ * <dt><code> -7</code> file not found;
+ * <dt><code> -8</code> I/O exception;
+ * <dt><code> -9</code> internal jmake exception;
+ * <dt><code>-10</code> deduced and actual class name mismatch;
+ * <dt><code>-11</code> invalid source file extension;
+ * <dt><code>-12</code> a class in a <code>JAR</code> is found dependent on a class with the .java source;
+ * <dt><code>-13</code> more than one entry for the same class is found in the project
+ * <dt><code>-20</code> internal Java error (caused by <code>java.lang.InternalError</code>);
+ * <dt><code>-30</code> internal Java error (caused by <code>java.lang.RuntimeException</code>).
+ * </dl>
+ */
+ public int mainExternal(String args[]) {
+ try {
+ mainProgrammatic(args);
+ } catch (PublicExceptions.NoActionRequestedException e0) {
+ // Nothing to do
+ } catch (PublicExceptions.InvalidCmdOptionException e1) {
+ Utils.printErrorMessage(e1.getMessage());
+ return -1;
+ } catch (PublicExceptions.CommandFileReadException e2) {
+ Utils.printErrorMessage("error parsing command file:");
+ Utils.printErrorMessage(e2.getMessage());
+ return -2;
+ } catch (PublicExceptions.PDBCorruptedException e3) {
+ Utils.printErrorMessage("project database corrupted: " + e3.getMessage());
+ return -3;
+ } catch (PublicExceptions.CompilerInteractionException e4) {
+ if (e4.getOriginalException() != null) {
+ Utils.printErrorMessage("error interacting with the compiler: ");
+ Utils.printErrorMessage(e4.getMessage());
+ Utils.printErrorMessage("original exception:");
+ Utils.printErrorMessage(e4.getOriginalException().getMessage());
+ return -4;
+ } else { // Otherwise there is a compilation error, and the compiler has already printed a lot...
+ return -5;
+ }
+ } catch (PublicExceptions.ClassFileParseException e6) {
+ Utils.printErrorMessage(e6.getMessage());
+ return -6;
+ } catch (FileNotFoundException e7) {
+ Utils.printErrorMessage(e7.getMessage());
+ return -7;
+ } catch (IOException e8) {
+ Utils.printErrorMessage(e8.getMessage());
+ return -8;
+ } catch (PublicExceptions.InternalException e9) {
+ Utils.printErrorMessage("internal jmake exception detected:");
+ Utils.printErrorMessage(e9.getMessage());
+ Utils.printErrorMessage(Utils.REPORT_PROBLEM);
+ Utils.printErrorMessage("the stack trace is as follows:");
+ e9.printStackTrace();
+ return -9;
+ } catch (PublicExceptions.ClassNameMismatchException e10) {
+ Utils.printErrorMessage(e10.getMessage());
+ return -10;
+ } catch (PublicExceptions.InvalidSourceFileExtensionException e11) {
+ Utils.printErrorMessage(e11.getMessage());
+ return -11;
+ } catch (PublicExceptions.JarDependsOnSourceException e12) {
+ Utils.printErrorMessage(e12.getMessage());
+ return -12;
+ } catch (PublicExceptions.DoubleEntryException e13) {
+ Utils.printErrorMessage(e13.getMessage());
+ return -13;
+ } catch (InternalError e20) {
+ Utils.printErrorMessage("internal Java error: " + e20);
+ Utils.printErrorMessage("Consult the following stack trace for more info:");
+ e20.printStackTrace();
+ return -20;
+ } catch (RuntimeException e30) {
+ Utils.printErrorMessage("internal Java exception: " + e30);
+ Utils.printErrorMessage("Consult the following stack trace for more info:");
+ e30.printStackTrace();
+ return -30;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Main entrypoint for applications such as Ant, that want to have full control over
+ * compilations that <b>jmake</b> invokes, and are willing to handle exceptions
+ * that it may throw.
+ *
+ * @param javaFileNames array of strings that specify <code>.java</code> file names.
+ * @param destDirName name of the destination directory (<b>jmake</b> will look up binary classes
+ * in there, it should be the same as the one used by the Java compiler method).
+ * If <code>null</code> is passed, classes will be looked up in the same directories
+ * as their sources, in agreement with the default Java compiler behaviour.
+ * @param pdbFileName project database file name (if <code>null</code> is passed,
+ * a file with the default name placed in the current directory will be used).
+ * @param externalApp an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
+ * @param externalCompileSourceFilesMethod a method of the form <code>int x(String[] args)</code>. It
+ * should return <code>0</code> if compilation is successful and any non-zero value
+ * otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
+ * recompile in the form of canonical full path file names.
+ *
+ * @throws PublicExceptions.NoActionRequestedException if <b>jmake</b> was not requested to do any real work;
+ * @throws PublicExceptions.InvalidCmdOptionException if invalid command line option was detected;
+ * @throws PublicExceptions.PDBCorruptedException if project database is corrupted;
+ * @throws PublicExceptions.CommandFileReadException if there was error reading a command file;
+ * @throws PublicExceptions.CompilerInteractionException if there was a problem initializing or calling the compiler,
+ * or compilation errors were detected;
+ * @throws PublicExceptions.ClassFileParseException if there was error parsing a class file;
+ * @throws PublicExceptions.ClassNameMismatchException if there is a mismatch between the deduced and the actual class name;
+ * @throws PublicExceptions.InvalidSourceFileExtensionException if a specified source file has an invalid extension (not .java);
+ * @throws PublicExceptions.JarDependsOnSourceException if a class in a <code>JAR</code> is found dependent on a class with the .java source;
+ * @throws PublicExceptions.DoubleEntryException if more than one entry for the same class is found in the project
+ * @throws PublicExceptions.InternalException if an internal problem that should never happen was detected.
+ * @throws FileNotFoundException if a <code>.java</code> or a <code>.class</code> file was not found;
+ * @throws IOException if there was an I/O problem of any kind;
+ */
+ public void mainProgrammaticControlled(String javaFileNames[], String destDirName, String pdbFileName,
+ Object externalApp, Method externalCompileSourceFilesMethod) throws
+ PublicExceptions.NoActionRequestedException,
+ PublicExceptions.InvalidCmdOptionException,
+ PublicExceptions.PDBCorruptedException,
+ PublicExceptions.CommandFileReadException,
+ PublicExceptions.CompilerInteractionException,
+ PublicExceptions.ClassFileParseException,
+ PublicExceptions.ClassNameMismatchException,
+ PublicExceptions.InvalidSourceFileExtensionException,
+ PublicExceptions.JarDependsOnSourceException,
+ PublicExceptions.DoubleEntryException,
+ PublicExceptions.InternalException,
+ FileNotFoundException,
+ IOException {
+
+ controlledExecution = true;
+ this.pdbFileName = pdbFileName;
+ this.destDir = destDirName;
+ this.allProjectJavaFileNames = javaFileNames;
+ this.externalApp = externalApp;
+ this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
+
+ mainProgrammatic(null);
+ }
+
+ /**
+ * Main entrypoint for applications such as Ant, that want to have full control over
+ * compilations that <b>jmake</b> invokes, and do not want to handle exceptions that it
+ * may throw. Error codes returned are the same as <code>mainExternal(String[])</code> returns.
+ *
+ * @param javaFileNames array of strings that specify <code>.java</code> file names.
+ * @param destDirName name of the destination directory (<b>jmake</b> will look up binary classes
+ * in there, it should be the same as the one used by the Java compiler method).
+ * If <code>null</code> is passed, classes will be looked up in the same directories
+ * as their sources, in agreement with the default Java compiler behaviour.
+ * @param pdbFileName project database file name (if <code>null</code> is passed,
+ * a file with the default name placed in the current directory will be used).
+ * @param externalApp an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
+ * @param externalCompileSourceFilesMethod a method of the form <code>int x(String[] args)</code>. It
+ * should return <code>0</code> if compilation is successful and any non-zero value
+ * otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
+ * recompile in the form of canonical full path file names.
+ *
+ * @see #mainExternal(String[])
+ */
+ public int mainExternalControlled(String javaFileNames[], String destDirName, String pdbFileName,
+ Object externalApp, Method externalCompileSourceFilesMethod) {
+ controlledExecution = true;
+ this.pdbFileName = pdbFileName;
+ this.destDir = destDirName;
+ this.allProjectJavaFileNames = javaFileNames;
+ this.externalApp = externalApp;
+ this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
+
+ return mainExternal(null);
+ }
+
+ /**
+ * Main entrypoint for applications such as IDEs, that themselves keep track of updated/added/removed sources,
+ * want to have full control over compilations that <b>jmake</b> invokes, and are willing to handle exceptions
+ * that it may throw.
+ *
+ * @param addedJavaFileNames names of <code>.java</code> files just added to the project
+ * @param removedJavaFileNames names of <code>.java</code> files just removed from the project
+ * @param updatedJavaFileNames names of updated project <code>.java</code> files
+ * @param destDirName name of the destination directory (<b>jmake</b> will look up binary classes
+ * in there, it should be the same as the one used by the Java compiler method).
+ * If <code>null</code> is passed, classes will be looked up in the same directories
+ * as their sources, in agreement with the default Java compiler behaviour.
+ * @param pdbFileName project database file name (if <code>null</code> is passed,
+ * a file with the default name placed in the current directory will be used).
+ * @param externalApp an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
+ * @param externalCompileSourceFilesMethod a method of the form <code>int x(String[] args)</code>. It
+ * should return <code>0</code> if compilation is successful and any non-zero value
+ * otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
+ * recompile in the form of canonical full path file names.
+ *
+ * @throws PublicExceptions.NoActionRequestedException if <b>jmake</b> was not requested to do any real work;
+ * @throws PublicExceptions.InvalidCmdOptionException if invalid command line option was detected;
+ * @throws PublicExceptions.PDBCorruptedException if project database is corrupted;
+ * @throws PublicExceptions.CommandFileReadException if there was error reading a command file;
+ * @throws PublicExceptions.CompilerInteractionException if there was a problem initializing or calling the compiler,
+ * or compilation errors were detected;
+ * @throws PublicExceptions.ClassFileParseException if there was error parsing a class file;
+ * @throws PublicExceptions.ClassNameMismatchException if there is a mismatch between the deduced and the actual class name;
+ * @throws PublicExceptions.InvalidSourceFileExtensionException if a specified source file has an invalid extension (not .java);
+ * @throws PublicExceptions.JarDependsOnSourceException if a class in a <code>JAR</code> is found dependent on a class with the .java source;
+ * @throws PublicExceptions.DoubleEntryException if more than one entry for the same class is found in the project
+ * @throws PublicExceptions.InternalException if an internal problem that should never happen was detected.
+ * @throws FileNotFoundException if a <code>.java</code> or a <code>.class</code> file was not found;
+ * @throws IOException if there was an I/O problem of any kind;
+ */
+ public void mainProgrammaticControlled(String addedJavaFileNames[], String removedJavaFileNames[], String updatedJavaFileNames[],
+ String destDirName, String pdbFileName,
+ Object externalApp, Method externalCompileSourceFilesMethod) throws
+ PublicExceptions.NoActionRequestedException,
+ PublicExceptions.InvalidCmdOptionException,
+ PublicExceptions.PDBCorruptedException,
+ PublicExceptions.CommandFileReadException,
+ PublicExceptions.CompilerInteractionException,
+ PublicExceptions.ClassFileParseException,
+ PublicExceptions.ClassNameMismatchException,
+ PublicExceptions.InvalidSourceFileExtensionException,
+ PublicExceptions.JarDependsOnSourceException,
+ PublicExceptions.DoubleEntryException,
+ PublicExceptions.InternalException,
+ FileNotFoundException,
+ IOException {
+
+ controlledExecution = true;
+ this.pdbFileName = pdbFileName;
+ this.destDir = destDirName;
+ this.addedJavaFileNames = addedJavaFileNames;
+ this.removedJavaFileNames = removedJavaFileNames;
+ this.updatedJavaFileNames = updatedJavaFileNames;
+ this.externalApp = externalApp;
+ this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
+
+ mainProgrammatic(null);
+ }
+
+ /**
+ * Main entrypoint for applications such as IDEs, that themselves keep track of updated/added/removed sources,
+ * want to have full control over compilations that <b>jmake</b> invokes, and do not want to handle exceptions
+ * that it may throw. Error codes returned are the same as <code>mainExternal(String[])</code> returns.
+ *
+ * @param addedJavaFileNames names of <code>.java</code> files just added to the project
+ * @param removedJavaFileNames names of <code>.java</code> files just removed from the project
+ * @param updatedJavaFileNames names of updated project <code>.java</code> files
+ * @param destDirName name of the destination directory (<b>jmake</b> will look up binary classes
+ * in there, it should be the same as the one used by the Java compiler method).
+ * If <code>null</code> is passed, classes will be looked up in the same directories
+ * as their sources, in agreement with the default Java compiler behaviour.
+ * @param pdbFileName project database file name (if <code>null</code> is passed,
+ * a file with the default name placed in the current directory will be used).
+ * @param externalApp an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
+ * @param externalCompileSourceFilesMethod a method of the form <code>int x(String[] args)</code>. It
+ * should return <code>0</code> if compilation is successful and any non-zero value
+ * otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
+ * recompile in the form of canonical full path file names.
+ *
+ * @see #mainExternal(String[])
+ */
+ public int mainExternalControlled(String addedJavaFileNames[], String removedJavaFileNames[], String updatedJavaFileNames[],
+ String destDirName, String pdbFileName,
+ Object externalApp, Method externalCompileSourceFilesMethod) {
+ controlledExecution = true;
+ this.pdbFileName = pdbFileName;
+ this.destDir = destDirName;
+ this.addedJavaFileNames = addedJavaFileNames;
+ this.removedJavaFileNames = removedJavaFileNames;
+ this.updatedJavaFileNames = updatedJavaFileNames;
+ this.externalApp = externalApp;
+ this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
+
+ return mainExternal(null);
+ }
+
+ /**
+ * Main entrypoint for the standalone <b>jmake</b> application. This method calls does little but calling
+ * <code>mainExternal</code>, and its execution always completes with <code>System.exit(code)</code>,
+ * where <code>code</code> is the value returned by <code>mainExternal</code>.
+ *
+ * @see #mainExternal(String[])
+ * @see #mainProgrammatic(String[])
+ *
+ * @param args command line arguments passed to <b>jmake</b>
+ */
+ public static void main(String args[]) {
+ Utils.startTiming(Utils.TIMING_TOTAL);
+
+ Main m = new Main();
+ int exitCode = m.mainExternal(args);
+
+ Utils.stopAndPrintTiming("Total", Utils.TIMING_TOTAL);
+ if ( exitCode != 0 ) {
+ System.exit(exitCode);
+ }
+ }
+
+ /**
+ * Customize the output of <b>jmake</b>.
+ *
+ * @see #setOutputStreams(PrintStream, PrintStream, PrintStream)
+ *
+ * @param printInfoMessages specify whether to print information messages
+ * @param printWarningMessages specify whether to print warning messages
+ * @param printErrorMessages specify whether to print error messages
+ */
+ public static void customizeOutput(boolean printInfoMessages,
+ boolean printWarningMessages,
+ boolean printErrorMessages) {
+ Utils.customizeOutput(printInfoMessages, printWarningMessages, printErrorMessages);
+ }
+
+ /**
+ * Set the class path to be used by the compiler, and also by the dependency checker for the purposes of
+ * superclass/superinterface change tracking. For the compiler, this class path will be merged with the
+ * project class path (set via setProjectClassPath(String)). Other than that, its value will be used only to
+ * look up superclasses/superinterfaces of project classes. Note that non-project superclasses and
+ * superinterfaces are first looked up at the boot class path, then on the extension class path, and then
+ * on this class path.
+ *
+ * @see #setProjectClassPath(String)
+ * @see #setBootClassPath(String)
+ * @see #setExtDirs(String)
+ *
+ * @param classPath the value of the class path, in the usual format (i.e. entries that are directories
+ * or JARs, separated by colon or semicolon depending on the platform).
+ *
+ * @throws PublicExceptions.InvalidCmdOptionException if invalid class path value is specified.
+ */
+ public static void setClassPath(String classPath) throws PublicExceptions.InvalidCmdOptionException {
+ ClassPath.setClassPath(classPath);
+ }
+
+ /**
+ * Set the class path to be used by the compiler, and also by the dependency checker for the purposes of
+ * superclass/superinterface change tracking and sourceless class dependency checking. For the compiler,
+ * and also in order to look up superclasses/superinterfaces of project classes, this class path will be
+ * merged with the standard class path (set via setClassPath(String)). But in addition, all binary classes
+ * that are on this class path are stored in the project database and checked for updates every time jmake
+ * is invoked. Any changes to these classes trigger the standard dependency checking procedure. However,
+ * dependent classes are looked up only among the "normal" project classes, i.e. those that have sources.
+ * Therefore sourceless classes are assumed to always be mutually consistent.
+ *
+ * Currently only JAR files can be present on this class path.
+ *
+ * @see #setClassPath(String)
+ *
+ * @param projectClassPath the value of the class path, in the usual format (i.e. entries that are directories
+ * or JARs, separated by colon or semicolon depending on the platform).
+ *
+ * @throws PublicExceptions.InvalidCmdOptionException if invalid class path value is specified.
+ */
+ public static void setProjectClassPath(String projectClassPath) throws PublicExceptions.InvalidCmdOptionException {
+ ClassPath.setProjectClassPath(projectClassPath);
+ }
+
+ /**
+ * Set the boot class path to be used by the compiler (-bootclasspath option) and also by the dependency
+ * checker (by default, the value of "sun.boot.class.path" property is used).
+ *
+ * @see #setClassPath(String)
+ *
+ * @param classPath the value of the boot class path, in the usual format (i.e. entries that are directories
+ * or JARs, separated by colon or semicolon depending on the platform).
+ *
+ * @throws PublicExceptions.InvalidCmdOptionException if invalid class path value is specified.
+ */
+ public static void setBootClassPath(String classPath) throws PublicExceptions.InvalidCmdOptionException {
+ ClassPath.setBootClassPath(classPath);
+ }
+
+ /**
+ * Set the extensions location to be used by the compiler (-extdirs option) and also by the dependency
+ * checker (by default, the value of "java.ext.dirs" property is used).
+ *
+ * @see #setClassPath(String)
+ *
+ * @param dirs the value of extension directories, in the usual format (one or more directory names
+ * separated by colon or semicolon depending on the platform).
+ *
+ * @throws PublicExceptions.InvalidCmdOptionException if invalid class path value is specified.
+ */
+ public static void setExtDirs(String dirs) throws PublicExceptions.InvalidCmdOptionException {
+ ClassPath.setExtDirs(dirs);
+ }
+
+ /**
+ * Set the virtual path used to find both source and class files that are part of the project
+ * but are not in the local directory.
+ *
+ * @see #setClassPath(String)
+ *
+ * @param dirs the value of extension directories, in the usual format (one or more directory names
+ * separated by colon or semicolon depending on the platform).
+ *
+ * @throws PublicExceptions.InvalidCmdOptionException if invalid path value is specified.
+ */
+ public static void setVirtualPath(String dirs) throws PublicExceptions.InvalidCmdOptionException {
+ ClassPath.setVirtualPath(dirs);
+ }
+
+ /** Produce no warning or error message upon a dependent <code>JAR</code> detection. */
+ public static final int DEPJAR_NOWARNORERROR = 0;
+ /** Produce a warning upon a dependent <code>JAR</code> detection. */
+ public static final int DEPJAR_WARNING = 1;
+ /** Produce an error message (throw an exception) upon a dependent <code>JAR</code> detection. */
+ public static final int DEPJAR_ERROR = 2;
+
+ /**
+ * Set the response of <b>jmake</b> in case a dependence of a class located in a <code>JAR</code> file on a
+ * class with a <code>.java</code> source is detected (such dependencies are highly discouraged, since it is not
+ * possible to recompile a class in the <code>JAR</code> that has no source).
+ *
+ * @param code response type: DEPJAR_NOWARNORERROR, DEPJAR_WARNING (default behaviour) or DEPJAR_ERROR.
+ */
+ public void setResponseOnDependentJar(int code) {
+ switch (code) {
+ case DEPJAR_NOWARNORERROR:
+ noWarnOnDependentJar = true;
+ failOnDependentJar = false;
+ break;
+ case DEPJAR_WARNING:
+ noWarnOnDependentJar = false;
+ failOnDependentJar = false;
+ break;
+ case DEPJAR_ERROR:
+ noWarnOnDependentJar = false;
+ failOnDependentJar = true;
+ break;
+ }
+ }
+
+ /**
+ * Return the names of all classes that <b>jmake</b>, on this invocation, found updated - either because
+ * <b>jmake</b> itself recompiled them or because they were updated independently (their timestamp/checksum
+ * found different from those contained in the project database).
+ */
+ public String[] getUpdatedClasses() {
+ return pcdm.getAllUpdatedClassesAsStringArray();
+ }
+
+ /**
+ * Set the output print streams to be used by <b>jmake</b>.
+ *
+ * @see #customizeOutput(boolean, boolean, boolean)
+ *
+ * @param out print stream to be used for information ("logging") messages that <b>jmake</b> emits
+ * @param warn print stream to be used for warning messages
+ * @param err print stream to be used for error messages
+ */
+ public static void setOutputStreams(PrintStream out, PrintStream warn, PrintStream err) {
+ Utils.setOutputStreams(out, warn, err);
+ }
+
+ /** Get the version of this copy of <b>jmake</b> */
+ public static String getVersion() {
+ return VERSION;
+ }
+ private static final String ERR_IS_INVALID_OPTION =
+ " is an invalid option or argument.";
+ private static final String ERR_NO_TWO_COMPILER_OPTIONS =
+ "You may not specify both compiler class and compiler executable application";
+ private static final String ERR_SHOULD_BE_EXPLICIT =
+ " compiler option should be specified directly as a jmake option";
+
+ private static void bailOut(String s) {
+ throw new PrivateException(new PublicExceptions.InvalidCmdOptionException("jmake: " + s + "\nRun \"jmake -h\" for help."));
+ }
+
+ private static void optRequiresArg(String s) {
+ bailOut("the " + s + " option requires an argument.");
+ }
+
+ private static void printUsage() {
+ Utils.printInfoMessage("Usage: jmake <options> <.java files> <@files>");
+ Utils.printInfoMessage("where possible options include:");
+ Utils.printInfoMessage(" -h, -help print this help message");
+ Utils.printInfoMessage(" -version print the product version number");
+ Utils.printInfoMessage(" -pdb <file name> specify non-default project database file");
+ Utils.printInfoMessage(" -pdb-text-format if specified, pdb file is stored in text format");
+ Utils.printInfoMessage(" -d <directory> specify where to place generated class files");
+ Utils.printInfoMessage(" -classpath <path> specify where to find user class files");
+ Utils.printInfoMessage(" -projclasspath <path> specify where to find sourceless project classes");
+ Utils.printInfoMessage(" (currently only JARs are allowed on this path)");
+ Utils.printInfoMessage(" -C<option> specify an option to be passed to the Java compiler");
+ Utils.printInfoMessage(" (this option's arguments should also be preceded by -C)");
+ Utils.printInfoMessage(" -jcpath <path> specify the class path for a non-default Java compiler");
+ Utils.printInfoMessage(" (default is <JAVAHOME>/lib/tools.jar)");
+ Utils.printInfoMessage(" -jcmainclass <class> specify the main class for a non-default Java compiler");
+ Utils.printInfoMessage(" (default is com.sun.tools.javac.Main)");
+ Utils.printInfoMessage(" -jcmethod <method> specify the method to call in the Java compiler class");
+ Utils.printInfoMessage(" (default is \"compile(String args[])\")");
+ Utils.printInfoMessage(" -jcexec <file name> specify a binary non-default Java compiler application");
+ Utils.printInfoMessage(" -failondependentjar fail if a class on projectclasspath depends on a class");
+ Utils.printInfoMessage(" with .java source (by default, a warning is issued)");
+ Utils.printInfoMessage(" -nowarnondependentjar no warning or error if a class on projectclasspath");
+ Utils.printInfoMessage(" depends on a class with a .java source (use with care)");
+ Utils.printInfoMessage(" -warnlimit <number> specify the maximum number of warnings (20 by default)");
+ Utils.printInfoMessage(" -bootclasspath <path> override location of bootstrap class files");
+ Utils.printInfoMessage(" -extdirs <dirs> override location of installed extensions");
+ Utils.printInfoMessage(" -vpath <dirs> a list of directories to search for Java and class files similar to GNUMake's VPATH");
+ Utils.printInfoMessage(" -depfile <path> a file generated by the compiler containing additional java->class mappings");
+ Utils.printInfoMessage("");
+ Utils.printInfoMessage("Examples:");
+ Utils.printInfoMessage(" jmake -d classes -classpath .;mylib.jar X.java Y.java Z.java");
+ Utils.printInfoMessage(" jmake -pdb myproject.pdb -jcexec c:\\java\\jikes\\jikes.exe @myproject.src");
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698