Revision: 6497 Author: [email protected] Date: Tue Oct 27 18:11:59 2009 Log: Refactor StandardLinkerContext output/extra/file/jar implementation to not suck.
Patch by: spoon Review by: me http://code.google.com/p/google-web-toolkit/source/detail?r=6497 Added: /trunk/dev/core/src/com/google/gwt/dev/util/NullOutputFileSet.java /trunk/dev/core/src/com/google/gwt/dev/util/OutputFileSet.java /trunk/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnDirectory.java /trunk/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnJar.java Modified: /trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java /trunk/dev/core/src/com/google/gwt/dev/DevMode.java /trunk/dev/core/src/com/google/gwt/dev/Link.java ======================================= --- /dev/null +++ /trunk/dev/core/src/com/google/gwt/dev/util/NullOutputFileSet.java Tue Oct 27 18:11:59 2009 @@ -0,0 +1,51 @@ +/* + * Copyright 2009 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; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * An {...@link OutputFileSet} that discards all data sent to it. + */ +public class NullOutputFileSet extends OutputFileSet { + private static class NullOutputStream extends OutputStream { + @Override + public void write(byte[] b) throws IOException { + } + + @Override + public void write(byte[] b, int i, int j) throws IOException { + } + + @Override + public void write(int b) throws IOException { + } + } + + public NullOutputFileSet() { + super("NULL"); + } + + @Override + public void close() { + } + + @Override + public OutputStream openForWrite(String path, long lastModifiedTime) { + return new NullOutputStream(); + } +} ======================================= --- /dev/null +++ /trunk/dev/core/src/com/google/gwt/dev/util/OutputFileSet.java Tue Oct 27 18:11:59 2009 @@ -0,0 +1,44 @@ +/* + * Copyright 2009 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; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * An abstract set of files that a linker links into. + */ +public abstract class OutputFileSet { + private final String pathDescription; + + protected OutputFileSet(String pathDescription) { + this.pathDescription = pathDescription; + } + + public abstract void close() throws IOException; + + /** + * Return a description of this output file set's path. The precise meaning is + * unspecified, except that it should be informative when used in log + * messages. + */ + public String getPathDescription() { + return pathDescription; + } + + public abstract OutputStream openForWrite(String path, long lastModifiedTime) + throws IOException; +} ======================================= --- /dev/null +++ /trunk/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnDirectory.java Tue Oct 27 18:11:59 2009 @@ -0,0 +1,73 @@ +/* + * Copyright 2009 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; + +import com.google.gwt.dev.util.collect.HashSet; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Set; + +/** + * An {...@link OutputFileSet} on a directory. + */ +public class OutputFileSetOnDirectory extends OutputFileSet { + private final Set<String> createdDirs = new HashSet<String>(); + private final File dir; + private final String prefix; + + public OutputFileSetOnDirectory(File dir, String prefix) { + super(dir.getAbsolutePath()); + this.dir = dir; + this.prefix = prefix; + } + + @Override + public void close() { + } + + @Override + public OutputStream openForWrite(String path, long lastModifiedTime) + throws IOException { + File file = dir; + for (String part : (prefix + path).split("/")) { + file = new File(file, part); + } + mkdirs(file.getParentFile()); + return new FileOutputStream(file); + } + + /** + * A faster bulk version of {...@link File#mkdirs()} that avoids recreating the + * same directory multiple times. + */ + private void mkdirs(File dir) { + if (dir == null) { + return; + } + String path = dir.getPath(); + if (createdDirs.contains(path)) { + return; + } + createdDirs.add(path); + if (!dir.exists()) { + mkdirs(dir.getParentFile()); + dir.mkdir(); + } + } +} ======================================= --- /dev/null +++ /trunk/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnJar.java Tue Oct 27 18:11:59 2009 @@ -0,0 +1,119 @@ +/* + * Copyright 2009 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; + +import com.google.gwt.dev.util.collect.HashSet; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Set; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * An {...@link OutputFileSet} on a jar file. + */ +public class OutputFileSetOnJar extends OutputFileSet { + /** + * An output stream on a jar entry for <code>jar</code>. It is assumed that + * the entry has already been written, so this class only has to forward the + * writes. + */ + private final class OutputStreamOnJarEntry extends OutputStream { + @Override + public void close() throws IOException { + jar.closeEntry(); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + jar.write(b, off, len); + } + + @Override + public void write(int b) throws IOException { + jar.write(b); + } + } + + /** + * 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; + } + + private Set<String> createdDirs = new HashSet<String>(); + + private final JarOutputStream jar; + + private final String pathPrefix; + + public OutputFileSetOnJar(File jarFile, String pathPrefix) throws IOException { + super(jarFile.getAbsolutePath()); + jarFile.delete(); + jar = new JarOutputStream(new FileOutputStream(jarFile)); + this.pathPrefix = pathPrefix; + } + + @Override + public void close() throws IOException { + jar.close(); + } + + @Override + public OutputStream openForWrite(String path, long lastModifiedTime) + throws IOException { + mkzipDirs(getParentPath(pathPrefix + path)); + + ZipEntry zipEntry = new ZipEntry(pathPrefix + path); + if (lastModifiedTime >= 0) { + zipEntry.setTime(lastModifiedTime); + } + jar.putNextEntry(zipEntry); + + return new OutputStreamOnJarEntry(); + } + + /** + * Creates directory entries within a zip archive. Uses + * <code>createdDirs</code> to avoid creating entries for the same path twice. + * + * @param path the path of a directory within the archive to create + */ + private void mkzipDirs(String path) throws IOException { + if (path == null) { + return; + } + if (createdDirs.contains(path)) { + return; + } + mkzipDirs(getParentPath(path)); + ZipEntry entry = new ZipEntry(path + '/'); + entry.setSize(0); + entry.setCompressedSize(0); + entry.setCrc(0); + entry.setMethod(ZipOutputStream.STORED); + jar.putNextEntry(entry); + createdDirs.add(path); + } +} ======================================= --- /trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java Tue Jul 28 12:27:56 2009 +++ /trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java Tue Oct 27 18:11:59 2009 @@ -51,29 +51,22 @@ 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.OutputFileSet; 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.OutputStream; 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 @@ -117,67 +110,6 @@ 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(); @@ -294,24 +226,9 @@ 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() - + /* + * Add static resources in the specified module as artifacts. + */ for (String path : module.getAllPublicFiles()) { String partialPath = path.replace(File.separatorChar, '/'); PublicResource resource = new StandardPublicResource(partialPath, @@ -319,6 +236,8 @@ artifacts.add(resource); logger.log(TreeLogger.SPAM, "Added public resource " + resource, null); } + + recordStaticReferences(logger, module); } /** @@ -340,27 +259,20 @@ /** * 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); - + public StandardCompilationResult getCompilation( + PermutationResult permutationResult) { 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()); + 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); - } - } + } + artifacts.addAll(permutationResult.getArtifacts()); return result; } @@ -500,140 +412,47 @@ } /** - * Writes artifacts into the extra directory in the standard way. + * Emit EmittedArtifacts artifacts onto <code>out</code>. Does not close + * <code>out</code>. * - * @param logger logs the operation - * @param artifacts the set of artifacts to write - * @param extraPath optional extra path for non-deployable artifacts - * @throws UnableToCompleteException + * @param logger where to log progress + * @param artifacts the artifacts to emit + * @param emitPrivates whether to emit the private artifacts only, vs. the + * public artifacts only + * @param out where to emit the artifact contents */ - 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>(); + public void produceOutput(TreeLogger logger, ArtifactSet artifacts, + boolean emitPrivates, OutputFileSet out) throws UnableToCompleteException { + String publicness = emitPrivates ? "private" : "public"; + logger = logger.branch(TreeLogger.TRACE, "Linking " + publicness + + " artifacts into " + out.getPathDescription(), null); + for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) { TreeLogger artifactLogger = logger.branch(TreeLogger.DEBUG, "Emitting resource " + artifact.getPartialPath(), null); - if (!artifact.isPrivate()) { + if (artifact.isPrivate() != emitPrivates) { 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); + String partialPath = artifact.getPartialPath(); + if (artifact.isPrivate()) { + partialPath = getExtraPathForLinker(artifact.getLinker(), partialPath); + if (partialPath.startsWith("/")) { + partialPath = partialPath.substring(1); + } + } + try { + OutputStream artifactStream = out.openForWrite(partialPath, + artifact.getLastModified()); + artifact.writeTo(artifactLogger, artifactStream); + artifactStream.close(); + } catch (IOException e) { + artifactLogger.log(TreeLogger.ERROR, + "Fatal error emitting this artifact", e); + } } } - - /** - * 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 @@ -646,42 +465,26 @@ 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()); + /** + * Record script references and CSS references that are listed in the module + * file. + */ + private void recordStaticReferences(TreeLogger logger, ModuleDef module) { + { + int index = 0; + for (Script script : module.getScripts()) { + String url = script.getSrc(); + artifacts.add(new StandardScriptReference(url, index++)); + logger.log(TreeLogger.SPAM, "Added script " + url, null); } } - } - - 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(); + + { + int index = 0; + for (String style : module.getStyles()) { + artifacts.add(new StandardStylesheetReference(style, index++)); + logger.log(TreeLogger.SPAM, "Added style " + style, null); + } } } } ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/DevMode.java Fri Oct 16 20:54:44 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/DevMode.java Tue Oct 27 18:11:59 2009 @@ -28,6 +28,9 @@ import com.google.gwt.dev.ui.RestartServerCallback; import com.google.gwt.dev.ui.RestartServerEvent; import com.google.gwt.dev.util.InstalledHelpInfo; +import com.google.gwt.dev.util.NullOutputFileSet; +import com.google.gwt.dev.util.OutputFileSet; +import com.google.gwt.dev.util.OutputFileSetOnDirectory; import com.google.gwt.dev.util.Util; import com.google.gwt.dev.util.arg.ArgHandlerExtraDir; import com.google.gwt.dev.util.arg.ArgHandlerLocalWorkers; @@ -486,11 +489,26 @@ private void produceOutput(TreeLogger logger, StandardLinkerContext linkerStack, ArtifactSet artifacts, ModuleDef module) throws UnableToCompleteException { - File moduleOutDir = new File(options.getWarDir(), module.getName()); - linkerStack.produceOutputDirectory(logger, artifacts, moduleOutDir); - if (options.getExtraDir() != null) { - File moduleExtraDir = new File(options.getExtraDir(), module.getName()); - linkerStack.produceExtraDirectory(logger, artifacts, moduleExtraDir); + TreeLogger linkLogger = logger.branch(TreeLogger.DEBUG, "Linking module '" + + module.getName() + "'"); + + try { + OutputFileSetOnDirectory outFileSet = new OutputFileSetOnDirectory( + options.getWarDir(), module.getName() + "/"); + OutputFileSet extraFileSet = new NullOutputFileSet(); + if (options.getExtraDir() != null) { + extraFileSet = new OutputFileSetOnDirectory(options.getExtraDir(), + module.getName() + "/"); + } + + linkerStack.produceOutput(linkLogger, artifacts, false, outFileSet); + linkerStack.produceOutput(linkLogger, artifacts, true, extraFileSet); + + outFileSet.close(); + extraFileSet.close(); + } catch (IOException e) { + linkLogger.log(TreeLogger.ERROR, "I/O exception", e); + throw new UnableToCompleteException(); } } ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/Link.java Mon Aug 17 09:47:12 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/Link.java Tue Oct 27 18:11:59 2009 @@ -30,6 +30,10 @@ 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.NullOutputFileSet; +import com.google.gwt.dev.util.OutputFileSet; +import com.google.gwt.dev.util.OutputFileSetOnDirectory; +import com.google.gwt.dev.util.OutputFileSetOnJar; import com.google.gwt.dev.util.Util; import com.google.gwt.dev.util.arg.ArgHandlerExtraDir; import com.google.gwt.dev.util.arg.ArgHandlerWarDir; @@ -86,8 +90,8 @@ LinkOptions { private File extraDir; - private File warDir; private File outDir; + private File warDir; public LinkOptionsImpl() { } @@ -133,24 +137,31 @@ public static void legacyLink(TreeLogger logger, ModuleDef module, ArtifactSet generatedArtifacts, Permutation[] permutations, List<FileBackedObject<PermutationResult>> resultFiles, File outDir, - JJSOptions precompileOptions) throws UnableToCompleteException { + JJSOptions precompileOptions) throws UnableToCompleteException, + IOException { StandardLinkerContext linkerContext = new StandardLinkerContext(logger, module, precompileOptions); ArtifactSet artifacts = doLink(logger, linkerContext, generatedArtifacts, permutations, resultFiles); - doProduceLegacyOutput(logger, artifacts, linkerContext, module, outDir); + OutputFileSet outFileSet = new OutputFileSetOnDirectory(outDir, + module.getName() + "/"); + OutputFileSet extraFileSet = new OutputFileSetOnDirectory(outDir, + module.getName() + "-aux/"); + doProduceOutput(logger, artifacts, linkerContext, outFileSet, extraFileSet); } public static void link(TreeLogger logger, ModuleDef module, ArtifactSet generatedArtifacts, Permutation[] permutations, List<FileBackedObject<PermutationResult>> resultFiles, File outDir, File extrasDir, JJSOptions precompileOptions) - throws UnableToCompleteException { + throws UnableToCompleteException, IOException { StandardLinkerContext linkerContext = new StandardLinkerContext(logger, module, precompileOptions); ArtifactSet artifacts = doLink(logger, linkerContext, generatedArtifacts, permutations, resultFiles); - doProduceOutput(logger, artifacts, linkerContext, module, outDir, extrasDir); + doProduceOutput(logger, artifacts, linkerContext, chooseOutputFileSet( + outDir, module.getName() + "/"), chooseOutputFileSet(extrasDir, + module.getName() + "/")); } public static void main(String[] args) { @@ -176,6 +187,37 @@ // Exit w/ non-success code. System.exit(1); } + + /** + * Choose an output file set for the given <code>dirOrJar</code> based on + * its name, whether it's null, and whether it already exists as a directory. + */ + private static OutputFileSet chooseOutputFileSet(File dirOrJar, + String pathPrefix) throws IOException { + return chooseOutputFileSet(dirOrJar, pathPrefix, pathPrefix); + } + + /** + * A version of {...@link #chooseOutputFileSet(File, String)} that allows + * choosing a separate path prefix depending on whether the output is a + * directory or a jar file. + */ + private static OutputFileSet chooseOutputFileSet(File dirOrJar, + String jarPathPrefix, String dirPathPrefix) throws IOException { + + if (dirOrJar == null) { + return new NullOutputFileSet(); + } + + String name = dirOrJar.getName(); + if (!dirOrJar.isDirectory() + && (name.endsWith(".war") || name.endsWith(".jar") || name.endsWith(".zip"))) { + return new OutputFileSetOnJar(dirOrJar, jarPathPrefix); + } else { + Util.recursiveDelete(new File(dirOrJar, dirPathPrefix), true); + return new OutputFileSetOnDirectory(dirOrJar, dirPathPrefix); + } + } private static ArtifactSet doLink(TreeLogger logger, StandardLinkerContext linkerContext, ArtifactSet generatedArtifacts, @@ -194,52 +236,26 @@ 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"); - } - + /** + * Emit final output. + */ 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); - } - } + StandardLinkerContext linkerContext, OutputFileSet outFileSet, + OutputFileSet extraFileSet) throws UnableToCompleteException, IOException { + linkerContext.produceOutput(logger, artifacts, false, outFileSet); + linkerContext.produceOutput(logger, artifacts, true, extraFileSet); + + outFileSet.close(); + extraFileSet.close(); + 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); + PermutationResult permutationResult = resultFile.newInstance(logger); + StandardCompilationResult compilation = linkerContext.getCompilation(permutationResult); StaticPropertyOracle[] propOracles = perm.getPropertyOracles(); for (StaticPropertyOracle propOracle : propOracles) { BindingProperty[] orderedProps = propOracle.getOrderedProps(); @@ -311,6 +327,35 @@ } ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName); + + OutputFileSet outFileSet; + OutputFileSet extraFileSet; + try { + if (options.getOutDir() == null) { + outFileSet = chooseOutputFileSet(options.getWarDir(), + module.getName() + "/"); + extraFileSet = chooseOutputFileSet(options.getExtraDir(), + module.getName() + "/"); + } else { + outFileSet = chooseOutputFileSet(options.getOutDir(), + module.getName() + "/"); + if (options.getExtraDir() != null) { + extraFileSet = chooseOutputFileSet(options.getExtraDir(), + module.getName() + "-aux/", ""); + } else if (outFileSet instanceof OutputFileSetOnDirectory) { + // Automatically emit extras into the output directory, if it's in + // fact a directory + extraFileSet = chooseOutputFileSet(options.getOutDir(), + module.getName() + "-aux/"); + } else { + extraFileSet = new NullOutputFileSet(); + } + } + } catch (IOException e) { + logger.log(TreeLogger.ERROR, + "Unexpected exception while producing output", e); + throw new UnableToCompleteException(); + } if (precomps.isEmpty()) { logger.log(TreeLogger.ERROR, "No precompilation files found in '" @@ -359,12 +404,12 @@ 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()); + try { + doProduceOutput(branch, artifacts, linkerContext, outFileSet, + extraFileSet); + } catch (IOException e) { + logger.log(TreeLogger.ERROR, + "Unexpected exception while producing output", e); } } return true; --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
