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"); |
+ } |
+} |