summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormikaelpeltier <mikaelpeltier@google.com>2014-04-08 09:42:00 +0200
committermikaelpeltier <mikaelpeltier@google.com>2014-05-19 09:31:07 +0200
commit9d9e23d43bf2cb18ca5d6b5993273fe36bd7d9a6 (patch)
treefe9690857b467e7f96b5413dd4adf385ccbabeb7
parent042b969743cc923722a0452ed9b1002ca2fbd247 (diff)
downloadtoolchain_jack-9d9e23d43bf2cb18ca5d6b5993273fe36bd7d9a6.zip
toolchain_jack-9d9e23d43bf2cb18ca5d6b5993273fe36bd7d9a6.tar.gz
toolchain_jack-9d9e23d43bf2cb18ca5d6b5993273fe36bd7d9a6.tar.bz2
Add incremental support
- Move options from CompilerStateWriter to JackIncremental. Change-Id: I6ad5d6c9a5cca74baf5678b33e3ffda89dbd3262
-rw-r--r--jack/src/com/android/jack/CommandLine.java5
-rw-r--r--jack/src/com/android/jack/Jack.java5
-rw-r--r--jack/src/com/android/jack/Options.java5
-rw-r--r--jack/src/com/android/jack/backend/jayce/JayceSingleTypeWriter.java10
-rw-r--r--jack/src/com/android/jack/experimental/incremental/CompilerState.java (renamed from jack/src/com/android/jack/experimental/incremental/CompilerStateMarker.java)33
-rw-r--r--jack/src/com/android/jack/experimental/incremental/CompilerStateWriter.java23
-rw-r--r--jack/src/com/android/jack/experimental/incremental/JackIncremental.java396
-rw-r--r--jack/src/com/android/jack/experimental/incremental/Main.java108
-rw-r--r--jack/src/com/android/jack/experimental/incremental/UsageFinder.java17
-rw-r--r--jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java2
-rw-r--r--jack/src/com/android/jack/ir/impl/GwtAstBuilder.java13
-rw-r--r--jack/tests/com/android/jack/AllTests.java2
-rw-r--r--jack/tests/com/android/jack/TestTools.java4
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest001.java559
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest002.java82
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest003.java78
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest004.java69
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest005.java226
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest006.java92
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest007.java81
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest008.java125
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest009.java75
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest010.java182
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependenciesTest011.java80
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/DependencyAllTests.java13
-rw-r--r--jack/tests/com/android/jack/experimental/incremental/IncrementalTestingEnvironment.java221
26 files changed, 2432 insertions, 74 deletions
diff --git a/jack/src/com/android/jack/CommandLine.java b/jack/src/com/android/jack/CommandLine.java
index 8045b4e..4f40e1e 100644
--- a/jack/src/com/android/jack/CommandLine.java
+++ b/jack/src/com/android/jack/CommandLine.java
@@ -43,7 +43,7 @@ import javax.annotation.Nonnull;
public abstract class CommandLine {
@Nonnull
- private static final String INTERRUPTED_COMPILATION_WARNING =
+ protected static final String INTERRUPTED_COMPILATION_WARNING =
"Warning: This may have produced partial or corrupted output.";
@Nonnull
@@ -170,7 +170,8 @@ public abstract class CommandLine {
}
}
- private static void printExceptionMessage(@Nonnull Throwable t, @Nonnull String defaultMessage) {
+ protected static void printExceptionMessage(@Nonnull Throwable t,
+ @Nonnull String defaultMessage) {
String exceptionMessage = t.getMessage();
if (exceptionMessage == null) {
exceptionMessage = defaultMessage;
diff --git a/jack/src/com/android/jack/Jack.java b/jack/src/com/android/jack/Jack.java
index aacb7f1..9b73dc9 100644
--- a/jack/src/com/android/jack/Jack.java
+++ b/jack/src/com/android/jack/Jack.java
@@ -52,6 +52,7 @@ import com.android.jack.cfg.CfgMarkerRemover;
import com.android.jack.config.id.JavaVersionPropertyId.JavaVersion;
import com.android.jack.experimental.incremental.CompilerStateProduct;
import com.android.jack.experimental.incremental.CompilerStateWriter;
+import com.android.jack.experimental.incremental.JackIncremental;
import com.android.jack.experimental.incremental.UsageFinder;
import com.android.jack.frontend.FrontendCompilationException;
import com.android.jack.frontend.MethodIdDuplicateRemover;
@@ -307,7 +308,7 @@ public abstract class Jack {
unmodifiableCollections =
ThreadConfig.get(UnmodifiableCollections.UNMODIFIABLE_COLLECTION).create();
}
- assert unmodifiableCollections != null; //FINDBUGS
+ assert unmodifiableCollections != null; // FINDBUGS
return unmodifiableCollections;
}
@@ -443,7 +444,7 @@ public abstract class Jack {
request.addInitialTagsOrMarkers(getJackFormatInitialTagSet());
request.addProduction(JackFormatProduct.class);
}
- if (ThreadConfig.get(CompilerStateWriter.GENERATE_COMPILER_STATE).booleanValue()) {
+ if (ThreadConfig.get(JackIncremental.GENERATE_COMPILER_STATE).booleanValue()) {
request.addProduction(CompilerStateProduct.class);
}
}
diff --git a/jack/src/com/android/jack/Options.java b/jack/src/com/android/jack/Options.java
index afd8bbe..bde620f 100644
--- a/jack/src/com/android/jack/Options.java
+++ b/jack/src/com/android/jack/Options.java
@@ -329,6 +329,11 @@ public class Options {
}
@Nonnull
+ public String getClasspathAsString() {
+ return classpath;
+ }
+
+ @Nonnull
public List<File> getClasspath() {
return getFilesFromPathString(classpath);
}
diff --git a/jack/src/com/android/jack/backend/jayce/JayceSingleTypeWriter.java b/jack/src/com/android/jack/backend/jayce/JayceSingleTypeWriter.java
index e445047..ae261e3 100644
--- a/jack/src/com/android/jack/backend/jayce/JayceSingleTypeWriter.java
+++ b/jack/src/com/android/jack/backend/jayce/JayceSingleTypeWriter.java
@@ -21,8 +21,8 @@ import com.android.jack.JackFileException;
import com.android.jack.Options;
import com.android.jack.Options.Container;
import com.android.jack.backend.VDirPathFormatter;
-import com.android.jack.experimental.incremental.CompilerStateMarker;
-import com.android.jack.experimental.incremental.CompilerStateWriter;
+import com.android.jack.experimental.incremental.CompilerState;
+import com.android.jack.experimental.incremental.JackIncremental;
import com.android.jack.ir.JackFormatIr;
import com.android.jack.ir.NonJackFormatIr;
import com.android.jack.ir.ast.JDefinedClassOrInterface;
@@ -89,10 +89,10 @@ public class JayceSingleTypeWriter implements RunnableSchedulable<JDefinedClassO
JayceWriter writer = new JayceWriter(out);
writer.write(type, "jack " + Jack.getVersionString());
- if (ThreadConfig.get(CompilerStateWriter.GENERATE_COMPILER_STATE).booleanValue()) {
- assert vFile instanceof OutputDirectDir;
+ if (ThreadConfig.get(JackIncremental.GENERATE_COMPILER_STATE).booleanValue()) {
+ assert vDir instanceof OutputDirectDir;
assert outputDir != null;
- CompilerStateMarker csm = type.getSession().getMarker(CompilerStateMarker.class);
+ CompilerState csm = JackIncremental.getCompilerState();
assert csm != null;
csm.addMappingBetweenJavaAndJackFile(type.getSourceInfo().getFileName(),
new File(outputDir.getFile(), filePath).getAbsolutePath());
diff --git a/jack/src/com/android/jack/experimental/incremental/CompilerStateMarker.java b/jack/src/com/android/jack/experimental/incremental/CompilerState.java
index 417ff8e..a0dd0da 100644
--- a/jack/src/com/android/jack/experimental/incremental/CompilerStateMarker.java
+++ b/jack/src/com/android/jack/experimental/incremental/CompilerState.java
@@ -17,11 +17,10 @@
package com.android.jack.experimental.incremental;
import com.android.jack.JackUserException;
-import com.android.jack.ir.ast.JSession;
import com.android.jack.util.TextUtils;
import com.android.sched.item.Description;
-import com.android.sched.marker.Marker;
-import com.android.sched.marker.ValidOn;
+import com.android.sched.item.Name;
+import com.android.sched.item.Tag;
import java.io.BufferedReader;
import java.io.File;
@@ -30,7 +29,6 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
-import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -42,15 +40,17 @@ import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
- * A marker which contains compiler state of a compilation. It could be reused later during
- * another compilation.
+ * A compiler state of a compilation. It could be reused later during another compilation.
*/
-@Description("Compiler state of a compilation. It could be reused later during " +
- "another compilation")
-@ValidOn(JSession.class)
-public final class CompilerStateMarker implements Marker, Serializable {
-
- private static final long serialVersionUID = 1L;
+public final class CompilerState {
+
+ /**
+ * This tag means that {@link CompilerState} is filled and could be write for later compilation.
+ */
+ @Description("Compiler state is filled and ready to be written")
+ @Name("CompilerState.Filled")
+ public static final class Filled implements Tag{
+ }
@Nonnull
private Map<String, Set<String>> codeFileToUsedFiles = new HashMap<String, Set<String>>();
@@ -64,11 +64,6 @@ public final class CompilerStateMarker implements Marker, Serializable {
@Nonnull
private Map<String, Set<String>> javaFileToJackFile = new HashMap<String, Set<String>>();
- @Override
- public Marker cloneIfNeeded() {
- return this;
- }
-
public void updateCompilerState(@Nonnull Set<String> filesToRecompile) {
for (String javaFileToRecompile : filesToRecompile) {
javaFileToJackFile.remove(javaFileToRecompile);
@@ -131,8 +126,8 @@ public final class CompilerStateMarker implements Marker, Serializable {
}
@Nonnull
- public static CompilerStateMarker read(@Nonnull File compilerStateFile) {
- CompilerStateMarker csm = new CompilerStateMarker();
+ public static CompilerState read(@Nonnull File compilerStateFile) {
+ CompilerState csm = new CompilerState();
BufferedReader br = null;
try {
diff --git a/jack/src/com/android/jack/experimental/incremental/CompilerStateWriter.java b/jack/src/com/android/jack/experimental/incremental/CompilerStateWriter.java
index 6de74dc..253caa6 100644
--- a/jack/src/com/android/jack/experimental/incremental/CompilerStateWriter.java
+++ b/jack/src/com/android/jack/experimental/incremental/CompilerStateWriter.java
@@ -22,13 +22,7 @@ import com.android.sched.item.Name;
import com.android.sched.schedulable.Constraint;
import com.android.sched.schedulable.Produce;
import com.android.sched.schedulable.RunnableSchedulable;
-import com.android.sched.util.codec.PathCodec;
-import com.android.sched.util.config.HasKeyId;
import com.android.sched.util.config.ThreadConfig;
-import com.android.sched.util.config.id.BooleanPropertyId;
-import com.android.sched.util.config.id.PropertyId;
-
-import java.io.File;
import javax.annotation.Nonnull;
@@ -37,24 +31,13 @@ import javax.annotation.Nonnull;
*/
@Description("Write compiler state to the disk")
@Name("CompilerStateWriter")
-@Constraint(need = CompilerStateMarker.class)
+@Constraint(need = CompilerState.Filled.class)
@Produce(CompilerStateProduct.class)
-@HasKeyId
public class CompilerStateWriter implements RunnableSchedulable<JSession>{
- public static final BooleanPropertyId GENERATE_COMPILER_STATE = BooleanPropertyId.create(
- "jack.experimental.compilerstate.generate", "Generate compiler state").addDefaultValue(
- Boolean.FALSE);
-
- @Nonnull
- public static final PropertyId<File> COMPILER_STATE_OUTPUT = PropertyId.create(
- "jack.experimental.compilerstate.output", "Compiler state output file", new PathCodec())
- .requiredIf(GENERATE_COMPILER_STATE.getValue().isTrue());
-
@Override
public void run(@Nonnull JSession program) {
- CompilerStateMarker csm = program.getMarker(CompilerStateMarker.class);
- assert csm != null;
- csm.write(ThreadConfig.get(CompilerStateWriter.COMPILER_STATE_OUTPUT));
+ JackIncremental.getCompilerState().write(
+ ThreadConfig.get(JackIncremental.COMPILER_STATE_OUTPUT));
}
} \ No newline at end of file
diff --git a/jack/src/com/android/jack/experimental/incremental/JackIncremental.java b/jack/src/com/android/jack/experimental/incremental/JackIncremental.java
new file mode 100644
index 0000000..bd1b0c5
--- /dev/null
+++ b/jack/src/com/android/jack/experimental/incremental/JackIncremental.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.CommandLine;
+import com.android.jack.ExitStatus;
+import com.android.jack.IllegalOptionsException;
+import com.android.jack.Jack;
+import com.android.jack.JackIOException;
+import com.android.jack.JackUserException;
+import com.android.jack.NothingToDoException;
+import com.android.jack.Options;
+import com.android.jack.dx.io.DexBuffer;
+import com.android.jack.dx.merge.CollisionPolicy;
+import com.android.jack.dx.merge.DexMerger;
+import com.android.jack.frontend.FrontendCompilationException;
+import com.android.jack.util.TextUtils;
+import com.android.sched.util.RunnableHooks;
+import com.android.sched.util.UnrecoverableException;
+import com.android.sched.util.codec.PathCodec;
+import com.android.sched.util.config.ChainedException;
+import com.android.sched.util.config.ConfigurationException;
+import com.android.sched.util.config.HasKeyId;
+import com.android.sched.util.config.ThreadConfig;
+import com.android.sched.util.config.id.BooleanPropertyId;
+import com.android.sched.util.config.id.PropertyId;
+import com.android.sched.util.log.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Executable class to run the jack compiler with incremental support.
+ */
+@HasKeyId
+public class JackIncremental extends CommandLine {
+
+ public static final BooleanPropertyId GENERATE_COMPILER_STATE = BooleanPropertyId.create(
+ "jack.experimental.compilerstate.generate", "Generate compiler state").addDefaultValue(
+ Boolean.FALSE);
+
+ @Nonnull
+ public static final PropertyId<File> COMPILER_STATE_OUTPUT = PropertyId.create(
+ "jack.experimental.compilerstate.output", "Compiler state output file", new PathCodec())
+ .requiredIf(GENERATE_COMPILER_STATE.getValue().isTrue());
+
+ @CheckForNull
+ private static CompilerState compilerState = null;
+
+ @Nonnull
+ private static final String INCREMENTAL_DEX_OUTPUT = "partialcompilation.dex";
+
+ @Nonnull
+ private static final Logger logger = LoggerFactory.getLogger();
+
+ protected static void runJackAndExitOnError(@Nonnull Options options) {
+ try {
+ run(options);
+ } catch (NothingToDoException e1) {
+ // End normally since there is nothing to do
+ } catch (ConfigurationException exceptions) {
+ System.err.println(exceptions.getNextExceptionCount() + " error"
+ + (exceptions.getNextExceptionCount() > 1 ? "s" : "")
+ + " during configuration. Try --help-properties for help.");
+ for (ChainedException exception : exceptions) {
+ System.err.println(" " + exception.getMessage());
+ }
+
+ System.exit(ExitStatus.FAILURE_USAGE);
+ } catch (IllegalOptionsException e) {
+ System.err.println(e.getMessage());
+ System.err.println("Try --help for help.");
+
+ System.exit(ExitStatus.FAILURE_USAGE);
+ } catch (FrontendCompilationException e) {
+ // Cause exception has already been logged
+ System.exit(ExitStatus.FAILURE_COMPILATION);
+ } catch (JackUserException e) {
+ System.err.println(e.getMessage());
+ logger.log(Level.FINE, "Jack user exception:", e);
+ System.exit(ExitStatus.FAILURE_COMPILATION);
+ } catch (OutOfMemoryError e) {
+ printExceptionMessage(e, "Out of memory error.");
+ System.err.println("Try increasing heap size with java option '-Xmx<size>'");
+ System.err.println(INTERRUPTED_COMPILATION_WARNING);
+ logger.log(Level.FINE, "Out of memory error:", e);
+ System.exit(ExitStatus.FAILURE_VM);
+ } catch (StackOverflowError e) {
+ printExceptionMessage(e, "Stack overflow error.");
+ System.err.println("Try increasing stack size with java option '-Xss<size>'");
+ System.err.println(INTERRUPTED_COMPILATION_WARNING);
+ logger.log(Level.FINE, "Stack overflow error:", e);
+ System.exit(ExitStatus.FAILURE_VM);
+ } catch (VirtualMachineError e) {
+ printExceptionMessage(e, "Virtual machine error: " + e.getClass() + ".");
+ System.err.println(INTERRUPTED_COMPILATION_WARNING);
+ logger.log(Level.FINE, "Virtual machine error:", e);
+ System.exit(ExitStatus.FAILURE_VM);
+ } catch (UnrecoverableException e) {
+ System.err.println("Unrecoverable error: " + e.getMessage());
+ System.err.println(INTERRUPTED_COMPILATION_WARNING);
+ logger.log(Level.FINE, "Unrecoverable exception:", e);
+ System.exit(ExitStatus.FAILURE_UNRECOVERABLE);
+ } catch (Throwable e) {
+ System.err.println("Internal compiler error.");
+ System.err.println(INTERRUPTED_COMPILATION_WARNING);
+ logger.log(Level.SEVERE, "Internal compiler error:", e);
+
+ System.exit(ExitStatus.FAILURE_INTERNAL);
+ }
+ }
+
+ public static void run(@Nonnull Options options) throws ConfigurationException,
+ IllegalOptionsException, NothingToDoException, IOException {
+
+ RunnableHooks hooks = new RunnableHooks();
+ List<String> ecjArgsSave = new ArrayList<String>(options.getEcjArguments());
+ options.checkValidity(hooks);
+ if (!ecjArgsSave.isEmpty()) {
+ options.setEcjArguments(ecjArgsSave);
+ }
+ ThreadConfig.setConfig(options.getConfig());
+
+ if (isIncrementalCompilation(options)) {
+ logger.log(Level.INFO, "Incremental compilation");
+
+ List<String> javaFilesNames = getJavaFilesSpecifiedOnCommandLine(options);
+
+ compilerState = CompilerState.read(ThreadConfig.get(JackIncremental.COMPILER_STATE_OUTPUT));
+ Map<String, Set<String>> fileDependencies = compilerState.computeDependencies();
+ printDependencyStat(fileDependencies);
+ logger.log(Level.FINE, "Compiler state {0}", new Object[] {compilerState});
+ logger.log(Level.FINE, "File dependencies {0}",
+ new Object[] {dependenciesToString(fileDependencies)});
+
+ Set<String> filesToRecompile = getFilesToRecompile(fileDependencies, options, javaFilesNames);
+
+ if (!filesToRecompile.isEmpty()) {
+ logger.log(Level.INFO, "{0} Files to recompile {1}",
+ new Object[] {Integer.valueOf(filesToRecompile.size()), filesToRecompile});
+
+ File outDexFile = options.getOutputFile();
+ updateOptions(options, filesToRecompile);
+
+ logger.log(Level.INFO, "Update compiler state");
+ getCompilerState().updateCompilerState(filesToRecompile);
+
+ logger.log(Level.INFO, "Generate {0}", new Object[] {options.getOutputFile()});
+ logger.log(Level.INFO, "Ecj options {0}", new Object[] {options.getEcjArguments()});
+ Jack.run(options);
+
+ logger.log(Level.INFO, "Merge {0} with {1}",
+ new Object[] {outDexFile, options.getOutputFile()});
+ mergeDexFiles(options, outDexFile);
+ } else {
+ logger.log(Level.INFO, "No files to recompile");
+ }
+ } else {
+ compilerState = new CompilerState();
+ Jack.run(options);
+ }
+ }
+
+ @Nonnull
+ public static CompilerState getCompilerState() {
+ if (compilerState == null) {
+ throw new JackUserException(
+ "Incremental support must be used with experimental Main class from "
+ + "com.android.jack.experimental.incremental");
+ }
+ return compilerState;
+ }
+
+ private static String dependenciesToString(@Nonnull Map<String, Set<String>> fileDependencies) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(TextUtils.LINE_SEPARATOR);
+ builder.append("*Dependencies list*");
+ builder.append(TextUtils.LINE_SEPARATOR);
+
+ for (Map.Entry<String, Set<String>> entry : fileDependencies.entrySet()) {
+ builder.append(entry.getKey());
+ builder.append("->");
+ builder.append(entry.getValue());
+ builder.append(TextUtils.LINE_SEPARATOR);
+ }
+
+ return (builder.toString());
+ }
+
+ private static void printDependencyStat(@Nonnull Map<String, Set<String>> fileDependencies) {
+ int dependencyNumber = 0;
+ int maxDependencyNumber = -1;
+ int minDependencyNumber = -1;
+
+ for (Set<String> dependency : fileDependencies.values()) {
+ int currentDepSize = dependency.size();
+ dependencyNumber += currentDepSize;
+ if (minDependencyNumber == -1 || minDependencyNumber > currentDepSize) {
+ minDependencyNumber = currentDepSize;
+ }
+ if (maxDependencyNumber == -1 || maxDependencyNumber < currentDepSize) {
+ maxDependencyNumber = currentDepSize;
+ }
+ }
+
+ logger.log(
+ Level.INFO,
+ "There are {0} dependencies, with {1} files per dependency in average",
+ new Object[] {Integer.valueOf(fileDependencies.size()),
+ Double.valueOf((double) dependencyNumber / (double) fileDependencies.size())});
+ logger.log(Level.INFO, "Dependencies are at minimun {0} and at maximun {1}", new Object[] {
+ Integer.valueOf(minDependencyNumber), Integer.valueOf(maxDependencyNumber)});
+ }
+
+ private static void mergeDexFiles(@Nonnull Options options, @Nonnull File outDexFile)
+ throws IOException {
+ DexBuffer merged =
+ new DexMerger(new DexBuffer(options.getOutputFile()), new DexBuffer(outDexFile),
+ CollisionPolicy.KEEP_FIRST).merge();
+ merged.writeTo(outDexFile);
+ }
+
+ private static void updateOptions(@Nonnull Options options,
+ @Nonnull Set<String> javaFilesToRecompile) {
+ List<String> newEcjArguments = new ArrayList<String>();
+
+ for (String ecjOptions : options.getEcjArguments()) {
+ if (!ecjOptions.startsWith("@") && !ecjOptions.endsWith(".java")) {
+ newEcjArguments.add(ecjOptions);
+ }
+ }
+
+ for (String fileToRecompile : javaFilesToRecompile) {
+ newEcjArguments.add(fileToRecompile);
+ }
+
+ // Compile
+ options.setClasspath(options.getClasspathAsString() + File.pathSeparator
+ + ThreadConfig.get(Options.JACK_FILE_OUTPUT_DIR).getFile().getAbsolutePath());
+ if (!newEcjArguments.isEmpty()) {
+ options.setEcjArguments(newEcjArguments);
+ }
+ options
+ .setOutputFile(new File(options.getOutputFile().getParentFile(), INCREMENTAL_DEX_OUTPUT));
+ }
+
+ @Nonnull
+ private static Set<String> getFilesToRecompile(@Nonnull Map<String, Set<String>> fileDependencies,
+ @Nonnull Options options, @Nonnull List<String> javaFileNames) {
+ Set<String> filesToRecompile = new HashSet<String>();
+
+ filesToRecompile.addAll(getModifiedFiles(fileDependencies, options, javaFileNames));
+ filesToRecompile.addAll(getAddedFiles(fileDependencies, javaFileNames));
+ filesToRecompile.addAll(getDeletedFiles(fileDependencies, javaFileNames));
+
+ return filesToRecompile;
+ }
+
+ @Nonnull
+ private static Set<String> getDeletedFiles(@Nonnull Map<String, Set<String>> fileDependencies,
+ @Nonnull List<String> javaFileNames) {
+ Set<String> deletedFiles = new HashSet<String>();
+ Iterator<String> previousFilesIt = getCompilerState().getJavaFilename().iterator();
+
+ while (previousFilesIt.hasNext()) {
+ String previousFileName = previousFilesIt.next();
+ if (!javaFileNames.contains(previousFileName)) {
+ logger.log(Level.INFO, "{0} was deleted", new Object[] {previousFileName});
+ deletedFiles.addAll(fileDependencies.get(previousFileName));
+ deleteJackFilesFromJavaFiles(previousFileName);
+ previousFilesIt.remove();
+ }
+ }
+
+ return deletedFiles;
+ }
+
+ private static void deleteJackFilesFromJavaFiles(@Nonnull String javaFileName) {
+ for (String jackFileToRemove :
+ getCompilerState().getJacksFileNameFromJavaFileName(javaFileName)) {
+ File jackFile = new File(jackFileToRemove);
+ if (jackFile.exists() && !jackFile.delete()) {
+ throw new JackIOException("Failed to delete file " + jackFileToRemove);
+ }
+ }
+ }
+
+ @Nonnull
+ private static Set<String> getAddedFiles(@Nonnull Map<String, Set<String>> fileDependencies,
+ @Nonnull List<String> javaFileNames) {
+ Set<String> addedFiles = new HashSet<String>();
+ Set<String> previousFiles = fileDependencies.keySet();
+
+ for (String javaFileName : javaFileNames) {
+ if (!previousFiles.contains(javaFileName)) {
+ logger.log(Level.INFO, "{0} was added", new Object[] {javaFileName});
+ addedFiles.add(javaFileName);
+ }
+ }
+
+ return addedFiles;
+ }
+
+ @Nonnull
+ private static Set<String> getModifiedFiles(@Nonnull Map<String, Set<String>> fileDependencies,
+ @Nonnull Options options, @Nonnull List<String> javaFileNames) {
+ Set<String> modifiedFiles = new HashSet<String>();
+
+ for (Map.Entry<String, Set<String>> previousFileEntry : fileDependencies.entrySet()) {
+ String javaFileName = previousFileEntry.getKey();
+ File javaFile = new File(javaFileName);
+ if (javaFileNames.contains(javaFileName) &&
+ javaFile.lastModified() > options.getOutputFile().lastModified()) {
+ logger.log(Level.INFO, "{0} was modified", new Object[] {javaFileName});
+ modifiedFiles.add(javaFileName);
+ modifiedFiles.addAll(previousFileEntry.getValue());
+ deleteJackFilesFromJavaFiles(javaFileName);
+ }
+ }
+
+ return modifiedFiles;
+ }
+
+ @Nonnull
+ private static List<String> getJavaFilesSpecifiedOnCommandLine(@Nonnull Options options)
+ throws NothingToDoException, IllegalOptionsException {
+ assert !options.getEcjArguments().isEmpty();
+
+ org.eclipse.jdt.internal.compiler.batch.Main compiler =
+ new org.eclipse.jdt.internal.compiler.batch.Main(new PrintWriter(System.out),
+ new PrintWriter(System.err), false /* exit */, null /* options */
+ , null /* compilationProgress */
+ );
+
+ try {
+ compiler.configure(options.getEcjArguments().toArray(
+ new String[options.getEcjArguments().size()]));
+ if (!compiler.proceed) {
+ throw new NothingToDoException();
+ }
+ } catch (IllegalArgumentException e) {
+ throw new IllegalOptionsException(e.getMessage(), e);
+ }
+
+ ArrayList<String> javaFiles = new ArrayList<String>();
+ for (String fileName : compiler.filenames) {
+ File file = new File(fileName);
+ assert file.exists();
+ try {
+ fileName = file.getCanonicalPath();
+ } catch (IOException e) {
+ // if we got exception keep the specified name
+ }
+ javaFiles.add(fileName);
+ }
+
+ return javaFiles;
+ }
+
+ private static boolean isIncrementalCompilation(@Nonnull Options options) {
+ if (!options.getEcjArguments().isEmpty()
+ && ThreadConfig.get(Options.GENERATE_DEX_FILE).booleanValue()
+ && ThreadConfig.get(JackIncremental.GENERATE_COMPILER_STATE).booleanValue()
+ && ThreadConfig.get(JackIncremental.COMPILER_STATE_OUTPUT).exists()) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/jack/src/com/android/jack/experimental/incremental/Main.java b/jack/src/com/android/jack/experimental/incremental/Main.java
new file mode 100644
index 0000000..b6cb37e
--- /dev/null
+++ b/jack/src/com/android/jack/experimental/incremental/Main.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.ExitStatus;
+import com.android.jack.Options;
+
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Command line to run the jack incremental compiler.
+ */
+public abstract class Main extends JackIncremental {
+ @Nonnull
+ private static final String[] EXTRA_ARGS = new String[]{"-noExit"};
+
+ /**
+ * Runs the jack compiler from the command line
+ *
+ * @param args supported arguments are the same as for the ecj batch compiler.
+ */
+ public static void main(@Nonnull String[] args) {
+ if (args.length == 0) {
+ printVersion();
+ System.err.println("Try --help for help.");
+ System.exit(ExitStatus.SUCCESS);
+ }
+
+ try {
+ Options options = parseCommandLine(args);
+
+ if (options.askForHelp()) {
+ printUsage(options);
+ System.exit(ExitStatus.SUCCESS);
+ }
+
+ if (options.askForPropertiesHelp()) {
+ printHelpProperties(options);
+ System.exit(ExitStatus.SUCCESS);
+ }
+
+ if (options.askForEcjHelp()) {
+ // ECJ help was already printed by Options.checkValidity()
+ System.exit(ExitStatus.SUCCESS);
+ }
+
+ if (options.askForVersion()) {
+ printVersion();
+ System.exit(ExitStatus.SUCCESS);
+ }
+
+ // Compile
+ runJackAndExitOnError(options);
+
+ System.exit(ExitStatus.SUCCESS);
+ } catch (CmdLineException e) {
+ System.err.println(e.getMessage());
+ CmdLineParser parser = e.getParser();
+ if (parser != null) {
+ parser.printUsage(System.err);
+ } else {
+ System.err.println("Try --help for help.");
+ }
+
+ System.exit(ExitStatus.FAILURE_USAGE);
+ } catch (IOException e) {
+ System.err.println(e.getMessage());
+
+ System.exit(ExitStatus.FAILURE_INTERNAL);
+ }
+ }
+
+ @Nonnull
+ public static Options parseCommandLine(@Nonnull String[] args)
+ throws CmdLineException {
+ Options options = new Options();
+
+ CmdLineParser parser = new CmdLineParser(options);
+ parser.setUsageWidth(100);
+
+ parser.parseArgument(args);
+ parser.stopOptionParsing();
+ if (!options.getEcjArguments().isEmpty()) {
+ parser.parseArgument(EXTRA_ARGS);
+ }
+
+ return options;
+ }
+}
diff --git a/jack/src/com/android/jack/experimental/incremental/UsageFinder.java b/jack/src/com/android/jack/experimental/incremental/UsageFinder.java
index 87c4ee7..849c784 100644
--- a/jack/src/com/android/jack/experimental/incremental/UsageFinder.java
+++ b/jack/src/com/android/jack/experimental/incremental/UsageFinder.java
@@ -29,7 +29,6 @@ import com.android.jack.ir.ast.JInterface;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JNode;
-import com.android.jack.ir.ast.JSession;
import com.android.jack.ir.ast.JType;
import com.android.jack.ir.ast.JVisitor;
import com.android.jack.ir.ast.marker.ThrownExceptionMarker;
@@ -46,19 +45,19 @@ import javax.annotation.Nonnull;
*/
@Description("Find all usages between java files")
@Name("UsageFinder")
-@Transform(add = CompilerStateMarker.class)
+@Transform(add = CompilerState.Filled.class)
@Synchronized
public class UsageFinder implements RunnableSchedulable<JDefinedClassOrInterface> {
private static class Visitor extends JVisitor {
@Nonnull
- private final CompilerStateMarker compilerState;
+ private final CompilerState compilerState;
@Nonnull
private final String currentFileName;
- public Visitor(@Nonnull JType currentType, @Nonnull CompilerStateMarker compilerState) {
+ public Visitor(@Nonnull JType currentType, @Nonnull CompilerState compilerState) {
this.compilerState = compilerState;
assert currentType.getSourceInfo() != SourceOrigin.UNKNOWN;
currentFileName = currentType.getSourceInfo().getFileName();
@@ -151,15 +150,7 @@ public class UsageFinder implements RunnableSchedulable<JDefinedClassOrInterface
return;
}
- JSession program = declaredType.getParent(JSession.class);
- assert program != null;
- CompilerStateMarker csm = program.getMarker(CompilerStateMarker.class);
- if (csm == null) {
- csm = new CompilerStateMarker();
- program.addMarker(csm);
- }
-
- Visitor v = new Visitor(declaredType, csm);
+ Visitor v = new Visitor(declaredType, JackIncremental.getCompilerState());
v.accept(declaredType);
}
}
diff --git a/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java b/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java
index aff021c..28a6832 100644
--- a/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java
+++ b/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java
@@ -43,8 +43,6 @@ public class MethodIdDuplicateRemover extends JVisitor {
Collection<JMethod> methods = id.getMethods();
if (!methods.isEmpty()) {
JMethod method = methods.iterator().next();
- assert method.isExternal() || method.getMethodId() ==
- receiverType.getMethodId(id.getName(), id.getParamTypes(), id.getKind());
return method.getMethodId();
} else {
return receiverType.getOrCreateMethodId(id.getName(), id.getParamTypes(), id.getKind());
diff --git a/jack/src/com/android/jack/ir/impl/GwtAstBuilder.java b/jack/src/com/android/jack/ir/impl/GwtAstBuilder.java
index 62862d7..b3c0e37 100644
--- a/jack/src/com/android/jack/ir/impl/GwtAstBuilder.java
+++ b/jack/src/com/android/jack/ir/impl/GwtAstBuilder.java
@@ -16,8 +16,7 @@
package com.android.jack.ir.impl;
import com.android.jack.Jack;
-import com.android.jack.experimental.incremental.CompilerStateMarker;
-import com.android.jack.experimental.incremental.CompilerStateWriter;
+import com.android.jack.experimental.incremental.JackIncremental;
import com.android.jack.frontend.ParentSetter;
import com.android.jack.ir.InternalCompilerException;
import com.android.jack.ir.SourceInfo;
@@ -2787,16 +2786,12 @@ public class GwtAstBuilder {
SourceInfo info = makeSourceInfo(x);
if (x.constant != Constant.NotAConstant) {
- if (ThreadConfig.get(CompilerStateWriter.GENERATE_COMPILER_STATE).booleanValue()) {
- CompilerStateMarker csm = session.getMarker(CompilerStateMarker.class);
- if (csm == null) {
- csm = new CompilerStateMarker();
- session.addMarker(csm);
- }
+ if (ThreadConfig.get(JackIncremental.GENERATE_COMPILER_STATE).booleanValue()) {
if (x.binding instanceof FieldBinding) {
FieldBinding b = ((FieldBinding) x.binding).original();
JField field = getTypeMap().get(b);
- csm.addCstUsage(info.getFileName(), field.getSourceInfo().getFileName());
+ JackIncremental.getCompilerState().addCstUsage(info.getFileName(),
+ field.getSourceInfo().getFileName());
}
}
diff --git a/jack/tests/com/android/jack/AllTests.java b/jack/tests/com/android/jack/AllTests.java
index ca28cdb..b1efd1d 100644
--- a/jack/tests/com/android/jack/AllTests.java
+++ b/jack/tests/com/android/jack/AllTests.java
@@ -18,6 +18,7 @@ package com.android.jack;
import com.android.jack.analysis.dfa.reachingdefs.ReachingDefsTest;
import com.android.jack.backend.dex.rop.RopRegisterManagerTest;
+import com.android.jack.experimental.incremental.DependencyAllTests;
import com.android.jack.ir.ast.MarkerCollectorTest;
import com.android.jack.jayce.v0002.io.EscapeStringTest;
import com.android.jack.optimizations.ExpressionSimplifierTest;
@@ -57,6 +58,7 @@ import org.junit.runners.Suite.SuiteClasses;
ConcatTest.class,
ConditionalTest.class,
ConstantTest.class,
+ DependencyAllTests.class,
DxTest.class,
EnumTest.class,
ErrorTest.class,
diff --git a/jack/tests/com/android/jack/TestTools.java b/jack/tests/com/android/jack/TestTools.java
index 6769e31..6f37c04 100644
--- a/jack/tests/com/android/jack/TestTools.java
+++ b/jack/tests/com/android/jack/TestTools.java
@@ -387,7 +387,7 @@ public class TestTools {
}
@Nonnull
- private static List<String> buildEcjArgs(
+ protected static List<String> buildEcjArgs(
boolean useOnlyCompatible) {
List<String> ecjArgs = new ArrayList<String>();
ecjArgs.add("-nowarn");
@@ -399,7 +399,7 @@ public class TestTools {
return ecjArgs;
}
- private static void addFile(@Nonnull File fileOrSourceList, @Nonnull List<String> args) {
+ protected static void addFile(@Nonnull File fileOrSourceList, @Nonnull List<String> args) {
if (fileOrSourceList instanceof Sourcelist) {
args.add("@" + fileOrSourceList.getAbsolutePath());
} else {
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest001.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest001.java
new file mode 100644
index 0000000..99ca423
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest001.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+import com.android.jack.frontend.FrontendCompilationException;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest001 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Checks that compilation failed due to deletion of B.
+ * Compilation must recompile A and failed since B does not longer exists.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A extends B {} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B {} \n");
+
+ ite.incrementalBuildFromFolder();
+
+ ite.deleteJavaFile("jack.incremental", "B.java");
+
+ try {
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Error is ok
+ }
+ }
+
+ /**
+ * Checks that compilation failed due to deletion of I.
+ * Compilation must recompile A and failed since I does not longer exists.
+ */
+ @Test
+ public void testDependency002() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_002"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A implements I {} \n");
+
+ ite.addJavaFile("jack.incremental", "I.java",
+ "package jack.incremental; \n"+
+ "public interface I {} \n");
+
+ ite.incrementalBuildFromFolder();
+
+ ite.deleteJavaFile("jack.incremental", "I.java");
+
+ try {
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Error is ok
+ }
+ }
+
+ /**
+ * Check that only type A is recompiled since it was modified (extends of B removed).
+ */
+ @Test
+ public void testDependency003() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_003"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A extends B {} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B {} \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A {} \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(1, fqnOfRebuiltTypes.size());
+ Assert.assertEquals("jack.incremental.A", fqnOfRebuiltTypes.get(0));
+ }
+
+ /**
+ * Check that no types are recompiled between two incremental build without modification.
+ */
+ @Test
+ public void testDependency004() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_004"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A extends B {} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B {} \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(0, fqnOfRebuiltTypes.size());
+ }
+
+ /**
+ * Check that B and C are recompiled since C is modified.
+ * B must also be recompiled since it used C.
+ */
+ @Test
+ public void testDependency005() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_005"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A {} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public void m(C c) {} } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C {} \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public void m() {} } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.C"));
+ }
+
+ /**
+ * Check that B and C are recompiled since C is modified.
+ * B must also be recompiled since it used C.
+ */
+ @Test
+ public void testDependency006() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_006"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A {} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public void m() {C c = new C();} } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C {} \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public void m() {} } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.C"));
+ }
+
+ /**
+ * Check that A, B and C are recompiled since C is modified.
+ * A, B must be recompiled since they are sub-type of C.
+ */
+ @Test
+ public void testDependency007() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_007"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A extends B {} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B extends C {} \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C {} \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public void m() {} } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(3, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.C"));
+ }
+
+ /**
+ * Check that compilation of A failed due to a modification into B.
+ */
+ @Test
+ public void testDependency008() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_008"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public void test() {new B().m();}} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public void m(){} } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public void m(int i){} } \n");
+
+ try {
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Error is ok
+ }
+ }
+
+ /**
+ * Check that A and B are recompiled.
+ * A must be recompiled since it used a field of B.
+ */
+ @Test
+ public void testDependency009() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_009"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public int getCst() {return B.i;} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static int i; } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static int i; public static int j; } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ }
+
+ /**
+ * Check that A and B are recompiled.
+ * A must be recompiled since it used a method of B.
+ */
+ @Test
+ public void testDependency010() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_010"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public void callTest() {B.test();} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static void test(){} } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static void test() {} public static void test1() {} } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ }
+
+ /**
+ * Check that A is recompiled and that compilation failed since B does not longer exists.
+ */
+ @Test
+ public void testDependency011() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_011"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public boolean callTest(Object o) { return o instanceof B;} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B {} \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.deleteJavaFile("jack.incremental", "B.java");
+
+ try {
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Error is ok
+ }
+ }
+
+ /**
+ * Check that A and B are recompiled.
+ * A must be recompiled since it used a constant from B.
+ */
+ @Test
+ public void testDependency012() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_012"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public int getCst() {return B.i;} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final int i = 10; } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final int i = 12; } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ }
+
+ /**
+ * Check that A and B are recompiled.
+ * A must be recompiled since it used a constant from B.
+ */
+ @Test
+ public void testDependency013() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_013"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public int getCst() {return B.i1 + B.i2;} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final int i1 = 10; public static final int i2 = 20; } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final int i1 = 20; public static final int i2 = 20; } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ }
+
+ /**
+ * Check that A and B are recompiled.
+ * A must be recompiled since it used a constant from B.
+ */
+ @Test
+ public void testDependency014() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_014"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public int getCst() {return B.b + C.c;} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final int b = 10; } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public static final int c = 10; } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public static final int c = 20; } \n");
+
+ ite.incrementalBuildFromFolder();
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.C"));
+
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final int b = 20; } \n");
+
+ ite.incrementalBuildFromFolder();
+ fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ }
+
+ /**
+ * Check that second and third compilation failed since I was modified but not B for the second
+ * compilation and not A for the third. Check that fourth compilation rebuilt A, B, I since I and
+ * B was modified.
+ */
+ @Test
+ public void testDependency015() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_015"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A extends B { @Override public void m() {} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B implements I { @Override public void m() {} } \n");
+
+ ite.addJavaFile("jack.incremental", "I.java",
+ "package jack.incremental; \n"+
+ "public interface I { public void m(); } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "I.java",
+ "package jack.incremental; \n"+
+ "public interface I { public void m(int i); } \n");
+
+ try {
+ ite.startErrRedirection();
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Error is ok
+ Assert.assertTrue(ite.endErrorRedirection().contains("2 problems (2 errors)"));
+ }
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B implements I { @Override public void m(int i) {} } \n");
+
+ try {
+ ite.startErrRedirection();
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Error is ok
+ Assert.assertTrue(ite.endErrorRedirection().contains("1 problem (1 error)"));
+ }
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A extends B { @Override public void m(int i) {} } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(3, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.I"));
+ }
+}
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest002.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest002.java
new file mode 100644
index 0000000..2fca23d
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest002.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest002 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that adding a more precise method is well detected.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B extends A { public void call(E e) { System.out.println(\"E\"); } } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public static void main(String[] args) {new B().call(new D()); } } \n");
+
+ ite.addJavaFile("jack.incremental", "D.java",
+ "package jack.incremental; \n"+
+ "public class D extends E { } \n");
+
+ ite.addJavaFile("jack.incremental", "E.java",
+ "package jack.incremental; \n"+
+ "public class E { } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public void call(D d) { System.out.println(\"D\"); } } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(3, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.C"));
+ }
+}
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest003.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest003.java
new file mode 100644
index 0000000..a18f83f
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest003.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest003 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that file modification implying to transform an interface call to a virtual call is well
+ * detected.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public void test(B b) {b.call1().call2();} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public I call1() {return new C();} } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C implements I { @Override public void call2() { } } \n");
+
+ ite.addJavaFile("jack.incremental", "I.java",
+ "package jack.incremental; \n"+
+ "public interface I { public void call2(); } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public C call1() {return new C();} } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ }
+}
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest004.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest004.java
new file mode 100644
index 0000000..dca5066
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest004.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest004 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that changing a class into an interface is well detected.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public void test(B b) {b.call1();} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public void call1() {} } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public interface B { public void call1(); } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ }
+}
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest005.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest005.java
new file mode 100644
index 0000000..4b88a29
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest005.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest005 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that runtime is correct after incremental compilation due to a constant modification.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public static void main(String[] args) {" +
+ "System.out.print(C.str + B.str);} " +
+ "} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final String str = \"HELLO\"; } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public static final String str = \"STRING:\"; } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final String str = \"INCREMENTAL\"; } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+
+ Assert.assertEquals("STRING:INCREMENTAL", ite.run("jack.incremental.A"));
+ }
+
+
+ /**
+ * Check that runtime is correct after incremental compilation due to a constant modification.
+ */
+ @Test
+ public void testDependency002() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_002"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public static void main(String[] args) {" +
+ "System.out.print(B.str);} " +
+ "} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final String str = \"HELLO\" + C.str; } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public static final String str = \" WORLD\"; } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public static final String str = \" EVERYBODY\"; } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(3, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.C"));
+
+ Assert.assertEquals("HELLO EVERYBODY", ite.run("jack.incremental.A"));
+ }
+
+ /**
+ * Check that runtime is correct after incremental compilation due to a constant modification.
+ */
+ @Test
+ public void testDependency003() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_003"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { " +
+ " public static final String A = \"A\";" +
+ " public static final String AB = \"A\" + B.B;"+
+ " public static void main(String[] args) { System.out.print(B.BA); } } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { \n" +
+ " public static final String B = \"B\"; \n" +
+ " public static final String BA = \"B\" + A.A; }");
+
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ Assert.assertEquals("BA", ite.run("jack.incremental.A"));
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { \n" +
+ " public static final String B = \"B\"; \n" +
+ " public static final String BA = \"B\" + A.AB; }");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+
+ Assert.assertEquals("BAB", ite.run("jack.incremental.A"));
+ }
+
+ /**
+ * Check that runtime is correct after incremental compilation due to a constant modification.
+ */
+ @Test
+ public void testDependency004() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_004"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public static void main(String[] args) {" +
+ "System.out.print(B.str);} " +
+ "} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public static final String str = D.str + C.str; } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public static final String str = \"WORLD\" + E.str; } \n");
+
+ ite.addJavaFile("jack.incremental", "D.java",
+ "package jack.incremental; \n"+
+ "public class D { public static final String str = \"HELLO\" + E.str; } \n");
+
+ ite.addJavaFile("jack.incremental", "E.java",
+ "package jack.incremental; \n"+
+ "public class E { public static final String str = \"/\"; } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { public static final String str = \"EVERYBODY\"; } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(3, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.C"));
+
+ Assert.assertEquals("HELLO/EVERYBODY", ite.run("jack.incremental.A"));
+
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "E.java",
+ "package jack.incremental; \n"+
+ "public class E { public static final String str = \" \"; } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(4, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.D"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.E"));
+
+ Assert.assertEquals("HELLO EVERYBODY", ite.run("jack.incremental.A"));
+ }
+}
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest006.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest006.java
new file mode 100644
index 0000000..9eb42e5
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest006.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+import com.android.jack.frontend.FrontendCompilationException;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest006 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that runtime is correct after class renaming.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public static void main(String[] args) {" +
+ "System.out.print(new B().getString());} " +
+ "} \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public String getString() { return (\"B\"); } } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C { } \n");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.deleteJavaFile("jack.incremental", "B.java");
+ ite.addJavaFile("jack.incremental", "_B.java",
+ "package jack.incremental; \n"+
+ "public class _B { public String getString() { return (\"_B\"); } } \n");
+
+ try {
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Error is ok
+ }
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public static void main(String[] args) {" +
+ "System.out.print(new _B().getString());} " +
+ "} \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental._B"));
+
+ Assert.assertEquals("_B", ite.run("jack.incremental.A"));
+ }
+}
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest007.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest007.java
new file mode 100644
index 0000000..3c81b5a
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest007.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest007 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that runtime is correct after transformation of an interface call to a virtual call.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n"+
+ "public class A { public static void main(String[] args) { new B().call1().call2();} } \n");
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public I call1() {return new C();} } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n"+
+ "public class C implements I { @Override public void call2(){System.out.print(\"C\");}\n"+
+ "} \n");
+
+ ite.addJavaFile("jack.incremental", "I.java",
+ "package jack.incremental; \n"+
+ "public interface I { public void call2(); } \n");
+
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+
+ ite.addJavaFile("jack.incremental", "B.java",
+ "package jack.incremental; \n"+
+ "public class B { public C call1() {return new C();} } \n");
+
+ ite.incrementalBuildFromFolder();
+
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+
+ Assert.assertEquals("C", ite.run("jack.incremental.A"));
+ }
+}
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest008.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest008.java
new file mode 100644
index 0000000..06bec55
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest008.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+import com.android.jack.frontend.FrontendCompilationException;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest008 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that jack files are deleted according to recompiled files.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n" +
+ "public class A { \n" +
+ " public static class B { public void m() {} } \n" +
+ " public static void main(String[] args) {new B().m();} } \n");
+
+ ite.incrementalBuildFromFolder();
+ Assert.assertEquals(2, ite.getJackFiles().size());
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n" +
+ "public class A { \n" +
+ " public static void main(String[] args) {new B().m();} } \n");
+
+ try {
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Error is ok
+ Assert.assertEquals(0, ite.getJackFiles().size());
+ }
+ }
+
+ /**
+ * Check that jack files are deleted according to recompiled files.
+ */
+ @Test
+ public void testDependency002() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_002"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n" +
+ "public class A { } \n" +
+ "class B { } \n");
+
+ ite.incrementalBuildFromFolder();
+ Assert.assertEquals(2, ite.getJackFiles().size());
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n" +
+ "public class A { }\n");
+
+ ite.incrementalBuildFromFolder();
+ Assert.assertEquals(1, ite.getJackFiles().size());
+ }
+
+ /**
+ * Check that jack files are deleted according to recompiled files.
+ */
+ @Test
+ public void testDependency003() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_003"));
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n" +
+ "public class A { } \n" +
+ "class B { } \n");
+
+ ite.addJavaFile("jack.incremental", "C.java",
+ "package jack.incremental; \n" +
+ "public class C { public void test() {new B();} } \n");
+
+ ite.incrementalBuildFromFolder();
+ Assert.assertEquals(3, ite.getJackFiles().size());
+
+ ite.addJavaFile("jack.incremental", "A.java",
+ "package jack.incremental; \n" +
+ "public class A { }\n");
+
+ try {
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Error is ok
+ Assert.assertEquals(1, ite.getJackFiles().size());
+ }
+ }
+}
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest009.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest009.java
new file mode 100644
index 0000000..02ea163
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest009.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest009 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that usages does not change during incremental compilation and that dependencies are
+ * identical.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java", "package jack.incremental; \n"
+ + "public class A extends B { }");
+
+ ite.addJavaFile("jack.incremental", "B.java", "package jack.incremental; \n"
+ + "public class B extends C { }");
+
+ ite.addJavaFile("jack.incremental", "C.java", "package jack.incremental; \n"
+ + "public class C { }");
+
+ ite.incrementalBuildFromFolder();
+
+ CompilerState csm = CompilerState.read(ite.getCompilerStateFile());
+ Map<String, Set<String>> dependencies1 = csm.computeDependencies();
+
+ ite.addJavaFile("jack.incremental", "A.java", "package jack.incremental; \n"
+ + "public class A extends B { public int field;}");
+
+ ite.incrementalBuildFromFolder();
+
+ csm = CompilerState.read(ite.getCompilerStateFile());
+ CompilerState.read(ite.getCompilerStateFile());
+ Map<String, Set<String>> dependencies2 = csm.computeDependencies();
+
+ assert dependencies1.equals(dependencies2);
+ Assert.assertEquals(dependencies1, dependencies2);
+ }
+}
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest010.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest010.java
new file mode 100644
index 0000000..d3b4ce9
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest010.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest010 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that incremental compilation support switch on constant value.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java", "package jack.incremental; \n"
+ + "public class A { "
+ + "public static void main(String[] args) {"
+ + " System.out.print(new A().test(2));"
+ + " }"
+ + " public int test(int value) {"
+ + " switch(value) {"
+ + " case B.val1: return 1;"
+ + " case B.val2: return 2;"
+ + " case B.val3: return 3;"
+ + " }"
+ + " return 0;"
+ + " } }");
+
+ ite.addJavaFile("jack.incremental", "B.java", "package jack.incremental; \n"
+ + "public class B { \n"
+ + " public static final int val1 = 1;"
+ + " public static final int val2 = 2;"
+ + " public static final int val3 = val2 + C.val4;"
+ + "}");
+
+ ite.addJavaFile("jack.incremental", "C.java", "package jack.incremental; \n"
+ + "public class C { \n"
+ + " public static final int val4 = 1;"
+ + "}");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+ Assert.assertEquals("2", ite.run("jack.incremental.A"));
+
+ ite.addJavaFile("jack.incremental", "B.java", "package jack.incremental; \n"
+ + "public class B { \n"
+ + " public static final int val1 =11;"
+ + " public static final int val2 =12;"
+ + " public static final int val3 =13;"
+ + "}");
+
+ ite.incrementalBuildFromFolder();
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertEquals("0", ite.run("jack.incremental.A"));
+
+ ite.snapshotJackFilesModificationDate();
+ ite.addJavaFile("jack.incremental", "C.java", "package jack.incremental; \n"
+ + "public class C { \n"
+ + " public static final int val4 = 0;"
+ + "}");
+
+ ite.incrementalBuildFromFolder();
+ fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(1, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.C"));
+ Assert.assertEquals("0", ite.run("jack.incremental.A"));
+
+ ite.snapshotJackFilesModificationDate();
+ ite.addJavaFile("jack.incremental", "A.java", "package jack.incremental; \n"
+ + "public class A { "
+ + "public static void main(String[] args) {"
+ + " System.out.print(new A().test(12));"
+ + " }"
+ + " public int test(int value) {"
+ + " switch(value) {"
+ + " case B.val1: return 1;"
+ + " case B.val2: return 2;"
+ + " case B.val3: return 3;"
+ + " }"
+ + " return 0;"
+ + " } }");
+
+ ite.incrementalBuildFromFolder();
+ fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(1, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertEquals("2", ite.run("jack.incremental.A"));
+ }
+
+
+ /**
+ * Check that incremental compilation support switch on enum.
+ */
+ @Test
+ public void testDependency002() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_002"));
+
+ ite.addJavaFile("jack.incremental", "A.java", "package jack.incremental; \n"
+ + "import jack.incremental.B;"
+ + "public class A { "
+ + "public static void main(String[] args) {"
+ + " System.out.print(new A().test(B.VAL1));"
+ + " }"
+ + " public int test(B b) {"
+ + " switch(b) {"
+ + " case VAL1: return 1;"
+ + " case VAL2: return C.val4;"
+ + " case VAL3: return 3;"
+ + " }"
+ + " return 0;"
+ + " } }");
+
+ ite.addJavaFile("jack.incremental", "B.java", "package jack.incremental; \n"
+ + "public enum B { \n"
+ + " VAL1,"
+ + " VAL2,"
+ + " VAL3"
+ + "}");
+
+ ite.addJavaFile("jack.incremental", "C.java", "package jack.incremental; \n"
+ + "public class C { \n"
+ + " public static final int val4 =2;"
+ + "}");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+ Assert.assertEquals("1", ite.run("jack.incremental.A"));
+
+ ite.addJavaFile("jack.incremental", "B.java", "package jack.incremental; \n"
+ + "public enum B { \n"
+ + " VAL1,"
+ + " VAL2,"
+ + " VAL3,"
+ + " VAL4"
+ + "}");
+
+ ite.incrementalBuildFromFolder();
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(2, fqnOfRebuiltTypes.size());
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.A"));
+ Assert.assertTrue(fqnOfRebuiltTypes.contains("jack.incremental.B"));
+ Assert.assertEquals("1", ite.run("jack.incremental.A"));
+ }
+
+}
+
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependenciesTest011.java b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest011.java
new file mode 100644
index 0000000..b00d4b8
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependenciesTest011.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Main;
+import com.android.jack.TestTools;
+import com.android.jack.frontend.FrontendCompilationException;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * JUnit test checking dependencies between Java files.
+ */
+public class DependenciesTest011 {
+
+ @BeforeClass
+ public static void setUpClass() {
+ Main.class.getClassLoader().setDefaultAssertionStatus(true);
+ }
+
+ /**
+ * Check that incremental compilation support throws declaration.
+ */
+ @Test
+ public void testDependency001() throws Exception {
+ IncrementalTestingEnvironment ite =
+ new IncrementalTestingEnvironment(TestTools.createTempDir("DependenciesTest_", "_001"));
+
+ ite.addJavaFile("jack.incremental", "A.java", "package jack.incremental; \n"
+ + "public class A extends Exception { "
+ + "}");
+
+ ite.addJavaFile("jack.incremental", "B.java", "package jack.incremental; \n"
+ + "public class B { \n"
+ + " public void m() throws A { }"
+ + "}");
+
+ ite.incrementalBuildFromFolder();
+ ite.snapshotJackFilesModificationDate();
+ Assert.assertEquals(2, ite.getJackFiles().size());
+
+ ite.deleteJavaFile("jack.incremental", "A.java");
+
+ try {
+ ite.incrementalBuildFromFolder();
+ Assert.fail();
+ } catch (FrontendCompilationException e) {
+ // Ok
+ }
+
+ ite.addJavaFile("jack.incremental", "B.java", "package jack.incremental; \n"
+ + "public class B { \n"
+ + " public void m() { }"
+ + "}");
+
+ ite.incrementalBuildFromFolder();
+ List<String> fqnOfRebuiltTypes = ite.getFQNOfRebuiltTypes();
+ Assert.assertEquals(1, fqnOfRebuiltTypes.size());
+ }
+}
+
diff --git a/jack/tests/com/android/jack/experimental/incremental/DependencyAllTests.java b/jack/tests/com/android/jack/experimental/incremental/DependencyAllTests.java
new file mode 100644
index 0000000..7fdc1b2
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/DependencyAllTests.java
@@ -0,0 +1,13 @@
+package com.android.jack.experimental.incremental;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses(value = {DependenciesTest001.class, DependenciesTest002.class,
+ DependenciesTest003.class, DependenciesTest004.class, DependenciesTest005.class,
+ DependenciesTest006.class, DependenciesTest007.class, DependenciesTest008.class,
+ DependenciesTest009.class, DependenciesTest010.class, DependenciesTest011.class})
+public class DependencyAllTests {
+} \ No newline at end of file
diff --git a/jack/tests/com/android/jack/experimental/incremental/IncrementalTestingEnvironment.java b/jack/tests/com/android/jack/experimental/incremental/IncrementalTestingEnvironment.java
new file mode 100644
index 0000000..75e54f5
--- /dev/null
+++ b/jack/tests/com/android/jack/experimental/incremental/IncrementalTestingEnvironment.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.jack.experimental.incremental;
+
+import com.android.jack.Options;
+import com.android.jack.TestTools;
+import com.android.jack.backend.jayce.JayceFileImporter;
+import com.android.jack.util.ExecuteFile;
+import com.android.jack.util.NamingTools;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+public class IncrementalTestingEnvironment extends TestTools {
+
+ @CheckForNull
+ ByteArrayOutputStream baos = null;
+
+ @CheckForNull
+ PrintStream errRedirectStream = null;
+
+ @Nonnull
+ private final File testingFolder;
+
+ @Nonnull
+ private final File sourceFolder;
+
+ @Nonnull
+ private final File dexFile;
+
+ private final Map<String, Long> fileModificationDate = new HashMap<String, Long>();
+
+ @Nonnull
+ private final Set<File> javaFiles = new HashSet<File>();
+
+ public IncrementalTestingEnvironment(@Nonnull File testingFolder) throws IOException {
+ this.testingFolder = testingFolder;
+ this.sourceFolder = new File(testingFolder, "src");
+ if (!this.sourceFolder.mkdirs()) {
+ throw new IOException("Failed to create folder " + this.sourceFolder.getAbsolutePath());
+ }
+ dexFile = new File(testingFolder, "result.dex");
+ }
+
+ public void addJavaFile(@Nonnull String packageName, @Nonnull String fileName,
+ @Nonnull String fileContent) throws IOException {
+ File packageFolder = new File(sourceFolder, packageName.replace('.', File.separatorChar));
+ if (!packageFolder.exists() && !packageFolder.mkdirs()) {
+ throw new IOException("Failed to create folder " + packageFolder.getAbsolutePath());
+ }
+ File javaFile = new File(packageFolder, fileName);
+ if (javaFile.exists() && !javaFile.delete()) {
+ throw new IOException("Failed to delete file " + javaFile.getAbsolutePath());
+ }
+ if (!javaFile.createNewFile()) {
+ throw new IOException("Failed to create file " + javaFile.getAbsolutePath());
+ }
+ javaFiles.add(javaFile);
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(javaFile);
+ fos.write(fileContent.getBytes());
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ }
+
+ public void deleteJavaFile(@Nonnull String packageName, @Nonnull String fileName)
+ throws IOException {
+ File packageFolder = new File(sourceFolder, NamingTools.getBinaryName(packageName));
+ File javaFile = new File(packageFolder, fileName);
+ if (!javaFile.delete()) {
+ throw new IOException("Failed to delete file " + javaFile.getAbsolutePath());
+ }
+ javaFiles.remove(javaFile);
+ }
+
+ @Nonnull
+ public File getCompilerStateFile() {
+ return new File(testingFolder, "compilerState.ser");
+ }
+
+ public void incrementalBuildFromFolder() throws Exception {
+ Options options = TestTools.buildCommandLineArgs(testingFolder);
+ options.addProperty(Options.GENERATE_JACK_FILE.getName(), "true");
+ options.addProperty(Options.JACK_OUTPUT_CONTAINER_TYPE.getName(), "dir");
+ options.addProperty(Options.JACK_FILE_OUTPUT_DIR.getName(), new File(testingFolder,
+ "jackIncrementalOutput").getAbsolutePath());
+ options.addProperty(JackIncremental.GENERATE_COMPILER_STATE.getName(), "true");
+ options.addProperty(JackIncremental.COMPILER_STATE_OUTPUT.getName(),
+ getCompilerStateFile().getAbsolutePath());
+
+ compileSourceToDex(options, sourceFolder, TestTools
+ .getClasspathAsString(TestTools.getDefaultBootclasspath()), dexFile);
+
+ Thread.sleep(1000);
+ }
+
+ @Nonnull
+ public String run(@Nonnull String mainClass) throws IOException {
+ ExecuteFile ef = new ExecuteFile("rm -rf /tmp/android-data/dalvik-cache/*");
+ ef.run();
+
+ ef = new ExecuteFile("dalvik -classpath " + dexFile.getAbsolutePath() + " " + mainClass);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ef.setOut(baos);
+ ef.setErr(System.out);
+ if (!ef.run()) {
+ throw new AssertionError("Execution failed");
+ }
+
+ baos.flush();
+ baos.close();
+
+ return (baos.toString());
+ }
+
+ public List<String> getFQNOfRebuiltTypes() {
+ assert !fileModificationDate.isEmpty();
+
+ List<String> fqnOfRebuiltTypes = new ArrayList<String>();
+ List<File> jackFiles = new ArrayList<File>();
+ File jackFolder = new File(testingFolder, "jackIncrementalOutput");
+ fillJackFiles(jackFolder, jackFiles);
+
+ for (File jackFile : jackFiles) {
+ Long previousDate = fileModificationDate.get(jackFile.getAbsolutePath());
+ if (previousDate == null || jackFile.lastModified() > previousDate.longValue()) {
+ String jackFileName = jackFile.getAbsolutePath();
+ String binaryTypeName = jackFileName.substring(0, jackFileName.indexOf(".jack"));
+ binaryTypeName = binaryTypeName.substring(jackFolder.getAbsolutePath().length() + 1);
+ fqnOfRebuiltTypes.add(binaryTypeName.replace(File.separatorChar,'.'));
+ }
+ }
+
+ return (fqnOfRebuiltTypes);
+ }
+
+ public void cleanSnapshot() {
+ fileModificationDate.clear();
+ }
+
+ public void snapshotJackFilesModificationDate() {
+ List<File> jackFiles = new ArrayList<File>();
+ fillJackFiles(new File(testingFolder, "jackIncrementalOutput"), jackFiles);
+ for (File jackFile : jackFiles) {
+ fileModificationDate.put(jackFile.getAbsolutePath(), Long.valueOf(jackFile.lastModified()));
+ }
+ }
+
+ @Nonnull
+ public String endErrorRedirection() {
+ assert baos != null;
+ String err = baos.toString();
+ assert errRedirectStream != null;
+ errRedirectStream.close();
+ return err;
+ }
+
+ public void startErrRedirection() {
+ baos = new ByteArrayOutputStream();
+ errRedirectStream = new PrintStream(baos);
+ System.setErr(errRedirectStream);
+ }
+
+ @Nonnull
+ public List<File> getJackFiles() {
+ List<File> jackFiles = new ArrayList<File>();
+ fillJackFiles(new File(testingFolder, "jackIncrementalOutput"), jackFiles);
+ return (jackFiles);
+ }
+
+ private void fillJackFiles(@Nonnull File file, @Nonnull List<File> jackFiles) {
+ if (file.isDirectory()) {
+ for (File subFile : file.listFiles()) {
+ fillJackFiles(subFile, jackFiles);
+ }
+ } else if (file.getName().endsWith(JayceFileImporter.JAYCE_FILE_EXTENSION)) {
+ jackFiles.add(file);
+ }
+ }
+
+ private static void compileSourceToDex(@Nonnull Options options,
+ @Nonnull File sourceFolderOrSourceList,
+ @CheckForNull String classpath,
+ @Nonnull File out) throws Exception {
+ options.setEcjArguments(TestTools.buildEcjArgs(false));
+ addFile(sourceFolderOrSourceList, options.getEcjArguments());
+ options.setClasspath(classpath);
+ options.setOutputFile(out);
+ JackIncremental.run(options);
+ }
+}