Package com.google.gwt.core.ext.linker.impl

Source Code of com.google.gwt.core.ext.linker.impl.StandardLinkerContext

/*
* 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.core.ext.linker.impl;

import com.google.gwt.core.ext.Linker;
import com.google.gwt.core.ext.LinkerContext;
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.ConfigurationProperty;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
import com.google.gwt.core.ext.linker.PublicResource;
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.Property;
import com.google.gwt.dev.cfg.Script;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.JJSOptions;
import com.google.gwt.dev.jjs.PermutationResult;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.js.JsObfuscateNamer;
import com.google.gwt.dev.js.JsParser;
import com.google.gwt.dev.js.JsParserException;
import com.google.gwt.dev.js.JsPrettyNamer;
import com.google.gwt.dev.js.JsSourceGenerationVisitor;
import com.google.gwt.dev.js.JsStringInterner;
import com.google.gwt.dev.js.JsSymbolResolver;
import com.google.gwt.dev.js.JsUnusedFunctionRemover;
import com.google.gwt.dev.js.JsVerboseNamer;
import com.google.gwt.dev.js.ast.JsContext;
import com.google.gwt.dev.js.ast.JsExpression;
import com.google.gwt.dev.js.ast.JsFunction;
import com.google.gwt.dev.js.ast.JsModVisitor;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsScope;
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.dev.util.FileBackedObject;
import com.google.gwt.dev.util.Util;
import com.google.gwt.util.tools.Utility;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
* An implementation of {@link LinkerContext} that is initialized from a
* {@link ModuleDef}.
*/
public class StandardLinkerContext extends Linker implements LinkerContext {

  /**
   * Applies the {@link JsStringInterner} optimization to each top-level
   * function defined within a JsProgram.
   */
  private static class TopFunctionStringInterner extends JsModVisitor {

    public static boolean exec(JsProgram program) {
      TopFunctionStringInterner v = new TopFunctionStringInterner(program);
      v.accept(program);
      return v.didChange();
    }

    private final JsProgram program;

    public TopFunctionStringInterner(JsProgram program) {
      this.program = program;
    }

    @Override
    public boolean visit(JsFunction x, JsContext<JsExpression> ctx) {
      didChange |= JsStringInterner.exec(program, x.getBody(), x.getScope());
      return false;
    }
  }

  static final Comparator<ConfigurationProperty> CONFIGURATION_PROPERTY_COMPARATOR = new Comparator<ConfigurationProperty>() {
    public int compare(ConfigurationProperty o1, ConfigurationProperty o2) {
      return o1.getName().compareTo(o2.getName());
    }
  };

  static final Comparator<SelectionProperty> SELECTION_PROPERTY_COMPARATOR = new Comparator<SelectionProperty>() {
    public int compare(SelectionProperty o1, SelectionProperty o2) {
      return o1.getName().compareTo(o2.getName());
    }
  };

  /**
   * Returns the parent path of forward-slash based partial path. Assumes the
   * given path does not end with a trailing slash.
   */
  private static String getParentPath(String path) {
    assert !path.endsWith("/");
    int pos = path.lastIndexOf('/');
    return (pos >= 0) ? path.substring(0, pos) : null;
  }

  /**
   * A faster bulk version of {@link File#mkdirs()} that takes advantage of
   * cached state to avoid a lot of file system access.
   */
  private static boolean mkdirs(File dir, Set<String> createdDirs) {
    if (dir == null) {
      return true;
    }
    String path = dir.getPath();
    if (createdDirs.contains(path)) {
      return true;
    }
    if (!dir.exists()) {
      if (!mkdirs(dir.getParentFile(), createdDirs)) {
        return false;
      }
      if (!dir.mkdir()) {
        return false;
      }
    }
    createdDirs.add(path);
    return true;
  }

