diff options
author | mikaelpeltier <mikaelpeltier@google.com> | 2014-04-08 09:42:00 +0200 |
---|---|---|
committer | mikaelpeltier <mikaelpeltier@google.com> | 2014-05-19 09:31:07 +0200 |
commit | 9d9e23d43bf2cb18ca5d6b5993273fe36bd7d9a6 (patch) | |
tree | fe9690857b467e7f96b5413dd4adf385ccbabeb7 | |
parent | 042b969743cc923722a0452ed9b1002ca2fbd247 (diff) | |
download | toolchain_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
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); + } +} |