Super Dev Mode: expose output module and sourcemap locations

Adds to the public API:

  JobEvent.getOutputModuleName()
  JobEvent.Builder.setOutputModuleName(String)
  CompileDir.findSourceMapFiles(String)

This is to make it easy to find the sizes of the sourcemap files
from outside the code server.

(Doesn't need to be in 2.7.)

Change-Id: Ic5b232a35b2ae13a3d2e0a40ab2777ba80521932
Review-Link: https://gwt-review.googlesource.com/#/c/9720/
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/CompileDir.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/CompileDir.java
index 6509cc0..54d8f3c 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/CompileDir.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/CompileDir.java
@@ -19,10 +19,12 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.thirdparty.guava.common.base.Charsets;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
 import com.google.gwt.thirdparty.guava.common.collect.Lists;
 import com.google.gwt.thirdparty.guava.common.io.Files;
 
 import java.io.File;
+import java.io.FilenameFilter;
 import java.io.IOException;
 import java.util.List;
 
@@ -91,12 +93,37 @@
     return new File(dir, "compile.log");
   }
 
-  File findSymbolMapDir(String moduleName) {
+  /**
+   * Given the outputModuleName from the compiler, returns all the sourcemap
+   * files generated, or null if the directory couldn't  be listed.
+   */
+  public List<File> findSourceMapFiles(String outputModuleName) {
+    File symbolMapsDir = findSymbolMapDir(outputModuleName);
+    if (symbolMapsDir == null) {
+      return null;
+    }
+    File mapDir = symbolMapsDir.getAbsoluteFile();
+
+    File[] files = mapDir.listFiles(new FilenameFilter() {
+      @Override
+      public boolean accept(File dir, String name) {
+        return name.endsWith(Outbox.SOURCEMAP_FILE_SUFFIX);
+      }
+    });
+
+    return files == null ? null : ImmutableList.copyOf(files);
+  }
+
+  /**
+   * Given the outputModuleName from the compiler, returns the location of
+   * the symbol maps directory, or null if not available.
+   */
+  File findSymbolMapDir(String outputModuleName) {
     // The JUnit module moves the symbolMaps directory in a post linker.
     // TODO(skybrian) query this information from the compiler somehow?
     File[] candidates = {
-        new File(getExtraDir(), moduleName + "/symbolMaps"),
-        new File(getWarDir(), moduleName + "/.junit_symbolMaps")
+        new File(getExtraDir(), outputModuleName + "/symbolMaps"),
+        new File(getWarDir(), outputModuleName + "/.junit_symbolMaps")
     };
 
     for (File candidate : candidates) {
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
index 9af7cda..2b883d3 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
@@ -80,6 +80,7 @@
 
   private CompileDir compileDir; // non-null after the compile has started
   private CompileStrategy compileStrategy; // non-null after the compile has started
+  private String outputModuleName; // non-null after successful compile
 
   private Exception listenerFailure;
 
@@ -256,6 +257,7 @@
     }
 
     result.set(newResult);
+    outputModuleName = newResult.outputModuleName;
     if (newResult.isOk()) {
       publish(makeEvent(Status.SERVING));
     } else {
@@ -284,6 +286,7 @@
     out.setBindings(getBindingProperties());
     out.setStatus(status);
     out.setMessage(message);
+    out.setOutputModuleName(outputModuleName);
     out.setCompileDir(compileDir);
     out.setCompileStrategy(compileStrategy);
     out.setArguments(args);
@@ -344,13 +347,19 @@
     final CompileDir outputDir;
 
     /**
+     * non-null if successful.
+     */
+    final String outputModuleName;
+
+    /**
      * non-null for an error
      */
     final Throwable error;
 
-    Result(CompileDir outputDir, Throwable error) {
+    Result(CompileDir outputDir, String outputModuleName, Throwable error) {
       assert (outputDir == null) != (error == null);
       this.outputDir = outputDir;
+      this.outputModuleName = outputModuleName;
       this.error = error;
     }
 
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEvent.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEvent.java
index e4582a6..bae068a 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEvent.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEvent.java
@@ -42,6 +42,7 @@
   private final Status status;
   private final String message;
 
+  private final String outputModuleName;
   private final CompileDir compileDir;
   private final CompileStrategy compileStrategy;
   private final ImmutableList<String> arguments;
@@ -55,6 +56,7 @@
     this.message = builder.message == null ? status.defaultMessage : builder.message;
 
     // The following fields may be null.
+    this.outputModuleName = builder.outputModuleName;
     this.compileDir = builder.compileDir;
     this.compileStrategy = builder.compileStrategy;
     this.arguments = ImmutableList.copyOf(builder.args);
@@ -101,6 +103,16 @@
   }
 
   /**
+   * Returns the module name used for the output of the GWT compiler, or null if not available.
+   * (This name is used for the name of the module directory, the nocache.js file, and in all
+   * JavaScript generated by the GWT compiler.)
+   * Only available for completed, successful compiles.
+   */
+  public String getOutputModuleName() {
+    return outputModuleName;
+  }
+
+  /**
    * Returns the directory where the GWT module is being compiled, or null if not available.
    * (Not available for jobs that are WAITING.)
    */
@@ -208,6 +220,7 @@
     private CompileStrategy compileStrategy;
     private List<String> args = ImmutableList.of();
     private List<String> tags = ImmutableList.of();
+    private String outputModuleName;
 
     /**
      * A unique id for this job. Required.
@@ -260,6 +273,13 @@
     }
 
     /**
+     * The module name that the compiler returned (after rename).
+     */
+    public void setOutputModuleName(String name) {
+      this.outputModuleName = name;
+    }
+
+    /**
      * The directory where the GWT compiler will write its output.
      * Optional. (Not available until the compile starts.)
      */
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
index 6527445..8e472c9 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
@@ -26,7 +26,6 @@
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
@@ -44,7 +43,7 @@
   /**
    * The suffix that the GWT compiler uses when writing a sourcemap file.
    */
-  private static final String SOURCEMAP_FILE_SUFFIX = "_sourceMap0.json";
+  static final String SOURCEMAP_FILE_SUFFIX = "_sourceMap0.json";
 
   private final String id;
   private final Recompiler recompiler;
@@ -180,28 +179,22 @@
    * @throws RuntimeException if unable
    */
   File findSourceMapForOnePermutation() {
-    File dir = findSymbolMapDir();
+    String moduleName = recompiler.getOutputModuleName();
 
-    File[] sourceMapFiles = dir.listFiles(new FilenameFilter() {
-      @Override
-      public boolean accept(File dir, String name) {
-        return name.endsWith(SOURCEMAP_FILE_SUFFIX);
-      }
-    });
-
+    List<File> sourceMapFiles = getOutputDir().findSourceMapFiles(moduleName);
     if (sourceMapFiles == null) {
-      throw new RuntimeException("Can't list contents of symbol map directory: " + dir);
+      throw new RuntimeException("Can't find sourcemap files.");
     }
 
-    if (sourceMapFiles.length > 1) {
+    if (sourceMapFiles.size() > 1) {
       throw new RuntimeException("Multiple fragment 0 sourcemaps found. Too many permutations.");
     }
 
-    if (sourceMapFiles.length == 0) {
+    if (sourceMapFiles.isEmpty()) {
       throw new RuntimeException("No sourcemaps found. Not enabled?");
     }
 
-    return new File(dir, sourceMapFiles[0].getName());
+    return sourceMapFiles.get(0);
   }
 
   /**
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
index 85179a2..38dd1c4 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
@@ -122,10 +122,10 @@
     } catch (UnableToCompleteException e) {
       // No point in logging a stack trace for this exception
       job.getLogger().log(TreeLogger.Type.WARN, "recompile failed");
-      result = new Result(null, e);
+      result = new Result(null, null, e);
     } catch (Throwable error) {
       job.getLogger().log(TreeLogger.Type.WARN, "recompile failed", error);
-      result = new Result(null, error);
+      result = new Result(null, null, error);
     }
 
     job.onFinished(result);
@@ -171,7 +171,7 @@
     compileLogger.log(TreeLogger.Type.INFO,
         String.format("%.3fs total -- Compile completed", elapsedTime / 1000d));
 
-    return new Result(publishedCompileDir, null);
+    return new Result(publishedCompileDir, outputModuleName.get(), null);
   }
 
   /**
@@ -208,7 +208,7 @@
     long elapsedTime = System.currentTimeMillis() - startTime;
     compileLogger.log(TreeLogger.Type.INFO, "Module setup completed in " + elapsedTime + " ms");
 
-    return new Result(compileDir, null);
+    return new Result(compileDir, module.getName(), null);
   }
 
   /**