blob: d1a7fa212c01a763932224c5750994d8d035b38d [file] [log] [blame]
/*
* Copyright 2008 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.cfg.PropertyPermutations;
import com.google.gwt.dev.jjs.PermutationResult;
import com.google.gwt.dev.jjs.UnifiedAst;
import com.google.gwt.dev.util.FileBackedObject;
import com.google.gwt.dev.util.PerfCounter;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerLocalWorkers;
import com.google.gwt.dev.util.arg.OptionLocalWorkers;
import com.google.gwt.util.tools.ArgHandlerString;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Performs the second phase of compilation, converting the Precompile's AST
* into JavaScript outputs.
*/
public class CompilePerms {
/**
* Options for CompilePerms.
*/
public interface CompilePermsOptions extends CompileTaskOptions,
OptionLocalWorkers, OptionPerms {
}
/**
* Handles options for which permutations to compile.
*/
public interface OptionPerms {
/**
* Gets the ordered set of permutations to compile. Returns a zero-length
* array if all permutations should be compiled.
*/
int[] getPermsToCompile();
/**
* Adds another permutation to compile.
*/
void setPermsToCompile(int[] permsToCompile);
}
/**
* Argument handler for specifying the which perms to run.
*/
protected static final class ArgHandlerPerms extends ArgHandlerString {
private final OptionPerms option;
public ArgHandlerPerms(OptionPerms option) {
this.option = option;
}
@Override
public String getPurpose() {
return "Comma-delimited list of 0-based permutations to compile";
}
@Override
public String getTag() {
return "-perms";
}
@Override
public String[] getTagArgs() {
return new String[]{"permlist"};
}
@Override
public boolean setString(String str) {
String[] split = str.split(",");
if (split.length < 1) {
System.err.println(getTag()
+ " requires a comma-delimited list of integers");
return false;
}
SortedSet<Integer> permSet = new TreeSet<Integer>();
for (String item : split) {
try {
int value = Integer.parseInt(item);
if (value < 0) {
System.err.println(getTag() + " error: negative value '" + value
+ "' is not allowed");
return false;
}
permSet.add(value);
} catch (NumberFormatException e) {
System.err.println(getTag()
+ " requires a comma-delimited list of integers; '" + item
+ "' is not an integer");
return false;
}
}
int[] permsToCompile = new int[permSet.size()];
int i = 0;
for (int perm : permSet) {
permsToCompile[i++] = perm;
}
option.setPermsToCompile(permsToCompile);
return true;
}
}
static class ArgProcessor extends CompileArgProcessor {
public ArgProcessor(CompilePermsOptions options) {
super(options);
registerHandler(new ArgHandlerPerms(options));
registerHandler(new ArgHandlerLocalWorkers(options));
}
@Override
protected String getName() {
return CompilePerms.class.getName();
}
}
/**
* Concrete class to implement compiler perm options.
*/
static class CompilePermsOptionsImpl extends CompileTaskOptionsImpl implements
CompilePermsOptions {
private int localWorkers;
private int[] permsToCompile;
public CompilePermsOptionsImpl() {
}
public CompilePermsOptionsImpl(CompilePermsOptions other) {
copyFrom(other);
}
public void copyFrom(CompilePermsOptions other) {
super.copyFrom(other);
setPermsToCompile(other.getPermsToCompile());
setLocalWorkers(other.getLocalWorkers());
}
public int getLocalWorkers() {
return localWorkers;
}
public int[] getPermsToCompile() {
return (permsToCompile == null) ? null : permsToCompile.clone();
}
public void setLocalWorkers(int localWorkers) {
this.localWorkers = localWorkers;
}
public void setPermsToCompile(int[] permsToCompile) {
this.permsToCompile = (permsToCompile == null) ? null
: permsToCompile.clone();
}
}
/**
* Compile a single permutation.
*
* @throws UnableToCompleteException if the permutation compile fails
*/
public static PermutationResult compile(TreeLogger logger,
Permutation permutation, UnifiedAst unifiedAst)
throws UnableToCompleteException {
return unifiedAst.compilePermutation(logger, permutation);
}
/**
* Compile multiple permutations.
*/
public static void compile(TreeLogger logger, Precompilation precompilation,
Permutation[] perms, int localWorkers,
List<FileBackedObject<PermutationResult>> resultFiles)
throws UnableToCompleteException {
final TreeLogger branch = logger.branch(TreeLogger.INFO, "Compiling "
+ perms.length + " permutation" + (perms.length > 1 ? "s" : ""));
PermutationWorkerFactory.compilePermutations(branch, precompilation, perms,
localWorkers, resultFiles);
logger.log(TreeLogger.INFO, "Compile of permutations succeeded");
}
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 CompilePermsOptions options = new CompilePermsOptionsImpl();
if (new ArgProcessor(options).processArgs(args)) {
CompileTask task = new CompileTask() {
public boolean run(TreeLogger logger) throws UnableToCompleteException {
return new CompilePerms(options).run(logger);
}
};
if (CompileTaskRunner.runWithAppropriateLogger(options, task)) {
// Exit w/ success code.
exitCode = 0;
}
}
PerfCounter.print();
System.exit(exitCode);
}
public static List<FileBackedObject<PermutationResult>> makeResultFiles(
File compilerWorkDir, Permutation[] perms) {
List<FileBackedObject<PermutationResult>> toReturn = new ArrayList<FileBackedObject<PermutationResult>>(
perms.length);
for (int i = 0; i < perms.length; ++i) {
File f = makePermFilename(compilerWorkDir, perms[i].getId());
toReturn.add(new FileBackedObject<PermutationResult>(
PermutationResult.class, f));
}
return toReturn;
}
/**
* 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");
}
/**
* Choose the subset of requested permutations that correspond to the
* indicated precompilation.
*/
private static Permutation[] selectPermutationsForPrecompilation(
int[] permsToRun, Precompilation precompilation) {
if (permsToRun == null) {
// Special case: compile everything.
return precompilation.getPermutations();
}
ArrayList<Permutation> subPermsList = new ArrayList<Permutation>();
for (int id : permsToRun) {
for (Permutation perm : precompilation.getPermutations()) {
if (perm.getId() == id) {
subPermsList.add(perm);
}
}
}
return subPermsList.toArray(new Permutation[subPermsList.size()]);
}
private final CompilePermsOptionsImpl options;
public CompilePerms(CompilePermsOptions options) {
this.options = new CompilePermsOptionsImpl(options);
}
public boolean run(TreeLogger logger) throws UnableToCompleteException {
for (String moduleName : options.getModuleNames()) {
/*
* NOTE: as a special case, null means "compile everything".
*/
int[] permsToRun = options.getPermsToCompile();
File compilerWorkDir = options.getCompilerWorkDir(moduleName);
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;
}
if (precompileResults instanceof PrecompileOptions) {
PrecompileOptions precompilationOptions = (PrecompileOptions) precompileResults;
if (!precompileAndCompile(logger, moduleName, compilerWorkDir,
precompilationOptions)) {
return false;
}
} else {
Precompilation precompilation = (Precompilation) precompileResults;
// Choose which permutations go with this permutation
Permutation[] subPerms = selectPermutationsForPrecompilation(
permsToRun, precompilation);
List<FileBackedObject<PermutationResult>> resultFiles = makeResultFiles(
compilerWorkDir, subPerms);
compile(logger, precompilation, subPerms, options.getLocalWorkers(),
resultFiles);
}
}
return true;
}
/**
* Run both a precompile and a compile with the given precompilation options.
*/
private boolean precompileAndCompile(TreeLogger logger, String moduleName,
File compilerWorkDir, PrecompileOptions precompilationOptions)
throws UnableToCompleteException {
precompilationOptions.setOptimizePrecompile(false);
precompilationOptions.setGenDir(null);
ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName);
PropertyPermutations allPermutations = new PropertyPermutations(
module.getProperties(), module.getActiveLinkerNames());
List<PropertyPermutations> collapsedPermutations = allPermutations.collapseProperties();
int[] perms = options.getPermsToCompile();
if (perms == null) {
perms = new int[collapsedPermutations.size()];
for (int i = 0; i < perms.length; ++i) {
perms[i] = i;
}
}
logger = logger.branch(TreeLogger.INFO, "Compiling " + perms.length
+ " permutation" + (perms.length > 1 ? "s" : ""));
for (int permId : perms) {
/*
* TODO(spoon,scottb): move Precompile out of the loop to run only once
* per shard. Then figure out a way to avoid copying the generated
* artifacts into every perm result on a shard.
*/
PropertyPermutations onePerm = collapsedPermutations.get(permId);
Precompilation precompilation = Precompile.precompile(logger,
precompilationOptions, module, permId, onePerm,
precompilationOptions.getGenDir());
if (precompilation == null) {
return false;
}
// Choose which permutations go with this precompilation
Permutation[] subPerms = selectPermutationsForPrecompilation(
new int[]{permId}, precompilation);
assert subPerms.length == 1;
PermutationResult permResult = compile(logger, subPerms[0],
precompilation.getUnifiedAst());
Link.linkOnePermutationToJar(logger, module,
precompilation.getGeneratedArtifacts(), permResult, makePermFilename(
compilerWorkDir, permId), precompilationOptions);
}
logger.log(TreeLogger.INFO, "Compile of permutations succeeded");
return true;
}
}