Super Dev Mode: remove -Xincremental and add new JobEvent properties

Removed -Xincremental which was the old incremental mode.
(We are going to use -XcompilePerFile in Super Dev Mode.)

New Job Event properties:
  - getArguments() returns the command-line arguments.
  - getCompileStrategy() returns an enum indicating if the compile
was full, incremental, or skipped.

Change-Id: I0606db5374faab778a46f3a90e6904f00917e6ce
Review-Link: https://gwt-review.googlesource.com/#/c/9100/
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 845215e..b94c0c5 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
@@ -18,8 +18,10 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.TreeLogger.Type;
 import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.codeserver.JobEvent.CompileStrategy;
 import com.google.gwt.dev.codeserver.JobEvent.Status;
 import com.google.gwt.thirdparty.guava.common.base.Preconditions;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
 import com.google.gwt.thirdparty.guava.common.collect.ImmutableSortedMap;
 import com.google.gwt.thirdparty.guava.common.util.concurrent.Futures;
 import com.google.gwt.thirdparty.guava.common.util.concurrent.ListenableFuture;
@@ -67,12 +69,15 @@
 
   // Miscellaneous
 
+  private final ImmutableList<String> args;
+
   /**
    * The id to report to the recompile listener.
    */
   private int compileId = -1; // non-negative after the compile has started
 
   private CompileDir compileDir; // non-null after the compile has started
+  private CompileStrategy compileStrategy; // non-null after the compile has started
 
   private Exception listenerFailure;
 
@@ -83,16 +88,16 @@
    * @param parentLogger  The parent of the logger that will be used for this job.
    */
   Job(Outbox box, Map<String, String> bindingProperties,
-      TreeLogger parentLogger, RecompileListener recompileListener,
-      JobChangeListener jobChangeListener) {
+      TreeLogger parentLogger, Options options) {
     this.id = chooseNextId(box);
     this.outbox = box;
     this.inputModuleName = box.getInputModuleName();
     // TODO: we will use the binding properties to find or create the outbox,
     // then take binding properties from the outbox here.
     this.bindingProperties = ImmutableSortedMap.copyOf(bindingProperties);
-    this.recompileListener = Preconditions.checkNotNull(recompileListener);
-    this.jobChangeListener = Preconditions.checkNotNull(jobChangeListener);
+    this.recompileListener = Preconditions.checkNotNull(options.getRecompileListener());
+    this.jobChangeListener = Preconditions.checkNotNull(options.getJobChangeListener());
+    this.args = Preconditions.checkNotNull(options.getArgs());
     this.logSupplier = new LogSupplier(parentLogger, id);
   }
 
@@ -214,12 +219,20 @@
    * @throws IllegalStateException if the job is not running.
    */
   synchronized void onProgress(String stepMessage) {
-    if (table == null || table.getPublishedEvent(this).getStatus() != Status.COMPILING) {
-      throw new IllegalStateException("onProgress called for a job that isn't compiling: " + id);
-    }
+    checkIsCompiling("onProgress");
     publish(makeEvent(Status.COMPILING, stepMessage));
   }
 
+  synchronized void setCompileStrategy(CompileStrategy strategy) {
+    checkIsCompiling("setCompileStrategy");
+    if (compileStrategy != null) {
+      throw new IllegalStateException("setCompileStrategy can only be set once per job");
+    }
+    this.compileStrategy = strategy;
+    // Not bothering to send an event just for this change, so it will be included
+    // in the next event.
+  }
+
   /**
    * Reports that this job has finished.
    * @throws IllegalStateException if the job is not running.
@@ -269,6 +282,8 @@
     out.setStatus(status);
     out.setMessage(message);
     out.setCompileDir(compileDir);
+    out.setCompileStrategy(compileStrategy);
+    out.setArguments(args);
     return out.build();
   }
 
@@ -287,6 +302,12 @@
     table.publish(event, getLogger());
   }
 
+  private void checkIsCompiling(String methodName) {
+    if (table == null || table.getPublishedEvent(this).getStatus() != Status.COMPILING) {
+      throw new IllegalStateException(methodName + " called for a job that isn't compiling: " + id);
+    }
+  }
+
   /**
    * Creates a child logger on first use.
    */
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 915b248..ee26865 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEvent.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEvent.java
@@ -18,9 +18,11 @@
 import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.dev.cfg.ModuleDefSchema;
 import com.google.gwt.thirdparty.guava.common.base.Preconditions;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
 import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
 import com.google.gwt.thirdparty.guava.common.collect.ImmutableSortedMap;
 
