This patch modifies the compiler in two ways:
1. It can shard out precompiles during parallel builds.
2. Linkers can indicate which part should happen per-shard and which part should
happen on the final link node.
http://gwt-code-reviews.appspot.com/141811
Review by: cromwellian
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7650 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/Linker.java b/dev/core/src/com/google/gwt/core/ext/Linker.java
index 5dc6ff1..55e49d5 100644
--- a/dev/core/src/com/google/gwt/core/ext/Linker.java
+++ b/dev/core/src/com/google/gwt/core/ext/Linker.java
@@ -16,23 +16,34 @@
package com.google.gwt.core.ext;
import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.Shardable;
/**
* Defines a linker for the GWT compiler. Each Linker must be annotated with a
* {@link com.google.gwt.core.ext.linker.LinkerOrder} annotation to determine
* the relative ordering of the Linkers. Exact order of Linker execution will be
* determined by the order of <code>add-linker</code> tags in the module
- * configuration.
+ * configuration. Each Linker should also be annotated with {@link Shardable};
+ * non-shardable linkers are deprecated and will eventually not be supported.
*
* <p>
* A new instance of a linker is created each time a module is compiled or
* during hosted mode when a module first loads (or is refreshed). During a
- * compile, {@link #link(TreeLogger, LinkerContext, ArtifactSet)} will be called
- * exactly once, and the artifact set will contain any and all generated
- * artifacts. . In hosted mode,
- * {@link #link(TreeLogger, LinkerContext, ArtifactSet)} is called initially,
- * but with no generated artifacts. If any artifacts are subsequently generated
- * during the course of running hosted mode,
+ * compile, {@link #link(TreeLogger, LinkerContext, ArtifactSet)} is called
+ * exactly once on each non-shardable linker, and the artifact set will contain
+ * any and all generated artifacts. For shardable linkers,
+ * {@link #link(TreeLogger, LinkerContext, ArtifactSet, boolean)} is called once
+ * for each compiled permutation and once after all compiles are finished. The
+ * precise artifacts supplied differ with each call and are described in the
+ * method's documentation.
+ *
+ * <p>
+ * When hosted mode starts for a module, it calls
+ * {@link #link(TreeLogger, LinkerContext, ArtifactSet)} for non-shardable
+ * linkers and {@link #link(TreeLogger, LinkerContext, ArtifactSet, boolean)}
+ * for shardable ones, passing <code>false</code> as the
+ * <code>onePermutation</code> argument. If any artifacts are subsequently
+ * generated during the course of running hosted mode,
* {@link #relink(TreeLogger, LinkerContext, ArtifactSet)} will be called with
* the new artifacts.
* </p>
@@ -44,7 +55,17 @@
public abstract String getDescription();
/**
- * Invoke the Linker.
+ * Check whether this class has the {@link Shardable} annotation.
+ */
+ public final boolean isShardable() {
+ return getClass().isAnnotationPresent(Shardable.class);
+ }
+
+ /**
+ * This method is invoked for linkers not annotated with {@link Shardable}. It
+ * sees all artifacts across the whole compile and can modify them
+ * arbitrarily. This method is only called if the linker is not annotated with
+ * {@link Shardable}.
*
* @param logger the TreeLogger to record to
* @param context provides access to the Linker's environment
@@ -53,8 +74,44 @@
* @throws UnableToCompleteException if compilation violates assumptions made
* by the Linker or for errors encountered by the Linker
*/
- public abstract ArtifactSet link(TreeLogger logger, LinkerContext context,
- ArtifactSet artifacts) throws UnableToCompleteException;
+ public ArtifactSet link(TreeLogger logger, LinkerContext context,
+ ArtifactSet artifacts) throws UnableToCompleteException {
+ assert !isShardable();
+ return artifacts;
+ }
+
+ /**
+ * <p>
+ * This method is invoked for linkers annotated with {@link Shardable}. It is
+ * called at two points during compilation: after the compile of each
+ * permutation, and after all compilation has finished. The
+ * <code>onePermutation</code> is <code>true</code> for a per-permutation call
+ * and <code>false</code> for a global final-link call.
+ *
+ * <p>
+ * For one-permutation calls, this method is passed all artifacts generated
+ * for just the one permutation. For the global call at the end of
+ * compilation, this method sees artifacts for the whole compilation, but with
+ * two modifications intended to support builds on computer clusters:
+ * <ol>
+ * <li>All EmittedArtifacts have been converted to BinaryEmittedArtifacts
+ * <li>All artifacts not marked as
+ * {@link com.google.gwt.core.ext.linker.Transferable} have been discarded.
+ * </ol>
+ *
+ * @param logger the TreeLogger to record to
+ * @param context provides access to the Linker's environment
+ * @param artifacts an unmodifiable view of the artifacts to link
+ * @return the artifacts that should be propagated through the linker chain
+ * @throws UnableToCompleteException if compilation violates assumptions made
+ * by the Linker or for errors encountered by the Linker
+ */
+ public ArtifactSet link(TreeLogger logger, LinkerContext context,
+ ArtifactSet artifacts, boolean onePermutation)
+ throws UnableToCompleteException {
+ assert isShardable();
+ return artifacts;
+ }
/**
* Re-invoke the Linker with newly generated artifacts. Linkers that need to
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java b/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java
index a54eaea..1e715f5 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java
@@ -30,8 +30,8 @@
*/
public abstract class Artifact<C extends Artifact<C>> implements
Comparable<Artifact<?>>, Serializable {
- private final String linkerName;
private transient Class<? extends Linker> linker;
+ private final String linkerName;
/**
* Constructor.
@@ -91,6 +91,14 @@
@Override
public abstract int hashCode();
+ /**
+ * Returns whether the {@link Transferable} annotation is present on this
+ * class. See {@link Transferable} for the implications.
+ */
+ public final boolean isTransferableFromShards() {
+ return getClass().isAnnotationPresent(Transferable.class);
+ }
+
@Override
public String toString() {
return getClass().getName() + " created by " + getLinker().getName();
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/BinaryEmittedArtifact.java b/dev/core/src/com/google/gwt/core/ext/linker/BinaryEmittedArtifact.java
new file mode 100644
index 0000000..1828b1f
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/BinaryEmittedArtifact.java
@@ -0,0 +1,34 @@
+/*
+ * 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.core.ext.linker;
+
+import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
+
+/**
+ * <p>
+ * A thinned down version of some {@link EmittedArtifact}. Only its essentials,
+ * including name and contents, are available.
+ * </p>
+ *
+ * <p>
+ * This class should only be extended within the GWT implementation.
+ * </p>
+ */
+public abstract class BinaryEmittedArtifact extends EmittedArtifact {
+ protected BinaryEmittedArtifact(String partialPath) {
+ super(StandardLinkerContext.class, partialPath);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java b/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
index 301c1ef..7ce343a 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
@@ -16,6 +16,7 @@
package com.google.gwt.core.ext.linker;
import com.google.gwt.core.ext.Linker;
+import com.google.gwt.dev.Permutation;
import java.util.Map;
import java.util.SortedMap;
@@ -26,8 +27,12 @@
* result in identical JavaScript.
*/
public abstract class CompilationResult extends Artifact<CompilationResult> {
- protected CompilationResult(Class<? extends Linker> linkerType) {
+ private final Permutation permutation;
+
+ protected CompilationResult(Class<? extends Linker> linkerType,
+ Permutation permutation) {
super(linkerType);
+ this.permutation = permutation;
}
/**
@@ -35,17 +40,27 @@
* the code that should be run when the application starts up. The remaining
* elements are loaded via
* {@link com.google.gwt.core.client.GWT#runAsync(com.google.gwt.core.client.RunAsyncCallback)
- * GWT.runAsync}. See {@link com.google.gwt.core.client.impl.AsyncFragmentLoader
+ * GWT.runAsync}. See
+ * {@link com.google.gwt.core.client.impl.AsyncFragmentLoader
* AsyncFragmentLoader} for details on the necessary linker support for
* runAsync.
*/
public abstract String[] getJavaScript();
/**
+ * Return the permutation that compiled to this result.
+ */
+ public Permutation getPermutation() {
+ return permutation;
+ }
+
+ /**
* Returns the permutation ID.
*/
- public abstract int getPermutationId();
-
+ public int getPermutationId() {
+ return getPermutation().getId();
+ }
+
/**
* Provides values for {@link SelectionProperty} instances that are not
* explicitly set during the compilation phase. This method will return
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/Shardable.java b/dev/core/src/com/google/gwt/core/ext/linker/Shardable.java
new file mode 100644
index 0000000..03921d0
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/Shardable.java
@@ -0,0 +1,37 @@
+/*
+ * 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.core.ext.linker;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation, when placed on a {@link com.google.gwt.core.ext.Linker}
+ * class, indicates that the linker supports the shardable version of the Linker
+ * API. Specifically, it implements
+ * {@link com.google.gwt.core.ext.Linker#link(com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.LinkerContext, ArtifactSet, boolean)}
+ * rather than
+ * {@link com.google.gwt.core.ext.Linker#link(com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.LinkerContext, ArtifactSet)}
+ * .
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Shardable {
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/Transferable.java b/dev/core/src/com/google/gwt/core/ext/linker/Transferable.java
new file mode 100644
index 0000000..1c50432
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/Transferable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.core.ext.linker;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An artifact marked with this annotation is passed through a compilation all
+ * the way to the final call to
+ * {@link com.google.gwt.core.ext.Linker#link(com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.LinkerContext, ArtifactSet, boolean)}
+ * . Try to minimize the number of artifacts so marked.
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Transferable {
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/BinaryOnlyArtifactWrapper.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/BinaryOnlyArtifactWrapper.java
new file mode 100644
index 0000000..3cf6fca
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/BinaryOnlyArtifactWrapper.java
@@ -0,0 +1,43 @@
+/*
+ * 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.core.ext.linker.impl;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.BinaryEmittedArtifact;
+import com.google.gwt.core.ext.linker.EmittedArtifact;
+
+import java.io.InputStream;
+
+/**
+ * A wrapper around an emitted artifact that only allows reading the artifact's
+ * path and its binary contents.
+ */
+public class BinaryOnlyArtifactWrapper extends BinaryEmittedArtifact {
+ private final EmittedArtifact underlyingArtifact;
+
+ public BinaryOnlyArtifactWrapper(String path, EmittedArtifact artifact) {
+ super(path);
+ setPrivate(artifact.isPrivate());
+ this.underlyingArtifact = artifact;
+ }
+
+ @Override
+ public InputStream getContents(TreeLogger logger)
+ throws UnableToCompleteException {
+ return underlyingArtifact.getContents(logger);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java
index d3e8f22..ced3eee 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java
@@ -19,6 +19,7 @@
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.Shardable;
import com.google.gwt.util.tools.Utility;
import java.io.IOException;
@@ -27,6 +28,7 @@
* This is a partial implementation of the Linker interface to support hosted
* mode.
*/
+@Shardable
public final class HostedModeLinker extends SelectionScriptLinker {
public static String getHostedHtml() throws IOException {
@@ -74,8 +76,7 @@
}
@Override
- protected String getSelectionScriptTemplate(TreeLogger logger,
- LinkerContext context) throws UnableToCompleteException {
+ protected String getSelectionScriptTemplate(TreeLogger logger, LinkerContext context) {
return "com/google/gwt/core/ext/linker/impl/HostedModeTemplate.js";
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/JarEntryEmittedArtifact.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/JarEntryEmittedArtifact.java
new file mode 100644
index 0000000..1c1885e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/JarEntryEmittedArtifact.java
@@ -0,0 +1,86 @@
+/*
+ * 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.core.ext.linker.impl;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.BinaryEmittedArtifact;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ *A <code>BinaryEmittedArtifact</code> that reads a jar entry.
+ */
+public class JarEntryEmittedArtifact extends BinaryEmittedArtifact {
+ /**
+ * An input stream that has its own open {@link JarFile}. When this stream is
+ * closed, the {@link JarFile} is as well.
+ */
+ private class JarEntryInputStream extends InputStream {
+ private JarFile jarFile;
+ private InputStream stream;
+
+ public JarEntryInputStream() throws IOException {
+ this.jarFile = new JarFile(file);
+ this.stream = jarFile.getInputStream(entry);
+ }
+
+ @Override
+ public void close() throws IOException {
+ stream.close();
+ jarFile.close();
+ }
+
+ @Override
+ public int read() throws IOException {
+ return stream.read();
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ return stream.read(b);
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return stream.read(b, off, len);
+ }
+ }
+
+ private final JarEntry entry;
+ private final File file;
+
+ public JarEntryEmittedArtifact(String path, File jarFile, JarEntry entry) {
+ super(path);
+ this.file = jarFile;
+ this.entry = entry;
+ }
+
+ @Override
+ public InputStream getContents(TreeLogger logger)
+ throws UnableToCompleteException {
+ try {
+ return new JarEntryInputStream();
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "unexpected IOException", e);
+ throw new UnableToCompleteException();
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionInformation.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionInformation.java
new file mode 100644
index 0000000..f0980a6
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionInformation.java
@@ -0,0 +1,95 @@
+/*
+ * 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.core.ext.linker.impl;
+
+import com.google.gwt.core.ext.linker.Artifact;
+import com.google.gwt.core.ext.linker.Transferable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ * Used by {@link SelectionScriptLinker} to hold selection information about an
+ * individual compiled permutation. The linker generates one instance of this
+ * class per permutation and then accumulates them in the final link, where it
+ * generates a selection script.
+ */
+@Transferable
+public class SelectionInformation extends Artifact<SelectionInformation> {
+ private final int hashCode;
+ private final TreeMap<String, String> propMap;
+ private final String strongName;
+
+ public SelectionInformation(String strongName, TreeMap<String, String> propMap) {
+ super(SelectionScriptLinker.class);
+ this.strongName = strongName;
+ this.propMap = propMap;
+ hashCode = strongName.hashCode() + propMap.hashCode() * 17 + 11;
+ }
+
+ public TreeMap<String, String> getPropMap() {
+ return propMap;
+ }
+
+ public String getStrongName() {
+ return strongName;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ protected int compareToComparableArtifact(SelectionInformation o) {
+ // compare the strong names
+ int cmp = getStrongName().compareTo(o.getStrongName());
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ // compare the size of the property maps
+ if (getPropMap().size() != o.getPropMap().size()) {
+ return getPropMap().size() - o.getPropMap().size();
+ }
+
+ // compare the key sets of the property maps
+ List<String> myKeys = new ArrayList<String>(getPropMap().keySet());
+ List<String> oKeys = new ArrayList<String>(o.getPropMap().keySet());
+ for (int i = 0; i < myKeys.size(); i++) {
+ cmp = myKeys.get(i).compareTo(oKeys.get(i));
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+
+ // compare the property map values
+ for (String key : getPropMap().keySet()) {
+ cmp = getPropMap().get(key).compareTo(o.getPropMap().get(key));
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+
+ return 0;
+ }
+
+ @Override
+ protected Class<SelectionInformation> getComparableArtifactType() {
+ return SelectionInformation.class;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
index 5278b84..c5d332a 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
@@ -19,13 +19,18 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.AbstractLinker;
+import com.google.gwt.core.ext.linker.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationResult;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.ScriptReference;
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.StylesheetReference;
+import com.google.gwt.dev.cfg.BindingProperty;
+import com.google.gwt.dev.cfg.StaticPropertyOracle;
import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.collect.HashSet;
+import com.google.gwt.dev.util.collect.Lists;
import com.google.gwt.util.tools.Utility;
import java.io.File;
@@ -34,10 +39,11 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
+import java.util.List;
import java.util.Map;
-import java.util.SortedSet;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
/**
* A base class for Linkers that use an external script to boostrap the GWT
@@ -66,7 +72,6 @@
* @param src the test url
* @return <code>true</code> if the URL is relative, <code>false</code> if not
*/
- @SuppressWarnings("unused")
protected static boolean isRelativeURL(String src) {
// A straight absolute url for the same domain, server, and protocol.
if (src.startsWith("/")) {
@@ -97,22 +102,50 @@
}
}
- private final Map<CompilationResult, String> compilationStrongNames = new IdentityHashMap<CompilationResult, String>();
+ /**
+ * This maps each compilation strong name to the property settings for that
+ * compilation. A single compilation can have multiple property settings if
+ * the compiles for those settings yielded the exact same compiled output.
+ */
+ private final SortedMap<String, List<Map<String, String>>> propMapsByStrongName = new TreeMap<String, List<Map<String, String>>>();
+ /**
+ * This method is left in place for existing subclasses of
+ * SelectionScriptLinker that have not been upgraded for the sharding API.
+ */
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
ArtifactSet artifacts) throws UnableToCompleteException {
- ArtifactSet toReturn = new ArtifactSet(artifacts);
-
- for (CompilationResult compilation : toReturn.find(CompilationResult.class)) {
- toReturn.addAll(doEmitCompilation(logger, context, compilation));
- }
-
- toReturn.add(emitSelectionScript(logger, context, artifacts));
+ ArtifactSet toReturn = link(logger, context, artifacts, true);
+ toReturn = link(logger, context, artifacts, false);
return toReturn;
}
- protected Collection<EmittedArtifact> doEmitCompilation(TreeLogger logger,
+ @Override
+ public ArtifactSet link(TreeLogger logger, LinkerContext context,
+ ArtifactSet artifacts, boolean onePermutation)
+ throws UnableToCompleteException {
+ if (onePermutation) {
+ ArtifactSet toReturn = new ArtifactSet(artifacts);
+
+ /*
+ * Support having multiple compilation results because this method is also
+ * called from the legacy link method.
+ */
+ for (CompilationResult compilation : toReturn.find(CompilationResult.class)) {
+ toReturn.addAll(doEmitCompilation(logger, context, compilation));
+ }
+ return toReturn;
+ } else {
+ processSelectionInformation(artifacts);
+
+ ArtifactSet toReturn = new ArtifactSet(artifacts);
+ toReturn.add(emitSelectionScript(logger, context, artifacts));
+ return toReturn;
+ }
+ }
+
+ protected Collection<Artifact<?>> doEmitCompilation(TreeLogger logger,
LinkerContext context, CompilationResult result)
throws UnableToCompleteException {
String[] js = result.getJavaScript();
@@ -122,7 +155,7 @@
bytes[i] = Util.getBytes(js[i]);
}
- Collection<EmittedArtifact> toReturn = new ArrayList<EmittedArtifact>();
+ Collection<Artifact<?>> toReturn = new ArrayList<Artifact<?>>();
toReturn.add(emitBytes(logger, bytes[0], result.getStrongName()
+ getCompilationExtension(logger, context)));
for (int i = 1; i < js.length; i++) {
@@ -130,7 +163,8 @@
+ result.getStrongName() + File.separator + i + FRAGMENT_EXTENSION));
}
- compilationStrongNames.put(result, result.getStrongName());
+ toReturn.addAll(emitSelectionInformation(result.getPermutation().getId(),
+ result.getStrongName(), result.getPermutation().getPropertyOracles()));
return toReturn;
}
@@ -217,15 +251,17 @@
}
}
+ /**
+ * Generate a selection script. The selection information should previously
+ * have been scanned using {@link #processSelectionInformation(ArtifactSet)}.
+ */
protected String generateSelectionScript(TreeLogger logger,
LinkerContext context, ArtifactSet artifacts)
throws UnableToCompleteException {
-
StringBuffer selectionScript;
try {
selectionScript = new StringBuffer(
- Utility.getFileFromClassPath(getSelectionScriptTemplate(logger,
- context)));
+ Utility.getFileFromClassPath(getSelectionScriptTemplate(logger, context)));
} catch (IOException e) {
logger.log(TreeLogger.ERROR, "Unable to read selection script template",
e);
@@ -268,31 +304,30 @@
}
// Possibly add permutations
- SortedSet<CompilationResult> compilations = artifacts.find(CompilationResult.class);
startPos = selectionScript.indexOf("// __PERMUTATIONS_END__");
if (startPos != -1) {
StringBuffer text = new StringBuffer();
- if (compilations.size() == 0) {
+ if (propMapsByStrongName.size() == 0) {
// Hosted mode link.
- text.append("alert(\"GWT module '"
- + context.getModuleName()
+ text.append("alert(\"GWT module '" + context.getModuleName()
+ "' may need to be (re)compiled\");");
text.append("return;");
- } else if (compilations.size() == 1) {
+ } else if (propMapsByStrongName.size() == 1) {
// Just one distinct compilation; no need to evaluate properties
- Iterator<CompilationResult> iter = compilations.iterator();
- CompilationResult result = iter.next();
- text.append("strongName = '" + compilationStrongNames.get(result)
- + "';");
+ text.append("strongName = '"
+ + propMapsByStrongName.keySet().iterator().next() + "';");
} else {
- for (CompilationResult r : compilations) {
- for (Map<SelectionProperty, String> propertyMap : r.getPropertyMap()) {
+ Set<String> propertiesUsed = new HashSet<String>();
+ for (String strongName : propMapsByStrongName.keySet()) {
+ for (Map<String, String> propertyMap : propMapsByStrongName.get(strongName)) {
// unflatten([v1, v2, v3], 'strongName');
text.append("unflattenKeylistIntoAnswers([");
boolean needsComma = false;
for (SelectionProperty p : context.getProperties()) {
- if (!propertyMap.containsKey(p)) {
+ if (p.tryGetValue() != null) {
+ continue;
+ } else if (p.isDerived()) {
continue;
}
@@ -301,10 +336,10 @@
} else {
needsComma = true;
}
- text.append("'" + propertyMap.get(p) + "'");
+ text.append("'" + propertyMap.get(p.getName()) + "'");
+ propertiesUsed.add(p.getName());
}
- text.append("], '").append(compilationStrongNames.get(r)).append(
- "');\n");
+ text.append("], '").append(strongName).append("');\n");
}
}
@@ -312,9 +347,7 @@
text.append("strongName = answers[");
boolean needsIndexMarkers = false;
for (SelectionProperty p : context.getProperties()) {
- if (p.tryGetValue() != null) {
- continue;
- } else if (p.isDerived()) {
+ if (!propertiesUsed.contains(p.getName())) {
continue;
}
if (needsIndexMarkers) {
@@ -364,16 +397,6 @@
LinkerContext context) throws UnableToCompleteException;
/**
- * Get the partial path on which a CompilationResult has been emitted.
- *
- * @return the partial path, or <code>null</code> if the CompilationResult has
- * not been emitted.
- */
- protected String getCompilationStrongName(CompilationResult result) {
- return compilationStrongNames.get(result);
- }
-
- /**
* Compute the beginning of a JavaScript file that will hold the main module
* implementation.
*/
@@ -398,6 +421,49 @@
protected abstract String getModuleSuffix(TreeLogger logger,
LinkerContext context) throws UnableToCompleteException;
- protected abstract String getSelectionScriptTemplate(TreeLogger logger,
- LinkerContext context) throws UnableToCompleteException;
+ protected abstract String getSelectionScriptTemplate(TreeLogger logger, LinkerContext context)
+ throws UnableToCompleteException;
+
+ /**
+ * Find all instances of {@link SelectionInformation} and add them to the
+ * internal map of selection information.
+ */
+ protected void processSelectionInformation(ArtifactSet artifacts) {
+ for (SelectionInformation selInfo : artifacts.find(SelectionInformation.class)) {
+ processSelectionInformation(selInfo);
+ }
+ }
+
+ private List<Artifact<?>> emitSelectionInformation(int id, String strongName,
+ StaticPropertyOracle[] staticPropertyOracles) {
+ List<Artifact<?>> emitted = new ArrayList<Artifact<?>>();
+
+ for (int propOracleId = 0; propOracleId < staticPropertyOracles.length; propOracleId++) {
+ StaticPropertyOracle propOracle = staticPropertyOracles[propOracleId];
+ TreeMap<String, String> propMap = new TreeMap<String, String>();
+
+ BindingProperty[] orderedProps = propOracle.getOrderedProps();
+ String[] orderedPropValues = propOracle.getOrderedPropValues();
+ for (int i = 0; i < orderedProps.length; i++) {
+ propMap.put(orderedProps[i].getName(), orderedPropValues[i]);
+ }
+ emitted.add(new SelectionInformation(strongName, propMap));
+ }
+
+ return emitted;
+ }
+
+ private Map<String, String> processSelectionInformation(
+ SelectionInformation selInfo) {
+ TreeMap<String, String> entries = selInfo.getPropMap();
+ String strongName = selInfo.getStrongName();
+ if (!propMapsByStrongName.containsKey(strongName)) {
+ propMapsByStrongName.put(strongName,
+ Lists.<Map<String, String>> create(entries));
+ } else {
+ propMapsByStrongName.put(strongName, Lists.add(
+ propMapsByStrongName.get(strongName), entries));
+ }
+ return entries;
+ }
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
index 131eed6..b80f8bb 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
@@ -19,7 +19,10 @@
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.StatementRanges;
import com.google.gwt.core.ext.linker.SymbolData;
+import com.google.gwt.dev.Permutation;
+import com.google.gwt.dev.jjs.PermutationResult;
import com.google.gwt.dev.util.DiskCache;
+import com.google.gwt.dev.util.Util;
import java.io.Serializable;
import java.util.Collections;
@@ -71,8 +74,6 @@
private static final DiskCache diskCache = new DiskCache();
private final long jsToken[];
-
- private final int permutationId;
private final SortedSet<SortedMap<SelectionProperty, String>> propertyValues = new TreeSet<SortedMap<SelectionProperty, String>>(
MAP_COMPARATOR);
@@ -83,17 +84,18 @@
private final long symbolToken;
- public StandardCompilationResult(String strongName, byte[][] js,
- byte[] serializedSymbolMap, StatementRanges[] statementRanges, int permutationId) {
- super(StandardLinkerContext.class);
- this.strongName = strongName;
+ public StandardCompilationResult(PermutationResult permutationResult) {
+ super(StandardLinkerContext.class, permutationResult.getPermutation());
+ byte[][] js = permutationResult.getJs();
+ strongName = Util.computeStrongName(js);
+ byte[] serializedSymbolMap = permutationResult.getSerializedSymbolMap();
+ statementRanges = permutationResult.getStatementRanges();
+ Permutation permutation = permutationResult.getPermutation();
jsToken = new long[js.length];
for (int i = 0; i < jsToken.length; ++i) {
jsToken[i] = diskCache.writeByteArray(js[i]);
}
symbolToken = diskCache.writeByteArray(serializedSymbolMap);
- this.statementRanges = statementRanges;
- this.permutationId = permutationId;
}
/**
@@ -117,11 +119,6 @@
}
@Override
- public int getPermutationId() {
- return permutationId;
- }
-
- @Override
public SortedSet<SortedMap<SelectionProperty, String>> getPropertyMap() {
return Collections.unmodifiableSortedSet(propertyValues);
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
index 52c7ccc..16c8091 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
@@ -32,7 +32,6 @@
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;
@@ -52,7 +51,6 @@
import com.google.gwt.dev.js.ast.JsScope;
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.dev.util.OutputFileSet;
-import com.google.gwt.dev.util.Util;
import java.io.File;
import java.io.IOException;
@@ -111,21 +109,20 @@
}
};
- 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 JJSOptions jjsOptions;
+
+ private final List<Class<? extends Linker>> linkerClasses;
+ private Linker[] linkers;
+ 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;
+ private final SortedSet<SelectionProperty> selectionProperties;
public StandardLinkerContext(TreeLogger logger, ModuleDef module,
JJSOptions jjsOptions) throws UnableToCompleteException {
@@ -138,24 +135,26 @@
this.moduleLastModified = module.lastModified();
// Sort the linkers into the order they should actually run.
- List<Class<? extends Linker>> sortedLinkers = new ArrayList<Class<? extends Linker>>();
+ linkerClasses = 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);
+ linkerClasses.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?");
+ logger.log(
+ TreeLogger.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());
+ linkerClasses.add(module.getActivePrimaryLinker());
}
// Get all the post-linkers IN REVERSE ORDER.
@@ -169,22 +168,10 @@
}
}
Collections.reverse(postLinkerClasses);
- sortedLinkers.addAll(postLinkerClasses);
+ linkerClasses.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();
- }
- }
+ resetLinkers(logger);
for (Map.Entry<String, Class<? extends Linker>> entry : module.getLinkers().entrySet()) {
linkerShortNames.put(entry.getValue(), entry.getKey());
@@ -225,10 +212,33 @@
selectionProperties = Collections.unmodifiableSortedSet(mutableSelectionProperties);
configurationProperties = Collections.unmodifiableSortedSet(mutableConfigurationProperties);
}
+ }
- /*
- * Add static resources in the specified module as artifacts.
- */
+ public boolean allLinkersAreShardable() {
+ return findUnshardableLinkers().isEmpty();
+ }
+
+ /**
+ * Find all linkers that are not updated to support running generators on
+ * compilations shards.
+ */
+ public List<Linker> findUnshardableLinkers() {
+ List<Linker> problemLinkers = new ArrayList<Linker>();
+
+ for (Linker linker : linkers) {
+ if (!linker.isShardable()) {
+ problemLinkers.add(linker);
+ }
+ }
+ return problemLinkers;
+ }
+
+ /**
+ * Convert all static resources in the specified module to artifacts.
+ */
+ public ArtifactSet getArtifactsForPublicResources(TreeLogger logger,
+ ModuleDef module) {
+ ArtifactSet artifacts = new ArtifactSet();
for (String path : module.getAllPublicFiles()) {
String partialPath = path.replace(File.separatorChar, '/');
PublicResource resource = new StandardPublicResource(partialPath,
@@ -237,43 +247,23 @@
logger.log(TreeLogger.SPAM, "Added public resource " + resource, null);
}
- recordStaticReferences(logger, module);
- }
-
- /**
- * 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(
- 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.getPermutation().getId());
- resultsByStrongName.put(result.getStrongName(), result);
- artifacts.add(result);
+ {
+ 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);
+ }
}
- artifacts.addAll(permutationResult.getArtifacts());
- return result;
+
+ {
+ int index = 0;
+ for (String style : module.getStyles()) {
+ artifacts.add(new StandardStylesheetReference(style, index++));
+ logger.log(TreeLogger.SPAM, "Added style " + style, null);
+ }
+ }
+ return artifacts;
}
public SortedSet<ConfigurationProperty> getConfigurationProperties() {
@@ -285,6 +275,19 @@
return "Root Linker";
}
+ /**
+ * Return the full path for an artifact produced by <code>linkertype</code>
+ * that has the specified partial path. The full path will be the linker's
+ * short name, as defined in the module file, followed by the given partial
+ * path.
+ */
+ public String getExtraPathForLinker(Class<? extends Linker> linkerType,
+ String partialPath) {
+ assert linkerShortNames.containsKey(linkerType) : linkerType.getName()
+ + " unknown";
+ return linkerShortNames.get(linkerType) + '/' + partialPath;
+ }
+
public String getModuleFunctionName() {
return moduleFunctionName;
}
@@ -305,27 +308,75 @@
return propertiesByName.get(name);
}
- /**
- * Run the linker stack.
- */
- public ArtifactSet invokeLink(TreeLogger logger)
+ public ArtifactSet invokeFinalLink(TreeLogger logger, ArtifactSet artifacts)
throws UnableToCompleteException {
+ for (Linker linker : linkers) {
+ if (linker.isShardable()) {
+ TreeLogger linkerLogger = logger.branch(TreeLogger.TRACE,
+ "Invoking Linker " + linker.getDescription(), null);
+ artifacts = linker.link(linkerLogger, this, artifacts, false);
+ }
+ }
+ return artifacts;
+ }
+
+ /**
+ * Run linkers that have not been updated for the shardable API.
+ */
+ public ArtifactSet invokeLegacyLinkers(TreeLogger logger,
+ ArtifactSet artifacts) 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();
+ if (!linker.isShardable()) {
+ 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;
}
+ /**
+ * Invoke the shardable linkers on one permutation result. Those linkers run
+ * with the precompile artifacts as input.
+ */
+ public ArtifactSet invokeLinkForOnePermutation(TreeLogger logger,
+ StandardCompilationResult permResult, ArtifactSet permArtifacts)
+ throws UnableToCompleteException {
+ ArtifactSet workingArtifacts = new ArtifactSet(permArtifacts);
+ workingArtifacts.add(permResult);
+
+ for (Linker linker : linkers) {
+ if (linker.isShardable()) {
+ TreeLogger linkerLogger = logger.branch(TreeLogger.TRACE,
+ "Invoking Linker " + linker.getDescription(), null);
+ try {
+ workingArtifacts.freeze();
+ workingArtifacts = linker.link(logger, this, workingArtifacts, true);
+ } catch (Throwable e) {
+ linkerLogger.log(TreeLogger.ERROR, "Failed to link", e);
+ throw new UnableToCompleteException();
+ }
+ }
+ }
+
+ /*
+ * Reset linkers so that they don't accidentally carry any state across
+ * permutations
+ */
+ resetLinkers(logger);
+
+ workingArtifacts.freeze();
+ return workingArtifacts;
+ }
+
public ArtifactSet invokeRelink(TreeLogger logger,
ArtifactSet newlyGeneratedArtifacts) throws UnableToCompleteException {
ArtifactSet workingArtifacts = new ArtifactSet(newlyGeneratedArtifacts);
@@ -350,7 +401,7 @@
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
- ArtifactSet artifacts) throws UnableToCompleteException {
+ ArtifactSet artifacts) {
throw new UnsupportedOperationException();
}
@@ -455,35 +506,20 @@
}
/**
- * Creates a linker-specific subdirectory in the module's auxiliary output
- * directory.
+ * (Re)instantiate all linkers.
*/
- private String getExtraPathForLinker(Class<? extends Linker> linkerType,
- String partialPath) {
- assert linkerShortNames.containsKey(linkerType) : linkerType.getName()
- + " unknown";
- return linkerShortNames.get(linkerType) + '/' + partialPath;
- }
-
- /**
- * 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);
- }
- }
-
- {
- int index = 0;
- for (String style : module.getStyles()) {
- artifacts.add(new StandardStylesheetReference(style, index++));
- logger.log(TreeLogger.SPAM, "Added style " + style, null);
+ private void resetLinkers(TreeLogger logger) throws UnableToCompleteException {
+ linkers = new Linker[linkerClasses.size()];
+ int i = 0;
+ for (Class<? extends Linker> linkerClass : linkerClasses) {
+ 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();
}
}
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardScriptReference.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardScriptReference.java
index 456d381..b25c841 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardScriptReference.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardScriptReference.java
@@ -16,10 +16,12 @@
package com.google.gwt.core.ext.linker.impl;
import com.google.gwt.core.ext.linker.ScriptReference;
+import com.google.gwt.core.ext.linker.Transferable;
/**
* The standard implementation of {@link ScriptReference}.
*/
+@Transferable
public class StandardScriptReference extends ScriptReference {
public StandardScriptReference(String src, int index) {
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStylesheetReference.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStylesheetReference.java
index 3534663..e2e7f53 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStylesheetReference.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStylesheetReference.java
@@ -16,10 +16,12 @@
package com.google.gwt.core.ext.linker.impl;
import com.google.gwt.core.ext.linker.StylesheetReference;
+import com.google.gwt.core.ext.linker.Transferable;
/**
* The standard implementation of {@link StylesheetReference}.
*/
+@Transferable
public class StandardStylesheetReference extends StylesheetReference {
public StandardStylesheetReference(String src, int index) {
diff --git a/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java b/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
index 5300b5e..e0c972b 100644
--- a/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
@@ -23,6 +23,7 @@
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.Shardable;
import com.google.gwt.core.ext.linker.StatementRanges;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.linker.impl.HostedModeLinker;
@@ -42,6 +43,7 @@
* a separate iframe.
*/
@LinkerOrder(Order.PRIMARY)
+@Shardable
public class IFrameLinker extends SelectionScriptLinker {
/**
* This string is inserted between script chunks. It is made default access
@@ -111,8 +113,12 @@
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
- ArtifactSet artifacts) throws UnableToCompleteException {
- ArtifactSet toReturn = super.link(logger, context, artifacts);
+ ArtifactSet artifacts, boolean onePerm) throws UnableToCompleteException {
+ ArtifactSet toReturn = super.link(logger, context, artifacts, onePerm);
+
+ if (onePerm) {
+ return toReturn;
+ }
try {
// Add hosted mode iframe contents
@@ -153,7 +159,7 @@
return toReturn;
}
- /**
+ /*
* This implementation divides the code of the initial fragment into multiple
* script tags. These chunked script tags loads faster on Firefox even when
* the data is cached. Additionally, having the script tags separated means
@@ -221,8 +227,7 @@
}
@Override
- protected String getSelectionScriptTemplate(TreeLogger logger,
- LinkerContext context) {
+ protected String getSelectionScriptTemplate(TreeLogger logger, LinkerContext context) {
return "com/google/gwt/core/linker/IFrameTemplate.js";
}
diff --git a/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java b/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
index 9b2769b..140d90c 100644
--- a/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
@@ -18,10 +18,12 @@
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.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationResult;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.Shardable;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.linker.impl.SelectionScriptLinker;
import com.google.gwt.dev.About;
@@ -36,6 +38,7 @@
* result.
*/
@LinkerOrder(Order.PRIMARY)
+@Shardable
public class SingleScriptLinker extends SelectionScriptLinker {
@Override
public String getDescription() {
@@ -44,16 +47,20 @@
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
- ArtifactSet artifacts) throws UnableToCompleteException {
- ArtifactSet toReturn = new ArtifactSet(artifacts);
-
- toReturn.add(emitSelectionScript(logger, context, artifacts));
-
- return toReturn;
+ ArtifactSet artifacts, boolean onePermutation)
+ throws UnableToCompleteException {
+ if (onePermutation) {
+ processSelectionInformation(artifacts);
+ ArtifactSet toReturn = new ArtifactSet(artifacts);
+ toReturn.add(emitSelectionScript(logger, context, artifacts));
+ return toReturn;
+ } else {
+ return artifacts;
+ }
}
@Override
- protected Collection<EmittedArtifact> doEmitCompilation(TreeLogger logger,
+ protected Collection<Artifact<?>> doEmitCompilation(TreeLogger logger,
LinkerContext context, CompilationResult result)
throws UnableToCompleteException {
if (result.getJavaScript().length != 1) {
@@ -71,7 +78,7 @@
throws UnableToCompleteException {
DefaultTextOutput out = new DefaultTextOutput(true);
-
+
// Emit the selection script.
String bootstrap = generateSelectionScript(logger, context, artifacts);
bootstrap = context.optimizeJavaScript(logger, bootstrap);
@@ -95,9 +102,8 @@
// Find the single CompilationResult
Set<CompilationResult> results = artifacts.find(CompilationResult.class);
if (results.size() != 1) {
- logger.log(TreeLogger.ERROR,
- "The module must have exactly one distinct"
- + " permutation when using the " + getDescription() + " Linker.",
+ logger.log(TreeLogger.ERROR, "The module must have exactly one distinct"
+ + " permutation when using the " + getDescription() + " Linker.",
null);
throw new UnableToCompleteException();
}
@@ -158,8 +164,8 @@
}
@Override
- protected String getSelectionScriptTemplate(TreeLogger logger,
- LinkerContext context) throws UnableToCompleteException {
+ protected String getSelectionScriptTemplate(TreeLogger logger, LinkerContext context)
+ throws UnableToCompleteException {
return "com/google/gwt/core/linker/SingleScriptTemplate.js";
}
}
diff --git a/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java b/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
index c2216f3..7b85d26 100644
--- a/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
@@ -18,12 +18,14 @@
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.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
import com.google.gwt.core.ext.linker.SelectionProperty;
-import com.google.gwt.core.ext.linker.SyntheticArtifact;
+import com.google.gwt.core.ext.linker.Shardable;
+import com.google.gwt.core.ext.linker.Transferable;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.soyc.SoycDashboard;
import com.google.gwt.soyc.io.ArtifactsOutputDirectory;
@@ -32,15 +34,72 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.SortedMap;
-import java.util.SortedSet;
import java.util.TreeMap;
/**
- * Generates the top-level report files for a compile report.
+ * Converts SOYC report files into emitted private artifacts.
*/
@LinkerOrder(Order.POST)
+@Shardable
public class SoycReportLinker extends Linker {
+ /**
+ * An artifact giving a one-line description of a permutation ID in terms of
+ * its deferred bindings.
+ */
+ @Transferable
+ private static class PermDescriptionArtifact extends
+ Artifact<PermDescriptionArtifact> {
+
+ private List<String> permDesc;
+ private int permId;
+
+ public PermDescriptionArtifact(int permId, List<String> permDesc) {
+ super(SoycReportLinker.class);
+ this.permId = permId;
+ this.permDesc = permDesc;
+ }
+
+ public List<String> getPermDesc() {
+ return permDesc;
+ }
+
+ public int getPermId() {
+ return permId;
+ }
+
+ @Override
+ public int hashCode() {
+ return permId;
+ }
+
+ @Override
+ protected int compareToComparableArtifact(PermDescriptionArtifact o) {
+ int cmp;
+ cmp = permId - o.getPermId();
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ cmp = permDesc.size() - o.getPermDesc().size();
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ for (int i = 0; i < permDesc.size(); i++) {
+ cmp = permDesc.get(i).compareTo(o.getPermDesc().get(i));
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+
+ return 0;
+ }
+
+ @Override
+ protected Class<PermDescriptionArtifact> getComparableArtifactType() {
+ return PermDescriptionArtifact.class;
+ }
+ }
@Override
public String getDescription() {
@@ -49,51 +108,70 @@
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
- ArtifactSet artifacts) throws UnableToCompleteException {
- if (!includesReports(artifacts)) {
+ ArtifactSet artifacts, boolean onePermutation) {
+ if (!anyReportFilesPresent(artifacts)) {
+ // No report was generated
return artifacts;
}
- ArtifactSet results = new ArtifactSet(artifacts);
+ if (onePermutation) {
+ return emitPermutationDescriptions(logger, context, artifacts);
+ } else {
+ return buildTopLevelFiles(logger, context, artifacts);
+ }
+ }
- // Run the final step of the dashboard to generate top-level files.
+ private boolean anyReportFilesPresent(ArtifactSet artifacts) {
+ String prefix = "soycReport/"
+ + ArtifactsOutputDirectory.COMPILE_REPORT_DIRECTORY + "/";
+ for (EmittedArtifact art : artifacts.find(EmittedArtifact.class)) {
+ if (art.getPartialPath().startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private ArtifactSet buildTopLevelFiles(TreeLogger logger,
+ LinkerContext context, ArtifactSet artifacts) {
+ artifacts = new ArtifactSet(artifacts);
+
ArtifactsOutputDirectory out = new ArtifactsOutputDirectory();
try {
new SoycDashboard(out).generateCrossPermutationFiles(extractPermutationDescriptions(artifacts));
} catch (IOException e) {
logger.log(TreeLogger.ERROR,
"Error while generating a Story of Your Compile", e);
+ e.printStackTrace();
}
- results.addAll(out.getArtifacts());
- return results;
+ artifacts.addAll(out.getArtifacts());
+ return artifacts;
+ }
+
+ private ArtifactSet emitPermutationDescriptions(TreeLogger logger,
+ LinkerContext context, ArtifactSet artifacts) {
+ artifacts = new ArtifactSet(artifacts);
+
+ for (CompilationResult res : artifacts.find(CompilationResult.class)) {
+ int permId = res.getPermutationId();
+ List<String> permDesc = new ArrayList<String>();
+ for (Map<SelectionProperty, String> propertyMap : res.getPropertyMap()) {
+ permDesc.add(SymbolMapsLinker.propertyMapToString(propertyMap));
+ }
+
+ artifacts.add(new PermDescriptionArtifact(permId, permDesc));
+ }
+
+ return artifacts;
}
private Map<String, List<String>> extractPermutationDescriptions(
ArtifactSet artifacts) {
- Map<String, List<String>> permutationDescriptions = new TreeMap<String, List<String>>();
-
- for (CompilationResult res : artifacts.find(CompilationResult.class)) {
- String permId = Integer.toString(res.getPermutationId());
- List<String> permDescList = new ArrayList<String>();
- SortedSet<SortedMap<SelectionProperty, String>> allPropertiesMap = res.getPropertyMap();
- for (SortedMap<SelectionProperty, String> propertyMap : allPropertiesMap) {
- String permDesc = SymbolMapsLinker.propertyMapToString(propertyMap);
- permDescList.add(permDesc);
- }
- permutationDescriptions.put(permId, permDescList);
+ Map<String, List<String>> permDescriptions = new TreeMap<String, List<String>>();
+ for (PermDescriptionArtifact art : artifacts.find(PermDescriptionArtifact.class)) {
+ permDescriptions.put(Integer.toString(art.getPermId()), art.getPermDesc());
}
-
- return permutationDescriptions;
- }
-
- private boolean includesReports(ArtifactSet artifacts) {
- for (SyntheticArtifact art : artifacts.find(SyntheticArtifact.class)) {
- if (art.getPartialPath().startsWith(
- ArtifactsOutputDirectory.COMPILE_REPORT_DIRECTORY + "/")) {
- return true;
- }
- }
- return false;
+ return permDescriptions;
}
}
diff --git a/dev/core/src/com/google/gwt/core/linker/SymbolMapsLinker.java b/dev/core/src/com/google/gwt/core/linker/SymbolMapsLinker.java
index 298b9f8..321c29e 100644
--- a/dev/core/src/com/google/gwt/core/linker/SymbolMapsLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/SymbolMapsLinker.java
@@ -24,6 +24,7 @@
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
import com.google.gwt.core.ext.linker.SelectionProperty;
+import com.google.gwt.core.ext.linker.Shardable;
import com.google.gwt.core.ext.linker.SymbolData;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
@@ -40,6 +41,7 @@
* {@link CompilationResult#getStrongName()}.
*/
@LinkerOrder(Order.POST)
+@Shardable
public class SymbolMapsLinker extends AbstractLinker {
/**
@@ -80,23 +82,33 @@
return "Export CompilationResult symbol maps";
}
+ /**
+ * Included to support legacy non-shardable subclasses.
+ */
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
ArtifactSet artifacts) throws UnableToCompleteException {
+ return link(logger, context, artifacts, true);
+ }
- artifacts = new ArtifactSet(artifacts);
+ @Override
+ public ArtifactSet link(TreeLogger logger, LinkerContext context,
+ ArtifactSet artifacts, boolean onePermutation)
+ throws UnableToCompleteException {
+ if (onePermutation) {
+ artifacts = new ArtifactSet(artifacts);
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- for (CompilationResult result : artifacts.find(CompilationResult.class)) {
- PrintWriter pw = new PrintWriter(out);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ for (CompilationResult result : artifacts.find(CompilationResult.class)) {
+ PrintWriter pw = new PrintWriter(out);
- doWriteSymbolMap(logger, result, pw);
- pw.close();
+ doWriteSymbolMap(logger, result, pw);
+ pw.close();
- doEmitSymbolMap(logger, artifacts, result, out);
- out.reset();
+ doEmitSymbolMap(logger, artifacts, result, out);
+ out.reset();
+ }
}
-
return artifacts;
}
diff --git a/dev/core/src/com/google/gwt/core/linker/XSLinker.java b/dev/core/src/com/google/gwt/core/linker/XSLinker.java
index bdff4f7..6dbedba 100644
--- a/dev/core/src/com/google/gwt/core/linker/XSLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/XSLinker.java
@@ -18,9 +18,10 @@
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.Artifact;
import com.google.gwt.core.ext.linker.CompilationResult;
-import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.Shardable;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.linker.impl.SelectionScriptLinker;
import com.google.gwt.dev.About;
@@ -32,6 +33,7 @@
* Generates a cross-site compatible bootstrap sequence.
*/
@LinkerOrder(Order.PRIMARY)
+@Shardable
public class XSLinker extends SelectionScriptLinker {
@Override
public String getDescription() {
@@ -39,7 +41,7 @@
}
@Override
- protected Collection<EmittedArtifact> doEmitCompilation(TreeLogger logger,
+ protected Collection<Artifact<?>> doEmitCompilation(TreeLogger logger,
LinkerContext context, CompilationResult result)
throws UnableToCompleteException {
if (result.getJavaScript().length != 1) {
@@ -112,8 +114,8 @@
}
@Override
- protected String getSelectionScriptTemplate(TreeLogger logger,
- LinkerContext context) throws UnableToCompleteException {
+ protected String getSelectionScriptTemplate(TreeLogger logger, LinkerContext context)
+ throws UnableToCompleteException {
return "com/google/gwt/core/linker/XSTemplate.js";
}
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index bf8815e..cc1019e 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -300,11 +300,6 @@
return false;
}
- /*
- * TODO(spoon) Check that all requested permutations have a permutation
- * available before starting. probably needs two branches.
- */
-
if (precompileResults instanceof PrecompileOptions) {
PrecompileOptions precompilationOptions = (PrecompileOptions) precompileResults;
if (!precompileAndCompile(logger, moduleName, compilerWorkDir,
@@ -374,10 +369,9 @@
PermutationResult permResult = compile(logger, subPerms[0],
precompilation.getUnifiedAst());
- FileBackedObject<PermutationResult> resultFile = new FileBackedObject<PermutationResult>(
- PermutationResult.class, makePermFilename(compilerWorkDir, permId));
- permResult.addArtifacts(precompilation.getGeneratedArtifacts());
- resultFile.set(logger, permResult);
+ Link.linkOnePermutationToJar(logger, module,
+ precompilation.getGeneratedArtifacts(), permResult, makePermFilename(
+ compilerWorkDir, permId), precompilationOptions);
}
logger.log(TreeLogger.INFO, "Compile of permutations succeeded");
diff --git a/dev/core/src/com/google/gwt/dev/CompilePermsServer.java b/dev/core/src/com/google/gwt/dev/CompilePermsServer.java
index 012aac7..e119a8e 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePermsServer.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePermsServer.java
@@ -296,8 +296,8 @@
Throwable caught = null;
try {
- logger.log(TreeLogger.DEBUG, "Compiling");
- PermutationResult result = CompilePerms.compile(logger, p, ast);
+ PermutationResult result = CompilePerms.compile(logger.branch(
+ TreeLogger.DEBUG, "Compiling"), p, ast);
resultFile.set(logger, result);
logger.log(TreeLogger.DEBUG, "Successfully compiled permutation");
} catch (UnableToCompleteException e) {
diff --git a/dev/core/src/com/google/gwt/dev/Compiler.java b/dev/core/src/com/google/gwt/dev/Compiler.java
index c946cab..b3dabb2 100644
--- a/dev/core/src/com/google/gwt/dev/Compiler.java
+++ b/dev/core/src/com/google/gwt/dev/Compiler.java
@@ -218,12 +218,13 @@
String logMessage = "Linking into " + absPath;
if (options.getExtraDir() != null) {
- File absExtrasPath = new File(options.getExtraDir(), module.getName());
- absExtrasPath = absExtrasPath.getAbsoluteFile();
- logMessage += "; Writing extras to " + absExtrasPath;
+ File absExtrasPath = new File(options.getExtraDir(),
+ module.getName());
+ absExtrasPath = absExtrasPath.getAbsoluteFile();
+ logMessage += "; Writing extras to " + absExtrasPath;
}
- Link.link(logger.branch(TreeLogger.TRACE, logMessage),
- module, generatedArtifacts, resultFiles, options.getWarDir(),
+ Link.link(logger.branch(TreeLogger.TRACE, logMessage), module,
+ generatedArtifacts, allPerms, resultFiles, options.getWarDir(),
options.getExtraDir(), precompileOptions);
long compileDone = System.currentTimeMillis();
diff --git a/dev/core/src/com/google/gwt/dev/DevMode.java b/dev/core/src/com/google/gwt/dev/DevMode.java
index 4051228..f348824 100644
--- a/dev/core/src/com/google/gwt/dev/DevMode.java
+++ b/dev/core/src/com/google/gwt/dev/DevMode.java
@@ -26,6 +26,8 @@
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;
@@ -386,8 +388,8 @@
// Create the war directory if it doesn't exist
File warDir = options.getWarDir();
if (!warDir.exists() && !warDir.mkdirs()) {
- getTopLogger().log(TreeLogger.ERROR, "Unable to create war directory "
- + warDir);
+ getTopLogger().log(TreeLogger.ERROR,
+ "Unable to create war directory " + warDir);
return -1;
}
@@ -397,7 +399,7 @@
ServletContainerLauncher scl = options.getServletContainerLauncher();
- TreeLogger serverLogger = ui.getWebServerLogger(getWebServerName(),
+ TreeLogger serverLogger = ui.getWebServerLogger(getWebServerName(),
scl.getIconBytes());
String sclArgs = options.getServletContainerLauncherArgs();
@@ -424,7 +426,10 @@
clearCallback = false;
return server.getPort();
} catch (BindException e) {
- System.err.println("Port " + bindAddress + ':' + getPort()
+ System.err.println("Port "
+ + bindAddress
+ + ':'
+ + getPort()
+ " is already is use; you probably still have another session active");
} catch (Exception e) {
System.err.println("Unable to start embedded HTTP server");
@@ -473,24 +478,35 @@
protected synchronized void produceOutput(TreeLogger logger,
StandardLinkerContext linkerStack, ArtifactSet artifacts,
ModuleDef module, boolean isRelink) throws UnableToCompleteException {
+ TreeLogger linkLogger = logger.branch(TreeLogger.DEBUG, "Linking module '"
+ + module.getName() + "'");
+
OutputFileSetOnDirectory outFileSet = new OutputFileSetOnDirectory(
options.getWarDir(), module.getName() + "/");
- linkerStack.produceOutput(logger, artifacts, false, outFileSet);
- outFileSet.close();
-
+ OutputFileSet extraFileSet = new NullOutputFileSet();
if (options.getExtraDir() != null) {
- OutputFileSetOnDirectory extraFileSet = new OutputFileSetOnDirectory(
- options.getExtraDir(), module.getName() + "/");
- linkerStack.produceOutput(logger, artifacts, true, extraFileSet);
+ extraFileSet = new OutputFileSetOnDirectory(options.getExtraDir(),
+ module.getName() + "/");
+ }
+
+ linkerStack.produceOutput(linkLogger, artifacts, false, outFileSet);
+ linkerStack.produceOutput(linkLogger, artifacts, true, extraFileSet);
+
+ outFileSet.close();
+ try {
extraFileSet.close();
+ } catch (IOException e) {
+ linkLogger.log(TreeLogger.ERROR, "Error emiting extra files", e);
+ throw new UnableToCompleteException();
}
}
@Override
protected void warnAboutNoStartupUrls() {
- getTopLogger().log(TreeLogger.WARN,
+ getTopLogger().log(
+ TreeLogger.WARN,
"No startup URLs supplied and no plausible ones found -- use "
- + "-startupUrl");
+ + "-startupUrl");
}
private void validateServletTags(TreeLogger logger,
diff --git a/dev/core/src/com/google/gwt/dev/DevModeBase.java b/dev/core/src/com/google/gwt/dev/DevModeBase.java
index 9d02650..a70c7b0 100644
--- a/dev/core/src/com/google/gwt/dev/DevModeBase.java
+++ b/dev/core/src/com/google/gwt/dev/DevModeBase.java
@@ -151,8 +151,7 @@
// replace a wildcard address with our machine's local address
// this isn't fully accurate, as there is no guarantee we will get
// the right one on a multihomed host
- options.setConnectAddress(
- InetAddress.getLocalHost().getHostAddress());
+ options.setConnectAddress(InetAddress.getLocalHost().getHostAddress());
} else {
options.setConnectAddress(value);
}
@@ -565,7 +564,7 @@
protected interface OptionBindAddress {
String getBindAddress();
-
+
String getConnectAddress();
void setBindAddress(String bindAddress);
@@ -744,8 +743,8 @@
* Gets the base log level recommended by the UI for INFO-level messages. This
* method can only be called once {@link #createUI()} has been called. Please
* do not depend on this method, as it is subject to change.
- *
- * @return the log level to use for INFO-level messages
+ *
+ * @return the log level to use for INFO-level messages
*/
public TreeLogger.Type getBaseLogLevelForUI() {
if (baseLogLevelForUI == null) {
@@ -784,7 +783,7 @@
// The web server is running now, so launch browsers for startup urls.
ui.moduleLoadComplete(success);
-
+
blockUntilDone.acquire();
} catch (Exception e) {
e.printStackTrace();
@@ -837,9 +836,9 @@
protected abstract void doShutDownServer();
/**
- * Perform any slower startup tasks, such as loading modules. This is
- * separate from {@link #doStartup()} so that the UI can be updated as
- * soon as possible and the web server can be started earlier.
+ * Perform any slower startup tasks, such as loading modules. This is separate
+ * from {@link #doStartup()} so that the UI can be updated as soon as possible
+ * and the web server can be started earlier.
*
* @return false if startup failed
*/
@@ -944,7 +943,10 @@
// Create a new active linker stack for the fresh link.
StandardLinkerContext linkerStack = new StandardLinkerContext(linkLogger,
module, options);
- ArtifactSet artifacts = linkerStack.invokeLink(linkLogger);
+ ArtifactSet artifacts = linkerStack.getArtifactsForPublicResources(logger,
+ module);
+ artifacts = linkerStack.invokeLegacyLinkers(linkLogger, artifacts);
+ artifacts = linkerStack.invokeFinalLink(linkLogger, artifacts);
produceOutput(linkLogger, linkerStack, artifacts, module, false);
return linkerStack;
}
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index d5eea34..90c4330 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -69,8 +69,8 @@
/**
* Simple implementation of {@link LegacyCompilerOptions}.
*/
- public static class GWTCompilerOptionsImpl extends PrecompileOptionsImpl implements
- LegacyCompilerOptions {
+ public static class GWTCompilerOptionsImpl extends PrecompileOptionsImpl
+ implements LegacyCompilerOptions {
private int localWorkers;
private File outDir;
@@ -120,8 +120,8 @@
public boolean run(TreeLogger logger) throws UnableToCompleteException {
FutureTask<UpdateResult> updater = null;
if (!options.isUpdateCheckDisabled()) {
- updater = CheckForUpdates.checkForUpdatesInBackgroundThread(
- logger, CheckForUpdates.ONE_DAY);
+ updater = CheckForUpdates.checkForUpdatesInBackgroundThread(logger,
+ CheckForUpdates.ONE_DAY);
}
boolean success = new GWTCompiler(options).run(logger);
if (success) {
@@ -207,7 +207,7 @@
Link.legacyLink(logger.branch(TreeLogger.TRACE, "Linking into "
+ options.getOutDir().getPath()), module, generatedArtifacts,
- resultFiles, options.getOutDir(), precompileOptions);
+ allPerms, resultFiles, options.getOutDir(), precompileOptions);
long compileDone = System.currentTimeMillis();
long delta = compileDone - compileStart;
diff --git a/dev/core/src/com/google/gwt/dev/Link.java b/dev/core/src/com/google/gwt/dev/Link.java
index 92a5ab7..8cf22f4 100644
--- a/dev/core/src/com/google/gwt/dev/Link.java
+++ b/dev/core/src/com/google/gwt/dev/Link.java
@@ -17,8 +17,12 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.SelectionProperty;
+import com.google.gwt.core.ext.linker.impl.BinaryOnlyArtifactWrapper;
+import com.google.gwt.core.ext.linker.impl.JarEntryEmittedArtifact;
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;
@@ -42,13 +46,19 @@
import com.google.gwt.dev.util.arg.OptionOutDir;
import com.google.gwt.dev.util.arg.OptionWarDir;
+import java.io.BufferedInputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
/**
* Performs the last phase of compilation, merging the compilation outputs.
@@ -135,14 +145,14 @@
}
public static void legacyLink(TreeLogger logger, ModuleDef module,
- ArtifactSet generatedArtifacts,
+ ArtifactSet generatedArtifacts, Permutation[] permutations,
List<FileBackedObject<PermutationResult>> resultFiles, File outDir,
JJSOptions precompileOptions) throws UnableToCompleteException,
IOException {
StandardLinkerContext linkerContext = new StandardLinkerContext(logger,
module, precompileOptions);
- ArtifactSet artifacts = doLink(logger, linkerContext, generatedArtifacts,
- resultFiles);
+ ArtifactSet artifacts = doSimulatedShardingLink(logger, module,
+ linkerContext, generatedArtifacts, permutations, resultFiles);
OutputFileSet outFileSet = new OutputFileSetOnDirectory(outDir,
module.getName() + "/");
OutputFileSet extraFileSet = new OutputFileSetOnDirectory(outDir,
@@ -151,19 +161,85 @@
}
public static void link(TreeLogger logger, ModuleDef module,
- ArtifactSet generatedArtifacts,
+ ArtifactSet generatedArtifacts, Permutation[] permutations,
List<FileBackedObject<PermutationResult>> resultFiles, File outDir,
File extrasDir, JJSOptions precompileOptions)
throws UnableToCompleteException, IOException {
StandardLinkerContext linkerContext = new StandardLinkerContext(logger,
module, precompileOptions);
- ArtifactSet artifacts = doLink(logger, linkerContext, generatedArtifacts,
- resultFiles);
+ ArtifactSet artifacts = doSimulatedShardingLink(logger, module,
+ linkerContext, generatedArtifacts, permutations, resultFiles);
doProduceOutput(logger, artifacts, linkerContext, chooseOutputFileSet(
outDir, module.getName() + "/"), chooseOutputFileSet(extrasDir,
module.getName() + "/"));
}
+ /**
+ * This link operation is performed on a CompilePerms shard for one
+ * permutation. It sees the generated artifacts for one permutation compile,
+ * and it runs the per-permutation part of each shardable linker.
+ */
+ @SuppressWarnings("unchecked")
+ public static void linkOnePermutationToJar(TreeLogger logger,
+ ModuleDef module, ArtifactSet generatedArtifacts,
+ PermutationResult permResult, File jarFile,
+ PrecompileOptions precompileOptions) throws UnableToCompleteException {
+ try {
+ JarOutputStream jar = new JarOutputStream(new FileOutputStream(jarFile));
+
+ StandardLinkerContext linkerContext = new StandardLinkerContext(logger,
+ module, precompileOptions);
+
+ StandardCompilationResult compilation = new StandardCompilationResult(
+ permResult);
+ addSelectionPermutations(compilation, permResult.getPermutation(),
+ linkerContext);
+ ArtifactSet permArtifacts = new ArtifactSet(generatedArtifacts);
+ permArtifacts.addAll(permResult.getArtifacts());
+ permArtifacts.add(compilation);
+
+ ArtifactSet linkedArtifacts = linkerContext.invokeLinkForOnePermutation(
+ logger, compilation, permArtifacts);
+
+ // Write the data of emitted artifacts
+ for (EmittedArtifact art : linkedArtifacts.find(EmittedArtifact.class)) {
+ String jarEntryPath;
+ if (art.isPrivate()) {
+ String pathWithLinkerName = linkerContext.getExtraPathForLinker(
+ art.getLinker(), art.getPartialPath());
+ if (pathWithLinkerName.startsWith("/")) {
+ // This happens if the linker has no extra path
+ pathWithLinkerName = pathWithLinkerName.substring(1);
+ }
+ jarEntryPath = "aux/" + pathWithLinkerName;
+ } else {
+ jarEntryPath = "target/" + art.getPartialPath();
+ }
+ jar.putNextEntry(new ZipEntry(jarEntryPath));
+ art.writeTo(logger, jar);
+ jar.closeEntry();
+ }
+
+ // Serialize artifacts marked as Transferable
+ int numSerializedArtifacts = 0;
+ // The raw type Artifact is to work around a Java compiler bug:
+ // http://bugs.sun.com/view_bug.do?bug_id=6548436
+ for (Artifact art : linkedArtifacts) {
+ if (art.isTransferableFromShards() && !(art instanceof EmittedArtifact)) {
+ String jarEntryPath = "arts/" + numSerializedArtifacts++;
+ jar.putNextEntry(new ZipEntry(jarEntryPath));
+ Util.writeObjectToStream(jar, art);
+ jar.closeEntry();
+ }
+ }
+
+ jar.close();
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Error linking", e);
+ throw new UnableToCompleteException();
+ }
+ }
+
public static void main(String[] args) {
/*
* NOTE: main always exits with a call to System.exit to terminate any
@@ -189,6 +265,32 @@
}
/**
+ * In a parallel build, artifact sets are thinned down in transit between
+ * compilation and linking. All emitted artifacts are changed to binary
+ * emitted artifacts, and all other artifacts are dropped except @Transferable
+ * ones. This method simulates the thinning that happens in a parallel build.
+ */
+ @SuppressWarnings("unchecked")
+ public static ArtifactSet simulateTransferThinning(ArtifactSet artifacts,
+ StandardLinkerContext context) {
+ ArtifactSet thinnedArtifacts = new ArtifactSet();
+ // The raw type Artifact is to work around a compiler bug:
+ // http://bugs.sun.com/view_bug.do?bug_id=6548436
+ for (Artifact artifact : artifacts) {
+ if (artifact instanceof EmittedArtifact) {
+ EmittedArtifact emittedArtifact = (EmittedArtifact) artifact;
+ String path = getFullArtifactPath(emittedArtifact, context);
+ thinnedArtifacts.add(new BinaryOnlyArtifactWrapper(path,
+ emittedArtifact));
+ } else if (artifact.isTransferableFromShards()) {
+ thinnedArtifacts.add(artifact);
+ }
+ }
+
+ return thinnedArtifacts;
+ }
+
+ /**
* Add to a compilation result all of the selection permutations from its
* associated permutation.
*/
@@ -202,22 +304,11 @@
}
/**
- * 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.
+ * 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();
}
@@ -225,10 +316,10 @@
String name = dirOrJar.getName();
if (!dirOrJar.isDirectory()
&& (name.endsWith(".war") || name.endsWith(".jar") || name.endsWith(".zip"))) {
- return new OutputFileSetOnJar(dirOrJar, jarPathPrefix);
+ return new OutputFileSetOnJar(dirOrJar, pathPrefix);
} else {
- Util.recursiveDelete(new File(dirOrJar, dirPathPrefix), true);
- return new OutputFileSetOnDirectory(dirOrJar, dirPathPrefix);
+ Util.recursiveDelete(new File(dirOrJar, pathPrefix), true);
+ return new OutputFileSetOnDirectory(dirOrJar, pathPrefix);
}
}
@@ -260,18 +351,6 @@
return unboundProperties;
}
- private static ArtifactSet doLink(TreeLogger logger,
- StandardLinkerContext linkerContext, ArtifactSet generatedArtifacts,
- List<FileBackedObject<PermutationResult>> resultFiles)
- throws UnableToCompleteException {
- linkerContext.addOrReplaceArtifacts(generatedArtifacts);
- for (FileBackedObject<PermutationResult> resultFile : resultFiles) {
- PermutationResult result = resultFile.newInstance(logger);
- finishPermutation(logger, result.getPermutation(), result, linkerContext);
- }
- return linkerContext.invokeLink(logger);
- }
-
/**
* Emit final output.
*/
@@ -288,13 +367,70 @@
}
/**
- * Add a compilation to a linker context.
+ * This link operation simulates sharded linking even though all generating
+ * and linking is happening on the same computer. It can tolerate
+ * non-shardable linkers.
*/
- private static void finishPermutation(TreeLogger logger, Permutation perm,
- PermutationResult permResult, StandardLinkerContext linkerContext) {
- StandardCompilationResult compilation = linkerContext.getCompilation(permResult);
+ private static ArtifactSet doSimulatedShardingLink(TreeLogger logger,
+ ModuleDef module, StandardLinkerContext linkerContext,
+ ArtifactSet generatedArtifacts, Permutation[] perms,
+ List<FileBackedObject<PermutationResult>> resultFiles)
+ throws UnableToCompleteException {
+ ArtifactSet combinedArtifacts = new ArtifactSet();
+ for (int i = 0; i < perms.length; ++i) {
+ ArtifactSet newArtifacts = finishPermutation(logger, perms[i],
+ resultFiles.get(i), linkerContext, generatedArtifacts);
+ combinedArtifacts.addAll(newArtifacts);
+ }
+
+ combinedArtifacts.addAll(linkerContext.getArtifactsForPublicResources(
+ logger, module));
+
+ ArtifactSet legacyLinkedArtifacts = linkerContext.invokeLegacyLinkers(
+ logger, combinedArtifacts);
+
+ ArtifactSet thinnedArtifacts = simulateTransferThinning(
+ legacyLinkedArtifacts, linkerContext);
+
+ return linkerContext.invokeFinalLink(logger, thinnedArtifacts);
+ }
+
+ /**
+ * Add a compilation to a linker context. Also runs the shardable part of all
+ * linkers that support sharding.
+ *
+ * @return the new artifacts generated by the shardable part of this link
+ * operation
+ */
+ private static ArtifactSet finishPermutation(TreeLogger logger,
+ Permutation perm, FileBackedObject<PermutationResult> resultFile,
+ StandardLinkerContext linkerContext, ArtifactSet generatedArtifacts)
+ throws UnableToCompleteException {
+ PermutationResult permResult = resultFile.newInstance(logger);
+ StandardCompilationResult compilation = new StandardCompilationResult(
+ permResult);
addSelectionPermutations(compilation, perm, linkerContext);
logScriptSize(logger, perm.getId(), compilation);
+
+ ArtifactSet permArtifacts = new ArtifactSet(generatedArtifacts);
+ permArtifacts.addAll(permResult.getArtifacts());
+ permArtifacts.add(compilation);
+ permArtifacts.freeze();
+ return linkerContext.invokeLinkForOnePermutation(logger, compilation,
+ permArtifacts);
+ }
+
+ private static String getFullArtifactPath(EmittedArtifact emittedArtifact,
+ StandardLinkerContext context) {
+ String path = emittedArtifact.getPartialPath();
+ if (emittedArtifact.isPrivate()) {
+ path = context.getExtraPathForLinker(emittedArtifact.getLinker(), path);
+ if (path.startsWith("/")) {
+ // This happens if the linker has no extra path
+ path = path.substring(1);
+ }
+ }
+ return path;
}
/**
@@ -321,6 +457,54 @@
+ javaScript[0].length() + " and total script size of " + totalSize);
}
+ private static ArtifactSet scanCompilePermResults(TreeLogger logger,
+ List<File> resultFiles) throws IOException, UnableToCompleteException {
+ final ArtifactSet artifacts = new ArtifactSet();
+
+ for (File resultFile : resultFiles) {
+ JarFile jarFile = new JarFile(resultFile);
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ if (entry.isDirectory()) {
+ continue;
+ }
+
+ String path;
+ Artifact<?> artForEntry;
+
+ if (entry.getName().startsWith("target/")) {
+ path = entry.getName().substring("target/".length());
+ artForEntry = new JarEntryEmittedArtifact(path, resultFile, entry);
+ } else if (entry.getName().startsWith("aux/")) {
+ path = entry.getName().substring("aux/".length());
+ JarEntryEmittedArtifact jarArtifact = new JarEntryEmittedArtifact(
+ path, resultFile, entry);
+ jarArtifact.setPrivate(true);
+ artForEntry = jarArtifact;
+ } else if (entry.getName().startsWith("arts/")) {
+ try {
+ artForEntry = Util.readStreamAsObject(new BufferedInputStream(
+ jarFile.getInputStream(entry)), Artifact.class);
+ assert artForEntry.isTransferableFromShards();
+ } catch (ClassNotFoundException e) {
+ logger.log(TreeLogger.ERROR,
+ "Failed trying to deserialize an artifact", e);
+ throw new UnableToCompleteException();
+ }
+ } else {
+ continue;
+ }
+
+ artifacts.add(artForEntry);
+ }
+
+ jarFile.close();
+ }
+
+ return artifacts;
+ }
+
private final LinkOptionsImpl options;
public Link(LinkOptions options) {
@@ -328,44 +512,10 @@
}
public boolean run(TreeLogger logger) throws UnableToCompleteException {
- for (String moduleName : options.getModuleNames()) {
+ loop_modules : for (String moduleName : options.getModuleNames()) {
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();
- }
-
- List<Permutation> permsList = new ArrayList<Permutation>();
- ArtifactSet generatedArtifacts = new ArtifactSet();
- JJSOptions precompileOptions = null;
-
File compilerWorkDir = options.getCompilerWorkDir(moduleName);
- List<Integer> permutationIds = new ArrayList<Integer>();
PrecompilationResult precompileResults;
try {
precompileResults = Util.readFileAsObject(new File(compilerWorkDir,
@@ -384,53 +534,88 @@
/**
* Precompiling happened on the shards.
*/
- precompileOptions = (JJSOptions) precompileResults;
- int numPermutations = module.getProperties().numPermutations();
- for (int i = 0; i < numPermutations; ++i) {
- permutationIds.add(i);
+ if (!doLinkFinal(logger, compilerWorkDir, module,
+ (JJSOptions) precompileResults)) {
+ return false;
}
+ continue loop_modules;
} else {
/**
* Precompiling happened on the start node.
*/
- Precompilation precompilation = (Precompilation) precompileResults;
- permsList.addAll(Arrays.asList(precompilation.getPermutations()));
- generatedArtifacts.addAll(precompilation.getGeneratedArtifacts());
- precompileOptions = precompilation.getUnifiedAst().getOptions();
-
- for (Permutation perm : precompilation.getPermutations()) {
- permutationIds.add(perm.getId());
+ Precompilation precomp = (Precompilation) precompileResults;
+ Permutation[] perms = precomp.getPermutations();
+ List<FileBackedObject<PermutationResult>> resultFiles = CompilePerms.makeResultFiles(
+ compilerWorkDir, perms);
+
+ // Check that all files are present
+ for (FileBackedObject<PermutationResult> file : resultFiles) {
+ if (!file.getFile().exists()) {
+ logger.log(TreeLogger.ERROR, "File not found '"
+ + file.getFile().getAbsolutePath()
+ + "'; please compile all permutations");
+ return false;
+ }
}
- }
- List<FileBackedObject<PermutationResult>> resultFiles = new ArrayList<FileBackedObject<PermutationResult>>(
- permutationIds.size());
- for (int id : permutationIds) {
- File f = CompilePerms.makePermFilename(compilerWorkDir, id);
- if (!f.exists()) {
- logger.log(TreeLogger.ERROR, "File not found '" + f.getAbsolutePath()
- + "'; please compile all permutations");
- return false;
+ TreeLogger branch = logger.branch(TreeLogger.INFO, "Linking module "
+ + module.getName());
+
+ try {
+ link(branch, module, precomp.getGeneratedArtifacts(), perms,
+ resultFiles, options.getWarDir(), options.getExtraDir(),
+ precomp.getUnifiedAst().getOptions());
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR,
+ "Unexpected exception while producing output", e);
+ throw new UnableToCompleteException();
}
- 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,
- resultFiles);
- try {
- doProduceOutput(branch, artifacts, linkerContext, outFileSet,
- extraFileSet);
- } catch (IOException e) {
- logger.log(TreeLogger.ERROR,
- "Unexpected exception while producing output", e);
}
}
return true;
}
+
+ /**
+ * Do a final link, assuming the precompiles were done on the CompilePerms
+ * shards.
+ */
+ private boolean doLinkFinal(TreeLogger logger, File compilerWorkDir,
+ ModuleDef module, JJSOptions precompileOptions)
+ throws UnableToCompleteException {
+ int numPermutations = module.getProperties().numPermutations();
+ List<File> resultFiles = new ArrayList<File>(numPermutations);
+ for (int i = 0; i < numPermutations; ++i) {
+ File f = CompilePerms.makePermFilename(compilerWorkDir, i);
+ if (!f.exists()) {
+ logger.log(TreeLogger.ERROR, "File not found '" + f.getAbsolutePath()
+ + "'; please compile all permutations");
+ return false;
+ }
+ resultFiles.add(f);
+ }
+
+ TreeLogger branch = logger.branch(TreeLogger.INFO, "Linking module "
+ + module.getName());
+ StandardLinkerContext linkerContext = new StandardLinkerContext(branch,
+ module, precompileOptions);
+
+ try {
+ OutputFileSet outFileSet = chooseOutputFileSet(options.getWarDir(),
+ module.getName() + "/");
+ OutputFileSet extraFileSet = chooseOutputFileSet(options.getExtraDir(),
+ module.getName() + "/");
+
+ ArtifactSet artifacts = scanCompilePermResults(logger, resultFiles);
+ artifacts.addAll(linkerContext.getArtifactsForPublicResources(logger,
+ module));
+ artifacts = linkerContext.invokeFinalLink(logger, artifacts);
+ doProduceOutput(logger, artifacts, linkerContext, outFileSet,
+ extraFileSet);
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Exception during final linking", e);
+ throw new UnableToCompleteException();
+ }
+
+ return true;
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/PermutationWorker.java b/dev/core/src/com/google/gwt/dev/PermutationWorker.java
index 485c26c..9007d03 100644
--- a/dev/core/src/com/google/gwt/dev/PermutationWorker.java
+++ b/dev/core/src/com/google/gwt/dev/PermutationWorker.java
@@ -38,8 +38,8 @@
* @throws UnableToCompleteException if the compile fails for any reason
*/
void compile(TreeLogger logger, Permutation permutation,
- FileBackedObject<PermutationResult> resultFile)
- throws TransientWorkerException, UnableToCompleteException;
+ FileBackedObject<PermutationResult> resultFile) throws TransientWorkerException,
+ UnableToCompleteException;
/**
* Returns a human-readable description of the worker instance. This may be
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index 890038d..b2801f0 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -15,9 +15,11 @@
*/
package com.google.gwt.dev;
+import com.google.gwt.core.ext.Linker;
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.impl.StandardLinkerContext;
import com.google.gwt.dev.CompileTaskRunner.CompileTask;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.ConfigurationProperty;
@@ -47,6 +49,7 @@
import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization;
import com.google.gwt.dev.util.arg.ArgHandlerDisableCastChecking;
import com.google.gwt.dev.util.arg.ArgHandlerDisableClassMetadata;
+import com.google.gwt.dev.util.arg.ArgHandlerDisableGeneratingOnShards;
import com.google.gwt.dev.util.arg.ArgHandlerDisableRunAsync;
import com.google.gwt.dev.util.arg.ArgHandlerDisableUpdateCheck;
import com.google.gwt.dev.util.arg.ArgHandlerDraftCompile;
@@ -54,8 +57,8 @@
import com.google.gwt.dev.util.arg.ArgHandlerEnableAssertions;
import com.google.gwt.dev.util.arg.ArgHandlerGenDir;
import com.google.gwt.dev.util.arg.ArgHandlerMaxPermsPerPrecompile;
-import com.google.gwt.dev.util.arg.ArgHandlerShardPrecompile;
import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle;
+import com.google.gwt.dev.util.arg.ArgHandlerShardPrecompile;
import com.google.gwt.dev.util.arg.ArgHandlerSoyc;
import com.google.gwt.dev.util.arg.ArgHandlerSoycDetailed;
import com.google.gwt.dev.util.arg.ArgHandlerValidateOnlyFlag;
@@ -97,7 +100,8 @@
registerHandler(new ArgHandlerGenDir(options));
registerHandler(new ArgHandlerScriptStyle(options));
registerHandler(new ArgHandlerEnableAssertions(options));
- registerHandler(new ArgHandlerShardPrecompile(options));
+ registerHandler(new ArgHandlerShardPrecompile());
+ registerHandler(new ArgHandlerDisableGeneratingOnShards(options));
registerHandler(new ArgHandlerDisableAggressiveOptimization(options));
registerHandler(new ArgHandlerDisableClassMetadata(options));
registerHandler(new ArgHandlerDisableCastChecking(options));
@@ -122,7 +126,7 @@
PrecompileOptions, Serializable {
private boolean disableUpdateCheck;
private File dumpFile;
- private boolean enableGeneratingOnShards;
+ private boolean enableGeneratingOnShards = true;
private File genDir;
private final JJSOptionsImpl jjsOptions = new JJSOptionsImpl();
private int maxPermsPerPrecompile;
@@ -581,8 +585,23 @@
ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName);
- boolean generateOnShards = options.isEnabledGeneratingOnShards();
- if (options.isValidateOnly()) {
+ StandardLinkerContext linkerContext = new StandardLinkerContext(
+ TreeLogger.NULL, module, options);
+
+ boolean generateOnShards = true;
+
+ if (!options.isEnabledGeneratingOnShards()) {
+ logger.log(TreeLogger.INFO, "Precompiling on the start node");
+ generateOnShards = false;
+ } else if (!linkerContext.allLinkersAreShardable()) {
+ TreeLogger legacyLinkersLogger = logger.branch(TreeLogger.INFO,
+ "Precompiling on the start node, because some linkers are not updated");
+ for (Linker linker : linkerContext.findUnshardableLinkers()) {
+ legacyLinkersLogger.log(TreeLogger.INFO, "Linker"
+ + linker.getClass().getCanonicalName() + " is not updated");
+ }
+ generateOnShards = false;
+ } else if (options.isValidateOnly()) {
// Don't bother running on shards for just a validation run
generateOnShards = false;
} else if (options.getDumpSignatureFile() != null) {
diff --git a/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java b/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java
index caacaec..65e8fe4 100644
--- a/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java
+++ b/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java
@@ -79,8 +79,7 @@
@Override
public Collection<PermutationWorker> getWorkers(TreeLogger logger,
UnifiedAst unifiedAst, int numWorkers) throws UnableToCompleteException {
- logger.log(TreeLogger.SPAM,
- "Creating ThreadedPermutationWorkers");
+ logger.log(TreeLogger.SPAM, "Creating ThreadedPermutationWorkers");
numWorkers = Math.min(numWorkers, Integer.getInteger(MAX_THREADS_PROPERTY,
1));
@@ -100,8 +99,7 @@
@Override
public void init(TreeLogger logger) throws UnableToCompleteException {
- logger.log(TreeLogger.SPAM,
- "Initializing ThreadedPermutationWorkerFactory");
+ logger.log(TreeLogger.SPAM, "Initializing ThreadedPermutationWorkerFactory");
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index c7d07f5..c4bbe8a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -206,7 +206,7 @@
*
* @param logger the logger to use
* @param unifiedAst the result of a
- * {@link #precompile(TreeLogger, WebModeCompilerFrontEnd, String[], JJSOptions, boolean)}
+ * {@link #precompile(TreeLogger, ModuleDef, RebindPermutationOracle, String[], String[], JJSOptions, boolean)}
* @param permutation the permutation to compile
* @return the output JavaScript
* @throws UnableToCompleteException if an error other than
@@ -219,7 +219,8 @@
PropertyOracle[] propertyOracles = permutation.getPropertyOracles();
int permutationId = permutation.getId();
Map<String, String> rebindAnswers = permutation.getRebindAnswers();
- logger.log(TreeLogger.INFO, "Compiling permutation " + permutationId + "...");
+ logger.log(TreeLogger.INFO, "Compiling permutation " + permutationId
+ + "...");
long permStart = System.currentTimeMillis();
try {
if (JProgram.isTracingEnabled()) {
@@ -660,7 +661,7 @@
// remove same parameters value
didChange = SameParameterValueOptimizer.exec(jprogram) || didChange;
}
-
+
// prove that any types that have been culled from the main tree are
// unreferenced due to type tightening?
diff --git a/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java b/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java
index 269fdbd..b3543df 100644
--- a/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java
+++ b/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java
@@ -17,6 +17,7 @@
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.impl.HostedModeLinker;
import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
import com.google.gwt.dev.cfg.ModuleDef;
@@ -552,12 +553,13 @@
throws UnableToCompleteException {
logger.log(TreeLogger.TRACE,
"Generating a script selection script for module " + moduleName);
-
- StandardLinkerContext context = new StandardLinkerContext(logger,
- getModuleDef(logger, moduleName), new JJSOptionsImpl());
+ ModuleDef module = getModuleDef(logger, moduleName);
+ StandardLinkerContext context = new StandardLinkerContext(logger, module,
+ new JJSOptionsImpl());
+ ArtifactSet artifacts = context.getArtifactsForPublicResources(logger,
+ module);
HostedModeLinker linker = new HostedModeLinker();
- return linker.generateSelectionScript(logger, context,
- context.getArtifacts());
+ return linker.generateSelectionScript(logger, context, artifacts);
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/util/NullOutputFileSet.java b/dev/core/src/com/google/gwt/dev/util/NullOutputFileSet.java
index f7e7c4b..959465d 100644
--- a/dev/core/src/com/google/gwt/dev/util/NullOutputFileSet.java
+++ b/dev/core/src/com/google/gwt/dev/util/NullOutputFileSet.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.dev.util;
-import java.io.IOException;
import java.io.OutputStream;
/**
@@ -24,15 +23,15 @@
public class NullOutputFileSet extends OutputFileSet {
static class NullOutputStream extends OutputStream {
@Override
- public void write(byte[] b) throws IOException {
+ public void write(byte[] b) {
}
@Override
- public void write(byte[] b, int i, int j) throws IOException {
+ public void write(byte[] b, int i, int j) {
}
@Override
- public void write(int b) throws IOException {
+ public void write(int b) {
}
}
@@ -45,7 +44,8 @@
}
@Override
- public OutputStream openForWrite(String path, long lastModifiedTime) {
+ protected OutputStream createNewOutputStream(String path,
+ long lastModifiedTime) {
return new NullOutputStream();
}
}
diff --git a/dev/core/src/com/google/gwt/dev/util/OutputFileSet.java b/dev/core/src/com/google/gwt/dev/util/OutputFileSet.java
index b29d355..04bff20 100644
--- a/dev/core/src/com/google/gwt/dev/util/OutputFileSet.java
+++ b/dev/core/src/com/google/gwt/dev/util/OutputFileSet.java
@@ -17,17 +17,24 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Set;
/**
* An abstract set of files that a linker links into.
*/
public abstract class OutputFileSet {
private final String pathDescription;
+ private final Set<String> pathsSeen = new HashSet<String>();
protected OutputFileSet(String pathDescription) {
this.pathDescription = pathDescription;
}
+ public boolean alreadyContains(String path) {
+ return pathsSeen.contains(path);
+ }
+
public abstract void close() throws IOException;
/**
@@ -39,6 +46,17 @@
return pathDescription;
}
- public abstract OutputStream openForWrite(String path, long lastModifiedTime)
- throws IOException;
+ public final OutputStream openForWrite(String path) throws IOException {
+ int lastModifiedTime = -1;
+ return openForWrite(path, lastModifiedTime);
+ }
+
+ public OutputStream openForWrite(String path, long lastModifiedTime)
+ throws IOException {
+ pathsSeen.add(path);
+ return createNewOutputStream(path, lastModifiedTime);
+ }
+
+ protected abstract OutputStream createNewOutputStream(String path,
+ long lastModifiedTime) throws IOException;
}
diff --git a/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnDirectory.java b/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnDirectory.java
index b4a1216..ade5ee3 100644
--- a/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnDirectory.java
+++ b/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnDirectory.java
@@ -43,12 +43,13 @@
}
@Override
- public OutputStream openForWrite(String path, final long lastModifiedTime)
- throws IOException {
- final File file = makeFileForPath(path);
+ protected OutputStream createNewOutputStream(String path,
+ final long lastModifiedTime) throws IOException {
+ final File file = pathToFile(path);
if (file.exists() && file.lastModified() >= lastModifiedTime) {
return new NullOutputStream();
}
+
mkdirs(file.getParentFile());
return new FileOutputStream(file) {
@Override
@@ -59,14 +60,6 @@
};
}
- private File makeFileForPath(String path) {
- File file = dir;
- for (String part : (prefix + path).split("/")) {
- file = new File(file, part);
- }
- return file;
- }
-
/**
* A faster bulk version of {@link File#mkdirs()} that avoids recreating the
* same directory multiple times.
@@ -85,4 +78,12 @@
dir.mkdir();
}
}
+
+ private File pathToFile(String path) {
+ File file = dir;
+ for (String part : (prefix + path).split("/")) {
+ file = new File(file, part);
+ }
+ return file;
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnJar.java b/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnJar.java
index 856533a..132c5bb 100644
--- a/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnJar.java
+++ b/dev/core/src/com/google/gwt/dev/util/OutputFileSetOnJar.java
@@ -81,7 +81,7 @@
}
@Override
- public OutputStream openForWrite(String path, long lastModifiedTime)
+ public OutputStream createNewOutputStream(String path, long lastModifiedTime)
throws IOException {
mkzipDirs(getParentPath(pathPrefix + path));
diff --git a/dev/core/src/com/google/gwt/dev/util/Util.java b/dev/core/src/com/google/gwt/dev/util/Util.java
index 9713a20..2a9f5b5 100644
--- a/dev/core/src/com/google/gwt/dev/util/Util.java
+++ b/dev/core/src/com/google/gwt/dev/util/Util.java
@@ -49,6 +49,7 @@
import java.io.Serializable;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
+import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -1220,7 +1221,7 @@
}
objectStream.flush();
}
-
+
public static boolean writeStringAsFile(File file, String string) {
FileOutputStream stream = null;
OutputStreamWriter writer = null;
@@ -1264,6 +1265,12 @@
Utility.close(stream);
}
}
+
+ public static void writeStringToStream(OutputStream stream, String string) throws IOException {
+ Writer writer = new OutputStreamWriter(stream, DEFAULT_ENCODING);
+ writer.write(string);
+ writer.close();
+ }
/**
* Writes the contents of a StringBuilder to an OutputStream, encoding
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerDisableGeneratingOnShards.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerDisableGeneratingOnShards.java
new file mode 100644
index 0000000..47bd4f0
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerDisableGeneratingOnShards.java
@@ -0,0 +1,56 @@
+/*
+ * 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.arg;
+
+import com.google.gwt.util.tools.ArgHandlerFlag;
+
+/**
+ * An undocumented option to disable running generators on CompilePerms shards.
+ * This is present as a safety valve, in case something is new with the newer
+ * staging. Note that the old staging is used, regardless of this option's
+ * setting, if any linker is seen that has been updated. Thus, this option is
+ * useful only when all linkers have been updated but nonetheless there is a
+ * problem.
+ */
+public class ArgHandlerDisableGeneratingOnShards extends ArgHandlerFlag {
+ private OptionEnableGeneratingOnShards options;
+
+ public ArgHandlerDisableGeneratingOnShards(
+ OptionEnableGeneratingOnShards options) {
+ this.options = options;
+ }
+
+ @Override
+ public String getPurpose() {
+ return "Disables running generators on CompilePerms shards, even when it would be a likely speedup";
+ }
+
+ @Override
+ public String getTag() {
+ return "-XdisableGeneratingOnShards";
+ }
+
+ @Override
+ public boolean isUndocumented() {
+ return true;
+ }
+
+ @Override
+ public boolean setFlag() {
+ options.setEnabledGeneratingOnShards(false);
+ return true;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerShardPrecompile.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerShardPrecompile.java
index 706895d..98bc731 100644
--- a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerShardPrecompile.java
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerShardPrecompile.java
@@ -18,18 +18,12 @@
import com.google.gwt.util.tools.ArgHandlerFlag;
/**
- * An argument handler that enables running generators on shards.
+ * No effect. Only present for backwards compatibility.
*/
public class ArgHandlerShardPrecompile extends ArgHandlerFlag {
- private OptionEnableGeneratingOnShards options;
-
- public ArgHandlerShardPrecompile(OptionEnableGeneratingOnShards options) {
- this.options = options;
- }
-
@Override
public String getPurpose() {
- return "Enables running generators on CompilePerms shards";
+ return "No effect. Only present for backwards compatibility.";
}
@Override
@@ -38,8 +32,12 @@
}
@Override
+ public boolean isUndocumented() {
+ return true;
+ }
+
+ @Override
public boolean setFlag() {
- options.setEnabledGeneratingOnShards(true);
return true;
}
}
diff --git a/dev/core/test/com/google/gwt/dev/cfg/ModuleDefTest.java b/dev/core/test/com/google/gwt/dev/cfg/ModuleDefTest.java
index 2e8a84f..157703c 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/ModuleDefTest.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/ModuleDefTest.java
@@ -21,6 +21,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.Shardable;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import junit.framework.TestCase;
@@ -33,6 +34,7 @@
*/
public class ModuleDefTest extends TestCase {
+ @Shardable
static class FakeLinker extends Linker {
@Override
public String getDescription() {
@@ -40,12 +42,6 @@
}
@Override
- public ArtifactSet link(TreeLogger logger, LinkerContext context,
- ArtifactSet artifacts) throws UnableToCompleteException {
- return null;
- }
-
- @Override
public ArtifactSet relink(TreeLogger logger, LinkerContext context,
ArtifactSet newArtifacts) throws UnableToCompleteException {
return null;
@@ -53,26 +49,32 @@
}
@LinkerOrder(Order.POST)
+ @Shardable
static class FakeLinkerPost extends FakeLinker {
}
@LinkerOrder(Order.POST)
+ @Shardable
static class FakeLinkerPost2 extends FakeLinker {
}
@LinkerOrder(Order.PRE)
+ @Shardable
static class FakeLinkerPre extends FakeLinker {
}
@LinkerOrder(Order.PRE)
+ @Shardable
static class FakeLinkerPre2 extends FakeLinker {
}
@LinkerOrder(Order.PRIMARY)
+ @Shardable
static class FakeLinkerPrimary extends FakeLinker {
}
@LinkerOrder(Order.PRIMARY)
+ @Shardable
static class FakeLinkerPrimary2 extends FakeLinker {
}
diff --git a/user/src/com/google/gwt/junit/linker/JUnitSymbolMapsLinker.java b/user/src/com/google/gwt/junit/linker/JUnitSymbolMapsLinker.java
index 2a7fc93..723654c 100644
--- a/user/src/com/google/gwt/junit/linker/JUnitSymbolMapsLinker.java
+++ b/user/src/com/google/gwt/junit/linker/JUnitSymbolMapsLinker.java
@@ -20,6 +20,7 @@
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationResult;
import com.google.gwt.core.ext.linker.EmittedArtifact;
+import com.google.gwt.core.ext.linker.Shardable;
import com.google.gwt.core.linker.SymbolMapsLinker;
import java.io.ByteArrayOutputStream;
@@ -28,6 +29,7 @@
* Emits the symbol maps into the application output directory so that the
* JUnitHostImpl servlet can read them.
*/
+@Shardable
public class JUnitSymbolMapsLinker extends SymbolMapsLinker {
@Override
protected void doEmitSymbolMap(TreeLogger logger, ArtifactSet artifacts,
diff --git a/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java b/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
index 733c3f6..1727296 100644
--- a/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
+++ b/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
@@ -22,6 +22,7 @@
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationResult;
import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.Shardable;
import com.google.gwt.core.ext.linker.SymbolData;
import com.google.gwt.core.ext.linker.SyntheticArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
@@ -37,6 +38,7 @@
* Exports data required by server components for directly-evalable RPC.
*/
@LinkerOrder(Order.PRE)
+@Shardable
public class ClientOracleLinker extends AbstractLinker {
private static final String SUFFIX = ".gwt.rpc";
@@ -48,42 +50,44 @@
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
- ArtifactSet artifacts) throws UnableToCompleteException {
- artifacts = new ArtifactSet(artifacts);
+ ArtifactSet artifacts, boolean onePermutation)
+ throws UnableToCompleteException {
+ if (onePermutation) {
+ artifacts = new ArtifactSet(artifacts);
- Map<String, List<String>> allSerializableFields = new HashMap<String, List<String>>();
+ Map<String, List<String>> allSerializableFields = new HashMap<String, List<String>>();
- for (RpcDataArtifact data : artifacts.find(RpcDataArtifact.class)) {
- allSerializableFields.putAll(data.getOperableFields());
+ for (RpcDataArtifact data : artifacts.find(RpcDataArtifact.class)) {
+ allSerializableFields.putAll(data.getOperableFields());
+ }
+
+ for (CompilationResult result : artifacts.find(CompilationResult.class)) {
+ Builder builder = new Builder();
+
+ for (Map.Entry<String, List<String>> entry : allSerializableFields.entrySet()) {
+ builder.setSerializableFields(entry.getKey(), entry.getValue());
+ }
+
+ for (SymbolData symbolData : result.getSymbolMap()) {
+ builder.add(symbolData.getSymbolName(), symbolData.getJsniIdent(),
+ symbolData.getClassName(), symbolData.getMemberName(),
+ symbolData.getTypeId());
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ builder.getOracle().store(out);
+ } catch (IOException e) {
+ // Should generally not happen
+ logger.log(TreeLogger.ERROR, "Unable to store deRPC data", e);
+ throw new UnableToCompleteException();
+ }
+
+ SyntheticArtifact a = emitBytes(logger, out.toByteArray(),
+ result.getStrongName() + SUFFIX);
+ artifacts.add(a);
+ }
}
-
- for (CompilationResult result : artifacts.find(CompilationResult.class)) {
- Builder builder = new Builder();
-
- for (Map.Entry<String, List<String>> entry : allSerializableFields.entrySet()) {
- builder.setSerializableFields(entry.getKey(), entry.getValue());
- }
-
- for (SymbolData symbolData : result.getSymbolMap()) {
- builder.add(symbolData.getSymbolName(), symbolData.getJsniIdent(),
- symbolData.getClassName(), symbolData.getMemberName(),
- symbolData.getTypeId());
- }
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- try {
- builder.getOracle().store(out);
- } catch (IOException e) {
- // Should generally not happen
- logger.log(TreeLogger.ERROR, "Unable to store deRPC data", e);
- throw new UnableToCompleteException();
- }
-
- SyntheticArtifact a = emitBytes(logger, out.toByteArray(),
- result.getStrongName() + SUFFIX);
- artifacts.add(a);
- }
-
return artifacts;
}
diff --git a/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java b/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java
index 65dd36d..5203f94 100644
--- a/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java
+++ b/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java
@@ -38,6 +38,7 @@
import com.google.gwt.rpc.linker.RpcDataArtifact;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.google.gwt.user.client.rpc.impl.RemoteServiceProxy;
+import com.google.gwt.user.linker.rpc.RpcLogArtifact;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwt.user.rebind.rpc.CustomFieldSerializerValidator;
@@ -296,7 +297,7 @@
ctx.commitArtifact(logger, data);
- return "unused";
+ return RpcLogArtifact.UNSPECIFIED_STRONGNAME;
}
private StringBuilder writeArtificialRescues(TypeOracle typeOracle,
diff --git a/user/src/com/google/gwt/user/RemoteService.gwt.xml b/user/src/com/google/gwt/user/RemoteService.gwt.xml
index 8899cb5..af6d99b 100644
--- a/user/src/com/google/gwt/user/RemoteService.gwt.xml
+++ b/user/src/com/google/gwt/user/RemoteService.gwt.xml
@@ -58,6 +58,9 @@
<when-type-assignable class="com.google.gwt.user.client.rpc.RemoteService"/>
</generate-with>
+ <define-linker name="rpcLog" class="com.google.gwt.user.linker.rpc.RpcLogLinker" />
+ <add-linker name="rpcLog" />
+
<define-linker name="rpcPolicyManifest" class="com.google.gwt.user.linker.rpc.RpcPolicyManifestLinker" />
<add-linker name="rpcPolicyManifest" />
</module>
diff --git a/user/src/com/google/gwt/user/linker/rpc/RpcLogArtifact.java b/user/src/com/google/gwt/user/linker/rpc/RpcLogArtifact.java
new file mode 100644
index 0000000..052a6f53
--- /dev/null
+++ b/user/src/com/google/gwt/user/linker/rpc/RpcLogArtifact.java
@@ -0,0 +1,98 @@
+/*
+ * 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.user.linker.rpc;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.linker.Artifact;
+import com.google.gwt.dev.util.DiskCache;
+import com.google.gwt.dev.util.Util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * This artifact holds a log of the reasoning for which types are considered
+ * serializable for a particular RPC interface.
+ */
+public class RpcLogArtifact extends Artifact<RpcLogArtifact> {
+ /**
+ * This strong name indicates that the artifact doesn't really have its own
+ * strong name.
+ */
+ public static final String UNSPECIFIED_STRONGNAME = "UNSPECIFIED";
+ private static DiskCache diskCache = new DiskCache();
+
+ private long diskCacheToken;
+ private final String qualifiedSourceName;
+ private final String serializationPolicyStrongName;
+
+ public RpcLogArtifact(String qualifiedSourceName,
+ String serializationPolicyStrongName, String rpcLog) {
+ super(RpcLogLinker.class);
+ this.qualifiedSourceName = qualifiedSourceName;
+ this.serializationPolicyStrongName = serializationPolicyStrongName;
+ diskCacheToken = diskCache.writeString(rpcLog);
+ }
+
+ public InputStream getContents(TreeLogger logger) {
+ return new ByteArrayInputStream(diskCache.readByteArray(diskCacheToken));
+ }
+
+ public String getQualifiedSourceName() {
+ return qualifiedSourceName;
+ }
+
+ public String getSerializationPolicyStrongName() {
+ return serializationPolicyStrongName;
+ }
+
+ @Override
+ public int hashCode() {
+ return serializationPolicyStrongName.hashCode();
+ }
+
+ @Override
+ protected int compareToComparableArtifact(RpcLogArtifact o) {
+ int comp;
+ comp = qualifiedSourceName.compareTo(o.getQualifiedSourceName());
+ if (comp != 0) {
+ return comp;
+ }
+ return serializationPolicyStrongName.compareTo(o.getSerializationPolicyStrongName());
+ }
+
+ @Override
+ protected Class<RpcLogArtifact> getComparableArtifactType() {
+ return RpcLogArtifact.class;
+ }
+
+ private void readObject(ObjectInputStream stream) throws IOException,
+ ClassNotFoundException {
+ stream.defaultReadObject();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Util.copyNoClose(stream, baos);
+ diskCacheToken = diskCache.writeByteArray(baos.toByteArray());
+ }
+
+ private void writeObject(ObjectOutputStream stream) throws IOException {
+ stream.defaultWriteObject();
+ diskCache.transferToStream(diskCacheToken, stream);
+ }
+}
diff --git a/user/src/com/google/gwt/user/linker/rpc/RpcLogLinker.java b/user/src/com/google/gwt/user/linker/rpc/RpcLogLinker.java
new file mode 100644
index 0000000..58c70e7
--- /dev/null
+++ b/user/src/com/google/gwt/user/linker/rpc/RpcLogLinker.java
@@ -0,0 +1,72 @@
+/*
+ * 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.user.linker.rpc;
+
+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.AbstractLinker;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.EmittedArtifact;
+import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.Shardable;
+import com.google.gwt.core.ext.linker.LinkerOrder.Order;
+
+/**
+ * This linker emits {@link RpcLogArtifact}s as output files.
+ */
+@LinkerOrder(Order.POST)
+@Shardable
+public class RpcLogLinker extends AbstractLinker {
+
+ @Override
+ public String getDescription() {
+ return "RPC log linker";
+ }
+
+ @Override
+ public ArtifactSet link(TreeLogger logger, LinkerContext context,
+ ArtifactSet artifacts, boolean onePermutation)
+ throws UnableToCompleteException {
+ if (onePermutation) {
+ ArtifactSet toReturn = new ArtifactSet(artifacts);
+ logger = logger.branch(TreeLogger.TRACE, "Emitting RPC log files");
+
+ for (CompilationResult result : artifacts.find(CompilationResult.class)) {
+ for (RpcLogArtifact logArt : artifacts.find(RpcLogArtifact.class)) {
+ String policyStrongName = logArt.getSerializationPolicyStrongName();
+ if (policyStrongName.equals(RpcLogArtifact.UNSPECIFIED_STRONGNAME)) {
+ /*
+ * If the artifact has no strong name of its own, use the
+ * compilation strong name.
+ */
+ policyStrongName = result.getStrongName();
+ }
+ EmittedArtifact art = emitInputStream(logger,
+ logArt.getContents(logger), logArt.getQualifiedSourceName() + "-"
+ + policyStrongName + ".rpc.log");
+ art.setPrivate(true);
+ toReturn.add(art);
+ }
+ }
+
+ return toReturn;
+ } else {
+ return artifacts;
+ }
+ }
+}
diff --git a/user/src/com/google/gwt/user/linker/rpc/RpcPolicyManifestLinker.java b/user/src/com/google/gwt/user/linker/rpc/RpcPolicyManifestLinker.java
index 9a841b2..8adb7f4 100644
--- a/user/src/com/google/gwt/user/linker/rpc/RpcPolicyManifestLinker.java
+++ b/user/src/com/google/gwt/user/linker/rpc/RpcPolicyManifestLinker.java
@@ -22,14 +22,34 @@
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.Shardable;
+import com.google.gwt.core.ext.linker.SyntheticArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
+import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.util.Util;
+import com.google.gwt.user.rebind.rpc.ProxyCreator;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
/**
- * Emit a file contating a map of RPC proxy classes to the partial path of the
+ * Emit a file containing a map of RPC proxy classes to the partial path of the
* RPC policy file.
*/
@LinkerOrder(Order.PRE)
+@Shardable
public class RpcPolicyManifestLinker extends AbstractLinker {
+ private static final String MANIFEST_TXT = "manifest.txt";
+
+ /**
+ * The main body of the manifest. It is built up as per-permutation manifests
+ * are looped over.
+ */
+ private StringBuilder manifestBody = new StringBuilder();
@Override
public String getDescription() {
@@ -38,26 +58,79 @@
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
- ArtifactSet artifacts) throws UnableToCompleteException {
- artifacts = new ArtifactSet(artifacts);
+ ArtifactSet artifacts, boolean onePermutation)
+ throws UnableToCompleteException {
+ if (onePermutation) {
+ return artifacts;
+ } else {
+ for (EmittedArtifact art : artifacts.find(EmittedArtifact.class)) {
+ if (art.getPartialPath().startsWith(ProxyCreator.MANIFEST_ARTIFACT_DIR)) {
+ readOneManifest(logger, art.getContents(logger));
+ }
+ }
- StringBuilder contents = new StringBuilder();
- contents.append("# Module ").append(context.getModuleName()).append("\n");
- contents.append("# RPC service class, partial path of RPC policy file\n");
+ ArtifactSet toReturn = new ArtifactSet(artifacts);
+ SyntheticArtifact manifestArt = emitString(logger,
+ generateManifest(context), MANIFEST_TXT);
+ manifestArt.setPrivate(true);
+ toReturn.add(manifestArt);
+ return toReturn;
+ }
+ }
- // Loop over all policy file artifacts
- for (RpcPolicyFileArtifact artifact : artifacts.find(RpcPolicyFileArtifact.class)) {
- // com.foo.Service, 12345.rpc.txt
- contents.append(artifact.getProxyClass()).append(", ").append(
- artifact.getEmittedArtifact().getPartialPath()).append("\n");
+ /**
+ * Compute a manifest for all RPC policy files seen so far.
+ */
+ private String generateManifest(LinkerContext context) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("# Module " + context.getModuleName() + "\n");
+ sb.append("# RPC service class, partial path of RPC policy file\n");
+ sb.append(manifestBody.toString());
+ return sb.toString();
+ }
+
+ /**
+ * Read one manifest and close the input stream.
+ */
+ private void readOneManifest(TreeLogger logger, InputStream manifestStream)
+ throws UnableToCompleteException {
+ Map<String, String> entries = new HashMap<String, String>();
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ manifestStream, Util.DEFAULT_ENCODING));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ int idx = line.indexOf(':');
+ if (idx < 0) {
+ throw new InternalCompilerException(
+ "invalid selection information line: " + line);
+ }
+ String propName = line.substring(0, idx).trim();
+ String propValue = line.substring(idx + 1).trim();
+ entries.put(propName, propValue);
+ }
+ reader.close();
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Unexpected IOException", e);
+ throw new UnableToCompleteException();
}
- // The name of the linker is prepended as a directory prefix
- EmittedArtifact manifest = emitString(logger, contents.toString(),
- "manifest.txt");
- manifest.setPrivate(true);
- artifacts.add(manifest);
+ String serviceClass = entries.get("serviceClass");
+ if (serviceClass == null) {
+ logger.log(TreeLogger.ERROR,
+ "Internal error: manifest file does not include a serviceClass");
+ throw new UnableToCompleteException();
+ }
+ String path = entries.get("path");
+ if (path == null) {
+ logger.log(TreeLogger.ERROR,
+ "Internal error: manifest file does not include a path");
+ throw new UnableToCompleteException();
+ }
- return artifacts;
+ manifestBody.append(serviceClass);
+ manifestBody.append(", ");
+ manifestBody.append(path);
+ manifestBody.append("\n");
}
}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
index d59e6f7..6a67d73 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -48,7 +48,7 @@
import com.google.gwt.user.client.rpc.impl.FailingRequestBuilder;
import com.google.gwt.user.client.rpc.impl.RemoteServiceProxy;
import com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.ResponseReader;
-import com.google.gwt.user.linker.rpc.RpcPolicyFileArtifact;
+import com.google.gwt.user.linker.rpc.RpcLogArtifact;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
@@ -59,7 +59,9 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
+import java.io.Writer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -72,6 +74,11 @@
* as well as the necessary type and field serializers.
*/
public class ProxyCreator {
+ /**
+ * The directory within which RPC manifests are placed for individual
+ * permutations.
+ */
+ public static final String MANIFEST_ARTIFACT_DIR = "rpcPolicyManifest/manifests";
private static final Map<JPrimitiveType, ResponseReader> JPRIMITIVETYPE_TO_RESPONSEREADER = new HashMap<JPrimitiveType, ResponseReader>();
@@ -263,19 +270,17 @@
throw new UnableToCompleteException();
}
- // Create a resource file to receive all of the serialization information
- // computed by STOB and mark it as private so it does not end up in the
- // output.
- OutputStream pathInfo = context.tryCreateResource(logger,
- serviceIntf.getQualifiedSourceName() + ".rpc.log");
- PrintWriter writer = null;
+ // Decide what types to send in each direction.
+ // Log the decisions to a string that will be written later in this method
SerializableTypeOracle typesSentFromBrowser;
SerializableTypeOracle typesSentToBrowser;
- try {
- writer = new PrintWriter(pathInfo);
+ String rpcLog;
+ {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
- typesSentFromBrowserBuilder.setLogOutputStream(pathInfo);
- typesSentToBrowserBuilder.setLogOutputStream(pathInfo);
+ typesSentFromBrowserBuilder.setLogOutputWriter(writer);
+ typesSentToBrowserBuilder.setLogOutputWriter(writer);
writer.write("====================================\n");
writer.write("Types potentially sent from browser:\n");
@@ -289,13 +294,8 @@
writer.flush();
typesSentToBrowser = typesSentToBrowserBuilder.build(logger);
- if (pathInfo != null) {
- context.commitResource(logger, pathInfo).setPrivate(true);
- }
- } finally {
- if (writer != null) {
- writer.close();
- }
+ writer.close();
+ rpcLog = stringWriter.toString();
}
generateTypeHandlers(logger, context, typesSentFromBrowser,
@@ -321,6 +321,12 @@
srcWriter.commit(logger);
+ // Create an artifact explaining STOB's decisions. It will be emitted by
+ // RpcLogLinker
+ context.commitArtifact(logger, new RpcLogArtifact(
+ serviceIntf.getQualifiedSourceName(), serializationPolicyStrongName,
+ rpcLog));
+
return getProxyQualifiedName();
}
@@ -620,7 +626,7 @@
protected Class<? extends SerializationStreamWriter> getStreamWriterClass() {
return ClientSerializationStreamWriter.class;
}
-
+
protected String writeSerializationPolicyFile(TreeLogger logger,
GeneratorContext ctx, SerializableTypeOracle serializationSto,
SerializableTypeOracle deserializationSto)
@@ -663,8 +669,7 @@
* containing the keyword '@ClientFields', the class name, and a list of
* all potentially serializable client-visible fields.
*/
- if ((type instanceof JClassType)
- && ((JClassType) type).isEnhanced()) {
+ if ((type instanceof JClassType) && ((JClassType) type).isEnhanced()) {
JField[] fields = ((JClassType) type).getFields();
JField[] rpcFields = new JField[fields.length];
int numRpcFields = 0;
@@ -703,8 +708,7 @@
* Record which proxy class created the resource. A manifest will be
* emitted by the RpcPolicyManifestLinker.
*/
- ctx.commitArtifact(logger, new RpcPolicyFileArtifact(
- serviceIntf.getQualifiedSourceName(), resource));
+ emitPolicyFileArtifact(logger, ctx, resource.getPartialPath());
} else {
logger.log(TreeLogger.TRACE,
"SerializationPolicy file for RemoteService '"
@@ -724,6 +728,38 @@
}
}
+ private void emitPolicyFileArtifact(TreeLogger logger,
+ GeneratorContext context, String partialPath)
+ throws UnableToCompleteException {
+ try {
+ String qualifiedSourceName = serviceIntf.getQualifiedSourceName();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Writer writer;
+ writer = new OutputStreamWriter(baos,
+ SerializationPolicyLoader.SERIALIZATION_POLICY_FILE_ENCODING);
+ writer.write("serviceClass: " + qualifiedSourceName + "\n");
+ writer.write("path: " + partialPath + "\n");
+ writer.close();
+
+ byte[] manifestBytes = baos.toByteArray();
+ String md5 = Util.computeStrongName(manifestBytes);
+ OutputStream os = context.tryCreateResource(logger, MANIFEST_ARTIFACT_DIR
+ + "/" + md5 + ".txt");
+ os.write(manifestBytes);
+
+ GeneratedResource resource = context.commitResource(logger, os);
+ resource.setPrivate(true);
+ } catch (UnsupportedEncodingException e) {
+ logger.log(TreeLogger.ERROR,
+ SerializationPolicyLoader.SERIALIZATION_POLICY_FILE_ENCODING
+ + " is not supported", e);
+ throw new UnableToCompleteException();
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, null, e);
+ throw new UnableToCompleteException();
+ }
+ }
+
private String getProxyQualifiedName() {
String[] name = Shared.synthesizeTopLevelClassName(serviceIntf,
PROXY_SUFFIX);
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
index f952c9d..e97a099 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -37,7 +37,6 @@
import com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer.TypeParameterFlowInfo;
import com.google.gwt.user.rebind.rpc.TypePaths.TypePath;
-import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.annotation.Annotation;
@@ -60,13 +59,13 @@
/**
* Builds a {@link SerializableTypeOracle} for a given set of root types.
- *
+ *
* <p>
* There are two goals for this builder. First, discover the set of serializable
* types that can be serialized if you serialize one of the root types. Second,
* to make sure that all root types can actually be serialized by GWT.
* </p>
- *
+ *
* <p>
* To find the serializable types, it includes the root types, and then it
* iteratively traverses the type hierarchy and the fields of any type already
@@ -75,7 +74,7 @@
* parameterized type, these exposure values are used to determine how to treat
* the arguments.
* </p>
- *
+ *
* <p>
* A type qualifies for serialization if it or one of its subtypes is
* automatically or manually serializable. Automatic serialization is selected
@@ -86,19 +85,19 @@
* qualifies for both manual and automatic serialization, manual serialization
* is preferred.
* </p>
- *
+ *
* <p>
* Some types may be marked as "enhanced," either automatically by the presence
* of a JDO <code>@PersistenceCapable</code> or JPA <code>@Entity</code> tag on
* the class definition, or manually by extending the 'rpc.enhancedClasses'
* configuration property in the GWT module XML file. For example, to manually
* mark the class com.google.myapp.MyPersistentClass as enhanced, use:
- *
+ *
* <pre>
* <extend-configuration-property name='rpc.enhancedClasses'
* value='com.google.myapp.MyPersistentClass'/>
* </pre>
- *
+ *
* <p>
* Enhanced classes are checked for the presence of additional serializable
* fields on the server that were not defined in client code as seen by the GWT
@@ -117,12 +116,6 @@
class TypeInfoComputed {
/**
- * All instantiable types found when this type was quaried, including the
- * type itself.
- */
- private Set<JClassType> instantiableTypes = new HashSet<JClassType>();
-
- /**
* <code>true</code> if the type is assignable to {@link IsSerializable} or
* {@link java.io.Serializable Serializable}.
*/
@@ -152,6 +145,12 @@
private boolean instantiableSubtypes;
/**
+ * All instantiable types found when this type was quaried, including the
+ * type itself.
+ */
+ private Set<JClassType> instantiableTypes = new HashSet<JClassType>();
+
+ /**
* Custom field serializer or <code>null</code> if there isn't one.
*/
private final JClassType manualSerializer;
@@ -198,9 +197,9 @@
/**
* Returns the internal set of instantiable types for this TIC.
* Modifications to this set are immediately recorded into the TIC as well.
- * TODO(spoon) maybe pass the TIC around instead of the set?
- * then there could be addInstantiableType(JClassType) instead of
- * speccing this to be mutable.
+ * TODO(spoon) maybe pass the TIC around instead of the set? then there
+ * could be addInstantiableType(JClassType) instead of speccing this to be
+ * mutable.
*/
public Set<JClassType> getInstantiableTypes() {
return instantiableTypes;
@@ -352,8 +351,8 @@
JDO_PERSISTENCE_CAPABLE_ANNOTATION = Class.forName(
"javax.jdo.annotations.PersistenceCapable").asSubclass(
Annotation.class);
- JDO_PERSISTENCE_CAPABLE_DETACHABLE_METHOD =
- JDO_PERSISTENCE_CAPABLE_ANNOTATION.getDeclaredMethod("detachable", (Class[]) null);
+ JDO_PERSISTENCE_CAPABLE_DETACHABLE_METHOD = JDO_PERSISTENCE_CAPABLE_ANNOTATION.getDeclaredMethod(
+ "detachable", (Class[]) null);
} catch (ClassNotFoundException e) {
// Ignore, JDO_PERSISTENCE_CAPABLE_ANNOTATION will be null
} catch (NoSuchMethodException e) {
@@ -378,8 +377,8 @@
if (!type.isDefaultInstantiable() && !isManuallySerializable(type)) {
// Warn and return false.
- problems.add(type, type.getParameterizedQualifiedSourceName() +
- " is not default instantiable (it must have a zero-argument "
+ problems.add(type, type.getParameterizedQualifiedSourceName()
+ + " is not default instantiable (it must have a zero-argument "
+ "constructor or no constructors at all) and has no custom "
+ "serializer.", Priority.DEFAULT);
return false;
@@ -396,13 +395,14 @@
/**
* Finds the custom field serializer for a given type.
- *
+ *
* @param typeOracle
* @param type
* @return the custom field serializer for a type or <code>null</code> if
* there is not one
*/
- public static JClassType findCustomFieldSerializer(TypeOracle typeOracle, JType type) {
+ public static JClassType findCustomFieldSerializer(TypeOracle typeOracle,
+ JType type) {
JClassType classOrInterface = type.isClassOrInterface();
if (classOrInterface == null) {
return null;
@@ -532,7 +532,7 @@
* this class should be serialized.
*/
static boolean shouldConsiderFieldsForSerialization(JClassType type,
- TypeFilter filter, ProblemReport problems) {
+ TypeFilter filter, ProblemReport problems) {
if (!isAllowedByFilter(filter, type, problems)) {
return false;
}
@@ -564,18 +564,18 @@
if (!isAccessibleToSerializer(type)) {
// Class is not visible to a serializer class in the same package
problems.add(type, type.getParameterizedQualifiedSourceName()
- + " is not accessible from a class in its same package; it "
- + "will be excluded from the set of serializable types",
+ + " is not accessible from a class in its same package; it "
+ + "will be excluded from the set of serializable types",
Priority.DEFAULT);
return false;
}
if (type.isMemberType() && !type.isStatic()) {
// Non-static member types cannot be serialized
- problems.add(type,
- type.getParameterizedQualifiedSourceName() + " is nested but " +
- "not static; it will be excluded from the set of serializable " +
- "types", Priority.DEFAULT);
+ problems.add(type, type.getParameterizedQualifiedSourceName()
+ + " is nested but "
+ + "not static; it will be excluded from the set of serializable "
+ + "types", Priority.DEFAULT);
return false;
}
}
@@ -674,11 +674,10 @@
}
private static boolean isAllowedByFilter(TypeFilter filter,
- JClassType classType, ProblemReport problems) {
+ JClassType classType, ProblemReport problems) {
if (!filter.isAllowed(classType)) {
- problems.add(classType,
- classType.getParameterizedQualifiedSourceName()
- + " is excluded by type filter ", Priority.AUXILIARY);
+ problems.add(classType, classType.getParameterizedQualifiedSourceName()
+ + " is excluded by type filter ", Priority.AUXILIARY);
return false;
}
@@ -716,10 +715,10 @@
* Cache of the {@link JClassType} for {@link Collection}.
*/
private final JGenericType collectionClass;
-
+
private Set<String> enhancedClasses = null;
- private OutputStream logOutputStream;
+ private PrintWriter logOutputWriter;
/**
* Cache of the {@link JClassType} for {@link Map}.
@@ -757,11 +756,11 @@
/**
* Constructs a builder.
- *
+ *
* @param logger
* @param propertyOracle
* @param typeOracle
- *
+ *
* @throws UnableToCompleteException if we fail to find one of our special
* types
*/
@@ -802,12 +801,12 @@
/**
* Builds a {@link SerializableTypeOracle} for a given set of root types.
- *
+ *
* @param logger
- *
+ *
* @return a {@link SerializableTypeOracle} for the specified set of root
* types
- *
+ *
* @throws UnableToCompleteException if there was not at least one
* instantiable type assignable to each of the specified root types
*/
@@ -892,11 +891,11 @@
}
/**
- * Set the {@link OutputStream} which will receive a detailed log of the types
+ * Set the {@link PrintWriter} which will receive a detailed log of the types
* which were examined in order to determine serializability.
*/
- public void setLogOutputStream(OutputStream logOutputStream) {
- this.logOutputStream = logOutputStream;
+ public void setLogOutputWriter(PrintWriter logOutputWriter) {
+ this.logOutputWriter = logOutputWriter;
}
public void setTypeFilter(TypeFilter typeFilter) {
@@ -965,7 +964,8 @@
if (isWildcard != null) {
boolean success = true;
for (JClassType bound : isWildcard.getUpperBounds()) {
- success &= computeTypeInstantiability(localLogger, bound, path, problems).hasInstantiableSubtypes();
+ success &= computeTypeInstantiability(localLogger, bound, path,
+ problems).hasInstantiableSubtypes();
}
tic = getTypeInfoComputed(classType, path, true);
tic.setInstantiableSubtypes(success);
@@ -975,8 +975,8 @@
JArrayType isArray = classType.isArray();
if (isArray != null) {
- TypeInfoComputed arrayTic = checkArrayInstantiable(localLogger, isArray, path,
- problems);
+ TypeInfoComputed arrayTic = checkArrayInstantiable(localLogger, isArray,
+ path, problems);
assert getTypeInfoComputed(classType, path, false) != null;
return arrayTic;
}
@@ -984,12 +984,12 @@
if (classType == typeOracle.getJavaLangObject()) {
/*
* Report an error if the type is or erases to Object since this violates
- * our restrictions on RPC. Should be fatal, but I worry users may have
+ * our restrictions on RPC. Should be fatal, but I worry users may have
* Object-using code they can't readily get out of the class hierarchy.
*/
problems.add(classType,
- "In order to produce smaller client-side code, 'Object' is not " +
- "allowed; please use a more specific type", Priority.DEFAULT);
+ "In order to produce smaller client-side code, 'Object' is not "
+ + "allowed; please use a more specific type", Priority.DEFAULT);
tic = getTypeInfoComputed(classType, path, true);
tic.setInstantiable(false);
return tic;
@@ -1025,18 +1025,17 @@
/**
* Returns <code>true</code> if the fields of the type should be considered
* for serialization.
- *
+ *
* Default access to allow for testing.
*/
boolean shouldConsiderFieldsForSerialization(JClassType type,
ProblemReport problems) {
- return shouldConsiderFieldsForSerialization(type, typeFilter,
- problems);
+ return shouldConsiderFieldsForSerialization(type, typeFilter, problems);
}
/**
* Consider any subtype of java.lang.Object which qualifies for serialization.
- *
+ *
* @param logger
*/
private void checkAllSubtypesOfObject(TreeLogger logger, TypePath parent,
@@ -1064,8 +1063,8 @@
}
}
- private TypeInfoComputed checkArrayInstantiable(TreeLogger logger, JArrayType array,
- TypePath path, ProblemReport problems) {
+ private TypeInfoComputed checkArrayInstantiable(TreeLogger logger,
+ JArrayType array, TypePath path, ProblemReport problems) {
JType leafType = array.getLeafType();
JWildcardType leafWild = leafType.isWildcard();
@@ -1080,7 +1079,7 @@
if (isLeafTypeParameter != null
&& !typeParametersInRootTypes.contains(isLeafTypeParameter)) {
// Don't deal with non root type parameters, but make a TIC entry to
- // save time if it recurs. We assume they're indirectly instantiable.
+ // save time if it recurs. We assume they're indirectly instantiable.
TypeInfoComputed tic = getTypeInfoComputed(array, path, true);
tic.setInstantiableSubtypes(true);
tic.setInstantiable(false);
@@ -1089,7 +1088,7 @@
if (!isAllowedByFilter(array, problems)) {
// Don't deal with filtered out types either, but make a TIC entry to
- // save time if it recurs. We assume they're not instantiable.
+ // save time if it recurs. We assume they're not instantiable.
TypeInfoComputed tic = getTypeInfoComputed(array, path, true);
tic.setInstantiable(false);
return tic;
@@ -1183,8 +1182,8 @@
"Object was reached from a manually serializable type", null),
path, problems);
} else {
- allSucceeded &= computeTypeInstantiability(fieldLogger, fieldType, path,
- problems).hasInstantiableSubtypes();
+ allSucceeded &= computeTypeInstantiability(fieldLogger, fieldType,
+ path, problems).hasInstantiableSubtypes();
}
}
}
@@ -1333,13 +1332,13 @@
* it is applied to be serializable. As a side effect, populates
* {@link #typeToTypeInfoComputed} in the same way as
* {@link #checkTypeInstantiable(TreeLogger, JType, boolean)}.
- *
+ *
* @param logger
* @param baseType - The generic type the parameter is on
* @param paramIndex - The index of the parameter in the generic type
* @param typeArg - An upper bound on the actual argument being applied to the
* generic type
- *
+ *
* @return Whether the a parameterized type can be serializable if
* <code>baseType</code> is the base type and the
* <code>paramIndex</code>th type argument is a subtype of
@@ -1366,13 +1365,12 @@
paramIndex);
if (otherFlowInfo.getExposure() >= 0
&& otherFlowInfo.isTransitivelyAffectedBy(flowInfoForArrayParam)) {
- problems.add(baseType,
- "Cannot serialize type '"
- + baseType.getParameterizedQualifiedSourceName()
- + "' when given an argument of type '"
- + typeArg.getParameterizedQualifiedSourceName()
- + "' because it appears to require serializing arrays "
- + "of unbounded dimension", Priority.DEFAULT);
+ problems.add(baseType, "Cannot serialize type '"
+ + baseType.getParameterizedQualifiedSourceName()
+ + "' when given an argument of type '"
+ + typeArg.getParameterizedQualifiedSourceName()
+ + "' because it appears to require serializing arrays "
+ + "of unbounded dimension", Priority.DEFAULT);
return false;
}
}
@@ -1403,7 +1401,8 @@
default: {
assert (exposure >= TypeParameterExposureComputer.EXPOSURE_MIN_BOUNDED_ARRAY);
- problems.add(getArrayType(typeOracle, exposure, typeArg),
+ problems.add(
+ getArrayType(typeOracle, exposure, typeArg),
"Checking type argument "
+ paramIndex
+ " of type '"
@@ -1475,9 +1474,9 @@
JClassType subtype = candidates.get(i);
String worstMessage = problems.getWorstMessageForType(subtype);
if (worstMessage == null) {
- possibilities[i] = " subtype " +
- subtype.getParameterizedQualifiedSourceName() +
- " is not instantiable";
+ possibilities[i] = " subtype "
+ + subtype.getParameterizedQualifiedSourceName()
+ + " is not instantiable";
} else {
// message with have the form "FQCN some-problem-description"
possibilities[i] = " subtype " + worstMessage;
@@ -1500,8 +1499,7 @@
return tic;
}
- private boolean isAllowedByFilter(JClassType classType,
- ProblemReport problems) {
+ private boolean isAllowedByFilter(JClassType classType, ProblemReport problems) {
return isAllowedByFilter(typeFilter, classType, problems);
}
@@ -1531,12 +1529,10 @@
}
private void logReachableTypes(TreeLogger logger) {
- PrintWriter printWriter = null;
- if (logOutputStream != null) {
+ if (logOutputWriter != null) {
// Route the TreeLogger output to an output stream.
- printWriter = new PrintWriter(logOutputStream);
PrintWriterTreeLogger printWriterTreeLogger = new PrintWriterTreeLogger(
- printWriter);
+ logOutputWriter);
printWriterTreeLogger.setMaxDetail(TreeLogger.ALL);
logger = printWriterTreeLogger;
}
@@ -1571,8 +1567,8 @@
logger.log(TreeLogger.DEBUG, "");
}
- if (printWriter != null) {
- printWriter.flush();
+ if (logOutputWriter != null) {
+ logOutputWriter.flush();
}
}
@@ -1596,8 +1592,8 @@
boolean success = canBeInstantiated(type, problems)
&& shouldConsiderFieldsForSerialization(type, problems);
if (success) {
- logger.log(TreeLogger.DEBUG,
- type.getParameterizedQualifiedSourceName() + " might be instantiable");
+ logger.log(TreeLogger.DEBUG, type.getParameterizedQualifiedSourceName()
+ + " might be instantiable");
}
return success;
}
@@ -1610,7 +1606,7 @@
/**
* Remove serializable types that were visited due to speculative paths but
* are not really needed for serialization.
- *
+ *
* NOTE: This is currently much more limited than it should be. For example, a
* path sensitive prune could remove instantiable types also.
*/
diff --git a/user/test/com/google/gwt/core/ext/LinkerTest.gwt.xml b/user/test/com/google/gwt/core/ext/LinkerTest.gwt.xml
index c015e0c..7350e5c 100644
--- a/user/test/com/google/gwt/core/ext/LinkerTest.gwt.xml
+++ b/user/test/com/google/gwt/core/ext/LinkerTest.gwt.xml
@@ -12,8 +12,7 @@
<!-- implied. License for the specific language governing permissions and -->
<!-- limitations under the License. -->
-<!-- This is an abstract module. Specific linker tests should inherit -->
-<!-- from this module and specify a linker. -->
+<!-- Module for linker tests -->
<module>
<inherits name='com.google.gwt.junit.JUnit' />
diff --git a/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureIFrameLinker.java b/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureIFrameLinker.java
index 33c5acc..56b3222 100644
--- a/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureIFrameLinker.java
+++ b/user/test/com/google/gwt/user/server/runasync/RunAsyncFailureIFrameLinker.java
@@ -15,15 +15,14 @@
*/
package com.google.gwt.user.server.runasync;
-import com.google.gwt.core.ext.linker.LinkerOrder;
-import com.google.gwt.core.ext.linker.LinkerOrder.Order;
+import com.google.gwt.core.ext.linker.Shardable;
import com.google.gwt.core.linker.IFrameLinker;
/**
* Load modules from a custom servlet in order to test download failure
* behavior.
*/
-@LinkerOrder(Order.PRE)
+@Shardable
public class RunAsyncFailureIFrameLinker extends IFrameLinker {
@Override