Gives legacy defaults for -extra and -workDir, to smooth migration
to the new WAR-based output format.

Patch by: fabbott
Review by: jat, spoon, scottb


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4118 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index 12faf69..53180f9 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -29,8 +29,8 @@
 import java.util.TreeSet;
 
 /**
- * Performs the first phase of compilation, generating the set of permutations
- * to compile, and a ready-to-compile AST.
+ * Performs the second phase of compilation, converting the Precompile's AST into
+ * JavaScript outputs.
  */
 public class CompilePerms {
 
@@ -180,6 +180,10 @@
      */
     final CompilePermsOptions options = new CompilePermsOptionsImpl();
     if (new ArgProcessor(options).processArgs(args)) {
+      if (options.getWorkDir() == null) {
+        System.err.println("The -workDir is required for the CompilePerms phase.");
+        System.exit(1);
+      }
       CompileTask task = new CompileTask() {
         public boolean run(TreeLogger logger) throws UnableToCompleteException {
           return new CompilePerms(options).run(logger);
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index 9a85324..71c96a0 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -21,11 +21,14 @@
 import com.google.gwt.dev.CompileTaskRunner.CompileTask;
 import com.google.gwt.dev.Link.LinkOptionsImpl;
 import com.google.gwt.dev.Precompile.PrecompileOptionsImpl;
+import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.dev.util.PerfLogger;
+import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
 import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
 
 import java.io.File;
+import java.io.IOException;
 
 /**
  * The main executable entry point for the GWT Java to JavaScript compiler.
@@ -66,6 +69,10 @@
       return linkOptions.getExtraDir();
     }
 
+    public File getLegacyExtraDir(TreeLogger logger, ModuleDef module) {
+      return linkOptions.getLegacyExtraDir(logger, module);
+    }
+
     public File getOutDir() {
       return linkOptions.getOutDir();
     }
@@ -86,19 +93,34 @@
      * shutdown AWT related threads, since the contract for their termination is
      * still implementation-dependent.
      */
+    boolean deleteWorkDir = false;
     final CompilerOptions options = new GWTCompilerOptionsImpl();
     if (new ArgProcessor(options).processArgs(args)) {
+      try {
+        deleteWorkDir = options.ensureWorkDir();
+        System.err.println("deleteWorkDir: " + deleteWorkDir);
+      } catch (IOException ex) {
+        System.err.println("Couldn't create new workDir: " + ex.getMessage());
+        System.err.println("Either fix the error, or supply an explicit -workDir flag.");
+        System.exit(1);
+      }
       CompileTask task = new CompileTask() {
         public boolean run(TreeLogger logger) throws UnableToCompleteException {
           return new GWTCompiler(options).run(logger);
         }
       };
       if (CompileTaskRunner.runWithAppropriateLogger(options, task)) {
+        if (deleteWorkDir) {
+          Util.recursiveDelete(options.getWorkDir(), false);
+        }
         // Exit w/ success code.
         System.exit(0);
       }
     }
     // Exit w/ non-success code.
+    if (deleteWorkDir) {
+      Util.recursiveDelete(options.getWorkDir(), false);
+    }
     System.exit(1);
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/Link.java b/dev/core/src/com/google/gwt/dev/Link.java
index 97968b0..182d44b 100644
--- a/dev/core/src/com/google/gwt/dev/Link.java
+++ b/dev/core/src/com/google/gwt/dev/Link.java
@@ -34,11 +34,12 @@
 
 import java.io.File;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 /**
- * Performs the first phase of compilation, generating the set of permutations
- * to compile, and a ready-to-compile AST.
+ * Performs the last phase of compilation, merging the compilation outputs.
  */
 public class Link {
   /**
@@ -46,6 +47,20 @@
    */
   public interface LinkOptions extends CompileTaskOptions, OptionExtraDir,
       OptionOutDir {
+    
+    /**
+     * A born-deprecated method to enact "legacy" -aux support if -extra is not
+     * specified.  This method will <i>either</i> return the same as 
+     * {@link OptionExtraDir#getExtraDir()}, if -extra is specified, <i>or</i>
+     * return the legacy equivalent <i>outputDir</i>/<i>ModuleName</i>-aux.  In
+     * this second case, it will also emit a warning to direct people to -extra,
+     * and flag the fact that non-deployable bits are in what should be the 
+     * deployable output directory. 
+     *
+     * @return pathname for extra files
+     */
+    @Deprecated
+    File getLegacyExtraDir(TreeLogger logger, ModuleDef module);
   }
 
   static class ArgProcessor extends CompileArgProcessor {
@@ -69,6 +84,7 @@
 
     private File extraDir;
     private File outDir;
+    private Set<File> alreadyWarned = new HashSet<File>();
 
     public LinkOptionsImpl() {
     }
@@ -87,6 +103,26 @@
       return extraDir;
     }
 
+    public File getLegacyExtraDir(TreeLogger logger, ModuleDef module) {
+      // safety that we need this at all...
+      if (extraDir != null) {
+        return extraDir;
+      }
+      // okay, figure the old location, by module...
+      String deployDir = module.getDeployTo();
+      File moduleExtraDir = new File(outDir, 
+          deployDir.substring(0, deployDir.length() - 1) + "-aux");
+      // and warn the user about it, but only if it'd be new news.
+      if (!alreadyWarned.contains(moduleExtraDir)) {
+        logger.log(TreeLogger.WARN, "Non-deployed artificats will be in " 
+            + moduleExtraDir.getPath()
+            + ", inside deployable output directory " + outDir.getPath()
+            + ".  Use -extra to relocate the auxilliary files.");
+        alreadyWarned.add(moduleExtraDir);
+      }
+      return moduleExtraDir;
+    }
+
     public File getOutDir() {
       return outDir;
     }
@@ -117,6 +153,10 @@
      */
     final LinkOptions options = new LinkOptionsImpl();
     if (new ArgProcessor(options).processArgs(args)) {
+      if (options.getWorkDir() == null) {
+        System.err.println("The -workDir is required for the Link phase.");
+        System.exit(1);
+      }
       CompileTask task = new CompileTask() {
         public boolean run(TreeLogger logger) throws UnableToCompleteException {
           return new Link(options).run(logger);
@@ -244,9 +284,12 @@
     module = ModuleDefLoader.loadFromClassPath(logger, options.getModuleName());
     moduleOutDir = new File(options.getOutDir(), module.getDeployTo());
     Util.recursiveDelete(moduleOutDir, true);
-    if (options.getExtraDir() != null) {
+    if (options.getExtraDir() == null) {
+      // legacy location
+      moduleExtraDir = options.getLegacyExtraDir(logger, module);
+    } else {
       moduleExtraDir = new File(options.getExtraDir(), module.getDeployTo());
-      Util.recursiveDelete(moduleExtraDir, false);
     }
+    Util.recursiveDelete(moduleExtraDir, false);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index a7e8bf0..6be2c2f 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -47,8 +47,10 @@
 import com.google.gwt.dev.util.arg.ArgHandlerValidateOnlyFlag;
 import com.google.gwt.dev.util.arg.OptionGenDir;
 import com.google.gwt.dev.util.arg.OptionValidateOnly;
+import com.google.gwt.util.tools.Utility;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.SortedMap;
@@ -66,6 +68,7 @@
    */
   public interface PrecompileOptions extends JJSOptions, CompileTaskOptions,
       OptionGenDir, OptionValidateOnly {
+    boolean ensureWorkDir() throws IOException;
   }
 
   static class ArgProcessor extends CompileArgProcessor {
@@ -106,6 +109,24 @@
       setValidateOnly(other.isValidateOnly());
     }
 
+    /**
+     * Checks that the workDir is set, or creates one if not using a temporary
+     * name.
+     * @returns true if intervention was required, i.e. we created a new directory
+     */
+    public boolean ensureWorkDir() throws IOException {
+      File workDir = super.getWorkDir();
+      if (workDir == null) {
+        // user didn't give us one, make one in a random location
+        workDir = Utility.makeTemporaryDirectory(null, "work-");
+        System.err.println("Created new work directory at " + workDir.getAbsolutePath()
+            + ", which will be removed on exit.  (Use -workDir DIRNAME to retain.)");
+        super.setWorkDir(workDir);
+        return true;
+      }
+      return false;
+    }
+
     public File getGenDir() {
       return genDir;
     }
@@ -226,6 +247,10 @@
      */
     final PrecompileOptions options = new PrecompileOptionsImpl();
     if (new ArgProcessor(options).processArgs(args)) {
+      if (options.getWorkDir() == null) {
+        System.err.println("The -workDir is required for the Precompile phase.");
+        System.exit(1);
+      }
       CompileTask task = new CompileTask() {
         public boolean run(TreeLogger logger) throws UnableToCompleteException {
           return new Precompile(options).run(logger);
diff --git a/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java b/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
index 1fc2d9b..166ac03 100644
--- a/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
+++ b/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
@@ -174,8 +174,11 @@
     // Tell Tomcat its base directory so that it won't complain.
     //
     String catBase = System.getProperty("catalina.base");
-    if (catBase == null) {
-      catBase = generateDefaultCatalinaBase(logger, topWorkDir);
+    if (catBase == null || !(new File(catBase)).exists()) {
+      if (catBase == null) {
+        catBase = topWorkDir.getAbsolutePath() + "tomcat"; 
+      }
+      generateDefaultCatalinaBase(logger, new File(catBase));
       System.setProperty("catalina.base", catBase);
     }
 
@@ -328,10 +331,10 @@
    * Extracts a valid catalina base instance from the classpath. Does not
    * overwrite any existing files.
    */
-  private String generateDefaultCatalinaBase(TreeLogger logger, File workDir) {
+  private void generateDefaultCatalinaBase(TreeLogger logger, File catBase) {
     logger = logger.branch(
         TreeLogger.TRACE,
-        "Property 'catalina.base' not specified; checking for a standard catalina base image instead",
+        "Property 'catalina.base' not specified or not present; checking for a standard catalina base image instead",
         null);
 
     // Recursively copies out files and directories
@@ -346,7 +349,6 @@
       caught = e;
     }
 
-    File catBase = new File(workDir, "tomcat");
     if (resourceMap == null || resourceMap.isEmpty()) {
       logger.log(TreeLogger.WARN, "Could not find " + tomcatEtcDir, caught);
     } else {
@@ -354,8 +356,6 @@
         copyFileNoOverwrite(logger, entry.getKey(), entry.getValue(), catBase);
       }
     }
-
-    return catBase.getAbsolutePath();
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerExtraDir.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerExtraDir.java
index 42626cf..b45b807 100644
--- a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerExtraDir.java
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerExtraDir.java
@@ -20,7 +20,8 @@
 import java.io.File;
 
 /**
- * Argument handler for processing the extra directory option.
+ * Argument handler for processing the extra directory option.  If not specified,
+ * legacy behavior is to write private artifacts to ${outdir}/Module-aux.
  */
 public final class ArgHandlerExtraDir extends ArgHandlerDir {
 
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerWorkDir.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerWorkDir.java
index c08130f..7c3ef77 100644
--- a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerWorkDir.java
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerWorkDir.java
@@ -20,7 +20,10 @@
 import java.io.File;
 
 /**
- * Argument handler for processing the output directory flag.
+ * Argument handler for processing the output directory flag.  If not specified,
+ * the GWTCompiler and Precompile classes will create one (under 
+ * ${java.io.tmpdir}/GWT_TMP_DIR) with a guaranteed-unique name; the other tasks
+ * will fail.
  */
 public final class ArgHandlerWorkDir extends ArgHandlerDir {
 
@@ -32,14 +35,8 @@
     this.option = option;
   }
 
-  public String[] getDefaultArgs() {
-    return new String[] {
-        "-workDir",
-        new File(System.getProperty("java.io.tmpdir"), GWT_TMP_DIR).getAbsolutePath()};
-  }
-
   public String getPurpose() {
-    return "The compiler work directory (must be writeable; defaults to system temp dir)";
+    return "The compiler work directory (must be writeable; defaults to random name under system temp dir)";
   }
 
   public String getTag() {
diff --git a/dev/core/src/com/google/gwt/util/tools/Utility.java b/dev/core/src/com/google/gwt/util/tools/Utility.java
index 53a5e4c..3e99fcf 100644
--- a/dev/core/src/com/google/gwt/util/tools/Utility.java
+++ b/dev/core/src/com/google/gwt/util/tools/Utility.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.util.tools;
 
+import com.google.gwt.dev.util.arg.ArgHandlerWorkDir;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -226,6 +228,46 @@
     return sInstallPath;
   }
 
+  /**
+   * Creates a randomly-named temporary directory.
+   * 
+   * @param baseDir base directory to contain the new directory.  May be 
+   *    {@code null}, in which case a subdirectory under the
+   *    {@code java.io.tmpdir} system property, named by 
+   *    {@link ArgHandlerWorkDir#GWT_TMP_DIR}, will be used.
+   * @param prefix the initial characters of the new directory name.  Since all the
+   *    platforms we care about allow long names, this will not be pruned as for
+   *    {@link File#createTempFile(String, String, File), but short names are still
+   *    preferable.  The directory created will have five random characters appended
+   *    to the prefix.
+   * @returns a newly-created temporary directory
+   */
+  public static File makeTemporaryDirectory(File baseDir, String prefix) 
+      throws IOException {
+    if (baseDir == null) {
+      baseDir = new File(System.getProperty("java.io.tmpdir"), ArgHandlerWorkDir.GWT_TMP_DIR);
+    }
+    int tries = 0;
+    while (tries < 20) {
+      tries++;
+      int rand = (int) (Math.random() * Integer.MAX_VALUE);
+      StringBuffer tag = new StringBuffer();
+      for (int i = 0; i < 5; i++) {
+        int bits = rand & 0x1f; // low 5 bits: [a-z][0-4] for 32 values
+        rand >>= 5;
+        tag.append(Character.forDigit(bits, 36)); 
+      }
+      File result = new File(baseDir, prefix + tag.toString());
+      if (!result.exists()) {
+        if (result.mkdirs()) {
+          return result;
+        }
+      }
+    }
+    throw new IOException("couldn't create temporary directory in 20 tries in " 
+        + baseDir.getAbsolutePath());
+  }
+  
   public static void streamOut(File file, OutputStream out, int bufferSize)
       throws IOException {
     FileInputStream fis = null;