Modifies the GWT compiler sharded entry points to support an alternative
method of sharding where multiple instances of Precompile and CompilePerms
are invoked.

Review at http://gwt-code-reviews.appspot.com/1074801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9206 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/AnalyzeModule.java b/dev/core/src/com/google/gwt/dev/AnalyzeModule.java
index 6b7886c..ae1a249 100644
--- a/dev/core/src/com/google/gwt/dev/AnalyzeModule.java
+++ b/dev/core/src/com/google/gwt/dev/AnalyzeModule.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 Google Inc.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -18,11 +18,11 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.CompileTaskRunner.CompileTask;
+import com.google.gwt.dev.Precompile.PrecompileOptions;
 import com.google.gwt.dev.Precompile.PrecompileOptionsImpl;
 import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.dev.cfg.ModuleDefLoader;
 import com.google.gwt.dev.cfg.PropertyPermutations;
-import com.google.gwt.dev.jjs.JJSOptions;
 import com.google.gwt.dev.util.Memory;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
@@ -30,6 +30,7 @@
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
 
 import java.io.File;
+import java.io.IOException;
 
 /**
  * Performs the first phase of compilation, generating the set of permutations
@@ -37,27 +38,13 @@
  */
 public class AnalyzeModule {
 
-  private interface AnalyzeModuleOptions extends JJSOptions, CompileTaskOptions {
-  }
-
-  static class ArgProcessor extends CompileArgProcessor {
-    public ArgProcessor(AnalyzeModuleOptions options) {
-      super(options);
-    }
-
-    @Override
-    protected String getName() {
-      return AnalyzeModule.class.getName();
-    }
-  }
-
   /**
-   * Mirror all the compiler options, in case this makes some
-   * difference in module analysis.
+   * Mirror all the compiler options, in case this makes some difference in
+   * module analysis.
    */
-  static class AnalyzeModuleOptionsImpl extends PrecompileOptionsImpl
-      implements AnalyzeModuleOptions {
-    private final PrecompileOptionsImpl precompileOptions = new PrecompileOptionsImpl();
+  @SuppressWarnings("serial")
+  static class AnalyzeModuleOptionsImpl extends PrecompileOptionsImpl implements
+      AnalyzeModuleOptions {
 
     public AnalyzeModuleOptionsImpl() {
     }
@@ -68,13 +55,40 @@
 
     public void copyFrom(AnalyzeModuleOptions other) {
       super.copyFrom(other);
-      precompileOptions.copyFrom(other);
     }
   }
 
+  static class ArgProcessor extends Precompile.ArgProcessor {
+    public ArgProcessor(AnalyzeModuleOptions options) {
+      super(options);
+    }
+
+    @Override
+    protected String getName() {
+      return AnalyzeModule.class.getName();
+    }
+  }
+
+  private interface AnalyzeModuleOptions extends Precompile.PrecompileOptions {
+    // This interface is here to support future options.
+  }
+
   /**
-   * Performs a command-line analysis of the module with output to files
-   * for use in further sharded build steps.
+   * The options passed to the AnalyzeModule step are saved here and passed
+   * through to future steps.
+   */
+  static final String OPTIONS_FILENAME = "compilerOptions.ser";
+
+  /**
+   * Count of the maximum number of permutations in the module configuration.
+   * Used to communicate the number of permutations defined in a module to an
+   * external build tool.
+   */
+  static final String PERM_COUNT_FILENAME = "permCount.txt";
+
+  /**
+   * Performs a command-line analysis of the module with output to files for use
+   * in further sharded build steps.
    */
   public static void main(String[] args) {
     Memory.initialize();
@@ -92,6 +106,29 @@
     analyzeModuleEvent.end();
   }
 
+  /**
+   * Loads the AnalyzeModule.OPTIONS_FILENAME data.
+   *
+   * Silently returns <code>null</code> if the file is not found or another problem is
+   * encountered reading the file.
+   */
+  public static PrecompileOptions readAnalyzeModuleOptionsFile(
+      TreeLogger logger, File compilerWorkDir) {
+    File optionsFile = new File(compilerWorkDir, AnalyzeModule.OPTIONS_FILENAME);
+    PrecompileOptions precompilationOptions = null;
+    try {
+      precompilationOptions = Util.readFileAsObject(optionsFile,
+          PrecompileOptions.class);
+    } catch (IOException e) {
+      logger.log(TreeLogger.DEBUG, "Failed to read " + optionsFile
+          + "\nHas AnalyzeModule been run?  Falling back.", e);
+      return null;
+    } catch (ClassNotFoundException e) {
+      logger.log(TreeLogger.ERROR, "Failed to read " + optionsFile, e);
+      return null;
+    }
+    return precompilationOptions;
+  }
 
   private final AnalyzeModuleOptionsImpl options;
 
@@ -100,31 +137,31 @@
   }
 
   public boolean run(TreeLogger logger) throws UnableToCompleteException {
-
     for (String moduleName : options.getModuleNames()) {
       File compilerWorkDir = options.getCompilerWorkDir(moduleName);
       Util.recursiveDelete(compilerWorkDir, true);
       // No need to check mkdirs result because an IOException will occur anyway
       compilerWorkDir.mkdirs();
-     
+
       ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName);
-      
       logger.log(TreeLogger.INFO, "Analyzing module " + module.getName());
 
       /*
-       * Count the permutations to expose to external build tools performing
-       * a sharded compile.
+       * Count the permutations to expose to external build tools performing a
+       * sharded compile.
        */
-      int numPermutations =
-          new PropertyPermutations(module.getProperties(), module.getActiveLinkerNames())
-              .collapseProperties().size();
-      Util.writeStringAsFile(logger, new File(compilerWorkDir, Precompile.PERM_COUNT_FILENAME),
-          String.valueOf(numPermutations));
-      
-      // TODO(zundel): Serializing the ModuleDef structure would save time in subsequent steps.
-      
-      // TODO(zundel): Building the initial type oracle in this step would save cputime when
-      //               the precompile step is sharded.
+      int numPermutations = new PropertyPermutations(module.getProperties(),
+          module.getActiveLinkerNames()).collapseProperties().size();
+      Util.writeStringAsFile(logger, new File(compilerWorkDir,
+          AnalyzeModule.PERM_COUNT_FILENAME), String.valueOf(numPermutations));
+      Util.writeObjectAsFile(logger, new File(compilerWorkDir,
+          AnalyzeModule.OPTIONS_FILENAME), options);
+
+      // TODO(zundel): Serializing the ModuleDef structure would save time in
+      // subsequent steps.
+
+      // TODO(zundel): Building the initial type oracle in this step would save
+      // cputime when the precompile step is sharded.
     }
 
     return true;
diff --git a/dev/core/src/com/google/gwt/dev/CompileOnePerm.java b/dev/core/src/com/google/gwt/dev/CompileOnePerm.java
new file mode 100644
index 0000000..ddfbddd
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/CompileOnePerm.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.CompileTaskRunner.CompileTask;
+import com.google.gwt.dev.Precompile.PrecompileOptions;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.jjs.PermutationResult;
+import com.google.gwt.dev.util.PerfCounter;
+import com.google.gwt.dev.util.arg.ArgHandlerPerm;
+import com.google.gwt.dev.util.arg.OptionPerm;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Performs the second phase of compilation, converting one of the Precompile's
+ * serialized AST files into JavaScript outputs.
+ */
+public class CompileOnePerm {
+
+  /**
+   * Options for CompilePerm.
+   */
+  public interface CompileOnePermOptions extends CompileTaskOptions, OptionPerm {
+  }
+
+  static class ArgProcessor extends CompileArgProcessor {
+    public ArgProcessor(CompileOnePermOptions options) {
+      super(options);
+      registerHandler(new ArgHandlerPerm(options));
+    }
+
+    @Override
+    protected String getName() {
+      return CompileOnePerm.class.getName();
+    }
+  }
+
+  /**
+   * Concrete class to implement compiler perm options.
+   */
+  static class CompileOnePermOptionsImpl extends CompileTaskOptionsImpl
+      implements CompileOnePermOptions {
+
+    private int permToCompile = -1;
+
+    public CompileOnePermOptionsImpl() {
+    }
+
+    public CompileOnePermOptionsImpl(CompileOnePermOptions other) {
+      copyFrom(other);
+    }
+
+    public void copyFrom(CompileOnePermOptions other) {
+      super.copyFrom(other);
+      setPermToCompile(other.getPermToCompile());
+    }
+
+    public int getPermToCompile() {
+      return permToCompile;
+    }
+
+    public void setPermToCompile(int permToCompile) {
+      this.permToCompile = permToCompile;
+    }
+  }
+
+  public static void main(String[] args) {
+    int exitCode = -1;
+    /*
+     * NOTE: main always exits with a call to System.exit to terminate any
+     * non-daemon threads that were started in Generators. Typically, this is to
+     * shutdown AWT related threads, since the contract for their termination is
+     * still implementation-dependent.
+     */
+    final CompileOnePermOptions options = new CompileOnePermOptionsImpl();
+    if (new ArgProcessor(options).processArgs(args)) {
+      CompileTask task = new CompileTask() {
+        public boolean run(TreeLogger logger) throws UnableToCompleteException {
+          return new CompileOnePerm(options).run(logger);
+        }
+      };
+      if (CompileTaskRunner.runWithAppropriateLogger(options, task)) {
+        // Exit w/ success code.
+        exitCode = 0;
+      }
+    }
+    PerfCounter.print();
+    System.exit(exitCode);
+  }
+
+  /**
+   * Return the filename corresponding to the given permutation number,
+   * one-based.
+   */
+  static File makePermFilename(File compilerWorkDir, int permNumber) {
+    return new File(compilerWorkDir, "permutation-" + permNumber + ".js");
+  }
+
+  /**
+   * Run Compile where precompilation occurred previously on sharded steps of
+   * Precompile. There will be a different serialized AST file named
+   * permutation-*.ser, one per permutation.
+   *
+   * @param logger tree logger
+   * @param moduleName Name of the GWT module with the entry point
+   * @param permId permutations to compile
+   * @param compilerWorkDir directory where work files are stored
+   *
+   * @return <code>true</code> if compilation succeeds
+   */
+  private static boolean compileSpecificPermutation(TreeLogger logger,
+      String moduleName, PrecompileOptions precompilationOptions, int permId,
+      File compilerWorkDir) throws UnableToCompleteException {
+
+    ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName);
+
+    logger = logger.branch(TreeLogger.INFO, "Compiling permutation " + permId);
+
+    File precompilationFile = new File(compilerWorkDir,
+        PrecompileOnePerm.getPrecompileFilename(permId));
+    Precompilation precompilation = (Precompilation) CompilePerms.readPrecompilationFile(
+        logger, precompilationFile);
+
+    // Choose the permutation that goes with this precompilation
+    Permutation[] subPerms = CompilePerms.selectPermutationsForPrecompilation(
+        new int[]{permId}, precompilation);
+    assert subPerms.length == 1;
+
+    PermutationResult permResult = precompilation.getUnifiedAst().compilePermutation(
+        logger, subPerms[0]);
+    Link.linkOnePermutationToJar(logger, module,
+        precompilation.getGeneratedArtifacts(), permResult, makePermFilename(
+            compilerWorkDir, permId), precompilationOptions);
+    return true;
+  }
+
+  private final CompileOnePermOptionsImpl options;
+
+  public CompileOnePerm(CompileOnePermOptions options) {
+    this.options = new CompileOnePermOptionsImpl(options);
+  }
+
+  public boolean run(TreeLogger logger) throws UnableToCompleteException {
+    List<String> moduleNames = options.getModuleNames();
+    if (moduleNames.size() != 1) {
+      logger.log(TreeLogger.ERROR, "Expected a single module name.");
+      return false;
+    }
+    int permToRun = options.getPermToCompile();
+    if (permToRun < 0) {
+      logger.log(TreeLogger.ERROR,
+          "Expected argument -perm to specify the permutation to compile.");
+      return false;
+    }
+
+    String moduleName = moduleNames.get(0);
+    File compilerWorkDir = options.getCompilerWorkDir(moduleName);
+
+    // Look for the sentinel file that indicates that this compilation already
+    // has a precompilation result from a previous Precompile step.
+    PrecompileOptions precompilationOptions = AnalyzeModule.readAnalyzeModuleOptionsFile(
+        logger, compilerWorkDir);
+    if (precompilationOptions == null) {
+      logger.log(TreeLogger.ERROR, "Could not read file "
+          + AnalyzeModule.OPTIONS_FILENAME + " output from AnalyzeModule step.");
+      return false;
+    }
+
+    return compileSpecificPermutation(logger, moduleName,
+        precompilationOptions, permToRun, compilerWorkDir);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index d1a7fa2..176bad1 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -250,11 +250,27 @@
     return new File(compilerWorkDir, "permutation-" + permNumber + ".js");
   }
 
+  static PrecompilationResult readPrecompilationFile(TreeLogger logger,
+      File precompilationFile) {
+    PrecompilationResult precompileResults = null;
+    try {
+      precompileResults = Util.readFileAsObject(precompilationFile,
+          PrecompilationResult.class);
+    } catch (IOException e) {
+      logger.log(TreeLogger.ERROR, "Failed to read "
+          + precompilationFile + "\nHas Precompile been run?");
+    } catch (ClassNotFoundException e) {
+      logger.log(TreeLogger.ERROR, "Failed to read "
+          + precompilationFile, e);
+    }
+    return precompileResults;
+  }
+
   /**
    * Choose the subset of requested permutations that correspond to the
    * indicated precompilation.
    */
-  private static Permutation[] selectPermutationsForPrecompilation(
+  static Permutation[] selectPermutationsForPrecompilation(
       int[] permsToRun, Precompilation precompilation) {
     if (permsToRun == null) {
       // Special case: compile everything.
@@ -288,19 +304,8 @@
       File precompilationFile = new File(compilerWorkDir,
           Precompile.PRECOMPILE_FILENAME);
 
-      PrecompilationResult precompileResults;
-      try {
-        precompileResults = Util.readFileAsObject(precompilationFile,
-            PrecompilationResult.class);
-      } catch (IOException e) {
-        logger.log(TreeLogger.ERROR, "Failed to read "
-            + Precompile.PRECOMPILE_FILENAME + "; has Precompile been run?");
-        return false;
-      } catch (ClassNotFoundException e) {
-        logger.log(TreeLogger.ERROR, "Failed to read "
-            + Precompile.PRECOMPILE_FILENAME, e);
-        return false;
-      }
+      PrecompilationResult precompileResults = readPrecompilationFile(logger, 
+          precompilationFile);
 
       if (precompileResults instanceof PrecompileOptions) {
         PrecompileOptions precompilationOptions = (PrecompileOptions) precompileResults;
diff --git a/dev/core/src/com/google/gwt/dev/DistillerRebindPermutationOracle.java b/dev/core/src/com/google/gwt/dev/DistillerRebindPermutationOracle.java
new file mode 100644
index 0000000..babb988
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/DistillerRebindPermutationOracle.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.cfg.BindingProperty;
+import com.google.gwt.dev.cfg.ConfigurationProperty;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.PropertyPermutations;
+import com.google.gwt.dev.cfg.Rules;
+import com.google.gwt.dev.cfg.StaticPropertyOracle;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.javac.StandardGeneratorContext;
+import com.google.gwt.dev.jdt.RebindOracle;
+import com.google.gwt.dev.jdt.RebindPermutationOracle;
+import com.google.gwt.dev.shell.StandardRebindOracle;
+import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.collect.HashSet;
+import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+
+import java.io.File;
+import java.util.Set;
+import java.util.SortedSet;
+
+/**
+ * Implementation of RebindPermutationOracle used by Precompile.
+ */
+class DistillerRebindPermutationOracle implements RebindPermutationOracle {
+  private CompilationState compilationState;
+  private StandardGeneratorContext generatorContext;
+  private final Permutation[] permutations;
+  private final StaticPropertyOracle[] propertyOracles;
+  private final RebindOracle[] rebindOracles;
+
+  public DistillerRebindPermutationOracle(ModuleDef module,
+      CompilationState compilationState, ArtifactSet generatorArtifacts,
+      PropertyPermutations perms, File genDir) {
+    this.compilationState = compilationState;
+    permutations = new Permutation[perms.size()];
+    propertyOracles = new StaticPropertyOracle[perms.size()];
+    rebindOracles = new RebindOracle[perms.size()];
+    generatorContext = new StandardGeneratorContext(compilationState, module,
+        genDir, generatorArtifacts);
+    BindingProperty[] orderedProps = perms.getOrderedProperties();
+    SortedSet<ConfigurationProperty> configPropSet = module.getProperties().getConfigurationProperties();
+    ConfigurationProperty[] configProps = configPropSet.toArray(new ConfigurationProperty[configPropSet.size()]);
+    Rules rules = module.getRules();
+    for (int i = 0; i < rebindOracles.length; ++i) {
+      String[] orderedPropValues = perms.getOrderedPropertyValues(i);
+      propertyOracles[i] = new StaticPropertyOracle(orderedProps,
+          orderedPropValues, configProps);
+      rebindOracles[i] = new StandardRebindOracle(propertyOracles[i], rules,
+          generatorContext);
+      permutations[i] = new Permutation(i, propertyOracles[i]);
+    }
+  }
+
+  public void clear() {
+    generatorContext.clear();
+    compilationState = null;
+    generatorContext = null;
+  }
+
+  public String[] getAllPossibleRebindAnswers(TreeLogger logger,
+      String requestTypeName) throws UnableToCompleteException {
+
+    String msg = "Computing all possible rebind results for '"
+        + requestTypeName + "'";
+    logger = logger.branch(TreeLogger.DEBUG, msg, null);
+
+    Set<String> answers = new HashSet<String>();
+    Event getAllRebindsEvent = SpeedTracerLogger.start(CompilerEventType.GET_ALL_REBINDS);
+    for (int i = 0; i < getPermuationCount(); ++i) {
+      String resultTypeName = rebindOracles[i].rebind(logger, requestTypeName);
+      answers.add(resultTypeName);
+      // Record the correct answer into each permutation.
+      permutations[i].putRebindAnswer(requestTypeName, resultTypeName);
+    }
+    String[] result = Util.toArray(String.class, answers);
+    getAllRebindsEvent.end();
+    return result;
+  }
+
+  public CompilationState getCompilationState() {
+    return compilationState;
+  }
+
+  public StandardGeneratorContext getGeneratorContext() {
+    return generatorContext;
+  }
+
+  public int getPermuationCount() {
+    return rebindOracles.length;
+  }
+
+  public Permutation[] getPermutations() {
+    return permutations;
+  }
+
+  public StaticPropertyOracle getPropertyOracle(int permNumber) {
+    return propertyOracles[permNumber];
+  }
+
+  public RebindOracle getRebindOracle(int permNumber) {
+    return rebindOracles[permNumber];
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/Link.java b/dev/core/src/com/google/gwt/dev/Link.java
index 9c46fc3..261f522 100644
--- a/dev/core/src/com/google/gwt/dev/Link.java
+++ b/dev/core/src/com/google/gwt/dev/Link.java
@@ -67,6 +67,7 @@
 import java.util.jar.JarFile;
 import java.util.jar.JarOutputStream;
 import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
 
 /**
  * Performs the last phase of compilation, merging the compilation outputs.
@@ -199,8 +200,7 @@
     if (deployDir.equals(extrasDir)) {
       deployFileSet = extrasFileSet;
     } else {
-      deployFileSet = chooseOutputFileSet(deployDir,
-          module.getName() + "/");
+      deployFileSet = chooseOutputFileSet(deployDir, module.getName() + "/");
     }
     doProduceOutput(logger, artifacts, linkerContext, chooseOutputFileSet(
         outDir, module.getName() + "/"), deployFileSet, extrasFileSet);
@@ -220,7 +220,7 @@
       if (jarFile.exists()) {
         boolean success = jarFile.delete();
         if (!success) {
-          logger.log(Type.ERROR, "Linker output file " + jarFile.getName() 
+          logger.log(Type.ERROR, "Linker output file " + jarFile.getName()
               + " already exists and can't be deleted.");
         }
       }
@@ -523,8 +523,8 @@
    * @param linkerContext
    * @return prefixed path
    */
-  private static String prefixArtifactPath(
-      EmittedArtifact art, StandardLinkerContext linkerContext) {
+  private static String prefixArtifactPath(EmittedArtifact art,
+      StandardLinkerContext linkerContext) {
     String pathWithLinkerName = linkerContext.getExtraPathForLinker(
         art.getLinker(), art.getPartialPath());
     if (pathWithLinkerName.startsWith("/")) {
@@ -539,7 +539,15 @@
     final ArtifactSet artifacts = new ArtifactSet();
 
     for (File resultFile : resultFiles) {
-      JarFile jarFile = new JarFile(resultFile);
+      JarFile jarFile = null;
+      try {
+        jarFile = new JarFile(resultFile);
+      } catch (ZipException ze) {
+        logger.log(TreeLogger.ERROR, "Error opening " + resultFile
+            + " as jar file.", ze);
+        throw new UnableToCompleteException();
+      }
+
       Enumeration<JarEntry> entries = jarFile.entries();
       while (entries.hasMoreElements()) {
         JarEntry entry = entries.nextElement();
@@ -599,26 +607,32 @@
       ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName);
 
       File compilerWorkDir = options.getCompilerWorkDir(moduleName);
-      PrecompilationResult precompileResults;
-      try {
-        precompileResults = Util.readFileAsObject(new File(compilerWorkDir,
-            Precompile.PRECOMPILE_FILENAME), PrecompilationResult.class);
-      } catch (ClassNotFoundException e) {
-        logger.log(TreeLogger.ERROR, "Error reading "
-            + Precompile.PRECOMPILE_FILENAME);
-        return false;
-      } catch (IOException e) {
-        logger.log(TreeLogger.ERROR, "Error reading "
-            + Precompile.PRECOMPILE_FILENAME);
-        return false;
+
+      // Look for the compilerOptions file output from running AnalyzeModule
+      PrecompileOptions precompileOptions = AnalyzeModule.readAnalyzeModuleOptionsFile(
+          logger, compilerWorkDir);
+
+      PrecompilationResult precompileResults = null;
+      if (precompileOptions == null) {
+        // Check for the output from Precompile where precompiling has
+        // been delegated to shards.
+        File precompilationFile = new File(compilerWorkDir,
+            Precompile.PRECOMPILE_FILENAME);
+        precompileResults = CompilePerms.readPrecompilationFile(logger,
+            precompilationFile);
+        if (precompileResults == null) {
+          return false;
+        }
+        if (precompileResults instanceof PrecompileOptions) {
+          precompileOptions = (PrecompileOptions) precompileResults;
+        }
       }
 
-      if (precompileResults instanceof PrecompileOptions) {
+      if (precompileOptions != null) {
         /**
          * Precompiling happened on the shards.
          */
-        if (!doLinkFinal(logger, compilerWorkDir, module,
-            (JJSOptions) precompileResults)) {
+        if (!doLinkFinal(logger, compilerWorkDir, module, precompileOptions)) {
           return false;
         }
         continue loop_modules;
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index 52b1ea3..830e240 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -24,18 +24,12 @@
 import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.dev.CompileTaskRunner.CompileTask;
-import com.google.gwt.dev.cfg.BindingProperty;
 import com.google.gwt.dev.cfg.ConfigurationProperty;
 import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.dev.cfg.ModuleDefLoader;
 import com.google.gwt.dev.cfg.PropertyPermutations;
-import com.google.gwt.dev.cfg.Rules;
-import com.google.gwt.dev.cfg.StaticPropertyOracle;
 import com.google.gwt.dev.javac.CompilationState;
 import com.google.gwt.dev.javac.CompilationUnit;
-import com.google.gwt.dev.javac.StandardGeneratorContext;
-import com.google.gwt.dev.jdt.RebindOracle;
-import com.google.gwt.dev.jdt.RebindPermutationOracle;
 import com.google.gwt.dev.jjs.AbstractCompiler;
 import com.google.gwt.dev.jjs.JJSOptions;
 import com.google.gwt.dev.jjs.JJSOptionsImpl;
@@ -44,7 +38,6 @@
 import com.google.gwt.dev.jjs.UnifiedAst;
 import com.google.gwt.dev.shell.CheckForUpdates;
 import com.google.gwt.dev.shell.CheckForUpdates.UpdateResult;
-import com.google.gwt.dev.shell.StandardRebindOracle;
 import com.google.gwt.dev.util.CollapsedPropertyKey;
 import com.google.gwt.dev.util.Memory;
 import com.google.gwt.dev.util.Util;
@@ -72,7 +65,6 @@
 import com.google.gwt.dev.util.arg.OptionGenDir;
 import com.google.gwt.dev.util.arg.OptionMaxPermsPerPrecompile;
 import com.google.gwt.dev.util.arg.OptionValidateOnly;
-import com.google.gwt.dev.util.collect.HashSet;
 import com.google.gwt.dev.util.collect.Lists;
 import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
@@ -88,7 +80,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.SortedMap;
 import java.util.SortedSet;
 import java.util.TreeMap;
@@ -308,89 +299,6 @@
     }
   }
 
-  private static class DistillerRebindPermutationOracle implements
-      RebindPermutationOracle {
-
-    private CompilationState compilationState;
-    private StandardGeneratorContext generatorContext;
-    private final Permutation[] permutations;
-    private final StaticPropertyOracle[] propertyOracles;
-    private final RebindOracle[] rebindOracles;
-
-    public DistillerRebindPermutationOracle(ModuleDef module,
-        CompilationState compilationState, ArtifactSet generatorArtifacts,
-        PropertyPermutations perms, File genDir) {
-      this.compilationState = compilationState;
-      permutations = new Permutation[perms.size()];
-      propertyOracles = new StaticPropertyOracle[perms.size()];
-      rebindOracles = new RebindOracle[perms.size()];
-      generatorContext = new StandardGeneratorContext(compilationState, module,
-          genDir, generatorArtifacts);
-      BindingProperty[] orderedProps = perms.getOrderedProperties();
-      SortedSet<ConfigurationProperty> configPropSet = module.getProperties().getConfigurationProperties();
-      ConfigurationProperty[] configProps = configPropSet.toArray(new ConfigurationProperty[configPropSet.size()]);
-      Rules rules = module.getRules();
-      for (int i = 0; i < rebindOracles.length; ++i) {
-        String[] orderedPropValues = perms.getOrderedPropertyValues(i);
-        propertyOracles[i] = new StaticPropertyOracle(orderedProps,
-            orderedPropValues, configProps);
-        rebindOracles[i] = new StandardRebindOracle(propertyOracles[i], rules,
-            generatorContext);
-        permutations[i] = new Permutation(i, propertyOracles[i]);
-      }
-    }
-
-    public void clear() {
-      generatorContext.clear();
-      compilationState = null;
-      generatorContext = null;
-    }
-
-    public String[] getAllPossibleRebindAnswers(TreeLogger logger,
-        String requestTypeName) throws UnableToCompleteException {
-
-      String msg = "Computing all possible rebind results for '"
-          + requestTypeName + "'";
-      logger = logger.branch(TreeLogger.DEBUG, msg, null);
-
-      Set<String> answers = new HashSet<String>();
-      Event getAllRebindsEvent = SpeedTracerLogger.start(CompilerEventType.GET_ALL_REBINDS);
-      for (int i = 0; i < getPermuationCount(); ++i) {
-        String resultTypeName = rebindOracles[i].rebind(logger, requestTypeName);
-        answers.add(resultTypeName);
-        // Record the correct answer into each permutation.
-        permutations[i].putRebindAnswer(requestTypeName, resultTypeName);
-      }
-      String[] result = Util.toArray(String.class, answers);
-      getAllRebindsEvent.end();
-      return result;
-    }
-
-    public CompilationState getCompilationState() {
-      return compilationState;
-    }
-
-    public StandardGeneratorContext getGeneratorContext() {
-      return generatorContext;
-    }
-
-    public int getPermuationCount() {
-      return rebindOracles.length;
-    }
-
-    public Permutation[] getPermutations() {
-      return permutations;
-    }
-
-    public StaticPropertyOracle getPropertyOracle(int permNumber) {
-      return propertyOracles[permNumber];
-    }
-
-    public RebindOracle getRebindOracle(int permNumber) {
-      return rebindOracles[permNumber];
-    }
-  }
-
   /**
    * Creates a Graphics2D context in a thread in order to go ahead and get first
    * time initialization out of the way. Delays ranging from 200ms to 6s have
@@ -413,12 +321,23 @@
   };
 
   /**
-   * The file name for the result of Precompile.
+   * The file name for the max number of permutations output as plain text.
    */
-  public static final String PRECOMPILE_FILENAME = "precompilation.ser";
-
   static final String PERM_COUNT_FILENAME = "permCount.txt";
 
+  static final String PRECOMPILE_FILENAME = Precompile.PRECOMPILE_FILENAME_PREFIX
+      + Precompile.PRECOMPILE_FILENAME_SUFFIX;
+
+  /**
+   * The file name for the serialized AST artifact from the Precompile step.
+   * Sometimes this file is overloaded and only contains a PrecompileOptions
+   * object to indicate that precompilation should run inside the CompilePerms
+   * step.
+   */
+  static final String PRECOMPILE_FILENAME_PREFIX = "precompilation";
+
+  static final String PRECOMPILE_FILENAME_SUFFIX = ".ser";
+
   /**
    * Performs a command-line precompile.
    */
@@ -486,7 +405,7 @@
    * @param logger a logger to use
    * @param jjsOptions a set of compiler options
    * @param module the module to compile
-   * @param genDir optional directory to dump generated source, may be 
+   * @param genDir optional directory to dump generated source, may be
    *               <code>null</code>
    */
   public static boolean validate(TreeLogger logger, JJSOptions jjsOptions,
@@ -529,6 +448,39 @@
     }
   }
 
+  /**
+   * Create a list of all possible permutations configured for this module after
+   * collapsing soft permutations.
+   */
+  static List<PropertyPermutations> getCollapsedPermutations(ModuleDef module) {
+    PropertyPermutations allPermutations = new PropertyPermutations(
+        module.getProperties(), module.getActiveLinkerNames());
+    List<PropertyPermutations> collapsedPermutations = allPermutations.collapseProperties();
+    return collapsedPermutations;
+  }
+
+  static AbstractCompiler getCompiler(ModuleDef module) {
+    ConfigurationProperty compilerClassProp = module.getProperties().createConfiguration(
+        "x.compiler.class", false);
+    String compilerClassName = compilerClassProp.getValue();
+    if (compilerClassName == null || compilerClassName.length() == 0) {
+      return new JavaScriptCompiler();
+    }
+    Throwable caught;
+    try {
+      Class<?> compilerClass = Class.forName(compilerClassName);
+      return (AbstractCompiler) compilerClass.newInstance();
+    } catch (ClassNotFoundException e) {
+      caught = e;
+    } catch (InstantiationException e) {
+      caught = e;
+    } catch (IllegalAccessException e) {
+      caught = e;
+    }
+    throw new RuntimeException("Unable to instantiate compiler class '"
+        + compilerClassName + "'", caught);
+  }
+
   static Precompilation precompile(TreeLogger logger, JJSOptions jjsOptions,
       ModuleDef module, int permutationBase,
       PropertyPermutations allPermutations, File genDir) {
@@ -623,7 +575,7 @@
         precompilationMetrics.setPermuationIds(ids);
         // TODO(zundel): Right now this double counts module load and
         // precompile time. It correctly counts the amount of time spent
-        // in this process.  The elapsed time in ModuleMetricsArtifact
+        // in this process. The elapsed time in ModuleMetricsArtifact
         // represents time which could be done once for all permutations.
         precompilationMetrics.setElapsedMilliseconds(System.currentTimeMillis()
             - startTimeMilliseconds);
@@ -646,29 +598,6 @@
         "Aborting compile due to errors in some input files");
     throw new UnableToCompleteException();
   }
-
-  private static AbstractCompiler getCompiler(ModuleDef module) {
-    ConfigurationProperty compilerClassProp = module.getProperties().createConfiguration(
-        "x.compiler.class", false);
-    String compilerClassName = compilerClassProp.getValue();
-    if (compilerClassName == null || compilerClassName.length() == 0) {
-      return new JavaScriptCompiler();
-    }
-    Throwable caught;
-    try {
-      Class<?> compilerClass = Class.forName(compilerClassName);
-      return (AbstractCompiler) compilerClass.newInstance();
-    } catch (ClassNotFoundException e) {
-      caught = e;
-    } catch (InstantiationException e) {
-      caught = e;
-    } catch (IllegalAccessException e) {
-      caught = e;
-    }
-    throw new RuntimeException("Unable to instantiate compiler class '"
-        + compilerClassName + "'", caught);
-  }
-
   /**
    * This merges Permutations that can be considered equivalent by considering
    * their collapsed properties. The list passed into this method may have
@@ -716,7 +645,6 @@
       permutations.set(i, new Permutation(i, permutations.get(i)));
     }
   }
-
   private final PrecompileOptionsImpl options;
 
   public Precompile(PrecompileOptions options) {
@@ -731,7 +659,8 @@
     for (String moduleName : options.getModuleNames()) {
       File compilerWorkDir = options.getCompilerWorkDir(moduleName);
       Util.recursiveDelete(compilerWorkDir, true);
-      // No need to check mkdirs result because an IOException will occur anyway.
+      // No need to check mkdirs result because an IOException will occur
+      // anyway.
       compilerWorkDir.mkdirs();
 
       File precompilationFile = new File(compilerWorkDir, PRECOMPILE_FILENAME);
diff --git a/dev/core/src/com/google/gwt/dev/PrecompileOnePerm.java b/dev/core/src/com/google/gwt/dev/PrecompileOnePerm.java
new file mode 100644
index 0000000..f02a115
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/PrecompileOnePerm.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
+import com.google.gwt.dev.CompileTaskRunner.CompileTask;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.cfg.PropertyPermutations;
+import com.google.gwt.dev.shell.CheckForUpdates;
+import com.google.gwt.dev.shell.CheckForUpdates.UpdateResult;
+import com.google.gwt.dev.util.Memory;
+import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.arg.ArgHandlerPerm;
+import com.google.gwt.dev.util.arg.OptionPerm;
+import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Creates a ready-to-compile AST for a single permutation.
+ *
+ * Also collapses similar permutations and builds them together.
+ */
+public class PrecompileOnePerm {
+
+  /**
+   * The set of options for the precompiler.
+   */
+  public interface PrecompileOnePermOptions extends
+      Precompile.PrecompileOptions, OptionPerm {
+  }
+
+  static class ArgProcessor extends Precompile.ArgProcessor {
+    public ArgProcessor(PrecompileOnePermOptions options) {
+      super(options);
+      registerHandler(new ArgHandlerPerm(options));
+    }
+
+    @Override
+    protected String getName() {
+      return PrecompileOnePerm.class.getName();
+    }
+  }
+
+  @SuppressWarnings("serial")
+  static class PrecompileOnePermOptionsImpl extends
+      Precompile.PrecompileOptionsImpl implements PrecompileOnePermOptions {
+
+    int permToCompile = -1;
+
+    public PrecompileOnePermOptionsImpl() {
+    }
+
+    public PrecompileOnePermOptionsImpl(PrecompileOnePermOptions other) {
+      copyFrom(other);
+    }
+
+    public void copyFrom(PrecompileOnePermOptions other) {
+      super.copyFrom(other);
+      setPermToCompile(other.getPermToCompile());
+    }
+
+    public int getPermToCompile() {
+      return permToCompile;
+    }
+
+    public void setPermToCompile(int permToCompile) {
+      this.permToCompile = permToCompile;
+    }
+  }
+
+  /**
+   * Performs a command-line precompile.
+   */
+  public static void main(String[] args) {
+    Memory.initialize();
+    SpeedTracerLogger.init();
+    Event precompileEvent = SpeedTracerLogger.start(CompilerEventType.PRECOMPILE);
+    if (System.getProperty("gwt.jjs.dumpAst") != null) {
+      System.out.println("Will dump AST to: "
+          + System.getProperty("gwt.jjs.dumpAst"));
+    }
+
+    /*
+     * NOTE: main always exits with a call to System.exit to terminate any
+     * non-daemon threads that were started in Generators. Typically, this is to
+     * shutdown AWT related threads, since the contract for their termination is
+     * still implementation-dependent.
+     */
+    final PrecompileOnePermOptions options = new PrecompileOnePermOptionsImpl();
+    boolean success = false;
+    if (new ArgProcessor(options).processArgs(args)) {
+      CompileTask task = new CompileTask() {
+        public boolean run(TreeLogger logger) throws UnableToCompleteException {
+          FutureTask<UpdateResult> updater = null;
+          if (!options.isUpdateCheckDisabled()) {
+            updater = CheckForUpdates.checkForUpdatesInBackgroundThread(logger,
+                CheckForUpdates.ONE_DAY);
+          }
+          boolean success = new PrecompileOnePerm(options).run(logger);
+          if (success) {
+            CheckForUpdates.logUpdateAvailable(logger, updater);
+          }
+          return success;
+        }
+      };
+      if (CompileTaskRunner.runWithAppropriateLogger(options, task)) {
+        // Exit w/ success code.
+        success = true;
+      }
+    }
+    precompileEvent.end();
+    System.exit(success ? 0 : 1);
+  }
+
+  /**
+   * Returns a filename for the serialized AST when precompile is performed
+   * separately on a per-permutation basis.
+   */
+  static String getPrecompileFilename(int permutationBase) {
+    return Precompile.PRECOMPILE_FILENAME_PREFIX + "-" + permutationBase
+        + Precompile.PRECOMPILE_FILENAME_SUFFIX;
+  }
+
+  private static boolean validateOptions(TreeLogger logger, PrecompileOnePermOptions options) {
+    // Fatal Errors
+    if (options.getModuleNames().size() != 1) {
+      logger.log(TreeLogger.ERROR, "Expected a single module name.");
+      return false;
+    }
+    if (options.getPermToCompile() < 0) {
+      logger.log(TreeLogger.ERROR,
+          "Expected argument -perm to specify the permutation to compile.");
+      return false;
+    }
+
+    // Warnings
+    if (!options.isEnabledGeneratingOnShards()) {
+      logger.log(TreeLogger.WARN,
+      "-XdisableGeneratingOnShards has no effect in PrecompileOnePerm");
+    }
+    if (options.getMaxPermsPerPrecompile() != -1) {
+      logger.log(TreeLogger.WARN,
+      "-XmaxPermsPerPrecompile has no effect in PrecompileOnePerm");
+    }
+
+    return true;
+  }
+
+  private final PrecompileOnePermOptionsImpl options;
+
+  public PrecompileOnePerm(PrecompileOnePermOptions options) {
+    this.options = new PrecompileOnePermOptionsImpl(options);
+  }
+
+  public boolean run(TreeLogger logger) throws UnableToCompleteException {
+
+    if (!validateOptions(logger, options)) {
+      return false;
+    }
+
+    // Avoid early optimizations since permutation compiles will run separately.
+    options.setOptimizePrecompile(false);
+
+    List<String> moduleNames = options.getModuleNames();
+
+    int permToRun = options.getPermToCompile();
+
+    String moduleName = moduleNames.get(0);
+    File compilerWorkDir = options.getCompilerWorkDir(moduleName);
+
+    ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName);
+    StandardLinkerContext linkerContext = new StandardLinkerContext(
+        TreeLogger.NULL, module, options);
+
+    if (!linkerContext.allLinkersAreShardable()) {
+      logger.log(TreeLogger.ERROR,
+          "This compilation mode requires all linkers to be shardable.");
+      return false;
+    }
+
+    Precompile.PrecompileOptions optionsFileData = AnalyzeModule.readAnalyzeModuleOptionsFile(
+        logger, compilerWorkDir);
+    if (optionsFileData == null) {
+      logger.log(TreeLogger.ERROR, "Couldn't find "
+          + AnalyzeModule.OPTIONS_FILENAME + " in " + compilerWorkDir);
+      return false;
+    }
+    logger.log(TreeLogger.INFO, "Precompiling only specified permutations");
+
+    if (options.isValidateOnly()) {
+      TreeLogger branch = logger.branch(TreeLogger.INFO,
+          "Validating compilation " + module.getName());
+      if (!Precompile.validate(branch, options, module, options.getGenDir())) {
+        branch.log(TreeLogger.ERROR, "Validation failed");
+        return false;
+      }
+      branch.log(TreeLogger.INFO, "Validation succeeded");
+    } else {
+      TreeLogger branch = logger.branch(TreeLogger.INFO, "Precompiling module "
+          + module.getName());
+      if (!precompilePermutation(logger, compilerWorkDir, module, branch,
+          permToRun)) {
+        branch.log(TreeLogger.ERROR, "Precompile failed");
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private boolean precompilePermutation(TreeLogger logger,
+      File compilerWorkDir, ModuleDef module, TreeLogger branch, int permId)
+      throws UnableToCompleteException {
+
+    // Only precompile specified permutations
+    List<PropertyPermutations> collapsedPermutations =
+      Precompile.getCollapsedPermutations(module);
+
+    PropertyPermutations onePerm = collapsedPermutations.get(permId);
+    Precompilation precompilation = Precompile.precompile(branch, options,
+        module, permId, onePerm, options.getGenDir());
+    if (precompilation == null) {
+      branch.log(TreeLogger.ERROR, "Precompilation failed");
+      return false;
+    }
+    File precompilationFile = new File(compilerWorkDir,
+        getPrecompileFilename(permId));
+    Util.writeObjectAsFile(logger, precompilationFile, precompilation);
+
+    branch.log(TreeLogger.INFO, "Precompilation succeeded for permutation "
+        + permId);
+
+    return true;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerPerm.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerPerm.java
new file mode 100644
index 0000000..bb7a6a1
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerPerm.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util.arg;
+
+import com.google.gwt.util.tools.ArgHandlerInt;
+
+/**
+ * Argument handler for specifying the which permutation to run.
+ */
+public class ArgHandlerPerm extends ArgHandlerInt {
+  private final OptionPerm option;
+
+  public ArgHandlerPerm(OptionPerm option) {
+    this.option = option;
+  }
+
+  @Override
+  public String getPurpose() {
+    return "Specifies (0-based) the permutation to compile";
+  }
+
+  @Override
+  public String getTag() {
+    return "-perm";
+  }
+
+  @Override
+  public String[] getTagArgs() {
+    return new String[]{"perm"};
+  }
+
+  @Override
+  public void setInt(int value) {
+    // TODO Auto-generated method stub
+    if (value < 0) {
+      System.err.println(getTag() + " error: negative value '" + value
+          + "' is not allowed");
+    } else {
+      option.setPermToCompile(value);
+    }
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionPerm.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionPerm.java
new file mode 100644
index 0000000..9d51e98
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionPerm.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util.arg;
+
+/**
+ * Handles options for which permutation to compile.
+ */
+public interface OptionPerm {
+  /**
+   * Gets the permutation to compile. Returns -1 if no permutation was
+   * specified.
+   */
+  int getPermToCompile();
+
+  /**
+   * Sets a permutation to compile.
+   */
+  void setPermToCompile(int permToCompile);
+}