diff options
Diffstat (limited to 'jack')
17 files changed, 692 insertions, 846 deletions
diff --git a/jack/src/com/android/jack/Jack.java b/jack/src/com/android/jack/Jack.java index 730b11d..9f3900d 100644 --- a/jack/src/com/android/jack/Jack.java +++ b/jack/src/com/android/jack/Jack.java @@ -23,10 +23,8 @@ import com.android.jack.analysis.UsedVariableRemover; import com.android.jack.analysis.defsuses.DefUsesAndUseDefsChainComputation; import com.android.jack.analysis.defsuses.DefUsesAndUseDefsChainRemover; import com.android.jack.analysis.defsuses.UseDefsChecker; -import com.android.jack.analysis.dependency.file.FileDependencies; import com.android.jack.analysis.dependency.file.FileDependenciesCollector; import com.android.jack.analysis.dependency.file.FileDependenciesWriter; -import com.android.jack.analysis.dependency.type.TypeDependencies; import com.android.jack.analysis.dependency.type.TypeDependenciesCollector; import com.android.jack.analysis.dependency.type.TypeDependenciesWriter; import com.android.jack.analysis.dfa.reachingdefs.ReachingDefinitions; @@ -400,11 +398,6 @@ public abstract class Jack { return unmodifiableCollections; } - public static void run(@Nonnull Options options) throws ConfigurationException, JackUserException, - IllegalOptionsException, NothingToDoException { - Jack.run(options, new TypeDependencies(), new FileDependencies()); - } - /** * Runs the jack compiler on source files and generates a dex file. * @@ -419,9 +412,7 @@ public abstract class Jack { * @throws JPackageLookupException thrown when the lookup of a package failed. * @throws JTypeLookupException thrown when the lookup of a type failed. */ - // TODO(jack-team): Remove typeDependencies and fileDependencies parameters - public static void run(@Nonnull Options options, @Nonnull TypeDependencies typeDependencies, - @Nonnull FileDependencies fileDependencies) + public static void run(@Nonnull Options options) throws IllegalOptionsException, NothingToDoException, ConfigurationException, @@ -475,10 +466,7 @@ public abstract class Jack { logger.log(Level.INFO, "Jack sanity checks {0}", (options.hasSanityChecks() ? "enabled" : "disabled")); - JSession session = getSession(); - session.setTypeDependencies(typeDependencies); - session.setFileDependencies(fileDependencies); buildSession(options, hooks); @@ -732,12 +720,12 @@ public abstract class Jack { @Nonnull static JSession buildSession(@Nonnull Options options, @Nonnull RunnableHooks hooks) throws JackUserException { - Tracer tracer = TracerFactory.getTracer(); List<String> ecjArguments = options.ecjArguments; JSession session = getSession(); + session.setInputFilter(ThreadConfig.get(Options.INPUT_FILTER).create(options)); try { getMetaImporter(options.metaImport, hooks).doImport(session); @@ -750,7 +738,7 @@ public abstract class Jack { try { jayceImporter = getJayceFileImporter(options.jayceImport, hooks, session); putInJackClasspath(options.getBootclasspath(), hooks, session); - putInJackClasspath(options.getClasspath(), hooks, session); + putInJackClasspath(session.getInputFilter().getClasspath(), hooks, session); } catch (LibraryReadingException e) { session.getReporter().report(Severity.FATAL, e); throw new JackAbortException(e); diff --git a/jack/src/com/android/jack/Options.java b/jack/src/com/android/jack/Options.java index 6f28ddf..ea165dc 100644 --- a/jack/src/com/android/jack/Options.java +++ b/jack/src/com/android/jack/Options.java @@ -25,6 +25,7 @@ import com.android.jack.backend.dex.rop.CodeItemBuilder; import com.android.jack.config.id.Arzon; import com.android.jack.config.id.JavaVersionPropertyId; import com.android.jack.config.id.Private; +import com.android.jack.incremental.InputFilter; import com.android.jack.ir.ast.JMethod; import com.android.jack.shrob.obfuscation.MappingPrinter; import com.android.jack.shrob.obfuscation.NameProviderFactory; @@ -52,6 +53,7 @@ import com.android.sched.util.config.id.EnumPropertyId; import com.android.sched.util.config.id.ImplementationPropertyId; import com.android.sched.util.config.id.ObjectId; import com.android.sched.util.config.id.PropertyId; +import com.android.sched.util.config.id.ReflectFactoryPropertyId; import com.android.sched.util.file.Directory; import com.android.sched.util.file.FileOrDirectory.ChangePermission; import com.android.sched.util.file.FileOrDirectory.Existence; @@ -95,6 +97,11 @@ import javax.annotation.Nonnull; public class Options { @Nonnull + public static final ReflectFactoryPropertyId<InputFilter> INPUT_FILTER = ReflectFactoryPropertyId + .create("jack.input.filter", "Inputs filter", InputFilter.class) + .addDefaultValue("no-filter").addArgType(Options.class); + + @Nonnull public static final JavaVersionPropertyId JAVA_SOURCE_VERSION = JavaVersionPropertyId .create("jack.java.source.version", "Java source version").addDefaultValue("1.7") .withCategory(Arzon.get()); @@ -140,9 +147,10 @@ public class Options { "jack.internal.library.output.dir", "Output folder for internal library", new DirectDirInputOutputVDirCodec(Existence.MAY_EXIST)) .withCategory(Private.get()).requiredIf(GENERATE_DEX_IN_LIBRARY.getValue() - .isTrue().and(DEX_OUTPUT_CONTAINER_TYPE.is(Container.DIR) - .or(DEX_OUTPUT_CONTAINER_TYPE.is(Container.ZIP)) - .and(GENERATE_JACK_LIBRARY.getValue().isFalse()))); + .isTrue().and(DEX_OUTPUT_CONTAINER_TYPE.is(Container.DIR).or( + DEX_OUTPUT_CONTAINER_TYPE.is(Container.ZIP)).and(GENERATE_JACK_LIBRARY.getValue() + .isFalse())).or(GENERATE_JACK_LIBRARY.getValue() + .isTrue().and(LIBRARY_OUTPUT_CONTAINER_TYPE.is(Container.DIR)))); @Nonnull public static final PropertyId<OutputVFS> DEX_OUTPUT_DIR = PropertyId.create( @@ -611,6 +619,8 @@ public class Options { configBuilder.setString(LIBRARY_OUTPUT_DIR, libraryOutDir.getAbsolutePath()); configBuilder.set(LIBRARY_OUTPUT_CONTAINER_TYPE, Container.DIR); configBuilder.set(GENERATE_JACK_LIBRARY, true); + configBuilder.set(GENERATE_DEX_IN_LIBRARY, true); + configBuilder.setString(Options.INTERNAL_LIBRARY_OUTPUT_DIR, libraryOutDir.getPath()); } switch (multiDexKind) { @@ -649,6 +659,10 @@ public class Options { FieldInitializerRemover.STRING_AS_INITIALVALUE_OF_OBJECT, !runtimeLegacy); if (incrementalFolder != null) { + configBuilder.setString(Options.INPUT_FILTER.getName(), "incremental"); + configBuilder.set(Options.GENERATE_JACK_LIBRARY, true); + configBuilder.setString(Options.LIBRARY_OUTPUT_CONTAINER_TYPE.getName(), "dir"); + configBuilder.setString(Options.LIBRARY_OUTPUT_DIR.getName(), incrementalFolder.getPath()); configBuilder.setString(Options.INTERNAL_LIBRARY_OUTPUT_DIR, incrementalFolder.getAbsolutePath()); } @@ -702,7 +716,7 @@ public class Options { try { compiler.configure(ecjArguments.toArray(new String[ecjArguments.size()])); - if (!compiler.proceed) { + if (!compiler.proceed && incrementalFolder == null) { throw new NothingToDoException(); } compiler.getLibraryAccess().cleanup(); diff --git a/jack/src/com/android/jack/analysis/dependency/file/FileDependencies.java b/jack/src/com/android/jack/analysis/dependency/file/FileDependencies.java index c7a428a..f31065d 100644 --- a/jack/src/com/android/jack/analysis/dependency/file/FileDependencies.java +++ b/jack/src/com/android/jack/analysis/dependency/file/FileDependencies.java @@ -70,6 +70,15 @@ public class FileDependencies extends Dependency { types.add(typeFqn); } + public void update(@Nonnull Set<String> deleteFileNames, @Nonnull Set<String> modifiedFileNames) { + for (String deletedJavaFileName : deleteFileNames) { + javaFileToTypes.remove(deletedJavaFileName); + } + for (String modifiedJavaFileName : modifiedFileNames) { + javaFileToTypes.remove(modifiedJavaFileName); + } + } + public void write(@Nonnull PrintStream ps) { writeMapOne2Many(ps, javaFileToTypes); ps.print(Dependency.MAP_SEPARATOR); @@ -77,11 +86,6 @@ public class FileDependencies extends Dependency { } @Nonnull - public void read(@Nonnull Readable reader) throws IOException { - javaFileToTypes = readMapOne2Many(new LineReader(reader)); - } - - @Nonnull public Set<String> getTypeNames(@Nonnull String javaFileName) { Set<String> typeNames = javaFileToTypes.get(javaFileName); if (typeNames == null) { @@ -105,4 +109,9 @@ public class FileDependencies extends Dependency { public Set<String> getCompiledJavaFiles() { return Jack.getUnmodifiableCollections().getUnmodifiableSet(javaFileToTypes.keySet()); } + + @Nonnull + public void read(@Nonnull Readable reader) throws IOException { + javaFileToTypes = readMapOne2Many(new LineReader(reader)); + } } diff --git a/jack/src/com/android/jack/analysis/dependency/file/FileDependenciesWriter.java b/jack/src/com/android/jack/analysis/dependency/file/FileDependenciesWriter.java index 5526d8b..8b87064 100644 --- a/jack/src/com/android/jack/analysis/dependency/file/FileDependenciesWriter.java +++ b/jack/src/com/android/jack/analysis/dependency/file/FileDependenciesWriter.java @@ -19,9 +19,10 @@ package com.android.jack.analysis.dependency.file; import com.android.jack.Jack; import com.android.jack.JackAbortException; import com.android.jack.JackUserException; -import com.android.jack.experimental.incremental.IncrementalException; +import com.android.jack.incremental.IncrementalException; import com.android.jack.ir.ast.JSession; import com.android.jack.library.FileType; +import com.android.jack.library.OutputJackLibrary; import com.android.jack.reporting.Reporter.Severity; import com.android.sched.item.Description; import com.android.sched.item.Name; @@ -44,11 +45,16 @@ public class FileDependenciesWriter implements RunnableSchedulable<JSession>{ @Override public void run(@Nonnull JSession session) throws JackUserException { - PrintStream ps = null; + write(session.getJackInternalOutputLibrary(), Jack.getSession().getFileDependencies()); + } + + public static void write(@Nonnull OutputJackLibrary ojl, + @Nonnull FileDependencies fileDependencies) { + PrintStream ps = null; try { - ps = new PrintStream(session.getJackInternalOutputLibrary().createFile( - FileType.DEPENDENCIES, FileDependencies.vpath).openWrite()); - Jack.getSession().getFileDependencies().write(ps); + ps = new PrintStream( + ojl.createFile(FileType.DEPENDENCIES, FileDependencies.vpath).openWrite()); + fileDependencies.write(ps); } catch (CannotCreateFileException e) { IncrementalException incrementalException = new IncrementalException(e); Jack.getSession().getReporter().report(Severity.FATAL, incrementalException); diff --git a/jack/src/com/android/jack/analysis/dependency/type/TypeDependencies.java b/jack/src/com/android/jack/analysis/dependency/type/TypeDependencies.java index 428aca1..0116964 100644 --- a/jack/src/com/android/jack/analysis/dependency/type/TypeDependencies.java +++ b/jack/src/com/android/jack/analysis/dependency/type/TypeDependencies.java @@ -19,6 +19,7 @@ package com.android.jack.analysis.dependency.type; import com.google.common.io.LineReader; import com.android.jack.analysis.dependency.Dependency; +import com.android.jack.analysis.dependency.file.FileDependencies; import com.android.jack.ir.ast.JType; import com.android.jack.ir.formatter.BinaryQualifiedNameFormatter; import com.android.sched.item.Description; @@ -89,14 +90,6 @@ public class TypeDependencies extends Dependency { } @Nonnull - public void read(@Nonnull Readable readable) throws IOException { - LineReader lr = new LineReader(readable); - hierarchyDependencies = readMapOne2Many(lr); - constantDependencies = readMapOne2Many(lr); - codeDependencies = readMapOne2Many(lr); - } - - @Nonnull public Map<String, Set<String>> getRecompileDependencies() { Map<String, Set<String>> recompileDependencies = new HashMap<String, Set<String>>(); @@ -201,4 +194,30 @@ public class TypeDependencies extends Dependency { } } } + + @Nonnull + public void read(@Nonnull Readable readable) throws IOException { + LineReader lr = new LineReader(readable); + hierarchyDependencies = readMapOne2Many(lr); + constantDependencies = readMapOne2Many(lr); + codeDependencies = readMapOne2Many(lr); + } + + public void update(@Nonnull FileDependencies fileDependencies, + @Nonnull Set<String> deleteFileNames, @Nonnull Set<String> modifiedFileNames) { + for (String deletedJavaFileName : deleteFileNames) { + for (String deleteTypeName : fileDependencies.getTypeNames(deletedJavaFileName)) { + codeDependencies.remove(deleteTypeName); + constantDependencies.remove(deleteTypeName); + hierarchyDependencies.remove(deleteTypeName); + } + } + for (String modifiedJavaFileName : modifiedFileNames) { + for (String deleteTypeName : fileDependencies.getTypeNames(modifiedJavaFileName)) { + codeDependencies.remove(deleteTypeName); + constantDependencies.remove(deleteTypeName); + hierarchyDependencies.remove(deleteTypeName); + } + } + } } diff --git a/jack/src/com/android/jack/analysis/dependency/type/TypeDependenciesWriter.java b/jack/src/com/android/jack/analysis/dependency/type/TypeDependenciesWriter.java index 68fc1c5..2229e2a 100644 --- a/jack/src/com/android/jack/analysis/dependency/type/TypeDependenciesWriter.java +++ b/jack/src/com/android/jack/analysis/dependency/type/TypeDependenciesWriter.java @@ -19,9 +19,10 @@ package com.android.jack.analysis.dependency.type; import com.android.jack.Jack; import com.android.jack.JackAbortException; import com.android.jack.JackUserException; -import com.android.jack.experimental.incremental.IncrementalException; +import com.android.jack.incremental.IncrementalException; import com.android.jack.ir.ast.JSession; import com.android.jack.library.FileType; +import com.android.jack.library.OutputJackLibrary; import com.android.jack.reporting.Reporter.Severity; import com.android.sched.item.Description; import com.android.sched.item.Name; @@ -44,11 +45,16 @@ public class TypeDependenciesWriter implements RunnableSchedulable<JSession>{ @Override public void run(@Nonnull JSession session) throws JackUserException { + write(session.getJackInternalOutputLibrary(), Jack.getSession().getTypeDependencies()); + } + + public static void write(@Nonnull OutputJackLibrary ojl, + @Nonnull TypeDependencies typeDependencies) { PrintStream ps = null; try { - ps = new PrintStream(session.getJackInternalOutputLibrary() - .createFile(FileType.DEPENDENCIES, TypeDependencies.vpath).openWrite()); - Jack.getSession().getTypeDependencies().write(ps); + ps = new PrintStream( + ojl.createFile(FileType.DEPENDENCIES, TypeDependencies.vpath).openWrite()); + typeDependencies.write(ps); } catch (CannotCreateFileException e) { IncrementalException incrementalException = new IncrementalException(e); Jack.getSession().getReporter().report(Severity.FATAL, incrementalException); diff --git a/jack/src/com/android/jack/experimental/incremental/JackIncremental.java b/jack/src/com/android/jack/experimental/incremental/JackIncremental.java deleted file mode 100644 index 2d54632..0000000 --- a/jack/src/com/android/jack/experimental/incremental/JackIncremental.java +++ /dev/null @@ -1,683 +0,0 @@ -/* - * 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.JackUserException; -import com.android.jack.NothingToDoException; -import com.android.jack.Options; -import com.android.jack.analysis.dependency.file.FileDependencies; -import com.android.jack.analysis.dependency.file.FileDependenciesWriter; -import com.android.jack.analysis.dependency.type.TypeDependencies; -import com.android.jack.analysis.dependency.type.TypeDependenciesWriter; -import com.android.jack.backend.dex.DexFileProduct; -import com.android.jack.backend.dex.DexFileWriter; -import com.android.jack.frontend.FrontendCompilationException; -import com.android.jack.ir.ast.JSession; -import com.android.jack.ir.formatter.BinaryQualifiedNameFormatter; -import com.android.jack.ir.formatter.TypeFormatter; -import com.android.jack.library.FileType; -import com.android.jack.library.FileTypeDoesNotExistException; -import com.android.jack.library.InputJackLibrary; -import com.android.jack.library.JackLibraryFactory; -import com.android.jack.library.LibraryFormatException; -import com.android.jack.library.LibraryIOException; -import com.android.jack.library.LibraryVersionException; -import com.android.jack.library.NotJackLibraryException; -import com.android.jack.library.OutputJackLibrary; -import com.android.jack.load.JackLoadingException; -import com.android.jack.scheduling.marker.ClassDefItemMarker; -import com.android.jack.util.TextUtils; -import com.android.sched.scheduler.IllegalRequestException; -import com.android.sched.scheduler.PlanBuilder; -import com.android.sched.scheduler.Request; -import com.android.sched.util.UnrecoverableException; -import com.android.sched.util.config.ChainedException; -import com.android.sched.util.config.ConfigurationException; -import com.android.sched.util.config.ThreadConfig; -import com.android.sched.util.file.CannotCreateFileException; -import com.android.sched.util.file.CannotDeleteFileException; -import com.android.sched.util.file.CannotReadException; -import com.android.sched.util.file.CannotSetPermissionException; -import com.android.sched.util.file.Directory; -import com.android.sched.util.file.FileAlreadyExistsException; -import com.android.sched.util.file.FileOrDirectory.ChangePermission; -import com.android.sched.util.file.FileOrDirectory.Existence; -import com.android.sched.util.file.FileOrDirectory.Permission; -import com.android.sched.util.file.NoSuchFileException; -import com.android.sched.util.file.NotFileOrDirectoryException; -import com.android.sched.util.file.WrongPermissionException; -import com.android.sched.util.log.LoggerFactory; -import com.android.sched.vfs.DirectVFS; -import com.android.sched.vfs.InputVFile; -import com.android.sched.vfs.VPath; - -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -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. - */ -public class JackIncremental extends CommandLine { - - @Nonnull - private static final Logger logger = LoggerFactory.getLogger(); - - @Nonnull - private static List<String> deletedTypes = new ArrayList<String>(); - - @CheckForNull - private static File incrementalFolder; - - @CheckForNull - private static InputJackLibrary incrementalInputLibrary; - - @CheckForNull - private static final TypeFormatter formatter = BinaryQualifiedNameFormatter.getFormatter(); - - @CheckForNull - private static final char fileSeparator = '/'; - - - 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 (JackLoadingException e) { - System.err.println(e.getMessage()); - logger.log(Level.FINE, "Jack loading 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) { - String info = - "Internal incremental compiler error (version " + Jack.getVersionString() + ")"; - logger.log(Level.SEVERE, info + ':', e); - e.printStackTrace(); - System.err.println(); - System.err.println(info + '.'); - if (e.getMessage() != null) { - System.err.println(e.getMessage() + '.'); - } - System.err.println(INTERRUPTED_COMPILATION_WARNING); - System.exit(ExitStatus.FAILURE_INTERNAL); - } - } - - public static void run(@Nonnull Options options) - throws ConfigurationException, - IllegalOptionsException, - NothingToDoException, - JackUserException, - IncrementalException { - - deletedTypes.clear(); - incrementalFolder = options.getIncrementalFolder(); - - try { - boolean firstCompilation = false; - - try { - assert incrementalFolder != null; - incrementalInputLibrary = JackLibraryFactory.getInputLibrary(new DirectVFS(new Directory( - incrementalFolder.getPath(), null, Existence.MAY_EXIST, Permission.WRITE, - ChangePermission.NOCHANGE))); - } catch (NotJackLibraryException e) { - // Nothing to do, it is the first compilation - firstCompilation = true; - } - - options.addProperty(Options.GENERATE_JACK_LIBRARY.getName(), "true"); - options.addProperty(Options.LIBRARY_OUTPUT_CONTAINER_TYPE.getName(), "dir"); - assert incrementalFolder != null; - options.addProperty(Options.LIBRARY_OUTPUT_DIR.getName(), incrementalFolder.getPath()); - - if (!firstCompilation && !needFullRebuild(options)) { - assert incrementalInputLibrary != null; - InputVFile typeDependenciesVFile = null; - InputVFile fileDependenciesVFile = null; - try { - typeDependenciesVFile = - incrementalInputLibrary.getFile(FileType.DEPENDENCIES, TypeDependencies.vpath); - } catch (FileTypeDoesNotExistException e) { - throw new AssertionError(e); - } - try { - fileDependenciesVFile = - incrementalInputLibrary.getFile(FileType.DEPENDENCIES, FileDependencies.vpath); - } catch (FileTypeDoesNotExistException e) { - throw new AssertionError(e); - } - - logger.log(Level.FINE, "Incremental compilation"); - - List<String> javaFilesNames = getJavaFilesSpecifiedOnCommandLine(options); - - TypeDependencies typeDependencies = new TypeDependencies(); - InputStreamReader typeReader = null; - try { - typeReader = new InputStreamReader(typeDependenciesVFile.openRead()); - typeDependencies.read(typeReader); - } catch (IOException e) { - throw new CannotReadException(typeDependenciesVFile.getLocation(), e); - } finally { - if (typeReader != null) { - try { - typeReader.close(); - } catch (IOException e) { - } - } - } - - FileDependencies fileDependencies = new FileDependencies(); - InputStreamReader fileReader = null; - try { - fileReader = new InputStreamReader(fileDependenciesVFile.openRead()); - fileDependencies.read(fileReader); - } catch (IOException e) { - throw new CannotReadException(typeDependenciesVFile.getLocation(), e); - } finally { - if (fileReader != null) { - try { - fileReader.close(); - } catch (IOException e) { - } - } - } - - Map<String, Set<String>> typeRecompileDependencies = - typeDependencies.getRecompileDependencies(); - printDependencyStat(typeRecompileDependencies); - - Set<String> deletedFiles = getDeletedFiles(javaFilesNames, fileDependencies); - Set<String> filesToRecompile = getFilesToRecompile(fileDependencies, - typeRecompileDependencies, javaFilesNames, deletedFiles); - - if (!filesToRecompile.isEmpty() || !deletedFiles.isEmpty()) { - logger.log(Level.FINE, "{0} Files to recompile {1}", - new Object[] {Integer.valueOf(filesToRecompile.size()), filesToRecompile}); - updateOptions(options, filesToRecompile); - - logger.log(Level.FINE, "Ecj options {0}", options.getEcjArguments()); - - try { - Jack.run(options, typeDependencies, fileDependencies); - } catch (NothingToDoException e) { - // Even if there is nothing to compile, the output dex file must be rebuild from all dex - // (one dex per types) since some dex files could be removed. To rebuild output dex - // file, a specific plan is used. - ThreadConfig.setConfig(options.getConfig()); - - JSession session = Jack.getSession(); - session.setTypeDependencies(typeDependencies); - session.setFileDependencies(fileDependencies); - - Request request = Jack.createInitialRequest(); - request.addProduction(DexFileProduct.class); - request.addInitialTagOrMarker(ClassDefItemMarker.Complete.class); - request.addInitialTagOrMarker(TypeDependencies.Collected.class); - request.addInitialTagOrMarker(FileDependencies.Collected.class); - - PlanBuilder<JSession> planBuilder; - try { - planBuilder = request.getPlanBuilder(JSession.class); - } catch (IllegalRequestException illegalRequest) { - throw new AssertionError(illegalRequest); - } - - planBuilder.append(TypeDependenciesWriter.class); - planBuilder.append(FileDependenciesWriter.class); - planBuilder.append(DexFileWriter.class); - - assert incrementalFolder != null; - OutputJackLibrary incrementalOutputLibrary = - JackLibraryFactory.getOutputLibrary(new DirectVFS(new Directory( - incrementalFolder.getPath(), null, Existence.MAY_EXIST, Permission.WRITE, - ChangePermission.NOCHANGE)), Jack.getEmitterId(), Jack.getVersionString()); - - assert incrementalInputLibrary != null; - session.setJackInternalOutputLibrary(incrementalOutputLibrary); - - try { - planBuilder.getPlan().getScheduleInstance().process(Jack.getSession()); - } catch (RuntimeException runtimeExcept) { - throw runtimeExcept; - } catch (Exception except) { - throw new AssertionError(except); - } finally { - if (incrementalOutputLibrary != null) { - incrementalOutputLibrary.close(); - } - } - } finally { - ThreadConfig.unsetConfig(); - } - - } else { - logger.log(Level.FINE, "No files to recompile"); - } - } else { - Jack.run(options); - } - } catch (WrongPermissionException e) { - throw new AssertionError(e); - } catch (CannotSetPermissionException e) { - throw new AssertionError(e); - } catch (NoSuchFileException e) { - throw new AssertionError(e); - } catch (NotFileOrDirectoryException e) { - throw new AssertionError(e); - } catch (FileAlreadyExistsException e) { - throw new AssertionError(e); - } catch (CannotCreateFileException e) { - throw new AssertionError(e); - } catch (CannotReadException e) { - throw new IncrementalException(e); - } catch (LibraryVersionException e) { - throw new AssertionError(e); - } catch (LibraryFormatException e) { - throw new AssertionError(e); - } catch (LibraryIOException e) { - throw new IncrementalException(e); - } - } - - /* - * A full rebuild is needed: - * - when a file contained inside a folder in the classpath or an archive in the classpath - * is more recent than the generated dex file. - * - when a file contained inside a folder in the import option or an archive in the import - * option is more recent than the generated dex file. - */ - private static boolean needFullRebuild(@Nonnull Options options) { - if (!options.isAutomaticFullRebuildEnabled()) { - return false; - } - - File outputDexFile = new File(options.getOutputDir(), DexFileWriter.DEX_FILENAME); - if (outputDexFile.exists()) { - for (File lib : options.getBootclasspath()) { - if (isModifiedLibrary(lib, outputDexFile.lastModified())) { - return true; - } - } - for (File lib : options.getClasspath()) { - if (isModifiedLibrary(lib, outputDexFile.lastModified())) { - return true; - } - } - } - - return hasModifiedImport(options, outputDexFile.lastModified()); - } - - private static boolean isModifiedLibrary(@Nonnull File lib, long time) { - if (lib.isFile() && (lib.lastModified() > time)) { - return true; - } else if (lib.isDirectory() && hasModifiedFile(lib, time)) { - return true; - } - - return false; - } - - private static boolean hasModifiedFile(@Nonnull File file, long time) { - assert file.isDirectory(); - - for (File f : file.listFiles()) { - if (f.isDirectory()) { - if (hasModifiedFile(f, time)) { - return true; - } - } else if (f.lastModified() > time) { - return true; - } - } - - return false; - } - - private static boolean hasModifiedImport(@Nonnull Options options, long time) { - for (File importedJackFiles : options.getJayceImport()) { - if (isModifiedLibrary(importedJackFiles, time)) { - return true; - } - } - return false; - } - - @Nonnull - 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>> typeRecompileDependencies) { - int dependencyNumber = 0; - int maxDependencyNumber = -1; - int minDependencyNumber = -1; - - for (Set<String> dependency : typeRecompileDependencies.values()) { - int currentDepSize = dependency.size(); - dependencyNumber += currentDepSize; - if (minDependencyNumber == -1 || minDependencyNumber > currentDepSize) { - minDependencyNumber = currentDepSize; - } - if (maxDependencyNumber == -1 || maxDependencyNumber < currentDepSize) { - maxDependencyNumber = currentDepSize; - } - } - - logger.log(Level.FINE, - "There are {0} dependencies, with {1} files per dependency in average", new Object[] { - Integer.valueOf(typeRecompileDependencies.size()), - Double.valueOf((double) dependencyNumber / (double) typeRecompileDependencies.size())}); - logger.log(Level.FINE, "Dependencies are at minimun {0} and at maximun {1}", - new Object[] {Integer.valueOf(minDependencyNumber), Integer.valueOf(maxDependencyNumber)}); - } - - 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") - && !new File(ecjOptions).isDirectory()) { - newEcjArguments.add(ecjOptions); - } - } - - for (String fileToRecompile : javaFilesToRecompile) { - newEcjArguments.add(fileToRecompile); - } - - assert incrementalFolder != null; - StringBuilder newClasspath = new StringBuilder(incrementalFolder.getPath()); - - String oldClasspath = options.getClasspathAsString(); - if (oldClasspath != null) { - newClasspath.append(File.pathSeparator); - newClasspath.append(oldClasspath); - } - - // Move imported jayce files from import to classpath option - List<File> jayceImport = options.getJayceImport(); - if (!jayceImport.isEmpty()) { - for (File importedJackFiles : jayceImport) { - newClasspath.append(File.pathSeparator); - newClasspath.append(importedJackFiles.getPath()); - } - options.setJayceImports(Collections.<File>emptyList()); - } - - options.setClasspath(newClasspath.toString()); - - if (!newEcjArguments.isEmpty()) { - options.setEcjArguments(newEcjArguments); - } - } - - @Nonnull - private static Set<String> getFilesToRecompile(@Nonnull FileDependencies fileDependencies, - @Nonnull Map<String, Set<String>> typeRecompileDependencies, - @Nonnull List<String> javaFileNames, Set<String> deletedFiles) throws IncrementalException { - Set<String> filesToRecompile = new HashSet<String>(); - - filesToRecompile.addAll(getModifiedFiles(fileDependencies, typeRecompileDependencies, - javaFileNames, deletedFiles)); - filesToRecompile.addAll(getAddedFiles(fileDependencies, javaFileNames)); - - for (String deletedFile : deletedFiles) { - deleteOldFilesFromJavaFiles(fileDependencies, deletedFile); - addNotModifiedDependencies(fileDependencies, typeRecompileDependencies, deletedFiles, - filesToRecompile, deletedFile); - } - - for (String fileToRecompile : filesToRecompile) { - deleteOldFilesFromJavaFiles(fileDependencies, fileToRecompile); - } - - return filesToRecompile; - } - - private static void addNotModifiedDependencies(@Nonnull FileDependencies fileDependencies, - @Nonnull Map<String, Set<String>> typeRecompileDependencies, - @Nonnull Set<String> deletedFiles, @Nonnull Set<String> filesToRecompile, - @Nonnull String modifiedJavaFileName) { - for (String modifiedTypeName : fileDependencies.getTypeNames(modifiedJavaFileName)) { - for (String typeName : typeRecompileDependencies.get(modifiedTypeName)) { - String dependentFileName = fileDependencies.getJavaFileName(typeName); - if (dependentFileName != null && !deletedFiles.contains(dependentFileName)) { - filesToRecompile.add(dependentFileName); - } - } - } - } - - @Nonnull - private static Set<String> getDeletedFiles(@Nonnull List<String> javaFileNames, - @Nonnull FileDependencies fileDependencies) - throws JackUserException { - Set<String> deletedFiles = new HashSet<String>(); - - for (String javaFileName : fileDependencies.getCompiledJavaFiles()) { - if (!javaFileNames.contains(javaFileName)) { - logger.log(Level.FINE, "{0} was deleted", javaFileName); - deletedFiles.add(javaFileName); - } - } - - return deletedFiles; - } - - private static void deleteOldFilesFromJavaFiles( - @Nonnull FileDependencies fileDependencies, @Nonnull String javaFileName) - throws IncrementalException { - assert incrementalInputLibrary != null; - for (String typeNameToRemove : fileDependencies.getTypeNames(javaFileName)) { - if (!deletedTypes.contains(typeNameToRemove)) { - deletedTypes.add(typeNameToRemove); - VPath vpath = new VPath(typeNameToRemove, fileSeparator); - deleteFile(FileType.JAYCE, vpath); - deleteFile(FileType.DEX, vpath); - } - } - } - - private static void deleteFile(@Nonnull FileType fileType, @Nonnull VPath vpath) - throws IncrementalException { - try { - // Check that file exists - assert incrementalInputLibrary != null; - incrementalInputLibrary.getFile(fileType, vpath); - incrementalInputLibrary.delete(fileType, vpath); - } catch (FileTypeDoesNotExistException e) { - } catch (CannotDeleteFileException e) { - throw new IncrementalException(e); - } - } - - @Nonnull - private static Set<String> getAddedFiles(@Nonnull FileDependencies fileDependencies, - @Nonnull List<String> javaFileNames) { - Set<String> addedFiles = new HashSet<String>(); - Set<String> previousFiles = fileDependencies.getCompiledJavaFiles(); - - for (String javaFileName : javaFileNames) { - if (!previousFiles.contains(javaFileName)) { - logger.log(Level.FINE, "{0} was added", javaFileName); - addedFiles.add(javaFileName); - } - } - - return addedFiles; - } - - @Nonnull - private static Set<String> getModifiedFiles(@Nonnull FileDependencies fileDependencies, - @Nonnull Map<String, Set<String>> typeRecompileDependencies, - @Nonnull List<String> javaFileNames, @Nonnull Set<String> deletedFiles) - throws JackUserException { - Set<String> modifiedFiles = new HashSet<String>(); - - for (Map.Entry<String, Set<String>> previousFileEntry : typeRecompileDependencies.entrySet()) { - String typeName = previousFileEntry.getKey(); - String javaFileName = fileDependencies.getJavaFileName(typeName); - if (javaFileName != null && !deletedFiles.contains(javaFileName)) { - File javaFile = new File(javaFileName); - File dexFile = getDexFile(typeName); - if (!dexFile.exists() || (javaFileNames.contains(javaFileName) - && javaFile.lastModified() > dexFile.lastModified())) { - logger.log(Level.FINE, "{0} was modified", new Object[] {javaFileName}); - modifiedFiles.add(javaFileName); - addNotModifiedDependencies(fileDependencies, typeRecompileDependencies, deletedFiles, - modifiedFiles, javaFileName); - break; - } - } - } - - 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; - } - - public static TypeFormatter getFormatter() { - return formatter; - } - - @Nonnull - protected static InputVFile getJackFile(@Nonnull String typeName) { - try { - assert incrementalInputLibrary != null; - return incrementalInputLibrary.getFile(FileType.JAYCE, new VPath(typeName, fileSeparator)); - } catch (FileTypeDoesNotExistException e) { - throw new AssertionError(e); - } - } - - @Nonnull - protected static File getDexFile(@Nonnull String typeName) { - assert incrementalInputLibrary != null; - return new File(incrementalFolder, FileType.DEX.buildFileVPath( - new VPath(typeName, fileSeparator)).getPathAsString(File.separatorChar)); - } -} diff --git a/jack/src/com/android/jack/experimental/incremental/Main.java b/jack/src/com/android/jack/experimental/incremental/Main.java deleted file mode 100644 index babe775..0000000 --- a/jack/src/com/android/jack/experimental/incremental/Main.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 java.io.PrintStream; - -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()) { - printIncrementalUsage(System.out); - 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) { - printIncrementalUsage(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; - } - - private static void printIncrementalUsage(@Nonnull PrintStream printStream) { - printStream.println( - " --incremental-folder FILE : Folder used for incremental data"); - printUsage(printStream); - } -} diff --git a/jack/src/com/android/jack/frontend/java/JackBatchCompiler.java b/jack/src/com/android/jack/frontend/java/JackBatchCompiler.java index a1644d3..19bcfbf 100644 --- a/jack/src/com/android/jack/frontend/java/JackBatchCompiler.java +++ b/jack/src/com/android/jack/frontend/java/JackBatchCompiler.java @@ -43,6 +43,7 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -239,6 +240,8 @@ public class JackBatchCompiler extends Main { super.configure(argv); checkedClasspaths = new FileSystem.Classpath[] { new JAstClasspath("<jack-logical-entry>", session.getLookup(), null)}; + List<String> fileNamesToCompile = session.getInputFilter().getFileNamesToCompile(); + filenames = fileNamesToCompile.toArray(new String[fileNamesToCompile.size()]); } @SuppressWarnings("rawtypes") diff --git a/jack/src/com/android/jack/incremental/CommonFilter.java b/jack/src/com/android/jack/incremental/CommonFilter.java new file mode 100644 index 0000000..4c0d804 --- /dev/null +++ b/jack/src/com/android/jack/incremental/CommonFilter.java @@ -0,0 +1,73 @@ +/* + * 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.incremental; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; + +import com.android.jack.Options; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; + +/** + * Common part of {@link InputFilter} + */ +public abstract class CommonFilter { + + @Nonnull + protected List<String> getJavaFileNamesSpecifiedOnCommandLine(@Nonnull Options options) { + final List<File> folders = new ArrayList<File>(); + final String extension = ".java"; + + List<String> javaFileNames = + Lists.newArrayList(Collections2.filter(options.getEcjArguments(), new Predicate<String>() { + @Override + public boolean apply(String arg) { + File argFile = new File(arg); + if (argFile.isDirectory()) { + folders.add(argFile); + } + return arg.endsWith(extension); + } + })); + + for (File folder : folders) { + fillFiles(folder, extension, javaFileNames); + } + + return (javaFileNames); + } + + private void fillFiles(@Nonnull File folder, @Nonnull String fileExt, + @Nonnull List<String> fileNames) { + for (File subFile : folder.listFiles()) { + if (subFile.isDirectory()) { + fillFiles(subFile, fileExt, fileNames); + } else { + String path = subFile.getPath(); + if (subFile.getName().endsWith(fileExt) && !fileNames.contains(path)) { + fileNames.add(path); + } + } + } + } +} diff --git a/jack/src/com/android/jack/experimental/incremental/IncrementalException.java b/jack/src/com/android/jack/incremental/IncrementalException.java index 4feb3ac..5fb44c8 100644 --- a/jack/src/com/android/jack/experimental/incremental/IncrementalException.java +++ b/jack/src/com/android/jack/incremental/IncrementalException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.jack.experimental.incremental; +package com.android.jack.incremental; import com.android.jack.reporting.ReportableException; diff --git a/jack/src/com/android/jack/incremental/IncrementalInputFilter.java b/jack/src/com/android/jack/incremental/IncrementalInputFilter.java new file mode 100644 index 0000000..74bc20d --- /dev/null +++ b/jack/src/com/android/jack/incremental/IncrementalInputFilter.java @@ -0,0 +1,415 @@ +/* + * 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.incremental; + +import com.android.jack.Jack; +import com.android.jack.JackAbortException; +import com.android.jack.Options; +import com.android.jack.analysis.dependency.file.FileDependencies; +import com.android.jack.analysis.dependency.file.FileDependenciesWriter; +import com.android.jack.analysis.dependency.type.TypeDependencies; +import com.android.jack.analysis.dependency.type.TypeDependenciesWriter; +import com.android.jack.backend.dex.DexFileWriter; +import com.android.jack.library.FileType; +import com.android.jack.library.FileTypeDoesNotExistException; +import com.android.jack.library.InputJackLibrary; +import com.android.jack.library.JackLibraryFactory; +import com.android.jack.library.LibraryFormatException; +import com.android.jack.library.LibraryReadingException; +import com.android.jack.library.LibraryVersionException; +import com.android.jack.library.NotJackLibraryException; +import com.android.jack.library.OutputJackLibrary; +import com.android.jack.reporting.Reporter.Severity; +import com.android.sched.util.codec.ImplementationName; +import com.android.sched.util.config.ThreadConfig; +import com.android.sched.util.file.CannotDeleteFileException; +import com.android.sched.util.file.CannotReadException; +import com.android.sched.vfs.InputVFile; +import com.android.sched.vfs.VPath; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + + +/** + * {@link InputFilter} that returns filtered inputs required by incremental support. + */ +@ImplementationName(iface = InputFilter.class, name = "incremental") +public class IncrementalInputFilter extends CommonFilter implements InputFilter { + + @Nonnull + private final Options options; + + @CheckForNull + private final InputJackLibrary incrementalInputLibrary; + + @CheckForNull + private FileDependencies fileDependencies; + + @CheckForNull + private TypeDependencies typeDependencies; + + @Nonnull + private final List<String> fileNamesOnCmdLine; + + public IncrementalInputFilter(@Nonnull Options options) { + this.options = options; + incrementalInputLibrary = getIncrementalInternalLibrary(); + fileNamesOnCmdLine = getJavaFileNamesSpecifiedOnCommandLine(options); + if (incrementalInputLibrary != null) { + try { + fileDependencies = getFileDependencies(incrementalInputLibrary); + typeDependencies = getTypeDependencies(incrementalInputLibrary); + } catch (CannotReadException e) { + LibraryReadingException reportable = new LibraryReadingException( + new LibraryFormatException(incrementalInputLibrary.getLocation())); + Jack.getSession().getReporter().report(Severity.FATAL, reportable); + throw new JackAbortException(reportable); + } catch (FileTypeDoesNotExistException e) { + LibraryReadingException reportable = new LibraryReadingException( + new LibraryFormatException(incrementalInputLibrary.getLocation())); + Jack.getSession().getReporter().report(Severity.FATAL, reportable); + throw new JackAbortException(reportable); + } + } + } + + @Override + @Nonnull + public List<File> getClasspath() { + InputJackLibrary incrementalInputLibrary = getIncrementalInternalLibrary(); + List<File> classpath = options.getClasspath(); + + if (incrementalInputLibrary == null || needFullRebuild()) { + Jack.getSession().setFileDependencies(new FileDependencies()); + Jack.getSession().setTypeDependencies(new TypeDependencies()); + return classpath; + } + + assert options.getIncrementalFolder() != null; + classpath.add(options.getIncrementalFolder()); + classpath.addAll(options.getJayceImport()); + + options.setJayceImports(Collections.<File>emptyList()); + + try { + updateLibrary(); + } catch (IncrementalException e) { + Jack.getSession().getReporter().report(Severity.FATAL, e); + throw new JackAbortException(e); + } + + return classpath; + } + + @Override + @Nonnull + public List<String> getFileNamesToCompile() { + InputJackLibrary incrementalInputLibrary = getIncrementalInternalLibrary(); + + if (incrementalInputLibrary == null || needFullRebuild()) { + return fileNamesOnCmdLine; + } + + assert typeDependencies != null; + + Map<String, Set<String>> typeRecompileDependencies = + typeDependencies.getRecompileDependencies(); + + List<String> filesToRecompile = new ArrayList<String>(); + + filesToRecompile.addAll(getAddedFileNames()); + + for (String modifiedFileName : getModifiedFileNames()) { + filesToRecompile.add(modifiedFileName); + filesToRecompile.addAll( + getDependencyFileNamesToRecompile(typeRecompileDependencies, modifiedFileName)); + } + + for (String deletedFileName : getDeleteFileNames()) { + filesToRecompile.addAll( + getDependencyFileNamesToRecompile(typeRecompileDependencies, deletedFileName)); + } + + return filesToRecompile; + } + + private void updateLibrary() throws IncrementalException { + assert fileDependencies != null; + assert typeDependencies != null; + + for (String fileToRecompile : getFileNamesToCompile()) { + deleteOldFilesFromJavaFiles(fileToRecompile); + } + + for (String deletedFileName : getDeleteFileNames()) { + deleteOldFilesFromJavaFiles(deletedFileName); + } + + fileDependencies.update(getDeleteFileNames(), getModifiedFileNames()); + typeDependencies.update(fileDependencies, getDeleteFileNames(), getModifiedFileNames()); + + OutputJackLibrary outputLibrary = JackLibraryFactory.getOutputLibrary( + ThreadConfig.get(Options.INTERNAL_LIBRARY_OUTPUT_DIR), Jack.getEmitterId(), + Jack.getVersionString()); + + FileDependenciesWriter.write(outputLibrary, fileDependencies); + TypeDependenciesWriter.write(outputLibrary, typeDependencies); + + Jack.getSession().setFileDependencies(fileDependencies); + Jack.getSession().setTypeDependencies(typeDependencies); + } + + private void deleteOldFilesFromJavaFiles(@Nonnull String javaFileName) + throws IncrementalException { + assert fileDependencies != null; + List<String> deletedTypes = new ArrayList<String>(); + for (String typeNameToRemove : fileDependencies.getTypeNames(javaFileName)) { + if (!deletedTypes.contains(typeNameToRemove)) { + deletedTypes.add(typeNameToRemove); + VPath vpath = new VPath(typeNameToRemove, '/'); + deleteFile(FileType.JAYCE, vpath); + deleteFile(FileType.DEX, vpath); + } + } + } + + private void deleteFile(@Nonnull FileType fileType, @Nonnull VPath vpath) + throws IncrementalException { + assert incrementalInputLibrary != null; + try { + // Check that file exists + incrementalInputLibrary.getFile(fileType, vpath); + incrementalInputLibrary.delete(fileType, vpath); + } catch (FileTypeDoesNotExistException e) { + // Nothing to do, file does no longer exists + } catch (CannotDeleteFileException e) { + throw new IncrementalException(e); + } + } + + /* + * A full rebuild is needed when an imported library was modified or when a library from classpath + * was modified. + */ + private boolean needFullRebuild() { + if (!options.isAutomaticFullRebuildEnabled()) { + return false; + } + + long timestamp = new File(options.getOutputDir(), DexFileWriter.DEX_FILENAME).lastModified(); + + for (File lib : options.getBootclasspath()) { + if (isModifiedLibrary(lib, timestamp)) { + return true; + } + } + + for (File lib : options.getClasspath()) { + if (isModifiedLibrary(lib, timestamp)) { + return true; + } + } + + for (File importedJackFiles : options.getJayceImport()) { + if (isModifiedLibrary(importedJackFiles, timestamp)) { + return true; + } + } + + return false; + } + + private boolean isModifiedLibrary(@Nonnull File inputLibrary, long timestamp) { + if (inputLibrary.isFile() && (inputLibrary.lastModified() > timestamp)) { + return true; + } else if (inputLibrary.isDirectory() && hasModifiedFile(inputLibrary, timestamp)) { + return true; + } + + return false; + } + + private boolean hasModifiedFile(@Nonnull File inputLibrary, long timestamp) { + assert inputLibrary.isDirectory(); + + for (File f : inputLibrary.listFiles()) { + if (f.isDirectory()) { + if (hasModifiedFile(f, timestamp)) { + return true; + } + } else if (f.lastModified() > timestamp) { + return true; + } + } + + return false; + } + + @Nonnull + private List<String> getDependencyFileNamesToRecompile( + @Nonnull Map<String, Set<String>> typeRecompileDependencies, + @Nonnull String modifiedJavaFileName) { + List<String> fileNamesToRecompile = new ArrayList<String>(); + Set<String> deleteFileNames = getDeleteFileNames(); + + assert fileDependencies != null; + for (String modifiedTypeName : fileDependencies.getTypeNames(modifiedJavaFileName)) { + for (String typeName : typeRecompileDependencies.get(modifiedTypeName)) { + String dependentFileName = fileDependencies.getJavaFileName(typeName); + if (dependentFileName != null && !deleteFileNames.contains(dependentFileName)) { + fileNamesToRecompile.add(dependentFileName); + } + } + } + + return fileNamesToRecompile; + } + + @CheckForNull + private InputJackLibrary getIncrementalInternalLibrary() { + try { + return JackLibraryFactory.getInputLibrary( + ThreadConfig.get(Options.INTERNAL_LIBRARY_OUTPUT_DIR)); + } catch (NotJackLibraryException e) { + // No incremental internal library, it is the first compilation + } catch (LibraryVersionException e) { + // Incremental internal library has changed, do not reuse it + } catch (LibraryFormatException e) { + // Incremental internal library has changed, do not reuse it + } + return null; + } + + @Nonnull + private Set<String> getAddedFileNames() { + assert fileDependencies != null; + Set<String> addedFileNames = new HashSet<String>(); + Set<String> previousFiles = fileDependencies.getCompiledJavaFiles(); + + for (String javaFileName : fileNamesOnCmdLine) { + if (!previousFiles.contains(javaFileName)) { + addedFileNames.add(javaFileName); + } + } + + return addedFileNames; + } + + @Nonnull + private Set<String> getModifiedFileNames() { + assert fileDependencies != null; + Set<String> modifiedFileNames = new HashSet<String>(); + + for (String javaFileName : fileDependencies.getCompiledJavaFiles()) { + if (fileNamesOnCmdLine.contains(javaFileName)) { + File javaFile = new File(javaFileName); + for (String typeName : fileDependencies.getTypeNames(javaFileName)) { + File dexFile = getDexFile(typeName); + if (!dexFile.exists() || ((javaFile.lastModified() > dexFile.lastModified()))) { + modifiedFileNames.add(javaFileName); + } + } + } + } + + return modifiedFileNames; + } + + + @Nonnull + private Set<String> getDeleteFileNames() { + assert fileDependencies != null; + Set<String> deletedFileNames = new HashSet<String>(); + + for (String javaFileName : fileDependencies.getCompiledJavaFiles()) { + if (!fileNamesOnCmdLine.contains(javaFileName)) { + deletedFileNames.add(javaFileName); + } + } + + return deletedFileNames; + } + + @Nonnull + private File getDexFile(@Nonnull String typeName) { + return new File(options.getIncrementalFolder(), FileType.DEX.buildFileVPath( + new VPath(typeName, '/')).getPathAsString(File.separatorChar)); + } + + @Nonnull + private FileDependencies getFileDependencies(@Nonnull InputJackLibrary library) + throws CannotReadException, FileTypeDoesNotExistException { + InputVFile fileDependenciesVFile = + library.getFile(FileType.DEPENDENCIES, FileDependencies.vpath); + + + FileDependencies fileDependencies = new FileDependencies(); + InputStreamReader fileReader = null; + try { + fileReader = new InputStreamReader(fileDependenciesVFile.openRead()); + fileDependencies.read(fileReader); + } catch (IOException e) { + throw new CannotReadException(fileDependenciesVFile.getLocation(), e); + } finally { + if (fileReader != null) { + try { + fileReader.close(); + } catch (IOException e) { + } + } + } + + return fileDependencies; + } + + @Nonnull + private TypeDependencies getTypeDependencies(@Nonnull InputJackLibrary library) + throws CannotReadException, FileTypeDoesNotExistException { + InputVFile typeDependenciesVFile = + library.getFile(FileType.DEPENDENCIES, TypeDependencies.vpath); + + TypeDependencies typeDependencies = new TypeDependencies(); + InputStreamReader fileReader = null; + try { + fileReader = new InputStreamReader(typeDependenciesVFile.openRead()); + typeDependencies.read(fileReader); + } catch (IOException e) { + throw new CannotReadException(typeDependenciesVFile.getLocation(), e); + } finally { + if (fileReader != null) { + try { + fileReader.close(); + } catch (IOException e) { + } + } + } + + return typeDependencies; + } + +} diff --git a/jack/src/com/android/jack/incremental/InputFilter.java b/jack/src/com/android/jack/incremental/InputFilter.java new file mode 100644 index 0000000..c41b81e --- /dev/null +++ b/jack/src/com/android/jack/incremental/InputFilter.java @@ -0,0 +1,34 @@ +/* + * 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.incremental; + +import java.io.File; +import java.util.List; + +import javax.annotation.Nonnull; + +/** + * Interface that filter some inputs of Jack. + */ +public interface InputFilter { + + @Nonnull + public List<String> getFileNamesToCompile(); + + @Nonnull + public List<File> getClasspath(); +} diff --git a/jack/src/com/android/jack/incremental/NoInputFilter.java b/jack/src/com/android/jack/incremental/NoInputFilter.java new file mode 100644 index 0000000..d8203a3 --- /dev/null +++ b/jack/src/com/android/jack/incremental/NoInputFilter.java @@ -0,0 +1,60 @@ +/* + * 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.incremental; + +import com.android.jack.Jack; +import com.android.jack.Options; +import com.android.jack.analysis.dependency.file.FileDependencies; +import com.android.jack.analysis.dependency.type.TypeDependencies; +import com.android.sched.util.codec.ImplementationName; + +import java.io.File; +import java.util.List; + +import javax.annotation.Nonnull; + +/** + * {@link InputFilter} that returns unfiltered inputs. + */ +@ImplementationName(iface = InputFilter.class, name = "no-filter") +public class NoInputFilter extends CommonFilter implements InputFilter { + + @Nonnull + private final List<String> fileNamesToCompile; + + @Nonnull + private final Options options; + + public NoInputFilter(@Nonnull Options options) { + this.options = options; + this.fileNamesToCompile = getJavaFileNamesSpecifiedOnCommandLine(options); + } + + @Override + @Nonnull + public List<String> getFileNamesToCompile() { + return fileNamesToCompile; + } + + @Override + @Nonnull + public List<File> getClasspath() { + Jack.getSession().setFileDependencies(new FileDependencies()); + Jack.getSession().setTypeDependencies(new TypeDependencies()); + return options.getClasspath(); + } +} diff --git a/jack/src/com/android/jack/ir/ast/JSession.java b/jack/src/com/android/jack/ir/ast/JSession.java index 83abb3b..0032093 100644 --- a/jack/src/com/android/jack/ir/ast/JSession.java +++ b/jack/src/com/android/jack/ir/ast/JSession.java @@ -21,6 +21,7 @@ import com.google.common.collect.Iterators; import com.android.jack.Jack; import com.android.jack.analysis.dependency.file.FileDependencies; import com.android.jack.analysis.dependency.type.TypeDependencies; +import com.android.jack.incremental.InputFilter; import com.android.jack.ir.JNodeInternalError; import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum; import com.android.jack.ir.sourceinfo.SourceInfo; @@ -112,6 +113,9 @@ public class JSession extends JNode { @CheckForNull private FileDependencies fileDependencies; + @CheckForNull + private InputFilter inputFilter; + public JSession() { super(SourceInfo.UNKNOWN); topLevelPackage = new JPackage("", this, null); @@ -121,6 +125,18 @@ public class JSession extends JNode { } @Nonnull + public InputFilter getInputFilter() { + assert inputFilter != null; + return inputFilter; + } + + @Nonnull + public void setInputFilter(@Nonnull InputFilter inputFilter) { + this.inputFilter = inputFilter; + } + + + @Nonnull public JNodeLookup getLookup() { return lookup; } diff --git a/jack/src/com/android/jack/library/FileType.java b/jack/src/com/android/jack/library/FileType.java index 7d62ec2..0bd0578 100644 --- a/jack/src/com/android/jack/library/FileType.java +++ b/jack/src/com/android/jack/library/FileType.java @@ -29,22 +29,22 @@ import javax.annotation.Nonnull; public enum FileType { DEX("dex", "dex", ".dex", "dex") { @Override - public void check() throws LibraryFormatException { + public void check() { } }, JAYCE("jayce", "jayce", ".jayce", "jayce") { @Override - public void check() throws LibraryFormatException { + public void check() { } }, JPP("jpp", "jpp", ".jpp", "java pre-processor") { @Override - public void check() throws LibraryFormatException { + public void check() { } }, RSC("rsc", "rsc", "", "resource") { @Override - public void check() throws LibraryFormatException { + public void check() { } }, DEPENDENCIES("dependencies", "dependencies", ".dep", "dependencies") { @@ -53,7 +53,7 @@ public enum FileType { return "dependencies"; } @Override - public void check() throws LibraryFormatException { + public void check() { } }; diff --git a/jack/tests/com/android/jack/experimental/incremental/IncrementalTestingEnvironment.java b/jack/tests/com/android/jack/experimental/incremental/IncrementalTestingEnvironment.java index e704a86..3c740a9 100644 --- a/jack/tests/com/android/jack/experimental/incremental/IncrementalTestingEnvironment.java +++ b/jack/tests/com/android/jack/experimental/incremental/IncrementalTestingEnvironment.java @@ -16,6 +16,7 @@ package com.android.jack.experimental.incremental; +import com.android.jack.Jack; import com.android.jack.Options; import com.android.jack.TestTools; import com.android.jack.backend.dex.DexFileWriter; @@ -275,6 +276,6 @@ public class IncrementalTestingEnvironment extends TestTools { addFile(sourceFolderOrSourceList, options.getEcjArguments()); options.setClasspath(classpath); options.setOutputDir(out); - JackIncremental.run(options); + Jack.run(options); } } |