blob: 964ac20b83994b5968bf150f05228f9383c6c0ba [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.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.cfg.PropertyPermutations;
import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
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.MemoryBackedObject;
import com.google.gwt.dev.util.PerfCounter;
import com.google.gwt.dev.util.PersistenceBackedObject;
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.thirdparty.guava.common.collect.Lists;
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 PrecompileTaskOptions,
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 PrecompileTaskArgProcessor {
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 PrecompileTaskOptionsImpl 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());
}
@Override
public int getLocalWorkers() {
return localWorkers;
}
@Override
public int[] getPermsToCompile() {
return (permsToCompile == null) ? null : permsToCompile.clone();
}
@Override
public void setLocalWorkers(int localWorkers) {
this.localWorkers = localWorkers;
}
@Override
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, CompilerContext compilerContext,
Permutation permutation, UnifiedAst unifiedAst) throws UnableToCompleteException {
return JavaToJavaScriptCompiler.compilePermutation(unifiedAst, logger, compilerContext,
permutation);
}
/**
* Compile multiple permutations.
*/
public static void compile(TreeLogger logger, CompilerContext compilerContext,
Precompilation precompilation, Permutation[] perms, int localWorkers,
List<PersistenceBackedObject<PermutationResult>> resultFiles)
throws UnableToCompleteException {
final TreeLogger branch = logger.branch(TreeLogger.INFO,
"Compiling " + perms.length + " permutation" + (perms.length > 1 ? "s" : ""));
PermutationWorkerFactory.compilePermutations(
branch, compilerContext, 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() {
@Override
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<PersistenceBackedObject<PermutationResult>> makeResultFiles(
File compilerWorkDir, Permutation[] perms, PrecompileTaskOptions options) {
List<PersistenceBackedObject<PermutationResult>> toReturn = Lists.newArrayList();
for (int i = 0; i < perms.length; ++i) {
if (options.isIncrementalCompileEnabled()) {
toReturn.add(new MemoryBackedObject<PermutationResult>(PermutationResult.class));
} else {
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");
}
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.
*/
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 CompilerContext compilerContext;
private CompilerContext.Builder compilerContextBuilder;
private final CompilePermsOptionsImpl options;
public CompilePerms(CompilePermsOptions options) {
this.options = new CompilePermsOptionsImpl(options);
compilerContextBuilder = new CompilerContext.Builder();
}
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 = readPrecompilationFile(logger,
precompilationFile);
if (precompileResults instanceof PrecompileTaskOptions) {
PrecompileTaskOptions precompilationOptions = (PrecompileTaskOptions) precompileResults;
if (!precompileAndCompile(logger, moduleName, compilerWorkDir,
precompilationOptions)) {
return false;
}
} else {
compilerContext = compilerContextBuilder.options(options).build();
ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, compilerContext, moduleName);
compilerContext = compilerContextBuilder.module(module).build();
Precompilation precompilation = (Precompilation) precompileResults;
// Choose which permutations go with this permutation
Permutation[] subPerms = selectPermutationsForPrecompilation(
permsToRun, precompilation);
List<PersistenceBackedObject<PermutationResult>> resultFiles = makeResultFiles(
compilerWorkDir, subPerms, options);
compile(logger, compilerContext, 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, PrecompileTaskOptions precompilationOptions)
throws UnableToCompleteException {
precompilationOptions.setOptimizePrecompile(false);
precompilationOptions.setGenDir(null);
compilerContext = compilerContextBuilder.options(precompilationOptions).build();
ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, compilerContext, moduleName);
compilerContext = compilerContextBuilder.module(module).build();
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, compilerContext, permId, onePerm);
if (precompilation == null) {
return false;
}
// TODO: move to precompile() after params are refactored
if (!options.shouldSaveSource()) {
precompilation.removeSourceArtifacts(logger);
}
// Choose which permutations go with this precompilation
Permutation[] subPerms = selectPermutationsForPrecompilation(
new int[]{permId}, precompilation);
assert subPerms.length == 1;
PermutationResult permResult =
compile(logger, compilerContext, subPerms[0], precompilation.getUnifiedAst());
Link.linkOnePermutationToJar(logger, compilerContext.getModule(),
compilerContext.getPublicResourceOracle(), precompilation.getGeneratedArtifacts(),
permResult, makePermFilename(compilerWorkDir, permId), compilerContext.getOptions());
}
logger.log(TreeLogger.INFO, "Compile of permutations succeeded");
return true;
}
}