  /**
   * Creates directory entries within a zip archive. This is consistent with how
   * most tools operate.
   *
   * @param path the path of a directory within the archive to create
   * @param zipOutputStream the archive we're creating
   * @param createdDirs the set of already-created directories to avoid
   *          duplication
   */
  private static void mkzipDirs(String path, ZipOutputStream zipOutputStream,
      Set<String> createdDirs) throws IOException {
    if (path == null) {
      return;
    }
    if (createdDirs.contains(path)) {
      return;
    }
    mkzipDirs(getParentPath(path), zipOutputStream, createdDirs);
    ZipEntry entry = new ZipEntry(path + '/');
    entry.setSize(0);
    entry.setCompressedSize(0);
    entry.setCrc(0);
    entry.setMethod(ZipOutputStream.STORED);
    zipOutputStream.putNextEntry(entry);
    createdDirs.add(path);
  }

  private final ArtifactSet artifacts = new ArtifactSet();

  private final SortedSet<ConfigurationProperty> configurationProperties;
  private final JJSOptions jjsOptions;
  private final Map<Class<? extends Linker>, String> linkerShortNames = new HashMap<Class<? extends Linker>, String>();

  private final String moduleFunctionName;
  private final long moduleLastModified;
  private final String moduleName;

  private final Map<String, StandardSelectionProperty> propertiesByName = new HashMap<String, StandardSelectionProperty>();
  private final Map<String, StandardCompilationResult> resultsByStrongName = new HashMap<String, StandardCompilationResult>();
  private final SortedSet<SelectionProperty> selectionProperties;

  private final Linker[] linkers;

  public StandardLinkerContext(TreeLogger logger, ModuleDef module,
      JJSOptions jjsOptions) throws UnableToCompleteException {
    logger = logger.branch(TreeLogger.DEBUG,
        "Constructing StandardLinkerContext", null);

    this.jjsOptions = jjsOptions;
    this.moduleFunctionName = module.getFunctionName();
    this.moduleName = module.getName();
    this.moduleLastModified = module.lastModified();

    // Sort the linkers into the order they should actually run.
    List<Class<? extends Linker>> sortedLinkers = new ArrayList<Class<? extends Linker>>();

    // Get all the pre-linkers first.
    for (Class<? extends Linker> linkerClass : module.getActiveLinkers()) {
      Order order = linkerClass.getAnnotation(LinkerOrder.class).value();
      assert (order != null);
      if (order == Order.PRE) {
        sortedLinkers.add(linkerClass);
      }
    }

    // Get the primary linker.
    Class<? extends Linker> primary = module.getActivePrimaryLinker();
    if (primary == null) {
      logger.log(logger.ERROR, "Primary linker is null.  Does your module " +
          "inherit from com.google.gwt.core.Core or com.google.gwt.user.User?");
    } else {
      sortedLinkers.add(module.getActivePrimaryLinker());
    }

    // Get all the post-linkers IN REVERSE ORDER.
    {
      List<Class<? extends Linker>> postLinkerClasses = new ArrayList<Class<? extends Linker>>();
      for (Class<? extends Linker> linkerClass : module.getActiveLinkers()) {
        Order order = linkerClass.getAnnotation(LinkerOrder.class).value();
        assert (order != null);
        if (order == Order.POST) {
          postLinkerClasses.add(linkerClass);
        }
      }
      Collections.reverse(postLinkerClasses);
      sortedLinkers.addAll(postLinkerClasses);
    }

    linkers = new Linker[sortedLinkers.size()];
    int i = 0;
    for (Class<? extends Linker> linkerClass : sortedLinkers) {
      try {
        linkers[i++] = linkerClass.newInstance();
      } catch (InstantiationException e) {
        logger.log(TreeLogger.ERROR, "Unable to create Linker", e);
        throw new UnableToCompleteException();
      } catch (IllegalAccessException e) {
        logger.log(TreeLogger.ERROR, "Unable to create Linker", e);
        throw new UnableToCompleteException();
      }
    }

    for (Map.Entry<String, Class<? extends Linker>> entry : module.getLinkers().entrySet()) {
      linkerShortNames.put(entry.getValue(), entry.getKey());
    }

    /*
     * This will make all private PublicResources and GeneratedResources appear
     * in the root of the module auxiliary directory.
     */
    linkerShortNames.put(this.getClass(), "");

    // Break ModuleDef properties out into LinkerContext interfaces
    {
      SortedSet<ConfigurationProperty> mutableConfigurationProperties = new TreeSet<ConfigurationProperty>(
          CONFIGURATION_PROPERTY_COMPARATOR);
      SortedSet<SelectionProperty> mutableSelectionProperties = new TreeSet<SelectionProperty>(
          SELECTION_PROPERTY_COMPARATOR);
      for (Property p : module.getProperties()) {
        // Create a new view
        if (p instanceof com.google.gwt.dev.cfg.ConfigurationProperty) {
          StandardConfigurationProperty newProp = new StandardConfigurationProperty(
              (com.google.gwt.dev.cfg.ConfigurationProperty) p);
          mutableConfigurationProperties.add(newProp);
          logger.log(TreeLogger.SPAM,
              "Added configuration property " + newProp, null);
        } else if (p instanceof BindingProperty) {
          StandardSelectionProperty newProp = new StandardSelectionProperty(
              (BindingProperty) p);
          mutableSelectionProperties.add(newProp);
          propertiesByName.put(newProp.getName(), newProp);
          logger.log(TreeLogger.SPAM, "Added selection property " + newProp,
              null);
        } else {
          logger.log(TreeLogger.ERROR, "Unknown property type "
              + p.getClass().getName());
        }
      }
      selectionProperties = Collections.unmodifiableSortedSet(mutableSelectionProperties);
      configurationProperties = Collections.unmodifiableSortedSet(mutableConfigurationProperties);
    }

    {
      int index = 0;
      for (Script script : module.getScripts()) {
        artifacts.add(new StandardScriptReference(script.getSrc(), index++));
        logger.log(TreeLogger.SPAM, "Added script " + script.getSrc(), null);
      }
    }

    {
      int index = 0;
      for (String style : module.getStyles()) {
        artifacts.add(new StandardStylesheetReference(style, index++));
        logger.log(TreeLogger.SPAM, "Added style " + style, null);
      }
    }

    // Generated files should be passed in via addArtifacts()

    for (String path : module.getAllPublicFiles()) {
      String partialPath = path.replace(File.separatorChar, '/');
      PublicResource resource = new StandardPublicResource(partialPath,
          module.findPublicFile(path));
      artifacts.add(resource);
      logger.log(TreeLogger.SPAM, "Added public resource " + resource, null);
    }
  }