+import java.util.List;
 import java.util.Map;
 import java.util.SortedMap;
 
@@ -39,6 +41,8 @@
   private final String message;
 
   private final CompileDir compileDir;
+  private final CompileStrategy compileStrategy;
+  private final ImmutableList<String> arguments;
 
   private JobEvent(Builder builder) {
     this.jobId = Preconditions.checkNotNull(builder.jobId);
@@ -49,6 +53,8 @@
 
     // The following fields may be null.
     this.compileDir = builder.compileDir;
+    this.compileStrategy = builder.compileStrategy;
+    this.arguments = ImmutableList.copyOf(builder.args);
 
     // Any new fields added should allow nulls for backward compatibility.
   }
@@ -99,9 +105,24 @@
   }
 
   /**
+   * Returns the strategy used to perform the compile or null if not available.
+   * (Normally available for finished compiles.)
+   */
+  public CompileStrategy getCompileStrategy() {
+    return compileStrategy;
+  }
+
+  /**
+   * The arguments passed to Super Dev Mode at startup, or null if not available.
+   */
+  public ImmutableList<String> getArguments() {
+    return arguments;
+  }
+
+  /**
    * Defines the lifecycle of a job.
    */
-  public static enum Status {
+  public enum Status {
     WAITING("waiting", "Waiting for the compiler to start"),
     COMPILING("compiling", "Compiling"),
     SERVING("serving", "Compiled output is ready"),
@@ -118,6 +139,28 @@
   }
 
   /**
+   * The approach taken to do the compile.
+   */
+  public enum CompileStrategy {
+    FULL("full"), // Compiled all the source.
+    INCREMENTAL("incremental"), // Only recompiled the source files that changed.
+    SKIPPED("skipped"); // Did not compile anything since nothing changed
+
+    final String jsonName;
+
+    CompileStrategy(String jsonName) {
+      this.jsonName = jsonName;
+    }
+
+    /**
+     * The string to use for serialization.
+     */
+    String getJsonName() {
+      return jsonName;
+    }
+  }
+
+  /**
    * Creates a JobEvent.
    * This is public to allow external tests of code that implements {@link JobChangeListener}.
    * Normally all JobEvents are created in the code server.
@@ -130,6 +173,8 @@
     private Status status;
     private String message;
     private CompileDir compileDir;
+    private CompileStrategy compileStrategy;
+    private List<String> args = ImmutableList.of();
 
     /**
      * A unique id for this job. Required.
@@ -189,6 +234,22 @@
       this.compileDir = compileDir;
     }
 
+    /**
+     * The strategy used to perform the compile.
+     * Optional.
+     */
+    public void setCompileStrategy(CompileStrategy compileStrategy) {
+      this.compileStrategy = compileStrategy;
+    }
+
+    /**
+     * The arguments passed to {@link Options#parseArgs} at startup.
+     * Optional but may not be null. If not set, defaults to the empty list.
+     */
+    public void setArguments(List<String> args) {
+      this.args = Preconditions.checkNotNull(args);
+    }
+
     public JobEvent build() {
       return new JobEvent(this);
     }
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java
index 3d4f6c5..b7454d1 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java
@@ -27,6 +27,7 @@
 import com.google.gwt.dev.util.arg.OptionLogLevel;
 import com.google.gwt.dev.util.arg.OptionSourceLevel;
 import com.google.gwt.dev.util.arg.SourceLevel;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
 import com.google.gwt.util.tools.ArgHandler;
 import com.google.gwt.util.tools.ArgHandlerDir;
 import com.google.gwt.util.tools.ArgHandlerExtra;
@@ -38,6 +39,7 @@
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -46,7 +48,8 @@
  * <p>These flags are EXPERIMENTAL and subject to change.</p>
  */
 public class Options {
-  private boolean compileIncremental = false;
+  private ImmutableList<String> args;
+
   private boolean compilePerFile = false;
   private boolean noPrecompile = false;
   private boolean isCompileTest = false;
@@ -75,6 +78,11 @@
    * @return true if the arguments were parsed successfully.
    */
   public boolean parseArgs(String[] args) {
+    if (this.args != null) {
+      throw new IllegalStateException("parseArgs may only be called once");
+    }
+    this.args = ImmutableList.copyOf(Arrays.asList(args));
+
     boolean ok = new ArgProcessor().processArgs(args);
     if (!ok) {
       return false;
@@ -85,11 +93,6 @@
       return false;
     }
 
-    if (compilePerFile && compileIncremental) {
-      System.err.println("Usage: -XcompilePerFile and -Xincremental are incompatible");
-      return false;
-    }
-
     if (moduleNames.isEmpty()) {
       System.err.println("Usage: at least one module must be supplied");
       return false;
@@ -104,14 +107,21 @@
   }
 
   /**
+   * Returns the arguments passed to {@link #parseArgs}.
+   */
+  ImmutableList<String> getArgs() {
+    return args;
+  }
+
+  /**
    * A Java application that embeds Super Dev Mode can use this hook to find out
    * when compiles start and end.
    *
    * @deprecated replaced by {@link #setJobChangeListener}
    */
   @Deprecated
-  public void setRecompileListener(RecompileListener recompileListener) {
-    this.recompileListener = recompileListener;
+  public void setRecompileListener(RecompileListener listener) {
+    this.recompileListener = listener == null ? RecompileListener.NONE : listener;
   }
 
   RecompileListener getRecompileListener() {
@@ -124,8 +134,8 @@
    *
    * <p>Replaces {@link #setRecompileListener}
    */
-  public void setJobChangeListener(JobChangeListener jobChangeListener) {
-    this.jobChangeListener = jobChangeListener;
+  public void setJobChangeListener(JobChangeListener listener) {
+    this.jobChangeListener = listener == null ? JobChangeListener.NONE : listener;
   }
 
   JobChangeListener getJobChangeListener() {
@@ -154,13 +164,6 @@
   }
 
   /**
-   * Whether to compile a series of reusable libraries that are linked at the end.
-   */
-  boolean shouldCompileIncremental() {
-    return compileIncremental;
-  }
-
-  /**
    * Compiles faster by creating a JavaScript file per class. Can't be turned on at the same time as
    * shouldCompileIncremental().
    */
@@ -258,7 +261,6 @@
       registerHandler(new ModuleNameArgument());
       registerHandler(new FailOnErrorFlag());
       registerHandler(new StrictResourcesFlag());
-      registerHandler(new CompileIncrementalFlag());
       registerHandler(new CompilePerFileFlag());
       registerHandler(new ArgHandlerSourceLevel(new OptionSourceLevel() {
         @Override
@@ -354,35 +356,6 @@
     }
   }
 
-  private class CompileIncrementalFlag extends ArgHandlerFlag {
-
-    @Override
-    public String getLabel() {
-      return "incremental";
-    }
-
-    @Override
-    public String getPurposeSnippet() {
-      return "Compile and link the application as a set of separate libraries.";
-    }
-
-    @Override
-    public boolean setFlag(boolean value) {
-      compileIncremental = value;
-      return true;
-    }
-
-    @Override
-    public boolean getDefaultValue() {
-      return false;
-    }
-
-    @Override
-    public boolean isExperimental() {
-      return true;
-    }
-  }
-
   private class CompileTestFlag extends ArgHandlerFlag {
 
     @Override
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 be501e6..08171ca 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
@@ -111,8 +111,7 @@
    * Creates a Job whose output will be saved in this outbox.
    */
   Job makeJob(Map<String, String> bindingProperties, TreeLogger parentLogger) {
-    return new Job(this, bindingProperties, parentLogger,
-        options.getRecompileListener(), options.getJobChangeListener());
+    return new Job(this, bindingProperties, parentLogger, options);
   }
 
   /**
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 789fb81..14f547e 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
@@ -24,8 +24,6 @@
 import com.google.gwt.dev.Compiler;
 import com.google.gwt.dev.CompilerContext;
 import com.google.gwt.dev.CompilerOptions;
-import com.google.gwt.dev.IncrementalBuilder;
-import com.google.gwt.dev.IncrementalBuilder.BuildResultStatus;
 import com.google.gwt.dev.MinimalRebuildCache;
 import com.google.gwt.dev.NullRebuildCache;
 import com.google.gwt.dev.cfg.BindingProperty;
@@ -36,6 +34,7 @@
 import com.google.gwt.dev.cfg.ResourceLoader;
 import com.google.gwt.dev.cfg.ResourceLoaders;
 import com.google.gwt.dev.codeserver.Job.Result;
+import com.google.gwt.dev.codeserver.JobEvent.CompileStrategy;
 import com.google.gwt.dev.javac.UnitCacheSingleton;
 import com.google.gwt.dev.resource.impl.ResourceOracleImpl;
 import com.google.gwt.dev.resource.impl.ZipFileClassPathEntry;
@@ -59,7 +58,6 @@
   private final AppSpace appSpace;
   private final String inputModuleName;
 
-  private IncrementalBuilder incrementalBuilder;
   private String serverPrefix;
   private int compilesDone = 0;
   private Map<Map<String, String>, MinimalRebuildCache> minimalRebuildCacheForProperties =
@@ -155,14 +153,7 @@
 
     job.onStarted(compileId, compileDir);
 
-    boolean success;
-    if (options.shouldCompileIncremental()) {
-      job.onProgress("Compiling (incrementally)");
-      success = compileIncremental(compileLogger, compileDir);
-    } else {
-      success = compileMonolithic(compileLogger, compileDir, job);
-    }
-
+    boolean success = doCompile(compileLogger, compileDir, job);
     if (!success) {
       compileLogger.log(TreeLogger.Type.ERROR, "Compiler returned false");
       throw new UnableToCompleteException();
@@ -224,40 +215,7 @@
     return new Result(compileDir, null);
   }
 
-  private boolean compileIncremental(TreeLogger compileLogger, CompileDir compileDir) {
-    BuildResultStatus buildResultStatus;
-    // Perform a compile.
-    if (incrementalBuilder == null) {
-      // If it's the first compile.
-      ResourceLoader resources = ResourceLoaders.forClassLoader(Thread.currentThread());
-      resources = ResourceLoaders.forPathAndFallback(options.getSourcePath(), resources);
-      this.resourceLoader.set(resources);
-
-      incrementalBuilder = new IncrementalBuilder(inputModuleName,
-          compileDir.getWarDir().getPath(), compileDir.getWorkDir().getPath(),
-          compileDir.getGenDir().getPath(), resourceLoader.get());
-      buildResultStatus = incrementalBuilder.build(compileLogger);
-    } else {
-      // If it's a rebuild.
-      incrementalBuilder.setWarDir(compileDir.getWarDir().getPath());
-      buildResultStatus = incrementalBuilder.rebuild(compileLogger);
-    }
-
-    if (incrementalBuilder.isRootModuleKnown()) {
-      outputModuleName.set(incrementalBuilder.getRootModuleName());
-    }
-    // Unlike a monolithic compile, the incremental builder can successfully build but have no new
-    // output (for example when no files have changed). So it's important to only publish the new
-    // compileDir if it actually contains output.
-    if (buildResultStatus.isSuccess() && buildResultStatus.outputChanged()) {
-      publishedCompileDir = compileDir;
-    }
-    lastBuild.set(compileDir); // makes compile log available over HTTP
-
-    return buildResultStatus.isSuccess();
-  }
-
-  private boolean compileMonolithic(TreeLogger compileLogger, CompileDir compileDir, Job job)
+  private boolean doCompile(TreeLogger compileLogger, CompileDir compileDir, Job job)
       throws UnableToCompleteException {
 
     job.onProgress("Loading modules");
@@ -277,9 +235,11 @@
     InputSummary input = new InputSummary(bindingProperties, module);
     if (input.equals(lastBuildInput)) {
       compileLogger.log(Type.INFO, "skipped compile because no input files have changed");
+      job.setCompileStrategy(CompileStrategy.SKIPPED);
       return true;
     }
 
+
     job.onProgress("Compiling");
     // TODO: use speed tracer to get more compiler events?
 
@@ -288,6 +248,10 @@
 
     // Looks up the matching rebuild cache using the final set of overridden binding properties.
     MinimalRebuildCache minimalRebuildCache = getOrCreateMinimalRebuildCache(bindingProperties);
+
+    job.setCompileStrategy(minimalRebuildCache.isPopulated() ? CompileStrategy.INCREMENTAL :
+        CompileStrategy.FULL);
+
     boolean success = new Compiler(runOptions, minimalRebuildCache).run(compileLogger, module);
     if (success) {
       publishedCompileDir = compileDir;
diff --git a/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java b/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
index 7a2bad8..21c5da0 100644
--- a/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
+++ b/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
@@ -373,6 +373,13 @@
     return immediateTypeRelations;
   }
 
+  /**
+   * Returns true if there is cached data to reuse in the next recompile.
+   */
+  public boolean isPopulated() {
+    return !immediateTypeRelations.isEmpty();
+  }
+
   public IntTypeIdGenerator getIntTypeIdGenerator() {
     return intTypeIdGenerator;
   }