diff options
Diffstat (limited to 'jack/src/com/android')
9 files changed, 307 insertions, 24 deletions
diff --git a/jack/src/com/android/jack/Options.java b/jack/src/com/android/jack/Options.java index 7f4e268..af8597e 100644 --- a/jack/src/com/android/jack/Options.java +++ b/jack/src/com/android/jack/Options.java @@ -42,11 +42,18 @@ import com.android.jack.shrob.obfuscation.annotation.ParameterAnnotationRemover; import com.android.jack.shrob.seed.SeedPrinter; import com.android.jack.shrob.spec.Flags; import com.android.jack.transformations.renamepackage.PackageRenamer; +import com.android.jack.util.ClassNameCodec; import com.android.jack.util.filter.Filter; import com.android.sched.util.RunnableHooks; import com.android.sched.util.codec.DirectDirOutputVFSCodec; import com.android.sched.util.codec.DirectFSCodec; +import com.android.sched.util.codec.DirectoryCodec; +import com.android.sched.util.codec.InputFileOrDirectoryCodec; import com.android.sched.util.codec.InputStreamOrDirectoryCodec; +import com.android.sched.util.codec.ListCodec; +import com.android.sched.util.codec.PairCodec; +import com.android.sched.util.codec.PairListToMapCodecConverter; +import com.android.sched.util.codec.StringValueCodec; import com.android.sched.util.codec.ZipFSCodec; import com.android.sched.util.codec.ZipOutputVFSCodec; import com.android.sched.util.config.Config; @@ -193,6 +200,12 @@ public class Options { "jack.statistic.source", "Enable compiled files statistics").addDefaultValue( Boolean.FALSE); + @Nonnull + public static final BooleanPropertyId ANNOTATION_PROCESSOR_ENABLED = + BooleanPropertyId.create( + "jack.annotation-processor", "Enable annotation processors") + .addDefaultValue(true); + @Option(name = "--version", usage = "display version") private boolean version; @@ -210,6 +223,23 @@ public class Options { @Nonnull private final Map<String, String> properties = new HashMap<String, String>(); + @Option(name = "-A", metaVar = "<option>=<value>", + usage = "set option for annotation processors (repeatable)", + handler = MapOptionHandler.class) + @Nonnull + public final Map<String, String> annotationProcessorOption = new HashMap<String, String>(); + + @Nonnull + public static final PropertyId<Map<String, String>> ANNOTATION_PROCESSOR_OPTIONS = + PropertyId.create( + "jack.annotation-processor.properties", "Options for annotation processors", + new PairListToMapCodecConverter<String, String>(new ListCodec<Entry<String, String>>("pair", + new PairCodec<String, String>( + new StringValueCodec("annotation processor option name"), + new StringValueCodec("annotation processor option value"))))) + .addDefaultValue(""); + + @CheckForNull private final File propertiesFile = null; /** @@ -309,8 +339,70 @@ public class Options { metaVar = "<DIRECTORY>") private File tracerDir; + @Option(name = "--processor", usage = "annotation processor class names", + metaVar = "<NAME>[,<NAME>...]") + @CheckForNull + public String processor; + + @Nonnull + public static final BooleanPropertyId ANNOTATION_PROCESSOR_MANUAL = + BooleanPropertyId.create( + "jack.annotation-processor.manual", "run only specified annotation processors") + .addDefaultValue(false); + + @Nonnull + public static final ListPropertyId<String> ANNOTATION_PROCESSOR_MANUAL_LIST = + ListPropertyId.create( + "jack.annotation-processor.manual.list", + "Annotation processor class names", + "class", + new ClassNameCodec()).requiredIf(ANNOTATION_PROCESSOR_MANUAL + .getValue().isTrue()); + + @Nonnull + public static final PropertyId<Directory> ANNOTATION_PROCESSOR_SOURCE_OUTPUT_DIR = + PropertyId.create( + "jack.annotation-processor.source.output", + "Output folder for sources generated by annotation processors", + new DirectoryCodec(Existence.MUST_EXIST, Permission.WRITE | Permission.READ)); + + @Nonnull + public static final PropertyId<Directory> ANNOTATION_PROCESSOR_CLASS_OUTPUT_DIR = + PropertyId.create( + "jack.annotation-processor.class.output", + "Output folder for classes generated by annotation processors", + new DirectoryCodec(Existence.MUST_EXIST, Permission.WRITE | Permission.READ)); + + @Option(name = "--processorpath", usage = "annotation processor classpath", metaVar = "<PATH>") + @CheckForNull + public String processorPath; + + @Nonnull + public static final BooleanPropertyId ANNOTATION_PROCESSOR_PATH = + BooleanPropertyId.create( + "jack.annotation-processor.path", + "Use annotation processor classpath for annotation processor loading") + .addDefaultValue(false); + + @Nonnull + public static final ListPropertyId<FileOrDirectory> ANNOTATION_PROCESSOR_PATH_LIST = + ListPropertyId.create( + "jack.annotation-processor.path.list", + "Annotation processor classpath", + "entry", + new InputFileOrDirectoryCodec()) + .on(File.pathSeparator) + .requiredIf(ANNOTATION_PROCESSOR_PATH.getValue().isTrue()); + + @Nonnull @Option(name = "-cp", aliases = "--classpath", usage = "set classpath", metaVar = "<PATH>") - protected String classpath = null; + protected String classpath = ""; + + @Nonnull + public static final PropertyId<String> CLASSPATH = + PropertyId.create( + "jack.classpath", + "Compilation classpath", new StringValueCodec("")); // This is a trick to document @<FILE>, but it has no real link to ecjArguments @Argument(usage = "read command line from file", metaVar = "@<FILE>") @@ -324,7 +416,7 @@ public class Options { .addDefaultValue(Collections.<FileOrDirectory>emptyList()); @Nonnull - private List<String> ecjExtraArguments = new ArrayList<String>(); + private final List<String> ecjExtraArguments = new ArrayList<String>(); @Option(name = "-g", usage = "emit debug infos") private Boolean emitLocalDebugInfo; @@ -534,6 +626,21 @@ public class Options { configBuilder.setString(PackageRenamer.JARJAR_FILE, jarjarRulesFile.getPath()); } + if (processor != null) { + configBuilder.set(ANNOTATION_PROCESSOR_MANUAL, true); + configBuilder.setString(ANNOTATION_PROCESSOR_MANUAL_LIST, processor); + } + configBuilder.set(ANNOTATION_PROCESSOR_SOURCE_OUTPUT_DIR, createTempDir(hooks)); + Directory annotationProcessorOutputClasses = createTempDir(hooks); + configBuilder.set(ANNOTATION_PROCESSOR_CLASS_OUTPUT_DIR, annotationProcessorOutputClasses); + addResource(annotationProcessorOutputClasses.getFile()); + if (processorPath != null) { + configBuilder.set(ANNOTATION_PROCESSOR_PATH, true); + configBuilder.setString(ANNOTATION_PROCESSOR_PATH_LIST, processorPath); + } + + configBuilder.set(ANNOTATION_PROCESSOR_OPTIONS, annotationProcessorOption); + if (!resImport.isEmpty()) { configBuilder.setString(ResourceImporter.IMPORTED_RESOURCES, Joiner.on(File.pathSeparator).join(resImport)); @@ -672,7 +779,7 @@ public class Options { configBuilder.set(GENERATE_JACK_LIBRARY, true); configBuilder.set(LIBRARY_OUTPUT_CONTAINER_TYPE, Container.DIR); configBuilder.set(Options.LIBRARY_OUTPUT_DIR, new CachedDirectFS( - createTempDirForTypeDexFiles(hooks), Permission.READ | Permission.WRITE)); + annotationProcessorOutputClasses, Permission.READ | Permission.WRITE)); } switch (multiDexKind) { @@ -735,6 +842,8 @@ public class Options { configBuilder.popDefaultLocation(); + configBuilder.setString(CLASSPATH, classpath); + for (Entry<String, String> entry : properties.entrySet()) { configBuilder.setString(entry.getKey(), entry.getValue(), new StringLocation("-D option")); } @@ -767,6 +876,10 @@ public class Options { } ecjExtraArguments.add("-source"); ecjExtraArguments.add(config.get(Options.JAVA_SOURCE_VERSION).toString()); + + if (!config.get(Options.ANNOTATION_PROCESSOR_ENABLED).booleanValue()) { + ecjExtraArguments.add("-proc:none"); + } } // Check Jack arguments @@ -817,7 +930,7 @@ public class Options { } String libraryJars = flags.getLibraryJars(); if (libraryJars != null) { - if (classpath == null) { + if (classpath.isEmpty()) { classpath = libraryJars; } else { classpath += File.pathSeparatorChar + libraryJars; @@ -825,8 +938,12 @@ public class Options { } } - public void setClasspath(String classpath) { - this.classpath = classpath; + public void setClasspath(@CheckForNull String classpath) { + if (classpath == null) { + this.classpath = ""; + } else { + this.classpath = classpath; + } } public void setMultiDexKind(@Nonnull MultiDexKind multiDexKind) { @@ -844,9 +961,9 @@ public class Options { properties.put(propertyName, propertyValue); } - // TODO(yroussel) remove when annotation processor is implemented - public void setEcjExtraArguments(@Nonnull List<String> ecjArguments) { - this.ecjExtraArguments = ecjArguments; + public void addAnnotationProcessorOption(@Nonnull String propertyName, + @Nonnull String propertyValue) { + annotationProcessorOption.put(propertyName, propertyValue); } @Nonnull @@ -891,25 +1008,25 @@ public class Options { } @Nonnull - private static Directory createTempDirForTypeDexFiles( + private static Directory createTempDir( @Nonnull RunnableHooks hooks) { try { File tmp = Files.createTempDir(); Directory dir = new Directory(tmp.getPath(), hooks, Existence.MUST_EXIST, Permission.WRITE, ChangePermission.NOCHANGE); - hooks.addHook(new TypeDexDirDeleter(dir)); + hooks.addHook(new TempDirDeleter(dir)); return dir; } catch (IOException e) { throw new JackUserException(e); } } - private static class TypeDexDirDeleter extends Thread { + private static class TempDirDeleter implements Runnable { @Nonnull private final Directory dir; - public TypeDexDirDeleter(@Nonnull Directory dir) { + public TempDirDeleter(@Nonnull Directory dir) { this.dir = dir; } diff --git a/jack/src/com/android/jack/api/v01/impl/Api01ConfigImpl.java b/jack/src/com/android/jack/api/v01/impl/Api01ConfigImpl.java index 0aa5d0e..c98622b 100644 --- a/jack/src/com/android/jack/api/v01/impl/Api01ConfigImpl.java +++ b/jack/src/com/android/jack/api/v01/impl/Api01ConfigImpl.java @@ -46,6 +46,7 @@ import java.io.File; import java.io.OutputStream; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import javax.annotation.Nonnull; @@ -251,17 +252,19 @@ public class Api01ConfigImpl implements Api01Config { @Override public void setProcessorNames(@Nonnull List<String> processorNames) { - throw new AssertionError(); + options.processor = Joiner.on(',').join(processorNames); } @Override public void setProcessorOptions(@Nonnull Map<String, String> processorOptions) { - throw new AssertionError(); + for (Entry<String, String> entry : processorOptions.entrySet()) { + options.addAnnotationProcessorOption(entry.getKey(), entry.getValue()); + } } @Override public void setProcessorPath(@Nonnull List<File> processorPath) { - throw new AssertionError(); + options.processorPath = Joiner.on(File.pathSeparatorChar).join(processorPath); } @Override diff --git a/jack/src/com/android/jack/frontend/java/EcjLogger.java b/jack/src/com/android/jack/frontend/java/EcjLogger.java index 11fe2fb..03a133b 100644 --- a/jack/src/com/android/jack/frontend/java/EcjLogger.java +++ b/jack/src/com/android/jack/frontend/java/EcjLogger.java @@ -24,6 +24,7 @@ import org.eclipse.jdt.internal.compiler.batch.Main; import org.eclipse.jdt.internal.compiler.batch.Main.Logger; import java.io.PrintWriter; +import java.util.List; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; @@ -39,14 +40,19 @@ public class EcjLogger extends Logger { @CheckForNull private Reporter reporter; - public EcjLogger(Main main, PrintWriter out, PrintWriter err, + public EcjLogger(@Nonnull Main main, @Nonnull PrintWriter out, @Nonnull PrintWriter err, @Nonnull JackBatchCompiler jackBatchCompiler) { super(main, out, err); this.jackBatchCompiler = jackBatchCompiler; } @Override - public int logProblems(CategorizedProblem[] problems, char[] unitSource, Main currentMain) { + public int logProblems(@Nonnull CategorizedProblem[] problems, @Nonnull char[] unitSource, + @Nonnull Main currentMain) { + return report(problems, currentMain); + } + + private int report(@Nonnull CategorizedProblem[] problems, @Nonnull Main currentMain) { if (reporter == null) { // lazy because the Reporter is not yet available when the EcjLogger is instantiated. reporter = jackBatchCompiler.getReporter(); @@ -73,4 +79,12 @@ public class EcjLogger extends Logger { int globalWarningsCount, int globalTasksCount) { // Do nothing } + + @Override + public void loggingExtraProblems(@Nonnull Main currentMain) { + List<CategorizedProblem> extras = jackBatchCompiler.getExtraProblems(); + if (extras != null) { + report(extras.toArray(new CategorizedProblem[extras.size()]), currentMain); + } + } }
\ No newline at end of file diff --git a/jack/src/com/android/jack/frontend/java/JackBatchCompiler.java b/jack/src/com/android/jack/frontend/java/JackBatchCompiler.java index 424a7c3..32e1081 100644 --- a/jack/src/com/android/jack/frontend/java/JackBatchCompiler.java +++ b/jack/src/com/android/jack/frontend/java/JackBatchCompiler.java @@ -16,17 +16,25 @@ package com.android.jack.frontend.java; +import com.google.common.base.Joiner; + import com.android.jack.JackUserException; +import com.android.jack.Options; import com.android.jack.ecj.loader.jast.JAstClasspath; import com.android.jack.ir.ast.JSession; import com.android.jack.reporting.Reporter; +import com.android.sched.util.config.Config; +import com.android.sched.util.config.ThreadConfig; +import com.android.sched.util.file.FileOrDirectory; import com.android.sched.util.file.InputStreamFile; import com.android.sched.util.file.NoSuchFileException; import com.android.sched.util.file.NotFileException; import com.android.sched.util.file.WrongPermissionException; import com.android.sched.util.log.LoggerFactory; +import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CompilationProgress; +import org.eclipse.jdt.internal.compiler.apt.dispatch.BatchAnnotationProcessorManager; import org.eclipse.jdt.internal.compiler.batch.ClasspathDirectory; import org.eclipse.jdt.internal.compiler.batch.ClasspathJar; import org.eclipse.jdt.internal.compiler.batch.ClasspathLocation; @@ -39,9 +47,12 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.logging.Level; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; /** @@ -230,6 +241,12 @@ public class JackBatchCompiler extends Main { logger.printStats(); } + @SuppressWarnings("unchecked") + @CheckForNull + public List<CategorizedProblem> getExtraProblems() { + return extraProblems; + } + @Override public void configure(String[] argv) { super.configure(argv); @@ -264,4 +281,49 @@ public class JackBatchCompiler extends Main { } return cu; } + + @Override + protected void initializeAnnotationProcessorManager() { + List<String> processorArgs = new ArrayList<String>(); + Config config = ThreadConfig.getConfig(); + for (Map.Entry<String, String> entry : + config.get(Options.ANNOTATION_PROCESSOR_OPTIONS).entrySet()) { + processorArgs.add("-A" + entry.getKey() + "=" + entry.getValue()); + } + if (config.get(Options.ANNOTATION_PROCESSOR_MANUAL).booleanValue()) { + processorArgs.add("-processor"); + processorArgs.add(Joiner.on(',').join(config.get(Options.ANNOTATION_PROCESSOR_MANUAL_LIST))); + } + if (config.get(Options.ANNOTATION_PROCESSOR_PATH).booleanValue()) { + processorArgs.add("-processorpath"); + processorArgs.add(getPathString(config.get(Options.ANNOTATION_PROCESSOR_PATH_LIST))); + } + processorArgs.add("-s"); + processorArgs.add(config.get(Options.ANNOTATION_PROCESSOR_SOURCE_OUTPUT_DIR).getPath()); + processorArgs.add("-d"); + processorArgs.add(config.get(Options.ANNOTATION_PROCESSOR_CLASS_OUTPUT_DIR).getPath()); + { + processorArgs.add("-classpath"); + processorArgs.add(config.get(Options.CLASSPATH)); + } + String[] args = processorArgs.toArray(new String[processorArgs.size()]); + + BatchAnnotationProcessorManager manager = new BatchAnnotationProcessorManager(); + manager.configure(this, args); + manager.setOut(out); + this.batchCompiler.annotationProcessorManager = manager; + } + + @Nonnull + private static String getPathString(@Nonnull List<FileOrDirectory> pathList) { + StringBuilder path = new StringBuilder(); + for (Iterator<FileOrDirectory> iter = pathList.iterator(); + iter.hasNext();) { + path.append(iter.next().getPath()); + if (iter.hasNext()) { + path.append(File.pathSeparatorChar); + } + } + return path.toString(); + } } diff --git a/jack/src/com/android/jack/ir/ast/JSession.java b/jack/src/com/android/jack/ir/ast/JSession.java index 1b31fd8..ccbf4b3 100644 --- a/jack/src/com/android/jack/ir/ast/JSession.java +++ b/jack/src/com/android/jack/ir/ast/JSession.java @@ -131,7 +131,6 @@ public class JSession extends JNode { this.inputFilter = inputFilter; } - @Nonnull public JNodeLookup getLookup() { return lookup; diff --git a/jack/src/com/android/jack/reporting/CommonReporter.java b/jack/src/com/android/jack/reporting/CommonReporter.java index 428e2d0..d7b8f2e 100644 --- a/jack/src/com/android/jack/reporting/CommonReporter.java +++ b/jack/src/com/android/jack/reporting/CommonReporter.java @@ -49,7 +49,8 @@ abstract class CommonReporter implements Reporter { CategorizedProblem problem = ((EcjProblem) reportable).getProblem(); printProblem(reportable.getDefaultProblemLevel(), reportable.getMessage(), - String.valueOf(problem.getOriginatingFileName()), + problem.getOriginatingFileName() != null ? + String.valueOf(problem.getOriginatingFileName()) : null, problem.getSourceLineNumber(), -1 /* endLine */, problem.getSourceEnd(), diff --git a/jack/src/com/android/jack/resource/ResourceImporter.java b/jack/src/com/android/jack/resource/ResourceImporter.java index a44efb7..0b6556c 100644 --- a/jack/src/com/android/jack/resource/ResourceImporter.java +++ b/jack/src/com/android/jack/resource/ResourceImporter.java @@ -30,6 +30,7 @@ import com.android.sched.vfs.InputVFS; import com.android.sched.vfs.InputVFile; import com.android.sched.vfs.VPath; +import java.io.File; import java.util.Collections; import java.util.List; import java.util.logging.Level; @@ -52,7 +53,9 @@ public class ResourceImporter extends ResourceOrMetaImporter { @Nonnull public static final ListPropertyId<InputVFS> IMPORTED_RESOURCES = ListPropertyId.create( "jack.import.resource", "Resources to import", "dir", - new DirectoryInputVFSCodec()).addDefaultValue(Collections.<InputVFS>emptyList()); + new DirectoryInputVFSCodec()) + .on(File.pathSeparator) + .addDefaultValue(Collections.<InputVFS>emptyList()); @Nonnull private final CollisionPolicy resourceCollisionPolicy = diff --git a/jack/src/com/android/jack/util/ClassNameCodec.java b/jack/src/com/android/jack/util/ClassNameCodec.java new file mode 100644 index 0000000..16d91eb --- /dev/null +++ b/jack/src/com/android/jack/util/ClassNameCodec.java @@ -0,0 +1,79 @@ +/* + * 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.util; + +import com.android.sched.util.codec.CheckingException; +import com.android.sched.util.codec.CodecContext; +import com.android.sched.util.codec.ParsingException; +import com.android.sched.util.codec.StringCodec; + +import java.util.Collections; +import java.util.List; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + + +/** + * This {@link StringCodec} is used to check that the string is a valid class name + */ +public class ClassNameCodec implements StringCodec<String>{ + + @Override + @Nonnull + public String getUsage() { + return "a java class name (e.g. java.lang.Object)"; + } + + @Override + @Nonnull + public List<ValueDescription> getValueDescriptions() { + return Collections.<ValueDescription> emptyList(); + } + + @Override + @Nonnull + public String parseString(@Nonnull CodecContext context, @Nonnull String string) { + return string; + } + + @Override + @CheckForNull + public String checkString(@Nonnull CodecContext context, @Nonnull String string) + throws ParsingException { + if (!NamingTools.isClassSourceName(string)) { + throw new ParsingException( + "The value must be " + getUsage() + " but is '" + string + "'"); + } + return string; + } + + @Override + public void checkValue(@Nonnull CodecContext context, @Nonnull String data) + throws CheckingException { + if (!NamingTools.isClassSourceName(data)) { + throw new CheckingException( + "The value must be " + getUsage() + " but is '" + data + "'"); + } + } + + @Override + @Nonnull + public String formatValue(@Nonnull String name) { + return name; + } +} diff --git a/jack/src/com/android/jack/util/NamingTools.java b/jack/src/com/android/jack/util/NamingTools.java index 381c6b8..b2cf05c 100644 --- a/jack/src/com/android/jack/util/NamingTools.java +++ b/jack/src/com/android/jack/util/NamingTools.java @@ -120,16 +120,21 @@ public class NamingTools { } public static boolean isPackageBinaryName(@Nonnull String name) { - return isPackageName(name.toCharArray(), 0, name.length(), JLookup.PACKAGE_SEPARATOR, + return isClassOrPackageName(name.toCharArray(), 0, name.length(), JLookup.PACKAGE_SEPARATOR, PACKAGE_SOURCE_SEPARATOR); } public static boolean isPackageSourceName(@Nonnull String name) { - return isPackageName(name.toCharArray(), 0, name.length(), PACKAGE_SOURCE_SEPARATOR, + return isClassOrPackageName(name.toCharArray(), 0, name.length(), PACKAGE_SOURCE_SEPARATOR, JLookup.PACKAGE_SEPARATOR); } - private static boolean isPackageName( + public static boolean isClassSourceName(@Nonnull String name) { + return isClassOrPackageName(name.toCharArray(), 0, name.length(), PACKAGE_SOURCE_SEPARATOR, + JLookup.PACKAGE_SEPARATOR); + } + + private static boolean isClassOrPackageName( @Nonnull char[] buffer, @Nonnegative int pos, @Nonnegative int length, char usedSeparator, char forbiddenSeparator) { while (pos < length) { |