diff options
41 files changed, 1542 insertions, 187 deletions
diff --git a/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java b/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java index be3414b3..13fc5bfb 100644 --- a/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java +++ b/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java @@ -27,10 +27,12 @@ import com.android.jack.test.runner.RuntimeRunnerFactory; import com.android.jack.test.util.ExecFileException; import com.android.jack.test.util.ExecuteFile; import com.android.jack.util.NamingTools; +import com.android.sched.util.stream.ByteStreamSucker; import org.junit.Assume; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -583,4 +585,33 @@ public abstract class AbstractTestTools { return rtLocation; } + public static void copyFileToDir(@Nonnull File fileToCopy, @Nonnull String relativePath, + @Nonnull File dest) throws IOException { + FileOutputStream fos = null; + FileInputStream fis = null; + try { + fis = new FileInputStream(fileToCopy); + File copiedFile = new File(dest, relativePath); + File parentDir = copiedFile.getParentFile(); + if (!parentDir.exists()) { + boolean res = parentDir.mkdirs(); + if (!res) { + throw new AssertionError(); + } + } + try { + fos = new FileOutputStream(copiedFile); + ByteStreamSucker sucker = new ByteStreamSucker(fis, fos); + sucker.suck(); + } finally { + if (fos != null) { + fos.close(); + } + } + } finally { + if (fis != null) { + fis.close(); + } + } + } } diff --git a/jack-tests/src/com/android/jack/test/toolchain/IToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/IToolchain.java index 7fa26205..fb97ef6c 100644 --- a/jack-tests/src/com/android/jack/test/toolchain/IToolchain.java +++ b/jack-tests/src/com/android/jack/test/toolchain/IToolchain.java @@ -23,7 +23,6 @@ import java.io.OutputStream; import java.util.List; import javax.annotation.Nonnull; -import javax.annotation.processing.Processor; /** @@ -69,8 +68,8 @@ public interface IToolchain { IToolchain setWithDebugInfos(boolean withDebugInfos); @Nonnull - IToolchain setAnnotationProcessorClass( - @Nonnull Class<? extends Processor> annotationProcessorClass); + IToolchain setAnnotationProcessorClasses( + @Nonnull List<String> annotationProcessorClasses); @Nonnull IToolchain setSourceLevel(@Nonnull SourceLevel sourceLevel); diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackApiV01Toolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JackApiV01Toolchain.java index 2d7f6f2b..52e4b3fa 100644 --- a/jack-tests/src/com/android/jack/test/toolchain/JackApiV01Toolchain.java +++ b/jack-tests/src/com/android/jack/test/toolchain/JackApiV01Toolchain.java @@ -16,6 +16,7 @@ package com.android.jack.test.toolchain; +import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.android.jack.Options; @@ -194,6 +195,20 @@ public class JackApiV01Toolchain extends JackApiToolchainBase { if (jarjarRules != null) { apiV01Config.setJarJarConfigFile(jarjarRules); } + apiV01Config.setProcessorOptions(annotationProcessorOptions); + + if (annotationProcessorClasses != null) { + apiV01Config.setProcessorNames(annotationProcessorClasses); + } + + + if (processorPath != null) { + List<File> fileList = new ArrayList<File>(); + for (String entry : Splitter.on(File.pathSeparatorChar).split(processorPath)) { + fileList.add(new File(entry)); + } + apiV01Config.setProcessorPath(fileList); + } } private void libToCommon(@Nonnull File... in) throws Exception { diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackBasedToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JackBasedToolchain.java index 34610eb8..a08fe713 100644 --- a/jack-tests/src/com/android/jack/test/toolchain/JackBasedToolchain.java +++ b/jack-tests/src/com/android/jack/test/toolchain/JackBasedToolchain.java @@ -23,7 +23,9 @@ import com.android.jack.test.TestsProperties; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; @@ -42,16 +44,31 @@ public abstract class JackBasedToolchain extends AndroidToolchain { LEGACY } - - @CheckForNull - protected File annotationProcessorOutDir; @Nonnull protected List<File> resImport = new ArrayList<File>(); + @Nonnull + protected final Map<String, String> annotationProcessorOptions = new HashMap<String, String>(); + @CheckForNull + protected String processorPath; @Nonnull public abstract JackBasedToolchain addProperty(@Nonnull String propertyName, @Nonnull String propertyValue); + @Nonnull + public final JackBasedToolchain addAnnotationProcessorOption(@Nonnull String propertyName, + @Nonnull String propertyValue) { + annotationProcessorOptions.put(propertyName, propertyValue); + return this; + } + + @Nonnull + public final JackBasedToolchain setAnnotationProcessorPath( + @Nonnull String processorPath) { + this.processorPath = processorPath; + return this; + } + public final JackBasedToolchain setMultiDexKind(@Nonnull MultiDexKind kind) { switch (kind) { case NATIVE: @@ -75,13 +92,6 @@ public abstract class JackBasedToolchain extends AndroidToolchain { return this; } - @Nonnull - public final JackBasedToolchain setAnnotationProcessorOutDir( - @Nonnull File annotationProcessorOutDir) { - this.annotationProcessorOutDir = annotationProcessorOutDir; - return this; - } - @Override @Nonnull public JackBasedToolchain setSourceLevel(@Nonnull SourceLevel sourceLevel) { diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackCliToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JackCliToolchain.java index 3542d03e..c68edda0 100644 --- a/jack-tests/src/com/android/jack/test/toolchain/JackCliToolchain.java +++ b/jack-tests/src/com/android/jack/test/toolchain/JackCliToolchain.java @@ -16,6 +16,8 @@ package com.android.jack.test.toolchain; +import com.google.common.base.Joiner; + import com.android.jack.Options; import com.android.jack.Options.VerbosityLevel; import com.android.jack.util.ExecuteFile; @@ -82,15 +84,6 @@ public class JackCliToolchain extends JackBasedToolchain { args.add("-g"); } - if (annotationProcessorClass != null) { - args.add("-processor"); - args.add(annotationProcessorClass.getName()); - } - if (annotationProcessorOutDir != null) { - args.add("-d"); - args.add(annotationProcessorOutDir.getAbsolutePath()); - } - AbstractTestTools.addFile(args, /* mustExist = */ false, sources); ExecuteFile exec = new ExecuteFile(args.toArray(new String[args.size()])); @@ -178,14 +171,7 @@ public class JackCliToolchain extends JackBasedToolchain { args.add("-g"); } - if (annotationProcessorClass != null) { - args.add("-processor"); - args.add(annotationProcessorClass.getName()); - } - if (annotationProcessorOutDir != null) { - args.add("-d"); - args.add(annotationProcessorOutDir.getAbsolutePath()); - } + addAnnotationProcessorArgs(args); for (File staticLib : staticLibs) { args.add("--import"); @@ -335,4 +321,21 @@ public class JackCliToolchain extends JackBasedToolchain { return this; } + private void addAnnotationProcessorArgs(@Nonnull List<String> args) { + for (Entry<String, String> entry : annotationProcessorOptions.entrySet()) { + args.add("-A"); + args.add(entry.getKey() + "=" + entry.getValue()); + } + + if (annotationProcessorClasses != null) { + args.add("--processor"); + args.add(Joiner.on(',').join(annotationProcessorClasses)); + } + + if (processorPath != null) { + args.add("--processorpath"); + args.add(processorPath); + } + } + } diff --git a/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java index f35e0538..489ef524 100644 --- a/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java +++ b/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java @@ -307,9 +307,9 @@ public class LegacyJillToolchain extends JillBasedToolchain { addSourceLevel(sourceLevel, arguments); - if (annotationProcessorClass != null) { + if (annotationProcessorClasses != null) { arguments.add("-processor"); - arguments.add(annotationProcessorClass.getName()); + arguments.add(Joiner.on(',').join(annotationProcessorClasses)); } arguments.add("-bootclasspath"); diff --git a/jack-tests/src/com/android/jack/test/toolchain/LegacyToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/LegacyToolchain.java index 809393f1..7a94bd82 100644 --- a/jack-tests/src/com/android/jack/test/toolchain/LegacyToolchain.java +++ b/jack-tests/src/com/android/jack/test/toolchain/LegacyToolchain.java @@ -16,6 +16,8 @@ package com.android.jack.test.toolchain; +import com.google.common.base.Joiner; + import com.android.dx.command.dexer.Main.Arguments; import com.android.jack.test.TestsProperties; import com.android.jack.test.util.ExecFileException; @@ -228,6 +230,11 @@ public class LegacyToolchain extends AndroidToolchain { } addSourceLevel(sourceLevel, args); + if (annotationProcessorClasses != null) { + args.add("-processor"); + args.add(Joiner.on(',').join(annotationProcessorClasses)); + } + args.add("-encoding"); args.add("utf8"); @@ -283,9 +290,9 @@ public class LegacyToolchain extends AndroidToolchain { arguments.add("-encoding"); arguments.add("utf8"); - if (annotationProcessorClass != null) { + if (annotationProcessorClasses != null) { arguments.add("-processor"); - arguments.add(annotationProcessorClass.getName()); + arguments.add(Joiner.on(',').join(annotationProcessorClasses)); } arguments.add("-bootclasspath"); diff --git a/jack-tests/src/com/android/jack/test/toolchain/Toolchain.java b/jack-tests/src/com/android/jack/test/toolchain/Toolchain.java index cf2ed55c..131b055d 100644 --- a/jack-tests/src/com/android/jack/test/toolchain/Toolchain.java +++ b/jack-tests/src/com/android/jack/test/toolchain/Toolchain.java @@ -27,7 +27,6 @@ import java.util.List; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; -import javax.annotation.processing.Processor; /** * A toolchain the ouptut of which can be redirected to user defined streams. @@ -40,7 +39,7 @@ public abstract class Toolchain implements IToolchain { protected List<File> classpath = new ArrayList<File>(); @CheckForNull - protected Class<? extends Processor> annotationProcessorClass; + protected List<String> annotationProcessorClasses; /** * Java source level. @@ -125,9 +124,9 @@ public abstract class Toolchain implements IToolchain { @Override @Nonnull - public final Toolchain setAnnotationProcessorClass( - @Nonnull Class<? extends Processor> annotationProcessorClass) { - this.annotationProcessorClass = annotationProcessorClass; + public final Toolchain setAnnotationProcessorClasses( + @Nonnull List<String> annotationProcessorClasses) { + this.annotationProcessorClasses = annotationProcessorClasses; return this; } diff --git a/jack-tests/tests/com/android/jack/annotation/processor/AnnotationProcessorTests.java b/jack-tests/tests/com/android/jack/annotation/processor/AnnotationProcessorTests.java new file mode 100644 index 00000000..2d4be981 --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/processor/AnnotationProcessorTests.java @@ -0,0 +1,292 @@ +/* + * 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.annotation.processor; + +import com.android.jack.annotation.processor.sample.processors.ResourceAnnotationProcessor; +import com.android.jack.annotation.processor.sample.processors.SourceAnnotationProcessor; +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.test.toolchain.AbstractTestTools; +import com.android.jack.test.toolchain.JackBasedToolchain; +import com.android.jack.test.toolchain.LegacyToolchain; +import com.android.jack.test.toolchain.Toolchain; +import com.android.sched.util.config.ConfigurationException; +import com.android.sched.util.config.GatherConfigBuilder; +import com.android.sched.util.config.ThreadConfig; +import com.android.sched.util.file.Directory; +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.vfs.DirectFS; +import com.android.sched.vfs.VPath; + +import junit.framework.Assert; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.util.Collections; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +public class AnnotationProcessorTests { + + @Nonnull + private static final File ANNOTATED_DIR = AbstractTestTools.getTestRootDir( + "com.android.jack.annotation.processor.sample.annotated"); + @Nonnull + private static final File ANNOTATIONS_DIR = AbstractTestTools.getTestRootDir( + "com.android.jack.annotation.processor.sample.annotations"); + + @CheckForNull + private static File noConfigProcessors; + + @CheckForNull + private static File autoProcessors; + + @BeforeClass + public static void setupClass() throws ConfigurationException { + // required for creating InputJackLibrary when running tests on cli + ThreadConfig.setConfig(new GatherConfigBuilder().build()); + noConfigProcessors = null; + autoProcessors = null; + } + + @Nonnull + private static File getNoConfigProcessors() throws Exception { + if (noConfigProcessors == null) { + Toolchain toolchain = AbstractTestTools.getReferenceToolchain(); + File processorsDir = AbstractTestTools.createTempDir(); + File processorsSrcDir = AbstractTestTools.getTestRootDir( + "com.android.jack.annotation.processor.sample.processors"); + toolchain.srcToLib(processorsDir, /*zipFiles=*/ false, + ANNOTATIONS_DIR, + processorsSrcDir + ); + noConfigProcessors = processorsDir; + } + return noConfigProcessors; + } + + @Nonnull + private static File getAutoProcessors() throws Exception { + if (autoProcessors == null) { + Toolchain toolchain = AbstractTestTools.getReferenceToolchain(); + File processorsDir = AbstractTestTools.createTempDir(); + File processorsSrcDir = AbstractTestTools.getTestRootDir( + "com.android.jack.annotation.processor.sample.processors"); + toolchain.srcToLib(processorsDir, /*zipFiles=*/ false, + ANNOTATIONS_DIR, + processorsSrcDir + ); + AbstractTestTools.copyFileToDir(new File(processorsSrcDir, + "javax.annotation.processing.Processor"), + "META-INF/services/javax.annotation.processing.Processor", processorsDir); + autoProcessors = processorsDir; + } + return autoProcessors; + } + + @Test + public void compileWithAnnotationProcessorAuto_classpath() throws Exception { + JackBasedToolchain jack = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + File jackOut = AbstractTestTools.createTempDir(); + File processors = getAutoProcessors(); + jack.addToClasspath(jack.getDefaultBootClasspath()); + jack.addToClasspath(processors); + jack.srcToLib(jackOut, /*zipFiles=*/false, + ANNOTATIONS_DIR, + ANNOTATED_DIR + ); + InputJackLibrary libOut = + JackLibraryFactory.getInputLibrary( + new DirectFS(new Directory(jackOut.getPath(), /* hooks = */ null, + Existence.MUST_EXIST, Permission.READ, ChangePermission.NOCHANGE), + Permission.READ)); + libOut.getFile(FileType.RSC, new VPath("rscGeneratedFile0", '/')); + libOut.getFile(FileType.JAYCE, new VPath("Annotated2Duplicated", '/')); + } + + @Test + public void compileWithAnnotationProcessorAuto_processorPath() throws Exception { + JackBasedToolchain jack = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + File jackOut = AbstractTestTools.createTempDir(); + File processors = getAutoProcessors(); + jack.setAnnotationProcessorPath(processors.getPath()); + jack.addToClasspath(jack.getDefaultBootClasspath()); + jack.srcToLib(jackOut, + /*zipFiles=*/false, + ANNOTATIONS_DIR, + ANNOTATED_DIR + ); + InputJackLibrary libOut = + JackLibraryFactory.getInputLibrary( + new DirectFS(new Directory(jackOut.getPath(), /* hooks = */ null, + Existence.MUST_EXIST, Permission.READ, ChangePermission.NOCHANGE), + Permission.READ)); + libOut.getFile(FileType.RSC, new VPath("rscGeneratedFile0", '/')); + libOut.getFile(FileType.JAYCE, new VPath("Annotated2Duplicated", '/')); + } + + @Test + public void compileWithAnnotationProcessorNoAuto_processorPath() throws Exception { + JackBasedToolchain jack = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + File jackOut = AbstractTestTools.createTempDir(); + File processors = getNoConfigProcessors(); + jack.setAnnotationProcessorPath(processors.getPath()); + jack.setAnnotationProcessorClasses( + Collections.singletonList(ResourceAnnotationProcessor.class.getName())); + jack.addToClasspath(jack.getDefaultBootClasspath()); + jack.srcToLib(jackOut, + /*zipFiles=*/false, + ANNOTATIONS_DIR, + ANNOTATED_DIR + ); + InputJackLibrary libOut = + JackLibraryFactory.getInputLibrary( + new DirectFS(new Directory(jackOut.getPath(), /* hooks = */ null, + Existence.MUST_EXIST, Permission.READ, ChangePermission.NOCHANGE), + Permission.READ)); + libOut.getFile(FileType.RSC, new VPath("rscGeneratedFile0", '/')); + try { + libOut.getFile(FileType.JAYCE, new VPath("Annotated2Duplicated", '/')); + Assert.fail(); + } catch (FileTypeDoesNotExistException e) { + // expected + } + } + + @Test + public void compileWithAnnotationProcessorNoAutoNoClasses_processorPath() throws Exception { + JackBasedToolchain jack = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + File jackOut = AbstractTestTools.createTempDir(); + File processors = getNoConfigProcessors(); + jack.setAnnotationProcessorPath(processors.getPath()); + jack.addToClasspath(jack.getDefaultBootClasspath()); + jack.srcToLib(jackOut, + /*zipFiles=*/false, + ANNOTATIONS_DIR, + ANNOTATED_DIR + ); + InputJackLibrary libOut = + JackLibraryFactory.getInputLibrary( + new DirectFS(new Directory(jackOut.getPath(), /* hooks = */ null, + Existence.MUST_EXIST, Permission.READ, ChangePermission.NOCHANGE), + Permission.READ)); + try { + libOut.getFile(FileType.RSC, new VPath("rscGeneratedFile0", '/')); + Assert.fail(); + } catch (FileTypeDoesNotExistException e) { + // expected + } + try { + libOut.getFile(FileType.JAYCE, new VPath("Annotated2Duplicated", '/')); + Assert.fail(); + } catch (FileTypeDoesNotExistException e) { + // expected + } + } + + @Test + public void compileWithAnnotationProcessorNoAuto_classpath() throws Exception { + JackBasedToolchain jack = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + File jackOut = AbstractTestTools.createTempDir(); + File processors = getNoConfigProcessors(); + jack.setAnnotationProcessorClasses( + Collections.singletonList(SourceAnnotationProcessor.class.getName())); + jack.addToClasspath(jack.getDefaultBootClasspath()); + jack.addToClasspath(processors); + jack.srcToLib(jackOut, /*zipFiles=*/false, + ANNOTATIONS_DIR, + ANNOTATED_DIR + ); + InputJackLibrary libOut = + JackLibraryFactory.getInputLibrary( + new DirectFS(new Directory(jackOut.getPath(), /* hooks = */ null, + Existence.MUST_EXIST, Permission.READ, ChangePermission.NOCHANGE), + Permission.READ)); + try { + libOut.getFile(FileType.RSC, new VPath("rscGeneratedFile0", '/')); + Assert.fail(); + } catch (FileTypeDoesNotExistException e) { + // expected + } + libOut.getFile(FileType.JAYCE, new VPath("Annotated2Duplicated", '/')); + } + + @Test + public void compileWithAnnotationProcessorReuseClassOut() throws Exception { + File classesOut = AbstractTestTools.createTempDir(); + File jackOut = AbstractTestTools.createTempDir(); + File processors = getAutoProcessors(); + { + JackBasedToolchain jack = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + jack.setAnnotationProcessorPath(processors.getPath()); + jack.addResource(classesOut); + jack.addToClasspath(jack.getDefaultBootClasspath()); + jack.srcToLib(jackOut, + /*zipFiles=*/false, + ANNOTATIONS_DIR, + ANNOTATED_DIR + ); + } + { + JackBasedToolchain jack = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + jack.setAnnotationProcessorPath(processors.getPath()); + jack.addResource(classesOut); + jack.addToClasspath(jack.getDefaultBootClasspath()); + jack.srcToLib(jackOut, + /*zipFiles=*/false, + ANNOTATIONS_DIR, + ANNOTATED_DIR + ); + } + InputJackLibrary libOut = + JackLibraryFactory.getInputLibrary( + new DirectFS(new Directory(jackOut.getPath(), /* hooks = */ null, + Existence.MUST_EXIST, Permission.READ, ChangePermission.NOCHANGE), + Permission.READ)); + libOut.getFile(FileType.RSC, new VPath("rscGeneratedFile0", '/')); + libOut.getFile(FileType.RSC, new VPath("rscGeneratedFile1", '/')); + libOut.getFile(FileType.JAYCE, new VPath("Annotated2Duplicated", '/')); + } + + @Test + public void compileWithAnnotationProcessorOption() throws Exception { + JackBasedToolchain jack = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); + File jackOut = AbstractTestTools.createTempDir(); + File processors = getAutoProcessors(); + jack.addAnnotationProcessorOption( + SourceAnnotationProcessor.SOURCE_ANNOTATION_PROCESSOR_SUFFIX, "WithOption"); + jack.addToClasspath(jack.getDefaultBootClasspath()); + jack.addToClasspath(processors); + jack.srcToLib(jackOut, /*zipFiles=*/false, + ANNOTATIONS_DIR, + ANNOTATED_DIR + ); + InputJackLibrary libOut = + JackLibraryFactory.getInputLibrary( + new DirectFS(new Directory(jackOut.getPath(), /* hooks = */ null, + Existence.MUST_EXIST, Permission.READ, ChangePermission.NOCHANGE), + Permission.READ)); + libOut.getFile(FileType.JAYCE, new VPath("Annotated2WithOption", '/')); + } +} diff --git a/jack-tests/tests/com/android/jack/annotation/processor/sample/annotated/Annotated1.java b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotated/Annotated1.java new file mode 100644 index 00000000..1e5fed68 --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotated/Annotated1.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 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.annotation.processor.sample.annotated; + +import com.android.jack.annotation.processor.sample.annotations.ResourceAnnotationTest; + +@ResourceAnnotationTest +public class Annotated1 { + +} diff --git a/jack-tests/tests/com/android/jack/annotation/processor/sample/annotated/Annotated2.java b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotated/Annotated2.java new file mode 100644 index 00000000..c5c00aa8 --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotated/Annotated2.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 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.annotation.processor.sample.annotated; + +import com.android.jack.annotation.processor.sample.annotations.SourceAnnotationTest; + +@SourceAnnotationTest +public class Annotated2 { + +} diff --git a/jack-tests/tests/com/android/jack/annotation/processor/sample/annotated/Annotated3.java b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotated/Annotated3.java new file mode 100644 index 00000000..bd47b551 --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotated/Annotated3.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2015 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.annotation.processor.sample.annotated; + +public class Annotated3 { + +} diff --git a/jack-tests/tests/com/android/jack/annotation/processor/sample/annotations/ResourceAnnotationTest.java b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotations/ResourceAnnotationTest.java new file mode 100644 index 00000000..48b88ef6 --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotations/ResourceAnnotationTest.java @@ -0,0 +1,29 @@ +/* + * 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.annotation.processor.sample.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates a class or interface with an annotation used by tests. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ResourceAnnotationTest {} diff --git a/jack-tests/tests/com/android/jack/annotation/processor/sample/annotations/SourceAnnotationTest.java b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotations/SourceAnnotationTest.java new file mode 100644 index 00000000..54b69579 --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/processor/sample/annotations/SourceAnnotationTest.java @@ -0,0 +1,29 @@ +/* + * 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.annotation.processor.sample.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates a class or interface with an annotation used by tests to generate source code. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface SourceAnnotationTest {} diff --git a/jack-tests/tests/com/android/jack/annotation/processor/sample/processors/ResourceAnnotationProcessor.java b/jack-tests/tests/com/android/jack/annotation/processor/sample/processors/ResourceAnnotationProcessor.java new file mode 100644 index 00000000..d6c61d7b --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/processor/sample/processors/ResourceAnnotationProcessor.java @@ -0,0 +1,142 @@ +/* + * 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.annotation.processor.sample.processors; + +import com.android.jack.annotation.processor.sample.annotations.ResourceAnnotationTest; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +/** + * Annotation processor generating a dedicated resource file called {@code rscGeneratedFile}. + */ +@SupportedSourceVersion(SourceVersion.RELEASE_6) +@SupportedAnnotationTypes("*") +public class ResourceAnnotationProcessor extends AbstractProcessor { + @CheckForNull + private ProcessingEnvironment env; + + @Nonnull + private final List<String> data = new ArrayList<String>(); + + @Nonnull + public final static String FILENAME = "rscGeneratedFile"; + + @Override + public synchronized void init(@Nonnull ProcessingEnvironment env) { + this.env = env; + try { + env.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", FILENAME); + } catch (IOException e) { + // Best effort + } + } + + @Override + public boolean process(@Nonnull Set<? extends TypeElement> annotations, + @Nonnull RoundEnvironment roundEnv) { + assert env != null; + env.getMessager().printMessage(Kind.NOTE, + "ResourceAnnotationProcessor.process"); + if (roundEnv.processingOver()) { + try { + assert env != null; + + int count = 0; + while (true) { + try { + FileObject fo = env.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", FILENAME + count); + fo.openInputStream().close(); + count ++; + } catch (IOException e) { + break; + } + } + OutputStream os = env.getFiler() + .createResource(StandardLocation.CLASS_OUTPUT, "", FILENAME + count) + .openOutputStream(); + Writer writer = new OutputStreamWriter(os); + try { + for (String val : data) { + writer.write(val); + writer.write("\n"); + } + } finally { + writer.close(); + } + } catch (IOException e) { + env.getMessager().printMessage(Kind.ERROR, + "Can not write resource file for '" + FILENAME + "': " + e.getMessage()); + } + } else { + // + // @ResourceAnnotationTest + // + + for (Element element : getElementsAnnotatedWith(roundEnv, ResourceAnnotationTest.class)) { + assert data != null; + + TypeMirror type = element.asType(); + + if (type.getKind() == TypeKind.DECLARED) { + data.add(ResourceAnnotationTest.class.getCanonicalName()); + assert env != null; + data.add(env.getElementUtils().getBinaryName((TypeElement) element).toString()); + + } + } + } + + return false; + } + + @Nonnull + private Set<? extends Element> getElementsAnnotatedWith(@Nonnull RoundEnvironment roundEnv, + @Nonnull Class<? extends Annotation> cls) { + assert env != null; + + String name = cls.getCanonicalName(); + assert name != null; + + TypeElement element = env.getElementUtils().getTypeElement(name); + assert element != null; + + return roundEnv.getElementsAnnotatedWith(element); + } +} diff --git a/jack-tests/tests/com/android/jack/annotation/processor/sample/processors/SourceAnnotationProcessor.java b/jack-tests/tests/com/android/jack/annotation/processor/sample/processors/SourceAnnotationProcessor.java new file mode 100644 index 00000000..7ef7a7ac --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/processor/sample/processors/SourceAnnotationProcessor.java @@ -0,0 +1,110 @@ +/* + * 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.annotation.processor.sample.processors; + +import com.android.jack.annotation.processor.sample.annotations.SourceAnnotationTest; + +import java.io.IOException; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.util.Set; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic.Kind; +import javax.tools.JavaFileObject; + +/** + * Annotation processor to generate a new source file. + */ +@SupportedSourceVersion(SourceVersion.RELEASE_6) +@SupportedAnnotationTypes("*") +public class SourceAnnotationProcessor extends AbstractProcessor { + @Nonnull + public static final String SOURCE_ANNOTATION_PROCESSOR_SUFFIX = + "SourceAnnotationProcessor.suffix"; + @CheckForNull + private ProcessingEnvironment env; + @Nonnull + private String suffix; + + @Override + public synchronized void init(@Nonnull ProcessingEnvironment env) { + this.env = env; + suffix = env.getOptions().get(SOURCE_ANNOTATION_PROCESSOR_SUFFIX); + if (suffix == null) { + suffix = "Duplicated"; + } + } + + @Override + public boolean process(@Nonnull Set<? extends TypeElement> annotations, @Nonnull RoundEnvironment roundEnv) { + assert env != null; + env.getMessager().printMessage(Kind.NOTE, + "SourceAnnotationProcessor.process"); + if (!roundEnv.processingOver()) { + + // + // @SourceAnnotationTest + // + + for (Element element : getElementsAnnotatedWith(roundEnv, SourceAnnotationTest.class)) { + TypeMirror type = element.asType(); + + if (type.getKind() == TypeKind.DECLARED) { + TypeElement classElement = (TypeElement) element; + try { + assert env != null; + JavaFileObject jfo = + env.getFiler().createSourceFile(classElement.getQualifiedName() + suffix); + Writer writer = jfo.openWriter(); + writer.write("public class " + classElement.getSimpleName() + suffix + " {}"); + writer.close(); + } catch (IOException e) { + throw new AssertionError(e); + } + } + } + } + + return false; + } + + @Nonnull + private Set<? extends Element> getElementsAnnotatedWith(@Nonnull RoundEnvironment roundEnv, + @Nonnull Class<? extends Annotation> cls) { + assert env != null; + + String name = cls.getCanonicalName(); + assert name != null; + + TypeElement element = env.getElementUtils().getTypeElement(name); + assert element != null; + + return roundEnv.getElementsAnnotatedWith(element); + } +} diff --git a/jack-tests/tests/com/android/jack/annotation/processor/sample/processors/javax.annotation.processing.Processor b/jack-tests/tests/com/android/jack/annotation/processor/sample/processors/javax.annotation.processing.Processor new file mode 100644 index 00000000..975dacba --- /dev/null +++ b/jack-tests/tests/com/android/jack/annotation/processor/sample/processors/javax.annotation.processing.Processor @@ -0,0 +1,2 @@ +com.android.jack.annotation.processor.sample.processors.SourceAnnotationProcessor +com.android.jack.annotation.processor.sample.processors.ResourceAnnotationProcessor diff --git a/jack-tests/tests/com/android/jack/classpath/ClasspathTests.java b/jack-tests/tests/com/android/jack/classpath/ClasspathTests.java index 3aaa72d0..9fb98b63 100644 --- a/jack-tests/tests/com/android/jack/classpath/ClasspathTests.java +++ b/jack-tests/tests/com/android/jack/classpath/ClasspathTests.java @@ -52,6 +52,7 @@ public class ClasspathTests { .srcToLib(libOut, false, new File(AbstractTestTools.getTestRootDir("com.android.jack.classpath.test001"), "lib")); + toolchain = AbstractTestTools.getCandidateToolchain(); File testOut = AbstractTestTools.createTempDir(); toolchain.addToClasspath(toolchain.getDefaultBootClasspath()) .addToClasspath(libOut) diff --git a/jack-tests/tests/com/android/jack/error/AnnotationProcessorErrorTest.java b/jack-tests/tests/com/android/jack/error/AnnotationProcessorErrorTest.java index a5e310c3..99b58f20 100644 --- a/jack-tests/tests/com/android/jack/error/AnnotationProcessorErrorTest.java +++ b/jack-tests/tests/com/android/jack/error/AnnotationProcessorErrorTest.java @@ -18,14 +18,17 @@ package com.android.jack.error; import com.google.common.io.Files; -import com.android.jack.JackUserException; +import com.android.jack.JackAbortException; import com.android.jack.Main; +import com.android.jack.backend.jayce.JayceFileImporter.CollisionPolicy; import com.android.jack.errorhandling.annotationprocessor.ResourceAnnotationProcessor; import com.android.jack.errorhandling.annotationprocessor.ResourceAnnotationTest; import com.android.jack.errorhandling.annotationprocessor.SourceAnnotationProcessor; import com.android.jack.errorhandling.annotationprocessor.SourceAnnotationTest; import com.android.jack.errorhandling.annotationprocessor.SourceErrorAnnotationTest; import com.android.jack.frontend.FrontendCompilationException; +import com.android.jack.resource.ResourceImporter; +import com.android.jack.resource.ResourceReadingException; import com.android.jack.test.TestsProperties; import com.android.jack.test.helper.ErrorTestHelper; import com.android.jack.test.toolchain.AbstractTestTools; @@ -43,6 +46,7 @@ import java.io.File; import java.io.FileReader; import java.io.LineNumberReader; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; @@ -58,32 +62,6 @@ public class AnnotationProcessorErrorTest { } /** - * Checks that compilation fails correctly when annotation processor is called without specifying - * output folder. - */ - @Test - public void testAnnotationProcessorError001() throws Exception { - ErrorTestHelper te = new ErrorTestHelper(); - - AbstractTestTools.createFile(te.getSourceFolder(),"jack.incremental", "A.java", - "package jack.incremental; \n"+ - "public class A {} \n"); - - JackApiToolchainBase jackApiToolchain = - AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); - jackApiToolchain.setAnnotationProcessorClass(ResourceAnnotationProcessor.class); - - try { - jackApiToolchain.addToClasspath(jackApiToolchain.getDefaultBootClasspath()) - .srcToExe(te.getOutputDexFolder(), /* zipFile = */ false, te.getSourceFolder()); - Assert.fail(); - } catch (JackUserException e) { - // Failure is ok since output for annotation processor is not specify. - Assert.assertTrue(e.getMessage().contains("Unknown location")); - } - } - - /** * Checks that compilation succeed when running annotation processor to generate resource file. */ @Test @@ -101,12 +79,14 @@ public class AnnotationProcessorErrorTest { runAnnotProcBuildingResource(te); - JackApiToolchainBase jackApiToolchain = - AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); - jackApiToolchain.setAnnotationProcessorClass(ResourceAnnotationProcessor.class); - jackApiToolchain.setAnnotationProcessorOutDir(te.getTestingFolder()); + JackApiToolchainBase jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); + jackApiToolchain.setAnnotationProcessorClasses( + Collections.singletonList(ResourceAnnotationProcessor.class.getName())); ByteArrayOutputStream errOut = new ByteArrayOutputStream(); jackApiToolchain.setErrorStream(errOut); + jackApiToolchain.addResource(te.getOutputDexFolder()); + jackApiToolchain.addProperty(ResourceImporter.RESOURCE_COLLISION_POLICY.getName(), + CollisionPolicy.FAIL.name()); try { @@ -115,10 +95,9 @@ public class AnnotationProcessorErrorTest { .srcToExe(te.getOutputDexFolder(), /* zipFile = */ false, te.getSourceFolder()); Assert.fail(); - } catch (FrontendCompilationException e) { + } catch (JackAbortException e) { + Assert.assertTrue(e.getCause() instanceof ResourceReadingException); // Failure is ok since created file already exists - } finally { - Assert.assertTrue(errOut.toString().contains("Resource already created")); } } @@ -139,10 +118,9 @@ public class AnnotationProcessorErrorTest { + "public class A {}\n"); - JackApiToolchainBase jackApiToolchain = - AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); - jackApiToolchain.setAnnotationProcessorClass(SourceAnnotationProcessor.class); - jackApiToolchain.setAnnotationProcessorOutDir(te.getTestingFolder()); + JackApiToolchainBase jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); + jackApiToolchain.setAnnotationProcessorClasses( + Collections.singletonList(SourceAnnotationProcessor.class.getName())); ByteArrayOutputStream errOut = new ByteArrayOutputStream(); jackApiToolchain.setErrorStream(errOut); @@ -173,10 +151,9 @@ public class AnnotationProcessorErrorTest { + "@" + SourceAnnotationTest.class.getSimpleName() + "\n" + "public class A {}\n"); - JackApiToolchainBase jackApiToolchain = - AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); - jackApiToolchain.setAnnotationProcessorClass(SourceAnnotationProcessor.class); - jackApiToolchain.setAnnotationProcessorOutDir(te.getTestingFolder()); + JackApiToolchainBase jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); + jackApiToolchain.setAnnotationProcessorClasses( + Collections.singletonList(SourceAnnotationProcessor.class.getName())); File dexOutput = te.getOutputDexFolder(); jackApiToolchain.addToClasspath(jackApiToolchain.getDefaultBootClasspath()) @@ -202,16 +179,15 @@ public class AnnotationProcessorErrorTest { + "@" + ResourceAnnotationTest.class.getSimpleName() + "\n" + "public class A {}\n"); - JackApiToolchainBase jackApiToolchain = - AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); - jackApiToolchain.setAnnotationProcessorClass(ResourceAnnotationProcessor.class); - jackApiToolchain.setAnnotationProcessorOutDir(te.getTestingFolder()); + JackApiToolchainBase jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); + jackApiToolchain.setAnnotationProcessorClasses( + Collections.singletonList(ResourceAnnotationProcessor.class.getName())); jackApiToolchain.addToClasspath(jackApiToolchain.getDefaultBootClasspath()) .addToClasspath(te.getJackFolder()) .srcToExe(te.getOutputDexFolder(), /* zipFile = */ false, te.getSourceFolder()); - File discoverFile = new File(te.getTestingFolder(), ResourceAnnotationProcessor.FILENAME); + File discoverFile = new File(te.getOutputDexFolder(), ResourceAnnotationProcessor.FILENAME); Assert.assertTrue(discoverFile.exists()); LineNumberReader lnr = new LineNumberReader(new FileReader(discoverFile)); Assert.assertEquals(ResourceAnnotationTest.class.getName(), lnr.readLine()); diff --git a/jack-tests/tests/com/android/jack/error/FileAccessErrorTest.java b/jack-tests/tests/com/android/jack/error/FileAccessErrorTest.java index ff2cef23..259bf6a0 100644 --- a/jack-tests/tests/com/android/jack/error/FileAccessErrorTest.java +++ b/jack-tests/tests/com/android/jack/error/FileAccessErrorTest.java @@ -184,6 +184,7 @@ public class FileAccessErrorTest { "package jack.incremental; \n"+ "public class B extends A {} \n"); + jackApiToolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); ByteArrayOutputStream errOut = new ByteArrayOutputStream(); try { for (File jackFile : AbstractTestTools.getFiles(te.getJackFolder(), JayceFileImporter.JAYCE_FILE_EXTENSION)) { diff --git a/jack-tests/tests/com/android/jack/fileconflict/FileConflictTests.java b/jack-tests/tests/com/android/jack/fileconflict/FileConflictTests.java index 61be8c25..1725143c 100644 --- a/jack-tests/tests/com/android/jack/fileconflict/FileConflictTests.java +++ b/jack-tests/tests/com/android/jack/fileconflict/FileConflictTests.java @@ -36,6 +36,7 @@ import com.android.jack.test.toolchain.JackCliToolchain; import com.android.jack.test.toolchain.LegacyJillToolchain; import com.android.sched.util.stream.ByteStreamSucker; + import junit.framework.Assert; import org.junit.BeforeClass; @@ -407,14 +408,14 @@ public class FileConflictTests { // create Jack dirs to import File jackImport1 = AbstractTestTools.createTempDir(); - copyFileToDir(libProperties, libPropName, jackImport1); - copyFileToDir(myClass1, jackFilePath, jackImport1); - copyFileToDir(myClass1Dex, dexFilePath, jackImport1); - copyFileToDir(digestFile, FileType.DEX.getPrefix() + "/digest", jackImport1); + AbstractTestTools.copyFileToDir(libProperties, libPropName, jackImport1); + AbstractTestTools.copyFileToDir(myClass1, jackFilePath, jackImport1); + AbstractTestTools.copyFileToDir(myClass1Dex, dexFilePath, jackImport1); + AbstractTestTools.copyFileToDir(digestFile, FileType.DEX.getPrefix() + "/digest", jackImport1); // copy Jack file to output dir - copyFileToDir(myClass1, jackFilePath, jackOutput); - copyFileToDir(myClass1Dex, dexFilePath, jackOutput); + AbstractTestTools.copyFileToDir(myClass1, jackFilePath, jackOutput); + AbstractTestTools.copyFileToDir(myClass1Dex, dexFilePath, jackOutput); toolchain = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class, exclude); toolchain.addProguardFlags(new File(testSrcDir, "proguard.flags")); @@ -454,13 +455,13 @@ public class FileConflictTests { File jackImport1 = AbstractTestTools.createTempDir(); String resourcePath = "com/android/jack/fileconflict/test003/jack/Resource"; File resource = new File(testSrcDir, "Resource"); - copyFileToDir(libProperties, libPropName, jackImport1); - copyFileToDir(myClass1, jackFilePath, jackImport1); - copyFileToDir(resource, resourcePath, jackImport1); + AbstractTestTools.copyFileToDir(libProperties, libPropName, jackImport1); + AbstractTestTools.copyFileToDir(myClass1, jackFilePath, jackImport1); + AbstractTestTools.copyFileToDir(resource, resourcePath, jackImport1); // copy a different resource to output dir with the same name File resource2 = new File(testSrcDir, "Resource2"); - copyFileToDir(resource2, resourcePath, jackOutput); + AbstractTestTools.copyFileToDir(resource2, resourcePath, jackOutput); // run Jack on Jack dir toolchain = AbstractTestTools.getCandidateToolchain(JackBasedToolchain.class); @@ -503,13 +504,13 @@ public class FileConflictTests { // create Jack dirs to import File jackImport1 = AbstractTestTools.createTempDir(); File resource = new File(testSrcDir, "MyClass.txt"); - copyFileToDir(libProperties, libPropName, jackImport1); - copyFileToDir(myClass1, jackFilePath, jackImport1); - copyFileToDir(resource, "com/android/jack/fileconflict/test004/jack/MyClass.txt", jackImport1); + AbstractTestTools.copyFileToDir(libProperties, libPropName, jackImport1); + AbstractTestTools.copyFileToDir(myClass1, jackFilePath, jackImport1); + AbstractTestTools.copyFileToDir(resource, "com/android/jack/fileconflict/test004/jack/MyClass.txt", jackImport1); // copy a different resource to output dir with the same name File resource2 = new File(testSrcDir, "a.txt"); - copyFileToDir(resource2, "pcz/nbqfcvq/wnpx/svyrpcbsyvph/hrgh004/wnpx/ZmPyngg.txt", jackOutput); + AbstractTestTools.copyFileToDir(resource2, "pcz/nbqfcvq/wnpx/svyrpcbsyvph/hrgh004/wnpx/ZmPyngg.txt", jackOutput); // run Jack on Jack dir toolchain = AbstractTestTools.getCandidateToolchain(JackApiToolchainBase.class); @@ -564,18 +565,18 @@ public class FileConflictTests { // create Jack dirs to import File jackImport1 = AbstractTestTools.createTempDir(); File jackImport2 = AbstractTestTools.createTempDir(); - copyFileToDir(digestFile, FileType.DEX.getPrefix() + "/digest", jackImport1); - copyFileToDir(digestFile, FileType.DEX.getPrefix() + "/digest", jackImport2); - copyFileToDir(libProperties, libPropName, jackImport1); - copyFileToDir(myClass1, JACK_FILE_PATH_1, jackImport1); - copyFileToDir(myClass1Dex, DEX_FILE_PATH_1, jackImport1); - copyFileToDir(myClass2, JACK_FILE_PATH_2, jackImport1); - copyFileToDir(myClass2Dex, DEX_FILE_PATH_2, jackImport1); - copyFileToDir(libProperties, libPropName, jackImport2); - copyFileToDir(myClass1, JACK_FILE_PATH_1, jackImport2); - copyFileToDir(myClass1Dex, DEX_FILE_PATH_1, jackImport2); - copyFileToDir(myClass3, JACK_FILE_PATH_3, jackImport2); - copyFileToDir(myClass3Dex, DEX_FILE_PATH_3, jackImport2); + AbstractTestTools.copyFileToDir(digestFile, FileType.DEX.getPrefix() + "/digest", jackImport1); + AbstractTestTools.copyFileToDir(digestFile, FileType.DEX.getPrefix() + "/digest", jackImport2); + AbstractTestTools.copyFileToDir(libProperties, libPropName, jackImport1); + AbstractTestTools.copyFileToDir(myClass1, JACK_FILE_PATH_1, jackImport1); + AbstractTestTools.copyFileToDir(myClass1Dex, DEX_FILE_PATH_1, jackImport1); + AbstractTestTools.copyFileToDir(myClass2, JACK_FILE_PATH_2, jackImport1); + AbstractTestTools.copyFileToDir(myClass2Dex, DEX_FILE_PATH_2, jackImport1); + AbstractTestTools.copyFileToDir(libProperties, libPropName, jackImport2); + AbstractTestTools.copyFileToDir(myClass1, JACK_FILE_PATH_1, jackImport2); + AbstractTestTools.copyFileToDir(myClass1Dex, DEX_FILE_PATH_1, jackImport2); + AbstractTestTools.copyFileToDir(myClass3, JACK_FILE_PATH_3, jackImport2); + AbstractTestTools.copyFileToDir(myClass3Dex, DEX_FILE_PATH_3, jackImport2); // run Jack on Jack dirs toolchain = getToolchain(isApiTest); @@ -608,6 +609,7 @@ public class FileConflictTests { /* zipFiles = */ false, lib1); + toolchain = getToolchain(isApiTest); File jackImport2 = AbstractTestTools.createTempDir(); File lib2 = new File(TEST002_DIR, "lib2"); toolchain = getToolchain(isApiTest); @@ -640,36 +642,6 @@ public class FileConflictTests { return jackOutput; } - private void copyFileToDir(@Nonnull File fileToCopy, @Nonnull String relativePath, - @Nonnull File dir) throws IOException { - FileOutputStream fos = null; - FileInputStream fis = null; - try { - fis = new FileInputStream(fileToCopy); - File copiedFile = new File(dir, relativePath); - File parentDir = copiedFile.getParentFile(); - if (!parentDir.exists()) { - boolean res = parentDir.mkdirs(); - if (!res) { - throw new AssertionError(); - } - } - try { - fos = new FileOutputStream(copiedFile); - ByteStreamSucker sucker = new ByteStreamSucker(fis, fos); - sucker.suck(); - } finally { - if (fos != null) { - fos.close(); - } - } - } finally { - if (fis != null) { - fis.close(); - } - } - } - private void checkResourceContent(@Nonnull File dir, @Nonnull String path, @Nonnull String expectedContent) throws IOException { assert dir.isDirectory(); diff --git a/jack/src/com/android/jack/Options.java b/jack/src/com/android/jack/Options.java index 7f4e2685..af8597e1 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 0aa5d0e8..c98622b1 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 11fe2fb9..03a133bd 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 424a7c32..32e10819 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 1b31fd86..ccbf4b3e 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 428e2d0a..d7b8f2ea 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 a44efb7b..0b6556cb 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 00000000..16d91eb4 --- /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 381c6b8c..b2cf05c4 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) { diff --git a/jack/tests/com/android/jack/TestTools.java b/jack/tests/com/android/jack/TestTools.java index 99aec20e..cfc9dc89 100644 --- a/jack/tests/com/android/jack/TestTools.java +++ b/jack/tests/com/android/jack/TestTools.java @@ -194,10 +194,10 @@ public class TestTools { "jack-tests/prebuilts/core-stubs-mini.jack").getAbsolutePath(); } - @CheckForNull + @Nonnull public static String getClasspathAsString(@CheckForNull File[] files) { if (files == null || files.length == 0) { - return null; + return ""; } StringBuilder classpathStr = new StringBuilder(); for (int i = 0; i < files.length; i++) { @@ -209,7 +209,7 @@ public class TestTools { return classpathStr.toString(); } - @CheckForNull + @Nonnull public static String getClasspathsAsString( @CheckForNull File[] bootClasspath, @CheckForNull File[] classpath) { if (bootClasspath == null) { @@ -222,12 +222,12 @@ public class TestTools { } } - @CheckForNull + @Nonnull private static String concatClasspathStrings( - @CheckForNull String bootclasspath, @CheckForNull String classpath) { + @Nonnull String bootclasspath, @Nonnull String classpath) { if (bootclasspath == null || bootclasspath.isEmpty()) { return classpath; - } else if (classpath == null || classpath.isEmpty()) { + } else if (classpath.isEmpty()) { return bootclasspath; } else { StringBuilder classpathStr = new StringBuilder(bootclasspath); diff --git a/jack/tests/com/android/jack/errorhandling/annotationprocessor/ResourceAnnotationProcessor.java b/jack/tests/com/android/jack/errorhandling/annotationprocessor/ResourceAnnotationProcessor.java index 2aca1bba..ae2eba0e 100644 --- a/jack/tests/com/android/jack/errorhandling/annotationprocessor/ResourceAnnotationProcessor.java +++ b/jack/tests/com/android/jack/errorhandling/annotationprocessor/ResourceAnnotationProcessor.java @@ -38,6 +38,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; import javax.tools.StandardLocation; /** @@ -71,8 +72,9 @@ public class ResourceAnnotationProcessor extends AbstractProcessor { try { assert env != null; - OutputStream os = env.getFiler() - .createResource(StandardLocation.CLASS_OUTPUT, "", FILENAME) + FileObject resource = env.getFiler() + .createResource(StandardLocation.CLASS_OUTPUT, "", FILENAME); + OutputStream os = resource .openOutputStream(); Writer writer = new OutputStreamWriter(os); try { diff --git a/sched/src/com/android/sched/util/RunnableHooks.java b/sched/src/com/android/sched/util/RunnableHooks.java index c7143ab4..07430fca 100644 --- a/sched/src/com/android/sched/util/RunnableHooks.java +++ b/sched/src/com/android/sched/util/RunnableHooks.java @@ -43,15 +43,6 @@ public class RunnableHooks { @Nonnull private final List<Runnable> hooks = new ArrayList<Runnable>(); - public RunnableHooks() { - Runtime.getRuntime().addShutdownHook(new Thread(){ - @Override - public void run() { - runHooks(); - } - }); - } - public synchronized void addHook(@Nonnull Runnable hook) { assert !hooks.contains(hook); @@ -65,7 +56,6 @@ public class RunnableHooks { } public synchronized void runHooks() { - for (Runnable hook : Lists.reverse(hooks)) { hook.run(); } diff --git a/sched/src/com/android/sched/util/codec/InputFileOrDirectoryCodec.java b/sched/src/com/android/sched/util/codec/InputFileOrDirectoryCodec.java new file mode 100644 index 00000000..b735e50a --- /dev/null +++ b/sched/src/com/android/sched/util/codec/InputFileOrDirectoryCodec.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 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.sched.util.codec; + +import com.android.sched.util.config.ConfigurationError; +import com.android.sched.util.file.Directory; +import com.android.sched.util.file.FileOrDirectory; +import com.android.sched.util.file.FileOrDirectory.Existence; +import com.android.sched.util.file.FileOrDirectory.Permission; +import com.android.sched.util.file.InputStreamFile; + +import java.io.File; +import java.io.IOException; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * This {@link StringCodec} is used to create an instance of {@link FileOrDirectory}. + */ +public class InputFileOrDirectoryCodec extends FileOrDirCodec<FileOrDirectory> { + + public InputFileOrDirectoryCodec() { + super(Existence.MUST_EXIST, Permission.READ); + } + + @Override + @Nonnull + public FileOrDirectory parseString(@Nonnull CodecContext context, @Nonnull String string) { + try { + return checkString(context, string); + } catch (ParsingException e) { + throw new ConfigurationError(e); + } + } + + @Override + @CheckForNull + public FileOrDirectory checkString(@Nonnull CodecContext context, @Nonnull String string) + throws ParsingException { + File file = new File(string); + try { + if (file.isFile()) { + return new InputStreamFile(string); + } else { + return new Directory(string, context.getRunnableHooks(), existence, permissions, change); + } + } catch (IOException e) { + throw new ParsingException(e.getMessage(), e); + } + + } + + @Override + @Nonnull + public String getUsage() { + return "a path to a file or directory (" + getUsageDetails() + ")"; + } + + @Override + @Nonnull + public String formatValue(@Nonnull FileOrDirectory data) { + return data.getPath(); + } + + @Override + public void checkValue(@Nonnull CodecContext context, @Nonnull FileOrDirectory data) { + } + +} diff --git a/sched/src/com/android/sched/util/codec/ListCodec.java b/sched/src/com/android/sched/util/codec/ListCodec.java index 25f5c758..45445730 100644 --- a/sched/src/com/android/sched/util/codec/ListCodec.java +++ b/sched/src/com/android/sched/util/codec/ListCodec.java @@ -56,13 +56,18 @@ public class ListCodec<T> implements StringCodec<List<T>> { this.var = var; } - public ListCodec<T> setSeperator(@Nonnull String separator) { + public ListCodec<T> setSeparator(@Nonnull String separator) { this.separator = separator; this.regexp = Pattern.quote(separator); return this; } + @Nonnull + public String getSeparator() { + return separator; + } + public ListCodec<T> setMin(@Nonnegative int min) { assert min < max; assert min >= 0; diff --git a/sched/src/com/android/sched/util/codec/ListParsingException.java b/sched/src/com/android/sched/util/codec/ListParsingException.java index 83d988d8..048b07b5 100644 --- a/sched/src/com/android/sched/util/codec/ListParsingException.java +++ b/sched/src/com/android/sched/util/codec/ListParsingException.java @@ -30,7 +30,7 @@ public class ListParsingException extends ParsingException { private final int index; public ListParsingException(@Nonnegative int index, @Nonnull String message) { - super(message); + super("element #" + (index + 1) + ": " + message); this.index = index; } diff --git a/sched/src/com/android/sched/util/codec/PairCodec.java b/sched/src/com/android/sched/util/codec/PairCodec.java new file mode 100644 index 00000000..4032bf89 --- /dev/null +++ b/sched/src/com/android/sched/util/codec/PairCodec.java @@ -0,0 +1,167 @@ +/* + * 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.sched.util.codec; + +import com.android.sched.util.config.ChainedException.ChainedExceptionBuilder; +import com.android.sched.util.config.ConfigurationError; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * This {@link StringCodec} is used to create an instance of {@link Entry} + */ +// STOPSHIP see https://android-review.googlesource.com/#/c/120553/17 +public class PairCodec<T, U> implements StringCodec<Entry<T, U>>{ + @Nonnull + private final StringCodec<T> keyParser; + @Nonnull + private final StringCodec<U> valueParser; + + @Nonnull + private String separator = "="; + + public PairCodec(@Nonnull StringCodec<T> keyParser, @Nonnull StringCodec<U> valueParser) { + this.keyParser = keyParser; + this.valueParser = valueParser; + } + + @Nonnull + public PairCodec<T, U> on(@Nonnull String separator) { + this.separator = separator; + + return this; + } + + @Nonnull + public String getSeparator() { + return separator; + } + + @Override + @Nonnull + public Entry<T, U> parseString(@Nonnull CodecContext context, + @Nonnull String string) { + int endKey = string.indexOf(separator); + if (endKey == -1) { + throw new ConfigurationError("Missing '" + + separator + "' in '" + string + "'"); + } + String key = string.substring(0, endKey); + String valueString = string.substring(endKey + separator.length()); + return new AbstractMap.SimpleEntry<T, U>(keyParser.parseString(context, key), + valueParser.parseString(context, valueString)); + } + + @Override + @CheckForNull + public Entry<T, U> checkString(@Nonnull CodecContext context, + @Nonnull String string) + throws ParsingException { + + ChainedExceptionBuilder<ParsingException> exceptions = + new ChainedExceptionBuilder<ParsingException>(); + + T keyElement = null; + U valueElement = null; + int endKey = string.indexOf(separator); + if (endKey == -1) { + exceptions.appendException(new ParsingException("Missing '" + + separator + "' in '" + string + "'")); + } else { + String key = string.substring(0, endKey); + String valueString = string.substring(endKey + separator.length()); + try { + keyElement = keyParser.checkString(context, key); + } catch (ParsingException e) { + exceptions.appendException(e); + } + try { + valueElement = valueParser.checkString(context, valueString); + } catch (ParsingException e) { + exceptions.appendException(e); + } + } + + exceptions.throwIfNecessary(); + // If one element is null, do not compute the pair + if (keyElement == null || valueElement == null) { + return null; + } else { + return new AbstractMap.SimpleEntry<T, U>(keyElement, valueElement); + } + } + + @Override + @Nonnull + public String getUsage() { + StringBuilder sb = new StringBuilder(); + sb.append("<key>" + separator + "<value>"); + sb.append(" where <key> is "); + sb.append(keyParser.getUsage()); + sb.append(" and where <value> is "); + sb.append(valueParser.getUsage()); + + return sb.toString(); + } + + @Override + @Nonnull + public List<com.android.sched.util.codec.Parser.ValueDescription> getValueDescriptions() { + List<com.android.sched.util.codec.Parser.ValueDescription> list = + new ArrayList<Parser.ValueDescription>(); + list.addAll(keyParser.getValueDescriptions()); + list.addAll(valueParser.getValueDescriptions()); + return list; + } + + @Override + @Nonnull + public String formatValue(@Nonnull Entry<T, U> data) { + StringBuilder sb = new StringBuilder(); + + sb.append(keyParser.formatValue(data.getKey())); + sb.append(separator); + sb.append(valueParser.formatValue(data.getValue())); + return sb.toString(); + } + + @Override + public void checkValue(@Nonnull CodecContext context, @Nonnull Entry<T, U> data) + throws CheckingException { + ChainedExceptionBuilder<CheckingException> exceptions = + new ChainedExceptionBuilder<CheckingException>(); + try { + keyParser.checkValue(context, data.getKey()); + } catch (CheckingException e) { + exceptions.appendException(e); + } + try { + valueParser.checkValue(context, data.getValue()); + } catch (CheckingException e) { + exceptions.appendException(e); + } + + exceptions.throwIfNecessary(); + } + +} diff --git a/sched/src/com/android/sched/util/codec/PairListToMapCodecConverter.java b/sched/src/com/android/sched/util/codec/PairListToMapCodecConverter.java new file mode 100644 index 00000000..b9c9e868 --- /dev/null +++ b/sched/src/com/android/sched/util/codec/PairListToMapCodecConverter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 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.sched.util.codec; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nonnull; + +/** + * A {@link ConvertCodec} allowing to convert list of {@link Entry} into a map. + */ +public class PairListToMapCodecConverter<T, U> extends + ConvertCodec<List<Entry<T, U>>, Map<T, U>> { + public PairListToMapCodecConverter(StringCodec<List<Entry<T, U>>> codec) { + super(codec); + } + + @Override + @Nonnull + public Map<T, U> parseString(@Nonnull CodecContext context, + @Nonnull String string) { + List<Entry<T, U>> list = codec.parseString(context, string); + Map<T, U> map = new HashMap<T, U>(Math.round(list.size() / 0.75f), + 0.75f); + for (Entry<T, U> entry : list) { + map.put(entry.getKey(), entry.getValue()); + } + return map; + } + + @Override + @Nonnull + public String formatValue(@Nonnull Map<T, U> data) { + return codec.formatValue(new ArrayList<Map.Entry<T, U>>(data.entrySet())); + } + + @Override + public void checkValue(@Nonnull CodecContext context, @Nonnull Map<T, U> data) + throws CheckingException { + codec.checkValue(context, new ArrayList<Map.Entry<T, U>>(data.entrySet())); + } +}
\ No newline at end of file diff --git a/sched/src/com/android/sched/util/codec/StringValueCodec.java b/sched/src/com/android/sched/util/codec/StringValueCodec.java new file mode 100644 index 00000000..19e13be0 --- /dev/null +++ b/sched/src/com/android/sched/util/codec/StringValueCodec.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.sched.util.codec; + +import java.util.Collections; +import java.util.List; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * A {@link StringCodec} performing no check nor conversion, just providing a usage. + */ +public class StringValueCodec implements StringCodec<String> { + @Nonnull + private final String usage; + + public StringValueCodec(@Nonnull String usage) { + this.usage = usage; + } + + @Override + @Nonnull + public String parseString(@Nonnull CodecContext context, @Nonnull String string) { + return string; + } + + @Override + @CheckForNull + public String checkString(@Nonnull CodecContext context, @Nonnull String string) { + return string; + } + + @Override + @Nonnull + public String getUsage() { + return usage; + } + + @Override + @Nonnull + public List<ValueDescription> getValueDescriptions() { + return Collections.<ValueDescription> emptyList(); + } + + @Override + @Nonnull + public String formatValue(@Nonnull String data) { + return data; + } + + @Override + public void checkValue(@Nonnull CodecContext context, @Nonnull String data) { + } +}
\ No newline at end of file diff --git a/sched/src/com/android/sched/util/config/id/EnumPropertyId.java b/sched/src/com/android/sched/util/config/id/EnumPropertyId.java index c209e3ca..d98baef0 100644 --- a/sched/src/com/android/sched/util/config/id/EnumPropertyId.java +++ b/sched/src/com/android/sched/util/config/id/EnumPropertyId.java @@ -20,6 +20,7 @@ import com.android.sched.util.codec.EnumCodec; import com.android.sched.util.config.ConfigChecker; import com.android.sched.util.config.MissingPropertyException; import com.android.sched.util.config.PropertyIdException; +import com.android.sched.util.config.category.Category; import com.android.sched.util.config.expression.BooleanExpression; import javax.annotation.Nonnull; @@ -85,6 +86,13 @@ public class EnumPropertyId<T extends Enum<T>> extends PropertyId<T> { return this; } + @Override + @Nonnull + public EnumPropertyId<T> withCategory(@Nonnull Category category) { + super.withCategory(category); + return this; + } + @Nonnull public BooleanExpression is(@Nonnull final T enumValue) { return new BooleanExpression() { diff --git a/sched/src/com/android/sched/util/config/id/ListPropertyId.java b/sched/src/com/android/sched/util/config/id/ListPropertyId.java index 075fa007..fe4e032e 100644 --- a/sched/src/com/android/sched/util/config/id/ListPropertyId.java +++ b/sched/src/com/android/sched/util/config/id/ListPropertyId.java @@ -80,7 +80,7 @@ public class ListPropertyId<T> extends PropertyId<List<T>> { @Nonnull public ListPropertyId<T> on(@Nonnull String separator) { - getCodec().setSeperator(separator); + getCodec().setSeparator(separator); return this; } |