  /**
   * Adds or replaces Artifacts in the ArtifactSet that will be passed into the
   * Linkers invoked.
   */
  public void addOrReplaceArtifacts(ArtifactSet artifacts) {
    this.artifacts.removeAll(artifacts);
    this.artifacts.addAll(artifacts);
  }

  /**
   * Returns the ArtifactSet that will passed into the invoke Linkers.
   */
  public ArtifactSet getArtifacts() {
    return artifacts;
  }

  /**
   * Gets or creates a CompilationResult for the given JavaScript program.
   */
  public StandardCompilationResult getCompilation(TreeLogger logger,
      FileBackedObject<PermutationResult> resultFile)
      throws UnableToCompleteException {
    PermutationResult permutationResult = resultFile.newInstance(logger);

    byte[][] js = permutationResult.getJs();
    String strongName = Util.computeStrongName(js);
    StandardCompilationResult result = resultsByStrongName.get(strongName);
    if (result == null) {
      result = new StandardCompilationResult(strongName, js,
          permutationResult.getSerializedSymbolMap(),
          permutationResult.getStatementRanges(), permutationResult.getPermutationId());
      resultsByStrongName.put(result.getStrongName(), result);
      artifacts.add(result);

      // Add any other Permutations
      ArtifactSet otherArtifacts = permutationResult.getArtifacts();
      if (otherArtifacts != null) {
        artifacts.addAll(otherArtifacts);
      }
    }
    return result;
  }

