Adds optional incremental compile to SuperDevMode.
Change-Id: Id85b6ef83864eb4d4d9b8fd2434ed3f4adf5c642
Review-Link: https://gwt-review.googlesource.com/#/c/7142/
diff --git a/dev/BUILD b/dev/BUILD
index 9d16278..841a50b 100644
--- a/dev/BUILD
+++ b/dev/BUILD
@@ -317,6 +317,24 @@
],
)
+gwt_test(
+ name = "tests_dev.IncrementalBuilderTest",
+ data = [
+ "//third_party/java_src/gwt:gwt-dev-full.jar",
+ "//third_party/java_src/gwt:gwt-user-full",
+ "//third_party/java_src/gwt/svn/trunk/user:incremental-build-system-test-res.jar",
+ ],
+ jvm_flags = [
+ "-Dgwt.cp=third_party/java_src/gwt/svn/trunk/user/incremental-build-system-test-res.jar\;third_party/java_src/gwt/gwt-dev-full.jar\;third_party/java_src/gwt/gwt-user-full.jar",
+ "-ea",
+ ],
+ main_class = "com.google.gwt.dev.IncrementalBuilderTest",
+ deps = [
+ ":dev-test-code",
+ "//third_party/java_src/gwt/svn/trunk/user:incremental-build-system-test-res",
+ ],
+)
+
# The "dev" directory is tested by loose tests. Some of them fail because of
# classpath expectations that are invalid in Google's distributed build
# environment.
@@ -329,6 +347,11 @@
exclude = [
"core/test/**/ClassPathEntryTest.java",
"core/test/**/ResourceOracleImplTest.java",
+ ] +
+ # Handled separately because it needs a custom test target with
+ # custom classpath.
+ [
+ "core/test/**/IncrementalBuilderTest.java",
],
)
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java
index da0a65c..d473f17 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java
@@ -92,6 +92,9 @@
System.out.println();
System.out.println("The code server is ready.");
System.out.println("Next, visit: " + url);
+ } catch (UnableToCompleteException e) {
+ // Already logged.
+ System.exit(1);
} catch (Throwable t) {
t.printStackTrace();
System.exit(1);
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/CompilerOptionsImpl.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/CompilerOptionsImpl.java
index c273114..a143bd6 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/CompilerOptionsImpl.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/CompilerOptionsImpl.java
@@ -17,6 +17,7 @@
package com.google.gwt.dev.codeserver;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.cfg.Properties;
import com.google.gwt.dev.jjs.JsOutputOption;
import com.google.gwt.dev.js.JsNamespaceOption;
import com.google.gwt.dev.util.arg.OptionOptimize;
@@ -37,23 +38,31 @@
private final List<String> moduleNames;
private final SourceLevel sourceLevel;
private final boolean failOnError;
- private final boolean strictResources;
+ private final boolean strictPublicResources;
+ private final boolean strictSourceResources;
private final TreeLogger.Type logLevel;
CompilerOptionsImpl(CompileDir compileDir, List<String> moduleNames, SourceLevel sourceLevel,
- boolean failOnError, boolean strictResources, TreeLogger.Type logLevel) {
+ boolean failOnError, boolean strictSourceResources, boolean strictPublicResources,
+ TreeLogger.Type logLevel) {
this.compileDir = compileDir;
- this.libraryPaths = ImmutableList.<String>of();
+ this.libraryPaths = ImmutableList.<String> of();
this.moduleNames = Lists.newArrayList(moduleNames);
this.sourceLevel = sourceLevel;
this.failOnError = failOnError;
- this.strictResources = strictResources;
+ this.strictSourceResources = strictSourceResources;
+ this.strictPublicResources = strictPublicResources;
this.logLevel = logLevel;
}
@Override
- public boolean enforceStrictResources() {
- return strictResources;
+ public boolean enforceStrictPublicResources() {
+ return strictPublicResources;
+ }
+
+ @Override
+ public boolean enforceStrictSourceResources() {
+ return strictSourceResources;
}
@Override
@@ -67,6 +76,11 @@
}
@Override
+ public Properties getFinalProperties() {
+ return null; // handling this in a different way
+ }
+
+ @Override
public int getFragmentCount() {
return -1;
}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java
index f4dd082..77aa526 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java
@@ -42,6 +42,7 @@
* <p>These flags are EXPERIMENTAL and subject to change.</p>
*/
public class Options {
+ private boolean compileIncremental = false;
private boolean noPrecompile = false;
private boolean isCompileTest = false;
private File workDir;
@@ -117,6 +118,13 @@
}
/**
+ * Whether to compile a series of reusable libraries that are linked at the end.
+ */
+ boolean shouldCompileIncremental() {
+ return compileIncremental;
+ }
+
+ /**
* Whether the codeServer should start without precompiling modules.
*/
boolean getNoPrecompile() {
@@ -202,6 +210,7 @@
registerHandler(new ModuleNameArgument());
registerHandler(new FailOnErrorFlag());
registerHandler(new StrictResourcesFlag());
+ registerHandler(new CompileIncrementalFlag());
registerHandler(new ArgHandlerSourceLevel(new OptionSourceLevel() {
@Override
public SourceLevel getSourceLevel() {
@@ -256,6 +265,35 @@
}
}
+ private class CompileIncrementalFlag extends ArgHandlerFlag {
+
+ @Override
+ public String getLabel() {
+ return "incremental";
+ }
+
+ @Override
+ public String getPurposeSnippet() {
+ return "Compile and link the application as a set of separate libraries.";
+ }
+
+ @Override
+ public boolean setFlag(boolean value) {
+ compileIncremental = value;
+ return true;
+ }
+
+ @Override
+ public boolean getDefaultValue() {
+ return false;
+ }
+
+ @Override
+ public boolean isExperimental() {
+ return true;
+ }
+ }
+
private class CompileTestFlag extends ArgHandlerFlag {
@Override
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
index b86810c..00b79c1 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
@@ -23,6 +23,8 @@
import com.google.gwt.dev.Compiler;
import com.google.gwt.dev.CompilerContext;
import com.google.gwt.dev.CompilerOptions;
+import com.google.gwt.dev.IncrementalBuilder;
+import com.google.gwt.dev.IncrementalBuilder.BuildResultStatus;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.ConfigurationProperty;
import com.google.gwt.dev.cfg.ModuleDef;
@@ -48,8 +50,10 @@
* Recompiles a GWT module on demand.
*/
class Recompiler {
+
private final AppSpace appSpace;
private final String originalModuleName;
+ private IncrementalBuilder incrementalBuilder;
private final TreeLogger logger;
private String serverPrefix;
private int compilesDone = 0;
@@ -58,6 +62,8 @@
private AtomicReference<String> moduleName = new AtomicReference<String>(null);
private final AtomicReference<CompileDir> lastBuild = new AtomicReference<CompileDir>();
+ private CompileDir publishedCompileDir;
+ private boolean listenerFailed;
private final AtomicReference<ResourceLoader> resourceLoader =
new AtomicReference<ResourceLoader>();
private final CompilerContext.Builder compilerContextBuilder = new CompilerContext.Builder();
@@ -90,7 +96,7 @@
CompileDir compileDir = makeCompileDir(compileId);
TreeLogger compileLogger = makeCompileLogger(compileDir);
- boolean listenerFailed = false;
+ listenerFailed = false;
try {
options.getRecompileListener().startedCompile(originalModuleName, compileId, compileDir);
} catch (Exception e) {
@@ -100,25 +106,14 @@
boolean success = false;
try {
- CompilerOptions compilerOptions = new CompilerOptionsImpl(
- compileDir, options.getModuleNames(), options.getSourceLevel(),
- options.isFailOnError(), options.enforceStrictResources(), options.getLogLevel());
- compilerContext = compilerContextBuilder.options(compilerOptions).build();
- ModuleDef module = loadModule(compileLogger, bindingProperties);
-
- // Propagates module rename.
- String newModuleName = module.getName();
- moduleName.set(newModuleName);
- compilerOptions = new CompilerOptionsImpl(
- compileDir, Lists.newArrayList(newModuleName), options.getSourceLevel(),
- options.isFailOnError(), options.enforceStrictResources(), options.getLogLevel());
- compilerContext = compilerContextBuilder.options(compilerOptions).build();
-
- success = new Compiler(compilerOptions).run(compileLogger, module);
- lastBuild.set(compileDir); // makes compile log available over HTTP
+ if (options.shouldCompileIncremental()) {
+ success = compileIncremental(compileLogger, compileDir);
+ } else {
+ success = compileMonolithic(compileLogger, bindingProperties, compileDir);
+ }
} finally {
try {
- options.getRecompileListener().finishedCompile(originalModuleName, compileId, success);
+ options.getRecompileListener().finishedCompile(originalModuleName, compilesDone, success);
} catch (Exception e) {
compileLogger.log(TreeLogger.Type.WARN, "listener threw exception", e);
listenerFailed = true;
@@ -131,13 +126,14 @@
}
long elapsedTime = System.currentTimeMillis() - startTime;
- compileLogger.log(TreeLogger.Type.INFO, "Compile completed in " + elapsedTime + " ms");
+ compileLogger.log(TreeLogger.Type.INFO,
+ String.format("%.3fs total -- Compile completed", elapsedTime / 1000d));
if (options.isCompileTest() && listenerFailed) {
throw new UnableToCompleteException();
}
- return compileDir;
+ return publishedCompileDir;
}
synchronized CompileDir noCompile() throws UnableToCompleteException {
@@ -175,6 +171,65 @@
return compileDir;
}
+ private boolean compileIncremental(TreeLogger compileLogger, CompileDir compileDir) {
+ BuildResultStatus buildResultStatus;
+ // Perform a compile.
+ if (incrementalBuilder == null) {
+ // If it's the first compile.
+ ResourceLoader resources = ResourceLoaders.forClassLoader(Thread.currentThread());
+ resources = ResourceLoaders.forPathAndFallback(options.getSourcePath(), resources);
+ this.resourceLoader.set(resources);
+
+ incrementalBuilder = new IncrementalBuilder(originalModuleName,
+ compileDir.getWarDir().getPath(), compileDir.getWorkDir().getPath(),
+ compileDir.getGenDir().getPath(), resourceLoader.get());
+ buildResultStatus = incrementalBuilder.build(compileLogger);
+ } else {
+ // If it's a rebuild.
+ incrementalBuilder.setWarDir(compileDir.getWarDir().getPath());
+ buildResultStatus = incrementalBuilder.rebuild(compileLogger);
+ }
+
+ if (incrementalBuilder.isRootModuleKnown()) {
+ moduleName.set(incrementalBuilder.getRootModuleName());
+ }
+ // Unlike a monolithic compile, the incremental builder can successfully build but have no new
+ // output (for example when no files have changed). So it's important to only publish the new
+ // compileDir if it actually contains output.
+ if (buildResultStatus.isSuccess() && buildResultStatus.outputChanged()) {
+ publishedCompileDir = compileDir;
+ }
+ lastBuild.set(compileDir); // makes compile log available over HTTP
+
+ return buildResultStatus.isSuccess();
+ }
+
+ private boolean compileMonolithic(TreeLogger compileLogger, Map<String, String> bindingProperties,
+ CompileDir compileDir) throws UnableToCompleteException {
+ CompilerOptions compilerOptions = new CompilerOptionsImpl(compileDir,
+ options.getModuleNames(), options.getSourceLevel(), options.isFailOnError(),
+ options.enforceStrictResources(), options.enforceStrictResources(),
+ options.getLogLevel());
+ compilerContext = compilerContextBuilder.options(compilerOptions).build();
+ ModuleDef module = loadModule(compileLogger, bindingProperties);
+
+ // Propagates module rename.
+ String newModuleName = module.getName();
+ moduleName.set(newModuleName);
+ compilerOptions = new CompilerOptionsImpl(compileDir, Lists.newArrayList(newModuleName),
+ options.getSourceLevel(), options.isFailOnError(), options.enforceStrictResources(),
+ options.enforceStrictResources(), options.getLogLevel());
+ compilerContext = compilerContextBuilder.options(compilerOptions).build();
+
+ boolean success = new Compiler(compilerOptions).run(compileLogger, module);
+ if (success) {
+ publishedCompileDir = compileDir;
+ }
+ lastBuild.set(compileDir); // makes compile log available over HTTP
+
+ return success;
+ }
+
/**
* Returns the log from the last compile. (It may be a failed build.)
*/
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/UnmodifiableCompilerOptions.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/UnmodifiableCompilerOptions.java
index ce2afb2..fb15e08 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/UnmodifiableCompilerOptions.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/UnmodifiableCompilerOptions.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.dev.CompilerOptions;
+import com.google.gwt.dev.cfg.Properties;
import com.google.gwt.dev.jjs.JsOutputOption;
import com.google.gwt.dev.js.JsNamespaceOption;
import com.google.gwt.dev.util.arg.SourceLevel;
@@ -94,7 +95,12 @@
}
@Override
- public final void setEnforceStrictResources(boolean strictResources) {
+ public void setEnforceStrictPublicResources(boolean strictPublicResources) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setEnforceStrictSourceResources(boolean strictSourceResources) {
throw new UnsupportedOperationException();
}
@@ -104,6 +110,11 @@
}
@Override
+ public final void setFinalProperties(Properties finalProperties) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void setFragmentCount(int numFragments) {
throw new UnsupportedOperationException();
}
diff --git a/dev/core/src/com/google/gwt/core/ext/impl/ResourceGeneratorUtilImpl.java b/dev/core/src/com/google/gwt/core/ext/impl/ResourceGeneratorUtilImpl.java
index a675b56..6d60b9d 100644
--- a/dev/core/src/com/google/gwt/core/ext/impl/ResourceGeneratorUtilImpl.java
+++ b/dev/core/src/com/google/gwt/core/ext/impl/ResourceGeneratorUtilImpl.java
@@ -50,6 +50,10 @@
generatedFilesByName.put(name, file);
}
+ public static void clearGeneratedFilesByName() {
+ generatedFilesByName.clear();
+ }
+
/**
* Returns the previously recorded generated file with the given name.
*/
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStatementRanges.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStatementRanges.java
index 03478bf..823bb60 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStatementRanges.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStatementRanges.java
@@ -16,6 +16,7 @@
package com.google.gwt.core.ext.linker.impl;
import com.google.gwt.core.ext.linker.StatementRanges;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import java.io.Serializable;
@@ -60,7 +61,7 @@
return ary;
}
- // VisibleForTesting
+ @VisibleForTesting
final int[] ends;
final int[] starts;
diff --git a/dev/core/src/com/google/gwt/dev/BuildTarget.java b/dev/core/src/com/google/gwt/dev/BuildTarget.java
new file mode 100644
index 0000000..a2f1d31
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/BuildTarget.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.RuntimeRebindRuleGenerator;
+import com.google.gwt.dev.resource.Resource;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
+import com.google.gwt.thirdparty.guava.common.base.Function;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
+import com.google.gwt.thirdparty.guava.common.collect.Sets;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/**
+ * Represents a module in a module tree, knows how to build that module into an output library and
+ * checks output library freshness.
+ */
+class BuildTarget {
+
+ public enum OutputFreshness {
+ FRESH, STALE, UNKNOWN;
+ }
+
+ public static final Function<BuildTarget, String> LIBRARY_PATH_FUNCTION =
+ new Function<BuildTarget, String>() {
+ @Override
+ public String apply(@Nullable BuildTarget buildTarget) {
+ return buildTarget.computeLibraryPath();
+ }
+ };
+
+ @VisibleForTesting
+ public static String formatCompilingModuleMessage(String canonicalModuleName) {
+ return "Compiling module " + canonicalModuleName;
+ }
+
+ @VisibleForTesting
+ public static String formatReusingCachedLibraryMessage(String canonicalModuleName) {
+ return "Reusing cached library for " + canonicalModuleName;
+ }
+
+ private final BuildTargetOptions buildTargetOptions;
+ private final String canonicalModuleName;
+ private final List<BuildTarget> dependencyBuildTargets;
+ private ModuleDef module;
+ private OutputFreshness outputFreshness = OutputFreshness.UNKNOWN;
+ private Set<BuildTarget> transitiveDependencyBuildTargets;
+
+ BuildTarget(String canonicalModuleName, BuildTargetOptions buildTargetOptions,
+ BuildTarget... dependencyBuildTargets) {
+ this.canonicalModuleName = canonicalModuleName;
+ this.buildTargetOptions = buildTargetOptions;
+ this.dependencyBuildTargets = Arrays.asList(dependencyBuildTargets);
+ }
+
+ public boolean build(TreeLogger logger) {
+ return build(logger, false);
+ }
+
+ public boolean build(TreeLogger logger, boolean link) {
+ if (outputFreshness == OutputFreshness.FRESH) {
+ logger.log(TreeLogger.SPAM, formatReusingCachedLibraryMessage(canonicalModuleName));
+ return true;
+ }
+
+ // Build all my dependencies before myself.
+ for (BuildTarget dependencyBuildTarget : dependencyBuildTargets) {
+ // If any dependency fails to build.
+ if (!dependencyBuildTarget.build(logger)) {
+ // Then I have failed to build as well.
+ return false;
+ }
+ }
+
+ TreeLogger branch =
+ logger.branch(TreeLogger.INFO, formatCompilingModuleMessage(canonicalModuleName));
+ boolean buildSucceeded;
+ try {
+ RuntimeRebindRuleGenerator.RUNTIME_REBIND_RULE_SOURCES_BY_SHORT_NAME.clear();
+ LibraryCompiler libraryCompiler = new LibraryCompiler(computeCompileOptions(link));
+ libraryCompiler.setResourceLoader(buildTargetOptions.getResourceLoader());
+ buildSucceeded = libraryCompiler.run(branch);
+ module = libraryCompiler.getModule();
+ } catch (Throwable t) {
+ logger.log(TreeLogger.ERROR, t.getMessage());
+ return false;
+ }
+ outputFreshness = OutputFreshness.FRESH;
+ return buildSucceeded;
+ }
+
+ public CompilerOptions computeCompileOptions(boolean link) {
+ CompilerOptions compilerOptions = new CompilerOptionsImpl();
+ // Must compile the canonical name, not name, since after module-renames there may be more
+ // than one module in the classpath with the same name and we don't want to find and recompile
+ // the same one over and over.
+ compilerOptions.setModuleNames(Lists.newArrayList(canonicalModuleName));
+ compilerOptions.setLink(link);
+ compilerOptions.setLogLevel(TreeLogger.ERROR);
+ compilerOptions.setGenDir(new File(buildTargetOptions.getGenDir()));
+ compilerOptions.setWorkDir(new File(buildTargetOptions.getOutputDir()));
+ compilerOptions.setLibraryPaths(Lists.newArrayList(
+ Iterables.transform(getTransitiveDependencyBuildTargets(), LIBRARY_PATH_FUNCTION)));
+ compilerOptions.setFinalProperties(buildTargetOptions.getFinalProperties());
+
+ if (!link) {
+ compilerOptions.setOutputLibraryPath(computeLibraryPath());
+ } else {
+ compilerOptions.setWarDir(new File(buildTargetOptions.getWarDir()));
+ }
+ return compilerOptions;
+ }
+
+ public String computeLibraryPath() {
+ return buildTargetOptions.getOutputDir() + "/" + canonicalModuleName + ".gwtlib";
+ }
+
+ public void computeOutputFreshness(TreeLogger logger) {
+ if (outputFreshness != OutputFreshness.UNKNOWN) {
+ return;
+ }
+
+ for (BuildTarget dependencyBuildTarget : dependencyBuildTargets) {
+ dependencyBuildTarget.computeOutputFreshness(logger);
+ }
+
+ if (module == null) {
+ logger.log(TreeLogger.SPAM,
+ "Library " + canonicalModuleName + " is stale: the module hasn't been loaded yet");
+ outputFreshness = OutputFreshness.STALE;
+ return;
+ }
+
+ for (BuildTarget dependencyBuildTarget : dependencyBuildTargets) {
+ if (dependencyBuildTarget.outputFreshness == OutputFreshness.STALE) {
+ logger.log(TreeLogger.SPAM,
+ "Library " + canonicalModuleName + " is stale: has a stale dependency");
+ outputFreshness = OutputFreshness.STALE;
+ return;
+ }
+ }
+
+ File libraryFile = new File(computeLibraryPath());
+ if (!libraryFile.exists()) {
+ logger.log(TreeLogger.SPAM,
+ "Library " + canonicalModuleName + " is stale: the library file is missing");
+ outputFreshness = OutputFreshness.STALE;
+ return;
+ }
+ long libraryFileLastModified = libraryFile.lastModified();
+ module.refresh();
+ if (libraryFileLastModified < module.getResourceLastModified()) {
+ Set<Resource> newerResources = module.getResourcesNewerThan(libraryFileLastModified);
+ TreeLogger branch = logger.branch(TreeLogger.SPAM,
+ "Library " + canonicalModuleName + " is stale: library is older than some resource(s)");
+ for (Resource newerResource : newerResources) {
+ branch.log(TreeLogger.SPAM, newerResource.getPath() + " has changed");
+ }
+ outputFreshness = OutputFreshness.STALE;
+ return;
+ }
+
+ logger.log(TreeLogger.SPAM, "Library " + canonicalModuleName + " is fresh");
+ outputFreshness = OutputFreshness.FRESH;
+ }
+
+ public String getCanonicalModuleName() {
+ return canonicalModuleName;
+ }
+
+ public List<BuildTarget> getDependencyBuildTargets() {
+ return dependencyBuildTargets;
+ }
+
+ public Set<BuildTarget> getTransitiveDependencyBuildTargets() {
+ if (transitiveDependencyBuildTargets == null) {
+ transitiveDependencyBuildTargets = Sets.newHashSet();
+ transitiveDependencyBuildTargets.addAll(dependencyBuildTargets);
+ for (BuildTarget buildTarget : dependencyBuildTargets) {
+ transitiveDependencyBuildTargets.addAll(buildTarget.getTransitiveDependencyBuildTargets());
+ }
+ }
+ return transitiveDependencyBuildTargets;
+ }
+
+ public boolean isOutputFresh() {
+ return outputFreshness == OutputFreshness.FRESH;
+ }
+
+ public boolean link(TreeLogger logger) {
+ return build(logger, true);
+ }
+
+ public void setModule(ModuleDef module) {
+ this.module = module;
+ }
+
+ public void setOutputFreshness(OutputFreshness outputFreshness) {
+ this.outputFreshness = outputFreshness;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/BuildTargetOptions.java b/dev/core/src/com/google/gwt/dev/BuildTargetOptions.java
new file mode 100644
index 0000000..210557a
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/BuildTargetOptions.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev;
+
+import com.google.gwt.dev.cfg.Properties;
+import com.google.gwt.dev.cfg.ResourceLoader;
+
+/**
+ * An interface for classes that can answer build target option questions.
+ */
+public interface BuildTargetOptions {
+
+ /**
+ * Returns the property object whose values should be considered "final" when deciding which
+ * generators to run during library compilation.
+ */
+ Properties getFinalProperties();
+
+ /**
+ * Returns the directory path into which to dump a copy of any generated source.
+ */
+ String getGenDir();
+
+ /**
+ * Returns the directory path the compiler is free to use to write temporary files which are not
+ * part of final link output.
+ */
+ String getOutputDir();
+
+ /**
+ * Returns the resource loader to use when finding and loading compilable source, build resources
+ * such as PNG and CSS files and public resources such as HTML files.
+ */
+ ResourceLoader getResourceLoader();
+
+ /**
+ * Returns the directory path into which the compiler should write linked output.
+ */
+ String getWarDir();
+}
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index 5de9faa..8a0a96a 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -203,11 +203,15 @@
Precompilation precompilation, Permutation[] perms, int localWorkers,
List<PersistenceBackedObject<PermutationResult>> resultFiles)
throws UnableToCompleteException {
- final TreeLogger branch = logger.branch(TreeLogger.INFO,
- "Compiling " + perms.length + " permutation" + (perms.length > 1 ? "s" : ""));
+ boolean shouldCompileMonolithic = compilerContext.shouldCompileMonolithic();
+ final TreeLogger branch = shouldCompileMonolithic ? (logger.branch(
+ TreeLogger.INFO,
+ "Compiling " + perms.length + " permutation" + (perms.length > 1 ? "s" : ""))) : logger;
PermutationWorkerFactory.compilePermutations(
branch, compilerContext, precompilation, perms, localWorkers, resultFiles);
- logger.log(TreeLogger.INFO, "Compile of permutations succeeded");
+ if (shouldCompileMonolithic) {
+ logger.log(TreeLogger.INFO, "Compile of permutations succeeded");
+ }
}
public static void main(String[] args) {
diff --git a/dev/core/src/com/google/gwt/dev/CompileTaskOptions.java b/dev/core/src/com/google/gwt/dev/CompileTaskOptions.java
index 1313332..58b03e2 100644
--- a/dev/core/src/com/google/gwt/dev/CompileTaskOptions.java
+++ b/dev/core/src/com/google/gwt/dev/CompileTaskOptions.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.dev;
+import com.google.gwt.dev.util.arg.OptionFinalProperties;
import com.google.gwt.dev.util.arg.OptionLibraryPaths;
import com.google.gwt.dev.util.arg.OptionLink;
import com.google.gwt.dev.util.arg.OptionLogLevel;
@@ -26,5 +27,5 @@
* A common set of options for all compile tasks.
*/
public interface CompileTaskOptions extends OptionModuleName, OptionLogLevel, OptionWorkDir,
- OptionOutputLibraryPath, OptionLibraryPaths, OptionLink {
+ OptionOutputLibraryPath, OptionLibraryPaths, OptionFinalProperties, OptionLink {
}
diff --git a/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java b/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java
index 8a5cc41..3ac04cd 100644
--- a/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java
@@ -16,6 +16,7 @@
package com.google.gwt.dev;
import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.dev.cfg.Properties;
import java.io.File;
import java.util.ArrayList;
@@ -26,6 +27,7 @@
*/
class CompileTaskOptionsImpl implements CompileTaskOptions {
+ private Properties finalProperties;
private List<String> libraryPaths = new ArrayList<String>();
private boolean link;
private Type logLevel;
@@ -51,6 +53,7 @@
setWorkDir(other.getWorkDir());
setLibraryPaths(other.getLibraryPaths());
setOutputLibraryPath(other.getOutputLibraryPath());
+ setFinalProperties(other.getFinalProperties());
setLink(other.shouldLink());
}
@@ -59,6 +62,11 @@
}
@Override
+ public Properties getFinalProperties() {
+ return finalProperties;
+ }
+
+ @Override
public List<String> getLibraryPaths() {
return libraryPaths;
}
@@ -84,6 +92,11 @@
}
@Override
+ public void setFinalProperties(Properties finalProperties) {
+ this.finalProperties = finalProperties;
+ }
+
+ @Override
public void setLibraryPaths(List<String> libraryPaths) {
this.libraryPaths.clear();
this.libraryPaths.addAll(libraryPaths);
diff --git a/dev/core/src/com/google/gwt/dev/CompilerContext.java b/dev/core/src/com/google/gwt/dev/CompilerContext.java
index 48b7bd9..98f4046 100644
--- a/dev/core/src/com/google/gwt/dev/CompilerContext.java
+++ b/dev/core/src/com/google/gwt/dev/CompilerContext.java
@@ -24,7 +24,8 @@
import com.google.gwt.dev.javac.MemoryUnitCache;
import com.google.gwt.dev.javac.UnitCache;
import com.google.gwt.dev.resource.ResourceOracle;
-import com.google.gwt.thirdparty.guava.common.collect.Multimap;
+import com.google.gwt.dev.util.TinyCompileSummary;
+import com.google.gwt.thirdparty.guava.common.collect.Sets;
import java.util.Set;
@@ -153,8 +154,8 @@
private boolean compileMonolithic = true;
private LibraryGroup libraryGroup = new ImmutableLibraryGroup();
private LibraryWriter libraryWriter = new NullLibraryWriter();
-
private ModuleDef module;
+ private TinyCompileSummary tinyCompileSummary = new TinyCompileSummary();
// TODO(stalcup): split this into module parsing, precompilation, compilation, and linking option
// sets.
@@ -163,50 +164,6 @@
private ResourceOracle sourceResourceOracle;
private UnitCache unitCache = new MemoryUnitCache();
- /**
- * Walks the parts of the library dependency graph that have not run the given generator
- * referenced by name and accumulates and returns a map from binding property name to newly legal
- * values that were declared in those libraries.<br />
- *
- * The resulting map represents the set of binding property changes that have not yet been taken
- * into account in the output of a particular generator and which may need to trigger the
- * re-execution of said generator.
- */
- public Multimap<String, String> gatherNewBindingPropertyValuesForGenerator(String generatorName) {
- Multimap<String, String> newBindingPropertyValues =
- getLibraryGroup().gatherNewBindingPropertyValuesForGenerator(generatorName);
- newBindingPropertyValues.putAll(libraryWriter.getNewBindingPropertyValuesByName());
- return newBindingPropertyValues;
- }
-
- /**
- * Walks the parts of the library dependency graph that have not run the given generator
- * referenced by name and accumulates and returns a map from configuration property name to newly
- * set values that were declared in those libraries.<br />
- *
- * The resulting map represents the set of configuration property value changes that have not yet
- * been taken into account in the output of a particular generator and which may need to trigger
- * the re-execution of said generator.
- */
- public Multimap<String, String> gatherNewConfigurationPropertyValuesForGenerator(
- String generatorName) {
- Multimap<String, String> newConfigurationPropertyValues =
- getLibraryGroup().gatherNewConfigurationPropertyValuesForGenerator(generatorName);
- newConfigurationPropertyValues.putAll(libraryWriter.getNewConfigurationPropertyValuesByName());
- return newConfigurationPropertyValues;
- }
-
- public Set<String> gatherNewReboundTypeNamesForGenerator(String generatorName) {
- Set<String> newReboundTypeNames =
- getLibraryGroup().gatherNewReboundTypeSourceNamesForGenerator(generatorName);
- newReboundTypeNames.addAll(libraryWriter.getReboundTypeSourceNames());
- return newReboundTypeNames;
- }
-
- public Set<String> gatherOldReboundTypeNamesForGenerator(String generatorName) {
- return getLibraryGroup().gatherOldReboundTypeSourceNamesForGenerator(generatorName);
- }
-
public ResourceOracle getBuildResourceOracle() {
return buildResourceOracle;
}
@@ -227,10 +184,34 @@
return options;
}
+ /**
+ * Returns the set of source names of rebound types that have been processed by the given
+ * Generator.
+ */
+ public Set<String> getProcessedReboundTypeSourceNames(String generatorName) {
+ Set<String> processedReboundTypeSourceNames = Sets.newHashSet();
+ processedReboundTypeSourceNames.addAll(
+ getLibraryWriter().getProcessedReboundTypeSourceNames(generatorName));
+ processedReboundTypeSourceNames.addAll(
+ getLibraryGroup().getProcessedReboundTypeSourceNames(generatorName));
+ return processedReboundTypeSourceNames;
+ }
+
public ResourceOracle getPublicResourceOracle() {
return publicResourceOracle;
}
+ /**
+ * Returns the set of source names of types for which GWT.create() rebind has been requested. The
+ * types may or may not yet have been processed by some Generators.
+ */
+ public Set<String> getReboundTypeSourceNames() {
+ Set<String> reboundTypeSourceNames = Sets.newHashSet();
+ reboundTypeSourceNames.addAll(getLibraryWriter().getReboundTypeSourceNames());
+ reboundTypeSourceNames.addAll(getLibraryGroup().getReboundTypeSourceNames());
+ return reboundTypeSourceNames;
+ }
+
public ResourceOracle getSourceResourceOracle() {
return sourceResourceOracle;
}
@@ -242,4 +223,8 @@
public boolean shouldCompileMonolithic() {
return compileMonolithic;
}
+
+ public TinyCompileSummary getTinyCompileSummary() {
+ return tinyCompileSummary;
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/IncrementalBuilder.java b/dev/core/src/com/google/gwt/dev/IncrementalBuilder.java
new file mode 100644
index 0000000..bd0151f
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/IncrementalBuilder.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.impl.ResourceGeneratorUtilImpl;
+import com.google.gwt.dev.BuildTarget.OutputFreshness;
+import com.google.gwt.dev.cfg.Library;
+import com.google.gwt.dev.cfg.LibraryGroup;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.cfg.Properties;
+import com.google.gwt.dev.cfg.ResourceLoader;
+import com.google.gwt.dev.resource.impl.ResourceOracleImpl;
+import com.google.gwt.dev.resource.impl.ZipFileClassPathEntry;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
+import com.google.gwt.thirdparty.guava.common.base.Joiner;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
+import com.google.gwt.thirdparty.guava.common.collect.Maps;
+import com.google.gwt.thirdparty.guava.common.collect.Sets;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Incrementally builds, links, and rebuilds module trees.
+ */
+public class IncrementalBuilder {
+
+ /**
+ * Represents a combination of whether a build succeeded and whether output changed.
+ */
+ public static enum BuildResultStatus {
+ FAILED(false, false), SUCCESS_NO_CHANGES(false, true), SUCCESS_WITH_CHANGES(true, true);
+
+ private static BuildResultStatus get(boolean success) {
+ return success ? SUCCESS_WITH_CHANGES : FAILED;
+ }
+
+ private boolean outputChanged;
+ private boolean success;
+
+ private BuildResultStatus(boolean outputChanged, boolean success) {
+ this.outputChanged = outputChanged;
+ this.success = success;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public boolean outputChanged() {
+ return outputChanged;
+ }
+ }
+
+ @VisibleForTesting
+ static final String NO_FILES_HAVE_CHANGED = "No files have changed; all output is still fresh.";
+
+ @VisibleForTesting
+ protected static String formatCircularModulePathMessage(List<String> circularModulePath) {
+ return "Can't compile because of a module circular reference:\n "
+ + Joiner.on("\n ").join(circularModulePath);
+ }
+
+ private Map<String, BuildTarget> buildTargetsByCanonicalModuleName = Maps.newLinkedHashMap();
+ private List<List<String>> circularReferenceModuleNameLoops = Lists.newArrayList();
+ private Properties finalProperties;
+ private String genDir;
+ private Set<String> knownCircularlyReferentModuleNames = Sets.newHashSet();
+ private Set<String> moduleReferencePath = Sets.newLinkedHashSet();
+ private String outputDir;
+ private final ResourceLoader resourceLoader;
+ private BuildTarget rootBuildTarget;
+ private ModuleDef rootModule;
+ private final String rootModuleName;
+ private String warDir;
+ private BuildTargetOptions buildTargetOptions = new BuildTargetOptions() {
+
+ @Override
+ public Properties getFinalProperties() {
+ return IncrementalBuilder.this.finalProperties;
+ }
+
+ @Override
+ public String getGenDir() {
+ return IncrementalBuilder.this.genDir;
+ }
+
+ @Override
+ public String getOutputDir() {
+ return IncrementalBuilder.this.outputDir;
+ }
+
+ @Override
+ public ResourceLoader getResourceLoader() {
+ return IncrementalBuilder.this.resourceLoader;
+ }
+
+ @Override
+ public String getWarDir() {
+ return IncrementalBuilder.this.warDir;
+ }
+ };
+
+ public IncrementalBuilder(String rootModuleName, String warDir, String libDir, String genDir,
+ ResourceLoader resourceLoader) {
+ this.rootModuleName = rootModuleName;
+ this.warDir = warDir;
+ this.outputDir = libDir;
+ this.genDir = genDir;
+ this.resourceLoader = resourceLoader;
+ }
+
+ public BuildResultStatus build(TreeLogger logger) {
+ try {
+ logger = logger.branch(TreeLogger.INFO, "Performing an incremental build");
+
+ CompilerContext compilerContext = new CompilerContext.Builder().compileMonolithic(false)
+ .libraryGroup(LibraryGroup.fromLibraries(Lists.<Library> newArrayList(), false)).build();
+ long beforeLoadRootModuleMs = System.currentTimeMillis();
+ rootModule = ModuleDefLoader.loadFromResources(logger, compilerContext, rootModuleName,
+ resourceLoader, false);
+ finalProperties = rootModule.getProperties();
+ long loadRootModuleDurationMs = System.currentTimeMillis() - beforeLoadRootModuleMs;
+ logger.log(TreeLogger.INFO, String.format(
+ "%.3fs -- Parsing and loading root module definition in %s",
+ loadRootModuleDurationMs / 1000d, rootModuleName));
+
+ long beforeCreateTargetGraphMs = System.currentTimeMillis();
+ rootBuildTarget = createBuildTarget(logger, rootModuleName);
+ rootBuildTarget.setModule(rootModule);
+ long createdTargetGraphDurationMs = System.currentTimeMillis() - beforeCreateTargetGraphMs;
+ logger.log(TreeLogger.INFO, String.format("%.3fs -- Creating target graph (%s targets)",
+ createdTargetGraphDurationMs / 1000d, buildTargetsByCanonicalModuleName.size()));
+
+ if (!circularReferenceModuleNameLoops.isEmpty()) {
+ for (List<String> circularReferenceModuleNameLoop : circularReferenceModuleNameLoops) {
+ logger.log(TreeLogger.ERROR,
+ formatCircularModulePathMessage(circularReferenceModuleNameLoop));
+ }
+ throw new UnableToCompleteException();
+ }
+ logLoadedBuildTargetGraph(logger, buildTargetsByCanonicalModuleName);
+
+ long beforeComputeOutputFreshnessMs = System.currentTimeMillis();
+ ModuleDefLoader.clearModuleCache();
+ rootBuildTarget.computeOutputFreshness(logger);
+ long computeOutputFreshnessDurationMs =
+ System.currentTimeMillis() - beforeComputeOutputFreshnessMs;
+ logger.log(TreeLogger.INFO, String.format("%.3fs -- Computing per-target output freshness",
+ computeOutputFreshnessDurationMs / 1000d));
+
+ TreeLogger branch = logger.branch(TreeLogger.INFO, "Compiling target graph");
+ boolean success = rootBuildTarget.link(branch);
+ return BuildResultStatus.get(success);
+ } catch (UnableToCompleteException e) {
+ // The real cause has been logged.
+ return BuildResultStatus.FAILED;
+ }
+ }
+
+ public String getRootModuleName() {
+ if (rootModule == null) {
+ return "UNKNOWN";
+ }
+ return rootModule.getName();
+ }
+
+ public boolean isRootModuleKnown() {
+ return rootModule != null;
+ }
+
+ public BuildResultStatus rebuild(TreeLogger logger) {
+ logger = logger.branch(TreeLogger.INFO, "Performing an incremental rebuild");
+
+ ResourceOracleImpl.clearCache();
+ ZipFileClassPathEntry.clearCache();
+ ModuleDefLoader.clearModuleCache();
+ ResourceGeneratorUtilImpl.clearGeneratedFilesByName();
+
+ long beforeComputeOutputFreshnessMs = System.currentTimeMillis();
+ forgetAllOutputFreshness();
+ rootBuildTarget.computeOutputFreshness(logger);
+ long computeOutputFreshnessDurationMs =
+ System.currentTimeMillis() - beforeComputeOutputFreshnessMs;
+ logger.log(TreeLogger.INFO, String.format("%.3fs -- Computing per-target output freshness",
+ computeOutputFreshnessDurationMs / 1000d));
+
+ if (rootBuildTarget.isOutputFresh()) {
+ logger.log(TreeLogger.INFO, NO_FILES_HAVE_CHANGED);
+ return BuildResultStatus.SUCCESS_NO_CHANGES;
+ }
+
+ TreeLogger branch = logger.branch(TreeLogger.INFO, "Compiling target graph");
+ boolean success = rootBuildTarget.link(branch);
+ return BuildResultStatus.get(success);
+ }
+
+ public void setWarDir(String warDir) {
+ this.warDir = warDir;
+ }
+
+ @VisibleForTesting
+ void clean() {
+ File[] files = new File(outputDir).listFiles();
+ if (files == null) {
+ // nothing to delete
+ return;
+ }
+ for (File file : files) {
+ file.delete();
+ }
+ }
+
+ private BuildTarget createBuildTarget(String canonicalModuleName, BuildTarget... buildTargets) {
+ if (!buildTargetsByCanonicalModuleName.containsKey(canonicalModuleName)) {
+ buildTargetsByCanonicalModuleName.put(canonicalModuleName,
+ new BuildTarget(canonicalModuleName, buildTargetOptions, buildTargets));
+ }
+ return buildTargetsByCanonicalModuleName.get(canonicalModuleName);
+ }
+
+ private BuildTarget createBuildTarget(TreeLogger logger, String moduleName)
+ throws UnableToCompleteException {
+ if (isCircularlyReferent(moduleName)) {
+ // Allow the target graph creation to continue so that all of the circular reference loops can
+ // be gathered.
+ return null;
+ }
+ if (buildTargetsByCanonicalModuleName.containsKey(moduleName)) {
+ return buildTargetsByCanonicalModuleName.get(moduleName);
+ }
+
+ logger.log(TreeLogger.SPAM, String.format("Adding target %s to build graph.", moduleName));
+ moduleReferencePath.add(moduleName);
+
+ List<BuildTarget> dependencyBuildTargets = Lists.newArrayList();
+ for (String dependencyModuleName : rootModule.getDirectDependencies(moduleName)) {
+ dependencyBuildTargets.add(createBuildTarget(logger, dependencyModuleName));
+ }
+ moduleReferencePath.remove(moduleName);
+
+ return createBuildTarget(moduleName, dependencyBuildTargets.toArray(new BuildTarget[0]));
+ }
+
+ private void forgetAllOutputFreshness() {
+ for (BuildTarget buildTarget : buildTargetsByCanonicalModuleName.values()) {
+ buildTarget.setOutputFreshness(OutputFreshness.UNKNOWN);
+ }
+ }
+
+ private boolean isCircularlyReferent(String potentialDuplicateModuleName) {
+ if (knownCircularlyReferentModuleNames.contains(potentialDuplicateModuleName)) {
+ return true;
+ }
+ if (!moduleReferencePath.contains(potentialDuplicateModuleName)) {
+ return false;
+ }
+
+ List<String> circularModuleReferencePath = Lists.newArrayList(moduleReferencePath);
+
+ // Attach the duplicate module name to the end of the loop.
+ circularModuleReferencePath.add(potentialDuplicateModuleName);
+
+ List<String> annotatedCircularModuleReferencePath = Lists.newArrayList();
+ // The current module path only includes libraries but the connections between libraries might
+ // be silently flowing through filesets. Add filesets to the path so that the output is more
+ // readable.
+ for (int moduleNameIndex = 0; moduleNameIndex < circularModuleReferencePath.size() - 1;
+ moduleNameIndex++) {
+ String thisModuleName = circularModuleReferencePath.get(moduleNameIndex);
+ String nextModuleName = circularModuleReferencePath.get(moduleNameIndex + 1);
+
+ annotatedCircularModuleReferencePath.add(
+ thisModuleName + (thisModuleName.equals(potentialDuplicateModuleName) ? " <loop>" : ""));
+
+ List<String> fileSetPath = rootModule.getFileSetPathBetween(thisModuleName, nextModuleName);
+ if (fileSetPath != null) {
+ for (String fileSetModuleName : fileSetPath) {
+ annotatedCircularModuleReferencePath.add(fileSetModuleName + " <fileset>");
+ }
+ }
+ }
+
+ // Attach the duplicate module name to the end of the loop.
+ annotatedCircularModuleReferencePath.add(potentialDuplicateModuleName + " <loop>");
+
+ knownCircularlyReferentModuleNames.addAll(annotatedCircularModuleReferencePath);
+ circularReferenceModuleNameLoops.add(annotatedCircularModuleReferencePath);
+ return true;
+ }
+
+ private void logLoadedBuildTargetGraph(TreeLogger logger,
+ Map<String, BuildTarget> buildTargetsByCanonicalModuleName) {
+ logger.log(TreeLogger.SPAM, "Loaded build target graph:");
+ for (String canonicalModuleName : buildTargetsByCanonicalModuleName.keySet()) {
+ logger.log(TreeLogger.SPAM, "\t" + canonicalModuleName);
+ BuildTarget gwtTarget = buildTargetsByCanonicalModuleName.get(canonicalModuleName);
+ for (BuildTarget dependencyBuildTarget : gwtTarget.getDependencyBuildTargets()) {
+ logger.log(TreeLogger.SPAM, "\t\t" + dependencyBuildTarget.getCanonicalModuleName());
+ }
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/LibraryCompiler.java b/dev/core/src/com/google/gwt/dev/LibraryCompiler.java
index 2505d03..406248c 100644
--- a/dev/core/src/com/google/gwt/dev/LibraryCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/LibraryCompiler.java
@@ -25,13 +25,17 @@
import com.google.gwt.dev.cfg.LibraryWriter;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.cfg.ResourceLoader;
+import com.google.gwt.dev.cfg.ResourceLoaders;
import com.google.gwt.dev.cfg.ZipLibrary;
import com.google.gwt.dev.cfg.ZipLibraryWriter;
-import com.google.gwt.dev.javac.LibraryGroupUnitCache;
+import com.google.gwt.dev.javac.UnitCacheSingleton;
import com.google.gwt.dev.jjs.JsOutputOption;
import com.google.gwt.dev.jjs.PermutationResult;
+import com.google.gwt.dev.url.CloseableJarHandlerFactory;
import com.google.gwt.dev.util.Memory;
import com.google.gwt.dev.util.PersistenceBackedObject;
+import com.google.gwt.dev.util.TinyCompileSummary;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerDeployDir;
import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
@@ -60,16 +64,13 @@
/**
* Executable compiler entry point that constructs a library from an input module and its library
- * dependencies.<br />
- *
+ * dependencies.
+ * <p>
* When compiling the top-level module, you should also pass the -link and -war flags to create the
- * GWT application.<br />
- *
+ * GWT application.
+ * <p>
* This is an alternative to Compiler.java which does a full world compile that neither reads nor
- * writes libraries.<br />
- *
- * EXPERIMENTAL: does not yet work as it depends on changes which have not yet been reviewed and
- * committed.
+ * writes libraries.
*/
public class LibraryCompiler {
@@ -123,17 +124,48 @@
private LibraryGroup libraryGroup;
private ModuleDef module;
private Permutation[] permutations;
+ private ResourceLoader resourceLoader = ResourceLoaders.forClassLoader(Thread.currentThread());
+ private Library resultLibrary;
public LibraryCompiler(CompilerOptions compilerOptions) {
this.compilerOptions = new CompilerOptionsImpl(compilerOptions);
this.compilerContext =
compilerContextBuilder.options(this.compilerOptions).compileMonolithic(false).build();
+ CloseableJarHandlerFactory.installOverride();
+ }
+
+ ModuleDef getModule() {
+ return module;
+ }
+
+ boolean run(TreeLogger logger) {
+ try {
+ normalizeOptions(logger);
+ loadLibraries(logger);
+ loadModule(logger);
+ compileModule(logger);
+ if (compilerOptions.shouldLink()) {
+ linkLibraries(logger);
+ }
+ return true;
+ } catch (UnableToCompleteException e) {
+ // The real cause has been logged.
+ return false;
+ } finally {
+ // Close all zip files, otherwise the JVM may crash on linux.
+ compilerContext.getLibraryGroup().close();
+ if (resultLibrary != null) {
+ resultLibrary.close();
+ }
+ }
+ }
+
+ void setResourceLoader(ResourceLoader resourceLoader) {
+ this.resourceLoader = resourceLoader;
}
private void compileModule(TreeLogger logger) throws UnableToCompleteException {
long beforeCompileMs = System.currentTimeMillis();
- TreeLogger branch =
- logger.branch(TreeLogger.INFO, "Compiling module " + module.getCanonicalName());
LibraryWriter libraryWriter = compilerContext.getLibraryWriter();
try {
@@ -145,7 +177,11 @@
}
}
- Precompilation precompilation = Precompile.precompile(branch, compilerContext);
+ Precompilation precompilation = Precompile.precompile(logger, compilerContext);
+ if (precompilation == null) {
+ // The real cause has been logged.
+ throw new UnableToCompleteException();
+ }
// TODO(stalcup): move to precompile() after params are refactored
if (!compilerOptions.shouldSaveSource()) {
precompilation.removeSourceArtifacts(logger);
@@ -157,9 +193,8 @@
List<PersistenceBackedObject<PermutationResult>> permutationResultFiles =
new ArrayList<PersistenceBackedObject<PermutationResult>>();
permutationResultFiles.add(libraryWriter.getPermutationResultHandle());
- CompilePerms.compile(
- branch, compilerContext, precompilation, permutations, compilerOptions.getLocalWorkers(),
- permutationResultFiles);
+ CompilePerms.compile(logger, compilerContext, precompilation, permutations,
+ compilerOptions.getLocalWorkers(), permutationResultFiles);
compilePermutationsEvent.end();
generatedArtifacts = precompilation.getGeneratedArtifacts();
@@ -170,8 +205,23 @@
}
long durationMs = System.currentTimeMillis() - beforeCompileMs;
- branch.log(TreeLogger.INFO,
- "Library compilation succeeded -- " + String.format("%.3f", durationMs / 1000d) + "s");
+ TreeLogger detailBranch = logger.branch(TreeLogger.INFO,
+ String.format("%.3fs -- Translating Java to Javascript", durationMs / 1000d));
+
+ TinyCompileSummary tinyCompileSummary = compilerContext.getTinyCompileSummary();
+ boolean shouldWarn =
+ tinyCompileSummary.getTypesForGeneratorsCount() + tinyCompileSummary.getTypesForAstCount()
+ > 1500;
+ String recommendation = shouldWarn ? " This module should probably be split into smaller "
+ + "modules or should trigger fewer generators since its current size hurts "
+ + "incremental compiles." : "";
+ detailBranch.log(shouldWarn ? TreeLogger.WARN : TreeLogger.INFO, String.format(
+ "There were %s static source files, %s generated source files, %s types loaded for "
+ + "generators and %s types loaded for AST construction. %s",
+ tinyCompileSummary.getStaticSourceFilesCount(),
+ tinyCompileSummary.getGeneratedSourceFilesCount(),
+ tinyCompileSummary.getTypesForGeneratorsCount(), tinyCompileSummary.getTypesForAstCount(),
+ recommendation));
}
private void linkLibraries(TreeLogger logger) throws UnableToCompleteException {
@@ -180,7 +230,6 @@
// Load up the library that was just created so that it's possible to get a read-reference to
// its contained PermutationResult.
- Library resultLibrary;
try {
resultLibrary = new ZipLibrary(compilerOptions.getOutputLibraryPath());
} catch (IncompatibleLibraryVersionException e) {
@@ -202,26 +251,20 @@
File absPath = new File(compilerOptions.getWarDir(), module.getName());
absPath = absPath.getAbsoluteFile();
- String logMessage = "Linking into " + absPath;
- if (compilerOptions.getExtraDir() != null) {
- File absExtrasPath = new File(compilerOptions.getExtraDir(), module.getName());
- absExtrasPath = absExtrasPath.getAbsoluteFile();
- logMessage += "; Writing extras to " + absExtrasPath;
- }
- TreeLogger branch = logger.branch(TreeLogger.TRACE, logMessage);
try {
- Link.link(branch,
- module, compilerContext.getPublicResourceOracle(), generatedArtifacts, permutations,
- resultFiles, libraryPermutationResults, compilerOptions, compilerOptions);
+ Link.link(TreeLogger.NULL, module, compilerContext.getPublicResourceOracle(),
+ generatedArtifacts, permutations, resultFiles, libraryPermutationResults, compilerOptions,
+ compilerOptions);
+ long durationMs = System.currentTimeMillis() - beforeLinkMs;
+ logger.log(TreeLogger.INFO,
+ String.format("%.3fs -- Successfully linking application", durationMs / 1000d));
} catch (IOException e) {
- // The real cause has been logged.
+ long durationMs = System.currentTimeMillis() - beforeLinkMs;
+ logger.log(TreeLogger.INFO,
+ String.format("%.3fs -- Failing to link application", durationMs / 1000d));
throw new UnableToCompleteException();
}
linkEvent.end();
-
- long durationMs = System.currentTimeMillis() - beforeLinkMs;
- branch.log(TreeLogger.INFO,
- "Library link succeeded -- " + String.format("%.3f", durationMs / 1000d) + "s");
}
private void loadLibraries(TreeLogger logger) throws UnableToCompleteException {
@@ -231,15 +274,31 @@
logger.log(TreeLogger.ERROR, e.getMessage());
throw new UnableToCompleteException();
}
- compilerContext = compilerContextBuilder.libraryGroup(libraryGroup)
- .libraryWriter(new ZipLibraryWriter(compilerOptions.getOutputLibraryPath()))
- .unitCache(new LibraryGroupUnitCache(libraryGroup)).build();
+ libraryGroup.verify(logger);
+
+ try {
+ CloseableJarHandlerFactory.closeStreams(compilerOptions.getOutputLibraryPath());
+ } catch (IOException e) {
+ logger.log(TreeLogger.WARN, String.format("Failed to close old connections to %s. "
+ + "Repeated incremental compiles in the same JVM process may fail.",
+ compilerOptions.getOutputLibraryPath()));
+ }
+
+ ZipLibraryWriter zipLibraryWriter =
+ new ZipLibraryWriter(compilerOptions.getOutputLibraryPath());
+ compilerContext = compilerContextBuilder.libraryGroup(libraryGroup).libraryWriter(
+ zipLibraryWriter).unitCache(UnitCacheSingleton.get(logger, compilerOptions.getWorkDir()))
+ .build();
}
private void loadModule(TreeLogger logger) throws UnableToCompleteException {
- module = ModuleDefLoader.loadFromClassPath(
- logger, compilerContext, compilerOptions.getModuleNames().get(0), false);
+ long beforeLoadModuleMs = System.currentTimeMillis();
+ module = ModuleDefLoader.loadFromResources(logger, compilerContext,
+ compilerOptions.getModuleNames().get(0), resourceLoader, false);
compilerContext = compilerContextBuilder.module(module).build();
+ long durationMs = System.currentTimeMillis() - beforeLoadModuleMs;
+ logger.log(TreeLogger.INFO,
+ String.format("%.3fs -- Parsing and loading module definition", durationMs / 1000d));
}
private void normalizeOptions(TreeLogger logger) throws UnableToCompleteException {
@@ -250,7 +309,7 @@
// Current optimization passes are not safe with only partial data.
compilerOptions.setOptimizationLevel(OptionOptimize.OPTIMIZE_LEVEL_DRAFT);
// Protects against rampant overlapping source inclusion.
- compilerOptions.setEnforceStrictResources(true);
+ compilerOptions.setEnforceStrictSourceResources(true);
// Ensures that output JS identifiers are named consistently in all modules.
compilerOptions.setOutput(JsOutputOption.DETAILED);
// Code splitting isn't possible when you can't trace the entire control flow.
@@ -281,20 +340,4 @@
// Optimize early since permutation compiles will run in process.
compilerOptions.setOptimizePrecompile(true);
}
-
- private boolean run(TreeLogger logger) {
- try {
- normalizeOptions(logger);
- loadLibraries(logger);
- loadModule(logger);
- compileModule(logger);
- if (compilerOptions.shouldLink()) {
- linkLibraries(logger);
- }
- return true;
- } catch (UnableToCompleteException e) {
- // The real cause has been logged.
- return false;
- }
- }
}
diff --git a/dev/core/src/com/google/gwt/dev/PrecompileTaskOptionsImpl.java b/dev/core/src/com/google/gwt/dev/PrecompileTaskOptionsImpl.java
index 26d4acb..3dc7ec5 100644
--- a/dev/core/src/com/google/gwt/dev/PrecompileTaskOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/PrecompileTaskOptionsImpl.java
@@ -70,8 +70,13 @@
}
@Override
- public boolean enforceStrictResources() {
- return jjsOptions.enforceStrictResources();
+ public boolean enforceStrictSourceResources() {
+ return jjsOptions.enforceStrictSourceResources();
+ }
+
+ @Override
+ public boolean enforceStrictPublicResources() {
+ return jjsOptions.enforceStrictPublicResources();
}
@Override
@@ -257,8 +262,13 @@
}
@Override
- public void setEnforceStrictResources(boolean strictResources) {
- jjsOptions.setEnforceStrictResources(strictResources);
+ public void setEnforceStrictSourceResources(boolean strictSourceResources) {
+ jjsOptions.setEnforceStrictSourceResources(strictSourceResources);
+ }
+ @Override
+
+ public void setEnforceStrictPublicResources(boolean strictPublicResources) {
+ jjsOptions.setEnforceStrictPublicResources(strictPublicResources);
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java b/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java
index 564b5e9..04f1df5 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java
@@ -19,6 +19,7 @@
import com.google.gwt.dev.util.collect.IdentityHashSet;
import com.google.gwt.dev.util.collect.Lists;
import com.google.gwt.dev.util.collect.Sets;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -119,6 +120,24 @@
values.addFirst(value);
}
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof BindingProperty) {
+ BindingProperty that = (BindingProperty) object;
+ return Objects.equal(this.name, that.name)
+ && Objects.equal(this.collapsedValues, that.collapsedValues)
+ && Objects.equal(this.conditionalValues, that.conditionalValues)
+ && Objects.equal(this.definedValues, that.definedValues)
+ && Objects.equal(this.fallback, that.fallback)
+ && Objects.equal(this.getFallbackValuesMap(), that.getFallbackValuesMap())
+ && Objects.equal(this.fallbackValues, that.fallbackValues)
+ && Objects.equal(this.provider, that.provider)
+ && Objects.equal(this.providerGenerator, that.providerGenerator)
+ && Objects.equal(this.rootCondition, that.rootCondition);
+ }
+ return false;
+ }
+
/**
* Returns the set of allowed values in sorted order when a certain condition
* is satisfied.
@@ -245,6 +264,12 @@
return rootCondition;
}
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name, collapsedValues, conditionalValues, definedValues, fallback,
+ getFallbackValuesMap(), fallbackValues, provider, providerGenerator, rootCondition);
+ }
+
public boolean hasTargetLibraryDefinedValues() {
return !targetLibraryDefinedValues.isEmpty();
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/CompoundCondition.java b/dev/core/src/com/google/gwt/dev/cfg/CompoundCondition.java
index a923abd..8b5b6cd 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/CompoundCondition.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/CompoundCondition.java
@@ -17,6 +17,7 @@
import com.google.gwt.dev.util.collect.Sets;
import com.google.gwt.thirdparty.guava.common.base.Joiner;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.google.gwt.thirdparty.guava.common.base.Strings;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
@@ -30,7 +31,7 @@
*/
public abstract class CompoundCondition extends Condition {
- private final Conditions conditions = new Conditions();
+ protected final Conditions conditions = new Conditions();
public CompoundCondition(Condition... conditions) {
for (Condition condition : conditions) {
@@ -38,6 +39,15 @@
}
}
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof CompoundCondition) {
+ CompoundCondition that = (CompoundCondition) object;
+ return Objects.equal(this.conditions, that.conditions);
+ }
+ return false;
+ }
+
public Conditions getConditions() {
return conditions;
}
@@ -52,6 +62,11 @@
}
@Override
+ public int hashCode() {
+ return Objects.hashCode(conditions);
+ }
+
+ @Override
public String toSource() {
List<String> conditionSources = Lists.newArrayList();
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java
index c81a136..f036129 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import java.util.Iterator;
@@ -32,6 +33,20 @@
}
@Override
+ public boolean equals(Object object) {
+ if (object instanceof ConditionAll) {
+ ConditionAll that = (ConditionAll) object;
+ return Objects.equal(this.conditions, that.conditions);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(conditions);
+ }
+
+ @Override
protected boolean doEval(TreeLogger logger, DeferredBindingQuery query)
throws UnableToCompleteException {
for (Iterator<Condition> iter = getConditions().iterator(); iter.hasNext();) {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java
index 8aa7ede..dd6f9c9 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import java.util.Iterator;
@@ -31,6 +32,20 @@
}
@Override
+ public boolean equals(Object object) {
+ if (object instanceof ConditionAny) {
+ ConditionAny that = (ConditionAny) object;
+ return Objects.equal(this.conditions, that.conditions);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(conditions);
+ }
+
+ @Override
protected boolean doEval(TreeLogger logger, DeferredBindingQuery query)
throws UnableToCompleteException {
for (Iterator<Condition> iter = getConditions().iterator(); iter.hasNext();) {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java
index 02bc7ef..2ccb5c5 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import java.util.Iterator;
@@ -32,6 +33,20 @@
}
@Override
+ public boolean equals(Object object) {
+ if (object instanceof ConditionNone) {
+ ConditionNone that = (ConditionNone) object;
+ return Objects.equal(this.conditions, that.conditions);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(conditions);
+ }
+
+ @Override
public String toSource() {
return "!(" + super.toSource() + ")";
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenLinkerAdded.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenLinkerAdded.java
index 82ed112..4d8fc5f 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenLinkerAdded.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenLinkerAdded.java
@@ -16,6 +16,7 @@
package com.google.gwt.dev.cfg;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
/**
* A condition that is true when the active linkers include the one specified.
@@ -29,9 +30,24 @@
}
@Override
+ public boolean equals(Object object) {
+ if (object instanceof ConditionWhenLinkerAdded) {
+ ConditionWhenLinkerAdded that = (ConditionWhenLinkerAdded) object;
+ return Objects.equal(this.linkerName, that.linkerName);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(linkerName);
+ }
+
+ @Override
public String toSource() {
- throw new UnsupportedOperationException(
- "Can't perform runtime rebinding with linker presence based conditions");
+ // TODO(stalcup): implement real runtime linker presence detection or else delete this already
+ // deprecated class...
+ return "false";
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
index 4b370d6..0298a3f 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
@@ -22,6 +22,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.util.collect.Sets;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import java.util.List;
import java.util.Set;
@@ -42,11 +43,26 @@
}
@Override
+ public boolean equals(Object object) {
+ if (object instanceof ConditionWhenPropertyIs) {
+ ConditionWhenPropertyIs that = (ConditionWhenPropertyIs) object;
+ return Objects.equal(this.propName, that.propName)
+ && Objects.equal(this.value, that.value);
+ }
+ return false;
+ }
+
+ @Override
public Set<String> getRequiredProperties() {
return Sets.create(propName);
}
@Override
+ public int hashCode() {
+ return Objects.hashCode(propName, value);
+ }
+
+ @Override
public String toSource() {
return String.format(
"@com.google.gwt.lang.RuntimePropertyRegistry::getPropertyValue(*)(\"%s\") == \"%s\"",
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
index c30f54a..559aa68 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
@@ -20,6 +20,7 @@
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.javac.CompilationProblemReporter;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
/**
* A deferred binding condition to determine whether the type being rebound is
@@ -37,11 +38,25 @@
this.assignableToTypeName = assignableToTypeName;
}
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof ConditionWhenTypeAssignableTo) {
+ ConditionWhenTypeAssignableTo that = (ConditionWhenTypeAssignableTo) object;
+ return Objects.equal(this.assignableToTypeName, that.assignableToTypeName);
+ }
+ return false;
+ }
+
public String getAssignableToTypeName() {
return assignableToTypeName;
}
@Override
+ public int hashCode() {
+ return Objects.hashCode(assignableToTypeName);
+ }
+
+ @Override
public String toSource() {
// Should only be used in non-monolithic (library) compiles. Dynamic cast checks are only safe
// when exhaustive cast maps are available as is the case with library compiles.
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java
index 038b5ea..ec6edac 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java
@@ -16,6 +16,7 @@
package com.google.gwt.dev.cfg;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
/**
* A deferred binding condition to determine whether the type being rebound is
@@ -32,6 +33,20 @@
}
@Override
+ public boolean equals(Object object) {
+ if (object instanceof ConditionWhenTypeIs) {
+ ConditionWhenTypeIs that = (ConditionWhenTypeIs) object;
+ return Objects.equal(this.exactTypeSourceName, that.exactTypeSourceName);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(exactTypeSourceName);
+ }
+
+ @Override
public String toSource() {
return String.format("requestTypeClass == @%s::class", exactTypeSourceName);
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Conditions.java b/dev/core/src/com/google/gwt/dev/cfg/Conditions.java
index 097b631..8f7f991 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Conditions.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Conditions.java
@@ -16,6 +16,7 @@
package com.google.gwt.dev.cfg;
import com.google.gwt.dev.util.collect.Lists;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import java.io.Serializable;
import java.util.Iterator;
@@ -36,6 +37,20 @@
}
@Override
+ public boolean equals(Object object) {
+ if (object instanceof Conditions) {
+ Conditions that = (Conditions) object;
+ return Objects.equal(this.list, that.list);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(list);
+ }
+
+ @Override
public Iterator<Condition> iterator() {
return list.iterator();
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConfigurationProperty.java b/dev/core/src/com/google/gwt/dev/cfg/ConfigurationProperty.java
index 29afedc..e01257a 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConfigurationProperty.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConfigurationProperty.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.dev.cfg;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -42,6 +43,11 @@
}
}
+ public void addTargetLibraryValue(String value) {
+ addValue(value);
+ targetLibraryValues.add(value);
+ }
+
public void addValue(String value) {
if (!allowMultipleValues) {
throw new IllegalStateException(
@@ -50,11 +56,6 @@
values.add(value);
}
- public void addTargetLibraryValue(String value) {
- addValue(value);
- targetLibraryValues.add(value);
- }
-
public boolean allowsMultipleValues() {
return allowMultipleValues;
}
@@ -63,6 +64,17 @@
values.clear();
}
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof ConfigurationProperty) {
+ ConfigurationProperty that = (ConfigurationProperty) object;
+ return Objects.equal(this.name, that.name)
+ && Objects.equal(this.allowMultipleValues, that.allowMultipleValues)
+ && Objects.equal(this.values, that.values);
+ }
+ return false;
+ }
+
public List<String> getTargetLibraryValues() {
return ImmutableList.copyOf(targetLibraryValues);
}
@@ -78,6 +90,11 @@
return Collections.unmodifiableList(values);
}
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name, allowMultipleValues, values);
+ }
+
public boolean hasTargetLibraryValues() {
return !targetLibraryValues.isEmpty();
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Libraries.java b/dev/core/src/com/google/gwt/dev/cfg/Libraries.java
index 9c91b2a..64a8e96 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Libraries.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Libraries.java
@@ -60,14 +60,11 @@
"nestedBinaryNamesByEnclosingName.txt";
public static final String NESTED_SOURCE_NAMES_BY_ENCLOSING_NAME_ENTRY_NAME =
"nestedSourceNamesByEnclosingName.txt";
- public static final String NEW_BINDING_PROPERTY_VALUES_BY_NAME_ENTRY_NAME =
- "newBindingPropertyValuesByName.txt";
- public static final String NEW_CONFIGURATION_PROPERTY_VALUES_BY_NAME_ENTRY_NAME =
- "newConfigurationPropertyValuesByName.txt";
public static final String PERMUTATION_RESULT_ENTRY_NAME = "permutationResult.ser";
public static final String PUBLIC_RESOURCE_PATHS_ENTRY_NAME = "publicResourcePaths.txt";
- public static final String RAN_GENERATOR_NAMES_ENTRY_NAME = "ranGeneratorNames.txt";
public static final String REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME = "reboundTypeSourceNames.txt";
+ public static final String PROCESSED_REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME =
+ "processedReboundTypeSourceNames.txt";
public static final String REGULAR_CLASS_FILE_PATHS_ENTRY_NAME = "regularClassFilePaths.txt";
public static final String REGULAR_COMPILATION_UNIT_TYPE_SOURCE_NAMES_ENTRY_NAME =
"regularCompilationUnitTypeSourceNames.txt";
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Library.java b/dev/core/src/com/google/gwt/dev/cfg/Library.java
index 2eb0f76..9fdb01b 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Library.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Library.java
@@ -37,6 +37,11 @@
public interface Library {
/**
+ * Closes all read streams.
+ */
+ void close();
+
+ /**
* Returns a resource handle or null for the provided path.
*/
Resource getBuildResourceByPath(String buildResourcePath);
@@ -93,18 +98,6 @@
Multimap<String, String> getNestedSourceNamesByCompilationUnitName();
/**
- * Returns a mapping from binding property name to a list of values which were made legal for that
- * binding property by this library. Facilitates partial generator execution.
- */
- Multimap<String, String> getNewBindingPropertyValuesByName();
-
- /**
- * Returns a mapping from configuration property name to a value or list of values which were set
- * for that configuration property by this library. Facilitates partial generator execution.
- */
- Multimap<String, String> getNewConfigurationPropertyValuesByName();
-
- /**
* Returns a handle to the serialized permutation result of this library. Final linking relies on
* the permutation result contents.
*/
@@ -113,6 +106,12 @@
PersistenceBackedObject<PermutationResult> getPermutationResultHandle();
/**
+ * Returns a mapping from generator name to the set of source names of types that have been
+ * processed by that generator in this library.
+ */
+ Multimap<String, String> getProcessedReboundTypeSourceNamesByGenerator();
+
+ /**
* Returns a resource handle or null for the provided path.
*/
Resource getPublicResourceByPath(String path);
@@ -124,13 +123,6 @@
Set<String> getPublicResourcePaths();
/**
- * Returns the set of names of generators which were executed for this library and thus whose
- * output is current for this library and all of its libraries. Facilitates partial generator
- * execution.
- */
- Set<String> getRanGeneratorNames();
-
- /**
* Returns the set of source names of types which are the subject of GWT.create() calls in source
* code for this library. This list of types is needed for generator execution and reconstructing
* this list from source would be very costly.
diff --git a/dev/core/src/com/google/gwt/dev/cfg/LibraryGroup.java b/dev/core/src/com/google/gwt/dev/cfg/LibraryGroup.java
index bdd47d3..418e44c 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/LibraryGroup.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/LibraryGroup.java
@@ -13,6 +13,8 @@
*/
package com.google.gwt.dev.cfg;
+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.dev.cfg.Libraries.IncompatibleLibraryVersionException;
import com.google.gwt.dev.javac.CompilationUnit;
@@ -20,20 +22,20 @@
import com.google.gwt.dev.jjs.PermutationResult;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.PersistenceBackedObject;
-import com.google.gwt.thirdparty.guava.common.base.Function;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.base.Predicates;
import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
-import com.google.gwt.thirdparty.guava.common.collect.Iterables;
import com.google.gwt.thirdparty.guava.common.collect.LinkedHashMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Multimap;
+import com.google.gwt.thirdparty.guava.common.collect.SetMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
-import java.util.LinkedList;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -85,6 +87,21 @@
}
}
+ private static final Comparator<Library> LIBRARY_NAME_COMPARATOR = new Comparator<Library>() {
+ @Override
+ public int compare(Library thisLibrary, Library thatLibrary) {
+ return thisLibrary.getLibraryName().compareTo(thatLibrary.getLibraryName());
+ }
+ };
+
+ @VisibleForTesting
+ public static String formatDuplicateCompilationUnitMessage(String compilationUnitTypeSourceName,
+ String oldLibraryName, String newLibraryName) {
+ return String.format("Compilation units must be unique but '%s' is being "
+ + "provided by both the '%s' and '%s' library.", compilationUnitTypeSourceName,
+ oldLibraryName, newLibraryName);
+ }
+
/**
* Factory function that constructs and returns a library group from a list of libraries.
*/
@@ -97,7 +114,6 @@
libraryGroup.buildLibraryIndexes(verifyLibraryReferences);
return libraryGroup;
}
-
/**
* Factory function that constructs and returns a library group from a list of zip library paths.
*/
@@ -120,9 +136,10 @@
private Map<String, Library> librariesByName;
private Map<String, Library> librariesByPublicResourcePath;
private List<PersistenceBackedObject<PermutationResult>> permutationResultHandles;
+ private SetMultimap<String, String> processedReboundTypeSourceNamesByGenerator =
+ HashMultimap.create();
private Set<String> reboundTypeSourceNames;
private List<Library> rootLibraries;
-
private Set<String> superSourceCompilationUnitTypeSourceNames;
protected LibraryGroup() {
@@ -130,6 +147,15 @@
}
/**
+ * Closes all read streams.
+ */
+ public void close() {
+ for (Library library : libraries) {
+ library.close();
+ }
+ }
+
+ /**
* Returns whether a build resource is is available at the given path.
*/
public boolean containsBuildResource(String buildResourcePath) {
@@ -146,61 +172,6 @@
}
/**
- * Walks the parts of the library dependency graph that have not run the given generator
- * referenced by name and accumulates and returns a map from binding property name to newly legal
- * values that were declared in those libraries.
- */
- public Multimap<String, String> gatherNewBindingPropertyValuesForGenerator(String generatorName) {
- Multimap<String, String> newBindingPropertyValuesByName = LinkedHashMultimap.create();
- for (Library libraryPendingGeneratorRun : gatherLibrariesForGenerator(generatorName, false)) {
- newBindingPropertyValuesByName.putAll(
- libraryPendingGeneratorRun.getNewBindingPropertyValuesByName());
- }
- return newBindingPropertyValuesByName;
- }
-
- /**
- * Walks the parts of the library dependency graph that have not run the given generator
- * referenced by name and accumulates and returns a map from configuration property name to newly
- * set values that were declared in those libraries.
- */
- public Multimap<String, String> gatherNewConfigurationPropertyValuesForGenerator(
- String generatorName) {
- Multimap<String, String> newConfigurationPropertyValuesByName = LinkedHashMultimap.create();
- for (Library libraryPendingGeneratorRun : gatherLibrariesForGenerator(generatorName, false)) {
- newConfigurationPropertyValuesByName.putAll(
- libraryPendingGeneratorRun.getNewConfigurationPropertyValuesByName());
- }
- return newConfigurationPropertyValuesByName;
- }
-
- /**
- * Walks the parts of the library dependency graph that have not run the given generator
- * referenced by name and accumulates and returns a set of newly rebound type names.
- */
- public Set<String> gatherNewReboundTypeSourceNamesForGenerator(String generatorName) {
- Set<String> newReboundTypeSourceNames = Sets.newHashSet();
- List<Library> unprocessedLibraries = gatherLibrariesForGenerator(generatorName, false);
- for (Library unprocessedLibrary : unprocessedLibraries) {
- newReboundTypeSourceNames.addAll(unprocessedLibrary.getReboundTypeSourceNames());
- }
- return newReboundTypeSourceNames;
- }
-
- /**
- * Walks the parts of the library dependency graph that have already run the given generator
- * referenced by name and accumulates and returns the set of old rebound type names.
- */
- public Set<String> gatherOldReboundTypeSourceNamesForGenerator(String generatorName) {
- Set<String> oldReboundTypeSourceNames = Sets.newHashSet();
- List<Library> processedLibraries = gatherLibrariesForGenerator(generatorName, true);
- for (Library processedLibrary : processedLibraries) {
- oldReboundTypeSourceNames.addAll(processedLibrary.getReboundTypeSourceNames());
- }
- return oldReboundTypeSourceNames;
- }
-
- /**
* Returns the resource referenced by name if present or null;
*/
public Resource getBuildResourceByPath(String buildResourcePath) {
@@ -295,7 +266,23 @@
}
/**
- * Returns the resource referenced by name if present or null;
+ * Returns a list of source names of types which have already been rebound by the given generator.
+ */
+ public Set<String> getProcessedReboundTypeSourceNames(String generatorName) {
+ if (!processedReboundTypeSourceNamesByGenerator.containsKey(generatorName)) {
+ Set<String> processedReboundTypeSourceNames = Sets.newHashSet();
+ for (Library library : libraries) {
+ processedReboundTypeSourceNames.addAll(
+ library.getProcessedReboundTypeSourceNamesByGenerator().get(generatorName));
+ }
+ processedReboundTypeSourceNamesByGenerator.putAll(generatorName,
+ Collections.unmodifiableSet(processedReboundTypeSourceNames));
+ }
+ return processedReboundTypeSourceNamesByGenerator.get(generatorName);
+ }
+
+ /**
+ * Returns the resource referenced by name if present or null.
*/
public Resource getPublicResourceByPath(String path) {
if (!getLibrariesByPublicResourcePath().containsKey(path)) {
@@ -322,7 +309,8 @@
for (Library library : libraries) {
reboundTypeSourceNames.addAll(library.getReboundTypeSourceNames());
}
- reboundTypeSourceNames = Collections.unmodifiableSet(reboundTypeSourceNames);
+ reboundTypeSourceNames =
+ Collections.unmodifiableSet(reboundTypeSourceNames);
}
return reboundTypeSourceNames;
}
@@ -344,27 +332,33 @@
return superSourceCompilationUnitTypeSourceNames;
}
- // VisibleForTesting
+ public void verify(TreeLogger logger) throws UnableToCompleteException {
+ try {
+ // Forces the building of the mapping from source names of compilation unit types to the
+ // library that provides them, so that there is an opportunity to notice and reject duplicate
+ // providing libraries.
+ getLibrariesByCompilationUnitTypeSourceName();
+ } catch (CollidingCompilationUnitException e) {
+ logger.log(TreeLogger.ERROR, e.getMessage());
+ throw new UnableToCompleteException();
+ }
+ }
+
+ @VisibleForTesting
List<Library> getLibraries() {
return libraries;
}
- // VisibleForTesting
+ @VisibleForTesting
List<Library> getLibraries(Collection<String> libraryNames) {
return Lists.newArrayList(
Maps.filterKeys(librariesByName, Predicates.in(libraryNames)).values());
}
- private Iterable<Library> asLibraries(Set<String> libraryNames) {
- return Iterables.transform(libraryNames, new Function<String, Library>() {
- @Override
- public Library apply(String libraryName) {
- return librariesByName.get(libraryName);
- }
- });
- }
-
private void buildLibraryIndexes(boolean verifyLibraryReferences) {
+ // Start processing with a consistent library order to ensure consistently ordered output.
+ Collections.sort(libraries, LIBRARY_NAME_COMPARATOR);
+
librariesByName = Maps.newLinkedHashMap();
for (Library library : libraries) {
if (librariesByName.containsKey(library.getLibraryName())) {
@@ -375,8 +369,8 @@
librariesByName.put(library.getLibraryName(), library);
}
- Multimap<Library, Library> parentLibrariesByChildLibrary = HashMultimap.create();
- Multimap<Library, Library> childLibrariesByParentLibrary = HashMultimap.create();
+ Multimap<Library, Library> parentLibrariesByChildLibrary = LinkedHashMultimap.create();
+ Multimap<Library, Library> childLibrariesByParentLibrary = LinkedHashMultimap.create();
for (Library parentLibrary : libraries) {
for (String childLibraryName : parentLibrary.getDependencyLibraryNames()) {
Library childLibrary = librariesByName.get(childLibraryName);
@@ -424,38 +418,16 @@
}
/**
- * Walks the library dependency graph and collects a list of libraries that either have or have
- * not run the given generator depending on the given gatherNotProcessed boolean.
+ * Throws an exception if the referenced compilation unit (which is being provided by the
+ * referenced new library) is already being provided by some older library.
*/
- private List<Library> gatherLibrariesForGenerator(
- String generatorName, boolean gatherLibrariesThatHaveAlreadyRunThisGenerator) {
- Set<Library> exploredLibraries = Sets.newHashSet();
- LinkedList<Library> unexploredLibraries = Lists.newLinkedList();
- List<Library> librariesForGenerator = Lists.newArrayList();
-
- unexploredLibraries.addAll(rootLibraries);
- while (!unexploredLibraries.isEmpty()) {
- Library library = unexploredLibraries.removeFirst();
- exploredLibraries.add(library);
-
- boolean libraryHasAlreadyRunThisGenerator =
- library.getRanGeneratorNames().contains(generatorName);
- if (!gatherLibrariesThatHaveAlreadyRunThisGenerator && libraryHasAlreadyRunThisGenerator) {
- // don't gather this one
- continue;
- }
-
- // gather this library
- librariesForGenerator.add(library);
- for (Library dependencyLibrary : asLibraries(library.getDependencyLibraryNames())) {
- if (exploredLibraries.contains(dependencyLibrary)) {
- continue;
- }
- unexploredLibraries.add(dependencyLibrary);
- }
+ private void assertUniquelyProvided(Library newLibrary, String compilationUnitTypeSourceName) {
+ if (librariesByCompilationUnitTypeSourceName.containsKey(compilationUnitTypeSourceName)) {
+ Library oldLibrary =
+ librariesByCompilationUnitTypeSourceName.get(compilationUnitTypeSourceName);
+ throw new CollidingCompilationUnitException(formatDuplicateCompilationUnitMessage(
+ compilationUnitTypeSourceName, oldLibrary.getLibraryName(), newLibrary.getLibraryName()));
}
-
- return librariesForGenerator;
}
private Map<String, Library> getLibrariesByBuildResourcePath() {
@@ -536,7 +508,6 @@
return Collections.unmodifiableMap(librariesByCompilationUnitTypeBinaryName);
}
- // TODO(stalcup): throw an error if more than one version of a type is provided.
private Map<String, Library> getLibrariesByCompilationUnitTypeSourceName() {
if (librariesByCompilationUnitTypeSourceName == null) {
librariesByCompilationUnitTypeSourceName = Maps.newLinkedHashMap();
@@ -545,7 +516,7 @@
for (Library library : libraries) {
for (String compilationUnitTypeSourceName :
library.getRegularCompilationUnitTypeSourceNames()) {
- checkCompilationUnitUnique(library, compilationUnitTypeSourceName);
+ assertUniquelyProvided(library, compilationUnitTypeSourceName);
librariesByCompilationUnitTypeSourceName.put(compilationUnitTypeSourceName, library);
Collection<String> nestedTypeSourceNames = library
@@ -560,7 +531,7 @@
for (Library library : libraries) {
for (String superSourceCompilationUnitTypeSourceName :
library.getSuperSourceCompilationUnitTypeSourceNames()) {
- checkCompilationUnitUnique(library, superSourceCompilationUnitTypeSourceName);
+ assertUniquelyProvided(library, superSourceCompilationUnitTypeSourceName);
librariesByCompilationUnitTypeSourceName.put(superSourceCompilationUnitTypeSourceName,
library);
@@ -576,18 +547,6 @@
return Collections.unmodifiableMap(librariesByCompilationUnitTypeSourceName);
}
- private void checkCompilationUnitUnique(Library newLibrary,
- String compilationUnitTypeSourceName) {
- if (librariesByCompilationUnitTypeSourceName.containsKey(compilationUnitTypeSourceName)) {
- Library oldLibrary =
- librariesByCompilationUnitTypeSourceName.get(compilationUnitTypeSourceName);
- throw new CollidingCompilationUnitException(String.format(
- "Compilation units must be unique but '%s' is being "
- + "provided by both the '%s' and '%s' library.", compilationUnitTypeSourceName,
- oldLibrary.getLibraryName(), newLibrary.getLibraryName()));
- }
- }
-
private Map<String, Library> getLibrariesByPublicResourcePath() {
if (librariesByPublicResourcePath == null) {
librariesByPublicResourcePath = Maps.newLinkedHashMap();
diff --git a/dev/core/src/com/google/gwt/dev/cfg/LibraryWriter.java b/dev/core/src/com/google/gwt/dev/cfg/LibraryWriter.java
index 570e57a..119a32b 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/LibraryWriter.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/LibraryWriter.java
@@ -18,7 +18,6 @@
import com.google.gwt.dev.jjs.PermutationResult;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.PersistenceBackedObject;
-import com.google.gwt.thirdparty.guava.common.collect.Multimap;
import java.util.Set;
@@ -59,49 +58,40 @@
void addGeneratedArtifacts(ArtifactSet generatedArtifacts);
/**
- * Registers newly legal values on some binding property.
- */
- void addNewBindingPropertyValuesByName(String propertyName, Iterable<String> propertyValues);
-
- /**
- * Sets the newly current value(s) on some configuration property.
- */
- void addNewConfigurationPropertyValuesByName(
- String propertyName, Iterable<String> propertyValues);
-
- /**
* Adds a public resource (such as a html, css, or png file).
*/
void addPublicResource(Resource publicResource);
/**
- * Marks a generator as having been executed for this library and all sub libraries.
- */
- void addRanGeneratorName(String generatorName);
-
- /**
- * Returns the map of binding property names to newly legal values.
- */
- Multimap<String, String> getNewBindingPropertyValuesByName();
-
- /**
- * Returns a map of configuration property names to newly set value(s).
- */
- Multimap<String, String> getNewConfigurationPropertyValuesByName();
-
- /**
* Returns a handle to the permutation result object that was constructed as part of the
* compilation for this library.
*/
PersistenceBackedObject<PermutationResult> getPermutationResultHandle();
/**
+ * Returns the set of source names of rebound types that have been processed by the given
+ * Generator.
+ */
+ Set<String> getProcessedReboundTypeSourceNames(String generatorName);
+
+ /**
* Returns the set of source names of types which are the subject of GWT.create() calls in source
* code for this library.
*/
Set<String> getReboundTypeSourceNames();
/**
+ * Registers the type (by it's source name) as having been processed by the given generator.
+ */
+ void markReboundTypeProcessed(String processedReboundTypeSourceName, String generatorName);
+
+ /**
+ * Records the set of names of types which are the subject of GWT.create() calls in source code
+ * for this library.
+ */
+ void markReboundTypesProcessed(Set<String> reboundTypeSourceNames);
+
+ /**
* Records the library name.<br />
*
* Library names are the way that libraries reference one another as dependencies and should be
@@ -110,12 +100,6 @@
void setLibraryName(String libraryName);
/**
- * Records the set of names of types which are the subject of GWT.create() calls in source code
- * for this library.
- */
- void setReboundTypeSourceNames(Set<String> reboundTypeSourceNames);
-
- /**
* Finishes writing all library contents and closes the library.
*/
void write();
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
index 264abe0..7f7a373 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -37,11 +37,14 @@
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.google.gwt.thirdparty.guava.common.base.Preconditions;
import com.google.gwt.thirdparty.guava.common.base.Predicates;
+import com.google.gwt.thirdparty.guava.common.collect.ArrayListMultimap;
import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import com.google.gwt.thirdparty.guava.common.collect.Iterators;
+import com.google.gwt.thirdparty.guava.common.collect.ListMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Multimap;
import com.google.gwt.thirdparty.guava.common.collect.Queues;
@@ -59,6 +62,7 @@
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -101,6 +105,44 @@
EXTERNAL_LIBRARY, TARGET_LIBRARY
}
+ /**
+ * Encapsulates two String names which conceptually represent the starting and ending points of a
+ * path.
+ * <p>
+ * For example in the following module dependency path ["com.acme.MyApp",
+ * "com.acme.widgets.Widgets", "com.google.gwt.user.User"] the fromName is "com.acme.MyApp" and
+ * the toName is "com.google.gwt.user.User" and together they form a PathEndNames instance.
+ * <p>
+ * The purpose of the class is to serve as a key by which to lookup the fileset path that connects
+ * two libraries. It makes it possible to insert fileset entries into a circular module reference
+ * report.
+ */
+ private static class LibraryDependencyEdge {
+
+ private String fromName;
+ private String toName;
+
+ public LibraryDependencyEdge(String fromName, String toName) {
+ this.fromName = fromName;
+ this.toName = toName;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof LibraryDependencyEdge) {
+ LibraryDependencyEdge that = (LibraryDependencyEdge) object;
+ return Objects.equal(this.fromName, that.fromName)
+ && Objects.equal(this.toName, that.toName);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(fromName, toName);
+ }
+ }
+
private static final ResourceFilter NON_JAVA_RESOURCES = new ResourceFilter() {
@Override
public boolean allows(String path) {
@@ -135,6 +177,13 @@
return true;
}
+ private static LinkedList<String> createExtendedCopy(LinkedList<String> list,
+ String extendingElement) {
+ LinkedList<String> extendedCopy = Lists.newLinkedList(list);
+ extendedCopy.add(extendingElement);
+ return extendedCopy;
+ }
+
/**
* All resources found on the public path, specified by <public> directives in
* modules (or the implicit ./public directory). Marked 'lazy' because it does not
@@ -165,6 +214,11 @@
private final DefaultFilters defaultFilters;
+ /**
+ * The canonical module names of dependency library modules per depending library module.
+ */
+ private Multimap<String, String> directDependencyModuleNamesByModuleName = HashMultimap.create();
+
private final List<String> entryPointTypeNames = new ArrayList<String>();
/**
@@ -172,6 +226,20 @@
*/
private final Set<String> externalLibraryCanonicalModuleNames = Sets.newLinkedHashSet();
+ /**
+ * Records the canonical module names for filesets.
+ */
+ private Set<String> filesetModuleNames = Sets.newHashSet();
+
+ /**
+ * A mapping from a pair of starting and ending module names of a path to the ordered list of
+ * names of fileset modules that connect those end points. The mapping allows one to discover what
+ * path of filesets are connecting a pair of libraries when those libraries do not directly depend
+ * on one another. Multiple paths may exist; this map contains only one of them.
+ */
+ private ListMultimap<LibraryDependencyEdge, String> filesetPathPerEdge =
+ ArrayListMultimap.create();
+
private final Set<File> gwtXmlFiles = new HashSet<File>();
private final Set<String> inheritedModules = new HashSet<String>();
@@ -207,17 +275,6 @@
*/
private String nameOverride;
-
- /**
- * The canonical module names of dependency library modules per depending library module.
- */
- private Multimap<String, String> directDependentsModuleNamesByModuleName = HashMultimap.create();
-
- /**
- * Records the canonical module names for filesets.
- */
- private Set<String> filesetModuleNames = Sets.newHashSet();
-
private final Properties properties = new Properties();
private PathPrefixSet publicPrefixSet = new PathPrefixSet();
@@ -263,17 +320,10 @@
}
/**
- * Register a {@code dependentModuleName} as a directDependent of {@code currentModuleName}
+ * Register a {@code dependencyModuleName} as a directDependency of {@code currentModuleName}
*/
- public void addDirectDependent(String currentModuleName, String dependentModuleName) {
- directDependentsModuleNamesByModuleName.put(currentModuleName, dependentModuleName);
- }
-
- /**
- * Registers a module as a fileset.
- */
- public void addFileset(String filesetModuleName) {
- filesetModuleNames.add(filesetModuleName);
+ public void addDirectDependency(String currentModuleName, String dependencyModuleName) {
+ directDependencyModuleNamesByModuleName.put(currentModuleName, dependencyModuleName);
}
public synchronized void addEntryPointTypeName(String typeName) {
@@ -283,6 +333,13 @@
entryPointTypeNames.add(typeName);
}
+ /**
+ * Registers a module as a fileset.
+ */
+ public void addFileset(String filesetModuleName) {
+ filesetModuleNames.add(filesetModuleName);
+ }
+
public void addGwtXmlFile(File xmlFile) {
gwtXmlFiles.add(xmlFile);
}
@@ -580,7 +637,7 @@
*/
public Collection<String> getDirectDependencies(String libraryModuleName) {
assert !filesetModuleNames.contains(libraryModuleName);
- return directDependentsModuleNamesByModuleName.get(libraryModuleName);
+ return directDependencyModuleNamesByModuleName.get(libraryModuleName);
}
public synchronized String[] getEntryPointTypeNames() {
@@ -596,6 +653,10 @@
return externalLibraryCanonicalModuleNames;
}
+ public List<String> getFileSetPathBetween(String fromModuleName, String toModuleName) {
+ return filesetPathPerEdge.get(new LibraryDependencyEdge(fromModuleName, toModuleName));
+ }
+
public synchronized String getFunctionName() {
return getName().replace('.', '_');
}
@@ -838,12 +899,12 @@
}
}
- private void addExternalLibraryCanonicalModuleName(String canonicalModuleName) {
+ private void addExternalLibraryCanonicalModuleName(String externalLibraryCanonicalModuleName) {
// Ignore circular dependencies on self.
- if (canonicalModuleName.equals(getCanonicalName())) {
+ if (externalLibraryCanonicalModuleName.equals(getCanonicalName())) {
return;
}
- externalLibraryCanonicalModuleNames.add(canonicalModuleName);
+ externalLibraryCanonicalModuleNames.add(externalLibraryCanonicalModuleName);
}
private boolean attributeIsForTargetLibrary() {
@@ -882,16 +943,21 @@
* Reduce the direct dependency graph to exclude filesets.
*/
private void computeLibraryDependencyGraph() {
- for (String moduleName : Lists.newArrayList(directDependentsModuleNamesByModuleName.keySet())) {
+ for (String moduleName : Lists.newArrayList(directDependencyModuleNamesByModuleName.keySet())) {
Set<String> libraryModules = Sets.newHashSet();
Set<String> filesetsProcessed = Sets.newHashSet();
// Direct dependents might be libraries or fileset, so add them to a queue of modules
// to process.
- Queue<String> modulesToProcess =
- Queues.newArrayDeque(directDependentsModuleNamesByModuleName.get(moduleName));
- while (!modulesToProcess.isEmpty()) {
- String dependentModuleName = modulesToProcess.poll();
+ Queue<LinkedList<String>> modulePathsToProcess = Queues.newArrayDeque();
+ Collection<String> directDependencyModuleNames =
+ directDependencyModuleNamesByModuleName.get(moduleName);
+ for (String directDependencyModuleName : directDependencyModuleNames) {
+ modulePathsToProcess.add(Lists.newLinkedList(ImmutableList.of(directDependencyModuleName)));
+ }
+ while (!modulePathsToProcess.isEmpty()) {
+ LinkedList<String> dependentModuleNamePath = modulePathsToProcess.poll();
+ String dependentModuleName = dependentModuleNamePath.getLast();
boolean isLibrary = !filesetModuleNames.contains(dependentModuleName);
if (isLibrary) {
@@ -899,6 +965,10 @@
// the library itself.
if (!moduleName.equals(dependentModuleName)) {
libraryModules.add(dependentModuleName);
+
+ dependentModuleNamePath.removeLast();
+ filesetPathPerEdge.putAll(new LibraryDependencyEdge(moduleName, dependentModuleName),
+ dependentModuleNamePath);
}
continue;
}
@@ -907,16 +977,18 @@
// Get the dependencies of the dependent module under consideration and add all those
// that have not been already processed to the queue of modules to process.
- Set<String> notAlreadyProcessed =
- Sets.newHashSet(directDependentsModuleNamesByModuleName.get(dependentModuleName));
- notAlreadyProcessed.removeAll(filesetsProcessed);
- modulesToProcess.addAll(notAlreadyProcessed);
+ Set<String> unProcessedModules =
+ Sets.newHashSet(directDependencyModuleNamesByModuleName.get(dependentModuleName));
+ unProcessedModules.removeAll(filesetsProcessed);
+ for (String unProcessedModule : unProcessedModules) {
+ modulePathsToProcess.add(createExtendedCopy(dependentModuleNamePath, unProcessedModule));
+ }
}
// Rewrite the dependents with the set just computed.
- directDependentsModuleNamesByModuleName.replaceValues(moduleName, libraryModules);
+ directDependencyModuleNamesByModuleName.replaceValues(moduleName, libraryModules);
}
// Remove all fileset entries.
- directDependentsModuleNamesByModuleName.removeAll(filesetModuleNames);
+ directDependencyModuleNamesByModuleName.removeAll(filesetModuleNames);
}
private synchronized void ensureResourcesScanned() {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
index fae7600..f95723d 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
@@ -25,6 +25,7 @@
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
import com.google.gwt.dev.util.xml.ReflectiveParser;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.collect.MapMaker;
import com.google.gwt.util.tools.Utility;
@@ -164,17 +165,6 @@
libraryWriter.setLibraryName(module.getCanonicalName());
libraryWriter.addDependencyLibraryNames(module.getExternalLibraryCanonicalModuleNames());
- // Records binding property defined values that were newly created in this library.
- for (BindingProperty bindingProperty : module.getProperties().getBindingProperties()) {
- libraryWriter.addNewBindingPropertyValuesByName(
- bindingProperty.getName(), bindingProperty.getTargetLibraryDefinedValues());
- }
- // Records configuration property value changes that occurred in this library.
- for (ConfigurationProperty configurationProperty :
- module.getProperties().getConfigurationProperties()) {
- libraryWriter.addNewConfigurationPropertyValuesByName(
- configurationProperty.getName(), configurationProperty.getTargetLibraryValues());
- }
// Saves non java and gwt.xml build resources, like PNG and CSS files.
for (Resource buildResource : module.getBuildResourceOracle().getResources()) {
if (buildResource.getPath().endsWith(".java")
@@ -275,8 +265,12 @@
this.resourceLoader = loader;
}
- public boolean enforceStrictResources() {
- return compilerContext.getOptions().enforceStrictResources();
+ public boolean enforceStrictSourceResources() {
+ return compilerContext.getOptions().enforceStrictSourceResources();
+ }
+
+ public boolean enforceStrictPublicResources() {
+ return compilerContext.getOptions().enforceStrictPublicResources();
}
/**
@@ -352,8 +346,7 @@
}
}
if (moduleURL == null) {
- logger.log(TreeLogger.ERROR,"Unable to find '" + resName + "' on your classpath; "
- + "could be a typo, or maybe you forgot to include a classpath entry for source?");
+ logger.log(TreeLogger.ERROR, formatUnableToFindModuleMessage(resName));
throw new UnableToCompleteException();
}
@@ -390,4 +383,10 @@
Utility.close(r);
}
}
+
+ @VisibleForTesting
+ public static String formatUnableToFindModuleMessage(String moduleResourcePath) {
+ return "Unable to find '" + moduleResourcePath + "' on your classpath; "
+ + "could be a typo, or maybe you forgot to include a classpath entry for source?";
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
index afb2cba..8ccff12 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -500,7 +500,7 @@
@SuppressWarnings("unused") // called reflectively
protected Schema __inherits_begin(String name)
throws UnableToCompleteException {
- moduleDef.addDirectDependent(moduleName, name);
+ moduleDef.addDirectDependency(moduleName, name);
loader.nestedLoad(logger, name, moduleDef);
return null;
}
@@ -1444,25 +1444,32 @@
}
@SuppressWarnings("unused")
- protected Schema __module_begin(NullableName renameTo, String type) {
+ protected Schema __module_begin(NullableName renameTo, String type)
+ throws UnableToCompleteException {
ModuleType moduleType = ModuleType.valueOf(type.toUpperCase(Locale.ENGLISH));
moduleDef.enterModule(moduleType, moduleName);
+
+ // All modules implicitly depend on com.google.gwt.core.Core. Processing of this dependency
+ // needs to occur early so that subsequent processing has the opportunity to override property
+ // values.
+ bodySchema.__inherits_begin("com.google.gwt.core.Core");
+
return bodySchema;
}
protected void __module_end(NullableName renameTo, String type) {
- if (!loader.enforceStrictResources()) {
- // If we're not being strict about resources and no dependencies have been added, go ahead and
- // implicitly add "client" and "public" resource dependencies.
- if (!foundExplicitSourceOrSuperSource) {
- bodySchema.addSourcePackage(modulePackageAsPath, "client", Empty.STRINGS,
- Empty.STRINGS, Empty.STRINGS, true, true, false);
- }
+ // If we're not being strict about source resources and no source paths have been added, go
+ // ahead and implicitly add the "client" directory.
+ if (!loader.enforceStrictSourceResources() && !foundExplicitSourceOrSuperSource) {
+ bodySchema.addSourcePackage(modulePackageAsPath, "client", Empty.STRINGS,
+ Empty.STRINGS, Empty.STRINGS, true, true, false);
+ }
- if (!foundAnyPublic) {
- bodySchema.addPublicPackage(modulePackageAsPath, "public", Empty.STRINGS,
- Empty.STRINGS, Empty.STRINGS, true, true);
- }
+ // If we're not being strict about public resources and no public paths have been added, go
+ // ahead and implicitly add the "public" directory.
+ if (!loader.enforceStrictPublicResources() && !foundAnyPublic) {
+ bodySchema.addPublicPackage(modulePackageAsPath, "public", Empty.STRINGS,
+ Empty.STRINGS, Empty.STRINGS, true, true);
}
// We do this in __module_end so this value is never inherited
diff --git a/dev/core/src/com/google/gwt/dev/cfg/NullLibraryWriter.java b/dev/core/src/com/google/gwt/dev/cfg/NullLibraryWriter.java
index a20ce56..01a0f41 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/NullLibraryWriter.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/NullLibraryWriter.java
@@ -19,9 +19,6 @@
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.PersistenceBackedObject;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
-import com.google.gwt.thirdparty.guava.common.collect.LinkedHashMultimap;
-import com.google.gwt.thirdparty.guava.common.collect.Multimap;
-import com.google.gwt.thirdparty.guava.common.collect.Multimaps;
import java.util.Set;
@@ -31,9 +28,6 @@
*/
public class NullLibraryWriter implements LibraryWriter {
- private Multimap<String, String> newBindingPropertyValuesByName = LinkedHashMultimap.create();
- private Multimap<String, String> newConfigurationPropertyValuesByName =
- LinkedHashMultimap.create();
private Set<String> strings = ImmutableSet.of();
@Override
@@ -57,51 +51,35 @@
}
@Override
- public void addNewBindingPropertyValuesByName(String propertyName,
- Iterable<String> propertyValues) {
- newBindingPropertyValuesByName.putAll(propertyName, propertyValues);
- }
-
- @Override
- public void addNewConfigurationPropertyValuesByName(String propertyName,
- Iterable<String> propertyValues) {
- newConfigurationPropertyValuesByName.putAll(propertyName, propertyValues);
- }
-
- @Override
public void addPublicResource(Resource publicResource) {
}
@Override
- public void addRanGeneratorName(String generatorName) {
- }
-
- @Override
- public Multimap<String, String> getNewBindingPropertyValuesByName() {
- return Multimaps.unmodifiableMultimap(newBindingPropertyValuesByName);
- }
-
- @Override
- public Multimap<String, String> getNewConfigurationPropertyValuesByName() {
- return Multimaps.unmodifiableMultimap(newConfigurationPropertyValuesByName);
- }
-
- @Override
public PersistenceBackedObject<PermutationResult> getPermutationResultHandle() {
return null;
}
@Override
+ public Set<String> getProcessedReboundTypeSourceNames(String generatorName) {
+ return null;
+ }
+
+ @Override
public Set<String> getReboundTypeSourceNames() {
return strings;
}
@Override
- public void setLibraryName(String libraryName) {
+ public void markReboundTypeProcessed(String processedReboundTypeSourceName,
+ String generatorName) {
}
@Override
- public void setReboundTypeSourceNames(Set<String> reboundTypeSourceNames) {
+ public void markReboundTypesProcessed(Set<String> reboundTypeSourceNames) {
+ }
+
+ @Override
+ public void setLibraryName(String libraryName) {
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Property.java b/dev/core/src/com/google/gwt/dev/cfg/Property.java
index c87bc06..02da80d 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Property.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Property.java
@@ -33,24 +33,11 @@
return name.compareTo(o.name);
}
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof Property)) {
- return false;
- }
- return name.equals(((Property) o).name);
- }
-
public String getName() {
return name;
}
@Override
- public int hashCode() {
- return name.hashCode();
- }
-
- @Override
public String toString() {
return name;
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java b/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java
index 24ca2fa..be67bd1 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.dev.cfg;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
+
import java.io.Serializable;
/**
@@ -28,7 +30,21 @@
this.body = body;
}
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof PropertyProvider) {
+ PropertyProvider that = (PropertyProvider) object;
+ return Objects.equal(this.body, that.body);
+ }
+ return false;
+ }
+
public String getBody() {
return body;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(body);
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/PropertyProviderRegistratorGenerator.java b/dev/core/src/com/google/gwt/dev/cfg/PropertyProviderRegistratorGenerator.java
index c5cd3e4..2aa71bb 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/PropertyProviderRegistratorGenerator.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/PropertyProviderRegistratorGenerator.java
@@ -97,6 +97,11 @@
out.println("public class " + typeName + " {");
for (BindingProperty bindingProperty : newBindingProperties) {
+ // If nothing about the binding property was set or created in the current module.
+ if (bindingProperty.getTargetLibraryDefinedValues().isEmpty()) {
+ // Then its previously created property provider is still valid.
+ continue;
+ }
createPropertyProviderClass(logger, out, bindingProperty);
}
// TODO(stalcup): create configuration property providers.
diff --git a/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java b/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java
index a7e09e5..dd41a9a 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java
@@ -23,8 +23,11 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.StandardGeneratorContext;
+import com.google.gwt.dev.javac.typemodel.LibraryTypeOracle.UnsupportedTypeOracleAccess;
import com.google.gwt.dev.javac.typemodel.TypeOracle;
import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
+import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
@@ -120,8 +123,8 @@
* gather runtime rebind rules for all corresponding pairs of property values and Generator
* output.
*/
- public void generate(
- TreeLogger logger, Properties moduleProperties, GeneratorContext context, String typeName) {
+ public void generate(TreeLogger logger, Properties moduleProperties, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
Map<Map<String, String>, String> resultTypeNamesByProperties =
computeResultTypeNamesByProperties(
logger, moduleProperties, (StandardGeneratorContext) context, typeName);
@@ -157,6 +160,36 @@
return context.runGeneratorIncrementally(logger, generatorClass, typeName);
}
+ public boolean relevantPropertiesAreFinal(Properties currentProperties,
+ Properties finalProperties) {
+ if (accessedPropertyNames.equals(ALL_PROPERTIES)) {
+ // Generator must be assumed to depend on all properties.
+ for (BindingProperty finalProperty : finalProperties.getBindingProperties()) {
+ Property currentProperty = currentProperties.find(finalProperty.getName());
+ if (!Objects.equal(finalProperty, currentProperty)) {
+ return false;
+ }
+ }
+ for (ConfigurationProperty finalProperty : finalProperties.getConfigurationProperties()) {
+ Property currentProperty = currentProperties.find(finalProperty.getName());
+ if (!Objects.equal(finalProperty, currentProperty)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Generator defines a limited set of properties it cares about.
+ for (String accessedPropertyName : accessedPropertyNames) {
+ Property finalProperty = finalProperties.find(accessedPropertyName);
+ Property currentProperty = currentProperties.find(accessedPropertyName);
+ if (!Objects.equal(finalProperty, currentProperty)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public String toString() {
return "<generate-with class='" + generatorClass.getName() + "'/>";
@@ -182,7 +215,7 @@
return "return " + rootCondition.toSource() + ";";
}
- // VisibleForTesting
+ @VisibleForTesting
protected Generator getGenerator() {
if (generator == null) {
try {
@@ -203,81 +236,82 @@
* values. Log corresponding pairs of property values and Generator output.
*/
private Map<Map<String, String>, String> computeResultTypeNamesByProperties(TreeLogger logger,
- Properties moduleProperties, StandardGeneratorContext context, String typeName) {
- try {
- Map<Map<String, String>, String> resultTypeNamesByProperties = Maps.newHashMap();
- DynamicPropertyOracle dynamicPropertyOracle =
- new DynamicPropertyOracle(moduleProperties);
+ Properties moduleProperties, StandardGeneratorContext context, String typeName)
+ throws UnableToCompleteException {
+ Map<Map<String, String>, String> resultTypeNamesByProperties = Maps.newHashMap();
+ DynamicPropertyOracle dynamicPropertyOracle = new DynamicPropertyOracle(moduleProperties);
- // Maybe prime the pump.
- if (!accessedPropertyNames.equals(ALL_PROPERTIES)) {
- for (String accessedPropertyName : accessedPropertyNames) {
- try {
- dynamicPropertyOracle.getSelectionProperty(logger, accessedPropertyName);
- } catch (BadPropertyValueException e) {
- // ignore
- }
+ // Maybe prime the pump.
+ if (!accessedPropertyNames.equals(ALL_PROPERTIES)) {
+ for (String accessedPropertyName : accessedPropertyNames) {
+ try {
+ dynamicPropertyOracle.getSelectionProperty(logger, accessedPropertyName);
+ } catch (BadPropertyValueException e) {
+ // ignore
}
}
- boolean needsAllTypesIfRun = contentDependsOnTypes() && context.isGlobalCompile();
- TypeOracle typeModelTypeOracle =
- (com.google.gwt.dev.javac.typemodel.TypeOracle) context.getTypeOracle();
-
- context.reset();
- context.setPropertyOracle(dynamicPropertyOracle);
-
- context.setCurrentGenerator(generatorClass);
-
- do {
- resultTypeNamesByProperties.clear();
- context.reset();
- Properties accessedProperties = new Properties();
-
- List<BindingProperty> accessedPropertiesList =
- new ArrayList<BindingProperty>(dynamicPropertyOracle.getAccessedProperties());
- for (BindingProperty bindingProperty : accessedPropertiesList) {
- accessedProperties.addBindingProperty(bindingProperty);
- }
- PropertyPermutations permutationsOfAccessedProperties =
- new PropertyPermutations(accessedProperties, Sets.<String>newHashSet());
-
- for (int permutationId = 0; permutationId < permutationsOfAccessedProperties.size();
- permutationId++) {
- String[] orderedPropertyValues =
- permutationsOfAccessedProperties.getOrderedPropertyValues(permutationId);
- BindingProperty[] orderedProperties =
- permutationsOfAccessedProperties.getOrderedProperties();
-
- dynamicPropertyOracle.reset();
- for (int propertyIndex = 0; propertyIndex < orderedPropertyValues.length;
- propertyIndex++) {
- dynamicPropertyOracle.prescribePropertyValue(
- orderedProperties[propertyIndex].getName(), orderedPropertyValues[propertyIndex]);
- }
-
- if (!isApplicable(logger, context, typeName)) {
- continue;
- }
- if (needsAllTypesIfRun) {
- typeModelTypeOracle.ensureAllLoaded();
- }
- String resultTypeName = getGenerator().generate(logger, context, typeName);
- if (resultTypeName != null) {
- // Some generators only run to create resource artifacts and don't actually participate
- // in the requestType->resultType rebind process.
- resultTypeNamesByProperties.put(
- dynamicPropertyOracle.getPrescribedPropertyValuesByName(), resultTypeName);
- }
-
- if (dynamicPropertyOracle.haveAccessedPropertiesChanged()) {
- break;
- }
- }
- } while (dynamicPropertyOracle.haveAccessedPropertiesChanged());
-
- return resultTypeNamesByProperties;
- } catch (UnableToCompleteException e) {
- throw new InternalCompilerException(e.getMessage());
}
+ boolean needsAllTypesIfRun = contentDependsOnTypes() && context.isGlobalCompile();
+ TypeOracle typeModelTypeOracle =
+ (com.google.gwt.dev.javac.typemodel.TypeOracle) context.getTypeOracle();
+
+ context.setPropertyOracle(dynamicPropertyOracle);
+
+ context.setCurrentGenerator(generatorClass);
+
+ do {
+ resultTypeNamesByProperties.clear();
+ Properties accessedProperties = new Properties();
+
+ List<BindingProperty> accessedPropertiesList =
+ new ArrayList<BindingProperty>(dynamicPropertyOracle.getAccessedProperties());
+ for (BindingProperty bindingProperty : accessedPropertiesList) {
+ accessedProperties.addBindingProperty(bindingProperty);
+ }
+ PropertyPermutations permutationsOfAccessedProperties =
+ new PropertyPermutations(accessedProperties, Sets.<String> newHashSet());
+
+ for (int permutationId = 0; permutationId < permutationsOfAccessedProperties.size();
+ permutationId++) {
+ String[] orderedPropertyValues =
+ permutationsOfAccessedProperties.getOrderedPropertyValues(permutationId);
+ BindingProperty[] orderedProperties =
+ permutationsOfAccessedProperties.getOrderedProperties();
+
+ dynamicPropertyOracle.reset();
+ for (int propertyIndex = 0; propertyIndex < orderedPropertyValues.length; propertyIndex++) {
+ dynamicPropertyOracle.prescribePropertyValue(orderedProperties[propertyIndex].getName(),
+ orderedPropertyValues[propertyIndex]);
+ }
+
+ if (!isApplicable(logger, context, typeName)) {
+ continue;
+ }
+ if (needsAllTypesIfRun) {
+ typeModelTypeOracle.ensureAllLoaded();
+ }
+ String resultTypeName;
+ try {
+ resultTypeName = getGenerator().generate(logger, context, typeName);
+ } catch (UnsupportedTypeOracleAccess e) {
+ logger.log(TreeLogger.ERROR, String.format(
+ "TypeOracle error when running generator '%s' in an incremental compile: %s",
+ getName(), e.getMessage()));
+ throw new UnableToCompleteException();
+ }
+ if (resultTypeName != null) {
+ // Some generators only run to create resource artifacts and don't actually participate
+ // in the requestType->resultType rebind process.
+ resultTypeNamesByProperties.put(dynamicPropertyOracle.getPrescribedPropertyValuesByName(),
+ resultTypeName);
+ }
+
+ if (dynamicPropertyOracle.haveAccessedPropertiesChanged()) {
+ break;
+ }
+ }
+ } while (dynamicPropertyOracle.haveAccessedPropertiesChanged());
+
+ return resultTypeNamesByProperties;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ZipLibrary.java b/dev/core/src/com/google/gwt/dev/cfg/ZipLibrary.java
index e16e6b4..7461c16 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ZipLibrary.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ZipLibrary.java
@@ -69,6 +69,28 @@
}
}
+ private void close() {
+ try {
+ // Close our ZipFile.
+ zipFile.close();
+ zipFile = null;
+ } catch (IOException e) {
+ // nothing to be done about it.
+ }
+ }
+
+ private String decode(String string) {
+ string = decodeCharacter(string, Libraries.VALUE_SEPARATOR);
+ string = decodeCharacter(string, Libraries.LINE_SEPARATOR);
+ string = decodeCharacter(string, Libraries.KEY_VALUE_SEPARATOR);
+ string = decodeCharacter(string, Libraries.ENCODE_PREFIX);
+ return string;
+ }
+
+ private String decodeCharacter(String string, char character) {
+ return string.replace(Libraries.ENCODE_PREFIX + Integer.toString(character), character + "");
+ }
+
private InputStream getClassFileStream(String classFilePath) {
ZipEntry classFileEntry =
zipFile.getEntry(Libraries.computeClassFileEntryName(classFilePath));
@@ -160,12 +182,8 @@
return readStringMultimap(Libraries.NESTED_SOURCE_NAMES_BY_ENCLOSING_NAME_ENTRY_NAME);
}
- private Multimap<String, String> readNewBindingPropertyValuesByName() {
- return readStringMultimap(Libraries.NEW_BINDING_PROPERTY_VALUES_BY_NAME_ENTRY_NAME);
- }
-
- private Multimap<String, String> readNewConfigurationPropertyValuesByName() {
- return readStringMultimap(Libraries.NEW_CONFIGURATION_PROPERTY_VALUES_BY_NAME_ENTRY_NAME);
+ private Multimap<String, String> readProcessedReboundTypeSourceNamesByGenerator() {
+ return readStringMultimap(Libraries.PROCESSED_REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME);
}
private Resource readPublicResourceByPath(String path) {
@@ -177,10 +195,6 @@
return readStringSet(Libraries.PUBLIC_RESOURCE_PATHS_ENTRY_NAME);
}
- private Set<String> readRanGeneratorNames() {
- return readStringSet(Libraries.RAN_GENERATOR_NAMES_ENTRY_NAME);
- }
-
private Set<String> readReboundTypeSourceNames() {
return readStringSet(Libraries.REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME);
}
@@ -232,18 +246,6 @@
return stringMultimap;
}
- private String decode(String string) {
- string = decodeCharacter(string, Libraries.VALUE_SEPARATOR);
- string = decodeCharacter(string, Libraries.LINE_SEPARATOR);
- string = decodeCharacter(string, Libraries.KEY_VALUE_SEPARATOR);
- string = decodeCharacter(string, Libraries.ENCODE_PREFIX);
- return string;
- }
-
- private String decodeCharacter(String string, char character) {
- return string.replace(Libraries.ENCODE_PREFIX + Integer.toString(character), character + "");
- }
-
private Set<String> readStringSet(String entryName) {
return Collections.unmodifiableSet(Sets.newLinkedHashSet(readStringList(entryName)));
}
@@ -282,12 +284,10 @@
private String libraryName;
private Multimap<String, String> nestedBinaryNamesByCompilationUnitName;
private Multimap<String, String> nestedSourceNamesByCompilationUnitName;
- private Multimap<String, String> newBindingPropertyValuesByName;
- private Multimap<String, String> newConfigurationPropertyValuesByName;
private ZipEntryBackedObject<PermutationResult> permutationResultHandle;
+ private Multimap<String, String> processedReboundTypeSourceNamesByGenerator;
private Set<String> publicResourcePaths;
private Map<String, Resource> publicResourcesByPath = Maps.newHashMap();
- private Set<String> ranGeneratorNames;
private Set<String> reboundTypeSourceNames;
private Set<String> regularCompilationUnitTypeSourceNames;
private Set<String> superSourceClassFilePaths;
@@ -303,6 +303,11 @@
}
@Override
+ public void close() {
+ zipLibraryReader.close();
+ }
+
+ @Override
public Resource getBuildResourceByPath(String path) {
if (!buildResourcesByPath.containsKey(path)) {
buildResourcesByPath.put(path, zipLibraryReader.readBuildResourceByPath(path));
@@ -419,24 +424,6 @@
}
@Override
- public Multimap<String, String> getNewBindingPropertyValuesByName() {
- if (newBindingPropertyValuesByName == null) {
- newBindingPropertyValuesByName =
- Multimaps.unmodifiableMultimap(zipLibraryReader.readNewBindingPropertyValuesByName());
- }
- return newBindingPropertyValuesByName;
- }
-
- @Override
- public Multimap<String, String> getNewConfigurationPropertyValuesByName() {
- if (newConfigurationPropertyValuesByName == null) {
- newConfigurationPropertyValuesByName = Multimaps.unmodifiableMultimap(
- zipLibraryReader.readNewConfigurationPropertyValuesByName());
- }
- return newConfigurationPropertyValuesByName;
- }
-
- @Override
public ZipEntryBackedObject<PermutationResult> getPermutationResultHandle() {
if (permutationResultHandle == null) {
permutationResultHandle = zipLibraryReader.getPermutationResultHandle();
@@ -445,6 +432,15 @@
}
@Override
+ public Multimap<String, String> getProcessedReboundTypeSourceNamesByGenerator() {
+ if (processedReboundTypeSourceNamesByGenerator == null) {
+ processedReboundTypeSourceNamesByGenerator = Multimaps.unmodifiableMultimap(
+ zipLibraryReader.readProcessedReboundTypeSourceNamesByGenerator());
+ }
+ return processedReboundTypeSourceNamesByGenerator;
+ }
+
+ @Override
public Resource getPublicResourceByPath(String path) {
if (!publicResourcesByPath.containsKey(path)) {
publicResourcesByPath.put(path, zipLibraryReader.readPublicResourceByPath(path));
@@ -461,14 +457,6 @@
}
@Override
- public Set<String> getRanGeneratorNames() {
- if (ranGeneratorNames == null) {
- ranGeneratorNames = Collections.unmodifiableSet(zipLibraryReader.readRanGeneratorNames());
- }
- return ranGeneratorNames;
- }
-
- @Override
public Set<String> getReboundTypeSourceNames() {
if (reboundTypeSourceNames == null) {
reboundTypeSourceNames =
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ZipLibraryWriter.java b/dev/core/src/com/google/gwt/dev/cfg/ZipLibraryWriter.java
index 3b54c2b..bcbef44 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ZipLibraryWriter.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ZipLibraryWriter.java
@@ -25,10 +25,11 @@
import com.google.gwt.dev.util.Name.InternalName;
import com.google.gwt.dev.util.ZipEntryBackedObject;
import com.google.gwt.thirdparty.guava.common.base.Joiner;
+import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
import com.google.gwt.thirdparty.guava.common.collect.LinkedHashMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Multimap;
-import com.google.gwt.thirdparty.guava.common.collect.Multimaps;
+import com.google.gwt.thirdparty.guava.common.collect.SetMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import com.google.gwt.thirdparty.guava.common.io.ByteStreams;
@@ -73,6 +74,11 @@
zipFile = new File(zipFileName);
}
+ public void writeProcessedReboundTypeSourceNamesForGenerators() {
+ writeStringMultimap(Libraries.PROCESSED_REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME,
+ processedReboundTypeSourceNamesByGenerator);
+ }
+
private void createFileIfMissing() {
if (!zipFile.exists()) {
try {
@@ -97,6 +103,18 @@
}
}
+ private String encode(String string) {
+ string = encodeCharacter(string, Libraries.ENCODE_PREFIX);
+ string = encodeCharacter(string, Libraries.KEY_VALUE_SEPARATOR);
+ string = encodeCharacter(string, Libraries.LINE_SEPARATOR);
+ string = encodeCharacter(string, Libraries.VALUE_SEPARATOR);
+ return string;
+ }
+
+ private String encodeCharacter(String string, char character) {
+ return string.replace(character + "", Libraries.ENCODE_PREFIX + Integer.toString(character));
+ }
+
private synchronized void ensureFileReady() {
if (fileReady) {
return;
@@ -157,10 +175,8 @@
writePublicResourcePaths();
// Generator related
- writeNewBindingPropertyValuesByName();
- writeNewConfigurationPropertyValuesByName();
writeReboundTypeSourceNames();
- writeRanGeneratorNames();
+ writeProcessedReboundTypeSourceNamesForGenerators();
writeGeneratedArtifactPaths();
writeGeneratedArtifacts();
} finally {
@@ -254,16 +270,6 @@
nestedBinaryNamesByCompilationUnitName);
}
- private void writeNewBindingPropertyValuesByName() {
- writeStringMultimap(
- Libraries.NEW_BINDING_PROPERTY_VALUES_BY_NAME_ENTRY_NAME, newBindingPropertyValuesByName);
- }
-
- private void writeNewConfigurationPropertyValuesByName() {
- writeStringMultimap(Libraries.NEW_CONFIGURATION_PROPERTY_VALUES_BY_NAME_ENTRY_NAME,
- newConfigurationPropertyValuesByName);
- }
-
private void writePublicResourcePaths() {
writeStringSet(Libraries.PUBLIC_RESOURCE_PATHS_ENTRY_NAME, publicResourcesByPath.keySet());
}
@@ -272,10 +278,6 @@
writeResources("public", Libraries.DIRECTORY_PUBLIC_RESOURCES, publicResourcesByPath);
}
- private void writeRanGeneratorNames() {
- writeStringSet(Libraries.RAN_GENERATOR_NAMES_ENTRY_NAME, ranGeneratorNames);
- }
-
private void writeReboundTypeSourceNames() {
writeStringSet(Libraries.REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME, reboundTypeSourceNames);
}
@@ -337,18 +339,6 @@
}
}
- private String encode(String string) {
- string = encodeCharacter(string, Libraries.ENCODE_PREFIX);
- string = encodeCharacter(string, Libraries.KEY_VALUE_SEPARATOR);
- string = encodeCharacter(string, Libraries.LINE_SEPARATOR);
- string = encodeCharacter(string, Libraries.VALUE_SEPARATOR);
- return string;
- }
-
- private String encodeCharacter(String string, char character) {
- return string.replace(character + "", Libraries.ENCODE_PREFIX + Integer.toString(character));
- }
-
private void writeStringSet(String entryName, Set<String> stringSet) {
createZipEntry(entryName);
Set<String> encodedStringSet = Sets.newHashSet();
@@ -378,15 +368,13 @@
LinkedHashMultimap.create();
private Multimap<String, String> nestedSourceNamesByCompilationUnitName =
LinkedHashMultimap.create();
- private Multimap<String, String> newBindingPropertyValuesByName = LinkedHashMultimap.create();
- private Multimap<String, String> newConfigurationPropertyValuesByName =
- LinkedHashMultimap.create();
private ZipEntryBackedObject<PermutationResult> permutationResultHandle;
+ private SetMultimap<String, String> processedReboundTypeSourceNamesByGenerator =
+ HashMultimap.create();
private Map<String, Resource> publicResourcesByPath = Maps.newHashMap();
- private Set<String> ranGeneratorNames = Sets.newHashSet();
- private Set<String> reboundTypeSourceNames = Sets.newHashSet();
private Set<String> regularClassFilePaths = Sets.newHashSet();
private Set<String> regularCompilationUnitTypeSourceNames = Sets.newLinkedHashSet();
+ private Set<String> reboundTypeSourceNames = Sets.newHashSet();
private Set<String> superSourceClassFilePaths = Sets.newHashSet();
private Set<String> superSourceCompilationUnitTypeSourceNames = Sets.newLinkedHashSet();
private ZipWriter zipWriter;
@@ -454,38 +442,11 @@
}
@Override
- public void addNewBindingPropertyValuesByName(
- String propertyName, Iterable<String> propertyValues) {
- newBindingPropertyValuesByName.putAll(propertyName, propertyValues);
- }
-
- @Override
- public void addNewConfigurationPropertyValuesByName(
- String propertyName, Iterable<String> propertyValues) {
- newConfigurationPropertyValuesByName.putAll(propertyName, propertyValues);
- }
-
- @Override
public void addPublicResource(Resource publicResource) {
publicResourcesByPath.put(publicResource.getPath(), publicResource);
}
@Override
- public void addRanGeneratorName(String generatorName) {
- ranGeneratorNames.add(generatorName);
- }
-
- @Override
- public Multimap<String, String> getNewBindingPropertyValuesByName() {
- return Multimaps.unmodifiableMultimap(newBindingPropertyValuesByName);
- }
-
- @Override
- public Multimap<String, String> getNewConfigurationPropertyValuesByName() {
- return Multimaps.unmodifiableMultimap(newConfigurationPropertyValuesByName);
- }
-
- @Override
public ZipEntryBackedObject<PermutationResult> getPermutationResultHandle() {
if (permutationResultHandle == null) {
permutationResultHandle = zipWriter.getPermutationResultHandle();
@@ -494,18 +455,29 @@
}
@Override
+ public Set<String> getProcessedReboundTypeSourceNames(String generatorName) {
+ return processedReboundTypeSourceNamesByGenerator.get(generatorName);
+ }
+
+ @Override
public Set<String> getReboundTypeSourceNames() {
return Collections.unmodifiableSet(reboundTypeSourceNames);
}
@Override
- public void setLibraryName(String libraryName) {
- this.libraryName = libraryName;
+ public void markReboundTypesProcessed(Set<String> reboundTypeSourceNames) {
+ this.reboundTypeSourceNames = reboundTypeSourceNames;
}
@Override
- public void setReboundTypeSourceNames(Set<String> reboundTypeSourceNames) {
- this.reboundTypeSourceNames = reboundTypeSourceNames;
+ public void markReboundTypeProcessed(String processedReboundTypeSourceName,
+ String generatorName) {
+ processedReboundTypeSourceNamesByGenerator.put(generatorName, processedReboundTypeSourceName);
+ }
+
+ @Override
+ public void setLibraryName(String libraryName) {
+ this.libraryName = libraryName;
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationState.java b/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
index ad39740..29c715f 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
@@ -23,6 +23,7 @@
import com.google.gwt.dev.util.log.speedtracer.DevModeEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import java.util.Collection;
import java.util.Collections;
@@ -53,8 +54,14 @@
*/
protected final Map<String, CompilationUnit> unitMap = new HashMap<String, CompilationUnit>();
+ private int cachedGeneratedSourceCount = 0;
+
+ private int cachedStaticSourceCount = 0;
+
private final CompileMoreLater compileMoreLater;
+ private CompilerContext compilerContext;
+
/**
* Unmodifiable view of {@link #classFileMap}.
*/
@@ -78,6 +85,10 @@
private final Collection<CompilationUnit> exposedUnits =
Collections.unmodifiableCollection(unitMap.values());
+ private int generatedSourceCount = 0;
+
+ private int staticSourceCount = 0;
+
/**
* Our type oracle.
*/
@@ -88,8 +99,6 @@
*/
private final CompilationUnitTypeOracleUpdater typeOracleUpdater;
- private CompilerContext compilerContext;
-
CompilationState(TreeLogger logger, CompilerContext compilerContext,
TypeOracle typeOracle, CompilationUnitTypeOracleUpdater typeOracleUpdater,
Collection<CompilationUnit> units, CompileMoreLater compileMoreLater) {
@@ -114,7 +123,7 @@
+ generatedUnits.size() + "' new generated units");
generatedUnitsAddEvent.addData("# new generated units", "" + generatedUnits.size());
Collection<CompilationUnit> newUnits = compileMoreLater.addGeneratedTypes(
- logger, generatedUnits);
+ logger, generatedUnits, this);
assimilateUnits(logger, newUnits, true);
} finally {
generatedUnitsAddEvent.end();
@@ -138,6 +147,14 @@
}
}
+ public int getCachedGeneratedSourceCount() {
+ return cachedGeneratedSourceCount;
+ }
+
+ public int getCachedStaticSourceCount() {
+ return cachedStaticSourceCount;
+ }
+
/**
* Returns a map of all compiled classes by internal name.
*/
@@ -166,11 +183,22 @@
public Collection<CompilationUnit> getCompilationUnits() {
return exposedUnits;
}
+ public int getGeneratedSourceCount() {
+ return generatedSourceCount;
+ }
+ public int getStaticSourceCount() {
+ return staticSourceCount;
+ }
public TypeOracle getTypeOracle() {
return typeOracle;
}
+ @VisibleForTesting
+ public Map<String, CompiledClass> getValidClasses() {
+ return compileMoreLater.getValidClasses();
+ }
+
/**
* Whether any errors were encountered while building this compilation state.
*/
@@ -183,6 +211,22 @@
return false;
}
+ public void incrementCachedGeneratedSourceCount(int extraCachedGeneratedSourceCount) {
+ cachedGeneratedSourceCount += extraCachedGeneratedSourceCount;
+ }
+
+ public void incrementCachedStaticSourceCount(int extraCachedStaticSourceCount) {
+ cachedStaticSourceCount += extraCachedStaticSourceCount;
+ }
+
+ public void incrementGeneratedSourceCount(int extraGeneratedSourceCount) {
+ generatedSourceCount += extraGeneratedSourceCount;
+ }
+
+ public void incrementStaticSourceCount(int extraStaticSourceCount) {
+ staticSourceCount += extraStaticSourceCount;
+ }
+
/**
* For testing.
*/
@@ -199,20 +243,16 @@
classFileMapBySource.put(compiledClass.getSourceName(), compiledClass);
}
}
- CompilationUnitInvalidator.retainValidUnits(logger, units, compileMoreLater.getValidClasses());
// Performed after compilation unit invalidator because only valid units should be saved in the
// library.
if (saveInLibrary) {
+ CompilationUnitInvalidator.retainValidUnits(logger, units,
+ compileMoreLater.getValidClasses());
for (CompilationUnit compilationUnit : units) {
compilerContext.getLibraryWriter().addCompilationUnit(compilationUnit);
}
}
typeOracleUpdater.addNewUnits(logger, units);
}
-
- // VisibleForTesting
- public Map<String, CompiledClass> getValidClasses() {
- return compileMoreLater.getValidClasses();
- }
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
index aacba1a..15f33e0 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
@@ -238,10 +238,12 @@
* UnableToCompleteException.
*/
public Collection<CompilationUnit> addGeneratedTypes(TreeLogger logger,
- Collection<GeneratedUnit> generatedUnits) throws UnableToCompleteException {
+ Collection<GeneratedUnit> generatedUnits, CompilationState compilationState)
+ throws UnableToCompleteException {
Event event = SpeedTracerLogger.start(DevModeEventType.CSB_ADD_GENERATED_TYPES);
try {
- return doBuildGeneratedTypes(logger, compilerContext, generatedUnits, this);
+ return doBuildGeneratedTypes(logger, compilerContext, generatedUnits, compilationState,
+ this);
} finally {
event.end();
}
@@ -556,9 +558,11 @@
}
builders.add(builder);
}
+ int cachedSourceCount = cachedUnits.size();
+ int sourceCount = resources.size();
if (logger.isLoggable(TreeLogger.TRACE)) {
- logger.log(TreeLogger.TRACE, "Found " + cachedUnits.size() + " cached/archived units. Used "
- + cachedUnits.size() + " / " + resources.size() + " units from cache.");
+ logger.log(TreeLogger.TRACE, "Found " + cachedSourceCount + " cached/archived units. Used "
+ + cachedSourceCount + " / " + sourceCount + " units from cache.");
}
Collection<CompilationUnit> resultUnits = compileMoreLater.compile(
@@ -576,8 +580,11 @@
typeOracleUpdater = ((LibraryTypeOracle) typeOracle).getTypeOracleUpdater();
}
- return new CompilationState(logger, compilerContext, typeOracle, typeOracleUpdater,
- resultUnits, compileMoreLater);
+ CompilationState compilationState = new CompilationState(logger, compilerContext, typeOracle,
+ typeOracleUpdater, resultUnits, compileMoreLater);
+ compilationState.incrementStaticSourceCount(sourceCount);
+ compilationState.incrementCachedStaticSourceCount(cachedSourceCount);
+ return compilationState;
}
public CompilationState doBuildFrom(
@@ -593,7 +600,8 @@
*/
synchronized Collection<CompilationUnit> doBuildGeneratedTypes(TreeLogger logger,
CompilerContext compilerContext, Collection<GeneratedUnit> generatedUnits,
- CompileMoreLater compileMoreLater) throws UnableToCompleteException {
+ CompilationState compilationState, CompileMoreLater compileMoreLater)
+ throws UnableToCompleteException {
UnitCache unitCache = compilerContext.getUnitCache();
// Units we definitely want to build.
@@ -619,6 +627,8 @@
}
builders.add(builder);
}
+ compilationState.incrementGeneratedSourceCount(builders.size() + cachedUnits.size());
+ compilationState.incrementCachedGeneratedSourceCount(cachedUnits.size());
return compileMoreLater.compile(logger, compilerContext, builders,
cachedUnits, CompilerEventType.JDT_COMPILER_CSB_GENERATED);
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitTypeOracleUpdater.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitTypeOracleUpdater.java
index a7ae4d7..af507d5 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitTypeOracleUpdater.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitTypeOracleUpdater.java
@@ -50,6 +50,7 @@
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import java.io.PrintWriter;
@@ -423,7 +424,7 @@
indexTypes();
}
- // VisibleForTesting
+ @VisibleForTesting
void indexTypes() {
Event finishEvent =
SpeedTracerLogger.start(CompilerEventType.TYPE_ORACLE_UPDATER, "phase", "Finish");
@@ -455,7 +456,7 @@
addNewTypesDontIndex(logger, typeDataList, argsLookup);
}
- // VisibleForTesting
+ @VisibleForTesting
public Resolver getMockResolver() {
return new CompilationUnitTypeOracleResolver(
new TypeOracleBuildContext(new MethodArgNamesLookup()));
@@ -465,7 +466,7 @@
return typeOracle;
}
- // VisibleForTesting
+ @VisibleForTesting
public Map<String, JRealClassType> getTypesByInternalName() {
return typesByInternalName;
}
@@ -886,8 +887,9 @@
// Process methods
for (CollectMethodData method : classData.getMethods()) {
- if (!resolveMethod(logger, unresolvedType, method, typeParamLookup, context)) {
- logger.log(TreeLogger.WARN, "Unable to resolve method " + method);
+ TreeLogger branch = logger.branch(TreeLogger.SPAM, "Resolving method " + method.getName());
+ if (!resolveMethod(branch, unresolvedType, method, typeParamLookup, context)) {
+ // Already logged.
return false;
}
}
@@ -896,8 +898,9 @@
// Track the next enum ordinal across resolveField calls.
int[] nextEnumOrdinal = new int[] {0};
for (CollectFieldData field : classData.getFields()) {
- if (!resolveField(logger, unresolvedType, field, typeParamLookup, nextEnumOrdinal, context)) {
- logger.log(TreeLogger.WARN, "Unable to resolve field " + field);
+ TreeLogger branch = logger.branch(TreeLogger.SPAM, "Resolving field " + field.getName());
+ if (!resolveField(branch, unresolvedType, field, typeParamLookup, nextEnumOrdinal, context)) {
+ // Already logged.
return false;
}
}
@@ -1006,21 +1009,27 @@
addModifierBits(jfield, mapBits(ASM_TO_SHARED_MODIFIERS, field.getAccess()));
String signature = field.getSignature();
- JType fieldType;
+ JType fieldJType;
if (signature != null) {
SignatureReader reader = new SignatureReader(signature);
JType[] fieldTypeRef = new JType[1];
reader.acceptType(new ResolveTypeSignature(
context.resolver, logger, fieldTypeRef, typeParamLookup, null));
- fieldType = fieldTypeRef[0];
-
+ fieldJType = fieldTypeRef[0];
+ if (fieldJType == null) {
+ logger.log(TreeLogger.ERROR, "Unable to resolve type in field signature " + signature);
+ return false;
+ }
} else {
- fieldType = resolveType(Type.getType(field.getDesc()));
+ Type fieldType = Type.getType(field.getDesc());
+ fieldJType = resolveType(fieldType);
+ if (fieldJType == null) {
+ logger.log(TreeLogger.ERROR, "Unable to resolve type " + fieldType.getInternalName()
+ + " of field " + field.getName());
+ return false;
+ }
}
- if (fieldType == null) {
- return false;
- }
- setFieldType(jfield, fieldType);
+ setFieldType(jfield, fieldJType);
return true;
}
@@ -1085,6 +1094,7 @@
methodData.getArgNames(), methodData.hasActualArgNames(), context.allMethodArgs);
reader.accept(methodResolver);
if (!methodResolver.finish()) {
+ logger.log(TreeLogger.ERROR, "Failed to resolve.");
return false;
}
} else {
@@ -1092,19 +1102,23 @@
Type returnType = Type.getReturnType(methodData.getDesc());
JType returnJType = resolveType(returnType);
if (returnJType == null) {
+ logger.log(TreeLogger.ERROR,
+ "Unable to resolve return type " + returnType.getInternalName());
return false;
}
setReturnType(method, returnJType);
}
if (!resolveParameters(logger, method, methodData, context)) {
+ // Already logged.
return false;
}
}
// The signature might not actually include the exceptions if they don't
// include a type variable, so resolveThrows is always used (it does
// nothing if there are already exceptions defined)
- if (!resolveThrows(method, methodData)) {
+ if (!resolveThrows(logger, method, methodData)) {
+ // Already logged.
return false;
}
typeParamLookup.popScope();
@@ -1142,8 +1156,11 @@
}
List<CollectAnnotationData>[] paramAnnot = methodData.getArgAnnotations();
for (int i = 0; i < argTypes.length; ++i) {
- JType argType = resolveType(argTypes[i]);
- if (argType == null) {
+ Type argType = argTypes[i];
+ JType argJType = resolveType(argType);
+ if (argJType == null) {
+ logger.log(TreeLogger.ERROR, "Unable to resolve type " + argType.getInternalName()
+ + " of argument " + methodData.getArgNames()[i]);
return false;
}
// Try to resolve annotations, ignore any that fail.
@@ -1151,19 +1168,23 @@
new HashMap<Class<? extends Annotation>, Annotation>();
resolveAnnotations(logger, paramAnnot[i], declaredAnnotations);
- newParameter(method, argType, argNames[i], declaredAnnotations, argNamesAreReal);
+ newParameter(method, argJType, argNames[i], declaredAnnotations, argNamesAreReal);
}
return true;
}
- private boolean resolveThrows(JAbstractMethod method, CollectMethodData methodData) {
+ private boolean resolveThrows(TreeLogger logger, JAbstractMethod method,
+ CollectMethodData methodData) {
if (method.getThrows().length == 0) {
for (String exceptionName : methodData.getExceptions()) {
- JType exceptionType = resolveType(Type.getObjectType(exceptionName));
- if (exceptionType == null) {
+ Type exceptionType = Type.getObjectType(exceptionName);
+ JType exceptionJType = resolveType(exceptionType);
+ if (exceptionJType == null) {
+ logger.log(TreeLogger.ERROR,
+ "Unable to resolve type " + exceptionType.getInternalName() + " of thrown exception");
return false;
}
- addThrows(method, (JClassType) exceptionType);
+ addThrows(method, (JClassType) exceptionJType);
}
}
return true;
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
index 6fe5512..e0e7e25 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
@@ -19,6 +19,7 @@
import com.google.gwt.dev.util.DiskCache;
import com.google.gwt.dev.util.DiskCacheToken;
import com.google.gwt.dev.util.StringInterner;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
@@ -87,7 +88,7 @@
/**
* For mock construction in tests.
*/
- // VisibleForTesting
+ @VisibleForTesting
protected CompiledClass(CompiledClass enclosingClass,
String internalName, String sourceName) {
this.enclosingClass = enclosingClass;
diff --git a/dev/core/src/com/google/gwt/dev/javac/typemodel/LibraryTypeOracle.java b/dev/core/src/com/google/gwt/dev/javac/typemodel/LibraryTypeOracle.java
index a3d8fe7..098b77f 100644
--- a/dev/core/src/com/google/gwt/dev/javac/typemodel/LibraryTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/javac/typemodel/LibraryTypeOracle.java
@@ -26,6 +26,20 @@
*/
public class LibraryTypeOracle extends TypeOracle {
+ /**
+ * An exception indicating that the Library flavor of TypeOracle does not support the requested
+ * behavior. This runtime exception can be thrown during LibraryTypeOracle access (from
+ * Generators) and is expected to be caught in the incremental Generator execution framework in
+ * RuleGenerateWith. It would be an error for this exception to ever become visible beyond those
+ * bounds.
+ */
+ public static class UnsupportedTypeOracleAccess extends RuntimeException {
+
+ public UnsupportedTypeOracleAccess(String message) {
+ super(message);
+ }
+ }
+
private boolean allLoaded;
private CompilerContext compilerContext;
private CompilationUnitTypeOracleUpdater typeOracleUpdater;
@@ -53,7 +67,7 @@
@Override
public JPackage findPackage(String pkgName) {
- throw new UnsupportedOperationException("Packages can't be lazily loaded from libraries.");
+ throw new UnsupportedTypeOracleAccess("Packages can't be lazily loaded from libraries.");
}
@Override
@@ -87,12 +101,12 @@
@Override
public JPackage getPackage(String pkgName) throws NotFoundException {
- throw new UnsupportedOperationException("Packages can't be lazily loaded from libraries.");
+ throw new UnsupportedTypeOracleAccess("Packages can't be lazily loaded from libraries.");
}
@Override
public JPackage[] getPackages() {
- throw new UnsupportedOperationException("Packages can't be lazily loaded from libraries.");
+ throw new UnsupportedTypeOracleAccess("Packages can't be lazily loaded from libraries.");
}
public CompilationUnitTypeOracleUpdater getTypeOracleUpdater() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
index 10a3a52..484e32c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
@@ -39,7 +39,8 @@
import com.google.gwt.dev.util.arg.OptionSoycEnabled;
import com.google.gwt.dev.util.arg.OptionSoycHtmlDisabled;
import com.google.gwt.dev.util.arg.OptionStrict;
-import com.google.gwt.dev.util.arg.OptionStrictResources;
+import com.google.gwt.dev.util.arg.OptionStrictPublicResources;
+import com.google.gwt.dev.util.arg.OptionStrictSourceResources;
/**
* Controls options for the {@link JavaToJavaScriptCompiler}.
@@ -48,9 +49,9 @@
OptionClusterSimilarFunctions, OptionDisableClassMetadata, OptionDisableCastChecking,
OptionEnableAssertions, OptionInlineLiteralParameters, OptionOptimizeDataflow,
OptionRunAsyncEnabled, OptionScriptStyle, OptionSoycEnabled, OptionSoycDetailed,
- OptionJsonSoycEnabled,
- OptionOptimizePrecompile, OptionOrdinalizeEnums, OptionRemoveDuplicateFunctions, OptionStrict,
- OptionStrictResources, OptionSoycHtmlDisabled, OptionEnableClosureCompiler,
- OptionFragmentsMerge, OptionFragmentCount, OptionSourceLevel,
- OptionNamespace, OptionCheckedMode {
+ OptionJsonSoycEnabled, OptionOptimizePrecompile, OptionOrdinalizeEnums,
+ OptionRemoveDuplicateFunctions, OptionStrict, OptionStrictSourceResources,
+ OptionStrictPublicResources, OptionSoycHtmlDisabled, OptionEnableClosureCompiler,
+ OptionFragmentsMerge, OptionFragmentCount, OptionSourceLevel, OptionNamespace,
+ OptionCheckedMode {
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
index 8d7a0b9..113699c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
@@ -51,7 +51,8 @@
private boolean soycExtra = false;
private boolean soycHtmlDisabled = false;
private boolean strict = false;
- private boolean strictResources = false;
+ private boolean strictSourceResources = false;
+ private boolean strictPublicResources = false;
public JJSOptionsImpl() {
}
@@ -83,14 +84,20 @@
setJsonSoycEnabled(other.isJsonSoycEnabled());
setSoycHtmlDisabled(other.isSoycHtmlDisabled());
setStrict(other.isStrict());
- setEnforceStrictResources(other.enforceStrictResources());
+ setEnforceStrictSourceResources(other.enforceStrictSourceResources());
+ setEnforceStrictPublicResources(other.enforceStrictPublicResources());
setSourceLevel(other.getSourceLevel());
setNamespace(other.getNamespace());
}
@Override
- public boolean enforceStrictResources() {
- return strictResources;
+ public boolean enforceStrictSourceResources() {
+ return strictSourceResources;
+ }
+
+ @Override
+ public boolean enforceStrictPublicResources() {
+ return strictPublicResources;
}
@Override
@@ -235,8 +242,13 @@
}
@Override
- public void setEnforceStrictResources(boolean strictResources) {
- this.strictResources = strictResources;
+ public void setEnforceStrictSourceResources(boolean strictSourceResources) {
+ this.strictSourceResources = strictSourceResources;
+ }
+
+ @Override
+ public void setEnforceStrictPublicResources(boolean strictPublicResources) {
+ this.strictPublicResources = strictPublicResources;
}
@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 2babe4b..ddcc783 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -41,6 +41,7 @@
import com.google.gwt.dev.PrecompileTaskOptions;
import com.google.gwt.dev.cfg.ConfigurationProperty;
import com.google.gwt.dev.cfg.EntryMethodHolderGenerator;
+import com.google.gwt.dev.cfg.LibraryGroup.CollidingCompilationUnitException;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.javac.CompilationProblemReporter;
import com.google.gwt.dev.javac.CompilationState;
@@ -130,6 +131,7 @@
import com.google.gwt.dev.util.Memory;
import com.google.gwt.dev.util.Name.SourceName;
import com.google.gwt.dev.util.Pair;
+import com.google.gwt.dev.util.TinyCompileSummary;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.OptionOptimize;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
@@ -137,6 +139,7 @@
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
import com.google.gwt.soyc.SoycDashboard;
import com.google.gwt.soyc.io.ArtifactsOutputDirectory;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Multimap;
@@ -232,7 +235,7 @@
new TreeMap<StandardSymbolData, JsName>(new SymbolData.ClassIdentComparator());
// TODO(stalcup): hide metrics gathering in a callback or subclass
- if (logger.isLoggable(TreeLogger.INFO)) {
+ if (compilerContext.shouldCompileMonolithic() && logger.isLoggable(TreeLogger.INFO)) {
logger.log(TreeLogger.INFO, "Compiling permutation " + permutationId + "...");
}
printPermutationTrace(permutation);
@@ -904,6 +907,12 @@
*/
private String buildEntryMethodHolder(StandardGeneratorContext context,
Set<String> allRootTypes) throws UnableToCompleteException {
+ // If there are no entry points.
+ if (entryPointTypeNames.length == 0) {
+ // Then there's no need to generate an EntryMethodHolder class to launch them.
+ return null;
+ }
+
EntryMethodHolderGenerator entryMethodHolderGenerator = new EntryMethodHolderGenerator();
String entryMethodHolderTypeName =
entryMethodHolderGenerator.generate(logger, context, module.getCanonicalName());
@@ -929,6 +938,19 @@
if (options.isSoycEnabled() || options.isJsonSoycEnabled()) {
SourceInfoCorrelator.exec(jprogram);
}
+
+ // Gathers simple metrics that can highlight overly-large modules in an incremental compile.
+ TinyCompileSummary tinyCompileSummary = compilerContext.getTinyCompileSummary();
+ tinyCompileSummary.setTypesForGeneratorsCount(
+ rpo.getGeneratorContext().getTypeOracle().getTypes().length);
+ tinyCompileSummary.setTypesForAstCount(jprogram.getDeclaredTypes().size());
+ tinyCompileSummary.setStaticSourceFilesCount(compilationState.getStaticSourceCount());
+ tinyCompileSummary.setGeneratedSourceFilesCount(compilationState.getGeneratedSourceCount());
+ tinyCompileSummary.setCachedStaticSourceFilesCount(
+ compilationState.getCachedStaticSourceCount());
+ tinyCompileSummary.setCachedGeneratedSourceFilesCount(
+ compilationState.getCachedGeneratedSourceCount());
+
// Free up memory.
rpo.clear();
jprogram.typeOracle.computeBeforeAST();
@@ -1101,16 +1123,30 @@
private void unifyJavaAst(Set<String> allRootTypes, String entryMethodHolderTypeName)
throws UnableToCompleteException {
- UnifyAst unifyAst = new UnifyAst(logger, compilerContext, jprogram, jsProgram, rpo);
+ UnifyAst unifyAst;
+ try {
+ unifyAst = new UnifyAst(logger, compilerContext, jprogram, jsProgram, rpo);
+ } catch (CollidingCompilationUnitException e) {
+ logger.log(TreeLogger.ERROR, e.getMessage());
+ throw new UnableToCompleteException();
+ }
// Makes JProgram aware of these types so they can be accessed via index.
unifyAst.addRootTypes(allRootTypes);
// Must synthesize entryPoint.onModuleLoad() calls because some EntryPoint classes are
// private.
- synthesizeEntryMethodHolderInit(unifyAst, entryMethodHolderTypeName);
+ if (entryMethodHolderTypeName != null) {
+ // Only synthesize the init method in the EntryMethodHolder class, if there is an
+ // EntryMethodHolder class.
+ synthesizeEntryMethodHolderInit(unifyAst, entryMethodHolderTypeName);
+ }
// Ensures that unification traversal starts from these methods.
jprogram.addEntryMethod(jprogram.getIndexedMethod("Impl.registerEntry"));
- jprogram.addEntryMethod(jprogram.getIndexedMethod(
- SourceName.getShortClassName(entryMethodHolderTypeName) + ".init"));
+ if (entryMethodHolderTypeName != null) {
+ // Only register the init method in the EntryMethodHolder class as an entry method, if there
+ // is an EntryMethodHolder class.
+ jprogram.addEntryMethod(jprogram.getIndexedMethod(
+ SourceName.getShortClassName(entryMethodHolderTypeName) + ".init"));
+ }
unifyAst.exec();
}
}
@@ -1213,7 +1249,7 @@
protected final PrecompileTaskOptions options;
- // VisibleForTesting
+ @VisibleForTesting
JProgram jprogram;
public JavaToJavaScriptCompiler(TreeLogger logger, CompilerContext compilerContext) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompiler.java
index 020f3ff..1124453 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompiler.java
@@ -29,6 +29,7 @@
import com.google.gwt.dev.cfg.RuleReplaceWithFallback;
import com.google.gwt.dev.cfg.Rules;
import com.google.gwt.dev.cfg.RuntimeRebindRegistratorGenerator;
+import com.google.gwt.dev.cfg.RuntimeRebindRuleGenerator;
import com.google.gwt.dev.javac.CompilationUnit;
import com.google.gwt.dev.javac.CompiledClass;
import com.google.gwt.dev.javac.StandardGeneratorContext;
@@ -60,10 +61,7 @@
import com.google.gwt.dev.resource.impl.FileResource;
import com.google.gwt.dev.util.Name.BinaryName;
import com.google.gwt.dev.util.Pair;
-import com.google.gwt.thirdparty.guava.common.base.Predicate;
-import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
-import com.google.gwt.thirdparty.guava.common.collect.Multimap;
-import com.google.gwt.thirdparty.guava.common.collect.SetMultimap;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import java.io.File;
@@ -86,7 +84,7 @@
*/
public class LibraryJavaToJavaScriptCompiler extends JavaToJavaScriptCompiler {
- // VisibleForTesting
+ @VisibleForTesting
class LibraryPermutationCompiler extends PermutationCompiler {
public LibraryPermutationCompiler(Permutation permutation) {
@@ -139,14 +137,9 @@
}
}
- // VisibleForTesting
+ @VisibleForTesting
class LibraryPrecompiler extends Precompiler {
- private Set<String> badRebindCombinations = Sets.newHashSet();
- private SetMultimap<String, String> generatorNamesByPreviouslyReboundTypeName =
- HashMultimap.create();
- private Set<String> previouslyReboundTypeNames = Sets.newHashSet();
-
public LibraryPrecompiler(RebindPermutationOracle rpo, String[] entryPointTypeNames) {
super(rpo, entryPointTypeNames);
}
@@ -175,7 +168,7 @@
jprogram = new JProgram(false);
}
- // VisibleForTesting
+ @VisibleForTesting
protected JDeclaredType ensureFullTypeLoaded(JDeclaredType type) {
return findTypeBySourceName(BinaryName.toSourceName(type.getName()));
}
@@ -214,7 +207,7 @@
return null;
}
- // VisibleForTesting
+ @VisibleForTesting
protected Set<JDeclaredType> gatherReboundTypes(RebindPermutationOracle rpo) {
Collection<CompilationUnit> compilationUnits =
rpo.getCompilationState().getCompilationUnits();
@@ -237,7 +230,7 @@
return rpo.getGeneratorContext();
}
- // VisibleForTesting
+ @VisibleForTesting
protected Set<String> getTypeNames(Set<JDeclaredType> types) {
Set<String> typeNames = Sets.newHashSet();
for (JDeclaredType type : types) {
@@ -249,48 +242,23 @@
/**
* Runs a particular generator on the provided set of rebound types. Takes care to guard against
* duplicate work during reruns as generation approaches a fixed point.
- *
- * @return whether a fixed point was reached.
*/
- // VisibleForTesting
- protected boolean runGenerator(RuleGenerateWith generatorRule, Set<String> reboundTypeNames)
+ @VisibleForTesting
+ protected void runGenerator(RuleGenerateWith generatorRule, Set<String> reboundTypeNames)
throws UnableToCompleteException {
- boolean fixedPoint = true;
- StandardGeneratorContext generatorContext = getGeneratorContext();
- removePreviouslyReboundCombinations(generatorRule.getName(), reboundTypeNames);
- reboundTypeNames.removeAll(previouslyReboundTypeNames);
-
for (String reboundTypeName : reboundTypeNames) {
- if (badRebindCombinations.contains(generatorRule.getName() + "-" + reboundTypeName)) {
- continue;
- }
- generatorNamesByPreviouslyReboundTypeName.put(reboundTypeName, generatorRule.getName());
- reboundTypeName = reboundTypeName.replace("$", ".");
- generatorRule.generate(logger, module.getProperties(), generatorContext, reboundTypeName);
-
- if (generatorContext.isDirty()) {
- fixedPoint = false;
- previouslyReboundTypeNames.add(reboundTypeName);
- // Ensure that cascading generations rerun properly.
- for (String generatedTypeName : generatorContext.getGeneratedUnitMap().keySet()) {
- generatorNamesByPreviouslyReboundTypeName.removeAll(generatedTypeName);
- }
- generatorContext.finish(logger);
- } else {
- badRebindCombinations.add(generatorRule.getName() + "-" + reboundTypeName);
- }
+ generatorRule.generate(logger, module.getProperties(), getGeneratorContext(),
+ BinaryName.toSourceName(reboundTypeName));
}
-
- return fixedPoint;
}
- // VisibleForTesting
+ @VisibleForTesting
protected void runGeneratorsToFixedPoint(RebindPermutationOracle rpo)
throws UnableToCompleteException {
boolean fixedPoint;
do {
compilerContext.getLibraryWriter()
- .setReboundTypeSourceNames(getTypeNames(gatherReboundTypes(rpo)));
+ .markReboundTypesProcessed(getTypeNames(gatherReboundTypes(rpo)));
fixedPoint = runGenerators();
} while (!fixedPoint);
@@ -307,7 +275,7 @@
}
}
- // VisibleForTesting
+ @VisibleForTesting
void buildFallbackRuntimeRebindRules(Set<JDeclaredType> reboundTypes)
throws UnableToCompleteException {
// Create fallback rebinds.
@@ -328,7 +296,7 @@
}
}
- // VisibleForTesting
+ @VisibleForTesting
void buildPropertyProviderRegistrator(Set<String> allRootTypes,
SortedSet<BindingProperty> bindingProperties,
SortedSet<ConfigurationProperty> configurationProperties) throws UnableToCompleteException {
@@ -348,8 +316,14 @@
generatorContext.finish(logger);
}
- // VisibleForTesting
+ @VisibleForTesting
void buildRuntimeRebindRegistrator(Set<String> allRootTypes) throws UnableToCompleteException {
+ // If no runtime rebind rules were created for this library.
+ if (RuntimeRebindRuleGenerator.RUNTIME_REBIND_RULE_SOURCES_BY_SHORT_NAME.isEmpty()) {
+ // Then there's no need to generate a registrator to attach them to the runtime registry.
+ return;
+ }
+
RuntimeRebindRegistratorGenerator runtimeRebindRegistratorGenerator =
new RuntimeRebindRegistratorGenerator();
StandardGeneratorContext generatorContext = getGeneratorContext();
@@ -366,7 +340,7 @@
generatorContext.finish(logger);
}
- // VisibleForTesting
+ @VisibleForTesting
void buildSimpleRuntimeRebindRules(Rules rules) throws UnableToCompleteException {
// Create rebinders for rules specified in the module.
Iterator<Rule> iterator = rules.iterator();
@@ -379,35 +353,6 @@
}
}
- private boolean relevantPropertiesHaveChanged(RuleGenerateWith generatorRule) {
- // Gather binding and configuration property values that have been changed in the part of
- // the library dependency tree on which this generator has not yet run.
- Multimap<String, String> newConfigurationPropertyValues =
- compilerContext.gatherNewConfigurationPropertyValuesForGenerator(generatorRule.getName());
- Multimap<String, String> newBindingPropertyValues =
- compilerContext.gatherNewBindingPropertyValuesForGenerator(generatorRule.getName());
-
- return generatorRule.caresAboutProperties(newConfigurationPropertyValues.keySet())
- || generatorRule.caresAboutProperties(newBindingPropertyValues.keySet());
- }
-
- /**
- * Generator output can create opportunities for further generator execution, so runGenerators()
- * is repeated to a fixed point. But previously handled generator/reboundType pairs should be
- * ignored.
- */
- private void removePreviouslyReboundCombinations(
- final String generatorName, Set<String> newReboundTypeNames) {
- newReboundTypeNames.removeAll(
- Sets.newHashSet(Sets.filter(newReboundTypeNames, new Predicate<String>() {
- @Override
- public boolean apply(String newReboundTypeName) {
- return generatorNamesByPreviouslyReboundTypeName.containsEntry(
- newReboundTypeName, generatorName);
- }
- })));
- }
-
/**
* Figures out which generators should run based on the current state and runs them. Generator
* execution can create new opportunities for further generator execution so this function
@@ -416,10 +361,11 @@
* Returns whether a fixed point was reached.
*/
private boolean runGenerators() throws UnableToCompleteException {
- boolean fixedPoint = true;
boolean globalCompile = compilerContext.getOptions().shouldLink();
Set<Rule> generatorRules = Sets.newHashSet(module.getGeneratorRules());
+ TreeLogger branch = logger.branch(TreeLogger.SPAM, "running generators");
+
for (Rule rule : generatorRules) {
RuleGenerateWith generatorRule = (RuleGenerateWith) rule;
String generatorName = generatorRule.getName();
@@ -428,27 +374,53 @@
// Type unstable generators can only be safely run in the global phase.
// TODO(stalcup): modify type unstable generators such that their output is no longer
// unstable.
+ branch.log(TreeLogger.SPAM,
+ "skipping generator " + generatorName + " since it can only run in the global phase");
continue;
}
- // Run generator for new rebound types.
- Set<String> newReboundTypeNames =
- compilerContext.gatherNewReboundTypeNamesForGenerator(generatorName);
- fixedPoint &= runGenerator(generatorRule, newReboundTypeNames);
-
- // If the content of generator output varies when some relevant properties change and some
- // relevant properties have changed.
- if (relevantPropertiesHaveChanged(generatorRule)) {
- // Rerun the generator on old rebound types to replace old stale output.
- Set<String> oldReboundTypeNames =
- compilerContext.gatherOldReboundTypeNamesForGenerator(generatorName);
- fixedPoint &= runGenerator(generatorRule, oldReboundTypeNames);
+ if (!generatorRule.relevantPropertiesAreFinal(module.getProperties(),
+ options.getFinalProperties())) {
+ // Some property(s) that this generator cares about have not yet reached their final
+ // value. Running the generator now would be wasted effort as it would just need to be run
+ // again later anyway.
+ branch.log(TreeLogger.SPAM, "skipping generator " + generatorName
+ + " since properties it cares about have not reached their final values.");
+ continue;
}
- compilerContext.getLibraryWriter().addRanGeneratorName(generatorName);
+ Set<String> reboundTypes = Sets.newHashSet(compilerContext.getReboundTypeSourceNames());
+ Set<String> processedReboundTypeSourceNamesForGenerator =
+ compilerContext.getProcessedReboundTypeSourceNames(generatorName);
+
+ Set<String> unprocessedReboundTypeSourceNames = Sets.newHashSet(reboundTypes);
+ unprocessedReboundTypeSourceNames.removeAll(processedReboundTypeSourceNamesForGenerator);
+ if (unprocessedReboundTypeSourceNames.isEmpty()) {
+ // All the requested rebound types have already been processed by this generator.
+ branch.log(TreeLogger.SPAM, "skipping generator " + generatorName
+ + " since it has already processed all requested rebound types.");
+ continue;
+ }
+
+ branch.log(TreeLogger.SPAM, "running generator " + generatorName + " on "
+ + unprocessedReboundTypeSourceNames.size() + " not yet processed rebound types");
+ runGenerator(generatorRule, unprocessedReboundTypeSourceNames);
+
+ // Marks the previously unprocessed types as processed.
+ for (String unprocessedReboundTypeSourceName : unprocessedReboundTypeSourceNames) {
+ compilerContext.getLibraryWriter().markReboundTypeProcessed(
+ unprocessedReboundTypeSourceName, generatorName);
+ }
}
- return fixedPoint;
+ // If there is output.
+ if (getGeneratorContext().isDirty()) {
+ // Compile and assimilate it.
+ getGeneratorContext().finish(logger);
+ return false;
+ }
+
+ return true;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
index 9358401..07719cb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
@@ -38,10 +38,8 @@
import com.google.gwt.dev.jjs.ast.JVariableRef;
import com.google.gwt.dev.jjs.impl.MakeCallsStatic.CreateStaticImplsVisitor;
import com.google.gwt.dev.jjs.impl.MakeCallsStatic.StaticCallConverter;
-import com.google.gwt.thirdparty.guava.common.collect.HashMultiset;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
-import com.google.gwt.thirdparty.guava.common.collect.Multiset;
import java.util.List;
import java.util.Map;
@@ -250,11 +248,6 @@
* Contains the Cast.instanceofArray method.
*/
private final JMethod isJavaArray;
- /**
- * Key is the method signature, value is the number of unique instances with
- * the same signature.
- */
- private Multiset<String> jsoMethodInstances = HashMultiset.create();
/**
* Contains the set of devirtualizing methods that replace polymorphic calls
@@ -274,8 +267,7 @@
private JMethod createDevirtualMethodFor(JMethod method, JDeclaredType inClass) {
SourceInfo sourceInfo = method.getSourceInfo().makeChild();
- int methodCount = jsoMethodInstances.add(method.getSignature(),1);
- String prefix = method.getName() + (methodCount == 0 ? "" : methodCount);
+ String prefix = computeEscapedSignature(method.getSignature());
JMethod devirtualMethod = new JMethod(sourceInfo, prefix + "__devirtual$",
inClass, method.getType(), false, true, true, AccessModifier.PUBLIC);
devirtualMethod.setBody(new JMethodBody(sourceInfo));
@@ -296,6 +288,15 @@
return devirtualMethod;
}
+ /**
+ * A normal method signature contains characters that are not valid in a method name. If you want
+ * to construct a method name based on an existing method signature then those characters need to
+ * be escaped.
+ */
+ private static String computeEscapedSignature(String methodSignature) {
+ return methodSignature.replaceAll("[\\<\\>\\(\\)\\;\\/\\[]", "_");
+ }
+
private Devirtualizer(JProgram program) {
this.program = program;
this.isJavaStringMethod = program.getIndexedMethod("Cast.isJavaString");
@@ -316,7 +317,6 @@
RewriteVirtualDispatches rewriter = new RewriteVirtualDispatches();
rewriter.accept(program);
- assert (rewriter.didChange());
}
/**
@@ -341,7 +341,7 @@
*/
private static JExpression constructMinimalCondition(JMethod checkMethod, JVariableRef target,
JMethodCall trueDispatch, JExpression falseDispatch) {
- // TODO(rluble): Maybe we should emit sligthly different code in checked mode, so that if
+ // TODO(rluble): Maybe we should emit slightly different code in checked mode, so that if
// no condition is met an exception would be thrown rather than cascading.
if (falseDispatch == null && trueDispatch == null) {
return null;
@@ -494,8 +494,14 @@
// It is an interface implemented by String or arrays, place it in Object.
devirtualMethodEnclosingClass = program.getTypeJavaLangObject();
}
- // The class where the method is being place is in the current compile.
- assert !program.isReferenceOnly(devirtualMethodEnclosingClass);
+ // Devirtualization of external methods stays external and devirtualization of internal methods
+ // stays internal.
+ assert program.isReferenceOnly(devirtualMethodEnclosingClass)
+ == program.isReferenceOnly(method.getEnclosingType());
+ // TODO(stalcup): devirtualization is modifying both internal and external types. Really
+ // external types should never be modified. Change the point at which types are saved into
+ // libraries to be after normalization has occurred, so that no further modification is
+ // necessary when loading external types.
JMethod devirtualMethod = createDevirtualMethodFor(method, devirtualMethodEnclosingClass);
/**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
index fa17743..72e6d84 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
@@ -121,6 +121,8 @@
// TODO: SOYC correlations.
// TODO(stalcup): perform only binary name based lookups so that libraries
// don't need to index compilation units by both source and binary name
+// TODO(stalcup): shrink the translate/flowInto graph for reference only types to eliminate
+// unnecessary loading of types and increase performance.
public class UnifyAst {
/**
@@ -453,7 +455,7 @@
ArrayList<JExpression> instantiationExpressions = new ArrayList<JExpression>(answers.size());
for (String answer : answers) {
- JDeclaredType answerType = findType(answer, sourceNameBasedTypeLocator);
+ JDeclaredType answerType = internalFindType(answer, sourceNameBasedTypeLocator);
if (answerType == null) {
error(gwtCreateCall, "Rebind result '" + answer + "' could not be found");
return null;
@@ -579,6 +581,13 @@
private final CompilationState compilationState;
private final Map<String, CompiledClass> compiledClassesByInternalName;
private final Map<String, CompiledClass> compiledClassesBySourceName;
+ /**
+ * JVisitor interferes with any exceptions thrown inside of a visitor traversal call tree so any
+ * time UnifyAst wants to log an error and end operation care it should be done by manually
+ * logging an error line and setting errorsFound to true. Adequate checking is already in place to
+ * interpret this as ending further exploration and errorsFound = true is already being converted
+ * to an UnableToCompleteException at the UnifyAst public function boundaries
+ */
private boolean errorsFound = false;
private final Set<CompilationUnit> failedUnits = new IdentityHashSet<CompilationUnit>();
private final Map<String, JField> fieldMap = new HashMap<String, JField>();
@@ -633,9 +642,10 @@
public void addRootTypes(Collection<String> sourceTypeNames) throws UnableToCompleteException {
for (String sourceTypeName : sourceTypeNames) {
- findType(sourceTypeName, sourceNameBasedTypeLocator);
+ internalFindType(sourceTypeName, sourceNameBasedTypeLocator);
}
if (errorsFound) {
+ // Already logged.
throw new UnableToCompleteException();
}
}
@@ -647,7 +657,7 @@
public void buildEverything() throws UnableToCompleteException {
for (String internalName : compiledClassesByInternalName.keySet()) {
String typeName = InternalName.toBinaryName(internalName);
- findType(typeName, binaryNameBasedTypeLocator);
+ internalFindType(typeName, binaryNameBasedTypeLocator);
}
for (JDeclaredType type : program.getDeclaredTypes()) {
@@ -695,7 +705,7 @@
// Trace execution from all types supplied as source and resolve references.
Set<String> internalNames = ImmutableSet.copyOf(compiledClassesByInternalName.keySet());
for (String internalName : internalNames) {
- JDeclaredType type = findType(internalName, internalNameBasedTypeLocator);
+ JDeclaredType type = internalFindType(internalName, internalNameBasedTypeLocator);
instantiate(type);
for (JField field : type.getFields()) {
flowInto(field);
@@ -718,9 +728,6 @@
mapApi(program.getTypeJavaLangString());
flowInto(methodMap.get("java.lang.String.valueOf(C)Ljava/lang/String;"));
- // Additional pre-optimization code gen.
- // TODO: roll these into this class?
-
// EnumNameObfuscator
flowInto(program.getIndexedMethod("Enum.obfuscatedName"));
@@ -755,6 +762,7 @@
}
computeOverrides();
if (errorsFound) {
+ // Already logged.
throw new UnableToCompleteException();
}
}
@@ -807,12 +815,8 @@
program.addType(referenceOnlyType);
program.addReferenceOnlyType(referenceOnlyType);
}
- // Flow into the signature of each contained method, so that method call references from
- // inside this library to functions on these external types can resolve.
- for (JDeclaredType t : types) {
- for (JMethod method : t.getMethods()) {
- flowInto(method);
- }
+ for (JDeclaredType referenceOnlyType : types) {
+ resolveType(referenceOnlyType);
}
}
@@ -1214,7 +1218,18 @@
type.resolve(resolvedInterfaces, resolvedRescues);
}
- public JDeclaredType findType(String typeName, NameBasedTypeLocator nameBasedTypeLocator) {
+ public JDeclaredType findType(String typeName, NameBasedTypeLocator nameBasedTypeLocator)
+ throws UnableToCompleteException {
+ JDeclaredType type = internalFindType(typeName, nameBasedTypeLocator);
+ if (errorsFound) {
+ // Already logged.
+ throw new UnableToCompleteException();
+ }
+ return type;
+ }
+
+ private JDeclaredType internalFindType(String typeName,
+ NameBasedTypeLocator nameBasedTypeLocator) {
if (nameBasedTypeLocator.resolvedTypeIsAvailable(typeName)) {
return nameBasedTypeLocator.getResolvedType(typeName);
}
@@ -1225,9 +1240,10 @@
}
if (compilerContext.shouldCompileMonolithic()) {
- throw new NoClassDefFoundError(String.format(
- "Could not find %s in types compiled from source. "
+ logger.log(TreeLogger.ERROR, String.format("Could not find %s in types compiled from source. "
+ "It was either unavailable or failed to compile.", typeName));
+ errorsFound = true;
+ return null;
}
if (nameBasedTypeLocator.libraryCompilationUnitIsAvailable(typeName)) {
@@ -1235,10 +1251,12 @@
return nameBasedTypeLocator.getResolvedType(typeName);
}
- throw new NoClassDefFoundError(String.format(
+ logger.log(TreeLogger.ERROR, String.format(
"Could not find %s in types compiled from source or in provided dependency libraries. "
+ "Either the source file was unavailable, failed to compile or there is a missing "
+ "dependency.", typeName));
+ errorsFound = true;
+ return null;
}
private void staticInitialize(JDeclaredType type) {
@@ -1280,14 +1298,12 @@
if (!type.isExternal()) {
return type;
}
-
String typeName = type.getName();
- JDeclaredType newType = findType(typeName, binaryNameBasedTypeLocator);
+ JDeclaredType newType = internalFindType(typeName, binaryNameBasedTypeLocator);
if (newType == null) {
assert errorsFound;
return type;
}
-
assert !newType.isExternal();
return newType;
}
diff --git a/dev/core/src/com/google/gwt/dev/url/CloseableJarHandler.java b/dev/core/src/com/google/gwt/dev/url/CloseableJarHandler.java
new file mode 100644
index 0000000..4d76854
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/url/CloseableJarHandler.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2014 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.url;
+
+import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
+import com.google.gwt.thirdparty.guava.common.collect.Multimap;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.Permission;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Opens connections to Zip files when requested and gathers them in a list for later closing.
+ * <p>
+ * This closing functionality makes it possible to run untrusted plugin code (for example
+ * Generators) and still guarantee that there will be no accidental attempts to read from old and
+ * deleted Zip files using stale InputStreams.
+ */
+@SuppressWarnings("restriction")
+public class CloseableJarHandler extends sun.net.www.protocol.jar.Handler {
+
+ /**
+ * Passes through all URLConnection access but collects all created InputStreams.
+ */
+ private class CloseableUrlConnection extends URLConnection {
+
+ private JarURLConnection jarUrlConnection;
+
+ public CloseableUrlConnection(URL jarUrl, JarURLConnection jarUrlConnection) {
+ super(jarUrl);
+ this.jarUrlConnection = jarUrlConnection;
+ }
+
+ @Override
+ public void addRequestProperty(String key, String value) {
+ jarUrlConnection.addRequestProperty(key, value);
+ }
+
+ @Override
+ public void connect() throws IOException {
+ jarUrlConnection.connect();
+ }
+
+ @Override
+ public boolean getAllowUserInteraction() {
+ return jarUrlConnection.getAllowUserInteraction();
+ }
+
+ @Override
+ public int getConnectTimeout() {
+ return jarUrlConnection.getConnectTimeout();
+ }
+
+ @Override
+ public Object getContent() throws IOException {
+ return jarUrlConnection.getContent();
+ }
+
+ @Override
+ public Object getContent(Class[] classes) throws IOException {
+ return jarUrlConnection.getContent(classes);
+ }
+
+ @Override
+ public String getContentEncoding() {
+ return jarUrlConnection.getContentEncoding();
+ }
+
+ @Override
+ public int getContentLength() {
+ return jarUrlConnection.getContentLength();
+ }
+
+ @Override
+ public long getContentLengthLong() {
+ return jarUrlConnection.getContentLengthLong();
+ }
+
+ @Override
+ public String getContentType() {
+ return jarUrlConnection.getContentType();
+ }
+
+ @Override
+ public long getDate() {
+ return jarUrlConnection.getDate();
+ }
+
+ @Override
+ public boolean getDefaultUseCaches() {
+ return jarUrlConnection.getDefaultUseCaches();
+ }
+
+ @Override
+ public boolean getDoInput() {
+ return jarUrlConnection.getDoInput();
+ }
+
+ @Override
+ public boolean getDoOutput() {
+ return jarUrlConnection.getDoOutput();
+ }
+
+ @Override
+ public long getExpiration() {
+ return jarUrlConnection.getExpiration();
+ }
+
+ @Override
+ public String getHeaderField(int n) {
+ return jarUrlConnection.getHeaderField(n);
+ }
+
+ @Override
+ public String getHeaderField(String name) {
+ return jarUrlConnection.getHeaderField(name);
+ }
+
+ @Override
+ public long getHeaderFieldDate(String name, long defaultValue) {
+ return jarUrlConnection.getHeaderFieldDate(name, defaultValue);
+ }
+
+ @Override
+ public int getHeaderFieldInt(String name, int defaultValue) {
+ return jarUrlConnection.getHeaderFieldInt(name, defaultValue);
+ }
+
+ @Override
+ public String getHeaderFieldKey(int n) {
+ return jarUrlConnection.getHeaderFieldKey(n);
+ }
+
+ @Override
+ public long getHeaderFieldLong(String name, long defaultValue) {
+ return jarUrlConnection.getHeaderFieldLong(name, defaultValue);
+ }
+
+ @Override
+ public Map<String, List<String>> getHeaderFields() {
+ return jarUrlConnection.getHeaderFields();
+ }
+
+ @Override
+ public long getIfModifiedSince() {
+ return jarUrlConnection.getIfModifiedSince();
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ InputStream inputStream = jarUrlConnection.getInputStream();
+ URL jarFileURL = jarUrlConnection.getJarFileURL();
+ inputStreamsByJarFilePath.put(jarFileURL.getFile(), inputStream);
+ return inputStream;
+ }
+
+ @Override
+ public long getLastModified() {
+ return jarUrlConnection.getLastModified();
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ return jarUrlConnection.getOutputStream();
+ }
+
+ @Override
+ public Permission getPermission() throws IOException {
+ return jarUrlConnection.getPermission();
+ }
+
+ @Override
+ public int getReadTimeout() {
+ return jarUrlConnection.getReadTimeout();
+ }
+
+ @Override
+ public Map<String, List<String>> getRequestProperties() {
+ return jarUrlConnection.getRequestProperties();
+ }
+
+ @Override
+ public String getRequestProperty(String key) {
+ return jarUrlConnection.getRequestProperty(key);
+ }
+
+ @Override
+ public URL getURL() {
+ return jarUrlConnection.getURL();
+ }
+
+ @Override
+ public boolean getUseCaches() {
+ return jarUrlConnection.getUseCaches();
+ }
+
+ @Override
+ public void setAllowUserInteraction(boolean allowuserinteraction) {
+ jarUrlConnection.setAllowUserInteraction(allowuserinteraction);
+ }
+
+ @Override
+ public void setConnectTimeout(int timeout) {
+ jarUrlConnection.setConnectTimeout(timeout);
+ }
+
+ @Override
+ public void setDefaultUseCaches(boolean defaultusecaches) {
+ jarUrlConnection.setDefaultUseCaches(defaultusecaches);
+ }
+
+ @Override
+ public void setDoInput(boolean doinput) {
+ jarUrlConnection.setDoInput(doinput);
+ }
+
+ @Override
+ public void setDoOutput(boolean dooutput) {
+ jarUrlConnection.setDoOutput(dooutput);
+ }
+
+ @Override
+ public void setIfModifiedSince(long ifmodifiedsince) {
+ jarUrlConnection.setIfModifiedSince(ifmodifiedsince);
+ }
+
+ @Override
+ public void setReadTimeout(int timeout) {
+ jarUrlConnection.setReadTimeout(timeout);
+ }
+
+ @Override
+ public void setRequestProperty(String key, String value) {
+ jarUrlConnection.setRequestProperty(key, value);
+ }
+
+ @Override
+ public void setUseCaches(boolean usecaches) {
+ jarUrlConnection.setUseCaches(usecaches);
+ }
+
+ @Override
+ public String toString() {
+ return jarUrlConnection.toString();
+ }
+ }
+
+ private Multimap<String, InputStream> inputStreamsByJarFilePath = HashMultimap.create();
+
+ /**
+ * Closes all InputStreams that were created by the URL system that point at the given Jar file.
+ */
+ public void closeStreams(String jarFilePath) throws IOException {
+ Collection<InputStream> inputStreams = inputStreamsByJarFilePath.get(jarFilePath);
+ if (inputStreams == null) {
+ return;
+ }
+
+ for (InputStream inputStream : inputStreams) {
+ inputStream.close();
+ }
+ inputStreamsByJarFilePath.removeAll(jarFilePath);
+ }
+
+ @Override
+ protected URLConnection openConnection(URL jarUrl) throws IOException {
+ // Let the Jar system do the heavy lifting of opening the connection.
+ JarURLConnection jarUrlConnection = (JarURLConnection) super.openConnection(jarUrl);
+ // Ensures that when all connections have been closed the cached Zip file references will be
+ // cleared.
+ jarUrlConnection.setUseCaches(false);
+
+ // Wrap the jar url connection in a way that will collect all created InputStreams so that they
+ // can be closed.
+ return new CloseableUrlConnection(jarUrl, jarUrlConnection);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/url/CloseableJarHandlerFactory.java b/dev/core/src/com/google/gwt/dev/url/CloseableJarHandlerFactory.java
new file mode 100644
index 0000000..90c11ea
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/url/CloseableJarHandlerFactory.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 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.url;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+
+/**
+ * A factory that constructs URL Stream Handlers and extends Jar protocol handling by making it
+ * possible to close all InputStreams for a given Zip file.
+ * <p>
+ * This closing functionality makes it possible to run untrusted plugin code (for example
+ * Generators) and still guarantee that there will be no accidental attempts to read from old and
+ * deleted Zip files using stale InputStreams.
+ */
+public class CloseableJarHandlerFactory implements URLStreamHandlerFactory {
+
+ /**
+ * Takes over the URL connection opening system so that Zip file connections can be gathered and
+ * then closed before Zip files are replaced on disk. This avoids a JVM crash on Linux that can
+ * occur if a third party plugin (ie. Generator) fails to close its InputStreams.
+ */
+ private static CloseableJarHandlerFactory closeableJarHandlerFactory;
+
+ /**
+ * Closes all InputStreams that were created by the URL system that point at the given Jar file.
+ */
+ public static void closeStreams(String jarFilePath) throws IOException {
+ closeableJarHandlerFactory.closeableJarHandler.closeStreams(jarFilePath);
+ }
+
+ /**
+ * Ensures that the standard UrlStreamHandlerFactory has been replaced.
+ */
+ public static synchronized void installOverride() {
+ if (closeableJarHandlerFactory == null) {
+ closeableJarHandlerFactory = new CloseableJarHandlerFactory();
+ URL.setURLStreamHandlerFactory(closeableJarHandlerFactory);
+ }
+ }
+
+ private static URLStreamHandler createHandlerForProtocol(String protocol) {
+ String className = String.format("sun.net.www.protocol.%s.Handler", protocol);
+ try {
+ return (URLStreamHandler) Class.forName(className).newInstance();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Couldn't find handler for protocol: " + protocol);
+ } catch (InstantiationException e) {
+ throw new RuntimeException("Handler isn't not instantiable for protocol: " + protocol);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Handler isn't not accessible for protocol: " + protocol);
+ }
+ }
+
+ private CloseableJarHandler closeableJarHandler = new CloseableJarHandler();
+
+ private CloseableJarHandlerFactory() {
+ // Force access through the public static functions.
+ }
+
+ @Override
+ public URLStreamHandler createURLStreamHandler(String protocol) {
+ if (protocol.equals("jar")) {
+ // Override the url stream handler for Jar files.
+ return closeableJarHandler;
+ } else {
+ // Regular url stream handling for other protocols.
+ return createHandlerForProtocol(protocol);
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/TinyCompileSummary.java b/dev/core/src/com/google/gwt/dev/util/TinyCompileSummary.java
new file mode 100644
index 0000000..e1a6e08
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/TinyCompileSummary.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 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;
+
+/**
+ * A small set of compile metrics that should be used to warn incremental compile users when a
+ * module is getting too large.
+ */
+public class TinyCompileSummary {
+
+ private int typesForGeneratorsCount;
+ private int typesForAstCount;
+ private int staticSourceFilesCount;
+ private int generatedSourceFilesCount;
+ private int cachedStaticSourceFilesCount;
+ private int cachedGeneratedSourceFilesCount;
+
+ public int getTypesForGeneratorsCount() {
+ return typesForGeneratorsCount;
+ }
+
+ public void setTypesForGeneratorsCount(int typesForGeneratorsCount) {
+ this.typesForGeneratorsCount = typesForGeneratorsCount;
+ }
+
+ public int getTypesForAstCount() {
+ return typesForAstCount;
+ }
+
+ public void setTypesForAstCount(int typesForAstCount) {
+ this.typesForAstCount = typesForAstCount;
+ }
+
+ public int getStaticSourceFilesCount() {
+ return staticSourceFilesCount;
+ }
+
+ public void setStaticSourceFilesCount(int staticSourceFilesCount) {
+ this.staticSourceFilesCount = staticSourceFilesCount;
+ }
+
+ public int getGeneratedSourceFilesCount() {
+ return generatedSourceFilesCount;
+ }
+
+ public void setGeneratedSourceFilesCount(int generatedSourceFilesCount) {
+ this.generatedSourceFilesCount = generatedSourceFilesCount;
+ }
+
+ public int getCachedStaticSourceFilesCount() {
+ return cachedStaticSourceFilesCount;
+ }
+
+ public void setCachedStaticSourceFilesCount(int cachedStaticSourceFilesCount) {
+ this.cachedStaticSourceFilesCount = cachedStaticSourceFilesCount;
+ }
+
+ public int getCachedGeneratedSourceFilesCount() {
+ return cachedGeneratedSourceFilesCount;
+ }
+
+ public void setCachedGeneratedSourceFilesCount(int cachedGeneratedSourceFilesCount) {
+ this.cachedGeneratedSourceFilesCount = cachedGeneratedSourceFilesCount;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerStrictResources.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerStrictResources.java
index c31e9a2..4f9eb18 100644
--- a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerStrictResources.java
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerStrictResources.java
@@ -24,17 +24,22 @@
*/
public class ArgHandlerStrictResources extends ArgHandlerFlag {
- private final OptionStrictResources options;
+ private final OptionStrictSourceResources optionStrictSourceResources;
+ private final OptionStrictPublicResources optionStrictPublicResources;
- public ArgHandlerStrictResources(OptionStrictResources options) {
- this.options = options;
+ public <T extends OptionStrictSourceResources &
+ OptionStrictPublicResources> ArgHandlerStrictResources(T options) {
+ this.optionStrictSourceResources = options;
+ this.optionStrictPublicResources = options;
addTagValue("-XstrictResources", true);
}
@Override
public boolean getDefaultValue() {
- return options.enforceStrictResources();
+ assert optionStrictSourceResources.enforceStrictSourceResources()
+ == optionStrictPublicResources.enforceStrictPublicResources();
+ return optionStrictSourceResources.enforceStrictSourceResources();
}
@Override
@@ -55,7 +60,8 @@
@Override
public boolean setFlag(boolean value) {
- options.setEnforceStrictResources(value);
+ optionStrictSourceResources.setEnforceStrictSourceResources(value);
+ optionStrictPublicResources.setEnforceStrictPublicResources(value);
return true;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionFinalProperties.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionFinalProperties.java
new file mode 100644
index 0000000..14f63fe
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionFinalProperties.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 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.dev.cfg.Properties;
+
+/**
+ * Option providing a set of properties representing the final property state that will be reached
+ * at the root of a library compile tree.
+ * <p>
+ * Library compilation uses this as a guide for how long it must delay generator execution to avoid
+ * having to rerun the same generator again later when properties it depends on change.
+ */
+public interface OptionFinalProperties {
+
+ /**
+ * Returns the final properties.
+ */
+ Properties getFinalProperties();
+
+ /**
+ * Sets the list of paths to input libraries.
+ */
+ void setFinalProperties(Properties properties);
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionStrictPublicResources.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionStrictPublicResources.java
new file mode 100644
index 0000000..fa3058b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionStrictPublicResources.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 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;
+
+/**
+ * Whether to be strict about public resource loading by implicitly adding "public" as a public
+ * folder for every module.
+ */
+public interface OptionStrictPublicResources {
+
+ /**
+ * Returns true if the compiler should not implicitly add a "public" dependency.
+ */
+ boolean enforceStrictPublicResources();
+
+ /**
+ * Sets whether the compiler should not implicitly add a "public" dependency.
+ */
+ void setEnforceStrictPublicResources(boolean strictPublicResources);
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionStrictResources.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionStrictResources.java
deleted file mode 100644
index 541628d..0000000
--- a/dev/core/src/com/google/gwt/dev/util/arg/OptionStrictResources.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2013 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;
-
-/**
- * Whether to be strict about resource loading and in particular whether to implicitly add "client"
- * and "public" resource dependencies when none are mentioned.
- */
-public interface OptionStrictResources {
-
- /**
- * Returns true if the compiler should not implicitly add "client" and "public" resource
- * dependencies when none are mentioned.
- */
- boolean enforceStrictResources();
-
- /**
- * Sets whether the compiler should not implicitly add "client" and "public" resource dependencies
- * when none are mentioned.
- */
- void setEnforceStrictResources(boolean strictResources);
-}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionStrictSourceResources.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionStrictSourceResources.java
new file mode 100644
index 0000000..e1a49d3
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionStrictSourceResources.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 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;
+
+/**
+ * Whether to be strict about source resource loading by implicitly adding "client" as a source
+ * folder for every module.
+ */
+public interface OptionStrictSourceResources {
+
+ /**
+ * Returns true if the compiler should not implicitly add a "client" dependency.
+ */
+ boolean enforceStrictSourceResources();
+
+ /**
+ * Sets whether the compiler should not implicitly add a "client" dependency.
+ */
+ void setEnforceStrictSourceResources(boolean strictSourceResources);
+}
diff --git a/dev/core/test/com/google/gwt/dev/FooBarGenerator.java b/dev/core/test/com/google/gwt/dev/FooBarGenerator.java
new file mode 100644
index 0000000..33239cd
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/FooBarGenerator.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.Generator.RunsLocal;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+
+import java.io.PrintWriter;
+
+/**
+ * A simple generator for running in tests, converts class Foo to Bar.
+ */
+@RunsLocal
+public class FooBarGenerator extends Generator {
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context, String typeName)
+ throws UnableToCompleteException {
+ PrintWriter pw = context.tryCreate(logger, "com.google.gwt.dev", "Bar");
+ if (pw != null) {
+ pw.println("package com.google.gwt.dev;");
+ pw.println("public class Bar {}");
+ context.commit(logger, pw);
+ }
+ return "com.google.gwt.dev.Bar";
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/IncrementalBuilderTest.java b/dev/core/test/com/google/gwt/dev/IncrementalBuilderTest.java
new file mode 100644
index 0000000..6634fac
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/IncrementalBuilderTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.cfg.LibraryGroup;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.cfg.ResourceLoader;
+import com.google.gwt.dev.cfg.ResourceLoaders;
+import com.google.gwt.dev.util.UnitTestTreeLogger;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
+import com.google.gwt.thirdparty.guava.common.io.Files;
+import com.google.gwt.thirdparty.guava.common.io.Resources;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Test for {@link IncrementalBuilder}.
+ */
+public class IncrementalBuilderTest extends TestCase {
+
+ private static void assertBuildResult(String rootModuleName, boolean expectedBuildSuccess,
+ String... expectedErrorMessages) throws MalformedURLException {
+ UnitTestTreeLogger.Builder loggerBuilder = new UnitTestTreeLogger.Builder();
+ loggerBuilder.setLowestLogLevel(TreeLogger.INFO);
+ for (String expectedErrorMessage : expectedErrorMessages) {
+ loggerBuilder.expectError(expectedErrorMessage, null);
+ }
+ UnitTestTreeLogger testLogger = loggerBuilder.createLogger();
+
+ IncrementalBuilder incrementalBuilder =
+ createIncrementalBuilder(rootModuleName, createGwtClassPathResourceLoader());
+ incrementalBuilder.clean();
+
+ boolean actualBuildSuccess = incrementalBuilder.build(testLogger).isSuccess();
+
+ assertEquals(expectedBuildSuccess, actualBuildSuccess);
+ testLogger.assertLogEntriesContainExpected();
+ }
+
+ private static ResourceLoader createGwtClassPathResourceLoader() throws MalformedURLException {
+ String gwtClassPathProperty = System.getProperty("gwt.cp");
+ String[] gwtClassPathStrings = gwtClassPathProperty.split(";");
+ final List<URL> gwtClassPathEntries = Lists.newArrayList();
+ for (String gwtClassPathString : gwtClassPathStrings) {
+ gwtClassPathEntries.add(new File(gwtClassPathString).toURI().toURL());
+ }
+ ResourceLoader gwtClassPathResourceLoader = new ResourceLoader() {
+ @Override
+ public List<URL> getClassPath() {
+ return gwtClassPathEntries;
+ }
+
+ @Override
+ public URL getResource(String resourceName) {
+ return Thread.currentThread().getContextClassLoader().getResource(resourceName);
+ }
+ };
+ return gwtClassPathResourceLoader;
+ }
+
+ private static ResourceLoader createGwtClassPathResourceLoaderWithMocks(
+ String... mockResourceNames) throws FileNotFoundException, IOException {
+ final ResourceLoader gwtClassPathResourceLoader = createGwtClassPathResourceLoader();
+ File mockResourcesDirectory = Files.createTempDir();
+
+ for (String mockResourceName : mockResourceNames) {
+ File mockFooFile = new File(mockResourcesDirectory, mockResourceName);
+ mockFooFile.getParentFile().mkdirs();
+ URL realFooResource = gwtClassPathResourceLoader.getResource(mockResourceName);
+ Resources.copy(realFooResource, new FileOutputStream(mockFooFile));
+ }
+
+ return ResourceLoaders.forPathAndFallback(Lists.newArrayList(mockResourcesDirectory),
+ gwtClassPathResourceLoader);
+ }
+
+ private static IncrementalBuilder createIncrementalBuilder(String rootModuleName,
+ ResourceLoader resourceLoader) {
+ String warDir = Files.createTempDir().getAbsolutePath();
+ String libDir = "/tmp/gwt/lib";
+ String genDir = "/tmp/gwt/gen";
+ IncrementalBuilder incrementalBuilder =
+ new IncrementalBuilder(rootModuleName, warDir, libDir, genDir, resourceLoader);
+ return incrementalBuilder;
+ }
+
+ public void testBuildRebuildEditRebuild() throws FileNotFoundException, IOException {
+ // Setup logging expectations for a full compile with no caching.
+ UnitTestTreeLogger.Builder loggerBuilder = new UnitTestTreeLogger.Builder();
+ loggerBuilder.setLowestLogLevel(TreeLogger.INFO);
+ loggerBuilder.expectInfo(BuildTarget.formatCompilingModuleMessage("com.google.gwt.core.Core"),
+ null);
+ loggerBuilder.expectInfo(BuildTarget.formatCompilingModuleMessage(
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.SimpleBottom"), null);
+ loggerBuilder.expectInfo(BuildTarget.formatCompilingModuleMessage(
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.SimpleMid"), null);
+ loggerBuilder.expectInfo(BuildTarget.formatCompilingModuleMessage(
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.SimpleTop"), null);
+ UnitTestTreeLogger testLogger = loggerBuilder.createLogger();
+
+ // Prepare the compiler.
+ ResourceLoader resourceLoader = createGwtClassPathResourceLoaderWithMocks(
+ "com/google/gwt/dev/testdata/incrementalbuildsystem/Foo.java");
+ IncrementalBuilder incrementalBuilder = createIncrementalBuilder(
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.SimpleTop", resourceLoader);
+ incrementalBuilder.clean();
+
+ // Compile.
+ boolean actualBuildSuccess = incrementalBuilder.build(testLogger).isSuccess();
+
+ // Assert compile succeeded and was a full compile with no caching.
+ assertEquals(true, actualBuildSuccess);
+ testLogger.assertLogEntriesContainExpected();
+
+ // Setup logging expectations for a fully cached compile.
+ loggerBuilder = new UnitTestTreeLogger.Builder();
+ loggerBuilder.setLowestLogLevel(TreeLogger.INFO);
+ loggerBuilder.expectInfo(IncrementalBuilder.NO_FILES_HAVE_CHANGED, null);
+ testLogger = loggerBuilder.createLogger();
+
+ // Recompile without changes.
+ actualBuildSuccess = incrementalBuilder.rebuild(testLogger).isSuccess();
+
+ // Assert compile succeeded and was fully cached.
+ assertEquals(true, actualBuildSuccess);
+ testLogger.assertLogEntriesContainExpected();
+
+ // Change modification date of the Foo.java seen by SimpleMid.gwt.xml
+ File fooFile = new File(resourceLoader.getResource(
+ "com/google/gwt/dev/testdata/incrementalbuildsystem/Foo.java").getPath());
+ fooFile.setLastModified(System.currentTimeMillis() + 60 * 1000);
+
+ // Setup logging expectations for partially cached recompile.
+ loggerBuilder = new UnitTestTreeLogger.Builder();
+ loggerBuilder.setLowestLogLevel(TreeLogger.SPAM);
+ loggerBuilder.expectSpam(
+ BuildTarget.formatReusingCachedLibraryMessage("com.google.gwt.core.Core"), null);
+ loggerBuilder.expectSpam(BuildTarget.formatReusingCachedLibraryMessage(
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.SimpleBottom"), null);
+ loggerBuilder.expectInfo(BuildTarget.formatCompilingModuleMessage(
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.SimpleMid"), null);
+ loggerBuilder.expectInfo(BuildTarget.formatCompilingModuleMessage(
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.SimpleTop"), null);
+ testLogger = loggerBuilder.createLogger();
+
+ // Recompile with changes.
+ actualBuildSuccess = incrementalBuilder.rebuild(testLogger).isSuccess();
+
+ // Assert compile succeeded and was partially cached.
+ assertEquals(true, actualBuildSuccess);
+ testLogger.assertLogEntriesContainExpected();
+ }
+
+ public void testCircularReference() throws MalformedURLException {
+ List<String> circularModulePath = Arrays.asList(new String[] {
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.CircularRoot",
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.CircularB <loop>",
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.CircularC",
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.CircularFilesetD <fileset>",
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.CircularB <loop>"});
+ assertBuildResult("com.google.gwt.dev.testdata.incrementalbuildsystem.CircularRoot", false,
+ IncrementalBuilder.formatCircularModulePathMessage(circularModulePath));
+ }
+
+ public void testDuplicateGeneratorOutput() throws MalformedURLException {
+ String duplicateCompilationUnitError = LibraryGroup.formatDuplicateCompilationUnitMessage(
+ "com.google.gwt.dev.Bar", "com.google.gwt.dev.testdata.incrementalbuildsystem.ParallelLeft",
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.ParallelRight");
+ assertBuildResult("com.google.gwt.dev.testdata.incrementalbuildsystem.ParallelRoot", false,
+ duplicateCompilationUnitError);
+ }
+
+ public void testDuplicateSourceInclusion() throws MalformedURLException {
+ String duplicateCompilationUnitError = LibraryGroup.formatDuplicateCompilationUnitMessage(
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.Foo",
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.DuplicateLeft",
+ "com.google.gwt.dev.testdata.incrementalbuildsystem.DuplicateRight");
+ assertBuildResult("com.google.gwt.dev.testdata.incrementalbuildsystem.DuplicateRoot", false,
+ duplicateCompilationUnitError);
+ }
+
+ public void testUnableToFindModule() throws MalformedURLException {
+ String unableToFindModuleMessage = ModuleDefLoader.formatUnableToFindModuleMessage(
+ "com/google/gwt/dev/testdata/incrementalbuildsystem/NoSuchModule.gwt.xml");
+ assertBuildResult("com.google.gwt.dev.testdata.incrementalbuildsystem.NoSuchModule", false,
+ unableToFindModuleMessage);
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/cfg/ConditionTest.java b/dev/core/test/com/google/gwt/dev/cfg/ConditionTest.java
index 0c62fe1..cb7e542 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/ConditionTest.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/ConditionTest.java
@@ -105,14 +105,6 @@
}
public void testToSource() {
- // Linker assertions are not supported.
- try {
- new ConditionWhenLinkerAdded("com.some.Linker").toSource();
- fail("expected linker condition source conversion to fail.");
- } catch (UnsupportedOperationException e) {
- // Expected behavior.
- }
-
// Compound conditions collapse effectively empty slots.
assertEquals("((requestTypeClass == @com.google.gwt.Foo::class))", new ConditionAll(
new ConditionAny(), new ConditionWhenTypeIs("com.google.gwt.Foo")).toSource());
diff --git a/dev/core/test/com/google/gwt/dev/cfg/DynamicPropertyOracleTest.java b/dev/core/test/com/google/gwt/dev/cfg/DynamicPropertyOracleTest.java
index 095cc79..d0a358e 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/DynamicPropertyOracleTest.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/DynamicPropertyOracleTest.java
@@ -60,13 +60,13 @@
assertEquals(1, dynamicPropertyOracle.getAccessedProperties().size());
// Finds XML Constrained.
- dynamicPropertyOracle.reset();
+ dynamicPropertyOracle = new DynamicPropertyOracle(properties);
userAgentProperty.addDefinedValue(new ConditionWhenLinkerAdded("foo"), "webkit");
userAgentProperty.addDefinedValue(new ConditionWhenLinkerAdded("bar"), "webkit");
userAgentProperty.addDefinedValue(new ConditionWhenLinkerAdded("baz"), "webkit");
assertEquals(
"webkit", dynamicPropertyOracle.getSelectionProperty(null, "user.agent").getCurrentValue());
- assertFalse(dynamicPropertyOracle.haveAccessedPropertiesChanged());
+ assertTrue(dynamicPropertyOracle.haveAccessedPropertiesChanged());
assertEquals(1, dynamicPropertyOracle.getAccessedProperties().size());
// Finds first defined.
diff --git a/dev/core/test/com/google/gwt/dev/cfg/LibraryGroupTest.java b/dev/core/test/com/google/gwt/dev/cfg/LibraryGroupTest.java
index 6f7c2c9..a314cb1 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/LibraryGroupTest.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/LibraryGroupTest.java
@@ -127,7 +127,8 @@
}
// Show that the library group collects and returns them all.
- assertTrue(expectedReboundTypeSourceNames.equals(libraryGroup.getReboundTypeSourceNames()));
+ assertTrue(
+ expectedReboundTypeSourceNames.equals(libraryGroup.getReboundTypeSourceNames()));
}
public void testLibraryLinkOrder() {
@@ -154,89 +155,26 @@
assertEquals(libraries.size(), linkOrderLibraries.size());
}
- /**
- * See buildVariedPropertyGeneratorLibraryGroup() for test library group structure.
- */
- public void testPropertyCollectionByGenerator() {
- LibraryGroup libraryGroup = buildVariedPropertyGeneratorLibraryGroup();
-
- // Collect "new" legal binding property values from the perspective of each generator.
- Collection<String> newUserAgentsForUserAgentAsserter =
- libraryGroup.gatherNewBindingPropertyValuesForGenerator("UserAgentAsserter").get(
- "user.agent");
- Collection<String> newUserAgentsForLocalizedDatePickerGenerator =
- libraryGroup.gatherNewBindingPropertyValuesForGenerator("LocalizedDatePickerGenerator").get(
- "user.agent");
- Collection<String> newLocalesForLocalizedDatePickerGenerator =
- libraryGroup.gatherNewBindingPropertyValuesForGenerator("LocalizedDatePickerGenerator").get(
- "locale");
-
- // Verify that the results are as expected.
- assertEquals(
- Sets.newHashSet("webkit_phone", "webkit_tablet"), newUserAgentsForUserAgentAsserter);
- assertEquals(Sets.newHashSet("webkit_phone", "webkit_tablet", "webkit", "mozilla", "ie"),
- newUserAgentsForLocalizedDatePickerGenerator);
- assertEquals(Sets.newHashSet("ru"), newLocalesForLocalizedDatePickerGenerator);
- }
-
- /**
- * <pre>
- * root library: {
- * user.agent: [webkit_phone, webkit_tablet],
- * locale: [ru]
- * ranGenerators: [],
- * libraries: [
- * sub library 1: {
- * user.agent: [webkit, mozilla, ie],
- * locale: [],
- * ranGenerators: [UserAgentAsserter]
- * },
- * sub library 2: {
- * user.agent: [],
- * locale: [en, fr],
- * ranGenerators: [LocalizedDatePickerGenerator]
- * }
- * ]
- * }
- * </pre>
- */
- private LibraryGroup buildVariedPropertyGeneratorLibraryGroup() {
- String generatorNameOne = "UserAgentAsserter";
- String generatorNameTwo = "LocalizedDatePickerGenerator";
- return buildVariedPropertyGeneratorLibraryGroup(
- generatorNameOne, Sets.<String>newHashSet(), generatorNameTwo, Sets.<String>newHashSet());
- }
-
- public static LibraryGroup buildVariedPropertyGeneratorLibraryGroup(String generatorNameOne,
- Set<String> reboundTypeSourceNamesOne, String generatorNameTwo,
- Set<String> reboundTypeSourceNamesTwo) {
+ public static LibraryGroup buildVariedPropertyGeneratorLibraryGroup(
+ Set<String> reboundTypeSourceNamesOne, Set<String> reboundTypeSourceNamesTwo) {
// A root library that adds more legal user.agent and locale values but for which no generators
// have been run.
MockLibrary rootLibrary = new MockLibrary("RootLibrary");
- rootLibrary.getDependencyLibraryNames()
- .addAll(Lists.newArrayList("SubLibrary1", "SubLibrary2"));
- rootLibrary.getNewBindingPropertyValuesByName()
- .putAll("user.agent", Lists.newArrayList("webkit_phone", "webkit_tablet"));
- rootLibrary.getNewBindingPropertyValuesByName().putAll("locale", Lists.newArrayList("ru"));
+ rootLibrary.getDependencyLibraryNames().addAll(
+ Lists.newArrayList("SubLibrary1", "SubLibrary2"));
// A library that adds legal user.agent values and has already run the UserAgentAsserter
// generator.
MockLibrary subLibrary1 = new MockLibrary("SubLibrary1");
- subLibrary1.getNewBindingPropertyValuesByName()
- .putAll("user.agent", Lists.newArrayList("webkit", "mozilla", "ie"));
- subLibrary1.getRanGeneratorNames().add(generatorNameOne);
subLibrary1.getReboundTypeSourceNames().addAll(reboundTypeSourceNamesOne);
// A library that adds legal locale values and has already run the LocaleMessageGenerator
// generator.
MockLibrary subLibrary2 = new MockLibrary("SubLibrary2");
- subLibrary2.getNewBindingPropertyValuesByName()
- .putAll("locale", Lists.newArrayList("en", "fr"));
- subLibrary2.getRanGeneratorNames().add(generatorNameTwo);
subLibrary2.getReboundTypeSourceNames().addAll(reboundTypeSourceNamesTwo);
LibraryGroup libraryGroup = LibraryGroup.fromLibraries(
- Lists.<Library>newArrayList(rootLibrary, subLibrary1, subLibrary2), true);
+ Lists.<Library> newArrayList(rootLibrary, subLibrary1, subLibrary2), true);
return libraryGroup;
}
diff --git a/dev/core/test/com/google/gwt/dev/cfg/MockLibrary.java b/dev/core/test/com/google/gwt/dev/cfg/MockLibrary.java
index 370ff64..dc150fe 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/MockLibrary.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/MockLibrary.java
@@ -20,7 +20,6 @@
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.Name.InternalName;
import com.google.gwt.dev.util.ZipEntryBackedObject;
-import com.google.gwt.thirdparty.guava.common.collect.ArrayListMultimap;
import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
@@ -68,10 +67,8 @@
private String libraryName;
private Multimap<String, String> nestedBinaryNamesByCompilationUnitName = HashMultimap.create();
private Multimap<String, String> nestedSourceNamesByCompilationUnitName = HashMultimap.create();
- private Multimap<String, String> newBindingPropertyValuesByName = ArrayListMultimap.create();
- private Multimap<String, String> newConfigurationPropertyValuesByName =
- ArrayListMultimap.create();
- private Set<String> ranGeneratorNames = Sets.newHashSet();
+ private Multimap<String, String> processedReboundTypeSourceNamesByGenerator =
+ HashMultimap.create();
private Set<String> reboundTypeNames = Sets.newHashSet();
private Set<String> superSourceCompilationUnitTypeNames = Sets.newHashSet();
@@ -117,6 +114,10 @@
}
@Override
+ public void close() {
+ }
+
+ @Override
public Resource getBuildResourceByPath(String path) {
return null;
}
@@ -172,21 +173,16 @@
}
@Override
- public Multimap<String, String> getNewBindingPropertyValuesByName() {
- return newBindingPropertyValuesByName;
- }
-
- @Override
- public Multimap<String, String> getNewConfigurationPropertyValuesByName() {
- return newConfigurationPropertyValuesByName;
- }
-
- @Override
public ZipEntryBackedObject<PermutationResult> getPermutationResultHandle() {
return null;
}
@Override
+ public Multimap<String, String> getProcessedReboundTypeSourceNamesByGenerator() {
+ return processedReboundTypeSourceNamesByGenerator;
+ }
+
+ @Override
public Resource getPublicResourceByPath(String path) {
return null;
}
@@ -197,16 +193,6 @@
}
@Override
- public Set<String> getRanGeneratorNames() {
- return ranGeneratorNames;
- }
-
- @Override
- public Set<String> getReboundTypeSourceNames() {
- return reboundTypeNames;
- }
-
- @Override
public Set<String> getRegularClassFilePaths() {
return null;
}
@@ -217,6 +203,11 @@
}
@Override
+ public Set<String> getReboundTypeSourceNames() {
+ return reboundTypeNames;
+ }
+
+ @Override
public Set<String> getSuperSourceClassFilePaths() {
return null;
}
diff --git a/dev/core/test/com/google/gwt/dev/cfg/MockLibraryWriter.java b/dev/core/test/com/google/gwt/dev/cfg/MockLibraryWriter.java
index 98ab7c8..f998354 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/MockLibraryWriter.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/MockLibraryWriter.java
@@ -18,8 +18,8 @@
import com.google.gwt.dev.jjs.PermutationResult;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.ZipEntryBackedObject;
-import com.google.gwt.thirdparty.guava.common.collect.ArrayListMultimap;
-import com.google.gwt.thirdparty.guava.common.collect.Multimap;
+import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
+import com.google.gwt.thirdparty.guava.common.collect.SetMultimap;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import java.util.Set;
@@ -32,9 +32,8 @@
private Set<String> buildResourcePaths = Sets.newHashSet();
private Set<String> dependencyLibraryNames = Sets.newHashSet();
private String libraryName;
- private Multimap<String, String> newBindingPropertyValuesByName = ArrayListMultimap.create();
- private Multimap<String, String> newConfigurationPropertyValuesByName =
- ArrayListMultimap.create();
+ private SetMultimap<String, String> processedReboundTypeSourceNamesByGenerator =
+ HashMultimap.create();
private Set<String> reboundTypeNames;
@Override
@@ -61,25 +60,9 @@
}
@Override
- public void addNewBindingPropertyValuesByName(
- String propertyName, Iterable<String> propertyValues) {
- newBindingPropertyValuesByName.putAll(propertyName, propertyValues);
- }
-
- @Override
- public void addNewConfigurationPropertyValuesByName(
- String propertyName, Iterable<String> propertyValues) {
- newConfigurationPropertyValuesByName.putAll(propertyName, propertyValues);
- }
-
- @Override
public void addPublicResource(Resource publicResource) {
}
- @Override
- public void addRanGeneratorName(String generatorName) {
- }
-
public Set<String> getBuildResourcePaths() {
return buildResourcePaths;
}
@@ -93,36 +76,37 @@
}
@Override
- public Multimap<String, String> getNewBindingPropertyValuesByName() {
- return newBindingPropertyValuesByName;
- }
-
- @Override
- public Multimap<String, String> getNewConfigurationPropertyValuesByName() {
- return newConfigurationPropertyValuesByName;
- }
-
- @Override
public ZipEntryBackedObject<PermutationResult> getPermutationResultHandle() {
return null;
}
@Override
+ public Set<String> getProcessedReboundTypeSourceNames(String generatorName) {
+ return processedReboundTypeSourceNamesByGenerator.get(generatorName);
+ }
+
+ @Override
public Set<String> getReboundTypeSourceNames() {
return reboundTypeNames;
}
@Override
- public void setLibraryName(String libraryName) {
- this.libraryName = libraryName;
+ public void markReboundTypeProcessed(String processedReboundTypeSourceName,
+ String generatorName) {
+ processedReboundTypeSourceNamesByGenerator.put(generatorName, processedReboundTypeSourceName);
}
@Override
- public void setReboundTypeSourceNames(Set<String> reboundTypeSourceNames) {
+ public void markReboundTypesProcessed(Set<String> reboundTypeSourceNames) {
this.reboundTypeNames = reboundTypeSourceNames;
}
@Override
+ public void setLibraryName(String libraryName) {
+ this.libraryName = libraryName;
+ }
+
+ @Override
public void write() {
}
}
diff --git a/dev/core/test/com/google/gwt/dev/cfg/ModuleDefLoaderTest.java b/dev/core/test/com/google/gwt/dev/cfg/ModuleDefLoaderTest.java
index 7936c3c..5de014f 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/ModuleDefLoaderTest.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/ModuleDefLoaderTest.java
@@ -22,6 +22,7 @@
import com.google.gwt.dev.javac.testing.impl.MockResource;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.UnitTestTreeLogger;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
@@ -30,8 +31,8 @@
import java.io.File;
import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
+import java.util.Collection;
+import java.util.Set;
/**
* Test for the module def loading
@@ -39,13 +40,14 @@
public class ModuleDefLoaderTest extends TestCase {
private CompilerContext compilerContext;
- private CompilerContext.Builder compilerContextBuilder = new CompilerContext.Builder();
+ private CompilerContext.Builder compilerContextBuilder;
private MockLibraryWriter mockLibraryWriter = new MockLibraryWriter();
public void assertHonorsStrictResources(boolean strictResources)
throws UnableToCompleteException {
TreeLogger logger = TreeLogger.NULL;
- compilerContext.getOptions().setEnforceStrictResources(strictResources);
+ compilerContext.getOptions().setEnforceStrictSourceResources(strictResources);
+ compilerContext.getOptions().setEnforceStrictPublicResources(strictResources);
ModuleDef emptyModule = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.merging.Empty");
Resource sourceFile =
@@ -153,6 +155,9 @@
public void testLoadFromLibraryGroup() throws UnableToCompleteException, IOException,
IncompatibleLibraryVersionException {
+ PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
+ logger.setMaxDetail(TreeLogger.INFO);
+
// Create the library zip file.
File zipFile = File.createTempFile("FooLib", ".gwtlib");
zipFile.deleteOnExit();
@@ -175,11 +180,11 @@
// Prepare the LibraryGroup and ResourceLoader.
compilerContext = compilerContextBuilder.libraryGroup(
LibraryGroup.fromLibraries(Lists.<Library> newArrayList(zipLibrary), false)).build();
- ResourceLoader resourceLoader =
- ResourceLoaders.wrap(new URLClassLoader(new URL[] {zipFile.toURL()}, null));
+ ResourceLoader resourceLoader = ResourceLoaders.forPathAndFallback(Lists.newArrayList(zipFile),
+ ResourceLoaders.forClassLoader(Thread.currentThread()));
// Will throw an exception if com.google.gwt.user.User can't be found and parsed.
- ModuleDefLoader.loadFromResources(TreeLogger.NULL, compilerContext, "com.google.gwt.user.User",
+ ModuleDefLoader.loadFromResources(logger, compilerContext, "com.google.gwt.user.User",
resourceLoader, false);
}
@@ -191,14 +196,17 @@
ModuleDef six = ModuleDefLoader.loadFromClassPath(
logger, compilerContext, "com.google.gwt.dev.cfg.testdata.dependents.Six");
- assertTrue(six.getDirectDependencies("com.google.gwt.dev.cfg.testdata.dependents.Six")
- .equals(ImmutableSet.of("com.google.gwt.dev.cfg.testdata.dependents.Five")));
- assertTrue("Contains " + six.getDirectDependencies(
- "com.google.gwt.dev.cfg.testdata.dependents.Five"),
- six.getDirectDependencies("com.google.gwt.dev.cfg.testdata.dependents.Five")
- .equals(ImmutableSet.of("com.google.gwt.dev.cfg.testdata.dependents.One",
- "com.google.gwt.dev.cfg.testdata.dependents.Two",
- "com.google.gwt.dev.cfg.testdata.dependents.Four")));
+ Collection<String> sixActualDependencies =
+ six.getDirectDependencies("com.google.gwt.dev.cfg.testdata.dependents.Six");
+ Collection<String> fiveActualDependencies =
+ six.getDirectDependencies("com.google.gwt.dev.cfg.testdata.dependents.Five");
+
+ assertEquals(ImmutableSet.of("com.google.gwt.core.Core",
+ "com.google.gwt.dev.cfg.testdata.dependents.Five"), sixActualDependencies);
+ assertEquals("Contains " + fiveActualDependencies, ImmutableSet.of(
+ "com.google.gwt.core.Core", "com.google.gwt.dev.cfg.testdata.dependents.One",
+ "com.google.gwt.dev.cfg.testdata.dependents.Two",
+ "com.google.gwt.dev.cfg.testdata.dependents.Four"), fiveActualDependencies);
}
/**
@@ -293,9 +301,13 @@
ModuleDef one = ModuleDefLoader.loadFromClassPath(logger, compilerContext,
"com.google.gwt.dev.cfg.testdata.merging.One");
- // Sees the logo.png image but not the java source file.
- assertEquals(one.getBuildResourceOracle().getPathNames(),
- Sets.newHashSet("com/google/gwt/dev/cfg/testdata/merging/resources/logo.png"));
+ Set<String> visibleResourcePaths = one.getBuildResourceOracle().getPathNames();
+ // Sees the logo.png image.
+ assertTrue(visibleResourcePaths.contains(
+ "com/google/gwt/dev/cfg/testdata/merging/resources/logo.png"));
+ // But not the java source file.
+ assertFalse(visibleResourcePaths.contains(
+ "com/google/gwt/dev/cfg/testdata/merging/resources/NotAResource.java"));
}
public void testSeparateLibraryModuleReferences() throws UnableToCompleteException {
@@ -310,7 +322,8 @@
"com/google/gwt/dev/cfg/testdata/separate/libraryone/LibraryOne.gwt.xml"),
mockLibraryWriter.getBuildResourcePaths());
// The library writer was given LibraryTwo as a dependency library.
- assertEquals(Sets.newHashSet("com.google.gwt.dev.cfg.testdata.separate.librarytwo.LibraryTwo"),
+ assertEquals(Sets.newHashSet("com.google.gwt.core.Core",
+ "com.google.gwt.dev.cfg.testdata.separate.librarytwo.LibraryTwo"),
mockLibraryWriter.getDependencyLibraryNames());
}
@@ -333,7 +346,8 @@
"com.google.gwt.dev.cfg.testdata.separate.filesetone.FileSetOne"),
libraryOneModule.getTargetLibraryCanonicalModuleNames());
// The module sees the referenced library module as a "library" module.
- assertEquals(Sets.newHashSet("com.google.gwt.dev.cfg.testdata.separate.librarytwo.LibraryTwo"),
+ assertEquals(Sets.newHashSet("com.google.gwt.core.Core",
+ "com.google.gwt.dev.cfg.testdata.separate.librarytwo.LibraryTwo"),
libraryOneModule.getExternalLibraryCanonicalModuleNames());
}
@@ -379,16 +393,8 @@
}
assertEquals(Sets.newHashSet(bindingProperty.getDefinedValues()),
Sets.newHashSet("yes", "no", "maybe"));
- assertEquals(Sets.newHashSet(bindingProperty.getTargetLibraryDefinedValues()),
- Sets.newHashSet("maybe"));
}
- // Library one added a new defined value of "maybe" for the "libraryTwoProperty" binding
- // property.
- assertEquals(Sets.newHashSet(
- mockLibraryWriter.getNewBindingPropertyValuesByName().get("libraryTwoProperty")),
- Sets.newHashSet("maybe"));
-
// Library one sees all defined values for the "libraryTwoConfigProperty" property and knows
// which one was defined in this target library.
for (ConfigurationProperty configurationProperty :
@@ -400,11 +406,6 @@
assertEquals(Sets.newHashSet(configurationProperty.getTargetLibraryValues()),
Sets.newHashSet("false"));
}
-
- // Library one added a new defined value of "maybe" for the "libraryTwoConfigProperty"
- // property.
- assertEquals(Sets.newHashSet(mockLibraryWriter.getNewConfigurationPropertyValuesByName().get(
- "libraryTwoConfigProperty")), Sets.newHashSet("false"));
}
private void assertErrorsWhenLoading(String moduleName, String... errorMessages) {
@@ -430,6 +431,7 @@
protected void setUp() throws Exception {
super.setUp();
ModuleDefLoader.getModulesCache().clear();
+ compilerContextBuilder = new CompilerContext.Builder();
compilerContext = compilerContextBuilder.libraryWriter(mockLibraryWriter).build();
}
}
diff --git a/dev/core/test/com/google/gwt/dev/cfg/ZipLibrariesTest.java b/dev/core/test/com/google/gwt/dev/cfg/ZipLibrariesTest.java
index 858ddfc..3f919ca 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/ZipLibrariesTest.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/ZipLibrariesTest.java
@@ -87,10 +87,6 @@
String expectedLibraryName = "BazLib";
final String expectedResourceContents =
"<html><head><title>Index</title></head><body>Hi</body></html>";
- Set<String> expectedRanGeneratorNames =
- Sets.newHashSet("UiBinderGenerator", "PlatinumGenerator");
- Set<String> expectedUserAgentConfigurationValues = Sets.newHashSet("webkit");
- Set<String> expectedLocaleConfigurationValues = Sets.newHashSet("en,default,en_US", "fr");
Set<String> expectedDependencyLibraryNames = Sets.newHashSet("FooLib", "BarLib");
oracle.add(BAR, SUPER_FOO, JdtCompilerTest.OUTER_INNER);
rebuildCompilationState();
@@ -109,13 +105,6 @@
return expectedResourceContents;
}
});
- zipLibraryWriter.addNewConfigurationPropertyValuesByName("user.agent",
- expectedUserAgentConfigurationValues);
- zipLibraryWriter.addNewConfigurationPropertyValuesByName("locale",
- expectedLocaleConfigurationValues);
- for (String generatorName : expectedRanGeneratorNames) {
- zipLibraryWriter.addRanGeneratorName(generatorName);
- }
zipLibraryWriter.addDependencyLibraryNames(expectedDependencyLibraryNames);
for (CompilationUnit compilationUnit : compilationUnits) {
zipLibraryWriter.addCompilationUnit(compilationUnit);
@@ -133,11 +122,6 @@
assertEquals(expectedLibraryName, zipLibrary.getLibraryName());
assertEquals(expectedResourceContents,
Util.readStreamAsString(zipLibrary.getPublicResourceByPath("index.html").openContents()));
- assertEquals(expectedRanGeneratorNames, zipLibrary.getRanGeneratorNames());
- assertEquals(expectedUserAgentConfigurationValues,
- zipLibrary.getNewConfigurationPropertyValuesByName().get("user.agent"));
- assertEquals(expectedLocaleConfigurationValues,
- zipLibrary.getNewConfigurationPropertyValuesByName().get("locale"));
assertEquals(expectedDependencyLibraryNames, zipLibrary.getDependencyLibraryNames());
// CompilationUnit
diff --git a/dev/core/test/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompilerTest.java b/dev/core/test/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompilerTest.java
index 0bda11e..ffc4797 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompilerTest.java
@@ -21,6 +21,8 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.dev.CompilerContext;
+import com.google.gwt.dev.CompilerOptionsImpl;
+import com.google.gwt.dev.PrecompileTaskOptions;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.Condition;
import com.google.gwt.dev.cfg.ConditionWhenPropertyIs;
@@ -168,6 +170,7 @@
@Override
public ArtifactSet finish(TreeLogger logger) throws UnableToCompleteException {
+ dirty = false;
// Don't actually compile generated source code;
return new ArtifactSet();
}
@@ -241,11 +244,11 @@
}
@Override
- protected boolean runGenerator(RuleGenerateWith generatorRule,
+ protected void runGenerator(RuleGenerateWith generatorRule,
Set<String> reboundTypeSourceNames) throws UnableToCompleteException {
processedReboundTypeSourceNames.addAll(reboundTypeSourceNames);
runCountByGeneratorName.incrementAndGet(generatorRule.getName());
- return super.runGenerator(generatorRule, reboundTypeSourceNames);
+ super.runGenerator(generatorRule, reboundTypeSourceNames);
}
}
@@ -360,12 +363,12 @@
Properties properties = new Properties();
BindingProperty userAgentProperty = properties.createBinding("user.agent");
userAgentProperty.setProvider(new PropertyProvider("return navigator.userAgent;"));
- userAgentProperty.addDefinedValue(userAgentProperty.getRootCondition(), "mozilla");
- userAgentProperty.addDefinedValue(userAgentProperty.getRootCondition(), "webkit");
+ userAgentProperty.addTargetLibraryDefinedValue(userAgentProperty.getRootCondition(), "mozilla");
+ userAgentProperty.addTargetLibraryDefinedValue(userAgentProperty.getRootCondition(), "webkit");
BindingProperty flavorProperty = properties.createBinding("flavor");
flavorProperty.setProvider(new PropertyProvider("return window.properties.flavor;"));
- flavorProperty.addDefinedValue(flavorProperty.getRootCondition(), "Vanilla");
- flavorProperty.addDefinedValue(flavorProperty.getRootCondition(), "Chocolate");
+ flavorProperty.addTargetLibraryDefinedValue(flavorProperty.getRootCondition(), "Vanilla");
+ flavorProperty.addTargetLibraryDefinedValue(flavorProperty.getRootCondition(), "Chocolate");
ConfigurationProperty emulateStackProperty =
properties.createConfiguration("emulateStack", false);
emulateStackProperty.setValue("TRUE");
@@ -428,12 +431,12 @@
.getConditions().add(new ConditionWhenTypeEndsWith("Messages"));
module.addRule(localeMessageGenerateRule);
LibraryGroup libraryGroup = LibraryGroupTest.buildVariedPropertyGeneratorLibraryGroup(
- "com.google.gwt.dev.jjs.LibraryJavaToJavaScriptCompilerTest.BrowserShimGenerator",
Sets.newHashSet("com.google.ChromeMessages"),
- "com.google.gwt.dev.jjs.LibraryJavaToJavaScriptCompilerTest.LocaleMessageGenerator",
Sets.newHashSet("com.google.WindowShim"));
+ PrecompileTaskOptions options = new CompilerOptionsImpl();
+ options.setFinalProperties(module.getProperties());
compilerContext = new CompilerContext.Builder().libraryGroup(libraryGroup)
- .libraryWriter(libraryWriter).module(module).build();
+ .libraryWriter(libraryWriter).module(module).options(options).build();
finishSetUpWithCompilerContext();
// Analyzes properties and generators in the library group and watches output in the generator
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/DevirtualizerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/DevirtualizerTest.java
index 9b102bd..2382f7c 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/DevirtualizerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/DevirtualizerTest.java
@@ -74,10 +74,10 @@
// the method used for val2 and val3.
StringBuffer expected = new StringBuffer();
expected.append("int result = ");
- expected.append("EntryPoint$Jso1.a__devirtual$(EntryPoint.val1) + ");
- expected.append("EntryPoint$Jso1.a__devirtual$(EntryPoint.val2) + ");
- expected.append("EntryPoint$Jso2.a1__devirtual$(EntryPoint.val3) + ");
- expected.append("EntryPoint$Jso2.a1__devirtual$(EntryPoint.val4);");
+ expected.append("EntryPoint$Jso1.a__I__devirtual$(EntryPoint.val1) + ");
+ expected.append("EntryPoint$Jso1.a__I__devirtual$(EntryPoint.val2) + ");
+ expected.append("EntryPoint$Jso2.a__I__devirtual$(EntryPoint.val3) + ");
+ expected.append("EntryPoint$Jso2.a__I__devirtual$(EntryPoint.val4);");
Result result = optimize("void", code.toString());
// Asserts that a() method calls were redirected to the devirtualized version.
@@ -120,13 +120,14 @@
// String.length are devirtualized separately.
StringBuffer expected = new StringBuffer();
expected.append("int result = ");
- expected.append(
+ expected.append(String.format(
// Methods in Comparable and CharSequence end up in String even if used by a JSO.
- "String.compareTo__devirtual$(EntryPoint.javaVal, EntryPoint.javaVal) + " +
- "String.compareTo__devirtual$(EntryPoint.jsoVal, EntryPoint.jsoVal) + " +
- "String.compareTo__devirtual$(EntryPoint.stringVal, EntryPoint.stringVal) + " +
- "String.length__devirtual$(EntryPoint.stringCharSeq) + " +
- "String.length1__devirtual$(EntryPoint.aString);");
+ "String.compareTo_%s__I__devirtual$(EntryPoint.javaVal, EntryPoint.javaVal) + " +
+ "String.compareTo_%s__I__devirtual$(EntryPoint.jsoVal, EntryPoint.jsoVal) + " +
+ "String.compareTo_%s__I__devirtual$(EntryPoint.stringVal, EntryPoint.stringVal) + " +
+ "String.length__I__devirtual$(EntryPoint.stringCharSeq) + " +
+ "String.length__I__devirtual$(EntryPoint.aString);", "Ljava_lang_Object",
+ "Ljava_lang_Object", "Ljava_lang_Object"));
Result result = optimize("void", code.toString());
result.intoString(expected.toString());
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/Showcase.gwt.xml b/samples/showcase/src/com/google/gwt/sample/showcase/Showcase.gwt.xml
index f6677a6..b106b5c 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/Showcase.gwt.xml
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/Showcase.gwt.xml
@@ -1,6 +1,6 @@
<module rename-to="showcase">
<!-- Inherit the core Web Toolkit stuff. -->
- <inherits name="com.google.gwt.core.Core"/>
+ <inherits name='com.google.gwt.core.Core'/>
<inherits name='com.google.gwt.user.User'/>
<inherits name="com.google.gwt.i18n.I18N"/>
<inherits name="com.google.gwt.i18n.CldrLocales"/>
@@ -28,4 +28,6 @@
<set-property-fallback name="locale" value="en"/>
<set-configuration-property name="locale.cookie" value="SHOWCASE_LOCALE"/>
<set-configuration-property name="locale.useragent" value="Y"/>
+
+ <source path="client" />
</module>
diff --git a/user/BUILD b/user/BUILD
index 6d77783..1dd9061 100644
--- a/user/BUILD
+++ b/user/BUILD
@@ -144,6 +144,32 @@
],
)
+# Packages just IncrementalBuilder test resource into a jar with correct java root.
+AugmentedJar(
+ name = "incremental-build-system-test-res",
+ srcs = [],
+ added_root_globs = {
+ "test": [
+ "test/com/google/gwt/dev/testdata/incrementalbuildsystem/*",
+ ],
+ },
+ # We only need the Java classes so don't wait for the rest of :gwt-dev.
+ build_deps = [
+ "//third_party/java_src/gwt:gwt-user-full",
+ "//third_party/java_src/gwt/svn/tools:dev_deps",
+ "//third_party/java_src/gwt/svn/trunk/dev:compiler.standalone.super",
+ "//third_party/java_src/gwt/svn/trunk/dev:gwt-dev-classes",
+ ],
+ constraints = [],
+ dojarjar = 1,
+ exclude_glob = [
+ "**/package.html",
+ "**/package-info.java",
+ ],
+ output_name = "incremental-build-system-test-res.jar",
+ wrap_javalibrary = 0,
+)
+
# See //third_party/java/gwt:regexp
java_library(
name = "jvm_regexp",
diff --git a/user/src/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java b/user/src/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java
index 4aaadae..a9ddbbb 100644
--- a/user/src/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java
+++ b/user/src/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java
@@ -1,12 +1,12 @@
/*
* Copyright 2011 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
@@ -16,6 +16,7 @@
package com.google.gwt.safecss.shared;
import com.google.gwt.core.shared.GWT;
+import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.base.Preconditions;
import java.util.HashMap;
@@ -24,11 +25,11 @@
/**
* SafeStyles utilities whose implementation differs between Development and
* Production Mode.
- *
+ *
* <p>
* This class has a super-source peer that provides the Production Mode
* implementation.
- *
+ *
* <p>
* Do not use this class - it is used for implementation only, and its methods
* may change in the future.
@@ -50,21 +51,21 @@
/**
* Check if the specified style property name is valid.
- *
+ *
* <p>
* NOTE: This method does <em>NOT</em> guarantee the safety of a style name.
* It looks for common errors, but does not check for every possible error. It
* is intended to help validate a string that the user has already asserted is
* safe.
* </p>
- *
+ *
* @param name the name to check
* @return null if valid, an error string if not
* @see <a
* href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS
* 2.1 identifiers</a>
*/
- // VisibleForTesting.
+ @VisibleForTesting
public static String isValidStyleName(String name) {
// Empty name.
if (name == null || name.isEmpty()) {
@@ -101,20 +102,20 @@
/**
* Check if the specified style property value is valid.
- *
+ *
* <p>
* NOTE: This method does <em>NOT</em> guarantee the safety of a style value.
* It looks for common errors, but does not check for every possible error. It
* is intended to help validate a string that the user has already asserted is
* safe.
* </p>
- *
+ *
* @param value the value to check
* @return null if valid, an error string if not
* @see <a href="http://www.w3.org/TR/CSS21/syndata.html#declaration">CSS 2.1
* declarations and properties</a>
*/
- // VisibleForTesting.
+ @VisibleForTesting
public static String isValidStyleValue(String value) {
// Empty value.
if (value == null || value.length() == 0) {
@@ -166,8 +167,8 @@
/*
* Almost all tokens are valid within a URL.
- *
- *
+ *
+ *
* TODO(jlabanca): Check for unescaped quotes and whitespace in URLs.
* Quotes and whitespace (other than the optional ones that wrap the
* URL) should be escaped.
@@ -243,7 +244,7 @@
/**
* Checks if the provided string is a valid style property name.
- *
+ *
* @param name the style name
* @see <a
* href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS
@@ -260,7 +261,7 @@
/**
* Checks if the provided string is a valid style property value.
- *
+ *
* @param value the style value
* @see <a href="http://www.w3.org/TR/CSS21/syndata.html#declaration">CSS 2.1
* declarations and properties</a>
@@ -279,7 +280,7 @@
* {@link #maybeCheckValidStyleName(String)} and
* {@link #maybeCheckValidStyleValue(String)} should perform their checks in a
* server-side environment.
- *
+ *
* @param check if true, perform server-side checks.
*/
public static void setForceCheckValidStyle(boolean check) {
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularB.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularB.gwt.xml
new file mode 100644
index 0000000..f19c870
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularB.gwt.xml
@@ -0,0 +1,3 @@
+<module>
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.CircularC" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularC.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularC.gwt.xml
new file mode 100644
index 0000000..b5eae77
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularC.gwt.xml
@@ -0,0 +1,3 @@
+<module>
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.CircularFilesetD" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularFilesetD.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularFilesetD.gwt.xml
new file mode 100644
index 0000000..3e9a9e2
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularFilesetD.gwt.xml
@@ -0,0 +1,3 @@
+<module type="fileset">
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.CircularB" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularRoot.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularRoot.gwt.xml
new file mode 100644
index 0000000..402002f
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CircularRoot.gwt.xml
@@ -0,0 +1,3 @@
+<module>
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.CircularB" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/DuplicateLeft.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/DuplicateLeft.gwt.xml
new file mode 100644
index 0000000..b115103
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/DuplicateLeft.gwt.xml
@@ -0,0 +1,3 @@
+<module>
+ <source path="" includes="Foo.java" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/DuplicateRight.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/DuplicateRight.gwt.xml
new file mode 100644
index 0000000..b115103
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/DuplicateRight.gwt.xml
@@ -0,0 +1,3 @@
+<module>
+ <source path="" includes="Foo.java" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/DuplicateRoot.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/DuplicateRoot.gwt.xml
new file mode 100644
index 0000000..f94563c
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/DuplicateRoot.gwt.xml
@@ -0,0 +1,4 @@
+<module>
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.DuplicateLeft" />
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.DuplicateRight" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/Foo.java b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/Foo.java
new file mode 100644
index 0000000..0d9d7c8
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/Foo.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 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.testdata.incrementalbuildsystem;
+
+/**
+ * A test class that exists only to be replaced during compilation.
+ */
+public class Foo {
+}
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/LeftFooCreator.java b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/LeftFooCreator.java
new file mode 100644
index 0000000..aed6caa
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/LeftFooCreator.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 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.testdata.incrementalbuildsystem;
+
+import com.google.gwt.core.client.GWT;
+
+/**
+ * A test class that exists only to trigger generation on Foo.
+ */
+public class LeftFooCreator {
+
+ private Object foo = GWT.create(Foo.class);
+}
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ParallelLeft.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ParallelLeft.gwt.xml
new file mode 100644
index 0000000..7a4d98b
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ParallelLeft.gwt.xml
@@ -0,0 +1,4 @@
+<module>
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.ProvidesFoo" />
+ <source path="" includes="LeftFooCreator.java" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ParallelRight.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ParallelRight.gwt.xml
new file mode 100644
index 0000000..6dfff21
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ParallelRight.gwt.xml
@@ -0,0 +1,4 @@
+<module>
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.ProvidesFoo" />
+ <source path="" includes="RightFooCreator.java" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ParallelRoot.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ParallelRoot.gwt.xml
new file mode 100644
index 0000000..ed30116
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ParallelRoot.gwt.xml
@@ -0,0 +1,5 @@
+<module>
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.ParallelLeft" />
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.ParallelRight" />
+ <source path="" includes="SomeClass.java" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ProvidesFoo.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ProvidesFoo.gwt.xml
new file mode 100644
index 0000000..2f8caf4
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ProvidesFoo.gwt.xml
@@ -0,0 +1,6 @@
+<module>
+ <source path="" includes="Foo.java" />
+ <generate-with class="com.google.gwt.dev.FooBarGenerator">
+ <when-type-is class="com.google.gwt.dev.testdata.incrementalbuildsystem.Foo"/>
+ </generate-with>
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/RightFooCreator.java b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/RightFooCreator.java
new file mode 100644
index 0000000..a78bbee
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/RightFooCreator.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 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.testdata.incrementalbuildsystem;
+
+import com.google.gwt.core.client.GWT;
+
+/**
+ * A test class that exists only to trigger generation on Foo.
+ */
+public class RightFooCreator {
+
+ private Object foo = GWT.create(Foo.class);
+}
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SimpleBottom.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SimpleBottom.gwt.xml
new file mode 100644
index 0000000..bcf06b3
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SimpleBottom.gwt.xml
@@ -0,0 +1,2 @@
+<module>
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SimpleMid.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SimpleMid.gwt.xml
new file mode 100644
index 0000000..85c1023
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SimpleMid.gwt.xml
@@ -0,0 +1,4 @@
+<module>
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.SimpleBottom" />
+ <source path="" includes="Foo.java" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SimpleTop.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SimpleTop.gwt.xml
new file mode 100644
index 0000000..e752079
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SimpleTop.gwt.xml
@@ -0,0 +1,3 @@
+<module>
+ <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.SimpleMid" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SomeClass.java b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SomeClass.java
new file mode 100644
index 0000000..082ca98
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SomeClass.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 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.testdata.incrementalbuildsystem;
+
+/**
+ * A test class that exists only to give a test module something to compile.
+ */
+public class SomeClass {
+}