blob: 1305ccf038ced1cbee0ff0060c987ac01e240a86 [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.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.impl.StandardCompilationResult;
import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
import com.google.gwt.dev.CompileTaskRunner.CompileTask;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.cfg.StaticPropertyOracle;
import com.google.gwt.dev.jjs.JJSOptions;
import com.google.gwt.dev.jjs.PermutationResult;
import com.google.gwt.dev.jjs.impl.CodeSplitter;
import com.google.gwt.dev.util.FileBackedObject;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
import com.google.gwt.dev.util.arg.ArgHandlerWarDir;
import com.google.gwt.dev.util.arg.OptionExtraDir;
import com.google.gwt.dev.util.arg.OptionOutDir;
import com.google.gwt.dev.util.arg.OptionWarDir;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Performs the last phase of compilation, merging the compilation outputs.
*/
public class Link {
/**
* Options for Link.
*/
@Deprecated
public interface LegacyLinkOptions extends CompileTaskOptions, OptionOutDir {
}
/**
* Options for Link.
*/
public interface LinkOptions extends CompileTaskOptions, OptionExtraDir,
OptionWarDir, LegacyLinkOptions {
}
static class ArgProcessor extends CompileArgProcessor {
@SuppressWarnings("deprecation")
public ArgProcessor(LinkOptions options) {
super(options);
registerHandler(new ArgHandlerExtraDir(options));
registerHandler(new ArgHandlerWarDir(options));
registerHandler(new ArgHandlerOutDirDeprecated(options));
}
@Override
protected String getName() {
return Link.class.getName();
}
}
/**
* Concrete class to implement link options.
*/
static class LinkOptionsImpl extends CompileTaskOptionsImpl implements
LinkOptions {
private File extraDir;
private File warDir;
private File outDir;
public LinkOptionsImpl() {
}
public LinkOptionsImpl(LinkOptions other) {
copyFrom(other);
}
public void copyFrom(LinkOptions other) {
super.copyFrom(other);
setExtraDir(other.getExtraDir());
setWarDir(other.getWarDir());
setOutDir(other.getOutDir());
}
public File getExtraDir() {
return extraDir;
}
@Deprecated
public File getOutDir() {
return outDir;
}
public File getWarDir() {
return warDir;
}
public void setExtraDir(File extraDir) {
this.extraDir = extraDir;
}
@Deprecated
public void setOutDir(File outDir) {
this.outDir = outDir;
}
public void setWarDir(File warDir) {
this.warDir = warDir;
}
}
public static void legacyLink(TreeLogger logger, ModuleDef module,
ArtifactSet generatedArtifacts, Permutation[] permutations,
List<FileBackedObject<PermutationResult>> resultFiles, File outDir,
JJSOptions precompileOptions) throws UnableToCompleteException {
StandardLinkerContext linkerContext = new StandardLinkerContext(logger,
module, precompileOptions);
ArtifactSet artifacts = doLink(logger, linkerContext, generatedArtifacts,
permutations, resultFiles);
doProduceLegacyOutput(logger, artifacts, linkerContext, module, outDir);
}
public static void link(TreeLogger logger, ModuleDef module,
ArtifactSet generatedArtifacts, Permutation[] permutations,
List<FileBackedObject<PermutationResult>> resultFiles, File outDir,
File extrasDir, JJSOptions precompileOptions)
throws UnableToCompleteException {
StandardLinkerContext linkerContext = new StandardLinkerContext(logger,
module, precompileOptions);
ArtifactSet artifacts = doLink(logger, linkerContext, generatedArtifacts,
permutations, resultFiles);
doProduceOutput(logger, artifacts, linkerContext, module, outDir, extrasDir);
}
public static void main(String[] args) {
/*
* 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 LinkOptions options = new LinkOptionsImpl();
if (new ArgProcessor(options).processArgs(args)) {
CompileTask task = new CompileTask() {
public boolean run(TreeLogger logger) throws UnableToCompleteException {
return new Link(options).run(logger);
}
};
if (CompileTaskRunner.runWithAppropriateLogger(options, task)) {
// Exit w/ success code.
System.exit(0);
}
}
// Exit w/ non-success code.
System.exit(1);
}
private static ArtifactSet doLink(TreeLogger logger,
StandardLinkerContext linkerContext, ArtifactSet generatedArtifacts,
Permutation[] perms, List<FileBackedObject<PermutationResult>> resultFiles)
throws UnableToCompleteException {
if (perms.length != resultFiles.size()) {
throw new IllegalArgumentException(
"Mismatched resultFiles.length and permutation count");
}
for (int i = 0; i < perms.length; ++i) {
finishPermuation(logger, perms[i], resultFiles.get(i), linkerContext);
}
linkerContext.addOrReplaceArtifacts(generatedArtifacts);
return linkerContext.invokeLink(logger);
}
private static void doProduceLegacyOutput(TreeLogger logger,
ArtifactSet artifacts, StandardLinkerContext linkerContext,
ModuleDef module, File outDir) throws UnableToCompleteException {
File moduleOutDir = new File(outDir, module.getName());
File moduleExtraDir = new File(outDir, module.getName() + "-aux");
Util.recursiveDelete(moduleOutDir, true);
Util.recursiveDelete(moduleExtraDir, true);
linkerContext.produceOutputDirectory(logger, artifacts, moduleOutDir);
linkerContext.produceExtraDirectory(logger, artifacts, moduleExtraDir);
logger.log(TreeLogger.INFO, "Link succeeded");
}
private static void doProduceOutput(TreeLogger logger, ArtifactSet artifacts,
StandardLinkerContext linkerContext, ModuleDef module, File outDir,
File extraDir) throws UnableToCompleteException {
String outPath = outDir.getPath();
if (!outDir.isDirectory()
&& (outPath.endsWith(".war") || outPath.endsWith(".jar") || outPath.endsWith(".zip"))) {
linkerContext.produceOutputZip(logger, artifacts, outDir,
module.getName() + '/');
} else {
File moduleOutDir = new File(outDir, module.getName());
Util.recursiveDelete(moduleOutDir, true);
linkerContext.produceOutputDirectory(logger, artifacts, moduleOutDir);
}
if (extraDir != null) {
String extraPath = extraDir.getPath();
if (!extraDir.isDirectory()
&& (extraPath.endsWith(".war") || extraPath.endsWith(".jar") || extraPath.endsWith(".zip"))) {
linkerContext.produceExtraZip(logger, artifacts, extraDir,
module.getName() + '/');
} else {
File moduleExtraDir = new File(extraDir, module.getName());
Util.recursiveDelete(moduleExtraDir, true);
linkerContext.produceExtraDirectory(logger, artifacts, moduleExtraDir);
}
}
logger.log(TreeLogger.INFO, "Link succeeded");
}
private static void finishPermuation(TreeLogger logger, Permutation perm,
FileBackedObject<PermutationResult> resultFile,
StandardLinkerContext linkerContext) throws UnableToCompleteException {
StandardCompilationResult compilation = linkerContext.getCompilation(
logger, resultFile);
StaticPropertyOracle[] propOracles = perm.getPropertyOracles();
for (StaticPropertyOracle propOracle : propOracles) {
BindingProperty[] orderedProps = propOracle.getOrderedProps();
String[] orderedPropValues = propOracle.getOrderedPropValues();
Map<SelectionProperty, String> unboundProperties = new HashMap<SelectionProperty, String>();
for (int i = 0; i < orderedProps.length; i++) {
SelectionProperty key = linkerContext.getProperty(orderedProps[i].getName());
if (key.tryGetValue() != null) {
/*
* The view of the Permutation doesn't include properties with defined
* values.
*/
continue;
} else if (key.isDerived()) {
/*
* The property provider does not need to be invoked, because the
* value is determined entirely by other properties.
*/
continue;
}
unboundProperties.put(key, orderedPropValues[i]);
}
compilation.addSelectionPermutation(unboundProperties);
}
logScriptSize(logger, perm.getId(), compilation);
}
/**
* Logs the total script size for this permutation, as calculated by
* {@link CodeSplitter#totalScriptSize(int[])}.
*/
private static void logScriptSize(TreeLogger logger, int permId,
StandardCompilationResult compilation) {
if (!logger.isLoggable(TreeLogger.TRACE)) {
return;
}
String[] javaScript = compilation.getJavaScript();
int[] jsLengths = new int[javaScript.length];
for (int i = 0; i < javaScript.length; i++) {
jsLengths[i] = javaScript[i].length();
}
int totalSize = CodeSplitter.totalScriptSize(jsLengths);
logger.log(TreeLogger.TRACE, "Permutation " + permId + " (strong name "
+ compilation.getStrongName() + ") has an initial download size of "
+ javaScript[0].length() + " and total script size of " + totalSize);
}
private final LinkOptionsImpl options;
public Link(LinkOptions options) {
this.options = new LinkOptionsImpl(options);
}
public boolean run(TreeLogger logger) throws UnableToCompleteException {
for (String moduleName : options.getModuleNames()) {
File compilerWorkDir = options.getCompilerWorkDir(moduleName);
Collection<PrecompilationFile> precomps;
try {
precomps = PrecompilationFile.scanJarFile(new File(compilerWorkDir,
Precompile.PRECOMPILE_FILENAME));
} catch (IOException e) {
logger.log(TreeLogger.ERROR, "Failed to scan "
+ Precompile.PRECOMPILE_FILENAME, e);
return false;
}
ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName);
if (precomps.isEmpty()) {
logger.log(TreeLogger.ERROR, "No precompilation files found in '"
+ compilerWorkDir.getAbsolutePath()
+ "'; please run Precompile first");
return false;
}
List<Permutation> permsList = new ArrayList<Permutation>();
ArtifactSet generatedArtifacts = new ArtifactSet();
JJSOptions precompileOptions = null;
for (PrecompilationFile precompilationFile : precomps) {
Precompilation precompilation;
try {
precompilation = precompilationFile.newInstance(logger);
} catch (UnableToCompleteException e) {
return false;
}
permsList.addAll(Arrays.asList(precompilation.getPermutations()));
generatedArtifacts.addAll(precompilation.getGeneratedArtifacts());
precompileOptions = precompilation.getUnifiedAst().getOptions();
}
Permutation[] perms = permsList.toArray(new Permutation[permsList.size()]);
List<FileBackedObject<PermutationResult>> resultFiles = new ArrayList<FileBackedObject<PermutationResult>>(
perms.length);
for (int i = 0; i < perms.length; ++i) {
File f = CompilePerms.makePermFilename(compilerWorkDir,
perms[i].getId());
if (!f.exists()) {
logger.log(TreeLogger.ERROR, "File not found '" + f.getAbsolutePath()
+ "'; please compile all permutations");
return false;
}
resultFiles.add(new FileBackedObject<PermutationResult>(
PermutationResult.class, f));
}
TreeLogger branch = logger.branch(TreeLogger.INFO, "Linking module "
+ module.getName());
StandardLinkerContext linkerContext = new StandardLinkerContext(branch,
module, precompileOptions);
ArtifactSet artifacts = doLink(branch, linkerContext, generatedArtifacts,
perms, resultFiles);
if (options.getOutDir() == null) {
doProduceOutput(branch, artifacts, linkerContext, module,
options.getWarDir(), options.getExtraDir());
} else {
doProduceLegacyOutput(branch, artifacts, linkerContext, module,
options.getOutDir());
}
}
return true;
}
}