  public SortedSet<ConfigurationProperty> getConfigurationProperties() {
    return configurationProperties;
  }

  @Override
  public String getDescription() {
    return "Root Linker";
  }

  public String getModuleFunctionName() {
    return moduleFunctionName;
  }

  public long getModuleLastModified() {
    return moduleLastModified;
  }

  public String getModuleName() {
    return moduleName;
  }

  public SortedSet<SelectionProperty> getProperties() {
    return selectionProperties;
  }

  public StandardSelectionProperty getProperty(String name) {
    return propertiesByName.get(name);
  }

  /**
   * Run the linker stack.
   */
  public ArtifactSet invokeLink(TreeLogger logger)
      throws UnableToCompleteException {
    ArtifactSet workingArtifacts = new ArtifactSet(artifacts);

    for (Linker linker : linkers) {
      TreeLogger linkerLogger = logger.branch(TreeLogger.TRACE,
          "Invoking Linker " + linker.getDescription(), null);
      workingArtifacts.freeze();
      try {
        workingArtifacts = linker.link(linkerLogger, this, workingArtifacts);
      } catch (Throwable e) {
        linkerLogger.log(TreeLogger.ERROR, "Failed to link", e);
        throw new UnableToCompleteException();
      }
    }
    return workingArtifacts;
  }

  public ArtifactSet invokeRelink(TreeLogger logger,
      ArtifactSet newlyGeneratedArtifacts) throws UnableToCompleteException {
    ArtifactSet workingArtifacts = new ArtifactSet(newlyGeneratedArtifacts);

    for (Linker linker : linkers) {
      TreeLogger linkerLogger = logger.branch(TreeLogger.TRACE,
          "Invoking relink on Linker " + linker.getDescription(), null);
      workingArtifacts.freeze();
      try {
        workingArtifacts = linker.relink(linkerLogger, this, workingArtifacts);
      } catch (Throwable e) {
        linkerLogger.log(TreeLogger.ERROR, "Failed to relink", e);
        throw new UnableToCompleteException();
      }
    }
    return workingArtifacts;
  }

  public boolean isOutputCompact() {
    return jjsOptions.getOutput().shouldMinimize();
  }

  @Override
  public ArtifactSet link(TreeLogger logger, LinkerContext context,
      ArtifactSet artifacts) throws UnableToCompleteException {
    throw new UnsupportedOperationException();
  }

  public String optimizeJavaScript(TreeLogger logger, String program)
      throws UnableToCompleteException {
    logger = logger.branch(TreeLogger.DEBUG, "Attempting to optimize JS", null);
    Reader r = new StringReader(program);
    JsProgram jsProgram = new JsProgram();
    JsScope topScope = jsProgram.getScope();
    JsName funcName = topScope.declareName(getModuleFunctionName());
    funcName.setObfuscatable(false);

    try {
      SourceInfo sourceInfo = jsProgram.createSourceInfoSynthetic(
          StandardLinkerContext.class, "Linker-derived JS");
      JsParser.parseInto(sourceInfo, topScope, jsProgram.getGlobalBlock(), r);
    } catch (IOException e) {
      logger.log(TreeLogger.ERROR, "Unable to parse JavaScript", e);
      throw new UnableToCompleteException();
    } catch (JsParserException e) {
      logger.log(TreeLogger.ERROR, "Unable to parse JavaScript", e);
      throw new UnableToCompleteException();
    }

    JsSymbolResolver.exec(jsProgram);
    JsUnusedFunctionRemover.exec(jsProgram);

    switch (jjsOptions.getOutput()) {
      case OBFUSCATED:
        /*
         * We can't apply the regular JsStringInterner to the JsProgram that
         * we've just created. In the normal case, the JsStringInterner adds an
         * additional statement to the program's global JsBlock, however we
         * don't know exactly what the form and structure of our JsProgram are,
         * so we'll limit the scope of the modifications to each top-level
         * function within the program.
         */
        TopFunctionStringInterner.exec(jsProgram);
        JsObfuscateNamer.exec(jsProgram);
        break;
      case PRETTY:
        // We don't intern strings in pretty mode to improve readability
        JsPrettyNamer.exec(jsProgram);
        break;
      case DETAILED:
        // As above with OBFUSCATED
        TopFunctionStringInterner.exec(jsProgram);
        JsVerboseNamer.exec(jsProgram);
        break;
      default:
        throw new InternalCompilerException("Unknown output mode");
    }

    DefaultTextOutput out = new DefaultTextOutput(
        jjsOptions.getOutput().shouldMinimize());
    JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
    v.accept(jsProgram);
    return out.toString();
  }

