From 579c9daf5227d736520b9bbd07dd5de962e9f138 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Thu, 24 Oct 2024 14:03:47 -0700 Subject: [PATCH 01/25] Set JVM target back to Java 8. Newer AGP is only available with Java 11 variants. Therefore: * KSP is compiled with older AGP and targeting Java 8. * Gradle tests and integration tests run with newer AGP. --- build.gradle.kts | 6 +++--- common-deps/build.gradle.kts | 1 - .../google/devtools/ksp/gradle/AndroidPluginIntegration.kt | 2 +- .../com/google/devtools/ksp/gradle/testing/TestConfig.kt | 2 +- gradle.properties | 3 ++- integration-tests/build.gradle.kts | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f91c394941..e12dd01fa3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -102,12 +102,12 @@ subprojects { pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { configure { toolchain.languageVersion.set(compileJavaVersion) - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } configure { compilerOptions { - jvmTarget = JvmTarget.JVM_11 + jvmTarget = JvmTarget.JVM_1_8 languageVersion.set(KotlinVersion.KOTLIN_1_9) apiVersion.set(languageVersion) } diff --git a/common-deps/build.gradle.kts b/common-deps/build.gradle.kts index 88f0418b39..5b90471f51 100644 --- a/common-deps/build.gradle.kts +++ b/common-deps/build.gradle.kts @@ -5,7 +5,6 @@ description = "Kotlin Symbol Processor" val kotlinBaseVersion: String by project val junitVersion: String by project val googleTruthVersion: String by project -val agpBaseVersion: String by project val signingKey: String? by project val signingPassword: String? by project diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/AndroidPluginIntegration.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/AndroidPluginIntegration.kt index 9647ce6eaa..8c7a006c53 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/AndroidPluginIntegration.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/AndroidPluginIntegration.kt @@ -53,7 +53,7 @@ object AndroidPluginIntegration { private fun decorateAndroidExtension(project: Project, onSourceSet: (String) -> Unit) { val sourceSets = when (val androidExt = project.extensions.getByName("android")) { is BaseExtension -> androidExt.sourceSets - is CommonExtension<*, *, *, *, *, *> -> androidExt.sourceSets + is CommonExtension<*, *, *, *> -> androidExt.sourceSets else -> throw RuntimeException("Unsupported Android Gradle plugin version.") } sourceSets.all { diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/TestConfig.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/TestConfig.kt index 1c048da64d..de975ad2ac 100644 --- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/TestConfig.kt +++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/TestConfig.kt @@ -55,7 +55,7 @@ data class TestConfig( } val androidBaseVersion by lazy { - kspProjectProperties["agpBaseVersion"] as String + kspProjectProperties["agpTestVersion"] as String } val mavenRepoPath = mavenRepoDir.path.replace(File.separatorChar, '/') diff --git a/gradle.properties b/gradle.properties index 1ded0a3151..ac2ef90452 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,8 @@ org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx4096m -Dfile.encoding=UTF-8 kotlinBaseVersion=2.1.20-dev-201 -agpBaseVersion=8.7.0 +agpBaseVersion=7.3.1 +agpTestVersion=8.7.1 intellijVersion=233.13135.128 junitVersion=4.13.1 junit5Version=5.8.2 diff --git a/integration-tests/build.gradle.kts b/integration-tests/build.gradle.kts index 3d238281d0..3e900aa0b1 100644 --- a/integration-tests/build.gradle.kts +++ b/integration-tests/build.gradle.kts @@ -3,7 +3,7 @@ import kotlin.math.max val junitVersion: String by project val kotlinBaseVersion: String by project -val agpBaseVersion: String by project +val agpTestVersion: String by project plugins { kotlin("jvm") @@ -23,7 +23,7 @@ dependencies { fun Test.configureCommonSettings() { systemProperty("kotlinVersion", kotlinBaseVersion) systemProperty("kspVersion", version) - systemProperty("agpVersion", agpBaseVersion) + systemProperty("agpVersion", agpTestVersion) jvmArgumentProviders.add( RelativizingInternalPathProvider( "testRepo", From 2f1f62cc84874b7314d1c64ab692cc32d1f14463 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Tue, 29 Oct 2024 13:29:14 -0700 Subject: [PATCH 02/25] Unbundle kotlinx.coroutines Renaming of kotlinx.coroutines interfers with kotlin-stdlib. --- gradle-plugin/build.gradle.kts | 5 +++++ .../main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt | 3 +++ gradle.properties | 1 + kotlin-analysis-api/build.gradle.kts | 5 ++++- .../com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt | 1 - symbol-processing-aa-embeddable/build.gradle.kts | 3 ++- 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index fcc1181cf9..47d204a00f 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -8,6 +8,7 @@ val googleTruthVersion: String by project val agpBaseVersion: String by project val signingKey: String? by project val signingPassword: String? by project +val aaCoroutinesVersion: String? by project tasks.withType { compilerOptions.freeCompilerArgs.add("-Xjvm-default=all-compatibility") @@ -148,6 +149,8 @@ abstract class WriteVersionSrcTask : DefaultTask() { abstract val kspVersion: Property @get:Input abstract val kotlinVersion: Property + @get:Input + abstract val coroutinesVersion: Property @get:OutputDirectory abstract val outputSrcDir: DirectoryProperty @@ -159,6 +162,7 @@ abstract class WriteVersionSrcTask : DefaultTask() { package com.google.devtools.ksp.gradle val KSP_KOTLIN_BASE_VERSION = "${kotlinVersion.get()}" val KSP_VERSION = "${kspVersion.get()}" + val KSP_COROUTINES_VERSION = "${coroutinesVersion.get()}" """.trimIndent() ) } @@ -167,6 +171,7 @@ abstract class WriteVersionSrcTask : DefaultTask() { val writeVersionSrcTask = tasks.register("generateKSPVersions") { kspVersion = version.toString() kotlinVersion = kotlinBaseVersion + coroutinesVersion = aaCoroutinesVersion outputSrcDir = layout.buildDirectory.dir("generated/ksp-versions") } diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt index 3bef7179ef..cc2967fa58 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt @@ -143,6 +143,9 @@ abstract class KspAATask @Inject constructor( "${KspGradleSubplugin.KSP_GROUP_ID}:symbol-processing-aa-embeddable:$KSP_VERSION" ), project.dependencies.create("org.jetbrains.kotlin:kotlin-stdlib:$KSP_KOTLIN_BASE_VERSION"), + project.dependencies.create( + "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:$KSP_COROUTINES_VERSION" + ), ).apply { isTransitive = false } diff --git a/gradle.properties b/gradle.properties index ac2ef90452..db650d97ae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,6 +18,7 @@ aaFastutilVersion=8.5.11-18 aaStax2Version=4.2.1 aaAaltoXmlVersion=1.3.0 aaStreamexVersion=0.7.2 +aaCoroutinesVersion=1.6.4 compilerTestEnabled=false diff --git a/kotlin-analysis-api/build.gradle.kts b/kotlin-analysis-api/build.gradle.kts index d766af67ad..32eabdb649 100644 --- a/kotlin-analysis-api/build.gradle.kts +++ b/kotlin-analysis-api/build.gradle.kts @@ -23,6 +23,7 @@ val aaFastutilVersion: String by project val aaStax2Version: String by project val aaAaltoXmlVersion: String by project val aaStreamexVersion: String by project +val aaCoroutinesVersion: String by project plugins { kotlin("jvm") @@ -93,7 +94,7 @@ dependencies { implementation("javax.inject:javax.inject:1") implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.10") implementation("org.lz4:lz4-java:1.7.1") { isTransitive = false } - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0") { isTransitive = false } + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:$aaCoroutinesVersion") { isTransitive = false } implementation( "org.jetbrains.intellij.deps.fastutil:intellij-deps-fastutil:$aaFastutilVersion" ) { @@ -157,6 +158,7 @@ tasks.withType().configureEach { exclude(project(":api")) } exclude("kotlin/**") + exclude("kotlinx/coroutines/**") archiveClassifier.set("") minimize { exclude(dependency("org.lz4:lz4-java:.*")) @@ -235,6 +237,7 @@ publishing { asNode().appendNode("dependencies").apply { addDependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlinBaseVersion) + addDependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm", aaCoroutinesVersion) addDependency("com.google.devtools.ksp", "symbol-processing-api", version) addDependency("com.google.devtools.ksp", "symbol-processing-common-deps", version) } diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt index af475ac901..c1941ea9cb 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt @@ -645,7 +645,6 @@ fun String?.toKotlinVersion(): KotlinVersion { @Suppress("unused") @OptIn(KaImplementationDetail::class) internal val DEAR_SHADOW_JAR_PLEASE_DO_NOT_REMOVE_THESE = listOf( - kotlinx.coroutines.debug.internal.DebugProbesImpl::class.java, org.jetbrains.kotlin.analysis.api.impl.base.java.source.JavaElementSourceWithSmartPointerFactory::class.java, org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KaBaseModuleProvider::class.java, org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService::class.java, diff --git a/symbol-processing-aa-embeddable/build.gradle.kts b/symbol-processing-aa-embeddable/build.gradle.kts index 41ece919e8..d0f8310d09 100644 --- a/symbol-processing-aa-embeddable/build.gradle.kts +++ b/symbol-processing-aa-embeddable/build.gradle.kts @@ -15,6 +15,7 @@ val kotlinBaseVersion: String by project val aaKotlinBaseVersion: String by project val aaIntellijVersion: String by project +val aaCoroutinesVersion: String by project plugins { kotlin("jvm") @@ -50,7 +51,6 @@ val prefixesToRelocate = listOf( "javax.inject.", "javax.annotation.", "kotlinx.collections.immutable.", - "kotlinx.coroutines.", "kotlinx.serialization.", "org.apache.log4j.", "org.checkerframework.", @@ -264,6 +264,7 @@ publishing { asNode().appendNode("dependencies").apply { addDependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlinBaseVersion) + addDependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm", aaCoroutinesVersion) addDependency("com.google.devtools.ksp", "symbol-processing-api", version) addDependency("com.google.devtools.ksp", "symbol-processing-common-deps", version) } From ab35821b1307f64385dadbba5c1e26ce124ced85 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Wed, 9 Oct 2024 00:50:14 +0200 Subject: [PATCH 03/25] Kdoc: more details abuot what classpath is searched by Resolver Kdoc only change. See https://github.com/google/ksp/issues/1075 --- .../kotlin/com/google/devtools/ksp/processing/Resolver.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/src/main/kotlin/com/google/devtools/ksp/processing/Resolver.kt b/api/src/main/kotlin/com/google/devtools/ksp/processing/Resolver.kt index cdc3d899c7..6df2586d1d 100644 --- a/api/src/main/kotlin/com/google/devtools/ksp/processing/Resolver.kt +++ b/api/src/main/kotlin/com/google/devtools/ksp/processing/Resolver.kt @@ -38,12 +38,14 @@ interface Resolver { fun getAllFiles(): Sequence /** - * Get all symbols with specified annotation. + * Get all symbols with specified annotation in the current compilation unit. * Note that in multiple round processing, only symbols from deferred symbols of last round and symbols from newly generated files will be returned in this function. * * @param annotationName is the fully qualified name of the annotation; using '.' as separator. * @param inDepth whether to check symbols in depth, i.e. check symbols from local declarations. Operation can be expensive if true. * @return Elements annotated with the specified annotation. + * + * @see getDeclarationsFromPackage to get declarations outside the current compilation unit. */ fun getSymbolsWithAnnotation(annotationName: String, inDepth: Boolean = false): Sequence @@ -231,6 +233,9 @@ interface Resolver { /** * Returns declarations with the given package name. + * + * getDeclarationsFromPackage looks for declaration in the whole classpath, including dependencies. + * * @param packageName the package name to look up. * @return A sequence of [KSDeclaration] with matching package name. * This will return declarations from both dependencies and source. From d98bc9c3f8b8b14f3d62543d64707a49f8514c73 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Fri, 25 Oct 2024 16:49:45 -0700 Subject: [PATCH 04/25] KSP2: update for all, not just initial dirty sources * Dirtiness didn't propagate across generated files previously. * Also fixes the issue of using out-of-date PSIs when getting paths. --- .../com/google/devtools/ksp/test/PlaygroundIT.kt | 10 ++++++++++ .../google/devtools/ksp/impl/KotlinSymbolProcessing.kt | 3 +-- 2 files changed, 11 insertions(+), 2 deletions(-) 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 index 511294e91a..d775a049c2 100644 --- 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 @@ -372,6 +372,16 @@ class PlaygroundIT(val useKSP2: Boolean) { } } + @Test + fun testEmpty() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + File(project.root, "workload/src/main/java/Empty.kt").appendText("\n\n") + gradleRunner.withArguments("clean", "assemble", "-Pksp.incremental.log=false").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome) + } + } + companion object { @JvmStatic @Parameterized.Parameters(name = "KSP2={0}") diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt index c1941ea9cb..4c5f0bc312 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt @@ -495,7 +495,6 @@ class KotlinSymbolProcessing( ) var allDirtyKSFiles = incrementalContext.calcDirtyFiles(allKSFiles).toList() var newKSFiles = allDirtyKSFiles - val initialDirtySet = allDirtyKSFiles.toSet() val targetPlatform = ResolverAAImpl.ktModule.targetPlatform val symbolProcessorEnvironment = SymbolProcessorEnvironment( @@ -589,7 +588,7 @@ class KotlinSymbolProcessing( if (!logger.hasError) { incrementalContext.updateCachesAndOutputs( - initialDirtySet, + allDirtyKSFiles, codeGenerator.outputs, codeGenerator.sourceToOutputs ) From 8864a5e057faf99d580dc9a07e3311cc5e60bf1d Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Wed, 30 Oct 2024 14:06:11 -0700 Subject: [PATCH 05/25] KSP2: Reimplement getSymbolsWithAnnotation for aliased annotations in libraries. --- .../devtools/ksp/impl/ResolverAAImpl.kt | 58 +++---------------- 1 file changed, 9 insertions(+), 49 deletions(-) diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt index 88a4644097..344050f455 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt @@ -40,6 +40,7 @@ import org.jetbrains.kotlin.analysis.api.fir.types.KaFirType import org.jetbrains.kotlin.analysis.api.projectStructure.KaSourceModule import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.types.KaType +import org.jetbrains.kotlin.analysis.api.types.symbol import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade @@ -92,31 +93,6 @@ class ResolverAAImpl( allKSFiles.filter { it.fileName == "package-info.java" }.asSequence().memoized() } - private val aliasingFqNs: Map by lazy { - val result = mutableMapOf() - val visitor = object : KSVisitorVoid() { - override fun visitFile(file: KSFile, data: Unit) { - file.declarations.forEach { it.accept(this, data) } - - // TODO: evaluate with benchmarks: cost of getContainingFile v.s. name collision - // Import aliases are file-scoped. `aliasingNamesByFile` could be faster - ((file as? KSFileImpl)?.ktFileSymbol?.psi as? KtFile)?.importDirectives?.forEach { - it.aliasName?.let { aliasingNames.add(it) } - } - } - - override fun visitTypeAlias(typeAlias: KSTypeAlias, data: Unit) { - typeAlias.qualifiedName?.asString()?.let { fqn -> - result[fqn] = typeAlias - aliasingNames.add(fqn.substringAfterLast('.')) - } - } - } - allKSFiles.forEach { it.accept(visitor, Unit) } - result - } - private val aliasingNames: MutableSet = mutableSetOf() - // TODO: fix in upstream for builtin types. override val builtIns: KSBuiltIns by lazy { val builtIns = analyze { useSiteSession.builtinTypes } @@ -568,36 +544,20 @@ class ResolverAAImpl( } } - internal fun KSTypeReference.resolveToUnderlying(): KSType { - var candidate = resolve() - var declaration = candidate.declaration - while (declaration is KSTypeAlias) { - candidate = declaration.type.resolve() - declaration = candidate.declaration - } - return candidate - } - internal fun checkAnnotation(annotation: KSAnnotation, ksName: KSName, shortName: String): Boolean { - val annotationType = annotation.annotationType - val referencedName = (annotationType.element as? KSClassifierReference)?.referencedName() - val simpleName = referencedName?.substringAfterLast('.') - return (simpleName == shortName || simpleName in aliasingNames) && - annotationType.resolveToUnderlying().declaration.qualifiedName == ksName - } - // Currently, all annotation types are imlemented by KSTypeReferenceResolvedImpl. - // The short-name-check optimization doesn't help. override fun getSymbolsWithAnnotation(annotationName: String, inDepth: Boolean): Sequence { - val realAnnotationName = - aliasingFqNs[annotationName]?.type?.resolveToUnderlying()?.declaration?.qualifiedName?.asString() - ?: annotationName + val expandedIfAlias = analyze { + val classId = ClassId.fromString(annotationName) + findTypeAlias(classId)?.expandedType?.symbol?.classId?.asFqNameString() + } + val realAnnotationName = expandedIfAlias ?: annotationName - val ksName = KSNameImpl.getCached(realAnnotationName) - val shortName = ksName.getShortName() fun checkAnnotated(annotated: KSAnnotated): Boolean { return annotated.annotations.any { - checkAnnotation(it, ksName, shortName) + val kaType = (it.annotationType.resolve() as? KSTypeImpl)?.type ?: return@any false + kaType.toAbbreviatedType().symbol?.classId?.asFqNameString() == realAnnotationName } } + val newSymbols = if (inDepth) newAnnotatedSymbolsWithLocals else newAnnotatedSymbols return (newSymbols + deferredSymbolsRestored).asSequence().filter(::checkAnnotated) } From 9a40de304aa37709b1bebb470f2d7acbb119a8e8 Mon Sep 17 00:00:00 2001 From: Kuan-Ying Chou Date: Thu, 8 Aug 2024 23:08:02 +0100 Subject: [PATCH 06/25] Test typealias annotations --- .../com/google/devtools/ksp/test/KSPAATest.kt | 6 + .../testData/getSymbolsFromAnnotationInLib.kt | 179 ++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 kotlin-analysis-api/testData/getSymbolsFromAnnotationInLib.kt diff --git a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt index de94ca2602..1d95504fc6 100644 --- a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt +++ b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt @@ -305,6 +305,12 @@ class KSPAATest : AbstractKSPAATest() { runTest("../kotlin-analysis-api/testData/getSymbolsFromAnnotation.kt") } + @TestMetadata("getSymbolsFromAnnotationInLib.kt") + @Test + fun testGetSymbolsFromAnnotationInLib() { + runTest("../kotlin-analysis-api/testData/getSymbolsFromAnnotationInLib.kt") + } + @TestMetadata("hello.kt") @Test fun testHello() { diff --git a/kotlin-analysis-api/testData/getSymbolsFromAnnotationInLib.kt b/kotlin-analysis-api/testData/getSymbolsFromAnnotationInLib.kt new file mode 100644 index 0000000000..e39282d641 --- /dev/null +++ b/kotlin-analysis-api/testData/getSymbolsFromAnnotationInLib.kt @@ -0,0 +1,179 @@ +/* + * Copyright 2024 Google LLC + * Copyright 2010-2024 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. + */ + +// TEST PROCESSOR: GetSymbolsFromAnnotationProcessor +// EXPECTED: +// ==== Anno superficial==== +// Foo:KSClassDeclaration +// constructorParameterFoo:KSPropertyDeclaration +// propertyFoo:KSPropertyDeclaration +// functionFoo:KSFunctionDeclaration +// p1:KSValueParameter +// :KSFunctionDeclaration +// constructorParameterFoo:KSValueParameter +// param:KSValueParameter +// Bar:KSClassDeclaration +// Baz:KSClassDeclaration +// Burp:KSClassDeclaration +// Flux:KSTypeAlias +// RGB.B:KSClassDeclaration +// ==== Anno in depth ==== +// Foo:KSClassDeclaration +// constructorParameterFoo:KSPropertyDeclaration +// propertyFoo:KSPropertyDeclaration +// functionFoo:KSFunctionDeclaration +// p1:KSValueParameter +// local:KSPropertyDeclaration +// :KSFunctionDeclaration +// constructorParameterFoo:KSValueParameter +// param:KSValueParameter +// Bar:KSClassDeclaration +// Baz:KSClassDeclaration +// Burp:KSClassDeclaration +// Flux:KSTypeAlias +// RGB.B:KSClassDeclaration +// ==== Bnno superficial==== +// File: Foo.kt:KSFile +// propertyFoo.getter():KSPropertyAccessorImpl +// p2:KSValueParameter +// :KSFunctionDeclaration +// ==== Bnno in depth ==== +// File: Foo.kt:KSFile +// propertyFoo.getter():KSPropertyAccessorImpl +// p2:KSValueParameter +// :KSFunctionDeclaration +// ==== A1 superficial==== +// Foo:KSClassDeclaration +// constructorParameterFoo:KSPropertyDeclaration +// propertyFoo:KSPropertyDeclaration +// functionFoo:KSFunctionDeclaration +// p1:KSValueParameter +// :KSFunctionDeclaration +// constructorParameterFoo:KSValueParameter +// param:KSValueParameter +// Bar:KSClassDeclaration +// Baz:KSClassDeclaration +// Burp:KSClassDeclaration +// Flux:KSTypeAlias +// RGB.B:KSClassDeclaration +// ==== A1 in depth ==== +// Foo:KSClassDeclaration +// constructorParameterFoo:KSPropertyDeclaration +// propertyFoo:KSPropertyDeclaration +// functionFoo:KSFunctionDeclaration +// p1:KSValueParameter +// local:KSPropertyDeclaration +// :KSFunctionDeclaration +// constructorParameterFoo:KSValueParameter +// param:KSValueParameter +// Bar:KSClassDeclaration +// Baz:KSClassDeclaration +// Burp:KSClassDeclaration +// Flux:KSTypeAlias +// RGB.B:KSClassDeclaration +// ==== A2 superficial==== +// Foo:KSClassDeclaration +// constructorParameterFoo:KSPropertyDeclaration +// propertyFoo:KSPropertyDeclaration +// functionFoo:KSFunctionDeclaration +// p1:KSValueParameter +// :KSFunctionDeclaration +// constructorParameterFoo:KSValueParameter +// param:KSValueParameter +// Bar:KSClassDeclaration +// Baz:KSClassDeclaration +// Burp:KSClassDeclaration +// Flux:KSTypeAlias +// RGB.B:KSClassDeclaration +// ==== A2 in depth ==== +// Foo:KSClassDeclaration +// constructorParameterFoo:KSPropertyDeclaration +// propertyFoo:KSPropertyDeclaration +// functionFoo:KSFunctionDeclaration +// p1:KSValueParameter +// local:KSPropertyDeclaration +// :KSFunctionDeclaration +// constructorParameterFoo:KSValueParameter +// param:KSValueParameter +// Bar:KSClassDeclaration +// Baz:KSClassDeclaration +// Burp:KSClassDeclaration +// Flux:KSTypeAlias +// RGB.B:KSClassDeclaration +// ==== Cnno in depth ==== +// constructorParameterFoo:KSPropertyDeclaration +// value:KSValueParameter +// constructorParameterFoo:KSValueParameter +// x:KSPropertyDeclaration +// x:KSValueParameter +// END +// MODULE: lib +//FILE: annotaitons_in_lib.kt +annotation class Anno +annotation class Bnno +annotation class Cnno + +//FILE: aliases_in_lib.kt +typealias A1 = Anno +typealias A2 = A1 + +// MODULE: main(lib) +//FILE: Foo.kt +@file:Bnno + +import Anno +import Anno as A3 + +@Anno +class Foo @Anno constructor(@Anno @param:Cnno val constructorParameterFoo: Int, @Anno param: Int){ + @Bnno constructor() { + + } + + @Anno + val propertyFoo: String + @Bnno get() = TODO() + + @Anno + fun functionFoo(@Anno p1: Int, @Bnno p2: Int) { + @Anno val local = 1 + } + + @setparam:Cnno + var a = 1 +} + +class C(@Cnno val x: Int) + +@A1 +class Bar + +@A2 +class Baz + +@A3 +class Burp + +@Anno +typealias Flux = String + +enum class RGB{ + R, + G, + @Anno + B +} From 5b1cd4a998619a174058842fdb93e59dfc98dc46 Mon Sep 17 00:00:00 2001 From: Scott Pollom Date: Wed, 30 Oct 2024 22:07:15 -0700 Subject: [PATCH 07/25] Fix error when KSP plugin applied before AGP Previously, when the KSP plugin was applied before AGP and AGP's built-in Kotlin support for test fixtures or screenshot tests was enabled, there would be an error when building. The underlying cause for this error is https://github.com/gradle/gradle/issues/31092, which caused the KSP plugin to try to access the android extension before AGP's apply method had completed. This change fixes the problem by using the "com.android.base" plugin as the indicator that a module is an Android module. The "com.android.base" plugin is applied after the android extension has been added, so it works around the Gradle bug. Bug: https://github.com/google/ksp/issues/2174 Test: GradleCompilationTest --- .../ksp/gradle/AndroidPluginIntegration.kt | 10 +++----- .../ksp/gradle/GradleCompilationTest.kt | 12 ++++++++++ .../gradle/testing/KspIntegrationTestRule.kt | 23 ++++++++++++++----- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/AndroidPluginIntegration.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/AndroidPluginIntegration.kt index 8c7a006c53..a1e3655eee 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/AndroidPluginIntegration.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/AndroidPluginIntegration.kt @@ -39,14 +39,10 @@ import java.util.concurrent.Callable @Suppress("UnstableApiUsage") // some android APIs are unsable. object AndroidPluginIntegration { - private val agpPluginIds = listOf("com.android.application", "com.android.library", "com.android.dynamic-feature") - fun forEachAndroidSourceSet(project: Project, onSourceSet: (String) -> Unit) { - agpPluginIds.forEach { agpPluginId -> - project.pluginManager.withPlugin(agpPluginId) { - // for android apps, we need a configuration per source set - decorateAndroidExtension(project, onSourceSet) - } + project.pluginManager.withPlugin("com.android.base") { + // for android modules, we need a configuration per source set + decorateAndroidExtension(project, onSourceSet) } } diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt index 27ccd4cd47..d0fe401401 100644 --- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt +++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt @@ -414,4 +414,16 @@ class GradleCompilationTest { ) testRule.runner().withArguments(":app:assembleDebug").build() } + + /** + * Regression test for https://github.com/google/ksp/issues/2174 + */ + @Test + fun androidGradlePluginBuiltInKotlinWithKspAppliedFirst() { + testRule.setupAppAsAndroidApp(applyKspPluginFirst = true) + // Enable AGP's built-in Kotlin support for test fixtures + testRule.runner() + .withArguments("tasks", "-Pandroid.experimental.enableTestFixturesKotlinSupport=true") + .build() + } } diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/KspIntegrationTestRule.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/KspIntegrationTestRule.kt index f4fff4c835..baf69f5d83 100644 --- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/KspIntegrationTestRule.kt +++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/KspIntegrationTestRule.kt @@ -93,14 +93,25 @@ class KspIntegrationTestRule( /** * Sets up the app module as an android app, adding necessary plugin dependencies, a manifest * file and necessary gradle configuration. If [enableAgpBuiltInKotlinSupport] is true, enable AGP's built-in Kotlin - * support instead of applying the Kotlin Android Gradle plugin. + * support instead of applying the Kotlin Android Gradle plugin. If [applyKspPluginFirst] is true, apply the KSP + * plugin first. */ - fun setupAppAsAndroidApp(enableAgpBuiltInKotlinSupport: Boolean = false) { + fun setupAppAsAndroidApp( + enableAgpBuiltInKotlinSupport: Boolean = false, + applyKspPluginFirst: Boolean = false + ) { testProject.appModule.plugins.addAll( - listOf( - PluginDeclaration.id("com.android.application", testConfig.androidBaseVersion), - PluginDeclaration.id("com.google.devtools.ksp", testConfig.kspVersion) - ) + if (applyKspPluginFirst) { + listOf( + PluginDeclaration.id("com.google.devtools.ksp", testConfig.kspVersion), + PluginDeclaration.id("com.android.application", testConfig.androidBaseVersion) + ) + } else { + listOf( + PluginDeclaration.id("com.android.application", testConfig.androidBaseVersion), + PluginDeclaration.id("com.google.devtools.ksp", testConfig.kspVersion) + ) + } ) if (enableAgpBuiltInKotlinSupport) { testProject.appModule From 37f3dc7046a718bc4a981577418d88e5271f9da5 Mon Sep 17 00:00:00 2001 From: Jon Amireh Date: Thu, 31 Oct 2024 10:46:16 -0700 Subject: [PATCH 08/25] Check for properties starting with 'is' --- .../com/google/devtools/ksp/impl/ResolverAAImpl.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt index 344050f455..3dfaa123be 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt @@ -458,16 +458,18 @@ class ResolverAAImpl( return accessor.receiver.simpleName.asString() } - val prefix = if (accessor is KSPropertyGetter) { - "get" - } else { - "set" + val name = accessor.receiver.simpleName.asString() + val uppercasedName = name.replaceFirstChar(Char::uppercaseChar) + // https://kotlinlang.org/docs/java-to-kotlin-interop.html#properties + val prefixedName = when(accessor) { + is KSPropertyGetter -> if(name.startsWith("is")) name else "get$uppercasedName" + is KSPropertySetter -> if(name.startsWith("is")) "set${name.removePrefix("is")}" else "set$uppercasedName" + else -> "" } - val name = accessor.receiver.simpleName.asString().replaceFirstChar(Char::uppercaseChar) val inlineSuffix = symbol?.inlineSuffix ?: "" val mangledName = symbol?.internalSuffix ?: "" - return "$prefix$name$inlineSuffix$mangledName" + return "$prefixedName$inlineSuffix$mangledName" } // TODO: handle library symbols From 38ac066e7db697f5e86e6ea0d85b5ca5a5d007c0 Mon Sep 17 00:00:00 2001 From: Jon Amireh Date: Thu, 31 Oct 2024 12:12:56 -0700 Subject: [PATCH 09/25] Add tests --- .../com/google/devtools/ksp/test/AbstractKSPTest.kt | 4 ++++ kotlin-analysis-api/testData/jvmName.kt | 10 +++++----- test-utils/testData/api/jvmName.kt | 8 ++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt index 826f5eff8c..f9f3592151 100644 --- a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt +++ b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt @@ -227,6 +227,10 @@ abstract class AbstractKSPTest(frontend: FrontendKind<*>) : DisposableTest() { .map { it.substring(3).trim() } val results = runTest(testServices, mainModule, libModules, testProcessor) + val expected = expectedResults.joinToString("\n") + val actual = results.joinToString("\n") + println(expected) + println(actual) Assertions.assertEquals(expectedResults.joinToString("\n"), results.joinToString("\n")) } } diff --git a/kotlin-analysis-api/testData/jvmName.kt b/kotlin-analysis-api/testData/jvmName.kt index 591684d550..1f05af27fc 100644 --- a/kotlin-analysis-api/testData/jvmName.kt +++ b/kotlin-analysis-api/testData/jvmName.kt @@ -1,7 +1,7 @@ // TEST PROCESSOR: JvmNameProcessor // EXPECTED: -// (getX, setX), (getY, null) -// (getX, setX), (getY, null) +// (getX, setX), (getY, null), (isOpen, setOpen) +// (getX, setX), (getY, null), (isOpen, setOpen) // stringParameter // stringParameter // stringParameter @@ -10,7 +10,7 @@ // END // MODULE: lib // FILE: Lib.kt -data class TestLibDataClass(var x: Int, val y: String) +data class TestLibDataClass(var x: Int, val y: String, var isOpen: String) // FILE: MyAnnotationLib.kt annotation class MyAnnotationLib( @get:JvmName("stringParameter") @@ -21,13 +21,13 @@ annotation class MyAnnotationLib( class MyAnnotationUserLib {} // MODULE: main(lib) +// FILE: K.kt // FILE: MyAnnotation.kt annotation class MyAnnotation( @get:JvmName("stringParameter") val stringParam: String ) -// FILE: K.kt -data class TestDataClass(var x: Int, val y: String) +data class TestDataClass(var x: Int, val y: String, var isOpen: String) // FILE: MyAnnotationUser.java @MyAnnotationLib(stringParameter = "foo") @MyAnnotation(stringParameter = "foo") diff --git a/test-utils/testData/api/jvmName.kt b/test-utils/testData/api/jvmName.kt index 7fab74a5f6..4393e29a47 100644 --- a/test-utils/testData/api/jvmName.kt +++ b/test-utils/testData/api/jvmName.kt @@ -1,7 +1,7 @@ // TEST PROCESSOR: JvmNameProcessor // EXPECTED: -// (getX, setX), (getY, null) -// (getX, setX), (getY, null) +// (getX, setX), (getY, null), (isOpen, setOpen) +// (isOpen, setOpen), (getX, setX), (getY, null) // stringParameter // stringParameter // stringParameter @@ -10,7 +10,7 @@ // END // MODULE: lib // FILE: Lib.kt -data class TestLibDataClass(var x: Int, val y: String) +data class TestLibDataClass(var x: Int, val y: String, var isOpen: String) // FILE: MyAnnotationLib.kt annotation class MyAnnotationLib( @get:JvmName("stringParameter") @@ -27,7 +27,7 @@ annotation class MyAnnotation( @get:JvmName("stringParameter") val stringParam: String ) -data class TestDataClass(var x: Int, val y: String) +data class TestDataClass(var x: Int, val y: String, var isOpen: String) // FILE: MyAnnotationUser.java @MyAnnotationLib(stringParameter = "foo") @MyAnnotation(stringParameter = "foo") From 70853db0a8bc5eb182aa0c587c23e857c7309334 Mon Sep 17 00:00:00 2001 From: Jon Amireh Date: Thu, 31 Oct 2024 12:13:36 -0700 Subject: [PATCH 10/25] Revert unnecessary change --- .../kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt index f9f3592151..826f5eff8c 100644 --- a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt +++ b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt @@ -227,10 +227,6 @@ abstract class AbstractKSPTest(frontend: FrontendKind<*>) : DisposableTest() { .map { it.substring(3).trim() } val results = runTest(testServices, mainModule, libModules, testProcessor) - val expected = expectedResults.joinToString("\n") - val actual = results.joinToString("\n") - println(expected) - println(actual) Assertions.assertEquals(expectedResults.joinToString("\n"), results.joinToString("\n")) } } From 2a571ad54693975d76e62b63631800dfb75954c8 Mon Sep 17 00:00:00 2001 From: Jon Amireh Date: Fri, 1 Nov 2024 09:09:53 -0700 Subject: [PATCH 11/25] Lint --- .../kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt index 3dfaa123be..f78a84577c 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt @@ -461,9 +461,9 @@ class ResolverAAImpl( val name = accessor.receiver.simpleName.asString() val uppercasedName = name.replaceFirstChar(Char::uppercaseChar) // https://kotlinlang.org/docs/java-to-kotlin-interop.html#properties - val prefixedName = when(accessor) { - is KSPropertyGetter -> if(name.startsWith("is")) name else "get$uppercasedName" - is KSPropertySetter -> if(name.startsWith("is")) "set${name.removePrefix("is")}" else "set$uppercasedName" + val prefixedName = when (accessor) { + is KSPropertyGetter -> if (name.startsWith("is")) name else "get$uppercasedName" + is KSPropertySetter -> if (name.startsWith("is")) "set${name.removePrefix("is")}" else "set$uppercasedName" else -> "" } From bfa9de119be6a67793647990ba767e2a18d7809b Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Fri, 1 Nov 2024 15:30:04 -0700 Subject: [PATCH 12/25] Disable KSP2 tasks with corresponding compilation If the corresponding Kotlin native compilation is disabled, KSP should be disabled as well. This aligns with KSP1's behavior. --- .../main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt index cc2967fa58..17d8cabfff 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt @@ -43,6 +43,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile +import org.jetbrains.kotlin.konan.target.HostManager import java.io.ByteArrayOutputStream import java.io.File import java.io.ObjectOutputStream @@ -269,6 +270,11 @@ abstract class KspAATask @Inject constructor( val konanTargetName = kotlinCompilation.target.konanTarget.name cfg.konanTargetName.value(konanTargetName) cfg.konanHome.value((kotlinCompileProvider.get() as KotlinNativeCompile).konanHome) + kspAATask.onlyIf { + HostManager().enabled.any { + it.name == konanTargetName + } + } } // TODO: pass targets of common From 0d1cdb388707d2e1539fb90145314ea950dd4f02 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Tue, 5 Nov 2024 11:01:40 -0800 Subject: [PATCH 13/25] UPDATE_KOTLIN_VERSION: 2.1.20-dev-2637 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index db650d97ae..d9dd5f472e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Copied from kotlinc org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx4096m -Dfile.encoding=UTF-8 -kotlinBaseVersion=2.1.20-dev-201 +kotlinBaseVersion=2.1.20-dev-2637 agpBaseVersion=7.3.1 agpTestVersion=8.7.1 intellijVersion=233.13135.128 From e915b4aed3e8b00fa603028076ada1600cebfa58 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Tue, 5 Nov 2024 11:03:59 -0800 Subject: [PATCH 14/25] UPDATE_AA_VERSION: 2.1.20-dev-2637 --- gradle.properties | 2 +- kotlin-analysis-api/build.gradle.kts | 3 +-- .../com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/gradle.properties b/gradle.properties index d9dd5f472e..80f059e7f6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ junit5Version=5.8.2 junitPlatformVersion=1.8.2 googleTruthVersion=1.1 -aaKotlinBaseVersion=2.1.20-dev-1729 +aaKotlinBaseVersion=2.1.20-dev-2637 aaIntellijVersion=233.13135.128 aaGuavaVersion=33.2.0-jre aaAsmVersion=9.0 diff --git a/kotlin-analysis-api/build.gradle.kts b/kotlin-analysis-api/build.gradle.kts index 32eabdb649..37160860cb 100644 --- a/kotlin-analysis-api/build.gradle.kts +++ b/kotlin-analysis-api/build.gradle.kts @@ -114,8 +114,7 @@ dependencies { testRuntimeOnly("org.junit.jupiter:junit-jupiter-params:$junit5Version") testRuntimeOnly("org.junit.platform:junit-platform-suite:$junitPlatformVersion") testImplementation("org.jetbrains.kotlin:kotlin-compiler:$aaKotlinBaseVersion") - // FIXME: use aaKotlinBaseVersion after the dependency is fixed. - testImplementation("org.jetbrains.kotlin:kotlin-compiler-internal-test-framework:2.1.20-dev-201") + testImplementation("org.jetbrains.kotlin:kotlin-compiler-internal-test-framework:$aaKotlinBaseVersion") testImplementation(project(":common-deps")) testImplementation(project(":test-utils")) diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt index 4c5f0bc312..b226345d07 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt @@ -653,14 +653,14 @@ internal val DEAR_SHADOW_JAR_PLEASE_DO_NOT_REMOVE_THESE = listOf( org.jetbrains.kotlin.analysis.api.standalone.base.declarations.KotlinStandaloneFirDirectInheritorsProvider::class.java, org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLRealFirElementByPsiElementChooser::class.java, org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidationService::class.java, - org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased - .deserialization.LLStubBasedLibrarySymbolProviderFactory::class.java, + org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.LLStubBasedLibrarySymbolProviderFactory::class.java, org.jetbrains.kotlin.analysis.api.impl.base.permissions.KaBaseAnalysisPermissionChecker::class.java, org.jetbrains.kotlin.analysis.api.platform.KotlinProjectMessageBusProvider::class.java, org.jetbrains.kotlin.analysis.api.platform.permissions.KaAnalysisPermissionChecker::class.java, org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinSimpleGlobalSearchScopeMerger::class.java, org.jetbrains.kotlin.analysis.api.fir.modification.KaFirSourceModificationService::class.java, org.jetbrains.kotlin.analysis.api.fir.references.KotlinFirReferenceContributor::class.java, + org.jetbrains.kotlin.analysis.api.fir.statistics.KaFirStatisticsService::class.java, org.jetbrains.kotlin.light.classes.symbol.SymbolKotlinAsJavaSupport::class.java, org.jetbrains.kotlin.load.java.ErasedOverridabilityCondition::class.java, org.jetbrains.kotlin.load.java.FieldOverridabilityCondition::class.java, From 0d114d9e943b6d3962428b168ede2408281c8ce6 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Tue, 5 Nov 2024 11:07:28 -0800 Subject: [PATCH 15/25] Fix a dependency of integration-tests This is a followup of unbundling kotlinx.coroutines, which needs to be explicitly included in the classpath. --- docs/ksp2cmdline.md | 13 +++++++------ docs/ksp2entrypoints.md | 7 ++++--- integration-tests/build.gradle.kts | 2 ++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/ksp2cmdline.md b/docs/ksp2cmdline.md index a4324b94e9..a31a1185f9 100644 --- a/docs/ksp2cmdline.md +++ b/docs/ksp2cmdline.md @@ -2,21 +2,22 @@ KSP2 has 4 main classes, one for each platform: `KSPJvmMain`, `KSPJsMain`, `KSPNativeMain`, `KSPCommonMain`. They reside in the same jars from the -[artifacts.zip](https://github.com/google/ksp/releases/download/2.0.0-1.0.21/artifacts.zip) in the -[release page](https://github.com/google/ksp/releases/tag/2.0.0-1.0.21): -* `symbol-processing-aa-2.0.0-1.0.21.jar` +[artifacts.zip](https://github.com/google/ksp/releases/download/2.0.21-1.0.26/artifacts.zip) in the +[release page](https://github.com/google/ksp/releases/tag/2.0.21-1.0.26): +* `symbol-processing-aa-2.0.21-1.0.26.jar` and depend on: -* `symbol-processing-common-deps-2.0.0-1.0.21.jar` +* `symbol-processing-common-deps-2.0.21-1.0.26.jar` You’ll also need the Kotlin runtime: -* `kotlin-stdlib-2.0.0.jar` +* `kotlin-stdlib-2.0.21.jar` +* `kotlinx-coroutines-core-jvm-1.6.4.jar` Taking `KSPJvmMain` for example, ``` java -cp \ -kotlin-analysis-api-2.0.0-1.0.21.jar:common-deps-2.0.0-1.0.21.jar:symbol-processing-api-2.0.0-1.0.21.jar:kotlin-stdlib-2.0.0.jar \ +kotlin-analysis-api-2.0.21-1.0.26.jar:common-deps-2.0.21-1.0.26.jar:symbol-processing-api-2.0.21-1.0.26.jar:kotlin-stdlib-2.0.21.jar:kotlinx-coroutines-core-jvm-1.6.4.jar \ com.google.devtools.ksp.cmdline.KSPJvmMain \ -jvm-target 11 \ -module-name=main \ diff --git a/docs/ksp2entrypoints.md b/docs/ksp2entrypoints.md index 6c3ec89570..85f10a059a 100644 --- a/docs/ksp2entrypoints.md +++ b/docs/ksp2entrypoints.md @@ -1,9 +1,10 @@ # Calling KSP2 In Programs There are two flavors of KSP2 artifacts: `symbol-processing-aa` and `symbol-processing-aa-embeddable`. They are both -uber jars that include almost all runtime dependencies except `kotlin-stdlib` and `symbol-processing-common-deps`. -The `-embeddable` version is the regular version with all the runtime dependencies renamed, so that it can be used with -a Kotlin compiler in the same classpath without name clash. When in doubt, use `symbol-processing-aa-embeddable`. +uber jars that include almost all runtime dependencies except `kotlin-stdlib`, `kotlinx-coroutines` and +`symbol-processing-common-deps`. The `-embeddable` version is the regular version with all the runtime dependencies +renamed, so that it can be used with a Kotlin compiler in the same classpath without name clash. When in doubt, use +`symbol-processing-aa-embeddable`. Calling KSP2 consists of just 4 steps: 1. Load processors diff --git a/integration-tests/build.gradle.kts b/integration-tests/build.gradle.kts index 3e900aa0b1..7e985efbd8 100644 --- a/integration-tests/build.gradle.kts +++ b/integration-tests/build.gradle.kts @@ -4,6 +4,7 @@ import kotlin.math.max val junitVersion: String by project val kotlinBaseVersion: String by project val agpTestVersion: String by project +val aaCoroutinesVersion: String by project plugins { kotlin("jvm") @@ -18,6 +19,7 @@ dependencies { testImplementation(project(":symbol-processing")) testImplementation(project(":symbol-processing-cmdline")) testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:$aaCoroutinesVersion") } fun Test.configureCommonSettings() { From 3fac0c1a917acc9e52b35eb35fd32986544828ef Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Tue, 5 Nov 2024 14:33:01 -0800 Subject: [PATCH 16/25] KSP2: fix module names for Android builds by mimicking the rules from KGP. --- .../kotlin/com/google/devtools/ksp/gradle/KspAATask.kt | 7 ++++++- .../kotlin/com/google/devtools/ksp/test/AndroidIT.kt | 3 +++ .../test-processor/src/main/kotlin/TestProcessor.kt | 9 +++++++++ .../workload/src/main/java/com/example/AClass.kt | 2 ++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt index 17d8cabfff..2cec471e17 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt @@ -154,7 +154,12 @@ abstract class KspAATask @Inject constructor( kspAATask.kspClasspath.from(kspAADepCfg) kspAATask.kspConfig.let { cfg -> cfg.processorClasspath.from(processorClasspath) - cfg.moduleName.value(project.name) + // Ref: https://github.com/JetBrains/kotlin/blob/6535f86dfe36effeba976802ebd56a5a56071f45/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt#L92 + val moduleName = when (val compilationName = kotlinCompilation.name) { + KotlinCompilation.MAIN_COMPILATION_NAME -> project.name + else -> "${project.name}_$compilationName" + } + cfg.moduleName.value(moduleName) val kotlinOutputDir = KspGradleSubplugin.getKspKotlinOutputDir(project, sourceSetName, target) val javaOutputDir = KspGradleSubplugin.getKspJavaOutputDir(project, sourceSetName, target) val filteredTasks = 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 index 099f4bd921..0aed320ef5 100644 --- 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 @@ -32,6 +32,9 @@ class AndroidIT(useKSP2: Boolean) { assert("-keep class com.example.AClassBuilder { *; }" in configurationText) { "Merged configuration did not contain generated proguard rules!\n$configurationText" } + val outputs = result.output.lines() + assert("w: [ksp] [workload_debug] Mangled name for internalFun: internalFun\$workload_debug" in outputs) + assert("w: [ksp] [workload_release] Mangled name for internalFun: internalFun\$workload_release" in outputs) } } 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 index 2025a1ef49..57d479822d 100644 --- 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 @@ -1,5 +1,6 @@ import com.google.devtools.ksp.KspExperimental import com.google.devtools.ksp.containingFile +import com.google.devtools.ksp.getClassDeclarationByName import com.google.devtools.ksp.processing.* import com.google.devtools.ksp.symbol.* import java.io.OutputStream @@ -49,6 +50,14 @@ class TestProcessor : SymbolProcessor { emit("TestProcessor: processing ${file.fileName}", "") file.accept(visitor, "") } + + resolver.getClassDeclarationByName("com.example.AClass")?.let { aClass -> + aClass.declarations.single { it.simpleName.asString() == "internalFun" }.let { internalFun -> + val internalName = resolver.getJvmName(internalFun as KSFunctionDeclaration) + val moduleName = resolver.getModuleName().asString() + logger.warn("[$moduleName] Mangled name for internalFun: $internalName") + } + } invoked = true return emptyList() } 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 index 180dbe6a2a..446e78dfda 100644 --- 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 @@ -14,4 +14,6 @@ class AClass(private val a: Int, val b: String, val c: Double, val d: HELLO) { class innerClass val generic = innerClass() + + internal fun internalFun(): Int = 0 } From 1df379cbfed8d36b49a7563dcb0b7baeeea04255 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Thu, 7 Nov 2024 12:53:27 -0800 Subject: [PATCH 17/25] CI: Update release branch to 1.0.28 --- .github/workflows/auto-merge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 37c117f8b0..1f9b6b49e8 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 - ref: 1.0.27-release + ref: 1.0.28-release - name: merge commits from main to release branch run: | From 947bf7027b5f1d1d6ba233aa4d868c9d9d701e19 Mon Sep 17 00:00:00 2001 From: GeorgCantor Date: Sat, 9 Nov 2024 16:32:15 +0300 Subject: [PATCH 18/25] Update KSNameImpl.kt Using lastIndexOf --- .../com/google/devtools/ksp/common/impl/KSNameImpl.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common-util/src/main/kotlin/com/google/devtools/ksp/common/impl/KSNameImpl.kt b/common-util/src/main/kotlin/com/google/devtools/ksp/common/impl/KSNameImpl.kt index 0cbccba79d..c3475aa2a9 100644 --- a/common-util/src/main/kotlin/com/google/devtools/ksp/common/impl/KSNameImpl.kt +++ b/common-util/src/main/kotlin/com/google/devtools/ksp/common/impl/KSNameImpl.kt @@ -13,10 +13,12 @@ class KSNameImpl private constructor(val name: String) : KSName { } override fun getQualifier(): String { - return name.split(".").dropLast(1).joinToString(".") + val lastIndex = name.lastIndexOf('.') + return if (lastIndex != -1) name.substring(0, lastIndex) else "" } override fun getShortName(): String { - return name.split(".").last() + val lastIndex = name.lastIndexOf('.') + return if (lastIndex != -1) name.substring(lastIndex + 1) else name } } From 7fa10d94367e81ed7e4b0ec9d9237d444226e47d Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Mon, 11 Nov 2024 11:25:09 -0800 Subject: [PATCH 19/25] UPDATE_AA_VERSION: 2.1.20-dev-3305 --- gradle.properties | 2 +- .../kotlin/com/google/devtools/ksp/test/AbstractKSPAATest.kt | 3 +-- .../kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index 80f059e7f6..843eda7c20 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ junit5Version=5.8.2 junitPlatformVersion=1.8.2 googleTruthVersion=1.1 -aaKotlinBaseVersion=2.1.20-dev-2637 +aaKotlinBaseVersion=2.1.20-dev-3305 aaIntellijVersion=233.13135.128 aaGuavaVersion=33.2.0-jre aaAsmVersion=9.0 diff --git a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPAATest.kt b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPAATest.kt index 462640483c..bf702b5611 100644 --- a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPAATest.kt +++ b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPAATest.kt @@ -31,7 +31,6 @@ import org.jetbrains.kotlin.test.compileJavaFiles import org.jetbrains.kotlin.test.kotlinPathsForDistDirectoryForTests import org.jetbrains.kotlin.test.model.FrontendKinds import org.jetbrains.kotlin.test.model.TestModule -import org.jetbrains.kotlin.test.services.JUnit5Assertions import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.compilerConfigurationProvider import org.jetbrains.kotlin.test.services.isKtFile @@ -101,7 +100,7 @@ abstract class AbstractKSPAATest : AbstractKSPTest(FrontendKinds.FIR) { "-d", module.outDir.path ) if (javaFiles.isNotEmpty()) { - compileJavaFiles(javaFiles, options, assertions = JUnit5Assertions) + compileJavaFiles(javaFiles, options) } } diff --git a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt index 826f5eff8c..78a3fff699 100644 --- a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt +++ b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt @@ -185,7 +185,7 @@ abstract class AbstractKSPTest(frontend: FrontendKind<*>) : DisposableTest() { "-classpath", classpath, "-d", module.outDir.path ) - compileJavaFiles(javaFiles, options, assertions = JUnit5Assertions) + compileJavaFiles(javaFiles, options) } fun runTest(@TestDataFile path: String) { From dcf746df691bfb25bc512040bd2759ac2e92bd5f Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Mon, 11 Nov 2024 11:25:31 -0800 Subject: [PATCH 20/25] UPDATE_KOTLIN_VERSION: 2.1.20-dev-3305 --- .../test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler-plugin/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt b/compiler-plugin/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt index 826f5eff8c..78a3fff699 100644 --- a/compiler-plugin/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt +++ b/compiler-plugin/src/test/kotlin/com/google/devtools/ksp/test/AbstractKSPTest.kt @@ -185,7 +185,7 @@ abstract class AbstractKSPTest(frontend: FrontendKind<*>) : DisposableTest() { "-classpath", classpath, "-d", module.outDir.path ) - compileJavaFiles(javaFiles, options, assertions = JUnit5Assertions) + compileJavaFiles(javaFiles, options) } fun runTest(@TestDataFile path: String) { diff --git a/gradle.properties b/gradle.properties index 843eda7c20..2e2936e9ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Copied from kotlinc org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx4096m -Dfile.encoding=UTF-8 -kotlinBaseVersion=2.1.20-dev-2637 +kotlinBaseVersion=2.1.20-dev-3305 agpBaseVersion=7.3.1 agpTestVersion=8.7.1 intellijVersion=233.13135.128 From c00cad1ad0685029a5d08c134e750852c8eab5b0 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Mon, 11 Nov 2024 15:41:14 -0800 Subject: [PATCH 21/25] Enable both KSP1 and KSP2 in gradle plugin tests --- .../ksp/gradle/GradleCompilationTest.kt | 32 ++++++++++++++++--- .../ProcessorClasspathConfigurationsTest.kt | 14 ++++++-- .../ksp/gradle/SourceSetConfigurationsTest.kt | 14 ++++++-- .../gradle/testing/KspIntegrationTestRule.kt | 7 ++-- .../ksp/gradle/testing/TestProject.kt | 4 ++- 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt index d0fe401401..22f17be1c8 100644 --- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt +++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt @@ -32,15 +32,25 @@ import org.junit.Assume import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class GradleCompilationTest(val useKSP2: Boolean) { + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "KSP2={0}") + fun params() = listOf(arrayOf(true), arrayOf(false)) + } -class GradleCompilationTest { @Rule @JvmField val tmpDir = TemporaryFolder() @Rule @JvmField - val testRule = KspIntegrationTestRule(tmpDir) + val testRule = KspIntegrationTestRule(tmpDir, useKSP2) @Test fun errorMessageFailsCompilation() { @@ -239,8 +249,8 @@ class GradleCompilationTest { ) testRule.appModule.dependencies.addAll( listOf( - artifact(configuration = "ksp", "androidx.room:room-compiler:2.4.2"), - artifact(configuration = "implementation", "androidx.room:room-runtime:2.4.2") + artifact(configuration = "ksp", "androidx.room:room-compiler:2.6.1"), + artifact(configuration = "implementation", "androidx.room:room-runtime:2.6.1") ) ) testRule.appModule.buildFileAdditions.add( @@ -267,6 +277,13 @@ class GradleCompilationTest { } } } + tasks.withType().configureEach { + doFirst { + kspConfig.processorOptions.get().forEach { (key, value) -> + println("apoption=${'$'}key=${'$'}value") + } + } + } """.trimIndent() ) @@ -312,6 +329,7 @@ class GradleCompilationTest { @Test fun commandLineArgumentIsIncludedInApoptionsWhenAddedInKspTask() { Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + Assume.assumeFalse(useKSP2) testRule.setupAppAsAndroidApp() testRule.appModule.dependencies.addAll( listOf( @@ -366,6 +384,11 @@ class GradleCompilationTest { println("HAS LIBRARY: ${'$'}{it.path}") } } + tasks.withType().configureEach { + kspConfig.libraries.files.forEach { + println("HAS LIBRARY: ${'$'}{it.path}") + } + } } """.trimIndent() ) @@ -389,6 +412,7 @@ class GradleCompilationTest { @Test fun changingKsp2AtRuntime() { + Assume.assumeFalse(useKSP2) testRule.setupAppAsJvmApp() testRule.appModule.buildFileAdditions.add( """ diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/ProcessorClasspathConfigurationsTest.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/ProcessorClasspathConfigurationsTest.kt index 713eaf4c09..01db0b609e 100644 --- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/ProcessorClasspathConfigurationsTest.kt +++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/ProcessorClasspathConfigurationsTest.kt @@ -21,15 +21,25 @@ import com.google.devtools.ksp.gradle.testing.KspIntegrationTestRule import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class ProcessorClasspathConfigurationsTest(val useKSP2: Boolean) { + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "KSP2={0}") + fun params() = listOf(arrayOf(true), arrayOf(false)) + } -class ProcessorClasspathConfigurationsTest { @Rule @JvmField val tmpDir = TemporaryFolder() @Rule @JvmField - val testRule = KspIntegrationTestRule(tmpDir) + val testRule = KspIntegrationTestRule(tmpDir, useKSP2) private val kspConfigs by lazy { """configurations.matching { it.name.startsWith("ksp") && !it.name.endsWith("ProcessorClasspath") }""" diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/SourceSetConfigurationsTest.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/SourceSetConfigurationsTest.kt index e0506ae756..7cb1320029 100644 --- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/SourceSetConfigurationsTest.kt +++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/SourceSetConfigurationsTest.kt @@ -30,16 +30,26 @@ import com.google.devtools.ksp.symbol.KSClassDeclaration import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.junit.runners.Parameterized import java.io.File -class SourceSetConfigurationsTest { +@RunWith(Parameterized::class) +class SourceSetConfigurationsTest(val useKSP2: Boolean) { + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "KSP2={0}") + fun params() = listOf(arrayOf(true), arrayOf(false)) + } + @Rule @JvmField val tmpDir = TemporaryFolder() @Rule @JvmField - val testRule = KspIntegrationTestRule(tmpDir) + val testRule = KspIntegrationTestRule(tmpDir, useKSP2) @Test fun configurationsForJvmApp() { diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/KspIntegrationTestRule.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/KspIntegrationTestRule.kt index baf69f5d83..1186b83884 100644 --- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/KspIntegrationTestRule.kt +++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/KspIntegrationTestRule.kt @@ -29,7 +29,8 @@ import kotlin.reflect.KClass * Test must call [setupAppAsAndroidApp] or [setupAppAsJvmApp] before using the [runner]. */ class KspIntegrationTestRule( - private val tmpFolder: TemporaryFolder + private val tmpFolder: TemporaryFolder, + private val useKSP2: Boolean ) : TestWatcher() { /** * Initialized when the test starts. @@ -144,7 +145,7 @@ class KspIntegrationTestRule( """ android { namespace = "com.example.kspandroidtestapp" - compileSdk = 31 + compileSdk = 34 defaultConfig { minSdk = 24 } @@ -168,6 +169,6 @@ class KspIntegrationTestRule( override fun starting(description: Description) { super.starting(description) - testProject = TestProject(tmpFolder.newFolder(), testConfig) + testProject = TestProject(tmpFolder.newFolder(), testConfig, useKSP2) } } diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/TestProject.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/TestProject.kt index 0084410eaf..c82f951ae6 100644 --- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/TestProject.kt +++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/TestProject.kt @@ -23,7 +23,8 @@ import java.io.File */ class TestProject( val rootDir: File, - val testConfig: TestConfig + val testConfig: TestConfig, + val useKSP2: Boolean, ) { val processorModule = TestModule( rootDir.resolve("processor") @@ -59,6 +60,7 @@ class TestProject( val contents = """ kotlin.jvm.target.validation.mode=warning + ksp.useKSP2=$useKSP2 """.trimIndent() rootDir.resolve("gradle.properties").appendText(contents) } From 755cba4d0e522414f8f041e67430193313f4cf5e Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Mon, 11 Nov 2024 17:24:00 -0800 Subject: [PATCH 22/25] Introduce KspAATask.commandLineArgumentProviders so that each KSP task can configured differently. --- .../com/google/devtools/ksp/gradle/KspAATask.kt | 15 ++++++++++++--- .../devtools/ksp/gradle/GradleCompilationTest.kt | 12 +++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt index 2cec471e17..1b68e92cce 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt @@ -24,11 +24,13 @@ import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.logging.LogLevel +import org.gradle.api.provider.ListProperty import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.* import org.gradle.api.tasks.Optional +import org.gradle.process.CommandLineArgumentProvider import org.gradle.work.ChangeType import org.gradle.work.Incremental import org.gradle.work.InputChanges @@ -63,6 +65,9 @@ abstract class KspAATask @Inject constructor( @get:Nested abstract val kspConfig: KspGradleConfig + @get:Nested + abstract val commandLineArgumentProviders: ListProperty + @TaskAction fun execute(inputChanges: InputChanges) { // FIXME: Create a class loader with clean classpath instead of shadowing existing ones. It'll require either: @@ -220,8 +225,9 @@ abstract class KspAATask @Inject constructor( ) ) cfg.processorOptions.putAll(kspExtension.apOptions) - cfg.processorOptions.putAll( - kspExtension.commandLineArgumentProviders.map { providers -> + + fun ListProperty.mapArgProviders() = + map { providers -> buildMap { for (provider in providers) { provider.asArguments().forEach { argument -> @@ -234,7 +240,10 @@ abstract class KspAATask @Inject constructor( } } } - ) + + cfg.processorOptions.putAll(kspExtension.commandLineArgumentProviders.mapArgProviders()) + cfg.processorOptions.putAll(kspAATask.commandLineArgumentProviders.mapArgProviders()) + val logLevel = LogLevel.entries.first { project.logger.isEnabled(it) } diff --git a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt index 22f17be1c8..8a766215f3 100644 --- a/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt +++ b/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/GradleCompilationTest.kt @@ -329,7 +329,6 @@ class GradleCompilationTest(val useKSP2: Boolean) { @Test fun commandLineArgumentIsIncludedInApoptionsWhenAddedInKspTask() { Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) - Assume.assumeFalse(useKSP2) testRule.setupAppAsAndroidApp() testRule.appModule.dependencies.addAll( listOf( @@ -361,6 +360,17 @@ class GradleCompilationTest(val useKSP2: Boolean) { println("commandLine=${'$'}{commandLine.asArguments()}") } } + tasks.withType().configureEach { + val destination = project.layout.projectDirectory.dir("schemas-${'$'}{this.name}") + commandLineArgumentProviders.add(Provider(destination.asFile)) + + kspConfig.processorOptions.get().forEach { (key, value) -> + println("apoption=${'$'}key=${'$'}value") + } + commandLineArgumentProviders.get().forEach { commandLine -> + println("commandLine=${'$'}{commandLine.asArguments()}") + } + } } """.trimIndent() ) From e0154eac29cb883011a708c49dc6673902299b19 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Wed, 13 Nov 2024 13:28:45 -0800 Subject: [PATCH 23/25] KSP2 command line tool: exit with exit code --- .../main/kotlin/com/google/devtools/ksp/cmdline/KSPJvmMain.kt | 4 +++- .../com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/cmdline/KSPJvmMain.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/cmdline/KSPJvmMain.kt index a3672f79bd..24a131aad2 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/cmdline/KSPJvmMain.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/cmdline/KSPJvmMain.kt @@ -9,6 +9,7 @@ import com.google.devtools.ksp.processing.kspJvmArgParserHelp import java.io.File import java.net.URLClassLoader import java.util.ServiceLoader +import kotlin.system.exitProcess class KSPJvmMain { companion object { @@ -45,5 +46,6 @@ internal fun runWithArgs(args: Array, parse: (Array) -> Pair - KotlinSymbolProcessing(config, processorProviders, logger).execute() + val exitCode = KotlinSymbolProcessing(config, processorProviders, logger).execute() + exitProcess(exitCode.code) } diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt index b226345d07..083d4fcda3 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/KotlinSymbolProcessing.kt @@ -126,7 +126,7 @@ class KotlinSymbolProcessing( val logger: KSPLogger ) { enum class ExitCode( - @Suppress("UNUSED_PARAMETER") code: Int + val code: Int ) { OK(0), From 3a0da1ac8e2b2f358cadb932f94283e14c09998f Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Wed, 13 Nov 2024 16:41:27 -0800 Subject: [PATCH 24/25] IntelliJ: set application pool threads to daemon so that they don't block the application from exiting. --- .../AppScheduledExecutorService.java | 245 ++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 kotlin-analysis-api/src/main/java/com/intellij/util/concurrency/AppScheduledExecutorService.java diff --git a/kotlin-analysis-api/src/main/java/com/intellij/util/concurrency/AppScheduledExecutorService.java b/kotlin-analysis-api/src/main/java/com/intellij/util/concurrency/AppScheduledExecutorService.java new file mode 100644 index 0000000000..31e83f3f9d --- /dev/null +++ b/kotlin-analysis-api/src/main/java/com/intellij/util/concurrency/AppScheduledExecutorService.java @@ -0,0 +1,245 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.util.concurrency; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.util.LowMemoryWatcherManager; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.TestOnly; + +import java.util.List; +import java.util.concurrent.*; +import java.util.function.BiConsumer; + +import static com.intellij.util.concurrency.AppExecutorUtil.propagateContextOrCancellation; + +/** + * A ThreadPoolExecutor which also implements {@link ScheduledExecutorService} by awaiting scheduled tasks in a separate thread + * and then executing them in the owned ThreadPoolExecutor. + * Unlike the existing {@link ScheduledThreadPoolExecutor}, this pool is unbounded. + * @see AppExecutorUtil#getAppScheduledExecutorService() + */ +@ApiStatus.Internal +public final class AppScheduledExecutorService extends SchedulingWrapper { + static final String POOLED_THREAD_PREFIX = "ApplicationImpl pooled thread "; + private final @NotNull String myName; + private final LowMemoryWatcherManager myLowMemoryWatcherManager; + + private static final class Holder { + private static final AppScheduledExecutorService INSTANCE = new AppScheduledExecutorService("Global instance", 1, TimeUnit.MINUTES); + } + + static @NotNull ScheduledExecutorService getInstance() { + return Holder.INSTANCE; + } + + AppScheduledExecutorService(@NotNull String name, long keepAliveTime, @NotNull TimeUnit unit) { + super(new BackendThreadPoolExecutor(new MyThreadFactory(), keepAliveTime, unit), new AppDelayQueue()); + myName = name; + myLowMemoryWatcherManager = new LowMemoryWatcherManager(this); + } + + private MyThreadFactory getCountingThreadFactory() { + return (MyThreadFactory)((BackendThreadPoolExecutor)backendExecutorService).getThreadFactory(); + } + + private static final class MyThreadFactory extends CountingThreadFactory { + private BiConsumer newThreadListener; + private final ThreadFactory myThreadFactory = Executors.privilegedThreadFactory(); + + @Override + public @NotNull Thread newThread(final @NotNull Runnable r) { + Thread thread = myThreadFactory.newThread(r); + thread.setDaemon(true); + thread.setName(POOLED_THREAD_PREFIX + counter.incrementAndGet()); + + thread.setPriority(Thread.NORM_PRIORITY - 1); + + BiConsumer listener = newThreadListener; + if (listener != null) { + listener.accept(thread, r); + } + return thread; + } + + void setNewThreadListener(@NotNull BiConsumer threadListener) { + if (newThreadListener != null) throw new IllegalStateException("Listener was already set: " + newThreadListener); + newThreadListener = threadListener; + } + } + + public void setNewThreadListener(@NotNull BiConsumer threadListener) { + getCountingThreadFactory().setNewThreadListener(threadListener); + } + + @Override + public @NotNull List shutdownNow() { + return notAllowedMethodCall(); + } + + @Override + public void shutdown() { + notAllowedMethodCall(); + } + + static List notAllowedMethodCall() { + throw new IncorrectOperationException("You must not call this method on the global app pool"); + } + + @Override + void onDelayQueuePurgedOnShutdown() { + ((BackendThreadPoolExecutor)backendExecutorService).superShutdown(); + } + + void shutdownAppScheduledExecutorService() { + // LowMemoryWatcher starts background threads so stop it now to avoid RejectedExecutionException + myLowMemoryWatcherManager.shutdown(); + doShutdown(); + delayQueue.shutdown(new SchedulingWrapper.MyScheduledFutureTask(()->{}, null, 0) { + @Override + boolean executeMeInBackendExecutor() { + set(null); + return false; + } + }); + } + + @Override + public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException { + // let this task to bubble through the AppDelayQueue global queue to make sure there are no in-flight tasks leaked on stack of com.intellij.util.concurrency.AppDelayQueue.transferrerThread + long deadline = System.nanoTime() + unit.toNanos(timeout); + if (!delayQueue.awaitTermination(deadline - System.nanoTime(), TimeUnit.NANOSECONDS)) { + return false; + } + + return super.awaitTermination(deadline - System.nanoTime(), TimeUnit.NANOSECONDS); + } + + @TestOnly + public @NotNull String statistics() { + return myName + " threads created counter = " + getCountingThreadFactory().counter; + } + + @TestOnly + public String dumpQueue() { + return delayQueue.toString(); + } + + public int getBackendPoolExecutorSize() { + return ((BackendThreadPoolExecutor)backendExecutorService).getPoolSize(); + } + + @TestOnly + void setBackendPoolCorePoolSize(int size) { + ((BackendThreadPoolExecutor)backendExecutorService).superSetCorePoolSize(size); + } + + static final class BackendThreadPoolExecutor extends ThreadPoolExecutor { + + BackendThreadPoolExecutor(@NotNull ThreadFactory factory, + long keepAliveTime, + @NotNull TimeUnit unit) { + super(1, Integer.MAX_VALUE, keepAliveTime, unit, new SynchronousQueue<>(), factory); + } + + @Override + public void execute(@NotNull Runnable command) { + super.execute(capturePropagationAndCancellationContext(command)); + } + + @Override + protected RunnableFuture newTaskFor(Runnable runnable, T value) { + return newTaskFor(Executors.callable(runnable, value)); + } + + @Override + protected RunnableFuture newTaskFor(Callable callable) { + return capturePropagationAndCancellationContext(callable); + } + + @Override + protected void afterExecute(Runnable r, Throwable t) { + if (t != null && !(t instanceof ProcessCanceledException)) { + Logger.getInstance(SchedulingWrapper.class).error("Worker exited due to exception", t); + } + } + + private void superShutdown() { + super.shutdown(); + } + + private @NotNull List superShutdownNow() { + return super.shutdownNow(); + } + + // stub out sensitive methods + @Override + public void shutdown() { + notAllowedMethodCall(); + } + + @Override + public @NotNull List shutdownNow() { + return notAllowedMethodCall(); + } + + @Override + public void setCorePoolSize(int corePoolSize) { + notAllowedMethodCall(); + } + + private void superSetCorePoolSize(int corePoolSize) { + super.setCorePoolSize(corePoolSize); + } + + @Override + public void allowCoreThreadTimeOut(boolean value) { + notAllowedMethodCall(); + } + + @Override + public void setMaximumPoolSize(int maximumPoolSize) { + notAllowedMethodCall(); + } + + @Override + public void setKeepAliveTime(long time, TimeUnit unit) { + notAllowedMethodCall(); + } + + void superSetKeepAliveTime(long time, TimeUnit unit) { + super.setKeepAliveTime(time, unit); + } + + @Override + public void setThreadFactory(@NotNull ThreadFactory threadFactory) { + notAllowedMethodCall(); + } + } + + public static @NotNull Thread getPeriodicTasksThread() { + return Holder.INSTANCE.delayQueue.getThread(); + } + + @TestOnly + void waitForLowMemoryWatcherManagerInit(int timeout, @NotNull TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + myLowMemoryWatcherManager.waitForInitComplete(timeout, unit); + } + + public static @NotNull Runnable capturePropagationAndCancellationContext(@NotNull Runnable command) { + if (!propagateContextOrCancellation()) { + return command; + } + return Propagation.capturePropagationAndCancellationContext(command); + } + + public static @NotNull FutureTask capturePropagationAndCancellationContext(@NotNull Callable callable) { + if (!propagateContextOrCancellation()) { + return new FutureTask<>(callable); + } + return Propagation.capturePropagationAndCancellationContext(callable); + } +} From ece7ae3107a429e0567e70af378c362a13e86ee3 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Fri, 15 Nov 2024 14:15:11 -0800 Subject: [PATCH 25/25] CI: Update release branch to 1.0.29 --- .github/workflows/auto-merge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 1f9b6b49e8..ad1ca48467 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 - ref: 1.0.28-release + ref: 1.0.29-release - name: merge commits from main to release branch run: |