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