  /**
   * Writes artifacts into the extra directory in the standard way.
   *
   * @param logger logs the operation
   * @param artifacts the set of artifacts to write
   * @param extraPath optional extra path for non-deployable artifacts
   * @throws UnableToCompleteException
   */
  public void produceExtraDirectory(TreeLogger logger, ArtifactSet artifacts,
      File extraPath) throws UnableToCompleteException {
    extraPath = extraPath.getAbsoluteFile();
    logger = logger.branch(TreeLogger.TRACE, "Writing extras into "
        + extraPath.getPath(), null);

    Set<String> createdDirs = new HashSet<String>();
    for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) {
      TreeLogger artifactLogger = logger.branch(TreeLogger.DEBUG,
          "Emitting resource " + artifact.getPartialPath(), null);

      if (!artifact.isPrivate()) {
        continue;
      }

      File outFile = new File(extraPath, getExtraPathForLinker(
          artifact.getLinker(), artifact.getPartialPath()));
      writeArtifactToFile(artifactLogger, artifact, outFile, createdDirs);
    }
  }

  /**
   * Writes artifacts into an extra zip in the standard way.
   *
   * @param logger logs the operation
   * @param artifacts the set of artifacts to write
   * @param extraZip the output zip for deployable artifacts
   * @param pathPrefix path within the zip to write into; if non-empty must end
   *          with a trailing slash
   * @throws UnableToCompleteException
   */
  public void produceExtraZip(TreeLogger logger, ArtifactSet artifacts,
      File extraZip, String pathPrefix) throws UnableToCompleteException {
    extraZip = extraZip.getAbsoluteFile();
    logger = logger.branch(TreeLogger.TRACE, "Linking compilation into "
        + extraZip.getPath(), null);

    try {
      Set<String> createdDirs = new HashSet<String>();
      ZipOutputStream zipOutputStream = new ZipOutputStream(
          new BufferedOutputStream(new FileOutputStream(extraZip)));
      for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) {
        TreeLogger artifactLogger = logger.branch(TreeLogger.DEBUG,
            "Emitting resource " + artifact.getPartialPath(), null);

        if (!artifact.isPrivate()) {
          continue;
        }
        String path = pathPrefix
            + getExtraPathForLinker(artifact.getLinker(),
                artifact.getPartialPath());
        writeArtifactToZip(artifactLogger, artifact, path, zipOutputStream,
            createdDirs);
      }
      Utility.close(zipOutputStream);
    } catch (FileNotFoundException e) {
      logger.log(TreeLogger.ERROR, "Unable to create extra archive "
          + extraZip.getPath(), e);
      throw new UnableToCompleteException();
    }
  }

  /**
   * Writes artifacts into output directory in the standard way.
   *
   * @param logger logs the operation
   * @param artifacts the set of artifacts to write
   * @param outputPath the output path for deployable artifacts
   * @throws UnableToCompleteException
   */
  public void produceOutputDirectory(TreeLogger logger, ArtifactSet artifacts,
      File outputPath) throws UnableToCompleteException {
    outputPath = outputPath.getAbsoluteFile();
    logger = logger.branch(TreeLogger.TRACE, "Linking compilation into "
        + outputPath.getPath(), null);

    Set<String> createdDirs = new HashSet<String>();
    for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) {
      TreeLogger artifactLogger = logger.branch(TreeLogger.DEBUG,
          "Emitting resource " + artifact.getPartialPath(), null);

      if (artifact.isPrivate()) {
        continue;
      }
      File outFile = new File(outputPath, artifact.getPartialPath());
      writeArtifactToFile(artifactLogger, artifact, outFile, createdDirs);
    }
  }

  /**
   * Writes artifacts into an output zip in the standard way.
   *
   * @param logger logs the operation
   * @param artifacts the set of artifacts to write
   * @param outZip the output zip for deployable artifacts
   * @param pathPrefix path within the zip to write into; if non-empty must end
   *          with a trailing slash
   * @throws UnableToCompleteException
   */
  public void produceOutputZip(TreeLogger logger, ArtifactSet artifacts,
      File outZip, String pathPrefix) throws UnableToCompleteException {
    outZip = outZip.getAbsoluteFile();
    logger = logger.branch(TreeLogger.TRACE, "Linking compilation into "
        + outZip.getPath(), null);

    try {
      ZipOutputStream zipOutputStream = new ZipOutputStream(
          new BufferedOutputStream(new FileOutputStream(outZip)));
      Set<String> createdDirs = new HashSet<String>();
      for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) {
        TreeLogger artifactLogger = logger.branch(TreeLogger.DEBUG,
            "Emitting resource " + artifact.getPartialPath(), null);

        if (artifact.isPrivate()) {
          continue;
        }
        String path = pathPrefix + artifact.getPartialPath();
        writeArtifactToZip(artifactLogger, artifact, path, zipOutputStream,
            createdDirs);
      }
      Utility.close(zipOutputStream);
    } catch (FileNotFoundException e) {
      logger.log(TreeLogger.ERROR, "Unable to create output archive "
          + outZip.getPath(), e);
      throw new UnableToCompleteException();
    }
  }

  /**
   * Creates a linker-specific subdirectory in the module's auxiliary output
   * directory.
   */
  private String getExtraPathForLinker(Class<? extends Linker> linkerType,
      String partialPath) {
    assert linkerShortNames.containsKey(linkerType) : linkerType.getName()
        + " unknown";
    return linkerShortNames.get(linkerType) + '/' + partialPath;
  }

  private void writeArtifactToFile(TreeLogger logger, EmittedArtifact artifact,
      File outFile, Set<String> createdDirs) throws UnableToCompleteException {
    if (!outFile.exists()
        || (outFile.lastModified() < artifact.getLastModified())) {
      if (!mkdirs(outFile.getParentFile(), createdDirs)) {
        logger.log(TreeLogger.ERROR, "Unable to create directory for file '"
            + outFile.getAbsolutePath() + "'");
      } else {
        try {
          FileOutputStream out = new FileOutputStream(outFile);
          artifact.writeTo(logger, out);
          out.close();
        } catch (IOException e) {
          logger.log(TreeLogger.ERROR, "Unable to create file '"
              + outFile.getAbsolutePath() + "'", e);
          throw new UnableToCompleteException();
        }
        outFile.setLastModified(artifact.getLastModified());
      }
    }
  }

  private void writeArtifactToZip(TreeLogger logger, EmittedArtifact artifact,
      String path, ZipOutputStream zipOutputStream, Set<String> createdDirs)
      throws UnableToCompleteException {
    try {
      mkzipDirs(getParentPath(path), zipOutputStream, createdDirs);
      ZipEntry zipEntry = new ZipEntry(path);
      zipEntry.setTime(artifact.getLastModified());
      zipOutputStream.putNextEntry(zipEntry);
      artifact.writeTo(logger, zipOutputStream);
      zipOutputStream.closeEntry();
    } catch (IOException e) {
      logger.log(TreeLogger.ERROR, "Unable to write out artifact '"
          + artifact.getPartialPath() + "'", e);
      throw new UnableToCompleteException();
    }
  }
}
TOP

Related Classes of com.google.gwt.core.ext.linker.impl.StandardLinkerContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.