aboutsummaryrefslogtreecommitdiff
path: root/integration-tests
diff options
context:
space:
mode:
Diffstat (limited to 'integration-tests')
-rw-r--r--integration-tests/build.gradle.kts24
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIT.kt84
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIncrementalIT.kt62
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/Artifact.kt24
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/BuildCacheIT.kt41
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedRefsIncIT.kt69
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedSrcsIncIT.kt34
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GetSealedSubclassesIncIT.kt56
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/HmppIT.kt83
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalCPIT.kt130
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalIT.kt262
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalMultiChainIT.kt49
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalRemovalIT.kt35
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/InitPlusProviderIT.kt33
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaNestedClassIT.kt22
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaOnlyIT.kt34
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KMPImplementedIT.kt369
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KSPCmdLineOptionsIT.kt85
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KotlinConstsInJavaIT.kt42
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MapAnnotationArgumentsIT.kt40
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MultiplatformIT.kt34
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt111
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnlyResourcesFileIT.kt21
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt157
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PlaygroundIT.kt210
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PsiCacheIT.kt18
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/TemporaryTestProject.kt41
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VerboseIT.kt67
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VersionCheckIT.kt42
-rw-r--r--integration-tests/src/test/kotlin/com/google/devtools/ksp/test/fixtures/BuildResultFixture.kt34
-rw-r--r--integration-tests/src/test/resources/buildcache/settings.gradle.kts27
-rw-r--r--integration-tests/src/test/resources/cmd-options/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/cmd-options/gradle.properties0
-rw-r--r--integration-tests/src/test/resources/cmd-options/processors/build.gradle.kts23
-rw-r--r--integration-tests/src/test/resources/cmd-options/processors/src/main/kotlin/TestProcessor.kt37
-rw-r--r--integration-tests/src/test/resources/cmd-options/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/cmd-options/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/cmd-options/workload/build.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/cmd-options/workload/src/main/kotlin/com/example/A.kt4
-rw-r--r--integration-tests/src/test/resources/hmpp/build.gradle.kts12
-rw-r--r--integration-tests/src/test/resources/hmpp/gradle.properties1
-rw-r--r--integration-tests/src/test/resources/hmpp/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/hmpp/test-processor/build.gradle.kts22
-rw-r--r--integration-tests/src/test/resources/hmpp/test-processor/src/main/kotlin/EchoProcessor.kt27
-rw-r--r--integration-tests/src/test/resources/hmpp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/hmpp/workload/build.gradle.kts55
-rw-r--r--integration-tests/src/test/resources/hmpp/workload/src/commonMain/kotlin/CommonMain.kt1
-rw-r--r--integration-tests/src/test/resources/hmpp/workload/src/jsMain/kotlin/JsMain.kt1
-rw-r--r--integration-tests/src/test/resources/hmpp/workload/src/jvmJs/kotlin/JvmJs.kt1
-rw-r--r--integration-tests/src/test/resources/hmpp/workload/src/jvmLinuxX64/kotlin/JvmLinuxX64.kt1
-rw-r--r--integration-tests/src/test/resources/hmpp/workload/src/jvmMain/kotlin/JvmMain.kt1
-rw-r--r--integration-tests/src/test/resources/hmpp/workload/src/jvmOnly/kotlin/JvmOnly.kt1
-rw-r--r--integration-tests/src/test/resources/hmpp/workload/src/linuxX64Main/kotlin/LinuxX64Main.kt4
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/gradle.properties3
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l1/build.gradle.kts18
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l1/src/main/kotlin/p1/L1.kt3
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l2/build.gradle.kts17
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l2/src/main/kotlin/p1/L2.kt3
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l3/build.gradle.kts17
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l3/src/main/kotlin/p1/L3.kt3
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l4/build.gradle.kts17
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l4/src/main/kotlin/p1/L4.kt3
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l5/build.gradle.kts17
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/l5/src/main/kotlin/p1/L5.kt3
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/settings.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/validator/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/validator/src/main/kotlin/Validator.kt79
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/workload/build.gradle.kts26
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K1.kt5
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K2.kt5
-rw-r--r--integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K3.kt5
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/gradle.properties2
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/processors/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Aggregator.kt46
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/ImplGen.kt46
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Validator.kt36
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider3
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/workload/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/K1.kt2
-rw-r--r--integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/Main.kt8
-rw-r--r--integration-tests/src/test/resources/incremental-removal/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/incremental-removal/gradle.properties2
-rw-r--r--integration-tests/src/test/resources/incremental-removal/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/incremental-removal/validator/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/incremental-removal/validator/src/main/kotlin/Validator.kt48
-rw-r--r--integration-tests/src/test/resources/incremental-removal/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/incremental-removal/workload/build.gradle.kts27
-rw-r--r--integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/K1.kt4
-rw-r--r--integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/Main.kt12
-rw-r--r--integration-tests/src/test/resources/incremental/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/incremental/gradle.properties2
-rw-r--r--integration-tests/src/test/resources/incremental/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/incremental/validator/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/incremental/validator/src/main/kotlin/Validator.kt62
-rw-r--r--integration-tests/src/test/resources/incremental/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/incremental/workload/build.gradle.kts22
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J1.java3
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J2.java4
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2J.java11
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2K.java10
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/java/p2/J2.java4
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J1.java4
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J2.java4
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J3.java4
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K1.kt3
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K2.kt3
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2J.kt10
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2K.kt10
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p2/K2.kt3
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K1.kt3
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K2.kt3
-rw-r--r--integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K3.kt3
-rw-r--r--integration-tests/src/test/resources/init-plus-provider/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/init-plus-provider/provider-processor/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/kotlin/TestProcessor.kt41
-rw-r--r--integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/init-plus-provider/settings.gradle.kts22
-rw-r--r--integration-tests/src/test/resources/init-plus-provider/workload/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/init-plus-provider/workload/src/main/java/com/example/A.kt7
-rw-r--r--integration-tests/src/test/resources/java-only/test-processor/src/main/kotlin/TestProcessor.kt30
-rw-r--r--integration-tests/src/test/resources/java-only/workload/src/main/java/com/example/Foo.java4
-rw-r--r--integration-tests/src/test/resources/javaNestedClass/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/javaNestedClass/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/javaNestedClass/test-processor/build.gradle.kts25
-rw-r--r--integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/kotlin/ValidateProcessor.kt38
-rw-r--r--integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/javaNestedClass/workload/build.gradle.kts20
-rw-r--r--integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/A.kt4
-rw-r--r--integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/JavaClass.java22
-rw-r--r--integration-tests/src/test/resources/kmp/annotations/build.gradle.kts32
-rw-r--r--integration-tests/src/test/resources/kmp/annotations/src/commonMain/kotlin/com/example/MyAnnotation.kt3
-rw-r--r--integration-tests/src/test/resources/kmp/build.gradle.kts12
-rw-r--r--integration-tests/src/test/resources/kmp/gradle.properties1
-rw-r--r--integration-tests/src/test/resources/kmp/settings.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/kmp/test-processor/build.gradle.kts22
-rw-r--r--integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ErrorProcessor.kt61
-rw-r--r--integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/TestProcessor.kt74
-rw-r--r--integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ValidateProcessor.kt29
-rw-r--r--integration-tests/src/test/resources/kmp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider3
-rw-r--r--integration-tests/src/test/resources/kmp/workload-androidNative/build.gradle.kts41
-rw-r--r--integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeArm64Main/kotlin/Main.kt7
-rw-r--r--integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeX64Main/kotlin/Main.kt7
-rw-r--r--integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Bar.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Baz.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/ToBeValidated.kt9
-rw-r--r--integration-tests/src/test/resources/kmp/workload-js/build.gradle.kts30
-rw-r--r--integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Bar.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Baz.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/ToBeValidated.kt9
-rw-r--r--integration-tests/src/test/resources/kmp/workload-jvm/build.gradle.kts25
-rw-r--r--integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Bar.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Baz.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/ToBeValidated.kt9
-rw-r--r--integration-tests/src/test/resources/kmp/workload-linuxX64/build.gradle.kts40
-rw-r--r--integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Bar.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Baz.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/ToBeValidated.kt9
-rw-r--r--integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/Main.kt7
-rw-r--r--integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/ToBeRemoved.kt1
-rw-r--r--integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Test/kotlin/MyTest.kt3
-rw-r--r--integration-tests/src/test/resources/kmp/workload/build.gradle.kts66
-rw-r--r--integration-tests/src/test/resources/kmp/workload/src/androidNativeArm64Main/kotlin/Main.kt7
-rw-r--r--integration-tests/src/test/resources/kmp/workload/src/androidNativeX64Main/kotlin/Main.kt7
-rw-r--r--integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Bar.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Baz.kt5
-rw-r--r--integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/ToBeValidated.kt9
-rw-r--r--integration-tests/src/test/resources/kmp/workload/src/linuxX64Main/kotlin/Main.kt7
-rw-r--r--integration-tests/src/test/resources/kmp/workload/src/linuxX64Test/kotlin/MyTest.kt3
-rw-r--r--integration-tests/src/test/resources/kotlin-consts-in-java/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/kotlin-consts-in-java/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/kotlin/TestProcessor.kt27
-rw-r--r--integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/kotlin-consts-in-java/workload/build.gradle.kts20
-rw-r--r--integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/JavaClass.java9
-rw-r--r--integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/KotlinConsts.kt8
-rw-r--r--integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/ann/MyAnn.kt3
-rw-r--r--integration-tests/src/test/resources/map-annotation-arguments/test-processor/src/main/kotlin/TestProcessor.kt36
-rw-r--r--integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/AnnotationTest.java8
-rw-r--r--integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/JavaAnnotation.java6
-rw-r--r--integration-tests/src/test/resources/on-error/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/on-error/on-error-processor/build.gradle.kts23
-rw-r--r--integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/ErrorProcessor.kt70
-rw-r--r--integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/NormalProcessor.kt40
-rw-r--r--integration-tests/src/test/resources/on-error/on-error-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider2
-rw-r--r--integration-tests/src/test/resources/on-error/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/on-error/workload/build.gradle.kts23
-rw-r--r--integration-tests/src/test/resources/on-error/workload/src/main/kotlin/com/example/A.kt11
-rw-r--r--integration-tests/src/test/resources/only-resources-file/build.gradle.kts12
-rw-r--r--integration-tests/src/test/resources/only-resources-file/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/only-resources-file/test-processor/build.gradle.kts12
-rw-r--r--integration-tests/src/test/resources/only-resources-file/test-processor/src/main/kotlin/TestProcessor.kt24
-rw-r--r--integration-tests/src/test/resources/only-resources-file/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/only-resources-file/workload/build.gradle.kts16
-rw-r--r--integration-tests/src/test/resources/only-resources-file/workload/src/commonMain/kotlin/MyStub.kt1
-rw-r--r--integration-tests/src/test/resources/output-deps/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/output-deps/gradle.properties2
-rw-r--r--integration-tests/src/test/resources/output-deps/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/output-deps/test-processor/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt61
-rw-r--r--integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/output-deps/workload/build.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J1.java5
-rw-r--r--integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J2.java4
-rw-r--r--integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno1.kt2
-rw-r--r--integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno2.kt2
-rw-r--r--integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K1.kt4
-rw-r--r--integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K2.kt5
-rw-r--r--integration-tests/src/test/resources/playground-android-multi/application/build.gradle.kts30
-rw-r--r--integration-tests/src/test/resources/playground-android-multi/application/src/main/AndroidManifest.xml4
-rw-r--r--integration-tests/src/test/resources/playground-android-multi/application/src/main/java/com/example/application/Foo.kt3
-rw-r--r--integration-tests/src/test/resources/playground-android-multi/build.gradle.kts27
-rw-r--r--integration-tests/src/test/resources/playground-android-multi/gradle.properties1
-rw-r--r--integration-tests/src/test/resources/playground-android-multi/settings.gradle.kts26
-rw-r--r--integration-tests/src/test/resources/playground-android-multi/test-processor/build.gradle.kts25
-rw-r--r--integration-tests/src/test/resources/playground-android-multi/workload/build.gradle.kts35
-rw-r--r--integration-tests/src/test/resources/playground-android-multi/workload/src/main/AndroidManifest.xml4
-rw-r--r--integration-tests/src/test/resources/playground-android/build.gradle.kts20
-rw-r--r--integration-tests/src/test/resources/playground-android/gradle.properties1
-rw-r--r--integration-tests/src/test/resources/playground-android/settings.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/playground-android/test-processor/build.gradle.kts25
-rw-r--r--integration-tests/src/test/resources/playground-android/workload/build.gradle.kts45
-rw-r--r--integration-tests/src/test/resources/playground-android/workload/proguard-rules.pro1
-rw-r--r--integration-tests/src/test/resources/playground-android/workload/src/main/AndroidManifest.xml4
-rw-r--r--integration-tests/src/test/resources/playground-mpp/build.gradle.kts12
-rw-r--r--integration-tests/src/test/resources/playground-mpp/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/playground-mpp/test-processor/build.gradle.kts22
-rw-r--r--integration-tests/src/test/resources/playground-mpp/workload/build.gradle.kts39
-rw-r--r--integration-tests/src/test/resources/playground/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/playground/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/playground/test-processor/build.gradle.kts25
-rw-r--r--integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/Builder.kt3
-rw-r--r--integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/BuilderProcessor.kt104
-rw-r--r--integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/RewriteProcessor.kt30
-rw-r--r--integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/TestProcessor.kt283
-rw-r--r--integration-tests/src/test/resources/playground/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider2
-rw-r--r--integration-tests/src/test/resources/playground/workload/G.kt6
-rw-r--r--integration-tests/src/test/resources/playground/workload/build.gradle.kts36
-rw-r--r--integration-tests/src/test/resources/playground/workload/src/main/java/com/example/A.kt16
-rw-r--r--integration-tests/src/test/resources/playground/workload/src/main/java/com/example/AClass.kt17
-rw-r--r--integration-tests/src/test/resources/playground/workload/src/main/java/com/example/BClass.java14
-rw-r--r--integration-tests/src/test/resources/psi-cache/test-processor/src/main/kotlin/TestProcessor.kt40
-rw-r--r--integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Bar.kt6
-rw-r--r--integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Baz.java6
-rw-r--r--integration-tests/src/test/resources/refs-gen/test-processor/src/main/kotlin/TestProcessor.kt48
-rw-r--r--integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Bar.kt3
-rw-r--r--integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Baz.kt5
-rw-r--r--integration-tests/src/test/resources/sealed-subclasses/test-processor/src/main/kotlin/TestProcessor.kt28
-rw-r--r--integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl1.kt3
-rw-r--r--integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl2.kt3
-rw-r--r--integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Sealed.kt3
-rw-r--r--integration-tests/src/test/resources/srcs-gen/test-processor/src/main/kotlin/TestProcessor.kt54
-rw-r--r--integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Bar.kt3
-rw-r--r--integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Baz.kt3
-rw-r--r--integration-tests/src/test/resources/test-processor/build.gradle.kts8
-rw-r--r--integration-tests/src/test/resources/test-processor/settings.gradle.kts19
-rw-r--r--integration-tests/src/test/resources/test-processor/test-processor/build.gradle.kts24
-rw-r--r--integration-tests/src/test/resources/test-processor/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider1
-rw-r--r--integration-tests/src/test/resources/test-processor/workload/build.gradle.kts20
263 files changed, 6180 insertions, 0 deletions
diff --git a/integration-tests/build.gradle.kts b/integration-tests/build.gradle.kts
new file mode 100644
index 00000000..305592c4
--- /dev/null
+++ b/integration-tests/build.gradle.kts
@@ -0,0 +1,24 @@
+val junitVersion: String by project
+val kotlinBaseVersion: String by project
+val agpBaseVersion: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+dependencies {
+ testImplementation("junit:junit:$junitVersion")
+ testImplementation(gradleTestKit())
+ testImplementation("org.jetbrains.kotlin:kotlin-compiler:$kotlinBaseVersion")
+}
+
+tasks.named<Test>("test") {
+ systemProperty("kotlinVersion", kotlinBaseVersion)
+ systemProperty("kspVersion", version)
+ systemProperty("agpVersion", agpBaseVersion)
+ systemProperty("testRepo", File(rootProject.buildDir, "repos/test").absolutePath)
+ dependsOn(":api:publishAllPublicationsToTestRepository")
+ dependsOn(":gradle-plugin:publishAllPublicationsToTestRepository")
+ dependsOn(":symbol-processing:publishAllPublicationsToTestRepository")
+ dependsOn(":symbol-processing-cmdline:publishAllPublicationsToTestRepository")
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIT.kt
new file mode 100644
index 00000000..7c485212
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIT.kt
@@ -0,0 +1,84 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class AndroidIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("playground-android", "playground")
+
+ @Test
+ fun testPlaygroundAndroid() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ // Disabling configuration cache. See https://github.com/google/ksp/issues/299 for details
+ gradleRunner.withArguments(
+ "clean", "build", "minifyReleaseWithR8", "--configuration-cache-problems=warn", "--info", "--stacktrace"
+ ).build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:build")?.outcome)
+ val mergedConfiguration = File(project.root, "workload/build/outputs/mapping/release/configuration.txt")
+ assert(mergedConfiguration.exists()) {
+ "Merged configuration file not found!\n${printDirectoryTree(project.root)}"
+ }
+ val configurationText = mergedConfiguration.readText()
+ assert("-keep class com.example.AClassBuilder { *; }" in configurationText) {
+ "Merged configuration did not contain generated proguard rules!\n$configurationText"
+ }
+ }
+ }
+}
+
+/**
+ * Pretty print the directory tree and its file names.
+ *
+ * @param folder
+ * must be a folder.
+ * @return
+ */
+fun printDirectoryTree(folder: File): String? {
+ require(folder.isDirectory) { "folder is not a Directory" }
+ val indent = 0
+ val sb = StringBuilder()
+ printDirectoryTree(folder, indent, sb)
+ return sb.toString()
+}
+
+private fun printDirectoryTree(
+ folder: File,
+ indent: Int,
+ sb: StringBuilder
+) {
+ require(folder.isDirectory) { "folder is not a Directory" }
+ sb.append(getIndentString(indent))
+ sb.append("+--")
+ sb.append(folder.name)
+ sb.append("/")
+ sb.append("\n")
+ for (file in folder.listFiles()) {
+ if (file.isDirectory) {
+ printDirectoryTree(file, indent + 1, sb)
+ } else {
+ printFile(file, indent + 1, sb)
+ }
+ }
+}
+
+private fun printFile(file: File, indent: Int, sb: StringBuilder) {
+ sb.append(getIndentString(indent))
+ sb.append("+--")
+ sb.append(file.name)
+ sb.append("\n")
+}
+
+private fun getIndentString(indent: Int): String? {
+ val sb = StringBuilder()
+ for (i in 0 until indent) {
+ sb.append("| ")
+ }
+ return sb.toString()
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIncrementalIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIncrementalIT.kt
new file mode 100644
index 00000000..3cd01c14
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIncrementalIT.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 Google LLC
+ * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ *
+ * 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.google.devtools.ksp.test
+
+import com.google.devtools.ksp.test.fixtures.BuildResultFixture
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class AndroidIncrementalIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("playground-android-multi", "playground")
+
+ @Test
+ fun testPlaygroundAndroid() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments(
+ "clean", ":application:compileDebugKotlin", "--configuration-cache-problems=warn", "--debug", "--stacktrace"
+ ).build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:compileDebugKotlin")?.outcome)
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":application:compileDebugKotlin")?.outcome)
+ }
+
+ project.root.resolve("workload/src/main/java/com/example/A.kt").also {
+ it.appendText(
+ """
+
+ class Unused
+ """.trimIndent()
+ )
+ }
+
+ gradleRunner.withArguments(
+ ":application:compileDebugKotlin", "--configuration-cache-problems=warn", "--debug", "--stacktrace"
+ ).build().let { result ->
+ Assert.assertEquals(
+ setOf("workload/src/main/java/com/example/A.kt".replace('/', File.separatorChar)),
+ BuildResultFixture(result).compiledKotlinSources,
+ )
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/Artifact.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/Artifact.kt
new file mode 100644
index 00000000..0b09a07a
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/Artifact.kt
@@ -0,0 +1,24 @@
+import org.junit.*
+import java.io.File
+import java.util.zip.ZipFile
+
+// A snapshot of the digest of output jar.
+class Artifact(file: File) {
+ private fun getCRCs(file: File): Map<String, Long> {
+ Assert.assertTrue(file.exists())
+ return ZipFile(file).use {
+ it.entries().asSequence().map {
+ it.name to it.crc
+ }.toMap()
+ }
+ }
+
+ val crcs: Map<String, Long> = getCRCs(file)
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is Artifact)
+ return false
+
+ return crcs == other.crcs
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/BuildCacheIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/BuildCacheIT.kt
new file mode 100644
index 00000000..c1c0c498
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/BuildCacheIT.kt
@@ -0,0 +1,41 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class BuildCacheIT {
+ @Rule
+ @JvmField
+ val project1: TemporaryTestProject = TemporaryTestProject("buildcache", "playground")
+
+ @Rule
+ @JvmField
+ val project2: TemporaryTestProject = TemporaryTestProject("buildcache", "playground")
+
+ @Test
+ fun testBuildCache() {
+ val buildCacheDir = File(project1.root, "build-cache").absolutePath.replace(File.separatorChar, '/')
+ File(project1.root, "gradle.properties").appendText("\nbuildCacheDir=$buildCacheDir")
+ File(project2.root, "gradle.properties").appendText("\nbuildCacheDir=$buildCacheDir")
+
+ GradleRunner.create().withProjectDir(project1.root).withArguments(
+ "--build-cache",
+ ":workload:clean",
+ "build"
+ ).build().let {
+ Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload:kspKotlin")?.outcome)
+ }
+
+ GradleRunner.create().withProjectDir(project2.root).withArguments(
+ "--build-cache",
+ ":workload:clean",
+ "build"
+ ).build().let {
+ Assert.assertEquals(TaskOutcome.FROM_CACHE, it.task(":workload:kspKotlin")?.outcome)
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedRefsIncIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedRefsIncIT.kt
new file mode 100644
index 00000000..30a8f01c
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedRefsIncIT.kt
@@ -0,0 +1,69 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class GeneratedRefsIncIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("refs-gen", "test-processor")
+
+ @Test
+ fun testGeneratedRefsInc() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ val expected = listOf(
+ "w: [ksp] 1: [File: Bar.kt, File: Baz.kt]",
+ "w: [ksp] 2: [File: Foo.kt]",
+ "w: [ksp] 3: [File: Goo.kt]"
+ )
+
+ val expectedBar = listOf(
+ "w: [ksp] 1: [File: Bar.kt]",
+ "w: [ksp] 2: [File: Foo.kt]",
+ "w: [ksp] 3: [File: Goo.kt]"
+ )
+
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expected, outputs)
+ }
+
+ File(project.root, "workload/src/main/kotlin/com/example/Baz.kt").appendText("\n\n")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expected, outputs)
+ }
+
+ // Baz doesn't depend on Bar, so touching Bar won't invalidate Baz.
+ File(project.root, "workload/src/main/kotlin/com/example/Bar.kt").appendText("\n\n")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expectedBar, outputs)
+ }
+
+ // Make Baz depends on Bar; Bar will be invalidated.
+ File(project.root, "workload/src/main/kotlin/com/example/Bar.kt").appendText("\nclass List<T>\n")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expected, outputs)
+ }
+
+ // Baz depended on Bar, so Baz should be invalidated.
+ project.restore("workload/src/main/kotlin/com/example/Bar.kt")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expected, outputs)
+ }
+
+ // Baz doesn't depend on Bar, so touching Bar won't invalidate Baz.
+ File(project.root, "workload/src/main/kotlin/com/example/Bar.kt").appendText("\n\n")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expectedBar, outputs)
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedSrcsIncIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedSrcsIncIT.kt
new file mode 100644
index 00000000..df9fc89f
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedSrcsIncIT.kt
@@ -0,0 +1,34 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class GeneratedSrcsIncIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("srcs-gen", "test-processor")
+
+ @Test
+ fun testGeneratedSrcsInc() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ val expected = listOf(
+ "w: [ksp] 1: [File: Bar.kt, File: Baz.kt]",
+ "w: [ksp] 2: [File: Foo.kt]",
+ "w: [ksp] 3: [File: FooBar.kt, File: FooBaz.kt]"
+ )
+
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expected, outputs)
+ }
+ File(project.root, "workload/src/main/kotlin/com/example/Baz.kt").appendText(System.lineSeparator())
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expected, outputs)
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GetSealedSubclassesIncIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GetSealedSubclassesIncIT.kt
new file mode 100644
index 00000000..29f56108
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GetSealedSubclassesIncIT.kt
@@ -0,0 +1,56 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class GetSealedSubclassesIncIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("sealed-subclasses", "test-processor")
+
+ @Test
+ fun testGetSealedSubclassesInc() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ val expected2 = listOf(
+ "w: [ksp] Processing Impl1.kt",
+ "w: [ksp] Impl1 : []",
+ "w: [ksp] Processing Impl2.kt",
+ "w: [ksp] Impl2 : []",
+ "w: [ksp] Processing Sealed.kt",
+ "w: [ksp] Sealed : [Impl1, Impl2]",
+ )
+
+ val expected3 = listOf(
+ "w: [ksp] Processing Impl1.kt",
+ "w: [ksp] Impl1 : []",
+ "w: [ksp] Processing Impl2.kt",
+ "w: [ksp] Impl2 : []",
+ "w: [ksp] Processing Impl3.kt",
+ "w: [ksp] Impl3 : []",
+ "w: [ksp] Processing Sealed.kt",
+ "w: [ksp] Sealed : [Impl1, Impl2, Impl3]",
+ )
+
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expected2, outputs)
+ }
+
+ File(project.root, "workload/src/main/kotlin/com/example/Impl3.kt").appendText("package com.example\n\n")
+ File(project.root, "workload/src/main/kotlin/com/example/Impl3.kt").appendText("class Impl3 : Sealed()\n")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expected3, outputs)
+ }
+
+ File(project.root, "workload/src/main/kotlin/com/example/Impl3.kt").delete()
+ gradleRunner.withArguments("assemble").build().let { result ->
+ val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") }
+ Assert.assertEquals(expected2, outputs)
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/HmppIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/HmppIT.kt
new file mode 100644
index 00000000..9da354ed
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/HmppIT.kt
@@ -0,0 +1,83 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+
+class HmppIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("hmpp")
+
+ val taskToFilesTraditional = mapOf(
+ ":workload:kspCommonMainKotlinMetadata" to "w: [ksp] EchoProcessor: CommonMain",
+ ":workload:kspJvmJsKotlinMetadata" to "w: [ksp] EchoProcessor: CommonMain_JvmJs",
+ ":workload:kspJvmLinuxX64KotlinMetadata" to "w: [ksp] EchoProcessor: CommonMain_JvmLinuxX64",
+ ":workload:kspKotlinJvm" to "w: [ksp] EchoProcessor: CommonMain_JvmJs_JvmLinuxX64_JvmMain_JvmOnly",
+ ":workload:kspKotlinJs" to "w: [ksp] EchoProcessor: CommonMain_JsMain_JvmJs",
+ ":workload:kspKotlinLinuxX64" to "w: [ksp] EchoProcessor: CommonMain_JvmLinuxX64_LinuxX64Main",
+ )
+
+ @Test
+ fun testTraditional() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ taskToFilesTraditional.forEach { (task, expected) ->
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ task,
+ ).build().let { result ->
+ val logs = result.output.lines().filter { it.startsWith("w: [ksp] EchoProcessor: ") }.toSet()
+ Assert.assertTrue(expected in logs)
+ }
+ }
+ }
+
+ val taskToFilesHmpp = mapOf(
+ ":workload:kspCommonMainKotlinMetadata" to setOf(
+ "w: [ksp] EchoProcessor: CommonMain",
+ ),
+ ":workload:kspJvmJsKotlinMetadata" to setOf(
+ "w: [ksp] EchoProcessor: CommonMain",
+ "w: [ksp] EchoProcessor: (CommonMain)_JvmJs",
+ ),
+ ":workload:kspJvmLinuxX64KotlinMetadata" to setOf(
+ "w: [ksp] EchoProcessor: CommonMain",
+ "w: [ksp] EchoProcessor: (CommonMain)_JvmLinuxX64",
+ ),
+ ":workload:kspKotlinJvm" to setOf(
+ "w: [ksp] EchoProcessor: CommonMain",
+ "w: [ksp] EchoProcessor: (CommonMain)_JvmJs",
+ "w: [ksp] EchoProcessor: (CommonMain)_JvmLinuxX64",
+ "w: [ksp] EchoProcessor: ((CommonMain)_JvmJs)_((CommonMain)_JvmLinuxX64)_(CommonMain)_JvmMain_JvmOnly",
+ ),
+ ":workload:kspKotlinJs" to setOf(
+ "w: [ksp] EchoProcessor: CommonMain",
+ "w: [ksp] EchoProcessor: (CommonMain)_JvmJs",
+ "w: [ksp] EchoProcessor: ((CommonMain)_JvmJs)_(CommonMain)_JsMain",
+ ),
+ ":workload:kspKotlinLinuxX64" to setOf(
+ "w: [ksp] EchoProcessor: CommonMain",
+ "w: [ksp] EchoProcessor: (CommonMain)_JvmLinuxX64",
+ "w: [ksp] EchoProcessor: ((CommonMain)_JvmLinuxX64)_(CommonMain)_LinuxX64Main",
+ ),
+ )
+
+ @Test
+ fun testHmpp() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ taskToFilesHmpp.forEach { (task, expected) ->
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "--rerun-tasks",
+ "-Pksp.experimental.processing.model=hierarchical",
+ task,
+ ).build().let { result ->
+ val logs = result.output.lines().filter { it.startsWith("w: [ksp] EchoProcessor: ") }.toSet()
+ Assert.assertTrue(logs == expected)
+ }
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalCPIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalCPIT.kt
new file mode 100644
index 00000000..cf07dd66
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalCPIT.kt
@@ -0,0 +1,130 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class IncrementalCPIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("incremental-classpath")
+
+ val src2Dirty = listOf(
+ "l1/src/main/kotlin/p1/L1.kt" to setOf(
+ "w: [ksp] p1/K1.kt",
+ "w: [ksp] processing done",
+ ),
+ "l2/src/main/kotlin/p1/L2.kt" to setOf(
+ "w: [ksp] p1/K1.kt",
+ "w: [ksp] p1/K2.kt",
+ "w: [ksp] processing done",
+ ),
+ "l3/src/main/kotlin/p1/L3.kt" to setOf(
+ "w: [ksp] p1/K3.kt",
+ "w: [ksp] processing done",
+ ),
+ "l4/src/main/kotlin/p1/L4.kt" to setOf(
+ "w: [ksp] p1/K3.kt",
+ "w: [ksp] processing done",
+ ),
+ "l5/src/main/kotlin/p1/L5.kt" to setOf(
+ "w: [ksp] processing done",
+ ),
+ )
+
+ val emptyMessage = setOf("w: [ksp] processing done")
+
+ @Test
+ fun testCPChanges() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+
+ src2Dirty.forEach { (src, expectedDirties) ->
+ File(project.root, src).appendText("\n\n")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ // Trivial changes should not result in re-processing.
+ Assert.assertEquals(TaskOutcome.UP_TO_DATE, result.task(":workload:kspKotlin")?.outcome)
+ }
+ }
+
+ var i = 100
+ src2Dirty.forEach { (src, expectedDirties) ->
+ File(project.root, src).appendText("\n{ val v$i = ${i++} }\n")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet()
+ Assert.assertEquals(expectedDirties, dirties)
+ }
+ }
+
+ src2Dirty.forEach { (src, expectedDirties) ->
+ File(project.root, src).appendText("\n\nclass C${i++}\n")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet()
+ // Non-signature changes should not affect anything.
+ Assert.assertEquals(emptyMessage, dirties)
+ }
+ }
+ }
+
+ private fun toggleFlags(vararg extras: String) {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true)
+
+ gradleRunner.withArguments(
+ *extras,
+ "--rerun-tasks",
+ "-Pksp.incremental=true",
+ "-Pksp.incremental.intermodule=true",
+ "assemble"
+ ).build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ }
+
+ gradleRunner.withArguments(
+ *extras,
+ "--rerun-tasks",
+ "-Pksp.incremental=false",
+ "-Pksp.incremental.intermodule=true",
+ "assemble"
+ ).build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ }
+
+ gradleRunner.withArguments(
+ *extras,
+ "--rerun-tasks",
+ "-Pksp.incremental=true",
+ "-Pksp.incremental.intermodule=false",
+ "assemble"
+ ).build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ }
+
+ gradleRunner.withArguments(
+ *extras,
+ "--rerun-tasks",
+ "-Pksp.incremental=false",
+ "-Pksp.incremental.intermodule=false",
+ "assemble"
+ ).build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ }
+ }
+
+ @Test
+ fun toggleIncrementalFlagsWithoutConfigurationCache() {
+ toggleFlags("--no-configuration-cache")
+ }
+
+ @Test
+ fun toggleIncrementalFlagsWithConfigurationCache() {
+ toggleFlags("--configuration-cache")
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalIT.kt
new file mode 100644
index 00000000..880598bf
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalIT.kt
@@ -0,0 +1,262 @@
+package com.google.devtools.ksp.test
+
+import Artifact
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class IncrementalIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("incremental")
+
+ val src2Dirty = listOf(
+ "workload/src/main/java/p1/J1.java" to setOf(
+ "w: [ksp] p1/TestK2J.kt",
+ "w: [ksp] p1/TestJ2J.java",
+ "w: [ksp] p1/J1.java",
+ ),
+ "workload/src/main/java/p1/J2.java" to setOf(
+ "w: [ksp] p1/J2.java",
+ ),
+ "workload/src/main/java/p1/TestJ2J.java" to setOf(
+ "w: [ksp] p1/TestJ2J.java",
+ ),
+ "workload/src/main/java/p1/TestJ2K.java" to setOf(
+ "w: [ksp] p1/TestJ2K.java",
+ ),
+ "workload/src/main/java/p2/J2.java" to setOf(
+ "w: [ksp] p1/TestK2J.kt",
+ "w: [ksp] p2/J2.java",
+ "w: [ksp] p1/TestJ2J.java",
+ ),
+ "workload/src/main/java/p3/J1.java" to setOf(
+ "w: [ksp] p3/J1.java",
+ ),
+ "workload/src/main/java/p3/J2.java" to setOf(
+ "w: [ksp] p3/J2.java",
+ ),
+ "workload/src/main/java/p3/J3.java" to setOf(
+ "w: [ksp] p1/TestK2J.kt",
+ "w: [ksp] p1/TestJ2J.java",
+ "w: [ksp] p3/J3.java",
+ ),
+ "workload/src/main/kotlin/p1/K1.kt" to setOf(
+ "w: [ksp] p1/TestK2K.kt",
+ "w: [ksp] p1/K1.kt",
+ "w: [ksp] p1/TestJ2K.java",
+ ),
+ "workload/src/main/kotlin/p1/K2.kt" to setOf(
+ "w: [ksp] p1/K2.kt",
+ ),
+ "workload/src/main/kotlin/p1/TestK2J.kt" to setOf(
+ "w: [ksp] p1/TestK2J.kt",
+ ),
+ "workload/src/main/kotlin/p1/TestK2K.kt" to setOf(
+ "w: [ksp] p1/TestK2K.kt",
+ ),
+ "workload/src/main/kotlin/p2/K2.kt" to setOf(
+ "w: [ksp] p1/TestK2K.kt",
+ "w: [ksp] p2/K2.kt",
+ "w: [ksp] p1/TestJ2K.java",
+ ),
+ "workload/src/main/kotlin/p3/K1.kt" to setOf(
+ "w: [ksp] p3/K1.kt",
+ ),
+ "workload/src/main/kotlin/p3/K2.kt" to setOf(
+ "w: [ksp] p3/K2.kt",
+ ),
+ "workload/src/main/kotlin/p3/K3.kt" to setOf(
+ "w: [ksp] p1/TestK2K.kt",
+ "w: [ksp] p3/K3.kt",
+ "w: [ksp] p1/TestJ2K.java",
+ )
+ )
+
+ @Test
+ fun testUpToDate() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.UP_TO_DATE, result.task(":workload:kspKotlin")?.outcome)
+ }
+ }
+
+ @Test
+ fun testIsolating() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+ val cleanArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+
+ src2Dirty.forEach { (src, expectedDirties) ->
+ File(project.root, src).appendText("\n\n")
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet()
+ Assert.assertEquals(expectedDirties, dirties)
+ }
+ }
+ val incrementalArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+ Assert.assertEquals(cleanArtifact, incrementalArtifact)
+ }
+
+ val changeSets = listOf(
+ listOf(7, 5),
+ listOf(0, 12),
+ listOf(13, 14),
+ listOf(8, 10),
+ listOf(11, 4),
+ listOf(3, 15),
+ listOf(6, 9),
+ listOf(2, 1),
+ listOf(3, 1, 12),
+ listOf(13, 0, 11),
+ listOf(6, 8, 4),
+ listOf(10, 9, 15),
+ listOf(2, 14, 5, 7),
+ listOf(5, 0, 13, 15),
+ listOf(3, 2, 6, 7),
+ listOf(4, 14, 10, 1),
+ listOf(12, 9, 8, 11),
+ listOf(12, 13, 5, 14, 7),
+ listOf(11, 2, 8, 8, 9),
+ listOf(11, 2, 8, 8, 9),
+ listOf(4, 0, 15, 1, 10),
+ )
+
+ @Test
+ fun testMultipleChanges() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+
+ changeSets.forEach { changeSet ->
+ changeSet.forEach {
+ File(project.root, src2Dirty[it].first).appendText("\n\n")
+ }
+ val expectedDirties = changeSet.flatMapTo(mutableSetOf()) {
+ src2Dirty[it].second
+ }
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet()
+ Assert.assertEquals(expectedDirties, dirties)
+ }
+ }
+ }
+
+ @Test
+ fun testMultipleDeletes() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+
+ val srcs = src2Dirty.map { it.first }
+
+ changeSets.forEach { changeSet ->
+ val notChanged = IntRange(0, srcs.size - 1).filter { it !in changeSet }
+
+ // Touch a file so that Gradle won't UP_TO_DATE for us.
+ notChanged.first().let {
+ File(project.root, srcs[it]).appendText("\n\n")
+ }
+
+ // Delete files
+ changeSet.forEach {
+ File(project.root, srcs[it]).delete()
+ }
+
+ // in: "workload/src/main/kotlin/p1/K2.kt"
+ // out: "p1/K2.kt"
+ val expectedOutputs = notChanged.map() {
+ srcs[it].split("/").subList(4, 6).joinToString(File.separator) + ".log"
+ }.sorted()
+
+ gradleRunner.withArguments(":workload:kspKotlin").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ val outputRoot = File(project.root, "workload/build/generated/ksp/main/resources")
+ val outputs = outputRoot.walk().filter { it.isFile() }.map {
+ it.toRelativeString(outputRoot)
+ }.sorted().toList()
+
+ Assert.assertEquals(expectedOutputs, outputs)
+ }
+
+ changeSet.forEach {
+ project.restore(srcs[it])
+ }
+ }
+ }
+
+ @Test
+ fun testArgChange() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+ val cleanArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+
+ val expectedDirties = src2Dirty.map { it.second }.flatten().toSet()
+ fun buildAndCheck() {
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet()
+ Assert.assertEquals(dirties, expectedDirties)
+ }
+ val incrementalArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+ Assert.assertEquals(cleanArtifact, incrementalArtifact)
+ }
+
+ File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"option1\", \"value1\") }\n")
+ buildAndCheck()
+
+ project.restore("workload/build.gradle.kts")
+ File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"option1\", \"value2\") }\n")
+ buildAndCheck()
+
+ File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"option2\", \"value2\") }\n")
+ buildAndCheck()
+
+ project.restore("workload/build.gradle.kts")
+ buildAndCheck()
+ }
+
+ @Test
+ fun testProcessorChange() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.withArguments("build").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ Assert.assertEquals(TaskOutcome.NO_SOURCE, result.task(":workload:kspTestKotlin")?.outcome)
+ }
+ val cleanArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+
+ val expectedDirties = src2Dirty.map { it.second }.flatten().toSet()
+ fun buildAndCheck() {
+ gradleRunner.withArguments("build").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ Assert.assertEquals(TaskOutcome.NO_SOURCE, result.task(":workload:kspTestKotlin")?.outcome)
+ val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet()
+ Assert.assertEquals(dirties, expectedDirties)
+ }
+ val incrementalArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+ Assert.assertEquals(cleanArtifact, incrementalArtifact)
+ }
+
+ File(project.root, "validator/src/main/kotlin/Validator.kt").appendText("\n")
+ buildAndCheck()
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalMultiChainIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalMultiChainIT.kt
new file mode 100644
index 00000000..33680be3
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalMultiChainIT.kt
@@ -0,0 +1,49 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class IncrementalMultiChainIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("incremental-multi-chain")
+
+ @Test
+ fun testMultiChain() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ val k2 = File(project.root, "workload/src/main/kotlin/K2.kt")
+
+ gradleRunner.withArguments("run").build().let { result ->
+ Assert.assertTrue(result.output.contains("validating K1.kt"))
+ Assert.assertTrue(result.output.contains("validating Main.kt"))
+ Assert.assertTrue(result.output.contains("validating K1Impl.kt"))
+ Assert.assertTrue(result.output.contains("validating AllImpls.kt"))
+ Assert.assertTrue(result.output.contains("[K1Impl]"))
+ }
+
+ k2.writeText(
+ "@NeedsImpl\ninterface K2\n"
+ )
+ gradleRunner.withArguments("run").build().let { result ->
+ Assert.assertTrue(result.output.contains("validating K1.kt"))
+ Assert.assertTrue(result.output.contains("validating K2.kt"))
+ Assert.assertTrue(result.output.contains("validating Main.kt"))
+ Assert.assertTrue(result.output.contains("validating K1Impl.kt"))
+ Assert.assertTrue(result.output.contains("validating K2Impl.kt"))
+ Assert.assertTrue(result.output.contains("validating AllImpls.kt"))
+ Assert.assertTrue(result.output.contains("[K1Impl, K2Impl]"))
+ }
+
+ k2.delete()
+ gradleRunner.withArguments("run").build().let { result ->
+ Assert.assertTrue(result.output.contains("validating K1.kt"))
+ Assert.assertTrue(result.output.contains("validating Main.kt"))
+ Assert.assertTrue(result.output.contains("validating K1Impl.kt"))
+ Assert.assertTrue(result.output.contains("validating AllImpls.kt"))
+ Assert.assertTrue(result.output.contains("[K1Impl]"))
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalRemovalIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalRemovalIT.kt
new file mode 100644
index 00000000..78de3c04
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalRemovalIT.kt
@@ -0,0 +1,35 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class IncrementalRemovalIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("incremental-removal")
+
+ @Test
+ fun testRemoveOutputs() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ val k1 = "workload/src/main/kotlin/p1/K1.kt"
+
+ gradleRunner.withArguments("run").build().let { result ->
+ Assert.assertTrue(result.output.contains("result: generated"))
+ }
+
+ File(project.root, k1).writeText(
+ "package p1\n\nclass K1\n\nclass Foo : Bar { override fun s() = \"crafted\" }\n"
+ )
+ gradleRunner.withArguments("run").build().let { result ->
+ Assert.assertTrue(result.output.contains("result: crafted"))
+ }
+
+ project.restore(k1)
+ gradleRunner.withArguments("run").build().let { result ->
+ Assert.assertTrue(result.output.contains("result: generated"))
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/InitPlusProviderIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/InitPlusProviderIT.kt
new file mode 100644
index 00000000..31b7cb58
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/InitPlusProviderIT.kt
@@ -0,0 +1,33 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+import java.util.jar.JarFile
+
+class InitPlusProviderIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("init-plus-provider")
+
+ @Test
+ fun testInitPlusProvider() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ val resultCleanBuild = gradleRunner.withArguments("clean", "build").build()
+
+ Assert.assertEquals(TaskOutcome.SUCCESS, resultCleanBuild.task(":workload:build")?.outcome)
+
+ val artifact = File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar")
+ Assert.assertTrue(artifact.exists())
+
+ JarFile(artifact).use { jarFile ->
+ Assert.assertTrue(jarFile.getEntry("TestProcessor.log").size > 0)
+ Assert.assertTrue(jarFile.getEntry("HelloFromProvider.class").size > 0)
+ Assert.assertTrue(jarFile.getEntry("GeneratedFromProvider.class").size > 0)
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaNestedClassIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaNestedClassIT.kt
new file mode 100644
index 00000000..14cd0903
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaNestedClassIT.kt
@@ -0,0 +1,22 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+
+class JavaNestedClassIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("javaNestedClass")
+
+ @Test
+ fun testJavaNestedClass() {
+
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ val resultCleanBuild = gradleRunner.withArguments("clean", "build").build()
+ Assert.assertEquals(TaskOutcome.SUCCESS, resultCleanBuild.task(":workload:build")?.outcome)
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaOnlyIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaOnlyIT.kt
new file mode 100644
index 00000000..bf94e522
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaOnlyIT.kt
@@ -0,0 +1,34 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class JavaOnlyIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("java-only", "test-processor")
+
+ @Test
+ fun testJavaOnly() {
+ // FIXME: `clean` fails to delete files on windows.
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+
+ File(project.root, "workload/src/main/java/com/example/Foo.java").delete()
+
+ gradleRunner.withArguments("clean", "assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.NO_SOURCE, result.task(":workload:kspKotlin")?.outcome)
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KMPImplementedIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KMPImplementedIT.kt
new file mode 100644
index 00000000..7c9c2dcd
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KMPImplementedIT.kt
@@ -0,0 +1,369 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+import java.util.jar.*
+
+class KMPImplementedIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("kmp")
+
+ private fun verify(jarName: String, contents: List<String>) {
+ val artifact = File(project.root, jarName)
+ Assert.assertTrue(artifact.exists())
+
+ JarFile(artifact).use { jarFile ->
+ contents.forEach {
+ Assert.assertTrue(jarFile.getEntry(it).size > 0)
+ }
+ }
+ }
+
+ private fun verifyKexe(path: String) {
+ val artifact = File(project.root, path)
+ Assert.assertTrue(artifact.exists())
+ Assert.assertTrue(artifact.readBytes().size > 0)
+ }
+
+ private fun checkExecutionOptimizations(log: String) {
+ Assert.assertFalse(
+ "Execution optimizations have been disabled",
+ log.contains("Execution optimizations have been disabled")
+ )
+ }
+
+ @Test
+ fun testJvm() {
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ ":workload-jvm:build"
+ ).build().let {
+ Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-jvm:build")?.outcome)
+ verify(
+ "workload-jvm/build/libs/workload-jvm-jvm-1.0-SNAPSHOT.jar",
+ listOf(
+ "com/example/Foo.class"
+ )
+ )
+ Assert.assertFalse(it.output.contains("kotlin scripting plugin:"))
+ Assert.assertTrue(it.output.contains("w: [ksp] platforms: [JVM"))
+ Assert.assertTrue(it.output.contains("w: [ksp] List has superTypes: true"))
+ checkExecutionOptimizations(it.output)
+ }
+ }
+
+ @Test
+ fun testJvmErrorLog() {
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ File(project.root, "workload-jvm/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"process\") }\n")
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ ":workload-jvm:build"
+ ).buildAndFail().let {
+ val errors = it.output.lines().filter { it.startsWith("e: [ksp]") }
+ Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in process", errors.first())
+ }
+ project.restore("workload-jvm/build.gradle.kts")
+ }
+
+ @Test
+ fun testJs() {
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ ":workload-js:build"
+ ).build().let {
+ Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-js:build")?.outcome)
+ verify(
+ "workload-js/build/libs/workload-js-jslegacy-1.0-SNAPSHOT.jar",
+ listOf(
+ "playground-workload-js-js-legacy.js"
+ )
+ )
+ verify(
+ "workload-js/build/libs/workload-js-jsir-1.0-SNAPSHOT.klib",
+ listOf(
+ "default/ir/types.knt"
+ )
+ )
+ Assert.assertFalse(it.output.contains("kotlin scripting plugin:"))
+ Assert.assertTrue(it.output.contains("w: [ksp] platforms: [JS"))
+ Assert.assertTrue(it.output.contains("w: [ksp] List has superTypes: true"))
+ checkExecutionOptimizations(it.output)
+ }
+ }
+
+ @Test
+ fun testJsErrorLog() {
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ File(project.root, "workload-js/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"process\") }\n")
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ ":workload-js:build"
+ ).buildAndFail().let {
+ val errors = it.output.lines().filter { it.startsWith("e: [ksp]") }
+ Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in process", errors.first())
+ }
+ project.restore("workload-js/build.gradle.kts")
+ }
+
+ @Test
+ fun testJsFailWarning() {
+ File(project.root, "workload-js/build.gradle.kts")
+ .appendText("\nksp {\n allWarningsAsErrors = true\n}\n")
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ ":workload-js:build"
+ ).buildAndFail()
+ }
+
+ @Test
+ fun testAndroidNative() {
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ ":workload-androidNative:build"
+ ).build().let {
+ Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-androidNative:build")?.outcome)
+ verifyKexe(
+ "workload-androidNative/build/bin/androidNativeX64/debugExecutable/workload-androidNative.kexe"
+ )
+ verifyKexe(
+ "workload-androidNative/build/bin/androidNativeX64/releaseExecutable/workload-androidNative.kexe"
+ )
+ verifyKexe(
+ "workload-androidNative/build/bin/androidNativeArm64/debugExecutable/workload-androidNative.kexe"
+ )
+ verifyKexe(
+ "workload-androidNative/build/bin/androidNativeArm64/releaseExecutable/workload-androidNative.kexe"
+ )
+ Assert.assertFalse(it.output.contains("kotlin scripting plugin:"))
+ Assert.assertTrue(it.output.contains("w: [ksp] platforms: [Native"))
+ checkExecutionOptimizations(it.output)
+ }
+ }
+
+ @Test
+ fun testLinuxX64() {
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ val genDir = File(project.root, "workload-linuxX64/build/generated/ksp/linuxX64/linuxX64Main/kotlin")
+
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ ":workload-linuxX64:build"
+ ).build().let {
+ Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-linuxX64:build")?.outcome)
+ Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-linuxX64:kspTestKotlinLinuxX64")?.outcome)
+ verifyKexe("workload-linuxX64/build/bin/linuxX64/debugExecutable/workload-linuxX64.kexe")
+ verifyKexe("workload-linuxX64/build/bin/linuxX64/releaseExecutable/workload-linuxX64.kexe")
+
+ // TODO: Enable after CI's Xcode version catches up.
+ // Assert.assertTrue(
+ // result.task(":workload-linuxX64:kspKotlinIosArm64")?.outcome == TaskOutcome.SUCCESS ||
+ // result.task(":workload-linuxX64:kspKotlinIosArm64")?.outcome == TaskOutcome.SKIPPED
+ // )
+ // Assert.assertTrue(
+ // result.task(":workload-linuxX64:kspKotlinMacosX64")?.outcome == TaskOutcome.SUCCESS ||
+ // result.task(":workload-linuxX64:kspKotlinMacosX64")?.outcome == TaskOutcome.SKIPPED
+ // )
+ Assert.assertTrue(
+ it.task(":workload-linuxX64:kspKotlinMingwX64")?.outcome == TaskOutcome.SUCCESS ||
+ it.task(":workload-linuxX64:kspKotlinMingwX64")?.outcome == TaskOutcome.SKIPPED
+ )
+ Assert.assertFalse(it.output.contains("kotlin scripting plugin:"))
+ Assert.assertTrue(it.output.contains("w: [ksp] platforms: [Native"))
+ Assert.assertTrue(it.output.contains("w: [ksp] List has superTypes: true"))
+ Assert.assertTrue(File(genDir, "Main_dot_kt.kt").exists())
+ Assert.assertTrue(File(genDir, "ToBeRemoved_dot_kt.kt").exists())
+ checkExecutionOptimizations(it.output)
+ }
+
+ File(project.root, "workload-linuxX64/src/linuxX64Main/kotlin/ToBeRemoved.kt").delete()
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ ":workload-linuxX64:build"
+ ).build().let {
+ Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-linuxX64:build")?.outcome)
+ Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-linuxX64:kspTestKotlinLinuxX64")?.outcome)
+ verifyKexe("workload-linuxX64/build/bin/linuxX64/debugExecutable/workload-linuxX64.kexe")
+ verifyKexe("workload-linuxX64/build/bin/linuxX64/releaseExecutable/workload-linuxX64.kexe")
+ Assert.assertTrue(File(genDir, "Main_dot_kt.kt").exists())
+ Assert.assertFalse(File(genDir, "ToBeRemoved_dot_kt.kt").exists())
+ checkExecutionOptimizations(it.output)
+ }
+ }
+
+ @Test
+ fun testNonEmbeddableArtifact() {
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "-Pkotlin.native.useEmbeddableCompilerJar=false",
+ ":workload-linuxX64:kspTestKotlinLinuxX64"
+ ).build()
+
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "-Pkotlin.native.useEmbeddableCompilerJar=true",
+ ":workload-linuxX64:kspTestKotlinLinuxX64"
+ ).build()
+
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ ":workload-linuxX64:kspTestKotlinLinuxX64"
+ ).build()
+ }
+
+ @Test
+ fun testLinuxX64ErrorLog() {
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ File(project.root, "workload-linuxX64/build.gradle.kts")
+ .appendText("\nksp { arg(\"exception\", \"process\") }\n")
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ ":workload-linuxX64:build"
+ ).buildAndFail().let {
+ val errors = it.output.lines().filter { it.startsWith("e: [ksp]") }
+ Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in process", errors.first())
+ }
+ project.restore("workload-js/build.gradle.kts")
+ }
+
+ private fun verifyAll(result: BuildResult) {
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:build")?.outcome)
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspTestKotlinLinuxX64")?.outcome)
+
+ verify(
+ "workload/build/libs/workload-jvm-1.0-SNAPSHOT.jar",
+ listOf(
+ "com/example/Foo.class"
+ )
+ )
+
+ verify(
+ "workload/build/libs/workload-jslegacy-1.0-SNAPSHOT.jar",
+ listOf(
+ "playground-workload-js-legacy.js"
+ )
+ )
+
+ verify(
+ "workload/build/libs/workload-jsir-1.0-SNAPSHOT.klib",
+ listOf(
+ "default/ir/types.knt"
+ )
+ )
+
+ verifyKexe("workload/build/bin/linuxX64/debugExecutable/workload.kexe")
+ verifyKexe("workload/build/bin/linuxX64/releaseExecutable/workload.kexe")
+ verifyKexe("workload/build/bin/androidNativeX64/debugExecutable/workload.kexe")
+ verifyKexe("workload/build/bin/androidNativeX64/releaseExecutable/workload.kexe")
+ verifyKexe("workload/build/bin/androidNativeArm64/debugExecutable/workload.kexe")
+ verifyKexe("workload/build/bin/androidNativeArm64/releaseExecutable/workload.kexe")
+
+ // TODO: Enable after CI's Xcode version catches up.
+ // Assert.assertTrue(
+ // result.task(":workload:kspKotlinIosArm64")?.outcome == TaskOutcome.SUCCESS ||
+ // result.task(":workload:kspKotlinIosArm64")?.outcome == TaskOutcome.SKIPPED
+ // )
+ // Assert.assertTrue(
+ // result.task(":workload:kspKotlinMacosX64")?.outcome == TaskOutcome.SUCCESS ||
+ // result.task(":workload:kspKotlinMacosX64")?.outcome == TaskOutcome.SKIPPED
+ // )
+ Assert.assertTrue(
+ result.task(":workload:kspKotlinMingwX64")?.outcome == TaskOutcome.SUCCESS ||
+ result.task(":workload:kspKotlinMingwX64")?.outcome == TaskOutcome.SKIPPED
+ )
+
+ Assert.assertFalse(result.output.contains("kotlin scripting plugin:"))
+ Assert.assertTrue(result.output.contains("w: [ksp] platforms: [JVM"))
+ Assert.assertTrue(result.output.contains("w: [ksp] platforms: [JS"))
+ Assert.assertTrue(result.output.contains("w: [ksp] platforms: [Native"))
+ }
+
+ @Test
+ fun testMainConfiguration() {
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ val buildScript = File(project.root, "workload/build.gradle.kts")
+ val lines = buildScript.readLines().takeWhile {
+ it.trimEnd() != "dependencies {"
+ }
+ buildScript.writeText(lines.joinToString(System.lineSeparator()))
+ buildScript.appendText(System.lineSeparator())
+ buildScript.appendText("dependencies {")
+ buildScript.appendText(System.lineSeparator())
+ buildScript.appendText(" add(\"ksp\", project(\":test-processor\"))")
+ buildScript.appendText(System.lineSeparator())
+ buildScript.appendText("}")
+
+ val messages = listOf(
+ "The 'ksp' configuration is deprecated in Kotlin Multiplatform projects. ",
+ "Please use target-specific configurations like 'kspJvm' instead."
+ )
+
+ // KotlinNative doesn't support configuration cache yet.
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ "build",
+ "-Pksp.allow.all.target.configuration=false"
+ ).buildAndFail().apply {
+ Assert.assertTrue(
+ messages.all {
+ output.contains(it)
+ }
+ )
+ checkExecutionOptimizations(output)
+ }
+
+ // KotlinNative doesn't support configuration cache yet.
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "clean",
+ "build"
+ ).build().apply {
+ Assert.assertTrue(
+ messages.all {
+ output.contains(it)
+ }
+ )
+ verifyAll(this)
+ checkExecutionOptimizations(output)
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KSPCmdLineOptionsIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KSPCmdLineOptionsIT.kt
new file mode 100644
index 00000000..80f8cce6
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KSPCmdLineOptionsIT.kt
@@ -0,0 +1,85 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.jetbrains.kotlin.cli.common.ExitCode
+import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.PrintStream
+import java.net.URLClassLoader
+
+data class CompileResult(val exitCode: ExitCode, val output: String)
+
+class KSPCmdLineOptionsIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("cmd-options")
+
+ private fun runCmdCompiler(pluginOptions: List<String>): CompileResult {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.withArguments("clean", ":processors:build").build()
+ val processorJar = File(project.root, "processors/build/libs/processors-1.0-SNAPSHOT.jar")
+ val classLoader = URLClassLoader(arrayOf(processorJar.toURI().toURL()), javaClass.classLoader)
+ val compiler = classLoader.loadClass(K2JVMCompiler::class.java.name).newInstance() as K2JVMCompiler
+ val repoPath = "../build/repos/test/com/google/devtools/ksp/"
+ val kspPluginId = "com.google.devtools.ksp.symbol-processing"
+ val kspPluginJar = File("$repoPath/symbol-processing-cmdline/2.0.255-SNAPSHOT").listFiles()!!.filter {
+ it.name.matches(Regex(".*-\\d.jar"))
+ }.maxByOrNull { it.lastModified() }!!
+ val kspApiJar = File("$repoPath/symbol-processing-api/2.0.255-SNAPSHOT").listFiles()!!.filter {
+ it.name.matches(Regex(".*-\\d.jar"))
+ }.maxByOrNull { it.lastModified() }!!
+ val compilerArgs = mutableListOf(
+ "-no-stdlib",
+ "-Xplugin=${kspPluginJar.absolutePath}",
+ "-Xplugin=${kspApiJar.absolutePath}",
+ "-P", "plugin:$kspPluginId:apclasspath=${processorJar.absolutePath}",
+ "-P", "plugin:$kspPluginId:projectBaseDir=${project.root}/build",
+ "-P", "plugin:$kspPluginId:classOutputDir=${project.root}/build",
+ "-P", "plugin:$kspPluginId:javaOutputDir=${project.root}/build/out",
+ "-P", "plugin:$kspPluginId:kotlinOutputDir=${project.root}/build/out",
+ "-P", "plugin:$kspPluginId:resourceOutputDir=${project.root}/build/out",
+ "-P", "plugin:$kspPluginId:kspOutputDir=${project.root}/build/out",
+ "-P", "plugin:$kspPluginId:cachesDir=${project.root}/build/out",
+ "-P", "plugin:$kspPluginId:incremental=false",
+ "-d", "${project.root}/build/out"
+ )
+ pluginOptions.forEach {
+ compilerArgs.add("-P")
+ compilerArgs.add("plugin:$kspPluginId:$it")
+ }
+ compilerArgs.add(File(project.root, "workload/src/main/kotlin/com/example/A.kt").absolutePath)
+ val outStream = ByteArrayOutputStream()
+ val exitCode = compiler.exec(PrintStream(outStream), *compilerArgs.toTypedArray())
+ return CompileResult(exitCode, outStream.toString())
+ }
+
+ @Test
+ fun testWithCompilationOnError() {
+ val result = runCmdCompiler(listOf("apoption=error=true", "withCompilation=true"))
+ val errors = result.output.lines().filter { it.startsWith("error: [ksp]") }
+ val exitCode = result.exitCode
+ Assert.assertTrue(exitCode == ExitCode.COMPILATION_ERROR)
+ Assert.assertTrue(
+ errors.any {
+ it.startsWith("error: [ksp] java.lang.IllegalStateException: Error on request")
+ }
+ )
+ }
+
+ @Test
+ fun testWithCompilationOnErrorOk() {
+ val result = runCmdCompiler(listOf("apoption=error=true", "returnOkOnError=true", "withCompilation=true"))
+ val errors = result.output.lines().filter { it.startsWith("error: [ksp]") }
+ val exitCode = result.exitCode
+ Assert.assertTrue(exitCode == ExitCode.OK)
+ Assert.assertTrue(
+ errors.any {
+ it.startsWith("error: [ksp] java.lang.IllegalStateException: Error on request")
+ }
+ )
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KotlinConstsInJavaIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KotlinConstsInJavaIT.kt
new file mode 100644
index 00000000..d8714bf7
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KotlinConstsInJavaIT.kt
@@ -0,0 +1,42 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class KotlinConstsInJavaIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("kotlin-consts-in-java")
+
+ private fun GradleRunner.buildAndCheck(vararg args: String, extraCheck: (BuildResult) -> Unit = {}) =
+ buildAndCheckOutcome(*args, outcome = TaskOutcome.SUCCESS, extraCheck = extraCheck)
+
+ private fun GradleRunner.buildAndCheckOutcome(
+ vararg args: String,
+ outcome: TaskOutcome,
+ extraCheck: (BuildResult) -> Unit = {}
+ ) {
+ val result = this.withArguments(*args).build()
+
+ Assert.assertEquals(outcome, result.task(":workload:kspKotlin")?.outcome)
+
+ extraCheck(result)
+ }
+
+ @Test
+ fun testKotlinConstsInJava() {
+ // FIXME: `clean` fails to delete files on windows.
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true)
+ gradleRunner.buildAndCheck(":workload:kspKotlin")
+
+ File(project.root, "workload/src/main/java/com/example/JavaClass.java").appendText("\n")
+ gradleRunner.buildAndCheck(":workload:kspKotlin")
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MapAnnotationArgumentsIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MapAnnotationArgumentsIT.kt
new file mode 100644
index 00000000..870b4e60
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MapAnnotationArgumentsIT.kt
@@ -0,0 +1,40 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+
+class MapAnnotationArgumentsIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("map-annotation-arguments", "test-processor")
+
+ val expectedErrors = listOf(
+ "e: [ksp] unboxedChar: Char != Character\n",
+ "e: [ksp] boxedChar: (Char..Char?) != Character\n",
+ "e: Error occurred in KSP, check log for detail\n",
+ )
+
+ @Test
+ fun testMapAnnotationArguments() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("assemble", "-Pksp.map.annotation.arguments.in.java=true").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+
+ gradleRunner.withArguments("clean", "assemble", "--rerun-tasks").buildAndFail().let { result ->
+ Assert.assertEquals(TaskOutcome.FAILED, result.task(":workload:kspKotlin")?.outcome)
+ Assert.assertTrue(expectedErrors.all { it in result.output })
+ }
+
+ gradleRunner.withArguments("clean", "assemble", "-Pksp.map.annotation.arguments.in.java=false", "--rerun-tasks")
+ .buildAndFail().let { result ->
+ Assert.assertEquals(TaskOutcome.FAILED, result.task(":workload:kspKotlin")?.outcome)
+ Assert.assertTrue(expectedErrors.all { it in result.output })
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MultiplatformIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MultiplatformIT.kt
new file mode 100644
index 00000000..9d7cb56e
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MultiplatformIT.kt
@@ -0,0 +1,34 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+import java.util.jar.*
+
+class MultiplatformIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("playground-mpp", "playground")
+
+ @Test
+ fun testJVM() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ val resultCleanBuild =
+ gradleRunner.withArguments("--configuration-cache-problems=warn", "clean", "build").build()
+
+ Assert.assertEquals(TaskOutcome.SUCCESS, resultCleanBuild.task(":workload:build")?.outcome)
+
+ val artifact = File(project.root, "workload/build/libs/workload-jvm-1.0-SNAPSHOT.jar")
+ Assert.assertTrue(artifact.exists())
+
+ JarFile(artifact).use { jarFile ->
+ Assert.assertTrue(jarFile.getEntry("TestProcessor.log").size > 0)
+ Assert.assertTrue(jarFile.getEntry("hello/HELLO.class").size > 0)
+ Assert.assertTrue(jarFile.getEntry("com/example/AClassBuilder.class").size > 0)
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt
new file mode 100644
index 00000000..d44740af
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt
@@ -0,0 +1,111 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class OnErrorIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("on-error")
+
+ @Test
+ fun testOnError() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result ->
+ val errors = result.output.lines().filter { it.startsWith("e: [ksp]") }
+ Assert.assertEquals("e: [ksp] Error processor: errored at 2", errors.first())
+ Assert.assertEquals("e: [ksp] NormalProcessor called error on 2", errors.last())
+ }
+ }
+
+ @Test
+ fun testOnExceptionInInit() {
+ File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"init\") }\n")
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result ->
+ val errors = result.output.lines().filter { it.startsWith("e: [ksp]") }
+ Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in init", errors.first())
+ }
+ project.restore("workload/build.gradle.kts")
+ }
+
+ @Test
+ fun testOnExceptionInProcess() {
+ File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"process\") }\n")
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result ->
+ val errors = result.output.lines().filter { it.startsWith("e: [ksp]") }
+ Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in process", errors.first())
+ }
+ project.restore("workload/build.gradle.kts")
+ }
+
+ @Test
+ fun testOnExceptionInFinish() {
+ File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"finish\") }\n")
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result ->
+ val errors = result.output.lines().filter { it.startsWith("e: [ksp]") }
+ Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in finish", errors.first())
+ }
+ project.restore("workload/build.gradle.kts")
+ }
+
+ @Test
+ fun testOnExceptionInOnError() {
+ File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"error\") }\n")
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result ->
+ val errors = result.output.lines().filter { it.startsWith("e: [ksp]") }
+
+ Assert.assertEquals("e: [ksp] Error processor: errored at 2", errors.first())
+ Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in error", errors[1])
+ }
+ project.restore("workload/build.gradle.kts")
+ }
+
+ @Test
+ fun testCreateTwice() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true)
+
+ File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"createTwice\") }\n")
+ gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result ->
+ val errors = result.output.lines().filter { it.startsWith("e: [ksp]") }
+
+ Assert.assertTrue(
+ errors.any {
+ it.startsWith("e: [ksp] kotlin.io.FileAlreadyExistsException:")
+ }
+ )
+
+ Assert.assertFalse(result.output.contains("e: java.lang.IllegalStateException: Should not be called!"))
+ }
+ project.restore("workload/build.gradle.kts")
+ }
+
+ @Test
+ fun testCreateTwiceNotOkOnError() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true)
+
+ File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"createTwice\") }\n")
+ File(project.root, "gradle.properties").appendText("\nksp.return.ok.on.error=false")
+ gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result ->
+ val errors = result.output.lines().filter { it.startsWith("e: [ksp]") }
+
+ Assert.assertTrue(
+ errors.any {
+ it.startsWith("e: [ksp] kotlin.io.FileAlreadyExistsException:")
+ }
+ )
+ }
+ project.restore("workload/build.gradle.kts")
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnlyResourcesFileIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnlyResourcesFileIT.kt
new file mode 100644
index 00000000..21b182a5
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnlyResourcesFileIT.kt
@@ -0,0 +1,21 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Rule
+import org.junit.Test
+
+class OnlyResourcesFileIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("only-resources-file")
+
+ @Test
+ fun test() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments(
+ "--configuration-cache-problems=warn",
+ "jvmJar",
+ ).build()
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt
new file mode 100644
index 00000000..9c39e743
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt
@@ -0,0 +1,157 @@
+package com.google.devtools.ksp.test
+
+import Artifact
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class OutputDepsIt {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("output-deps")
+
+ val src2Dirty = listOf(
+ "workload/src/main/java/p1/J1.java" to setOf(
+ "w: [ksp] p1/J1.java",
+ "w: [ksp] p1/K1.kt",
+ "w: [ksp] p1/K2.kt",
+ ),
+ "workload/src/main/java/p1/J2.java" to setOf(
+ "w: [ksp] p1/J2.java",
+ ),
+ "workload/src/main/kotlin/p1/K1.kt" to setOf(
+ "w: [ksp] p1/J1.java",
+ "w: [ksp] p1/K1.kt",
+ "w: [ksp] p1/K2.kt",
+ ),
+ "workload/src/main/kotlin/p1/K2.kt" to setOf(
+ "w: [ksp] p1/J1.java",
+ "w: [ksp] p1/K1.kt",
+ "w: [ksp] p1/K2.kt",
+ ),
+ )
+
+ val src2Output = mapOf(
+ "workload/src/main/java/p1/J1.java" to setOf(
+ "kotlin/p1/J1Generated.kt",
+ "kotlin/p1/K1Generated.kt",
+ "kotlin/p1/K2Generated.kt",
+ "resources/p1.Anno1.log",
+ "resources/p1.Anno2.log",
+ ),
+ "workload/src/main/java/p1/J2.java" to setOf(
+ "kotlin/p1/J2Generated.kt",
+ "resources/p1.Anno1.log",
+ "resources/p1.Anno2.log",
+ ),
+ "workload/src/main/kotlin/p1/K1.kt" to setOf(
+ "kotlin/p1/J1Generated.kt",
+ "kotlin/p1/K1Generated.kt",
+ "kotlin/p1/K2Generated.kt",
+ "resources/p1.Anno1.log",
+ "resources/p1.Anno2.log",
+ ),
+ "workload/src/main/kotlin/p1/K2.kt" to setOf(
+ "kotlin/p1/J1Generated.kt",
+ "kotlin/p1/K1Generated.kt",
+ "kotlin/p1/K2Generated.kt",
+ "resources/p1.Anno1.log",
+ "resources/p1.Anno2.log",
+ ),
+ )
+
+ val deletedSrc2Output = listOf(
+ "workload/src/main/java/p1/J1.java" to listOf(
+ "kotlin/p1/Anno1Generated.kt",
+ "kotlin/p1/Anno2Generated.kt",
+ "kotlin/p1/J2Generated.kt",
+ "kotlin/p1/K1Generated.kt",
+ "kotlin/p1/K2Generated.kt",
+ "resources/p1.Anno1.log",
+ "resources/p1.Anno2.log",
+ ),
+ "workload/src/main/java/p1/J2.java" to listOf(
+ "kotlin/p1/Anno1Generated.kt",
+ "kotlin/p1/Anno2Generated.kt",
+ "kotlin/p1/K1Generated.kt",
+ "kotlin/p1/K2Generated.kt",
+ "resources/p1.Anno1.log",
+ "resources/p1.Anno2.log",
+ ),
+ "workload/src/main/kotlin/p1/K1.kt" to listOf(
+ "kotlin/p1/Anno1Generated.kt",
+ "kotlin/p1/Anno2Generated.kt",
+ "kotlin/p1/K2Generated.kt",
+ "resources/p1.Anno1.log",
+ "resources/p1.Anno2.log",
+ ),
+ "workload/src/main/kotlin/p1/K2.kt" to listOf(
+ "kotlin/p1/Anno1Generated.kt",
+ "kotlin/p1/Anno2Generated.kt",
+ "resources/p1.Anno1.log",
+ "resources/p1.Anno2.log",
+ ),
+ )
+
+ @Test
+ fun testOutputDeps() {
+ // FIXME
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+ val cleanArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+
+ src2Dirty.forEach { (src, expectedDirties) ->
+ val srcFile = File(project.root, src)
+ // In case that the test goes faster than the precision of timestamps.
+ // It's 1s on Github's CI.
+ Thread.sleep(1000)
+ srcFile.appendText("\n\n")
+ Thread.sleep(1000)
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet()
+ Assert.assertEquals(expectedDirties, dirties)
+
+ val outputRoot = File(project.root, "workload/build/generated/ksp/main/")
+ outputRoot.walk().filter { it.isFile() }.forEach {
+ if (it.toRelativeString(outputRoot) in src2Output[src]!!) {
+ Assert.assertTrue(it.lastModified() > srcFile.lastModified())
+ } else {
+ Assert.assertTrue(it.lastModified() < srcFile.lastModified())
+ }
+ }
+ }
+ }
+ val incrementalArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+ Assert.assertEquals(cleanArtifact, incrementalArtifact)
+ }
+
+ @Test
+ fun testDeletion() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+ }
+
+ deletedSrc2Output.forEach { (src, expectedDirties) ->
+ File(project.root, src).delete()
+ gradleRunner.withArguments("assemble").build().let { result ->
+ Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+ val outputRoot = File(project.root, "workload/build/generated/ksp/main/")
+ val outputs = outputRoot.walk().filter { it.isFile() }.map {
+ it.toRelativeString(outputRoot).replace(File.separatorChar, '/')
+ }.toList().sorted()
+ Assert.assertEquals(expectedDirties, outputs)
+ }
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PlaygroundIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PlaygroundIT.kt
new file mode 100644
index 00000000..eeaaf502
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PlaygroundIT.kt
@@ -0,0 +1,210 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+import java.util.jar.*
+
+class PlaygroundIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("playground")
+
+ private fun GradleRunner.buildAndCheck(vararg args: String, extraCheck: (BuildResult) -> Unit = {}) =
+ buildAndCheckOutcome(*args, outcome = TaskOutcome.SUCCESS, extraCheck = extraCheck)
+
+ private fun GradleRunner.buildAndCheckOutcome(
+ vararg args: String,
+ outcome: TaskOutcome,
+ extraCheck: (BuildResult) -> Unit = {}
+ ) {
+ val result = this.withArguments(*args).build()
+
+ Assert.assertEquals(outcome, result.task(":workload:build")?.outcome)
+
+ val artifact = File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar")
+ Assert.assertTrue(artifact.exists())
+
+ JarFile(artifact).use { jarFile ->
+ Assert.assertTrue(jarFile.getEntry("TestProcessor.log").size > 0)
+ Assert.assertTrue(jarFile.getEntry("hello/HELLO.class").size > 0)
+ Assert.assertTrue(jarFile.getEntry("g/G.class").size > 0)
+ Assert.assertTrue(jarFile.getEntry("com/example/AClassBuilder.class").size > 0)
+ }
+
+ extraCheck(result)
+ }
+
+ @Test
+ fun testPlayground() {
+ // FIXME: `clean` fails to delete files on windows.
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.buildAndCheck("clean", "build")
+ gradleRunner.buildAndCheck("clean", "build")
+ }
+
+ // TODO: add another plugin and see if it is blocked.
+ // Or use a project that depends on a builtin plugin like all-open and see if the build fails
+ @Test
+ fun testBlockOtherCompilerPlugins() {
+ // FIXME: `clean` fails to delete files on windows.
+ Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ File(project.root, "workload/build.gradle.kts")
+ .appendText("\nksp {\n blockOtherCompilerPlugins = false\n}\n")
+ gradleRunner.buildAndCheck("clean", "build")
+ gradleRunner.buildAndCheck("clean", "build")
+ project.restore("workload/build.gradle.kts")
+ }
+
+ @Test
+ fun testAllowSourcesFromOtherPlugins() {
+ fun checkGBuilder() {
+ val artifact = File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar")
+
+ JarFile(artifact).use { jarFile ->
+ Assert.assertTrue(jarFile.getEntry("g/GBuilder.class").size > 0)
+ }
+ }
+
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ File(project.root, "workload/build.gradle.kts")
+ .appendText("\nksp {\n allowSourcesFromOtherPlugins = true\n}\n")
+ gradleRunner.buildAndCheck("clean", "build") { checkGBuilder() }
+ gradleRunner.buildAndCheckOutcome("build", "--info", outcome = TaskOutcome.UP_TO_DATE) {
+ Assert.assertEquals(TaskOutcome.UP_TO_DATE, it.task(":workload:kspKotlin")?.outcome)
+ checkGBuilder()
+ }
+ project.restore("workload/build.gradle.kts")
+ }
+
+ /** Regression test for https://github.com/google/ksp/issues/518. */
+ @Test
+ fun testBuildWithConfigureOnDemand() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.buildAndCheck("--configure-on-demand", ":workload:build")
+ }
+
+ @Test
+ fun testBuildCache() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ // The first build can be FROM_CACHE or SUCCESS, and we only care about the second build.
+ gradleRunner.buildAndCheck("--build-cache", ":workload:clean", "build")
+ gradleRunner.buildAndCheck("--build-cache", ":workload:clean", "build") {
+ Assert.assertEquals(TaskOutcome.FROM_CACHE, it.task(":workload:kspKotlin")?.outcome)
+ }
+ }
+
+ @Test
+ fun testAllWarningsAsErrors() {
+ File(project.root, "workload/build.gradle.kts")
+ .appendText("\nksp {\n allWarningsAsErrors = true\n}\n")
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.withArguments("build").buildAndFail().let { result ->
+ Assert.assertTrue(result.output.contains("This is a harmless warning."))
+ }
+ }
+
+ // Compiler's test infra report this kind of error before KSP, so it is not testable there.
+ @Test
+ fun testNoFunctionName() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ fun buildAndFileAndCheck() {
+ gradleRunner.withArguments("build").buildAndFail().let { result ->
+ Assert.assertTrue(result.output.contains("Function declaration must have a name"))
+ }
+ }
+
+ File(project.root, "workload/src/main/java/com/example/A.kt").appendText("\n{}\n")
+ buildAndFileAndCheck()
+ project.restore("workload/src/main/java/com/example/A.kt")
+
+ File(project.root, "workload/src/main/java/com/example/A.kt").appendText("\nfun() = {0}\n")
+ buildAndFileAndCheck()
+ project.restore("workload/src/main/java/com/example/A.kt")
+ }
+
+ @Test
+ fun testRewriteFile() {
+ File(
+ project.root,
+ "test-processor/src/main/resources/META-INF/services/" +
+ "com.google.devtools.ksp.processing.SymbolProcessorProvider"
+ ).writeText("RewriteProcessorProvider")
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.withArguments("build").buildAndFail().let { result ->
+ Assert.assertTrue(result.output.contains("kotlin.io.FileAlreadyExistsException"))
+ }
+ }
+
+ // Disabled for now: ERROR: K2 does not support plugins yet, so please remove -Xuse-k2 flag
+ // Test -Xuse-fir for compilation; KSP still uses FE1.0
+ @Ignore
+ @Test
+ fun testFirPreview() {
+ val gradleProperties = File(project.root, "gradle.properties")
+ gradleProperties.appendText("\nkotlin.useK2=true")
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.buildAndCheck("clean", "build") { result ->
+ Assert.assertTrue(result.output.contains("This build uses experimental K2 compiler"))
+ Assert.assertTrue(result.output.contains("-Xuse-k2"))
+ }
+ project.restore(gradleProperties.path)
+ }
+
+ @Test
+ fun testVersions() {
+ val kotlinCompile = "org.jetbrains.kotlin.gradle.tasks.KotlinCompile"
+ val buildFile = File(project.root, "workload/build.gradle.kts")
+ buildFile.appendText("\ntasks.withType<$kotlinCompile> {")
+ buildFile.appendText("\n kotlinOptions.apiVersion = \"1.5\"")
+ buildFile.appendText("\n kotlinOptions.languageVersion = \"1.5\"")
+ buildFile.appendText("\n}")
+
+ val kotlinVersion = System.getProperty("kotlinVersion").split('-').first()
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.buildAndCheck("clean", "build") { result ->
+ Assert.assertTrue(result.output.contains("language version: 1.5"))
+ Assert.assertTrue(result.output.contains("api version: 1.5"))
+ Assert.assertTrue(result.output.contains("compiler version: $kotlinVersion"))
+ }
+ project.restore(buildFile.path)
+ }
+
+ @Test
+ fun testExcludeProcessor() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ File(project.root, "workload/build.gradle.kts")
+ .appendText("\nksp {\n excludeProcessor(\"TestProcessorProvider\")\n")
+ File(project.root, "workload/build.gradle.kts")
+ .appendText("\n excludeProcessor(\"NotMatchingAnything\")\n}\n")
+ gradleRunner.withArguments("build").buildAndFail().let {
+ Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload:kspKotlin")?.outcome)
+ Assert.assertEquals(TaskOutcome.FAILED, it.task(":workload:compileKotlin")?.outcome)
+ Assert.assertTrue("Unresolved reference: AClassBuilder" in it.output)
+ }
+ gradleRunner.withArguments("build").buildAndFail().let {
+ Assert.assertEquals(TaskOutcome.UP_TO_DATE, it.task(":workload:kspKotlin")?.outcome)
+ Assert.assertEquals(TaskOutcome.FAILED, it.task(":workload:compileKotlin")?.outcome)
+ Assert.assertTrue("Unresolved reference: AClassBuilder" in it.output)
+ }
+
+ project.restore("workload/build.gradle.kts")
+ File(project.root, "workload/build.gradle.kts")
+ .appendText("\nksp {\n excludeProcessor(\"DoNotMatch\")\n}\n")
+ gradleRunner.buildAndCheck("build")
+
+ project.restore("workload/build.gradle.kts")
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PsiCacheIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PsiCacheIT.kt
new file mode 100644
index 00000000..9f3a46dd
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PsiCacheIT.kt
@@ -0,0 +1,18 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Rule
+import org.junit.Test
+
+class PsiCacheIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("psi-cache", "test-processor")
+
+ @Test
+ fun testPsiCache() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+ gradleRunner.withArguments("assemble").build()
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/TemporaryTestProject.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/TemporaryTestProject.kt
new file mode 100644
index 00000000..fea748f0
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/TemporaryTestProject.kt
@@ -0,0 +1,41 @@
+package com.google.devtools.ksp.test
+
+import org.junit.rules.TemporaryFolder
+import java.io.File
+
+class TemporaryTestProject(projectName: String, baseProject: String? = null) : TemporaryFolder() {
+ private val testProjectSrc = File("src/test/resources", projectName)
+ private val baseProjectSrc = baseProject?.let { File("src/test/resources", baseProject) }
+
+ override fun before() {
+ super.before()
+
+ baseProjectSrc?.copyRecursively(root)
+ testProjectSrc.copyRecursively(root, true)
+
+ val kotlinVersion = System.getProperty("kotlinVersion")
+ val kspVersion = System.getProperty("kspVersion")
+ val agpVersion = System.getProperty("agpVersion")
+ val testRepo = System.getProperty("testRepo").replace(File.separator, "/")
+ val gradleProperties = File(root, "gradle.properties")
+ gradleProperties.appendText("\nkotlinVersion=$kotlinVersion")
+ gradleProperties.appendText("\nkspVersion=$kspVersion")
+ gradleProperties.appendText("\nagpVersion=$agpVersion")
+ gradleProperties.appendText("\ntestRepo=$testRepo")
+ gradleProperties.appendText("\norg.gradle.unsafe.configuration-cache=true")
+ gradleProperties.appendText("\nkotlin.jvm.target.validation.mode=warning")
+ // Uncomment this to debug compiler and compiler plugin.
+ // gradleProperties.appendText("\nsystemProp.kotlin.compiler.execution.strategy=in-process")
+ }
+
+ fun restore(file: String) {
+ fun copySafe(src: File, dst: File) {
+ if (src.exists())
+ src.copyTo(dst, true)
+ }
+ baseProjectSrc?.let {
+ copySafe(File(baseProjectSrc, file), File(root, file))
+ }
+ copySafe(File(testProjectSrc, file), File(root, file))
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VerboseIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VerboseIT.kt
new file mode 100644
index 00000000..935cd39b
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VerboseIT.kt
@@ -0,0 +1,67 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+import java.util.jar.JarFile
+
+class VerboseIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("playground")
+
+ private fun GradleRunner.buildAndCheck(vararg args: String, extraCheck: (BuildResult) -> Unit = {}) =
+ buildAndCheckOutcome(*args, outcome = TaskOutcome.SUCCESS, extraCheck = extraCheck)
+
+ private fun GradleRunner.buildAndCheckOutcome(
+ vararg args: String,
+ outcome: TaskOutcome,
+ extraCheck: (BuildResult) -> Unit = {}
+ ) {
+ val result = this.withArguments(*args).build()
+
+ Assert.assertEquals(outcome, result.task(":workload:build")?.outcome)
+
+ val artifact = File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar")
+ Assert.assertTrue(artifact.exists())
+
+ JarFile(artifact).use { jarFile ->
+ Assert.assertTrue(jarFile.getEntry("TestProcessor.log").size > 0)
+ Assert.assertTrue(jarFile.getEntry("hello/HELLO.class").size > 0)
+ Assert.assertTrue(jarFile.getEntry("g/G.class").size > 0)
+ Assert.assertTrue(jarFile.getEntry("com/example/AClassBuilder.class").size > 0)
+ }
+
+ extraCheck(result)
+ }
+
+ @Test
+ fun testNoProvider() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ File(
+ project.root,
+ "test-processor/src/main/resources/META-INF/services/" +
+ "com.google.devtools.ksp.processing.SymbolProcessorProvider"
+ ).delete()
+ gradleRunner.withArguments("build").buildAndFail().let { result ->
+ Assert.assertTrue(result.output.contains("No providers found in processor classpath."))
+ }
+ }
+
+ @Test
+ fun testProviderAndRoundLogging() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ gradleRunner.buildAndCheck("--debug", "clean", "build") { result ->
+ Assert.assertTrue(
+ result.output.contains(
+ "i: [ksp] loaded provider(s): [TestProcessorProvider, TestProcessorProvider2]"
+ )
+ )
+ Assert.assertTrue(result.output.contains("v: [ksp] round 3 of processing"))
+ }
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VersionCheckIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VersionCheckIT.kt
new file mode 100644
index 00000000..52a62a4d
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VersionCheckIT.kt
@@ -0,0 +1,42 @@
+package com.google.devtools.ksp.test
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Assert
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore
+class VersionCheckIT {
+ @Rule
+ @JvmField
+ val project: TemporaryTestProject = TemporaryTestProject("playground")
+
+ @Test
+ fun testVersion() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ val result = gradleRunner.withArguments(
+ "-PkotlinVersion=1.4.20", "clean", "build"
+ ).buildAndFail()
+ Assert.assertTrue(result.output.contains("is too new for kotlin"))
+ }
+
+ @Test
+ fun testVersionOK() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ val result = gradleRunner.withArguments(
+ "clean", "build"
+ ).build()
+ Assert.assertFalse(result.output.contains("is too new for kotlin"))
+ Assert.assertFalse(result.output.contains("is too old for kotlin"))
+ }
+
+ @Test
+ fun testMuteVersionCheck() {
+ val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+ val result = gradleRunner.withArguments(
+ "-PkotlinVersion=1.4.20", "-Pksp.version.check=false", "clean", "build"
+ ).buildAndFail()
+ Assert.assertFalse(result.output.contains("is too new for kotlin"))
+ }
+}
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/fixtures/BuildResultFixture.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/fixtures/BuildResultFixture.kt
new file mode 100644
index 00000000..cb66ea1b
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/fixtures/BuildResultFixture.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2020 Google LLC
+ * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ *
+ * 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.google.devtools.ksp.test.fixtures
+
+import org.gradle.testkit.runner.BuildResult
+
+private val compileKotlinSrcs = Regex("\\[KOTLIN\\] compile iteration: ([^\\r\\n]*)")
+
+class BuildResultFixture(private val result: BuildResult) {
+
+ /** Get all compiled Kotlin sources in the current build. */
+ val compiledKotlinSources by lazy {
+ compileKotlinSrcs.findAll(result.output)
+ .asIterable()
+ .flatMap {
+ it.groups[1]!!.value.split(", ")
+ }.toSet()
+ }
+}
diff --git a/integration-tests/src/test/resources/buildcache/settings.gradle.kts b/integration-tests/src/test/resources/buildcache/settings.gradle.kts
new file mode 100644
index 00000000..24430bc3
--- /dev/null
+++ b/integration-tests/src/test/resources/buildcache/settings.gradle.kts
@@ -0,0 +1,27 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+buildCache {
+ val buildCacheDir: String by settings
+ local {
+ directory = File(buildCacheDir)
+ removeUnusedEntriesAfterDays = 30
+ }
+}
+
+rootProject.name = "playground"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/cmd-options/build.gradle.kts b/integration-tests/src/test/resources/cmd-options/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/cmd-options/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/cmd-options/gradle.properties b/integration-tests/src/test/resources/cmd-options/gradle.properties
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/integration-tests/src/test/resources/cmd-options/gradle.properties
diff --git a/integration-tests/src/test/resources/cmd-options/processors/build.gradle.kts b/integration-tests/src/test/resources/cmd-options/processors/build.gradle.kts
new file mode 100644
index 00000000..999cb80a
--- /dev/null
+++ b/integration-tests/src/test/resources/cmd-options/processors/build.gradle.kts
@@ -0,0 +1,23 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/cmd-options/processors/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/cmd-options/processors/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..c38d2298
--- /dev/null
+++ b/integration-tests/src/test/resources/cmd-options/processors/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,37 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+
+class TestProcessor : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+ lateinit var options: Map<String, String>
+ var rounds = 0
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger
+ ) {
+ this.logger = logger
+ this.options = options
+ this.codeGenerator = codeGenerator
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (options.containsKey("error")) {
+ throw IllegalStateException("Error on request")
+ }
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return TestProcessor().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/cmd-options/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/cmd-options/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..e6522f19
--- /dev/null
+++ b/integration-tests/src/test/resources/cmd-options/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+TestProcessorProvider \ No newline at end of file
diff --git a/integration-tests/src/test/resources/cmd-options/settings.gradle.kts b/integration-tests/src/test/resources/cmd-options/settings.gradle.kts
new file mode 100644
index 00000000..79202ef6
--- /dev/null
+++ b/integration-tests/src/test/resources/cmd-options/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kspVersion: String by settings
+ val kotlinVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "cmd-options"
+
+include(":workload")
+include(":processors")
diff --git a/integration-tests/src/test/resources/cmd-options/workload/build.gradle.kts b/integration-tests/src/test/resources/cmd-options/workload/build.gradle.kts
new file mode 100644
index 00000000..a79cec86
--- /dev/null
+++ b/integration-tests/src/test/resources/cmd-options/workload/build.gradle.kts
@@ -0,0 +1,19 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ ksp(project(":processors"))
+}
diff --git a/integration-tests/src/test/resources/cmd-options/workload/src/main/kotlin/com/example/A.kt b/integration-tests/src/test/resources/cmd-options/workload/src/main/kotlin/com/example/A.kt
new file mode 100644
index 00000000..ebf0688c
--- /dev/null
+++ b/integration-tests/src/test/resources/cmd-options/workload/src/main/kotlin/com/example/A.kt
@@ -0,0 +1,4 @@
+package com.example
+
+fun main() {
+}
diff --git a/integration-tests/src/test/resources/hmpp/build.gradle.kts b/integration-tests/src/test/resources/hmpp/build.gradle.kts
new file mode 100644
index 00000000..8dd65667
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ kotlin("multiplatform") apply false
+}
+
+val testRepo: String by project
+allprojects {
+ repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
diff --git a/integration-tests/src/test/resources/hmpp/gradle.properties b/integration-tests/src/test/resources/hmpp/gradle.properties
new file mode 100644
index 00000000..4a9594ae
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx2048M \ No newline at end of file
diff --git a/integration-tests/src/test/resources/hmpp/settings.gradle.kts b/integration-tests/src/test/resources/hmpp/settings.gradle.kts
new file mode 100644
index 00000000..0f0b53a3
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion apply false
+ kotlin("multiplatform") version kotlinVersion apply false
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "hmpp"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/hmpp/test-processor/build.gradle.kts b/integration-tests/src/test/resources/hmpp/test-processor/build.gradle.kts
new file mode 100644
index 00000000..842898f6
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/test-processor/build.gradle.kts
@@ -0,0 +1,22 @@
+val kspVersion: String by project
+
+plugins {
+ kotlin("multiplatform")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm()
+ sourceSets {
+ val jvmMain by getting {
+ dependencies {
+ implementation("com.squareup:javapoet:1.12.1")
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+ }
+ kotlin.srcDir("src/main/kotlin")
+ resources.srcDir("src/main/resources")
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/hmpp/test-processor/src/main/kotlin/EchoProcessor.kt b/integration-tests/src/test/resources/hmpp/test-processor/src/main/kotlin/EchoProcessor.kt
new file mode 100644
index 00000000..0b1c39a6
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/test-processor/src/main/kotlin/EchoProcessor.kt
@@ -0,0 +1,27 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+
+class EchoProcessor(val codeGenerator: CodeGenerator, val logger: KSPLogger) : SymbolProcessor {
+ var invoked = false
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (invoked) {
+ return emptyList()
+ }
+ invoked = true
+
+ val allInputs = resolver.getAllFiles().map { it.fileName.split(".").first() }.sorted().joinToString("_")
+
+ logger.warn("EchoProcessor: $allInputs")
+
+ codeGenerator.createNewFile(Dependencies(true), "", "($allInputs)").close()
+
+ return emptyList()
+ }
+}
+
+class EchoProcessorProvider : SymbolProcessorProvider {
+ override fun create(env: SymbolProcessorEnvironment): SymbolProcessor {
+ return EchoProcessor(env.codeGenerator, env.logger)
+ }
+}
diff --git a/integration-tests/src/test/resources/hmpp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/hmpp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..c0cdf71a
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+EchoProcessorProvider
diff --git a/integration-tests/src/test/resources/hmpp/workload/build.gradle.kts b/integration-tests/src/test/resources/hmpp/workload/build.gradle.kts
new file mode 100644
index 00000000..a6574fc3
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/workload/build.gradle.kts
@@ -0,0 +1,55 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.google.devtools.ksp")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm {
+ withJava()
+ }
+
+ js(IR) {
+ browser()
+ }
+
+ linuxX64() {
+ binaries {
+ executable()
+ }
+ }
+
+ sourceSets {
+ val commonMain by getting
+
+ val jvmJs by sourceSets.creating {
+ dependsOn(commonMain)
+ }
+
+ val jvmLinuxX64 by sourceSets.creating {
+ dependsOn(commonMain)
+ }
+
+ val jvmOnly by sourceSets.creating {
+ dependsOn(jvmJs)
+ dependsOn(jvmLinuxX64)
+ }
+
+ val linuxX64Main by getting {
+ dependsOn(jvmLinuxX64)
+ }
+
+ val jvmMain by getting {
+ dependsOn(jvmOnly)
+ }
+
+ val jsMain by getting {
+ dependsOn(jvmJs)
+ }
+ }
+}
+
+dependencies {
+ add("ksp", project(":test-processor"))
+}
diff --git a/integration-tests/src/test/resources/hmpp/workload/src/commonMain/kotlin/CommonMain.kt b/integration-tests/src/test/resources/hmpp/workload/src/commonMain/kotlin/CommonMain.kt
new file mode 100644
index 00000000..301f3efb
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/workload/src/commonMain/kotlin/CommonMain.kt
@@ -0,0 +1 @@
+class CommonMain
diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jsMain/kotlin/JsMain.kt b/integration-tests/src/test/resources/hmpp/workload/src/jsMain/kotlin/JsMain.kt
new file mode 100644
index 00000000..1192259a
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/workload/src/jsMain/kotlin/JsMain.kt
@@ -0,0 +1 @@
+class JsMain
diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jvmJs/kotlin/JvmJs.kt b/integration-tests/src/test/resources/hmpp/workload/src/jvmJs/kotlin/JvmJs.kt
new file mode 100644
index 00000000..b5ad67b7
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/workload/src/jvmJs/kotlin/JvmJs.kt
@@ -0,0 +1 @@
+class JvmJs
diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jvmLinuxX64/kotlin/JvmLinuxX64.kt b/integration-tests/src/test/resources/hmpp/workload/src/jvmLinuxX64/kotlin/JvmLinuxX64.kt
new file mode 100644
index 00000000..38ab721d
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/workload/src/jvmLinuxX64/kotlin/JvmLinuxX64.kt
@@ -0,0 +1 @@
+class JvmLinuxX64
diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jvmMain/kotlin/JvmMain.kt b/integration-tests/src/test/resources/hmpp/workload/src/jvmMain/kotlin/JvmMain.kt
new file mode 100644
index 00000000..ffae14b7
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/workload/src/jvmMain/kotlin/JvmMain.kt
@@ -0,0 +1 @@
+class JvmMain
diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jvmOnly/kotlin/JvmOnly.kt b/integration-tests/src/test/resources/hmpp/workload/src/jvmOnly/kotlin/JvmOnly.kt
new file mode 100644
index 00000000..a373d6e5
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/workload/src/jvmOnly/kotlin/JvmOnly.kt
@@ -0,0 +1 @@
+class JvmOnly
diff --git a/integration-tests/src/test/resources/hmpp/workload/src/linuxX64Main/kotlin/LinuxX64Main.kt b/integration-tests/src/test/resources/hmpp/workload/src/linuxX64Main/kotlin/LinuxX64Main.kt
new file mode 100644
index 00000000..3058bc8d
--- /dev/null
+++ b/integration-tests/src/test/resources/hmpp/workload/src/linuxX64Main/kotlin/LinuxX64Main.kt
@@ -0,0 +1,4 @@
+class LinuxX64Main
+
+fun main() {
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/gradle.properties b/integration-tests/src/test/resources/incremental-classpath/gradle.properties
new file mode 100644
index 00000000..6b0be573
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/gradle.properties
@@ -0,0 +1,3 @@
+ksp.incremental=true
+ksp.incremental.log=true
+ksp.incremental.intermodule=true \ No newline at end of file
diff --git a/integration-tests/src/test/resources/incremental-classpath/l1/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l1/build.gradle.kts
new file mode 100644
index 00000000..b89421bb
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l1/build.gradle.kts
@@ -0,0 +1,18 @@
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":l2"))
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/l1/src/main/kotlin/p1/L1.kt b/integration-tests/src/test/resources/incremental-classpath/l1/src/main/kotlin/p1/L1.kt
new file mode 100644
index 00000000..dfd4184b
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l1/src/main/kotlin/p1/L1.kt
@@ -0,0 +1,3 @@
+package p1
+
+open class L1 : L2()
diff --git a/integration-tests/src/test/resources/incremental-classpath/l2/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l2/build.gradle.kts
new file mode 100644
index 00000000..d4adefee
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l2/build.gradle.kts
@@ -0,0 +1,17 @@
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/l2/src/main/kotlin/p1/L2.kt b/integration-tests/src/test/resources/incremental-classpath/l2/src/main/kotlin/p1/L2.kt
new file mode 100644
index 00000000..ce4f1ec1
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l2/src/main/kotlin/p1/L2.kt
@@ -0,0 +1,3 @@
+package p1
+
+open class L2
diff --git a/integration-tests/src/test/resources/incremental-classpath/l3/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l3/build.gradle.kts
new file mode 100644
index 00000000..d4adefee
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l3/build.gradle.kts
@@ -0,0 +1,17 @@
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/l3/src/main/kotlin/p1/L3.kt b/integration-tests/src/test/resources/incremental-classpath/l3/src/main/kotlin/p1/L3.kt
new file mode 100644
index 00000000..1b79ba13
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l3/src/main/kotlin/p1/L3.kt
@@ -0,0 +1,3 @@
+package p1
+
+open class L3
diff --git a/integration-tests/src/test/resources/incremental-classpath/l4/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l4/build.gradle.kts
new file mode 100644
index 00000000..d4adefee
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l4/build.gradle.kts
@@ -0,0 +1,17 @@
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/l4/src/main/kotlin/p1/L4.kt b/integration-tests/src/test/resources/incremental-classpath/l4/src/main/kotlin/p1/L4.kt
new file mode 100644
index 00000000..a8c6aa17
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l4/src/main/kotlin/p1/L4.kt
@@ -0,0 +1,3 @@
+package p1
+
+open class L4
diff --git a/integration-tests/src/test/resources/incremental-classpath/l5/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l5/build.gradle.kts
new file mode 100644
index 00000000..d4adefee
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l5/build.gradle.kts
@@ -0,0 +1,17 @@
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/l5/src/main/kotlin/p1/L5.kt b/integration-tests/src/test/resources/incremental-classpath/l5/src/main/kotlin/p1/L5.kt
new file mode 100644
index 00000000..5bfed66b
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/l5/src/main/kotlin/p1/L5.kt
@@ -0,0 +1,3 @@
+package p1
+
+open class L5
diff --git a/integration-tests/src/test/resources/incremental-classpath/settings.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/settings.gradle.kts
new file mode 100644
index 00000000..c4a1f4cd
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/settings.gradle.kts
@@ -0,0 +1,24 @@
+pluginManagement {
+ val kspVersion: String by settings
+ val kotlinVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "incremental-test"
+
+include(":workload")
+include(":validator")
+include(":l1")
+include(":l2")
+include(":l3")
+include(":l4")
+include(":l5")
diff --git a/integration-tests/src/test/resources/incremental-classpath/validator/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/validator/build.gradle.kts
new file mode 100644
index 00000000..ab249cf0
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/validator/build.gradle.kts
@@ -0,0 +1,24 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/validator/src/main/kotlin/Validator.kt b/integration-tests/src/test/resources/incremental-classpath/validator/src/main/kotlin/Validator.kt
new file mode 100644
index 00000000..a467ade0
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/validator/src/main/kotlin/Validator.kt
@@ -0,0 +1,79 @@
+import com.google.devtools.ksp.getClassDeclarationByName
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+import com.google.devtools.ksp.visitor.KSDefaultVisitor
+import java.io.OutputStreamWriter
+
+class Validator : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+ var processed = false
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger,
+ ) {
+ this.codeGenerator = codeGenerator
+ this.logger = logger
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (processed) {
+ return emptyList()
+ }
+ val validator = object : KSDefaultVisitor<OutputStreamWriter, Unit>() {
+ override fun defaultHandler(node: KSNode, data: OutputStreamWriter) = Unit
+
+ override fun visitDeclaration(declaration: KSDeclaration, data: OutputStreamWriter) {
+ data.write(declaration.qualifiedName?.asString() ?: declaration.simpleName.asString())
+ declaration.validate()
+ }
+ }
+
+ // for each file, create an output from it and everything reachable from it.
+ val files = resolver.getAllFiles()
+ files.forEach { file ->
+ logger.warn("${file.packageName.asString()}/${file.fileName}")
+ val output = OutputStreamWriter(
+ codeGenerator.createNewFile(
+ Dependencies(false, file),
+ file.packageName.asString(),
+ file.fileName,
+ "log"
+ )
+ )
+ file.declarations.forEach {
+ it.accept(validator, output)
+ }
+ output.close()
+ }
+
+ // create an output from k3 + l4
+ val k3 = resolver.getClassDeclarationByName("p1.K3")!!
+ val l4 = resolver.getClassDeclarationByName("p1.L4")!!
+ codeGenerator.createNewFile(Dependencies(false), "p1", "l4", "log")
+ codeGenerator.associateWithClasses(listOf(k3, l4), "p1", "l4", "log")
+
+ // create an output from l5
+ val l5 = resolver.getClassDeclarationByName("p1.L5")!!
+ codeGenerator.createNewFile(Dependencies(false), "p1", "l5", "log")
+ codeGenerator.associateWithClasses(listOf(l5), "p1", "l5", "log")
+ logger.warn("processing done")
+
+ processed = true
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment,
+ ): SymbolProcessor {
+ return Validator().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/incremental-classpath/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..c91e3e9e
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+TestProcessorProvider
diff --git a/integration-tests/src/test/resources/incremental-classpath/workload/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/workload/build.gradle.kts
new file mode 100644
index 00000000..e8f910b1
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/workload/build.gradle.kts
@@ -0,0 +1,26 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":validator"))
+ implementation(project(":l1"))
+ implementation(project(":l2"))
+ implementation(project(":l3"))
+ implementation(project(":l4"))
+ implementation(project(":l5"))
+ testImplementation("junit:junit:4.12")
+ ksp(project(":validator"))
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K1.kt b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K1.kt
new file mode 100644
index 00000000..69c56673
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K1.kt
@@ -0,0 +1,5 @@
+package p1
+
+open class K1 {
+ val v = L1()
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K2.kt b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K2.kt
new file mode 100644
index 00000000..ea09ec3d
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K2.kt
@@ -0,0 +1,5 @@
+package p1
+
+open class K2 {
+ val v = L2()
+}
diff --git a/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K3.kt b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K3.kt
new file mode 100644
index 00000000..29929705
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K3.kt
@@ -0,0 +1,5 @@
+package p1
+
+open class K3 {
+ val v = L3()
+}
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/build.gradle.kts b/integration-tests/src/test/resources/incremental-multi-chain/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/gradle.properties b/integration-tests/src/test/resources/incremental-multi-chain/gradle.properties
new file mode 100644
index 00000000..f9325643
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/gradle.properties
@@ -0,0 +1,2 @@
+ksp.incremental=true
+ksp.incremental.log=true \ No newline at end of file
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/processors/build.gradle.kts b/integration-tests/src/test/resources/incremental-multi-chain/processors/build.gradle.kts
new file mode 100644
index 00000000..ab249cf0
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/build.gradle.kts
@@ -0,0 +1,24 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Aggregator.kt b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Aggregator.kt
new file mode 100644
index 00000000..d7c25217
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Aggregator.kt
@@ -0,0 +1,46 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import java.io.OutputStreamWriter
+
+class Aggregator : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger,
+ ) {
+ this.codeGenerator = codeGenerator
+ this.logger = logger
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ val impls = resolver.getSymbolsWithAnnotation("Impl").map { it as KSDeclaration }.toList()
+ if (impls.isNotEmpty()) {
+ val names = impls.map { it.simpleName.asString() }.sorted()
+ OutputStreamWriter(
+ codeGenerator.createNewFile(
+ Dependencies(true), "", "AllImpls"
+ )
+ ).use {
+ it.write("class AllImpls {\n")
+ it.write(" override fun toString() = \"$names\"\n")
+ it.write("}\n")
+ }
+ codeGenerator.associate(impls.map { it.containingFile!! }.toList(), "", "AllImpls")
+ }
+ return emptyList()
+ }
+}
+
+class AggregatorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment,
+ ): SymbolProcessor {
+ return Aggregator().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/ImplGen.kt b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/ImplGen.kt
new file mode 100644
index 00000000..fec8d2d2
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/ImplGen.kt
@@ -0,0 +1,46 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import java.io.OutputStreamWriter
+
+class ImplGen : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger,
+ ) {
+ this.codeGenerator = codeGenerator
+ this.logger = logger
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ resolver.getSymbolsWithAnnotation("NeedsImpl").forEach { decl ->
+ decl as KSClassDeclaration
+ val file = decl.containingFile!!
+ val baseName = decl.simpleName.asString()
+ val implName = baseName + "Impl"
+ OutputStreamWriter(
+ codeGenerator.createNewFile(
+ Dependencies(false, file),
+ "", implName
+ )
+ ).use {
+ it.write("@Impl class $implName : $baseName\n")
+ }
+ }
+ return emptyList()
+ }
+}
+
+class ImplGenProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment,
+ ): SymbolProcessor {
+ return ImplGen().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Validator.kt b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Validator.kt
new file mode 100644
index 00000000..5ecf7527
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Validator.kt
@@ -0,0 +1,36 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+
+class Validator : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger,
+ ) {
+ this.codeGenerator = codeGenerator
+ this.logger = logger
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ resolver.getNewFiles().forEach {
+ logger.warn("validating ${it.fileName}")
+ it.validate()
+ }
+ return emptyList()
+ }
+}
+
+class ValidatorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment,
+ ): SymbolProcessor {
+ return Validator().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..906e3eaf
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1,3 @@
+AggregatorProvider
+ImplGenProvider
+ValidatorProvider
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/settings.gradle.kts b/integration-tests/src/test/resources/incremental-multi-chain/settings.gradle.kts
new file mode 100644
index 00000000..18f47de4
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kspVersion: String by settings
+ val kotlinVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "incremental-test"
+
+include(":workload")
+include(":processors")
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/workload/build.gradle.kts b/integration-tests/src/test/resources/incremental-multi-chain/workload/build.gradle.kts
new file mode 100644
index 00000000..2f6a0fc5
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/workload/build.gradle.kts
@@ -0,0 +1,24 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+ application
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ ksp(project(":processors"))
+}
+
+application {
+ mainClassName = "MainKt"
+}
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/K1.kt b/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/K1.kt
new file mode 100644
index 00000000..ac8854e9
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/K1.kt
@@ -0,0 +1,2 @@
+@NeedsImpl
+interface K1
diff --git a/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/Main.kt b/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/Main.kt
new file mode 100644
index 00000000..cdf05607
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/Main.kt
@@ -0,0 +1,8 @@
+annotation class NeedsImpl
+annotation class Impl
+
+val x: AllImpls = AllImpls()
+
+fun main() {
+ println(x.toString())
+}
diff --git a/integration-tests/src/test/resources/incremental-removal/build.gradle.kts b/integration-tests/src/test/resources/incremental-removal/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-removal/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/incremental-removal/gradle.properties b/integration-tests/src/test/resources/incremental-removal/gradle.properties
new file mode 100644
index 00000000..f9325643
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-removal/gradle.properties
@@ -0,0 +1,2 @@
+ksp.incremental=true
+ksp.incremental.log=true \ No newline at end of file
diff --git a/integration-tests/src/test/resources/incremental-removal/settings.gradle.kts b/integration-tests/src/test/resources/incremental-removal/settings.gradle.kts
new file mode 100644
index 00000000..75f651ef
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-removal/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kspVersion: String by settings
+ val kotlinVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "incremental-test"
+
+include(":workload")
+include(":validator")
diff --git a/integration-tests/src/test/resources/incremental-removal/validator/build.gradle.kts b/integration-tests/src/test/resources/incremental-removal/validator/build.gradle.kts
new file mode 100644
index 00000000..ab249cf0
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-removal/validator/build.gradle.kts
@@ -0,0 +1,24 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/incremental-removal/validator/src/main/kotlin/Validator.kt b/integration-tests/src/test/resources/incremental-removal/validator/src/main/kotlin/Validator.kt
new file mode 100644
index 00000000..635c7d7a
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-removal/validator/src/main/kotlin/Validator.kt
@@ -0,0 +1,48 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+import java.io.OutputStreamWriter
+
+class Validator : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger,
+ ) {
+ this.codeGenerator = codeGenerator
+ this.logger = logger
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ resolver.getSymbolsWithAnnotation("p1.MyAnnotation").singleOrNull()?.let { decl ->
+ decl as KSClassDeclaration
+ val file = decl.containingFile!!
+ OutputStreamWriter(
+ codeGenerator.createNewFile(
+ Dependencies(false, file),
+ "p1", "Foo"
+ )
+ ).use {
+ it.write("package p1\n\nclass Foo : Bar { override fun s() = \"generated\" }\n")
+ }
+ }
+ resolver.getNewFiles().forEach {
+ it.validate()
+ }
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment,
+ ): SymbolProcessor {
+ return Validator().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/incremental-removal/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/incremental-removal/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..c91e3e9e
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-removal/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+TestProcessorProvider
diff --git a/integration-tests/src/test/resources/incremental-removal/workload/build.gradle.kts b/integration-tests/src/test/resources/incremental-removal/workload/build.gradle.kts
new file mode 100644
index 00000000..caa2c8c2
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-removal/workload/build.gradle.kts
@@ -0,0 +1,27 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+ application
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":validator"))
+ testImplementation("junit:junit:4.12")
+ ksp(project(":validator"))
+ kspTest(project(":validator"))
+}
+
+application {
+ mainClassName = "p1.MainKt"
+}
diff --git a/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/K1.kt b/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/K1.kt
new file mode 100644
index 00000000..5b31cfdb
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/K1.kt
@@ -0,0 +1,4 @@
+package p1
+
+@MyAnnotation
+class K1
diff --git a/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/Main.kt b/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/Main.kt
new file mode 100644
index 00000000..24b10a60
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/Main.kt
@@ -0,0 +1,12 @@
+package p1
+
+interface Bar {
+ fun s(): String
+}
+val bar: Foo = Foo()
+
+annotation class MyAnnotation
+
+fun main() {
+ println("result: ${bar.s()}")
+}
diff --git a/integration-tests/src/test/resources/incremental/build.gradle.kts b/integration-tests/src/test/resources/incremental/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/incremental/gradle.properties b/integration-tests/src/test/resources/incremental/gradle.properties
new file mode 100644
index 00000000..f9325643
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/gradle.properties
@@ -0,0 +1,2 @@
+ksp.incremental=true
+ksp.incremental.log=true \ No newline at end of file
diff --git a/integration-tests/src/test/resources/incremental/settings.gradle.kts b/integration-tests/src/test/resources/incremental/settings.gradle.kts
new file mode 100644
index 00000000..75f651ef
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kspVersion: String by settings
+ val kotlinVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "incremental-test"
+
+include(":workload")
+include(":validator")
diff --git a/integration-tests/src/test/resources/incremental/validator/build.gradle.kts b/integration-tests/src/test/resources/incremental/validator/build.gradle.kts
new file mode 100644
index 00000000..ab249cf0
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/validator/build.gradle.kts
@@ -0,0 +1,24 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/incremental/validator/src/main/kotlin/Validator.kt b/integration-tests/src/test/resources/incremental/validator/src/main/kotlin/Validator.kt
new file mode 100644
index 00000000..96952d10
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/validator/src/main/kotlin/Validator.kt
@@ -0,0 +1,62 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+import com.google.devtools.ksp.visitor.KSDefaultVisitor
+import java.io.OutputStreamWriter
+
+class Validator : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+ var processed = false
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger,
+ ) {
+ this.codeGenerator = codeGenerator
+ this.logger = logger
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (processed) {
+ return emptyList()
+ }
+ val validator = object : KSDefaultVisitor<OutputStreamWriter, Unit>() {
+ override fun defaultHandler(node: KSNode, data: OutputStreamWriter) = Unit
+
+ override fun visitDeclaration(declaration: KSDeclaration, data: OutputStreamWriter) {
+ data.write(declaration.qualifiedName?.asString() ?: declaration.simpleName.asString())
+ declaration.validate()
+ }
+ }
+
+ val files = resolver.getAllFiles()
+ files.forEach { file ->
+ logger.warn("${file.packageName.asString()}/${file.fileName}")
+ val output = OutputStreamWriter(
+ codeGenerator.createNewFile(
+ Dependencies(false, file),
+ file.packageName.asString(), file.fileName, "log"
+ )
+ )
+ file.declarations.forEach {
+ it.accept(validator, output)
+ }
+ output.close()
+ }
+ processed = true
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment,
+ ): SymbolProcessor {
+ return Validator().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/incremental/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/incremental/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..c91e3e9e
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+TestProcessorProvider
diff --git a/integration-tests/src/test/resources/incremental/workload/build.gradle.kts b/integration-tests/src/test/resources/incremental/workload/build.gradle.kts
new file mode 100644
index 00000000..00e18e2a
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/build.gradle.kts
@@ -0,0 +1,22 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":validator"))
+ testImplementation("junit:junit:4.12")
+ ksp(project(":validator"))
+ kspTest(project(":validator"))
+}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J1.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J1.java
new file mode 100644
index 00000000..b2e2e87c
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J1.java
@@ -0,0 +1,3 @@
+package p1;
+
+public class J1 {}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J2.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J2.java
new file mode 100644
index 00000000..7de0687e
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J2.java
@@ -0,0 +1,4 @@
+package p1;
+
+public class J2 {
+}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2J.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2J.java
new file mode 100644
index 00000000..9866bfee
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2J.java
@@ -0,0 +1,11 @@
+package p1;
+
+import p2.J2;
+import p3.*;
+
+public class TestJ2J {
+ J1 j1 = null;
+ J2 j2 = null;
+ J3 j3 = null;
+}
+
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2K.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2K.java
new file mode 100644
index 00000000..b5d055bb
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2K.java
@@ -0,0 +1,10 @@
+package p1;
+
+import p2.K2;
+import p3.*;
+
+public class TestJ2K {
+ K1 k1 = null;
+ K2 k2 = null;
+ K3 k3 = null;
+}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p2/J2.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p2/J2.java
new file mode 100644
index 00000000..1e10b7c4
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p2/J2.java
@@ -0,0 +1,4 @@
+package p2;
+
+public class J2 {
+}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J1.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J1.java
new file mode 100644
index 00000000..2e5ed679
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J1.java
@@ -0,0 +1,4 @@
+package p3;
+
+public class J1 {
+}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J2.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J2.java
new file mode 100644
index 00000000..fc7d454e
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J2.java
@@ -0,0 +1,4 @@
+package p3;
+
+public class J2 {
+}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J3.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J3.java
new file mode 100644
index 00000000..08cfb5ec
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J3.java
@@ -0,0 +1,4 @@
+package p3;
+
+public class J3 {
+}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K1.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K1.kt
new file mode 100644
index 00000000..eb739a3d
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K1.kt
@@ -0,0 +1,3 @@
+package p1
+
+open class K1
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K2.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K2.kt
new file mode 100644
index 00000000..e5134f49
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K2.kt
@@ -0,0 +1,3 @@
+package p1
+
+open class K2
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2J.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2J.kt
new file mode 100644
index 00000000..81cb4d47
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2J.kt
@@ -0,0 +1,10 @@
+package p1
+
+import p2.J2
+import p3.*
+
+open class TestK2J() {
+ val v1: J1 = TODO()
+ val v2: J2 = TODO()
+ val v3: J3 = TODO()
+}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2K.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2K.kt
new file mode 100644
index 00000000..401e340f
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2K.kt
@@ -0,0 +1,10 @@
+package p1
+
+import p2.K2
+import p3.*
+
+open class TestK2K() {
+ val v1: K1 = TODO()
+ val v2: K2 = TODO()
+ val v3: K3 = TODO()
+}
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p2/K2.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p2/K2.kt
new file mode 100644
index 00000000..55ae6f0a
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p2/K2.kt
@@ -0,0 +1,3 @@
+package p2
+
+open class K2
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K1.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K1.kt
new file mode 100644
index 00000000..6f727609
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K1.kt
@@ -0,0 +1,3 @@
+package p3
+
+open class K1
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K2.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K2.kt
new file mode 100644
index 00000000..d3aa0f93
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K2.kt
@@ -0,0 +1,3 @@
+package p3
+
+open class K2
diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K3.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K3.kt
new file mode 100644
index 00000000..522ffff4
--- /dev/null
+++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K3.kt
@@ -0,0 +1,3 @@
+package p3
+
+open class K3
diff --git a/integration-tests/src/test/resources/init-plus-provider/build.gradle.kts b/integration-tests/src/test/resources/init-plus-provider/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/init-plus-provider/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/init-plus-provider/provider-processor/build.gradle.kts b/integration-tests/src/test/resources/init-plus-provider/provider-processor/build.gradle.kts
new file mode 100644
index 00000000..ab249cf0
--- /dev/null
+++ b/integration-tests/src/test/resources/init-plus-provider/provider-processor/build.gradle.kts
@@ -0,0 +1,24 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..7bb6d4d4
--- /dev/null
+++ b/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,41 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import java.io.OutputStream
+
+fun OutputStream.appendText(str: String) {
+ this.write(str.toByteArray())
+}
+
+class TestProcessor(options: Map<String, String>, val codeGenerator: CodeGenerator) : SymbolProcessor {
+ val file: OutputStream = codeGenerator.createNewFile(Dependencies(false), "", "TestProcessor", "log")
+
+ init {
+ file.appendText("TestProcessor: init($options)\n")
+
+ val javaFile = codeGenerator.createNewFile(Dependencies(false), "", "GeneratedFromProvider", "java")
+ javaFile.appendText("class GeneratedFromProvider {}")
+ }
+
+ var invoked = false
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (invoked) {
+ return emptyList()
+ }
+
+ val fileKt = codeGenerator.createNewFile(Dependencies(false), "", "HelloFromProvider", "java")
+
+ fileKt.appendText("public class HelloFromProvider{\n")
+ fileKt.appendText(" public int foo() { return 5678; }\n")
+ fileKt.appendText("}")
+
+ invoked = true
+ return emptyList()
+ }
+
+ class Provider : SymbolProcessorProvider {
+ override fun create(
+ environment: SymbolProcessorEnvironment
+ ): SymbolProcessor = TestProcessor(environment.options, environment.codeGenerator)
+ }
+}
diff --git a/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..5c51c309
--- /dev/null
+++ b/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+TestProcessor$Provider
diff --git a/integration-tests/src/test/resources/init-plus-provider/settings.gradle.kts b/integration-tests/src/test/resources/init-plus-provider/settings.gradle.kts
new file mode 100644
index 00000000..73f2260c
--- /dev/null
+++ b/integration-tests/src/test/resources/init-plus-provider/settings.gradle.kts
@@ -0,0 +1,22 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "init-plus-provider"
+
+include(":workload")
+include(":init-processor")
+include(":provider-processor")
diff --git a/integration-tests/src/test/resources/init-plus-provider/workload/build.gradle.kts b/integration-tests/src/test/resources/init-plus-provider/workload/build.gradle.kts
new file mode 100644
index 00000000..da44c4dd
--- /dev/null
+++ b/integration-tests/src/test/resources/init-plus-provider/workload/build.gradle.kts
@@ -0,0 +1,24 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ ksp(project(":provider-processor"))
+}
+
+ksp {
+ arg("option1", "value1")
+ arg("option2", "value2")
+}
diff --git a/integration-tests/src/test/resources/init-plus-provider/workload/src/main/java/com/example/A.kt b/integration-tests/src/test/resources/init-plus-provider/workload/src/main/java/com/example/A.kt
new file mode 100644
index 00000000..fd782149
--- /dev/null
+++ b/integration-tests/src/test/resources/init-plus-provider/workload/src/main/java/com/example/A.kt
@@ -0,0 +1,7 @@
+package com.example
+
+import HelloFromProvider
+
+fun main() {
+ HelloFromProvider().foo()
+}
diff --git a/integration-tests/src/test/resources/java-only/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/java-only/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..fe418e7b
--- /dev/null
+++ b/integration-tests/src/test/resources/java-only/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,30 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import java.io.OutputStreamWriter
+
+class TestProcessor(
+ val codeGenerator: CodeGenerator,
+ val logger: KSPLogger
+) : SymbolProcessor {
+ var rounds = 0
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (++rounds == 1) {
+ codeGenerator.createNewFile(Dependencies(false), "com.example", "Bar", "kt").use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write("package com.example\n\n")
+ writer.write("interface Bar\n")
+ }
+ }
+ }
+
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ environment: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return TestProcessor(environment.codeGenerator, environment.logger)
+ }
+}
diff --git a/integration-tests/src/test/resources/java-only/workload/src/main/java/com/example/Foo.java b/integration-tests/src/test/resources/java-only/workload/src/main/java/com/example/Foo.java
new file mode 100644
index 00000000..f6a7cf05
--- /dev/null
+++ b/integration-tests/src/test/resources/java-only/workload/src/main/java/com/example/Foo.java
@@ -0,0 +1,4 @@
+package com.example;
+
+class Foo implements Bar {
+}
diff --git a/integration-tests/src/test/resources/javaNestedClass/build.gradle.kts b/integration-tests/src/test/resources/javaNestedClass/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/javaNestedClass/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/javaNestedClass/settings.gradle.kts b/integration-tests/src/test/resources/javaNestedClass/settings.gradle.kts
new file mode 100644
index 00000000..5cb08c92
--- /dev/null
+++ b/integration-tests/src/test/resources/javaNestedClass/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "javaNestedClass"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/javaNestedClass/test-processor/build.gradle.kts b/integration-tests/src/test/resources/javaNestedClass/test-processor/build.gradle.kts
new file mode 100644
index 00000000..75c6c7c5
--- /dev/null
+++ b/integration-tests/src/test/resources/javaNestedClass/test-processor/build.gradle.kts
@@ -0,0 +1,25 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.squareup:javapoet:1.12.1")
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/kotlin/ValidateProcessor.kt b/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/kotlin/ValidateProcessor.kt
new file mode 100644
index 00000000..80931541
--- /dev/null
+++ b/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/kotlin/ValidateProcessor.kt
@@ -0,0 +1,38 @@
+import com.google.devtools.ksp.getClassDeclarationByName
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+
+class ValidateProcessor(env: SymbolProcessorEnvironment) : SymbolProcessor {
+ var logger: KSPLogger = env.logger
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ val javaClass = resolver.getClassDeclarationByName("com.example.JavaClass")!!
+ val nestedClass = javaClass.declarations.filterIsInstance<KSClassDeclaration>()
+ .single { it.simpleName.asString() == "NestedClass" }
+ val provideString = nestedClass.declarations.filterIsInstance<KSFunctionDeclaration>()
+ .single { it.simpleName.asString() == "provideString" }
+
+ if (provideString.returnType!!.resolve().isError) {
+ logger.error("provideString.returnType: not ok")
+ }
+ if (nestedClass.asStarProjectedType().isError == true) {
+ logger.error("$javaClass.asStarProjectedType(): not ok")
+ }
+ if (!nestedClass.validate()) {
+ logger.error("Failed to validate $nestedClass")
+ }
+ if (!javaClass.validate()) {
+ logger.error("Failed to validate $javaClass")
+ }
+ return emptyList()
+ }
+}
+
+class ValidateProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return ValidateProcessor(env)
+ }
+}
diff --git a/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..6af81c2a
--- /dev/null
+++ b/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+ValidateProcessorProvider
diff --git a/integration-tests/src/test/resources/javaNestedClass/workload/build.gradle.kts b/integration-tests/src/test/resources/javaNestedClass/workload/build.gradle.kts
new file mode 100644
index 00000000..f0ea52b0
--- /dev/null
+++ b/integration-tests/src/test/resources/javaNestedClass/workload/build.gradle.kts
@@ -0,0 +1,20 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":test-processor"))
+ ksp(project(":test-processor"))
+}
diff --git a/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/A.kt b/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/A.kt
new file mode 100644
index 00000000..ebf0688c
--- /dev/null
+++ b/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/A.kt
@@ -0,0 +1,4 @@
+package com.example
+
+fun main() {
+}
diff --git a/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/JavaClass.java b/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/JavaClass.java
new file mode 100644
index 00000000..2c0b0e32
--- /dev/null
+++ b/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/JavaClass.java
@@ -0,0 +1,22 @@
+package com.example;
+
+public class JavaClass {
+
+ public int b2;
+
+ public ENUM e;
+
+ public enum ENUM {
+ R,G,B
+ }
+
+ void inject(InjectionTarget t) {}
+
+ class InjectionTarget {}
+
+ static final class NestedClass {
+ static String provideString() {
+ return "str";
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/kmp/annotations/build.gradle.kts b/integration-tests/src/test/resources/kmp/annotations/build.gradle.kts
new file mode 100644
index 00000000..f02ad073
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/annotations/build.gradle.kts
@@ -0,0 +1,32 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.google.devtools.ksp")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm {
+ }
+ js(BOTH) {
+ browser()
+ nodejs()
+ }
+ linuxX64() {
+ }
+ androidNativeX64() {
+ }
+ androidNativeArm64() {
+ }
+ // TODO: Enable after CI's Xcode version catches up.
+ // iosArm64()
+ // macosX64()
+ mingwX64()
+ sourceSets {
+ val commonMain by getting
+ }
+}
+
+tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
+ kotlinOptions.freeCompilerArgs += "-Xuse-deprecated-legacy-compiler"
+}
diff --git a/integration-tests/src/test/resources/kmp/annotations/src/commonMain/kotlin/com/example/MyAnnotation.kt b/integration-tests/src/test/resources/kmp/annotations/src/commonMain/kotlin/com/example/MyAnnotation.kt
new file mode 100644
index 00000000..b938f1c1
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/annotations/src/commonMain/kotlin/com/example/MyAnnotation.kt
@@ -0,0 +1,3 @@
+package com.example
+
+annotation class MyAnnotation
diff --git a/integration-tests/src/test/resources/kmp/build.gradle.kts b/integration-tests/src/test/resources/kmp/build.gradle.kts
new file mode 100644
index 00000000..8dd65667
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ kotlin("multiplatform") apply false
+}
+
+val testRepo: String by project
+allprojects {
+ repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
diff --git a/integration-tests/src/test/resources/kmp/gradle.properties b/integration-tests/src/test/resources/kmp/gradle.properties
new file mode 100644
index 00000000..4a9594ae
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx2048M \ No newline at end of file
diff --git a/integration-tests/src/test/resources/kmp/settings.gradle.kts b/integration-tests/src/test/resources/kmp/settings.gradle.kts
new file mode 100644
index 00000000..651619e4
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/settings.gradle.kts
@@ -0,0 +1,24 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion apply false
+ kotlin("multiplatform") version kotlinVersion apply false
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "playground"
+
+include(":annotations")
+include(":workload")
+include(":workload-jvm")
+include(":workload-js")
+include(":workload-linuxX64")
+include(":workload-androidNative")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/kmp/test-processor/build.gradle.kts b/integration-tests/src/test/resources/kmp/test-processor/build.gradle.kts
new file mode 100644
index 00000000..842898f6
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/test-processor/build.gradle.kts
@@ -0,0 +1,22 @@
+val kspVersion: String by project
+
+plugins {
+ kotlin("multiplatform")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm()
+ sourceSets {
+ val jvmMain by getting {
+ dependencies {
+ implementation("com.squareup:javapoet:1.12.1")
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+ }
+ kotlin.srcDir("src/main/kotlin")
+ resources.srcDir("src/main/resources")
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ErrorProcessor.kt b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ErrorProcessor.kt
new file mode 100644
index 00000000..a702c7bc
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ErrorProcessor.kt
@@ -0,0 +1,61 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import java.io.OutputStream
+
+class ErrorProcessor : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+ lateinit var file: OutputStream
+ lateinit var exception: String
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger
+ ) {
+ exception = if (options.containsKey("exception")) {
+ options["exception"]!!
+ } else {
+ ""
+ }
+ if (exception == "init") {
+ throw Exception("Test Exception in init")
+ }
+ this.logger = logger
+ this.codeGenerator = codeGenerator
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (exception == "createTwice") {
+ codeGenerator.createNewFile(Dependencies.ALL_FILES, "create", "Twice").write("".toByteArray())
+ return emptyList()
+ }
+ if (exception == "process") {
+ throw Exception("Test Exception in process")
+ }
+ return emptyList()
+ }
+
+ override fun finish() {
+ if (exception == "finish") {
+ throw Exception("Test Exception in finish")
+ }
+ }
+
+ override fun onError() {
+ if (exception == "error") {
+ throw Exception("Test Exception in error")
+ }
+ }
+}
+
+class ErrorProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return ErrorProcessor().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..283d50f1
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,74 @@
+import com.google.devtools.ksp.getClassDeclarationByName
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.visitor.KSTopDownVisitor
+import java.io.OutputStreamWriter
+
+class TestProcessor(
+ val codeGenerator: CodeGenerator,
+ val logger: KSPLogger,
+ val env: SymbolProcessorEnvironment
+) : SymbolProcessor {
+ var invoked = false
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ val allFiles = resolver.getAllFiles().map { it.fileName }
+ logger.warn(allFiles.toList().toString())
+ if (invoked) {
+ return emptyList()
+ }
+ invoked = true
+
+ logger.warn("language version: ${env.kotlinVersion}")
+ logger.warn("api version: ${env.apiVersion}")
+ logger.warn("compiler version: ${env.compilerVersion}")
+ val platforms = env.platforms.map { it.toString() }
+ logger.warn("platforms: $platforms")
+ val list = resolver.getClassDeclarationByName("kotlin.collections.List")
+ logger.warn("List has superTypes: ${list!!.superTypes.count() > 0}")
+
+ codeGenerator.createNewFile(Dependencies(false), "", "Foo", "kt").use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write("package com.example\n\n")
+ writer.write("class Foo {\n")
+
+ val visitor = ClassVisitor()
+ resolver.getAllFiles().forEach {
+ it.accept(visitor, writer)
+ }
+
+ writer.write("}\n")
+ }
+ }
+
+ allFiles.forEach {
+ val fn = it.replace(".", "_dot_")
+ codeGenerator.createNewFile(Dependencies(false), "", fn, "kt").use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write("// empty\n")
+ }
+ }
+ }
+ return emptyList()
+ }
+}
+
+class ClassVisitor : KSTopDownVisitor<OutputStreamWriter, Unit>() {
+ override fun defaultHandler(node: KSNode, data: OutputStreamWriter) {
+ }
+
+ override fun visitClassDeclaration(
+ classDeclaration: KSClassDeclaration,
+ data: OutputStreamWriter
+ ) {
+ super.visitClassDeclaration(classDeclaration, data)
+ val symbolName = classDeclaration.simpleName.asString().toLowerCase()
+ data.write(" val $symbolName = true\n")
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(env: SymbolProcessorEnvironment): SymbolProcessor {
+ return TestProcessor(env.codeGenerator, env.logger, env)
+ }
+}
diff --git a/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ValidateProcessor.kt b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ValidateProcessor.kt
new file mode 100644
index 00000000..45dbe22b
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ValidateProcessor.kt
@@ -0,0 +1,29 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+
+class ValidateProcessor(val codeGenerator: CodeGenerator, val logger: KSPLogger) : SymbolProcessor {
+ var invoked = false
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (invoked) {
+ return emptyList()
+ }
+ invoked = true
+
+ val toValidate = resolver.getSymbolsWithAnnotation("com.example.MyAnnotation")
+ if (toValidate.firstOrNull() == null || !toValidate.all { it.validate() }) {
+ logger.error("$toValidate.validate(): not ok")
+ }
+ if ((toValidate as? KSClassDeclaration)?.asStarProjectedType()?.isError == true) {
+ logger.error("$toValidate.asStarProjectedType(): not ok")
+ }
+ return emptyList()
+ }
+}
+
+class ValidateProcessorProvider : SymbolProcessorProvider {
+ override fun create(env: SymbolProcessorEnvironment): SymbolProcessor {
+ return ValidateProcessor(env.codeGenerator, env.logger)
+ }
+}
diff --git a/integration-tests/src/test/resources/kmp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/kmp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..b688ee02
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1,3 @@
+TestProcessorProvider
+ValidateProcessorProvider
+ErrorProcessorProvider
diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload-androidNative/build.gradle.kts
new file mode 100644
index 00000000..ebf66a8e
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-androidNative/build.gradle.kts
@@ -0,0 +1,41 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.google.devtools.ksp")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm {
+ withJava()
+ }
+ androidNativeX64() {
+ binaries {
+ executable()
+ }
+ }
+ androidNativeArm64() {
+ binaries {
+ executable()
+ }
+ }
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(project(":annotations"))
+ }
+ }
+ val androidNativeX64Main by getting
+ val androidNativeArm64Main by getting
+ }
+}
+
+dependencies {
+ add("kspCommonMainMetadata", project(":test-processor"))
+ add("kspJvm", project(":test-processor"))
+ add("kspJvmTest", project(":test-processor"))
+ add("kspAndroidNativeX64", project(":test-processor"))
+ add("kspAndroidNativeX64Test", project(":test-processor"))
+ add("kspAndroidNativeArm64", project(":test-processor"))
+ add("kspAndroidNativeArm64Test", project(":test-processor"))
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeArm64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeArm64Main/kotlin/Main.kt
new file mode 100644
index 00000000..bf7bfe3e
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeArm64Main/kotlin/Main.kt
@@ -0,0 +1,7 @@
+import com.example.Bar
+import com.example.Foo
+
+fun main() {
+ println(Bar().toString())
+ println(Foo().toString())
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeX64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeX64Main/kotlin/Main.kt
new file mode 100644
index 00000000..bf7bfe3e
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeX64Main/kotlin/Main.kt
@@ -0,0 +1,7 @@
+import com.example.Bar
+import com.example.Foo
+
+fun main() {
+ println(Bar().toString())
+ println(Foo().toString())
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Bar.kt
new file mode 100644
index 00000000..480d3cb2
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Bar.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Bar {
+ val baz = Foo().baz
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Baz.kt
new file mode 100644
index 00000000..83015598
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Baz.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Baz {
+ val bar = Foo().bar
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/ToBeValidated.kt
new file mode 100644
index 00000000..05497afb
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/ToBeValidated.kt
@@ -0,0 +1,9 @@
+package com.example
+
+// https://github.com/google/ksp/issues/632
+@MyAnnotation
+@ExperimentalMultiplatform
+class ToBeValidated {
+ // https://github.com/google/ksp/issues/574
+ val ToBeInferred = listOf("string")
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-js/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload-js/build.gradle.kts
new file mode 100644
index 00000000..d94bf9eb
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-js/build.gradle.kts
@@ -0,0 +1,30 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.google.devtools.ksp")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ js(BOTH) {
+ browser()
+ nodejs()
+ }
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(project(":annotations"))
+ }
+ }
+ }
+}
+
+dependencies {
+ add("kspCommonMainMetadata", project(":test-processor"))
+ add("kspJs", project(":test-processor"))
+ add("kspJsTest", project(":test-processor"))
+}
+
+tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
+ kotlinOptions.freeCompilerArgs += "-Xuse-deprecated-legacy-compiler"
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Bar.kt
new file mode 100644
index 00000000..480d3cb2
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Bar.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Bar {
+ val baz = Foo().baz
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Baz.kt
new file mode 100644
index 00000000..83015598
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Baz.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Baz {
+ val bar = Foo().bar
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/ToBeValidated.kt
new file mode 100644
index 00000000..05497afb
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/ToBeValidated.kt
@@ -0,0 +1,9 @@
+package com.example
+
+// https://github.com/google/ksp/issues/632
+@MyAnnotation
+@ExperimentalMultiplatform
+class ToBeValidated {
+ // https://github.com/google/ksp/issues/574
+ val ToBeInferred = listOf("string")
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-jvm/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload-jvm/build.gradle.kts
new file mode 100644
index 00000000..f61b2569
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-jvm/build.gradle.kts
@@ -0,0 +1,25 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.google.devtools.ksp")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm {
+ withJava()
+ }
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(project(":annotations"))
+ }
+ }
+ }
+}
+
+dependencies {
+ add("kspCommonMainMetadata", project(":test-processor"))
+ add("kspJvm", project(":test-processor"))
+ add("kspJvmTest", project(":test-processor"))
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Bar.kt
new file mode 100644
index 00000000..480d3cb2
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Bar.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Bar {
+ val baz = Foo().baz
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Baz.kt
new file mode 100644
index 00000000..83015598
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Baz.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Baz {
+ val bar = Foo().bar
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/ToBeValidated.kt
new file mode 100644
index 00000000..05497afb
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/ToBeValidated.kt
@@ -0,0 +1,9 @@
+package com.example
+
+// https://github.com/google/ksp/issues/632
+@MyAnnotation
+@ExperimentalMultiplatform
+class ToBeValidated {
+ // https://github.com/google/ksp/issues/574
+ val ToBeInferred = listOf("string")
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload-linuxX64/build.gradle.kts
new file mode 100644
index 00000000..c9ff2f5d
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/build.gradle.kts
@@ -0,0 +1,40 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.google.devtools.ksp")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm {
+ withJava()
+ }
+ linuxX64() {
+ binaries {
+ executable()
+ }
+ }
+ // TODO: Enable after CI's Xcode version catches up.
+ // iosArm64()
+ // macosX64()
+ mingwX64()
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(project(":annotations"))
+ }
+ }
+ val linuxX64Main by getting
+ val linuxX64Test by getting
+ }
+}
+
+dependencies {
+ add("kspCommonMainMetadata", project(":test-processor"))
+ add("kspJvm", project(":test-processor"))
+ add("kspJvmTest", project(":test-processor"))
+ add("kspLinuxX64", project(":test-processor"))
+ add("kspLinuxX64Test", project(":test-processor"))
+ add("kspMingwX64", project(":test-processor"))
+ add("kspMingwX64Test", project(":test-processor"))
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Bar.kt
new file mode 100644
index 00000000..480d3cb2
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Bar.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Bar {
+ val baz = Foo().baz
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Baz.kt
new file mode 100644
index 00000000..83015598
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Baz.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Baz {
+ val bar = Foo().bar
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/ToBeValidated.kt
new file mode 100644
index 00000000..05497afb
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/ToBeValidated.kt
@@ -0,0 +1,9 @@
+package com.example
+
+// https://github.com/google/ksp/issues/632
+@MyAnnotation
+@ExperimentalMultiplatform
+class ToBeValidated {
+ // https://github.com/google/ksp/issues/574
+ val ToBeInferred = listOf("string")
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/Main.kt
new file mode 100644
index 00000000..bf7bfe3e
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/Main.kt
@@ -0,0 +1,7 @@
+import com.example.Bar
+import com.example.Foo
+
+fun main() {
+ println(Bar().toString())
+ println(Foo().toString())
+}
diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/ToBeRemoved.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/ToBeRemoved.kt
new file mode 100644
index 00000000..56063711
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/ToBeRemoved.kt
@@ -0,0 +1 @@
+class ToBeRemoved
diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Test/kotlin/MyTest.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Test/kotlin/MyTest.kt
new file mode 100644
index 00000000..836f9f2b
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Test/kotlin/MyTest.kt
@@ -0,0 +1,3 @@
+@com.example.MyAnnotation
+@ExperimentalMultiplatform
+class MyTest
diff --git a/integration-tests/src/test/resources/kmp/workload/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload/build.gradle.kts
new file mode 100644
index 00000000..f0ba36f1
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload/build.gradle.kts
@@ -0,0 +1,66 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.google.devtools.ksp")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm {
+ withJava()
+ }
+ js(BOTH) {
+ browser()
+ nodejs()
+ }
+ linuxX64() {
+ binaries {
+ executable()
+ }
+ }
+ androidNativeX64() {
+ binaries {
+ executable()
+ }
+ }
+ androidNativeArm64() {
+ binaries {
+ executable()
+ }
+ }
+ // TODO: Enable after CI's Xcode version catches up.
+ // iosArm64()
+ // macosX64()
+ mingwX64()
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(project(":annotations"))
+ }
+ }
+ val linuxX64Main by getting
+ val linuxX64Test by getting
+ val androidNativeX64Main by getting
+ val androidNativeArm64Main by getting
+ }
+}
+
+tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
+ kotlinOptions.freeCompilerArgs += "-Xuse-deprecated-legacy-compiler"
+}
+
+dependencies {
+ add("kspCommonMainMetadata", project(":test-processor"))
+ add("kspJvm", project(":test-processor"))
+ add("kspJvmTest", project(":test-processor"))
+ add("kspJs", project(":test-processor"))
+ add("kspJsTest", project(":test-processor"))
+ add("kspAndroidNativeX64", project(":test-processor"))
+ add("kspAndroidNativeX64Test", project(":test-processor"))
+ add("kspAndroidNativeArm64", project(":test-processor"))
+ add("kspAndroidNativeArm64Test", project(":test-processor"))
+ add("kspLinuxX64", project(":test-processor"))
+ add("kspLinuxX64Test", project(":test-processor"))
+ add("kspMingwX64", project(":test-processor"))
+ add("kspMingwX64Test", project(":test-processor"))
+}
diff --git a/integration-tests/src/test/resources/kmp/workload/src/androidNativeArm64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload/src/androidNativeArm64Main/kotlin/Main.kt
new file mode 100644
index 00000000..bf7bfe3e
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload/src/androidNativeArm64Main/kotlin/Main.kt
@@ -0,0 +1,7 @@
+import com.example.Bar
+import com.example.Foo
+
+fun main() {
+ println(Bar().toString())
+ println(Foo().toString())
+}
diff --git a/integration-tests/src/test/resources/kmp/workload/src/androidNativeX64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload/src/androidNativeX64Main/kotlin/Main.kt
new file mode 100644
index 00000000..bf7bfe3e
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload/src/androidNativeX64Main/kotlin/Main.kt
@@ -0,0 +1,7 @@
+import com.example.Bar
+import com.example.Foo
+
+fun main() {
+ println(Bar().toString())
+ println(Foo().toString())
+}
diff --git a/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Bar.kt
new file mode 100644
index 00000000..480d3cb2
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Bar.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Bar {
+ val baz = Foo().baz
+}
diff --git a/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Baz.kt
new file mode 100644
index 00000000..83015598
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Baz.kt
@@ -0,0 +1,5 @@
+package com.example
+
+class Baz {
+ val bar = Foo().bar
+}
diff --git a/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/ToBeValidated.kt
new file mode 100644
index 00000000..05497afb
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/ToBeValidated.kt
@@ -0,0 +1,9 @@
+package com.example
+
+// https://github.com/google/ksp/issues/632
+@MyAnnotation
+@ExperimentalMultiplatform
+class ToBeValidated {
+ // https://github.com/google/ksp/issues/574
+ val ToBeInferred = listOf("string")
+}
diff --git a/integration-tests/src/test/resources/kmp/workload/src/linuxX64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload/src/linuxX64Main/kotlin/Main.kt
new file mode 100644
index 00000000..bf7bfe3e
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload/src/linuxX64Main/kotlin/Main.kt
@@ -0,0 +1,7 @@
+import com.example.Bar
+import com.example.Foo
+
+fun main() {
+ println(Bar().toString())
+ println(Foo().toString())
+}
diff --git a/integration-tests/src/test/resources/kmp/workload/src/linuxX64Test/kotlin/MyTest.kt b/integration-tests/src/test/resources/kmp/workload/src/linuxX64Test/kotlin/MyTest.kt
new file mode 100644
index 00000000..836f9f2b
--- /dev/null
+++ b/integration-tests/src/test/resources/kmp/workload/src/linuxX64Test/kotlin/MyTest.kt
@@ -0,0 +1,3 @@
+@com.example.MyAnnotation
+@ExperimentalMultiplatform
+class MyTest
diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/build.gradle.kts b/integration-tests/src/test/resources/kotlin-consts-in-java/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/kotlin-consts-in-java/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/settings.gradle.kts b/integration-tests/src/test/resources/kotlin-consts-in-java/settings.gradle.kts
new file mode 100644
index 00000000..9d60cbc7
--- /dev/null
+++ b/integration-tests/src/test/resources/kotlin-consts-in-java/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "playground"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/build.gradle.kts b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/build.gradle.kts
new file mode 100644
index 00000000..ab249cf0
--- /dev/null
+++ b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/build.gradle.kts
@@ -0,0 +1,24 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..7e141927
--- /dev/null
+++ b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,27 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+
+class TestProcessor(
+ private val codeGenerator: CodeGenerator,
+ private val options: Map<String, String>,
+ private val logger: KSPLogger
+) : SymbolProcessor {
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ resolver
+ .getSymbolsWithAnnotation("com.example.ann.MyAnn")
+ .filterIsInstance<KSFunctionDeclaration>()
+ .forEach { func ->
+ val arg = func.annotations.first().arguments.first().value.toString()
+ if (!arg.startsWith("REPLACE"))
+ throw IllegalStateException(arg)
+ }
+
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
+ TestProcessor(environment.codeGenerator, environment.options, environment.logger)
+}
diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..c91e3e9e
--- /dev/null
+++ b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+TestProcessorProvider
diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/workload/build.gradle.kts b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/build.gradle.kts
new file mode 100644
index 00000000..f0ea52b0
--- /dev/null
+++ b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/build.gradle.kts
@@ -0,0 +1,20 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":test-processor"))
+ ksp(project(":test-processor"))
+}
diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/JavaClass.java b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/JavaClass.java
new file mode 100644
index 00000000..a196f754
--- /dev/null
+++ b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/JavaClass.java
@@ -0,0 +1,9 @@
+package com.example;
+
+import com.example.ann.MyAnn;
+
+public class JavaClass {
+ @MyAnn(KotlinConsts.ACTION)
+ public void f() {
+ }
+}
diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/KotlinConsts.kt b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/KotlinConsts.kt
new file mode 100644
index 00000000..e5231980
--- /dev/null
+++ b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/KotlinConsts.kt
@@ -0,0 +1,8 @@
+package com.example
+
+class KotlinConsts {
+ companion object {
+ const val ACTION = "REPLACE"
+ const val ACTION2 = "REPLACE"
+ }
+}
diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/ann/MyAnn.kt b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/ann/MyAnn.kt
new file mode 100644
index 00000000..6812ee63
--- /dev/null
+++ b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/ann/MyAnn.kt
@@ -0,0 +1,3 @@
+package com.example.ann
+
+annotation class MyAnn(val value: String)
diff --git a/integration-tests/src/test/resources/map-annotation-arguments/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/map-annotation-arguments/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..7892f012
--- /dev/null
+++ b/integration-tests/src/test/resources/map-annotation-arguments/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,36 @@
+import com.google.devtools.ksp.getClassDeclarationByName
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+
+class TestProcessor(
+ val codeGenerator: CodeGenerator,
+ val logger: KSPLogger
+) : SymbolProcessor {
+ val expected = mapOf(
+ "unboxedChar" to "Char",
+ "boxedChar" to "(Char..Char?)",
+ )
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ val j = resolver.getClassDeclarationByName("com.example.AnnotationTest")!!
+ j.annotations.forEach { annotation ->
+ annotation.arguments.forEach {
+ val key = it.name?.asString()
+ val value = it.value.toString()
+ if (expected[key] != value) {
+ logger.error("$key: ${expected[key]} != $value")
+ }
+ }
+ }
+
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ environment: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return TestProcessor(environment.codeGenerator, environment.logger)
+ }
+}
diff --git a/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/AnnotationTest.java b/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/AnnotationTest.java
new file mode 100644
index 00000000..18696806
--- /dev/null
+++ b/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/AnnotationTest.java
@@ -0,0 +1,8 @@
+package com.example;
+
+@JavaAnnotation(
+ unboxedChar = char.class,
+ boxedChar = Character.class
+)
+public class AnnotationTest {
+}
diff --git a/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/JavaAnnotation.java b/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/JavaAnnotation.java
new file mode 100644
index 00000000..c7c8368f
--- /dev/null
+++ b/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/JavaAnnotation.java
@@ -0,0 +1,6 @@
+package com.example;
+
+public @interface JavaAnnotation {
+ Class unboxedChar();
+ Class boxedChar();
+} \ No newline at end of file
diff --git a/integration-tests/src/test/resources/on-error/build.gradle.kts b/integration-tests/src/test/resources/on-error/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/on-error/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/on-error/on-error-processor/build.gradle.kts b/integration-tests/src/test/resources/on-error/on-error-processor/build.gradle.kts
new file mode 100644
index 00000000..999cb80a
--- /dev/null
+++ b/integration-tests/src/test/resources/on-error/on-error-processor/build.gradle.kts
@@ -0,0 +1,23 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/ErrorProcessor.kt b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/ErrorProcessor.kt
new file mode 100644
index 00000000..976019bf
--- /dev/null
+++ b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/ErrorProcessor.kt
@@ -0,0 +1,70 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import java.io.OutputStream
+
+class ErrorProcessor : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+ lateinit var file: OutputStream
+ var rounds = 0
+ lateinit var exception: String
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger
+ ) {
+ exception = if (options.containsKey("exception")) {
+ options["exception"]!!
+ } else {
+ ""
+ }
+ if (exception == "init") {
+ throw Exception("Test Exception in init")
+ }
+ this.logger = logger
+ this.codeGenerator = codeGenerator
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (exception == "createTwice") {
+ codeGenerator.createNewFile(Dependencies.ALL_FILES, "create", "Twice").write("".toByteArray())
+ return emptyList()
+ }
+ if (exception == "process") {
+ throw Exception("Test Exception in process")
+ }
+ rounds++
+ if (rounds == 2) {
+ if (exception == "" || exception == "error") {
+ logger.error("Error processor: errored at $rounds")
+ }
+ } else {
+ codeGenerator.createNewFile(Dependencies.ALL_FILES, "test", "error", "log")
+ }
+ return emptyList()
+ }
+
+ override fun finish() {
+ if (exception == "finish") {
+ throw Exception("Test Exception in finish")
+ }
+ }
+
+ override fun onError() {
+ if (exception == "error") {
+ throw Exception("Test Exception in error")
+ }
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return ErrorProcessor().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/NormalProcessor.kt b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/NormalProcessor.kt
new file mode 100644
index 00000000..c751c582
--- /dev/null
+++ b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/NormalProcessor.kt
@@ -0,0 +1,40 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+
+class NormalProcessor : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+ var rounds = 0
+
+ override fun onError() {
+ logger.error("NormalProcessor called error on $rounds")
+ }
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger
+ ) {
+ this.logger = logger
+ this.codeGenerator = codeGenerator
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ rounds++
+ if (rounds == 1) {
+ codeGenerator.createNewFile(Dependencies.ALL_FILES, "test", "normal", "log")
+ }
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider2 : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return NormalProcessor().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/on-error/on-error-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..3a1528c9
--- /dev/null
+++ b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1,2 @@
+TestProcessorProvider
+TestProcessorProvider2
diff --git a/integration-tests/src/test/resources/on-error/settings.gradle.kts b/integration-tests/src/test/resources/on-error/settings.gradle.kts
new file mode 100644
index 00000000..cff61bf9
--- /dev/null
+++ b/integration-tests/src/test/resources/on-error/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kspVersion: String by settings
+ val kotlinVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "on-error"
+
+include(":workload")
+include(":on-error-processor")
diff --git a/integration-tests/src/test/resources/on-error/workload/build.gradle.kts b/integration-tests/src/test/resources/on-error/workload/build.gradle.kts
new file mode 100644
index 00000000..015259a8
--- /dev/null
+++ b/integration-tests/src/test/resources/on-error/workload/build.gradle.kts
@@ -0,0 +1,23 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ ksp(project(":on-error-processor"))
+}
+
+tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
+ kotlinOptions.freeCompilerArgs += "-opt-in=MyOptIn"
+}
diff --git a/integration-tests/src/test/resources/on-error/workload/src/main/kotlin/com/example/A.kt b/integration-tests/src/test/resources/on-error/workload/src/main/kotlin/com/example/A.kt
new file mode 100644
index 00000000..4752447c
--- /dev/null
+++ b/integration-tests/src/test/resources/on-error/workload/src/main/kotlin/com/example/A.kt
@@ -0,0 +1,11 @@
+package com.example
+
+@RequiresOptIn
+@Retention(AnnotationRetention.BINARY)
+@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
+annotation class MyOptIn
+
+@OptIn(MyOptIn::class)
+fun main() {
+ print("hello world")
+}
diff --git a/integration-tests/src/test/resources/only-resources-file/build.gradle.kts b/integration-tests/src/test/resources/only-resources-file/build.gradle.kts
new file mode 100644
index 00000000..7a1ba5b1
--- /dev/null
+++ b/integration-tests/src/test/resources/only-resources-file/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ kotlin("multiplatform") apply false
+}
+
+val testRepo: String by project
+subprojects {
+ repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
diff --git a/integration-tests/src/test/resources/only-resources-file/settings.gradle.kts b/integration-tests/src/test/resources/only-resources-file/settings.gradle.kts
new file mode 100644
index 00000000..49845b92
--- /dev/null
+++ b/integration-tests/src/test/resources/only-resources-file/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion apply false
+ kotlin("multiplatform") version kotlinVersion apply false
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "playground"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/only-resources-file/test-processor/build.gradle.kts b/integration-tests/src/test/resources/only-resources-file/test-processor/build.gradle.kts
new file mode 100644
index 00000000..1d1cb300
--- /dev/null
+++ b/integration-tests/src/test/resources/only-resources-file/test-processor/build.gradle.kts
@@ -0,0 +1,12 @@
+val kspVersion: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+dependencies {
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
diff --git a/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..6d07416b
--- /dev/null
+++ b/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,24 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+
+class TestProcessor(val codeGenerator: CodeGenerator) : SymbolProcessor {
+
+ var invoked = false
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (invoked) {
+ return emptyList()
+ }
+
+ codeGenerator.createNewFile(Dependencies(false), "", "HelloSwift", "swift")
+
+ invoked = true
+ return emptyList()
+ }
+
+ class Provider : SymbolProcessorProvider {
+ override fun create(
+ environment: SymbolProcessorEnvironment
+ ): SymbolProcessor = TestProcessor(environment.codeGenerator)
+ }
+}
diff --git a/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..5c51c309
--- /dev/null
+++ b/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+TestProcessor$Provider
diff --git a/integration-tests/src/test/resources/only-resources-file/workload/build.gradle.kts b/integration-tests/src/test/resources/only-resources-file/workload/build.gradle.kts
new file mode 100644
index 00000000..ed0d40c6
--- /dev/null
+++ b/integration-tests/src/test/resources/only-resources-file/workload/build.gradle.kts
@@ -0,0 +1,16 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.google.devtools.ksp")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm {
+ withJava()
+ }
+}
+
+dependencies {
+ add("kspCommonMainMetadata", project(":test-processor"))
+}
diff --git a/integration-tests/src/test/resources/only-resources-file/workload/src/commonMain/kotlin/MyStub.kt b/integration-tests/src/test/resources/only-resources-file/workload/src/commonMain/kotlin/MyStub.kt
new file mode 100644
index 00000000..f0692550
--- /dev/null
+++ b/integration-tests/src/test/resources/only-resources-file/workload/src/commonMain/kotlin/MyStub.kt
@@ -0,0 +1 @@
+class MyStub
diff --git a/integration-tests/src/test/resources/output-deps/build.gradle.kts b/integration-tests/src/test/resources/output-deps/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/output-deps/gradle.properties b/integration-tests/src/test/resources/output-deps/gradle.properties
new file mode 100644
index 00000000..f9325643
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/gradle.properties
@@ -0,0 +1,2 @@
+ksp.incremental=true
+ksp.incremental.log=true \ No newline at end of file
diff --git a/integration-tests/src/test/resources/output-deps/settings.gradle.kts b/integration-tests/src/test/resources/output-deps/settings.gradle.kts
new file mode 100644
index 00000000..668c5a87
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kspVersion: String by settings
+ val kotlinVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "incremental-test"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/output-deps/test-processor/build.gradle.kts b/integration-tests/src/test/resources/output-deps/test-processor/build.gradle.kts
new file mode 100644
index 00000000..ab249cf0
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/test-processor/build.gradle.kts
@@ -0,0 +1,24 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..9e8436f9
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,61 @@
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import java.io.OutputStreamWriter
+
+class TestProcessor : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+ var processed = false
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger,
+ ) {
+ this.codeGenerator = codeGenerator
+ this.logger = logger
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (processed) {
+ return emptyList()
+ }
+ fun outputForAnno(anno: String) {
+ val annoFiles =
+ resolver.getSymbolsWithAnnotation(anno).map { (it as KSDeclaration).containingFile!! }.toList()
+ codeGenerator.createNewFile(Dependencies(false, *annoFiles.toTypedArray()), "", anno, "log").use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write(annoFiles.map { it.fileName }.joinToString(", "))
+ }
+ }
+ }
+
+ outputForAnno("p1.Anno1")
+ outputForAnno("p1.Anno2")
+
+ resolver.getNewFiles().forEach { file ->
+ logger.warn("${file.packageName.asString()}/${file.fileName}")
+ val outputBaseFN = file.fileName.replace(".kt", "Generated").replace(".java", "Generated")
+ codeGenerator.createNewFile(Dependencies(false, file), file.packageName.asString(), outputBaseFN, "kt")
+ .use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write("private val unused = \"unused\"")
+ }
+ }
+ }
+ processed = true
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment,
+ ): SymbolProcessor {
+ return TestProcessor().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..c91e3e9e
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+TestProcessorProvider
diff --git a/integration-tests/src/test/resources/output-deps/workload/build.gradle.kts b/integration-tests/src/test/resources/output-deps/workload/build.gradle.kts
new file mode 100644
index 00000000..f6b92cd5
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/build.gradle.kts
@@ -0,0 +1,19 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ ksp(project(":test-processor"))
+}
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J1.java b/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J1.java
new file mode 100644
index 00000000..e5e1bc0e
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J1.java
@@ -0,0 +1,5 @@
+package p1;
+
+@Anno2
+public class J1 {
+}
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J2.java b/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J2.java
new file mode 100644
index 00000000..7de0687e
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J2.java
@@ -0,0 +1,4 @@
+package p1;
+
+public class J2 {
+}
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno1.kt b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno1.kt
new file mode 100644
index 00000000..badbcbd8
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno1.kt
@@ -0,0 +1,2 @@
+package p1
+annotation class Anno1
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno2.kt b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno2.kt
new file mode 100644
index 00000000..1c517717
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno2.kt
@@ -0,0 +1,2 @@
+package p1
+annotation class Anno2
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K1.kt b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K1.kt
new file mode 100644
index 00000000..43301819
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K1.kt
@@ -0,0 +1,4 @@
+package p1
+
+@Anno1
+open class K1
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K2.kt b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K2.kt
new file mode 100644
index 00000000..506ed473
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K2.kt
@@ -0,0 +1,5 @@
+package p1
+
+@Anno1
+@Anno2
+open class K2
diff --git a/integration-tests/src/test/resources/playground-android-multi/application/build.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/application/build.gradle.kts
new file mode 100644
index 00000000..4b1192e1
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android-multi/application/build.gradle.kts
@@ -0,0 +1,30 @@
+val testRepo: String by project
+
+plugins {
+ id("com.android.application")
+ kotlin("android")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":workload"))
+}
+
+android {
+ compileSdkVersion(30)
+ defaultConfig {
+ applicationId = "org.gradle.kotlin.dsl.samples.androidstudio"
+ minSdkVersion(30)
+ targetSdkVersion(30)
+ versionCode = 1
+ versionName = "1.0"
+ }
+}
diff --git a/integration-tests/src/test/resources/playground-android-multi/application/src/main/AndroidManifest.xml b/integration-tests/src/test/resources/playground-android-multi/application/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..81a4ba30
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android-multi/application/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.myapplication">
+</manifest> \ No newline at end of file
diff --git a/integration-tests/src/test/resources/playground-android-multi/application/src/main/java/com/example/application/Foo.kt b/integration-tests/src/test/resources/playground-android-multi/application/src/main/java/com/example/application/Foo.kt
new file mode 100644
index 00000000..4f164e18
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android-multi/application/src/main/java/com/example/application/Foo.kt
@@ -0,0 +1,3 @@
+package com.example.application
+
+class Foo
diff --git a/integration-tests/src/test/resources/playground-android-multi/build.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/build.gradle.kts
new file mode 100644
index 00000000..e1f8d720
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android-multi/build.gradle.kts
@@ -0,0 +1,27 @@
+buildscript {
+ val testRepo: String by project
+
+ repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ google()
+ }
+}
+
+plugins {
+ id("com.android.application") apply false
+ kotlin("android") apply false
+ id("com.google.devtools.ksp") apply false
+ id("com.android.library") apply false
+}
+
+allprojects {
+ val testRepo: String by project
+ repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ google()
+ }
+}
diff --git a/integration-tests/src/test/resources/playground-android-multi/gradle.properties b/integration-tests/src/test/resources/playground-android-multi/gradle.properties
new file mode 100644
index 00000000..b3c7a033
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android-multi/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx2048M
diff --git a/integration-tests/src/test/resources/playground-android-multi/settings.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/settings.gradle.kts
new file mode 100644
index 00000000..4c6fe413
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android-multi/settings.gradle.kts
@@ -0,0 +1,26 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ val agpVersion: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion apply false
+ kotlin("jvm") version kotlinVersion apply false
+ kotlin("android") version kotlinVersion apply false
+ id("com.android.application") version agpVersion apply false
+ id("com.android.library") version agpVersion apply false
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "playground"
+
+include(":application")
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/playground-android-multi/test-processor/build.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/test-processor/build.gradle.kts
new file mode 100644
index 00000000..75c6c7c5
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android-multi/test-processor/build.gradle.kts
@@ -0,0 +1,25 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.squareup:javapoet:1.12.1")
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/playground-android-multi/workload/build.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/workload/build.gradle.kts
new file mode 100644
index 00000000..3897c89d
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android-multi/workload/build.gradle.kts
@@ -0,0 +1,35 @@
+val testRepo: String by project
+
+plugins {
+ // DO NOT CHANGE THE ORDER.
+ id("com.google.devtools.ksp")
+ id("com.android.library")
+ kotlin("android")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":test-processor"))
+ ksp(project(":test-processor"))
+}
+
+android {
+ compileSdkVersion(30)
+ defaultConfig {
+ minSdkVersion(30)
+ targetSdkVersion(30)
+ }
+}
+
+ksp {
+ arg("option1", "value1")
+ arg("option2", "value2")
+}
diff --git a/integration-tests/src/test/resources/playground-android-multi/workload/src/main/AndroidManifest.xml b/integration-tests/src/test/resources/playground-android-multi/workload/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..522bce61
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android-multi/workload/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.mylibrary">
+</manifest> \ No newline at end of file
diff --git a/integration-tests/src/test/resources/playground-android/build.gradle.kts b/integration-tests/src/test/resources/playground-android/build.gradle.kts
new file mode 100644
index 00000000..b54fb383
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android/build.gradle.kts
@@ -0,0 +1,20 @@
+buildscript {
+ val testRepo: String by project
+
+ repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ google()
+ }
+}
+
+allprojects {
+ val testRepo: String by project
+ repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ google()
+ }
+}
diff --git a/integration-tests/src/test/resources/playground-android/gradle.properties b/integration-tests/src/test/resources/playground-android/gradle.properties
new file mode 100644
index 00000000..b3c7a033
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx2048M
diff --git a/integration-tests/src/test/resources/playground-android/settings.gradle.kts b/integration-tests/src/test/resources/playground-android/settings.gradle.kts
new file mode 100644
index 00000000..67a08d91
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android/settings.gradle.kts
@@ -0,0 +1,24 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ val agpVersion: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion apply false
+ kotlin("jvm") version kotlinVersion apply false
+ kotlin("android") version kotlinVersion apply false
+ id("com.android.application") version agpVersion apply false
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "playground"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/playground-android/test-processor/build.gradle.kts b/integration-tests/src/test/resources/playground-android/test-processor/build.gradle.kts
new file mode 100644
index 00000000..75c6c7c5
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android/test-processor/build.gradle.kts
@@ -0,0 +1,25 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.squareup:javapoet:1.12.1")
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/playground-android/workload/build.gradle.kts b/integration-tests/src/test/resources/playground-android/workload/build.gradle.kts
new file mode 100644
index 00000000..b0a0038a
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android/workload/build.gradle.kts
@@ -0,0 +1,45 @@
+val testRepo: String by project
+
+plugins {
+ // DO NOT CHANGE THE ORDER.
+ id("com.google.devtools.ksp")
+ id("com.android.application")
+ kotlin("android")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":test-processor"))
+ ksp(project(":test-processor"))
+}
+
+android {
+ compileSdkVersion(30)
+ defaultConfig {
+ applicationId = "org.gradle.kotlin.dsl.samples.androidstudio"
+ minSdkVersion(30)
+ targetSdkVersion(30)
+ versionCode = 1
+ versionName = "1.0"
+ }
+ buildTypes {
+ getByName("release") {
+ // For regression testing https://github.com/google/ksp/pull/467
+ proguardFiles.add(file("proguard-rules.pro"))
+ isMinifyEnabled = true
+ }
+ }
+}
+
+ksp {
+ arg("option1", "value1")
+ arg("option2", "value2")
+}
diff --git a/integration-tests/src/test/resources/playground-android/workload/proguard-rules.pro b/integration-tests/src/test/resources/playground-android/workload/proguard-rules.pro
new file mode 100644
index 00000000..a90bbb3f
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android/workload/proguard-rules.pro
@@ -0,0 +1 @@
+-keep class com.example.AClass { *; } \ No newline at end of file
diff --git a/integration-tests/src/test/resources/playground-android/workload/src/main/AndroidManifest.xml b/integration-tests/src/test/resources/playground-android/workload/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..81a4ba30
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-android/workload/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.myapplication">
+</manifest> \ No newline at end of file
diff --git a/integration-tests/src/test/resources/playground-mpp/build.gradle.kts b/integration-tests/src/test/resources/playground-mpp/build.gradle.kts
new file mode 100644
index 00000000..8dd65667
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-mpp/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ kotlin("multiplatform") apply false
+}
+
+val testRepo: String by project
+allprojects {
+ repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
diff --git a/integration-tests/src/test/resources/playground-mpp/settings.gradle.kts b/integration-tests/src/test/resources/playground-mpp/settings.gradle.kts
new file mode 100644
index 00000000..49845b92
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-mpp/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion apply false
+ kotlin("multiplatform") version kotlinVersion apply false
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "playground"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/playground-mpp/test-processor/build.gradle.kts b/integration-tests/src/test/resources/playground-mpp/test-processor/build.gradle.kts
new file mode 100644
index 00000000..842898f6
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-mpp/test-processor/build.gradle.kts
@@ -0,0 +1,22 @@
+val kspVersion: String by project
+
+plugins {
+ kotlin("multiplatform")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm()
+ sourceSets {
+ val jvmMain by getting {
+ dependencies {
+ implementation("com.squareup:javapoet:1.12.1")
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+ }
+ kotlin.srcDir("src/main/kotlin")
+ resources.srcDir("src/main/resources")
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/playground-mpp/workload/build.gradle.kts b/integration-tests/src/test/resources/playground-mpp/workload/build.gradle.kts
new file mode 100644
index 00000000..a3179983
--- /dev/null
+++ b/integration-tests/src/test/resources/playground-mpp/workload/build.gradle.kts
@@ -0,0 +1,39 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.google.devtools.ksp")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ jvm {
+ withJava()
+ }
+ linuxX64()
+ mingwX64()
+ macosX64()
+ ios()
+ js(BOTH) {
+ browser()
+ nodejs()
+ }
+ sourceSets {
+ val commonMain by getting
+ val jvmMain by getting {
+ dependencies {
+ implementation(project(":test-processor"))
+ project.dependencies.add("kspJvm", project(":test-processor"))
+ }
+ kotlin.srcDir("src/main/java")
+ }
+ }
+}
+
+tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
+ kotlinOptions.freeCompilerArgs += "-Xuse-deprecated-legacy-compiler"
+}
+
+ksp {
+ arg("option1", "value1")
+ arg("option2", "value2")
+}
diff --git a/integration-tests/src/test/resources/playground/build.gradle.kts b/integration-tests/src/test/resources/playground/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/playground/settings.gradle.kts b/integration-tests/src/test/resources/playground/settings.gradle.kts
new file mode 100644
index 00000000..9d60cbc7
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "playground"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/playground/test-processor/build.gradle.kts b/integration-tests/src/test/resources/playground/test-processor/build.gradle.kts
new file mode 100644
index 00000000..75c6c7c5
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/test-processor/build.gradle.kts
@@ -0,0 +1,25 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.squareup:javapoet:1.12.1")
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/Builder.kt b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/Builder.kt
new file mode 100644
index 00000000..9434b362
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/Builder.kt
@@ -0,0 +1,3 @@
+package com.example.annotation
+
+annotation class Builder
diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/BuilderProcessor.kt b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/BuilderProcessor.kt
new file mode 100644
index 00000000..efa5ddcf
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/BuilderProcessor.kt
@@ -0,0 +1,104 @@
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+import java.io.OutputStream
+
+fun OutputStream.appendText(str: String) {
+ this.write(str.toByteArray())
+}
+
+class BuilderProcessor : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var logger: KSPLogger
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger,
+ ) {
+ this.codeGenerator = codeGenerator
+ this.logger = logger
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ val symbols = resolver.getSymbolsWithAnnotation("com.example.annotation.Builder")
+ val ret = symbols.filter { !it.validate() }
+ symbols
+ .filter { it is KSClassDeclaration && it.validate() }
+ .forEach { it.accept(BuilderVisitor(), Unit) }
+ return ret.toList()
+ }
+
+ inner class BuilderVisitor : KSVisitorVoid() {
+ override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
+ classDeclaration.primaryConstructor?.accept(this, data)
+ }
+
+ override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
+ val parent = function.parentDeclaration as KSClassDeclaration
+ val packageName = parent.containingFile!!.packageName.asString()
+ val className = "${parent.simpleName.asString()}Builder"
+
+ // For regression testing https://github.com/google/ksp/pull/467
+ codeGenerator.createNewFile(
+ Dependencies(true, function.containingFile!!),
+ "",
+ "META-INF/proguard/builder-$className",
+ "pro"
+ ).use { proguardFile ->
+ proguardFile.appendText("-keep class $packageName.$className { *; }")
+ }
+
+ val file = codeGenerator.createNewFile(
+ Dependencies(true, function.containingFile!!), packageName, className
+ )
+ file.appendText("package $packageName\n\n")
+ file.appendText("import hello.HELLO\n\n")
+ file.appendText("class $className{\n")
+ function.parameters.forEach {
+ val name = it.name!!.asString()
+ val typeName = StringBuilder(it.type.resolve().declaration.qualifiedName?.asString() ?: "<ERROR>")
+ val typeArgs = it.type.element!!.typeArguments
+ if (it.type.element!!.typeArguments.toList().isNotEmpty()) {
+ typeName.append("<")
+ typeName.append(
+ typeArgs.map {
+ val type = it.type?.resolve()
+ "${it.variance.label} ${type?.declaration?.qualifiedName?.asString() ?: "ERROR"}" +
+ if (type?.nullability == Nullability.NULLABLE) "?" else ""
+ }.joinToString(", ")
+ )
+ typeName.append(">")
+ }
+ file.appendText(" private var $name: $typeName? = null\n")
+ file.appendText(" internal fun with${name.capitalize()}($name: $typeName): $className {\n")
+ file.appendText(" this.$name = $name\n")
+ file.appendText(" return this\n")
+ file.appendText(" }\n\n")
+ }
+ file.appendText(" internal fun build(): ${parent.qualifiedName!!.asString()} {\n")
+ file.appendText(" return ${parent.qualifiedName!!.asString()}(")
+ file.appendText(
+ function.parameters.map {
+ "${it.name!!.asString()}!!"
+ }.joinToString(", ")
+ )
+ file.appendText(")\n")
+ file.appendText(" }\n")
+ file.appendText("}\n")
+ file.close()
+ }
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment,
+ ): SymbolProcessor {
+ return BuilderProcessor().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/RewriteProcessor.kt b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/RewriteProcessor.kt
new file mode 100644
index 00000000..6c6e2354
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/RewriteProcessor.kt
@@ -0,0 +1,30 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+
+class RewriteProcessor : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger
+ ) {
+ this.codeGenerator = codeGenerator
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ val fileKt = codeGenerator.createNewFile(Dependencies(false), "hello", "HELLO", "java")
+ return emptyList()
+ }
+}
+
+class RewriteProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return RewriteProcessor().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..4b5a3cb5
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,283 @@
+import com.google.devtools.ksp.containingFile
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import java.io.OutputStream
+
+class TestProcessor : SymbolProcessor {
+ lateinit var codeGenerator: CodeGenerator
+ lateinit var file: OutputStream
+ var invoked = false
+
+ fun emit(s: String, indent: String) {
+ file.appendText("$indent$s\n")
+ }
+
+ fun init(
+ options: Map<String, String>,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger
+ ) {
+ logger.warn("This is a harmless warning.")
+ this.codeGenerator = codeGenerator
+ file = codeGenerator.createNewFile(Dependencies(false), "", "TestProcessor", "log")
+ emit("TestProcessor: init($options)", "")
+
+ val javaFile = codeGenerator.createNewFile(Dependencies(false), "", "Generated", "java")
+ javaFile.appendText("class Generated {}")
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ if (invoked) {
+ return emptyList()
+ }
+ val fileKt = codeGenerator.createNewFile(Dependencies(false), "hello", "HELLO", "java")
+ fileKt.appendText("package hello;\n")
+ fileKt.appendText("public class HELLO{\n")
+ fileKt.appendText("public int foo() { return 1234; }\n")
+ fileKt.appendText("}")
+
+ val files = resolver.getAllFiles()
+ emit("TestProcessor: process()", "")
+ val visitor = TestVisitor()
+ for (file in files) {
+ emit("TestProcessor: processing ${file.fileName}", "")
+ file.accept(visitor, "")
+ }
+ invoked = true
+ return emptyList()
+ }
+
+ inner class TestVisitor : KSVisitor<String, Unit> {
+
+ override fun visitReferenceElement(element: KSReferenceElement, data: String) {
+ }
+
+ override fun visitModifierListOwner(modifierListOwner: KSModifierListOwner, data: String) {
+ TODO("Not yet implemented")
+ }
+
+ override fun visitNode(node: KSNode, data: String) {
+ TODO("Not yet implemented")
+ }
+
+ override fun visitPropertyAccessor(accessor: KSPropertyAccessor, data: String) {
+ TODO("Not yet implemented")
+ }
+
+ override fun visitDynamicReference(reference: KSDynamicReference, data: String) {
+ TODO("Not yet implemented")
+ }
+
+ override fun visitDefNonNullReference(reference: KSDefNonNullReference, data: String) {
+ TODO("Not yet implemented")
+ }
+
+ val visited = HashSet<Any>()
+
+ private fun checkVisited(symbol: Any): Boolean {
+ return if (visited.contains(symbol)) {
+ true
+ } else {
+ visited.add(symbol)
+ false
+ }
+ }
+
+ private fun invokeCommonDeclarationApis(declaration: KSDeclaration, indent: String) {
+ emit(
+ "${declaration.modifiers.joinToString(" ")} ${declaration.simpleName.asString()}", indent
+ )
+ declaration.annotations.map { it.accept(this, "$indent ") }
+ if (declaration.parentDeclaration != null)
+ emit(" enclosing: ${declaration.parentDeclaration!!.qualifiedName?.asString()}", indent)
+ declaration.containingFile?.let { emit("${it.packageName.asString()}.${it.fileName}", indent) }
+ declaration.typeParameters.map { it.accept(this, "$indent ") }
+ }
+
+ override fun visitFile(file: KSFile, data: String) {
+// if (!file.packageName.asString().startsWith("eu.kanade.tachiyomi.data")) {
+// return
+// }
+ if (checkVisited(file)) return
+ file.annotations.forEach { it.accept(this, "$data ") }
+ emit(file.packageName.asString(), data)
+ for (declaration in file.declarations) {
+ declaration.accept(this, data)
+ }
+ }
+
+ override fun visitAnnotation(annotation: KSAnnotation, data: String) {
+ if (checkVisited(annotation)) return
+ emit("annotation", data)
+ annotation.annotationType.accept(this, "$data ")
+ annotation.arguments.forEach { it.accept(this, "$data ") }
+ }
+
+ override fun visitCallableReference(reference: KSCallableReference, data: String) {
+ if (checkVisited(reference)) return
+ emit("element: ", data)
+ reference.functionParameters.forEach { it.accept(this, "$data ") }
+ reference.receiverType?.accept(this, "$data receiver")
+ reference.returnType.accept(this, "$data ")
+ }
+
+ override fun visitPropertyGetter(getter: KSPropertyGetter, data: String) {
+ if (checkVisited(getter)) return
+ emit("propertyGetter: ", data)
+ getter.annotations.forEach { it.accept(this, "$data ") }
+ emit(getter.modifiers.joinToString(" "), data)
+ getter.returnType?.accept(this, "$data ")
+ }
+
+ override fun visitPropertySetter(setter: KSPropertySetter, data: String) {
+ if (checkVisited(setter)) return
+ emit("propertySetter: ", data)
+ setter.annotations.forEach { it.accept(this, "$data ") }
+ emit(setter.modifiers.joinToString(" "), data)
+// setter.parameter.accept(this, "$data ")
+ }
+
+ override fun visitTypeArgument(typeArgument: KSTypeArgument, data: String) {
+ if (checkVisited(typeArgument)) return
+ typeArgument.annotations.forEach { it.accept(this, "$data ") }
+ emit(
+ when (typeArgument.variance) {
+ Variance.STAR -> "*"
+ Variance.COVARIANT -> "out"
+ Variance.CONTRAVARIANT -> "in"
+ else -> ""
+ },
+ data
+ )
+ typeArgument.type?.accept(this, "$data ")
+ }
+
+ override fun visitTypeParameter(typeParameter: KSTypeParameter, data: String) {
+ if (checkVisited(typeParameter)) return
+ typeParameter.annotations.forEach { it.accept(this, "$data ") }
+ if (typeParameter.isReified) {
+ emit("reified ", data)
+ }
+ emit(
+ when (typeParameter.variance) {
+ Variance.COVARIANT -> "out "
+ Variance.CONTRAVARIANT -> "in "
+ else -> ""
+ } + typeParameter.name.asString(),
+ data
+ )
+ if (typeParameter.bounds.toList().isNotEmpty()) {
+ typeParameter.bounds.forEach { it.accept(this, "$data ") }
+ }
+ }
+
+ override fun visitValueParameter(valueParameter: KSValueParameter, data: String) {
+ if (checkVisited(valueParameter)) return
+ valueParameter.annotations.forEach { it.accept(this, "$data ") }
+ if (valueParameter.isVararg) {
+ emit("vararg", "$data ")
+ }
+ if (valueParameter.isNoInline) {
+ emit("noinline", "$data ")
+ }
+ if (valueParameter.isCrossInline) {
+ emit("crossinline ", "$data ")
+ }
+ emit(valueParameter.name?.asString() ?: "_", "$data ")
+ valueParameter.type.accept(this, "$data ")
+ }
+
+ override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: String) {
+ if (checkVisited(function)) return
+ invokeCommonDeclarationApis(function, data)
+ for (declaration in function.declarations) {
+ declaration.accept(this, "$data ")
+ }
+ function.parameters.forEach { it.accept(this, "$data ") }
+ function.typeParameters.forEach { it.accept(this, "$data ") }
+ function.extensionReceiver?.accept(this, "$data extension:")
+ emit("returnType:", data)
+ function.returnType?.accept(this, "$data ")
+ }
+
+ override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: String) {
+ if (checkVisited(classDeclaration)) return
+ invokeCommonDeclarationApis(classDeclaration, data)
+ emit(classDeclaration.classKind.type, data)
+ for (declaration in classDeclaration.declarations) {
+ declaration.accept(this, "$data ")
+ }
+ classDeclaration.superTypes.forEach { it.accept(this, "$data ") }
+ classDeclaration.primaryConstructor?.accept(this, "$data ")
+ }
+
+ override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: String) {
+ if (checkVisited(property)) return
+ invokeCommonDeclarationApis(property, data)
+ property.type.accept(this, "$data ")
+ property.extensionReceiver?.accept(this, "$data extension:")
+ property.setter?.accept(this, "$data ")
+ property.getter?.accept(this, "$data ")
+ }
+
+ override fun visitTypeReference(typeReference: KSTypeReference, data: String) {
+ if (checkVisited(typeReference)) return
+ typeReference.annotations.forEach { it.accept(this, "$data ") }
+ val type = typeReference.resolve()
+ type.let {
+ emit("resolved to: ${it.declaration.qualifiedName?.asString()}", data)
+ }
+ // resolved.accept(this, "$data ")
+ // TODO: KSTypeReferenceJavaImpl hasn't completed yet.
+ try {
+ typeReference.element?.accept(this, "$data ")
+ } catch (e: IllegalStateException) {
+ emit("TestProcessor: exception: $e", data)
+ }
+ }
+
+ override fun visitAnnotated(annotated: KSAnnotated, data: String) {
+ }
+
+ override fun visitDeclaration(declaration: KSDeclaration, data: String) {
+ }
+
+ override fun visitDeclarationContainer(declarationContainer: KSDeclarationContainer, data: String) {
+ }
+
+ override fun visitParenthesizedReference(reference: KSParenthesizedReference, data: String) {
+ }
+
+ override fun visitClassifierReference(reference: KSClassifierReference, data: String) {
+ if (checkVisited(reference)) return
+ if (reference.typeArguments.toList().isNotEmpty()) {
+ reference.typeArguments.forEach { it.accept(this, "$data ") }
+ }
+ }
+
+ override fun visitTypeAlias(typeAlias: KSTypeAlias, data: String) {
+ }
+
+ override fun visitValueArgument(valueArgument: KSValueArgument, data: String) {
+ if (checkVisited(valueArgument)) return
+ val name = valueArgument.name?.asString() ?: "<no name>"
+ emit("$name: ${valueArgument.value}", data)
+ valueArgument.annotations.forEach { it.accept(this, "$data ") }
+ }
+ }
+}
+
+class TestProcessorProvider2 : SymbolProcessorProvider {
+ override fun create(
+ env: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return TestProcessor().apply {
+ init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
+
+ env.logger.warn("language version: ${env.kotlinVersion}")
+ env.logger.warn("api version: ${env.apiVersion}")
+ env.logger.warn("compiler version: ${env.compilerVersion}")
+ }
+ }
+}
diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/playground/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..3a1528c9
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1,2 @@
+TestProcessorProvider
+TestProcessorProvider2
diff --git a/integration-tests/src/test/resources/playground/workload/G.kt b/integration-tests/src/test/resources/playground/workload/G.kt
new file mode 100644
index 00000000..f2f80ffe
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/workload/G.kt
@@ -0,0 +1,6 @@
+package g
+
+import com.example.annotation.Builder
+
+@Builder
+class G
diff --git a/integration-tests/src/test/resources/playground/workload/build.gradle.kts b/integration-tests/src/test/resources/playground/workload/build.gradle.kts
new file mode 100644
index 00000000..6cb48fad
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/workload/build.gradle.kts
@@ -0,0 +1,36 @@
+import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool
+
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":test-processor"))
+ ksp(project(":test-processor"))
+}
+
+ksp {
+ arg("option1", "value1")
+ arg("option2", "value2")
+}
+
+val compileKotlin: AbstractKotlinCompileTool<*> by tasks
+tasks.register<Copy>("copyG") {
+ from("G.kt")
+ into(File(buildDir, "generatedSources").apply { mkdirs() })
+}.let {
+ // Magic. `map` creates a provider to propagate task dependency.
+ compileKotlin.setSource(it.map { it.destinationDir })
+}
diff --git a/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/A.kt b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/A.kt
new file mode 100644
index 00000000..e5fa7f97
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/A.kt
@@ -0,0 +1,16 @@
+package com.example
+
+import hello.HELLO
+
+fun main() {
+ val hello = HELLO()
+ println(hello.foo())
+
+ val builder = AClassBuilder()
+ builder
+ .withA(1)
+ .withB("foo")
+ .withC(2.3)
+ val aClass: AClass = builder.build()
+ println(aClass.foo())
+}
diff --git a/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/AClass.kt b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/AClass.kt
new file mode 100644
index 00000000..180dbe6a
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/AClass.kt
@@ -0,0 +1,17 @@
+package com.example
+
+import com.example.annotation.Builder
+import hello.HELLO
+
+@Builder
+class AClass(private val a: Int, val b: String, val c: Double, val d: HELLO) {
+ val p = "$a, $b, $c"
+ fun foo() = HELLO()
+ val hello = HELLO()
+ var hello2: HELLO = HELLO()
+ get() { return hello2 }
+ private set
+ class innerClass<T : HELLO>
+
+ val generic = innerClass<HELLO>()
+}
diff --git a/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/BClass.java b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/BClass.java
new file mode 100644
index 00000000..fcfbcbcf
--- /dev/null
+++ b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/BClass.java
@@ -0,0 +1,14 @@
+package com.example;
+
+import com.example.annotation.Builder;
+import hello.HELLO;
+import java.util.List;
+
+@Builder
+public class BClass {
+ public HELLO hello;
+ public HELLO helloFun(){
+ return null;
+ }
+ public List<HELLO> list = null;
+} \ No newline at end of file
diff --git a/integration-tests/src/test/resources/psi-cache/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/psi-cache/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..ad548224
--- /dev/null
+++ b/integration-tests/src/test/resources/psi-cache/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,40 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+
+class TestProcessor(
+ val codeGenerator: CodeGenerator,
+ val logger: KSPLogger
+) : SymbolProcessor {
+ var rounds = 0
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ rounds++
+
+ val syms = resolver.getSymbolsWithAnnotation("com.example.Anno").toList()
+
+ syms.forEach {
+ val v = it.validate()
+ if (rounds == 2 && v == false) {
+ logger.error("validation failed: $it")
+ }
+ }
+
+ if (rounds == 1) {
+ codeGenerator.createNewFile(Dependencies(true), "com.example", "Foo1").use {
+ it.write("package com.example\n\ninterface Foo1\n".toByteArray())
+ }
+ codeGenerator.createNewFile(Dependencies(true), "com.example", "Foo2", "java").use {
+ it.write("package com.example;\n\npublic interface Foo2{}\n".toByteArray())
+ }
+ return syms.toList()
+ }
+
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
+ return TestProcessor(environment.codeGenerator, environment.logger)
+ }
+}
diff --git a/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Bar.kt b/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Bar.kt
new file mode 100644
index 00000000..e6f5a794
--- /dev/null
+++ b/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Bar.kt
@@ -0,0 +1,6 @@
+package com.example
+
+annotation class Anno
+
+@Anno
+open class Bar : Foo1, Foo2
diff --git a/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Baz.java b/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Baz.java
new file mode 100644
index 00000000..4ccfb0db
--- /dev/null
+++ b/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Baz.java
@@ -0,0 +1,6 @@
+package com.example;
+
+@Anno
+public class Baz implements Foo1, Foo2 {
+
+}
diff --git a/integration-tests/src/test/resources/refs-gen/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/refs-gen/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..643deb7c
--- /dev/null
+++ b/integration-tests/src/test/resources/refs-gen/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,48 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+import java.io.OutputStreamWriter
+
+class TestProcessor(
+ val codeGenerator: CodeGenerator,
+ val logger: KSPLogger
+) : SymbolProcessor {
+ // FIXME: use getSymbolsWithAnnotation after it is fixed.
+ var rounds = 0
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ rounds++
+ logger.warn("$rounds: ${resolver.getNewFiles().toList()}")
+
+ if (rounds == 1) {
+ codeGenerator.createNewFile(Dependencies(false), "", "Foo", "kt").use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write("package com.example\n\n")
+ writer.write("open class Foo : Goo()\n")
+ }
+ }
+ }
+
+ if (rounds == 2) {
+ codeGenerator.createNewFile(Dependencies(false), "", "Goo", "kt").use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write("package com.example\n\n")
+ writer.write("open class Goo : Baz()\n")
+ }
+ }
+ }
+
+ resolver.getNewFiles().forEach {
+ it.validate()
+ }
+
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ environment: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return TestProcessor(environment.codeGenerator, environment.logger)
+ }
+}
diff --git a/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Bar.kt
new file mode 100644
index 00000000..9f126ff9
--- /dev/null
+++ b/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Bar.kt
@@ -0,0 +1,3 @@
+package com.example
+
+class Bar : Foo()
diff --git a/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Baz.kt
new file mode 100644
index 00000000..58e29ec8
--- /dev/null
+++ b/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Baz.kt
@@ -0,0 +1,5 @@
+package com.example
+
+open class Baz
+
+val l: List<Int> = TODO()
diff --git a/integration-tests/src/test/resources/sealed-subclasses/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/sealed-subclasses/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..934d43df
--- /dev/null
+++ b/integration-tests/src/test/resources/sealed-subclasses/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,28 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+
+class TestProcessor(
+ val codeGenerator: CodeGenerator,
+ val logger: KSPLogger
+) : SymbolProcessor {
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ resolver.getNewFiles().forEach { f ->
+ logger.warn("Processing ${f.fileName}")
+ f.declarations.forEach {
+ if (it is KSClassDeclaration) {
+ val subs = it.getSealedSubclasses().map { it.simpleName.asString() }.toList()
+ logger.warn("${it.simpleName.asString()} : $subs")
+ }
+ }
+ }
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ environment: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return TestProcessor(environment.codeGenerator, environment.logger)
+ }
+}
diff --git a/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl1.kt b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl1.kt
new file mode 100644
index 00000000..f4cd105e
--- /dev/null
+++ b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl1.kt
@@ -0,0 +1,3 @@
+package com.example
+
+class Impl1 : Sealed()
diff --git a/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl2.kt b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl2.kt
new file mode 100644
index 00000000..aa8f5e86
--- /dev/null
+++ b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl2.kt
@@ -0,0 +1,3 @@
+package com.example
+
+class Impl2 : Sealed()
diff --git a/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Sealed.kt b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Sealed.kt
new file mode 100644
index 00000000..d65d5cc9
--- /dev/null
+++ b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Sealed.kt
@@ -0,0 +1,3 @@
+package com.example
+
+sealed class Sealed
diff --git a/integration-tests/src/test/resources/srcs-gen/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/srcs-gen/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 00000000..f0b0f227
--- /dev/null
+++ b/integration-tests/src/test/resources/srcs-gen/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,54 @@
+import com.google.devtools.ksp.processing.*
+import com.google.devtools.ksp.symbol.*
+import java.io.OutputStreamWriter
+
+class TestProcessor(
+ val codeGenerator: CodeGenerator,
+ val logger: KSPLogger
+) : SymbolProcessor {
+ // FIXME: use getSymbolsWithAnnotation after it is fixed.
+ var rounds = 0
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ rounds++
+ logger.warn("$rounds: ${resolver.getNewFiles().toList().sortedBy { it.fileName }}")
+
+ // Would fail if "Bar.kt" isn't dirty.
+ val barKt = resolver.getAllFiles().single { it.fileName == "Bar.kt" }
+ val bazKt = resolver.getAllFiles().single { it.fileName == "Baz.kt" }
+
+ if (rounds == 1) {
+ codeGenerator.createNewFile(Dependencies(false), "", "Foo", "kt").use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write("package com.example\n\n")
+ writer.write("open class Foo\n")
+ }
+ }
+ }
+
+ if (rounds == 2) {
+ val fooKt = resolver.getAllFiles().single { it.fileName == "Foo.kt" }
+ codeGenerator.createNewFile(Dependencies(false, fooKt, barKt), "", "FooBar", "kt").use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write("package com.example\n\n")
+ writer.write("open class FooBar\n")
+ }
+ }
+ codeGenerator.createNewFile(Dependencies(false, fooKt, bazKt), "", "FooBaz", "kt").use { output ->
+ OutputStreamWriter(output).use { writer ->
+ writer.write("package com.example\n\n")
+ writer.write("open class FooBaz\n")
+ }
+ }
+ }
+
+ return emptyList()
+ }
+}
+
+class TestProcessorProvider : SymbolProcessorProvider {
+ override fun create(
+ environment: SymbolProcessorEnvironment
+ ): SymbolProcessor {
+ return TestProcessor(environment.codeGenerator, environment.logger)
+ }
+}
diff --git a/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Bar.kt
new file mode 100644
index 00000000..2eb12746
--- /dev/null
+++ b/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Bar.kt
@@ -0,0 +1,3 @@
+package com.example
+
+class Bar
diff --git a/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Baz.kt
new file mode 100644
index 00000000..f7b34a4f
--- /dev/null
+++ b/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Baz.kt
@@ -0,0 +1,3 @@
+package com.example
+
+class Baz
diff --git a/integration-tests/src/test/resources/test-processor/build.gradle.kts b/integration-tests/src/test/resources/test-processor/build.gradle.kts
new file mode 100644
index 00000000..c5737a2e
--- /dev/null
+++ b/integration-tests/src/test/resources/test-processor/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
diff --git a/integration-tests/src/test/resources/test-processor/settings.gradle.kts b/integration-tests/src/test/resources/test-processor/settings.gradle.kts
new file mode 100644
index 00000000..9d60cbc7
--- /dev/null
+++ b/integration-tests/src/test/resources/test-processor/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ val kotlinVersion: String by settings
+ val kspVersion: String by settings
+ val testRepo: String by settings
+ plugins {
+ id("com.google.devtools.ksp") version kspVersion
+ kotlin("jvm") version kotlinVersion
+ }
+ repositories {
+ maven(testRepo)
+ gradlePluginPortal()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+ }
+}
+
+rootProject.name = "playground"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/test-processor/test-processor/build.gradle.kts b/integration-tests/src/test/resources/test-processor/test-processor/build.gradle.kts
new file mode 100644
index 00000000..ab249cf0
--- /dev/null
+++ b/integration-tests/src/test/resources/test-processor/test-processor/build.gradle.kts
@@ -0,0 +1,24 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+ kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+ java.srcDirs("src/main/kotlin")
+}
diff --git a/integration-tests/src/test/resources/test-processor/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/test-processor/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 00000000..c91e3e9e
--- /dev/null
+++ b/integration-tests/src/test/resources/test-processor/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+TestProcessorProvider
diff --git a/integration-tests/src/test/resources/test-processor/workload/build.gradle.kts b/integration-tests/src/test/resources/test-processor/workload/build.gradle.kts
new file mode 100644
index 00000000..f0ea52b0
--- /dev/null
+++ b/integration-tests/src/test/resources/test-processor/workload/build.gradle.kts
@@ -0,0 +1,20 @@
+val testRepo: String by project
+
+plugins {
+ id("com.google.devtools.ksp")
+ kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+ maven(testRepo)
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(project(":test-processor"))
+ ksp(project(":test-processor"))
+}