From e94c94580b3f403ea339d015400ac89eaf2e9563 Mon Sep 17 00:00:00 2001 From: James Barr Date: Fri, 5 Apr 2024 00:08:25 -0700 Subject: [PATCH 1/7] Enable K2 jdeps tests --- .../kotlin/builder/tasks/jvm/KotlinBuilderJvmJdepsTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmJdepsTest.kt b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmJdepsTest.kt index 035a468d2..aded5fac3 100644 --- a/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmJdepsTest.kt +++ b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmJdepsTest.kt @@ -36,8 +36,7 @@ class KotlinBuilderJvmJdepsTest(private val enableK2Compiler: Boolean) { @Parameterized.Parameters(name = "enableK2Compiler={0}") fun data(): Collection> { return listOf( - // TODO: Enable k2 for tests -// arrayOf(true), + arrayOf(true), arrayOf(false), ) } From ed115c7d46469c7523d029ae3c8c6ebeecd988ee Mon Sep 17 00:00:00 2001 From: James Barr Date: Fri, 26 Apr 2024 11:03:05 -0700 Subject: [PATCH 2/7] Update to Kotlin & KSP 2.0.0 --- kotlin/internal/toolchains.bzl | 4 ++-- .../starlark/core/repositories/versions.bzl | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/kotlin/internal/toolchains.bzl b/kotlin/internal/toolchains.bzl index ce93cea89..20c67d47c 100644 --- a/kotlin/internal/toolchains.bzl +++ b/kotlin/internal/toolchains.bzl @@ -122,7 +122,7 @@ _kt_toolchain = rule( ), "language_version": attr.string( doc = "this is the -language_version flag [see](https://kotlinlang.org/docs/reference/compatibility.html)", - default = "1.9", + default = "2.0", values = [ "1.1", "1.2", @@ -138,7 +138,7 @@ _kt_toolchain = rule( ), "api_version": attr.string( doc = "this is the -api_version flag [see](https://kotlinlang.org/docs/reference/compatibility.html).", - default = "1.9", + default = "2.0", values = [ "1.1", "1.2", diff --git a/src/main/starlark/core/repositories/versions.bzl b/src/main/starlark/core/repositories/versions.bzl index 88c8b0864..2173c6f29 100644 --- a/src/main/starlark/core/repositories/versions.bzl +++ b/src/main/starlark/core/repositories/versions.bzl @@ -20,6 +20,8 @@ def _use_repository(name, version, rule, **kwargs): maybe(rule, name = name, **http_archive_arguments) versions = struct( + RULES_NODEJS_VERSION = "5.5.3", + RULES_NODEJS_SHA = "f10a3a12894fc3c9bf578ee5a5691769f6805c4be84359681a785a0c12e8d2b6", BAZEL_TOOLCHAINS_VERSION = "4.1.0", BAZEL_TOOLCHAINS_SHA = "179ec02f809e86abf56356d8898c8bd74069f1bd7c56044050c2cd3d79d0e024", # IMPORTANT! rules_kotlin does not use the bazel_skylib unittest in production @@ -29,8 +31,8 @@ versions = struct( # 1. Download archive # 2. Download dependencies and Configure rules # --> 3. Configure dependencies <-- - SKYLIB_VERSION = "1.7.1", - SKYLIB_SHA = "bc283cdfcd526a52c3201279cda4bc298652efa898b10b4db0837dc51652756f", + SKYLIB_VERSION = "1.4.2", + SKYLIB_SHA = "66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa", PROTOBUF_VERSION = "3.11.3", PROTOBUF_SHA = "cf754718b0aa945b00550ed7962ddc167167bd922b842199eeb6505e6f344852", RULES_JVM_EXTERNAL_TAG = "5.3", @@ -52,25 +54,25 @@ versions = struct( ], ), PINTEREST_KTLINT = version( - version = "1.3.0", + version = "0.49.1", url_templates = [ "https://github.com/pinterest/ktlint/releases/download/{version}/ktlint", ], - sha256 = "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985", + sha256 = "2b3f6f674a944d25bb8d283c3539947bbe86074793012909a55de4b771f74bcc", ), KOTLIN_CURRENT_COMPILER_RELEASE = version( - version = "1.9.23", + version = "2.0.0", url_templates = [ "https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-compiler-{version}.zip", ], - sha256 = "93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88", + sha256 = "ef578730976154fd2c5968d75af8c2703b3de84a78dffe913f670326e149da3b", ), KSP_CURRENT_COMPILER_PLUGIN_RELEASE = version( - version = "1.9.23-1.0.20", + version = "2.0.0-1.0.21", url_templates = [ "https://github.com/google/ksp/releases/download/{version}/artifacts.zip", ], - sha256 = "ee0618755913ef7fd6511288a232e8fad24838b9af6ea73972a76e81053c8c2d", + sha256 = "84100aed5b63effa992ce6574b3ba47d2dbb78529752daa4c181e203117ba7de", ), ANDROID = struct( VERSION = "0.1.1", From 493d67f676441718ba0eb8e2db7a2dec61a7d252 Mon Sep 17 00:00:00 2001 From: James Barr Date: Fri, 26 Apr 2024 12:35:11 -0700 Subject: [PATCH 3/7] Implement jdeps using K2 APIs --- .../io/bazel/kotlin/plugin/jdeps/BUILD.bazel | 7 +- .../jdeps/JdepsGenComponentRegistrar.kt | 25 +++- .../plugin/jdeps/k2/ClassUsageRecorder.kt | 132 ++++++++++++++++++ .../plugin/jdeps/k2/JdepsFirExtensions.kt | 54 +++++++ .../plugin/jdeps/k2/JdepsGenExtension2.kt | 17 +++ .../kotlin/plugin/jdeps/k2/JdepsK2Utils.kt | 80 +++++++++++ .../declaration/BasicDeclarationChecker.kt | 21 +++ .../k2/checker/declaration/CallableChecker.kt | 38 +++++ .../checker/declaration/ClassLikeChecker.kt | 24 ++++ .../k2/checker/declaration/FileChecker.kt | 95 +++++++++++++ .../k2/checker/declaration/FunctionChecker.kt | 25 ++++ .../expression/QualifiedAccessChecker.kt | 60 ++++++++ .../expression/ResolvedQualifierChecker.kt | 19 +++ 13 files changed, 595 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/ClassUsageRecorder.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsFirExtensions.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsGenExtension2.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsK2Utils.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/BasicDeclarationChecker.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/CallableChecker.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/ClassLikeChecker.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FileChecker.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FunctionChecker.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/QualifiedAccessChecker.kt create mode 100644 src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/ResolvedQualifierChecker.kt diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/BUILD.bazel b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/BUILD.bazel index 131fa544d..5cc2b476e 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/BUILD.bazel +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/BUILD.bazel @@ -19,7 +19,12 @@ load("//src/main/kotlin:bootstrap.bzl", "kt_bootstrap_library") # The compiler binary, this is co-located in the kotlin compiler classloader. kt_bootstrap_library( name = "jdeps-gen-lib", - srcs = glob(["*.kt"]), + srcs = glob([ + "*.kt", + "k2/*.kt", + "k2/checker/declaration/*.kt", + "k2/checker/expression/*.kt", + ]), visibility = ["//src:__subpackages__"], deps = [ "//kotlin/compiler:kotlin-compiler", diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt index be5420a9a..163da8860 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt @@ -1,20 +1,43 @@ package io.bazel.kotlin.plugin.jdeps +import io.bazel.kotlin.plugin.jdeps.k2.ClassUsageRecorder +import io.bazel.kotlin.plugin.jdeps.k2.JdepsFirExtensions +import io.bazel.kotlin.plugin.jdeps.k2.JdepsGenExtension2 +import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor +import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension +import java.nio.file.Paths @OptIn(org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi::class) class JdepsGenComponentRegistrar : CompilerPluginRegistrar() { override val supportsK2: Boolean - get() = false + get() = true override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { + when (configuration.languageVersionSettings.languageVersion.usesK2) { + true -> registerForK2(configuration) + false -> registerForK1(configuration) + } + } + + private fun ExtensionStorage.registerForK1(configuration: CompilerConfiguration) { // Capture all types referenced by the compiler for this module and look up the jar from which // they were loaded from val extension = JdepsGenExtension(configuration) AnalysisHandlerExtension.registerExtension(extension) StorageComponentContainerContributor.registerExtension(extension) } + + private fun ExtensionStorage.registerForK2(configuration: CompilerConfiguration) { + val projectRoot = Paths.get("").toAbsolutePath().toString() + "/" + ClassUsageRecorder.init(projectRoot) + JdepsGenExtension2(configuration).run { + FirExtensionRegistrarAdapter.registerExtension(JdepsFirExtensions()) + ClassFileFactoryFinalizerExtension.registerExtension(this) + } + } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/ClassUsageRecorder.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/ClassUsageRecorder.kt new file mode 100644 index 000000000..b42196c19 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/ClassUsageRecorder.kt @@ -0,0 +1,132 @@ +package io.bazel.kotlin.plugin.jdeps.k2 + +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.declarations.utils.sourceElement +import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.forEachType +import org.jetbrains.kotlin.name.ClassId +import java.nio.file.Paths +import java.util.SortedSet + +private const val JAR_FILE_SEPARATOR = "!/" +private const val ANONYMOUS = "" + +internal object ClassUsageRecorder { + private val explicitClassesCanonicalPaths = mutableSetOf() + private val implicitClassesCanonicalPaths = mutableSetOf() + + private val results by lazy { sortedMapOf>() } + private val seen = mutableSetOf() + private val javaHome: String by lazy { System.getenv()["JAVA_HOME"] ?: "" } + private var rootPath: String = Paths.get("").toAbsolutePath().toString() + "/" + + fun init(rootPath: String) { + this.rootPath = rootPath + results.clear() + explicitClassesCanonicalPaths.clear() + implicitClassesCanonicalPaths.clear() + seen.clear() + } + + fun getExplicitClassesCanonicalPaths(): Set = explicitClassesCanonicalPaths + + fun getImplicitClassesCanonicalPaths(): Set = implicitClassesCanonicalPaths + + internal fun recordTypeRef( + typeRef: FirTypeRef, + context: CheckerContext, + isExplicit: Boolean = true, + collectTypeArguments: Boolean = true, + visited: MutableSet> = mutableSetOf(), + ) { + recordConeType(typeRef.coneType, context, isExplicit, collectTypeArguments, visited) + } + + internal fun recordConeType( + coneKotlinType: ConeKotlinType, + context: CheckerContext, + isExplicit: Boolean = true, + collectTypeArguments: Boolean = true, + visited: MutableSet> = mutableSetOf(), + ) { + if (collectTypeArguments) { + coneKotlinType.forEachType { coneType -> + val classId = coneType.classId ?: return@forEachType + if (ANONYMOUS in classId.toString()) return@forEachType + context.session.symbolProvider + .getClassLikeSymbolByClassId(classId) + ?.recordClass(context, isExplicit, collectTypeArguments, visited) + } + } else { + coneKotlinType.classId?.let { classId -> + context.session.symbolProvider.getClassLikeSymbolByClassId(classId) + ?.recordClass(context, isExplicit, collectTypeArguments, visited) + } + } + } + + internal fun recordClass( + firClass: FirClassLikeSymbol<*>, + context: CheckerContext, + isExplicit: Boolean, + collectTypeArguments: Boolean = true, + visited: MutableSet> = mutableSetOf(), + ) { + val classIdAndIsExplicit = firClass.classId to isExplicit + if (classIdAndIsExplicit in visited) { + return + } else { + visited.add(classIdAndIsExplicit) + } + + firClass.sourceElement?.binaryClass()?.let { addFile(it, isExplicit) } + + if (firClass is FirClassSymbol<*>) { + firClass.resolvedSuperTypeRefs.forEach { + recordTypeRef(it, context, false, collectTypeArguments, visited) + } + if (collectTypeArguments) { + firClass.typeParameterSymbols.flatMap { it.resolvedBounds } + .forEach { recordTypeRef(it, context, isExplicit, collectTypeArguments, visited) } + } + } + } + + internal fun recordClass( + binaryClass: String, + isExplicit: Boolean = true, + ) { + addFile(binaryClass, isExplicit) + } + + private fun addFile( + path: String, + isExplicit: Boolean, + ) { + if (isExplicit) { + explicitClassesCanonicalPaths.add(path) + } else { + implicitClassesCanonicalPaths.add(path) + } + + if (path.contains(JAR_FILE_SEPARATOR) && !path.contains(javaHome)) { + val (jarPath, classPath) = path.split(JAR_FILE_SEPARATOR) + // Convert jar files in current directory to relative paths. Remaining absolute are outside + // of project and should be ignored + val relativizedJarPath = Paths.get(jarPath.replace(rootPath, "")) + if (!relativizedJarPath.isAbsolute) { + val occurrences = + results.computeIfAbsent(relativizedJarPath.toString()) { sortedSetOf() } + if (!isJvmClass(classPath)) { + occurrences.add(classPath) + } + } + } + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsFirExtensions.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsFirExtensions.kt new file mode 100644 index 000000000..5d9f0a11f --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsFirExtensions.kt @@ -0,0 +1,54 @@ +package io.bazel.kotlin.plugin.jdeps.k2 + +import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.BasicDeclarationChecker +import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.CallableChecker +import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.ClassLikeChecker +import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.FileChecker +import io.bazel.kotlin.plugin.jdeps.k2.checker.declaration.FunctionChecker +import io.bazel.kotlin.plugin.jdeps.k2.checker.expression.QualifiedAccessChecker +import io.bazel.kotlin.plugin.jdeps.k2.checker.expression.ResolvedQualifierChecker +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirCallableDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassLikeChecker +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker +import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirResolvedQualifierChecker +import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension +import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar + +internal class JdepsFirExtensions : FirExtensionRegistrar() { + override fun ExtensionRegistrarContext.configurePlugin() { + +::JdepsFirCheckersExtension + } +} + +internal class JdepsFirCheckersExtension(session: FirSession) : + FirAdditionalCheckersExtension(session) { + override val declarationCheckers: DeclarationCheckers = + object : DeclarationCheckers() { + override val basicDeclarationCheckers: Set = + setOf(BasicDeclarationChecker) + + override val fileCheckers: Set = setOf(FileChecker) + + override val classLikeCheckers: Set = setOf(ClassLikeChecker) + + override val functionCheckers: Set = setOf(FunctionChecker) + + override val callableDeclarationCheckers: Set = + setOf(CallableChecker) + } + + override val expressionCheckers: ExpressionCheckers = + object : ExpressionCheckers() { + override val qualifiedAccessExpressionCheckers: Set = + setOf(QualifiedAccessChecker) + + override val resolvedQualifierCheckers: Set = + setOf(ResolvedQualifierChecker) + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsGenExtension2.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsGenExtension2.kt new file mode 100644 index 000000000..44d300229 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsGenExtension2.kt @@ -0,0 +1,17 @@ +package io.bazel.kotlin.plugin.jdeps.k2 + +import io.bazel.kotlin.plugin.jdeps.BaseJdepsGenExtension +import org.jetbrains.kotlin.codegen.ClassFileFactory +import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension +import org.jetbrains.kotlin.config.CompilerConfiguration + +internal class JdepsGenExtension2( + configuration: CompilerConfiguration, +) : BaseJdepsGenExtension(configuration), ClassFileFactoryFinalizerExtension { + override fun finalizeClassFactory(factory: ClassFileFactory) { + onAnalysisCompleted( + ClassUsageRecorder.getExplicitClassesCanonicalPaths(), + ClassUsageRecorder.getImplicitClassesCanonicalPaths(), + ) + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsK2Utils.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsK2Utils.kt new file mode 100644 index 000000000..34632b869 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsK2Utils.kt @@ -0,0 +1,80 @@ +package io.bazel.kotlin.plugin.jdeps.k2 + +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.java.JavaBinarySourceElement +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource + +/** + * Returns whether class is coming from JVM runtime env. There is no need to track these classes. + * + * @param className the class name of the class + * @return whether class is provided by JSM runtime or not + */ +internal fun isJvmClass(className: String): Boolean { + return className.startsWith("java") || + className.startsWith("modules/java.base/java/") +} + +internal fun DeserializedContainerSource.classId(): ClassId? { + return when (this) { + is JvmPackagePartSource -> classId + is KotlinJvmBinarySourceElement -> binaryClass.classId + else -> null + } +} + +internal fun SourceElement.binaryClass(): String? { + return when (this) { + is KotlinJvmBinarySourceElement -> binaryClass.location + is JvmPackagePartSource -> this.knownJvmBinaryClass?.location + is JavaBinarySourceElement -> this.javaClass.virtualFile.path + else -> null + } +} + +internal fun DeserializedContainerSource.binaryClass(): String? { + return when (this) { + is JvmPackagePartSource -> this.knownJvmBinaryClass?.location + is KotlinJvmBinarySourceElement -> binaryClass.location + else -> null + } +} + +// Extension functions to clean up checker logic + +internal fun FirClassLikeSymbol<*>.recordClass( + context: CheckerContext, + isExplicit: Boolean = true, + collectTypeArguments: Boolean = true, + visited: MutableSet> = mutableSetOf(), +) { + ClassUsageRecorder.recordClass(this, context, isExplicit, collectTypeArguments, visited) +} + +internal fun FirTypeRef.recordTypeRef( + context: CheckerContext, + isExplicit: Boolean = true, + collectTypeArguments: Boolean = true, + visited: MutableSet> = mutableSetOf(), +) { + ClassUsageRecorder.recordTypeRef(this, context, isExplicit, collectTypeArguments, visited) +} + +internal fun ConeKotlinType.recordConeType( + context: CheckerContext, + isExplicit: Boolean = true, + collectTypeArguments: Boolean = true, +) { + ClassUsageRecorder.recordConeType(this, context, isExplicit, collectTypeArguments) +} + +internal fun String.recordClassBinaryPath(isExplicit: Boolean = true) { + ClassUsageRecorder.recordClass(this, isExplicit) +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/BasicDeclarationChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/BasicDeclarationChecker.kt new file mode 100644 index 000000000..cab0a9380 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/BasicDeclarationChecker.kt @@ -0,0 +1,21 @@ +package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration + +import io.bazel.kotlin.plugin.jdeps.k2.recordClass +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.toAnnotationClassLikeSymbol + +internal object BasicDeclarationChecker : FirBasicDeclarationChecker(MppCheckerKind.Common) { + override fun check( + declaration: FirDeclaration, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + declaration.annotations.forEach { annotation -> + annotation.toAnnotationClassLikeSymbol(context.session)?.recordClass(context) + } + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/CallableChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/CallableChecker.kt new file mode 100644 index 000000000..4fd34b45e --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/CallableChecker.kt @@ -0,0 +1,38 @@ +package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration + +import io.bazel.kotlin.plugin.jdeps.k2.recordTypeRef +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirCallableDeclarationChecker +import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction +import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration +import org.jetbrains.kotlin.fir.declarations.utils.isExtension + +internal object CallableChecker : FirCallableDeclarationChecker(MppCheckerKind.Common) { + /** + * Tracks the return type & type parameters of a callable declaration. Function parameters are + * tracked in [FunctionChecker]. + */ + override fun check( + declaration: FirCallableDeclaration, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + // return type + declaration.returnTypeRef.recordTypeRef(context) + + // type params + declaration.typeParameters.forEach { typeParam -> + typeParam.symbol.resolvedBounds.forEach { typeParamBound -> + typeParamBound.recordTypeRef(context) + } + } + + // receiver param for extensions + if (declaration !is FirAnonymousFunction) { + declaration.receiverParameter?.typeRef + ?.recordTypeRef(context, isExplicit = declaration.isExtension) + } + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/ClassLikeChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/ClassLikeChecker.kt new file mode 100644 index 000000000..738721bf3 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/ClassLikeChecker.kt @@ -0,0 +1,24 @@ +package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration + +import io.bazel.kotlin.plugin.jdeps.k2.recordClass +import io.bazel.kotlin.plugin.jdeps.k2.recordConeType +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassLikeChecker +import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration +import org.jetbrains.kotlin.fir.resolve.getSuperTypes + +internal object ClassLikeChecker : FirClassLikeChecker(MppCheckerKind.Common) { + override fun check( + declaration: FirClassLikeDeclaration, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + declaration.symbol.recordClass(context) + // [recordClass] also handles supertypes, but this marks direct supertypes as explicit + declaration.symbol.getSuperTypes(context.session, recursive = false).forEach { + it.recordConeType(context) + } + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FileChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FileChecker.kt new file mode 100644 index 000000000..efc493307 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FileChecker.kt @@ -0,0 +1,95 @@ +package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration + +import io.bazel.kotlin.plugin.jdeps.k2.ClassUsageRecorder +import io.bazel.kotlin.plugin.jdeps.k2.binaryClass +import io.bazel.kotlin.plugin.jdeps.k2.recordClass +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker +import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.declarations.FirResolvedImport +import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass +import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredFunctionSymbols +import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider +import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol +import org.jetbrains.kotlin.name.ClassId + +internal object FileChecker : FirFileChecker(MppCheckerKind.Common) { + override fun check( + declaration: FirFile, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + declaration.imports.filterIsInstance().forEach { import -> + // check for classlike import (class, interface, object, enum, annotation, etc) + if (import.resolvesToClass(context)) { + import.classId()?.resolveToClass(context)?.recordClass(context) + } else { + // check for function import + val callableBinaryClass = import.resolveToFun(context)?.containerSource?.binaryClass() + if (callableBinaryClass != null) { + ClassUsageRecorder.recordClass(callableBinaryClass) + } else { + // for other symbols, track the parent class + import.resolvedParentClassId?.resolveToClass(context)?.recordClass(context) + } + } + } + } +} + +@Suppress("ReturnCount") +private fun FirResolvedImport.resolveToFun(context: CheckerContext): FirCallableSymbol<*>? { + val funName = this.importedName ?: return null + val topLevelFun = + context.session.symbolProvider + .getTopLevelCallableSymbols(packageFqName, funName) + .firstOrNull() + if (topLevelFun != null) return topLevelFun + + val parentClassId = resolvedParentClassId ?: return null + return context.session.symbolProvider + .getClassDeclaredFunctionSymbols(parentClassId, funName) + .firstOrNull() +} + +private fun FirResolvedImport.classId(): ClassId? { + val importedFqName = importedFqName ?: return null + if (importedFqName.isRoot || importedFqName.shortName().asString().isEmpty()) return null + return this.resolvedParentClassId?.createNestedClassId(importedFqName.shortName()) + ?: ClassId.topLevel(importedFqName) +} + +// Below is the original private code from the Kotlin compiler +// https://github.com/JetBrains/kotlin/blob/8b7ca9527a55de33e943f8885b34456030ce9b19/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImportsChecker.kt#L232-L259 + +@Suppress("UnsafeCallOnNullableType", "UnnecessarySafeCall", "ReturnCount") +private fun FirResolvedImport.resolvesToClass(context: CheckerContext): Boolean { + if (resolvedParentClassId != null) { + if (isAllUnder) return true + val parentClass = resolvedParentClassId!! + val relativeClassName = this.relativeParentClassName ?: return false + val importedName = this.importedName ?: return false + val innerClassId = + ClassId(parentClass.packageFqName, relativeClassName.child(importedName), false) + return innerClassId.resolveToClass(context) != null + } else { + val importedFqName = importedFqName ?: return false + if (importedFqName.isRoot) return false + val importedClassId = ClassId.topLevel(importedFqName) + return importedClassId.resolveToClass(context) != null + } +} + +private fun ClassId.resolveToClass(context: CheckerContext): FirRegularClassSymbol? { + val classSymbol = context.session.symbolProvider.getClassLikeSymbolByClassId(this) ?: return null + return when (classSymbol) { + is FirRegularClassSymbol -> classSymbol + is FirTypeAliasSymbol -> classSymbol.fullyExpandedClass(context.session) + is FirAnonymousObjectSymbol -> null + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FunctionChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FunctionChecker.kt new file mode 100644 index 000000000..710fad8d3 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FunctionChecker.kt @@ -0,0 +1,25 @@ +package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration + +import io.bazel.kotlin.plugin.jdeps.k2.recordTypeRef +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker +import org.jetbrains.kotlin.fir.declarations.FirFunction + +internal object FunctionChecker : FirFunctionChecker(MppCheckerKind.Common) { + /** + * Tracks the value parameters of a function declaration. Return type & type parameters are + * tracked in [CallableChecker]. + */ + override fun check( + declaration: FirFunction, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + // function parameters + declaration.valueParameters.forEach { valueParam -> + valueParam.returnTypeRef.recordTypeRef(context) + } + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/QualifiedAccessChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/QualifiedAccessChecker.kt new file mode 100644 index 000000000..b94df24a2 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/QualifiedAccessChecker.kt @@ -0,0 +1,60 @@ +package io.bazel.kotlin.plugin.jdeps.k2.checker.expression + +import io.bazel.kotlin.plugin.jdeps.k2.binaryClass +import io.bazel.kotlin.plugin.jdeps.k2.recordClassBinaryPath +import io.bazel.kotlin.plugin.jdeps.k2.recordConeType +import io.bazel.kotlin.plugin.jdeps.k2.recordTypeRef +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker +import org.jetbrains.kotlin.fir.declarations.FirConstructor +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.expressions.arguments +import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.references.toResolvedFunctionSymbol +import org.jetbrains.kotlin.fir.types.isExtensionFunctionType +import org.jetbrains.kotlin.fir.types.isUnit +import org.jetbrains.kotlin.fir.types.resolvedType + +internal object QualifiedAccessChecker : + FirQualifiedAccessExpressionChecker(MppCheckerKind.Common) { + override fun check( + expression: FirQualifiedAccessExpression, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + // track function's owning class + val resolvedCallableSymbol = expression.toResolvedCallableSymbol() + resolvedCallableSymbol?.containerSource?.binaryClass()?.recordClassBinaryPath() + + // track return type + val isExplicitReturnType: Boolean = expression is FirConstructor + resolvedCallableSymbol?.resolvedReturnTypeRef + ?.recordTypeRef(context, isExplicit = isExplicitReturnType, collectTypeArguments = false) + + // type arguments + resolvedCallableSymbol?.typeParameterSymbols?.forEach { typeParam -> + typeParam.resolvedBounds.forEach { it.recordTypeRef(context) } + } + + // track fun parameter types based on referenced function + expression.calleeReference.toResolvedFunctionSymbol() + ?.valueParameterSymbols + ?.forEach { + it.resolvedReturnTypeRef.recordTypeRef(context, isExplicit = false) + } + // track fun arguments actually passed + (expression as? FirFunctionCall)?.arguments?.map { it.resolvedType }?.forEach { + it.recordConeType(context, isExplicit = !it.isExtensionFunctionType) + } + + // track dispatch receiver + expression.dispatchReceiver?.resolvedType?.let { + if (!it.isUnit) { + it.recordConeType(context, isExplicit = false) + } + } + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/ResolvedQualifierChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/ResolvedQualifierChecker.kt new file mode 100644 index 000000000..73acb4e98 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/ResolvedQualifierChecker.kt @@ -0,0 +1,19 @@ +package io.bazel.kotlin.plugin.jdeps.k2.checker.expression + +import io.bazel.kotlin.plugin.jdeps.k2.recordClass +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirResolvedQualifierChecker +import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier + +// Handles expressions such as enum constants and annotation usages +internal object ResolvedQualifierChecker : FirResolvedQualifierChecker(MppCheckerKind.Common) { + override fun check( + expression: FirResolvedQualifier, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + expression.symbol?.recordClass(context) + } +} From b30520adced222bb5d60fcb2d16e1a67a2570cc3 Mon Sep 17 00:00:00 2001 From: James Barr Date: Fri, 3 May 2024 17:52:36 -0700 Subject: [PATCH 4/7] Convert ClassUsageRecorder & checker objects to classes --- .../jdeps/JdepsGenComponentRegistrar.kt | 6 +-- .../plugin/jdeps/k2/ClassUsageRecorder.kt | 36 +++++--------- .../plugin/jdeps/k2/JdepsFirExtensions.kt | 47 ++++++++++--------- .../plugin/jdeps/k2/JdepsGenExtension2.kt | 5 +- .../kotlin/plugin/jdeps/k2/JdepsK2Utils.kt | 36 -------------- .../declaration/BasicDeclarationChecker.kt | 10 ++-- .../k2/checker/declaration/CallableChecker.kt | 15 +++--- .../checker/declaration/ClassLikeChecker.kt | 11 +++-- .../k2/checker/declaration/FileChecker.kt | 15 ++++-- .../k2/checker/declaration/FunctionChecker.kt | 8 ++-- .../expression/QualifiedAccessChecker.kt | 35 +++++++++----- .../expression/ResolvedQualifierChecker.kt | 10 ++-- 12 files changed, 111 insertions(+), 123 deletions(-) diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt index 163da8860..87fb65b9d 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenComponentRegistrar.kt @@ -34,9 +34,9 @@ class JdepsGenComponentRegistrar : CompilerPluginRegistrar() { private fun ExtensionStorage.registerForK2(configuration: CompilerConfiguration) { val projectRoot = Paths.get("").toAbsolutePath().toString() + "/" - ClassUsageRecorder.init(projectRoot) - JdepsGenExtension2(configuration).run { - FirExtensionRegistrarAdapter.registerExtension(JdepsFirExtensions()) + val classUsageRecorder = ClassUsageRecorder(rootPath = projectRoot) + JdepsGenExtension2(classUsageRecorder, configuration).run { + FirExtensionRegistrarAdapter.registerExtension(JdepsFirExtensions(classUsageRecorder)) ClassFileFactoryFinalizerExtension.registerExtension(this) } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/ClassUsageRecorder.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/ClassUsageRecorder.kt index b42196c19..a994bd0be 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/ClassUsageRecorder.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/ClassUsageRecorder.kt @@ -17,26 +17,14 @@ import java.util.SortedSet private const val JAR_FILE_SEPARATOR = "!/" private const val ANONYMOUS = "" -internal object ClassUsageRecorder { - private val explicitClassesCanonicalPaths = mutableSetOf() - private val implicitClassesCanonicalPaths = mutableSetOf() - - private val results by lazy { sortedMapOf>() } - private val seen = mutableSetOf() +class ClassUsageRecorder( + internal val explicitClassesCanonicalPaths: MutableSet = mutableSetOf(), + internal val implicitClassesCanonicalPaths: MutableSet = mutableSetOf(), + private val seen: MutableSet = mutableSetOf(), + private val results: MutableMap> = sortedMapOf(), + private val rootPath: String = Paths.get("").toAbsolutePath().toString() + "/", +) { private val javaHome: String by lazy { System.getenv()["JAVA_HOME"] ?: "" } - private var rootPath: String = Paths.get("").toAbsolutePath().toString() + "/" - - fun init(rootPath: String) { - this.rootPath = rootPath - results.clear() - explicitClassesCanonicalPaths.clear() - implicitClassesCanonicalPaths.clear() - seen.clear() - } - - fun getExplicitClassesCanonicalPaths(): Set = explicitClassesCanonicalPaths - - fun getImplicitClassesCanonicalPaths(): Set = implicitClassesCanonicalPaths internal fun recordTypeRef( typeRef: FirTypeRef, @@ -61,12 +49,14 @@ internal object ClassUsageRecorder { if (ANONYMOUS in classId.toString()) return@forEachType context.session.symbolProvider .getClassLikeSymbolByClassId(classId) - ?.recordClass(context, isExplicit, collectTypeArguments, visited) + ?.let { recordClass(it, context, isExplicit, collectTypeArguments, visited) } } } else { coneKotlinType.classId?.let { classId -> - context.session.symbolProvider.getClassLikeSymbolByClassId(classId) - ?.recordClass(context, isExplicit, collectTypeArguments, visited) + if (!classId.isLocal) { + context.session.symbolProvider.getClassLikeSymbolByClassId(classId) + ?.let { recordClass(it, context, isExplicit, collectTypeArguments, visited) } + } } } } @@ -74,7 +64,7 @@ internal object ClassUsageRecorder { internal fun recordClass( firClass: FirClassLikeSymbol<*>, context: CheckerContext, - isExplicit: Boolean, + isExplicit: Boolean = true, collectTypeArguments: Boolean = true, visited: MutableSet> = mutableSetOf(), ) { diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsFirExtensions.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsFirExtensions.kt index 5d9f0a11f..b7d174b37 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsFirExtensions.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsFirExtensions.kt @@ -20,35 +20,40 @@ import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirResolvedQualifie import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar -internal class JdepsFirExtensions : FirExtensionRegistrar() { +internal class JdepsFirExtensions( + private val classUsageRecorder: ClassUsageRecorder, +) : FirExtensionRegistrar() { override fun ExtensionRegistrarContext.configurePlugin() { +::JdepsFirCheckersExtension } -} -internal class JdepsFirCheckersExtension(session: FirSession) : - FirAdditionalCheckersExtension(session) { - override val declarationCheckers: DeclarationCheckers = - object : DeclarationCheckers() { - override val basicDeclarationCheckers: Set = - setOf(BasicDeclarationChecker) + internal inner class JdepsFirCheckersExtension(session: FirSession) : + FirAdditionalCheckersExtension(session) { + override val declarationCheckers: DeclarationCheckers = + object : DeclarationCheckers() { + override val basicDeclarationCheckers: Set = + setOf(BasicDeclarationChecker(classUsageRecorder)) - override val fileCheckers: Set = setOf(FileChecker) + override val fileCheckers: Set = + setOf(FileChecker(classUsageRecorder)) - override val classLikeCheckers: Set = setOf(ClassLikeChecker) + override val classLikeCheckers: Set = + setOf(ClassLikeChecker(classUsageRecorder)) - override val functionCheckers: Set = setOf(FunctionChecker) + override val functionCheckers: Set = + setOf(FunctionChecker(classUsageRecorder)) - override val callableDeclarationCheckers: Set = - setOf(CallableChecker) - } + override val callableDeclarationCheckers: Set = + setOf(CallableChecker(classUsageRecorder)) + } - override val expressionCheckers: ExpressionCheckers = - object : ExpressionCheckers() { - override val qualifiedAccessExpressionCheckers: Set = - setOf(QualifiedAccessChecker) + override val expressionCheckers: ExpressionCheckers = + object : ExpressionCheckers() { + override val qualifiedAccessExpressionCheckers: Set = + setOf(QualifiedAccessChecker(classUsageRecorder)) - override val resolvedQualifierCheckers: Set = - setOf(ResolvedQualifierChecker) - } + override val resolvedQualifierCheckers: Set = + setOf(ResolvedQualifierChecker(classUsageRecorder)) + } + } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsGenExtension2.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsGenExtension2.kt index 44d300229..8a11c8dde 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsGenExtension2.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsGenExtension2.kt @@ -6,12 +6,13 @@ import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtensio import org.jetbrains.kotlin.config.CompilerConfiguration internal class JdepsGenExtension2( + private val classUsageRecorder: ClassUsageRecorder, configuration: CompilerConfiguration, ) : BaseJdepsGenExtension(configuration), ClassFileFactoryFinalizerExtension { override fun finalizeClassFactory(factory: ClassFileFactory) { onAnalysisCompleted( - ClassUsageRecorder.getExplicitClassesCanonicalPaths(), - ClassUsageRecorder.getImplicitClassesCanonicalPaths(), + classUsageRecorder.explicitClassesCanonicalPaths, + classUsageRecorder.implicitClassesCanonicalPaths, ) } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsK2Utils.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsK2Utils.kt index 34632b869..3b9db4770 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsK2Utils.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/JdepsK2Utils.kt @@ -1,11 +1,7 @@ package io.bazel.kotlin.plugin.jdeps.k2 import org.jetbrains.kotlin.descriptors.SourceElement -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.java.JavaBinarySourceElement -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement import org.jetbrains.kotlin.name.ClassId @@ -46,35 +42,3 @@ internal fun DeserializedContainerSource.binaryClass(): String? { else -> null } } - -// Extension functions to clean up checker logic - -internal fun FirClassLikeSymbol<*>.recordClass( - context: CheckerContext, - isExplicit: Boolean = true, - collectTypeArguments: Boolean = true, - visited: MutableSet> = mutableSetOf(), -) { - ClassUsageRecorder.recordClass(this, context, isExplicit, collectTypeArguments, visited) -} - -internal fun FirTypeRef.recordTypeRef( - context: CheckerContext, - isExplicit: Boolean = true, - collectTypeArguments: Boolean = true, - visited: MutableSet> = mutableSetOf(), -) { - ClassUsageRecorder.recordTypeRef(this, context, isExplicit, collectTypeArguments, visited) -} - -internal fun ConeKotlinType.recordConeType( - context: CheckerContext, - isExplicit: Boolean = true, - collectTypeArguments: Boolean = true, -) { - ClassUsageRecorder.recordConeType(this, context, isExplicit, collectTypeArguments) -} - -internal fun String.recordClassBinaryPath(isExplicit: Boolean = true) { - ClassUsageRecorder.recordClass(this, isExplicit) -} diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/BasicDeclarationChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/BasicDeclarationChecker.kt index cab0a9380..ae10769c9 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/BasicDeclarationChecker.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/BasicDeclarationChecker.kt @@ -1,6 +1,6 @@ package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration -import io.bazel.kotlin.plugin.jdeps.k2.recordClass +import io.bazel.kotlin.plugin.jdeps.k2.ClassUsageRecorder import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext @@ -8,14 +8,18 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclaratio import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.toAnnotationClassLikeSymbol -internal object BasicDeclarationChecker : FirBasicDeclarationChecker(MppCheckerKind.Common) { +internal class BasicDeclarationChecker( + private val classUsageRecorder: ClassUsageRecorder, +) : FirBasicDeclarationChecker(MppCheckerKind.Common) { override fun check( declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter, ) { declaration.annotations.forEach { annotation -> - annotation.toAnnotationClassLikeSymbol(context.session)?.recordClass(context) + annotation.toAnnotationClassLikeSymbol(context.session)?.let { + classUsageRecorder.recordClass(it, context) + } } } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/CallableChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/CallableChecker.kt index 4fd34b45e..279838c94 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/CallableChecker.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/CallableChecker.kt @@ -1,6 +1,6 @@ package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration -import io.bazel.kotlin.plugin.jdeps.k2.recordTypeRef +import io.bazel.kotlin.plugin.jdeps.k2.ClassUsageRecorder import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext @@ -9,7 +9,9 @@ import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.utils.isExtension -internal object CallableChecker : FirCallableDeclarationChecker(MppCheckerKind.Common) { +internal class CallableChecker( + private val classUsageRecorder: ClassUsageRecorder, +) : FirCallableDeclarationChecker(MppCheckerKind.Common) { /** * Tracks the return type & type parameters of a callable declaration. Function parameters are * tracked in [FunctionChecker]. @@ -20,19 +22,20 @@ internal object CallableChecker : FirCallableDeclarationChecker(MppCheckerKind.C reporter: DiagnosticReporter, ) { // return type - declaration.returnTypeRef.recordTypeRef(context) + declaration.returnTypeRef.let { classUsageRecorder.recordTypeRef(it, context) } // type params declaration.typeParameters.forEach { typeParam -> typeParam.symbol.resolvedBounds.forEach { typeParamBound -> - typeParamBound.recordTypeRef(context) + typeParamBound.let { classUsageRecorder.recordTypeRef(it, context) } } } // receiver param for extensions if (declaration !is FirAnonymousFunction) { - declaration.receiverParameter?.typeRef - ?.recordTypeRef(context, isExplicit = declaration.isExtension) + declaration.receiverParameter?.typeRef?.let { + classUsageRecorder.recordTypeRef(it, context, isExplicit = declaration.isExtension) + } } } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/ClassLikeChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/ClassLikeChecker.kt index 738721bf3..20ab416e6 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/ClassLikeChecker.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/ClassLikeChecker.kt @@ -1,7 +1,6 @@ package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration -import io.bazel.kotlin.plugin.jdeps.k2.recordClass -import io.bazel.kotlin.plugin.jdeps.k2.recordConeType +import io.bazel.kotlin.plugin.jdeps.k2.ClassUsageRecorder import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext @@ -9,16 +8,18 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassLikeChecke import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration import org.jetbrains.kotlin.fir.resolve.getSuperTypes -internal object ClassLikeChecker : FirClassLikeChecker(MppCheckerKind.Common) { +internal class ClassLikeChecker( + private val classUsageRecorder: ClassUsageRecorder, +) : FirClassLikeChecker(MppCheckerKind.Common) { override fun check( declaration: FirClassLikeDeclaration, context: CheckerContext, reporter: DiagnosticReporter, ) { - declaration.symbol.recordClass(context) + declaration.symbol.let { classUsageRecorder.recordClass(it, context) } // [recordClass] also handles supertypes, but this marks direct supertypes as explicit declaration.symbol.getSuperTypes(context.session, recursive = false).forEach { - it.recordConeType(context) + classUsageRecorder.recordConeType(it, context) } } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FileChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FileChecker.kt index efc493307..cea3571fa 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FileChecker.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FileChecker.kt @@ -2,7 +2,6 @@ package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration import io.bazel.kotlin.plugin.jdeps.k2.ClassUsageRecorder import io.bazel.kotlin.plugin.jdeps.k2.binaryClass -import io.bazel.kotlin.plugin.jdeps.k2.recordClass import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext @@ -18,7 +17,9 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol import org.jetbrains.kotlin.name.ClassId -internal object FileChecker : FirFileChecker(MppCheckerKind.Common) { +internal class FileChecker( + private val classUsageRecorder: ClassUsageRecorder, +) : FirFileChecker(MppCheckerKind.Common) { override fun check( declaration: FirFile, context: CheckerContext, @@ -27,15 +28,19 @@ internal object FileChecker : FirFileChecker(MppCheckerKind.Common) { declaration.imports.filterIsInstance().forEach { import -> // check for classlike import (class, interface, object, enum, annotation, etc) if (import.resolvesToClass(context)) { - import.classId()?.resolveToClass(context)?.recordClass(context) + import.classId()?.resolveToClass(context)?.let { + classUsageRecorder.recordClass(it, context) + } } else { // check for function import val callableBinaryClass = import.resolveToFun(context)?.containerSource?.binaryClass() if (callableBinaryClass != null) { - ClassUsageRecorder.recordClass(callableBinaryClass) + classUsageRecorder.recordClass(callableBinaryClass) } else { // for other symbols, track the parent class - import.resolvedParentClassId?.resolveToClass(context)?.recordClass(context) + import.resolvedParentClassId?.resolveToClass(context)?.let { + classUsageRecorder.recordClass(it, context) + } } } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FunctionChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FunctionChecker.kt index 710fad8d3..35e85725e 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FunctionChecker.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/declaration/FunctionChecker.kt @@ -1,13 +1,15 @@ package io.bazel.kotlin.plugin.jdeps.k2.checker.declaration -import io.bazel.kotlin.plugin.jdeps.k2.recordTypeRef +import io.bazel.kotlin.plugin.jdeps.k2.ClassUsageRecorder import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker import org.jetbrains.kotlin.fir.declarations.FirFunction -internal object FunctionChecker : FirFunctionChecker(MppCheckerKind.Common) { +internal class FunctionChecker( + private val classUsageRecorder: ClassUsageRecorder, +) : FirFunctionChecker(MppCheckerKind.Common) { /** * Tracks the value parameters of a function declaration. Return type & type parameters are * tracked in [CallableChecker]. @@ -19,7 +21,7 @@ internal object FunctionChecker : FirFunctionChecker(MppCheckerKind.Common) { ) { // function parameters declaration.valueParameters.forEach { valueParam -> - valueParam.returnTypeRef.recordTypeRef(context) + valueParam.returnTypeRef.let { classUsageRecorder.recordTypeRef(it, context) } } } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/QualifiedAccessChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/QualifiedAccessChecker.kt index b94df24a2..fac06c601 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/QualifiedAccessChecker.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/QualifiedAccessChecker.kt @@ -1,9 +1,7 @@ package io.bazel.kotlin.plugin.jdeps.k2.checker.expression +import io.bazel.kotlin.plugin.jdeps.k2.ClassUsageRecorder import io.bazel.kotlin.plugin.jdeps.k2.binaryClass -import io.bazel.kotlin.plugin.jdeps.k2.recordClassBinaryPath -import io.bazel.kotlin.plugin.jdeps.k2.recordConeType -import io.bazel.kotlin.plugin.jdeps.k2.recordTypeRef import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext @@ -18,8 +16,9 @@ import org.jetbrains.kotlin.fir.types.isExtensionFunctionType import org.jetbrains.kotlin.fir.types.isUnit import org.jetbrains.kotlin.fir.types.resolvedType -internal object QualifiedAccessChecker : - FirQualifiedAccessExpressionChecker(MppCheckerKind.Common) { +internal class QualifiedAccessChecker( + private val classUsageRecorder: ClassUsageRecorder, +) : FirQualifiedAccessExpressionChecker(MppCheckerKind.Common) { override fun check( expression: FirQualifiedAccessExpression, context: CheckerContext, @@ -27,33 +26,43 @@ internal object QualifiedAccessChecker : ) { // track function's owning class val resolvedCallableSymbol = expression.toResolvedCallableSymbol() - resolvedCallableSymbol?.containerSource?.binaryClass()?.recordClassBinaryPath() + resolvedCallableSymbol?.containerSource?.binaryClass()?.let { + classUsageRecorder.recordClass(it) + } // track return type val isExplicitReturnType: Boolean = expression is FirConstructor - resolvedCallableSymbol?.resolvedReturnTypeRef - ?.recordTypeRef(context, isExplicit = isExplicitReturnType, collectTypeArguments = false) + resolvedCallableSymbol?.resolvedReturnTypeRef?.let { + classUsageRecorder.recordTypeRef( + it, + context, + isExplicit = isExplicitReturnType, + collectTypeArguments = false, + ) + } // type arguments resolvedCallableSymbol?.typeParameterSymbols?.forEach { typeParam -> - typeParam.resolvedBounds.forEach { it.recordTypeRef(context) } + typeParam.resolvedBounds.forEach { classUsageRecorder.recordTypeRef(it, context) } } // track fun parameter types based on referenced function expression.calleeReference.toResolvedFunctionSymbol() ?.valueParameterSymbols - ?.forEach { - it.resolvedReturnTypeRef.recordTypeRef(context, isExplicit = false) + ?.forEach { valueParam -> + valueParam.resolvedReturnTypeRef.let { + classUsageRecorder.recordTypeRef(it, context, isExplicit = false) + } } // track fun arguments actually passed (expression as? FirFunctionCall)?.arguments?.map { it.resolvedType }?.forEach { - it.recordConeType(context, isExplicit = !it.isExtensionFunctionType) + classUsageRecorder.recordConeType(it, context, isExplicit = !it.isExtensionFunctionType) } // track dispatch receiver expression.dispatchReceiver?.resolvedType?.let { if (!it.isUnit) { - it.recordConeType(context, isExplicit = false) + classUsageRecorder.recordConeType(it, context, isExplicit = false) } } } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/ResolvedQualifierChecker.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/ResolvedQualifierChecker.kt index 73acb4e98..0607f462c 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/ResolvedQualifierChecker.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/k2/checker/expression/ResolvedQualifierChecker.kt @@ -1,6 +1,6 @@ package io.bazel.kotlin.plugin.jdeps.k2.checker.expression -import io.bazel.kotlin.plugin.jdeps.k2.recordClass +import io.bazel.kotlin.plugin.jdeps.k2.ClassUsageRecorder import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext @@ -8,12 +8,16 @@ import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirResolvedQualifie import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier // Handles expressions such as enum constants and annotation usages -internal object ResolvedQualifierChecker : FirResolvedQualifierChecker(MppCheckerKind.Common) { +internal class ResolvedQualifierChecker( + private val classUsageRecorder: ClassUsageRecorder, +) : FirResolvedQualifierChecker(MppCheckerKind.Common) { override fun check( expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter, ) { - expression.symbol?.recordClass(context) + expression.symbol?.let { + classUsageRecorder.recordClass(it, context) + } } } From f3ac6acdc94c37a87f7237e76de602896b775e2a Mon Sep 17 00:00:00 2001 From: James Barr Date: Tue, 16 Jul 2024 16:59:52 -0700 Subject: [PATCH 5/7] Allow for overriding of the KSP Kotlin toolchain versions for K2 --- .../builder/tasks/jvm/CompilationTask.kt | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt index f845b9607..a783c3828 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt @@ -47,6 +47,8 @@ import java.util.stream.Stream import kotlin.io.path.exists private const val SOURCE_JARS_DIR = "_srcjars" +private const val API_VERSION_ARG = "-api-version" +private const val LANGUAGE_VERSION_ARG = "-language-version" fun JvmCompilationTask.codeGenArgs(): CompilationArgs = CompilationArgs() @@ -55,7 +57,7 @@ fun JvmCompilationTask.codeGenArgs(): CompilationArgs = }.flag("-d", directories.classes) .values(info.passthroughFlagsList) -fun JvmCompilationTask.baseArgs(): CompilationArgs { +fun JvmCompilationTask.baseArgs(overrides: Map = emptyMap()): CompilationArgs { val classpath = when (info.reducedClasspathMode) { "KOTLINBUILDER_REDUCED" -> { @@ -83,8 +85,11 @@ fun JvmCompilationTask.baseArgs(): CompilationArgs { it .map(Path::toString) .joinToString(File.pathSeparator) - }.flag("-api-version", info.toolchainInfo.common.apiVersion) - .flag("-language-version", info.toolchainInfo.common.languageVersion) + }.flag(API_VERSION_ARG, overrides[API_VERSION_ARG] ?: info.toolchainInfo.common.apiVersion) + .flag( + LANGUAGE_VERSION_ARG, + overrides[LANGUAGE_VERSION_ARG] ?: info.toolchainInfo.common.languageVersion, + ) .flag("-jvm-target", info.toolchainInfo.jvm.jvmTarget) .flag("-module-name", info.moduleName) } @@ -293,7 +298,15 @@ private fun JvmCompilationTask.runKspPlugin( compiler: KotlinToolchain.KotlincInvoker, ): JvmCompilationTask { return context.execute("Ksp (${inputs.processorsList.joinToString(", ")})") { - baseArgs() + val overrides = + mutableMapOf( + API_VERSION_ARG to kspKotlinToolchainVersion(info.toolchainInfo.common.apiVersion), + LANGUAGE_VERSION_ARG to + kspKotlinToolchainVersion( + info.toolchainInfo.common.languageVersion, + ), + ) + baseArgs(overrides) .plus(kspArgs(plugins)) .flag("-d", directories.generatedClasses) .values(inputs.kotlinSourcesList) @@ -316,6 +329,11 @@ private fun JvmCompilationTask.runKspPlugin( } } +private fun kspKotlinToolchainVersion(version: String): String { + // KSP doesn't support Kotlin 2.0 yet, so we need to use 1.9 + return if (version.toFloat() >= 2.0) "1.9" else version +} + /** * Produce the primary output jar. */ From 583cf8e0f9b0e89611fa7fe4eab87ee8445fd039 Mon Sep 17 00:00:00 2001 From: James Barr Date: Wed, 17 Jul 2024 16:49:07 -0700 Subject: [PATCH 6/7] Update examples for Kotlin 2.0 --- examples/anvil/BUILD.bazel | 2 ++ examples/jetpack_compose/BUILD | 2 +- examples/jetpack_compose/WORKSPACE | 8 +++----- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/anvil/BUILD.bazel b/examples/anvil/BUILD.bazel index 2b47d5e51..b60a2e550 100644 --- a/examples/anvil/BUILD.bazel +++ b/examples/anvil/BUILD.bazel @@ -2,6 +2,8 @@ load("@rules_kotlin//kotlin:core.bzl", "define_kt_toolchain") define_kt_toolchain( name = "kotlin_toolchain", + api_version = "1.9", + language_version = "1.9", ) platform( diff --git a/examples/jetpack_compose/BUILD b/examples/jetpack_compose/BUILD index 9d926b793..cce9c500d 100644 --- a/examples/jetpack_compose/BUILD +++ b/examples/jetpack_compose/BUILD @@ -22,7 +22,7 @@ kt_compiler_plugin( target_embedded_compiler = True, visibility = ["//visibility:public"], deps = [ - "@maven//:androidx_compose_compiler_compiler", + "@maven//:org_jetbrains_kotlin_kotlin_compose_compiler_plugin_embeddable", ], ) diff --git a/examples/jetpack_compose/WORKSPACE b/examples/jetpack_compose/WORKSPACE index 985e2576d..9ae898f4f 100644 --- a/examples/jetpack_compose/WORKSPACE +++ b/examples/jetpack_compose/WORKSPACE @@ -2,11 +2,9 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") _COMPOSE_VERSION = "1.2.1" -_COMPOSE_COMPILER_VERSION = "1.5.9" +_KOTLIN_COMPILER_VERSION = "2.0.0" -_KOTLIN_COMPILER_VERSION = "1.9.22" - -_KOTLIN_COMPILER_SHA = "88b39213506532c816ff56348c07bbeefe0c8d18943bffbad11063cf97cac3e6" +_KOTLIN_COMPILER_SHA = "ef578730976154fd2c5968d75af8c2703b3de84a78dffe913f670326e149da3b" # Setup Kotlin @@ -48,6 +46,7 @@ load("@rules_jvm_external//:defs.bzl", "maven_install") maven_install( artifacts = [ + "org.jetbrains.kotlin:kotlin-compose-compiler-plugin-embeddable:{}".format(_KOTLIN_COMPILER_VERSION), "org.jetbrains.kotlin:kotlin-stdlib:{}".format(_KOTLIN_COMPILER_VERSION), "androidx.core:core-ktx:1.7.0", "androidx.appcompat:appcompat:1.4.1", @@ -55,7 +54,6 @@ maven_install( "androidx.compose.material:material:{}".format(_COMPOSE_VERSION), "androidx.compose.ui:ui:{}".format(_COMPOSE_VERSION), "androidx.compose.ui:ui-tooling:{}".format(_COMPOSE_VERSION), - "androidx.compose.compiler:compiler:{}".format(_COMPOSE_COMPILER_VERSION), "androidx.compose.runtime:runtime:{}".format(_COMPOSE_VERSION), ], repositories = [ From 44254da89fd5766c0ef667d567035ed53acf4556 Mon Sep 17 00:00:00 2001 From: James Barr Date: Thu, 18 Jul 2024 11:26:21 -0700 Subject: [PATCH 7/7] Run ktlint to update formatting --- .../kotlin/builder/tasks/CompileKotlin.kt | 28 +- .../kotlin/builder/tasks/KotlinBuilder.kt | 462 +++++++++--------- .../bazel/kotlin/builder/tasks/MergeJdeps.kt | 24 +- .../builder/tasks/jvm/CompilationTask.kt | 4 +- .../tasks/jvm/KotlinJvmTaskExecutor.kt | 198 ++++---- .../builder/toolchain/KotlinToolchain.kt | 62 +-- .../kotlin/plugin/jdeps/JdepsGenExtension.kt | 2 +- 7 files changed, 390 insertions(+), 390 deletions(-) diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/CompileKotlin.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/CompileKotlin.kt index d800672f0..ababf3e95 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/CompileKotlin.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/CompileKotlin.kt @@ -23,17 +23,17 @@ import io.bazel.worker.WorkerContext import javax.inject.Inject class CompileKotlin - @Inject - constructor( - private val builder: KotlinBuilder, - ) : Work { - override fun invoke( - ctx: WorkerContext.TaskContext, - args: Iterable, - ): Status = - if (builder.build(ctx, args.toList()) != 0) { - Status.ERROR - } else { - Status.SUCCESS - } - } +@Inject +constructor( + private val builder: KotlinBuilder, +) : Work { + override fun invoke( + ctx: WorkerContext.TaskContext, + args: Iterable, + ): Status = + if (builder.build(ctx, args.toList()) != 0) { + Status.ERROR + } else { + Status.SUCCESS + } +} diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt index ea1c03797..2b973af3d 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt @@ -40,265 +40,265 @@ import javax.inject.Singleton @Singleton @Suppress("MemberVisibilityCanBePrivate") class KotlinBuilder - @Inject - internal constructor( - private val jvmTaskExecutor: KotlinJvmTaskExecutor, - ) { - companion object { - @JvmStatic - private val FLAGFILE_RE = Pattern.compile("""^--flagfile=((.*)-(\d+).params)$""").toRegex() +@Inject +internal constructor( + private val jvmTaskExecutor: KotlinJvmTaskExecutor, +) { + companion object { + @JvmStatic + private val FLAGFILE_RE = Pattern.compile("""^--flagfile=((.*)-(\d+).params)$""").toRegex() - enum class KotlinBuilderFlags( - override val flag: String, - ) : Flag { - TARGET_LABEL("--target_label"), - CLASSPATH("--classpath"), - DIRECT_DEPENDENCIES("--direct_dependencies"), - DEPS_ARTIFACTS("--deps_artifacts"), - SOURCES("--sources"), - SOURCE_JARS("--source_jars"), - PROCESSOR_PATH("--processorpath"), - PROCESSORS("--processors"), - STUBS_PLUGIN_OPTIONS("--stubs_plugin_options"), - STUBS_PLUGIN_CLASS_PATH("--stubs_plugin_classpath"), - COMPILER_PLUGIN_OPTIONS("--compiler_plugin_options"), - COMPILER_PLUGIN_CLASS_PATH("--compiler_plugin_classpath"), - OUTPUT("--output"), - RULE_KIND("--rule_kind"), - MODULE_NAME("--kotlin_module_name"), - PASSTHROUGH_FLAGS("--kotlin_passthrough_flags"), - API_VERSION("--kotlin_api_version"), - LANGUAGE_VERSION("--kotlin_language_version"), - JVM_TARGET("--kotlin_jvm_target"), - OUTPUT_SRCJAR("--kotlin_output_srcjar"), - GENERATED_CLASSDIR("--kotlin_generated_classdir"), - FRIEND_PATHS("--kotlin_friend_paths"), - OUTPUT_JDEPS("--kotlin_output_jdeps"), - DEBUG("--kotlin_debug_tags"), - TASK_ID("--kotlin_task_id"), - ABI_JAR("--abi_jar"), - GENERATED_JAVA_SRC_JAR("--generated_java_srcjar"), - GENERATED_JAVA_STUB_JAR("--kapt_generated_stub_jar"), - GENERATED_CLASS_JAR("--kapt_generated_class_jar"), - BUILD_KOTLIN("--build_kotlin"), - STRICT_KOTLIN_DEPS("--strict_kotlin_deps"), - REDUCED_CLASSPATH_MODE("--reduced_classpath_mode"), - INSTRUMENT_COVERAGE("--instrument_coverage"), - KSP_GENERATED_JAVA_SRCJAR("--ksp_generated_java_srcjar"), - } + enum class KotlinBuilderFlags( + override val flag: String, + ) : Flag { + TARGET_LABEL("--target_label"), + CLASSPATH("--classpath"), + DIRECT_DEPENDENCIES("--direct_dependencies"), + DEPS_ARTIFACTS("--deps_artifacts"), + SOURCES("--sources"), + SOURCE_JARS("--source_jars"), + PROCESSOR_PATH("--processorpath"), + PROCESSORS("--processors"), + STUBS_PLUGIN_OPTIONS("--stubs_plugin_options"), + STUBS_PLUGIN_CLASS_PATH("--stubs_plugin_classpath"), + COMPILER_PLUGIN_OPTIONS("--compiler_plugin_options"), + COMPILER_PLUGIN_CLASS_PATH("--compiler_plugin_classpath"), + OUTPUT("--output"), + RULE_KIND("--rule_kind"), + MODULE_NAME("--kotlin_module_name"), + PASSTHROUGH_FLAGS("--kotlin_passthrough_flags"), + API_VERSION("--kotlin_api_version"), + LANGUAGE_VERSION("--kotlin_language_version"), + JVM_TARGET("--kotlin_jvm_target"), + OUTPUT_SRCJAR("--kotlin_output_srcjar"), + GENERATED_CLASSDIR("--kotlin_generated_classdir"), + FRIEND_PATHS("--kotlin_friend_paths"), + OUTPUT_JDEPS("--kotlin_output_jdeps"), + DEBUG("--kotlin_debug_tags"), + TASK_ID("--kotlin_task_id"), + ABI_JAR("--abi_jar"), + GENERATED_JAVA_SRC_JAR("--generated_java_srcjar"), + GENERATED_JAVA_STUB_JAR("--kapt_generated_stub_jar"), + GENERATED_CLASS_JAR("--kapt_generated_class_jar"), + BUILD_KOTLIN("--build_kotlin"), + STRICT_KOTLIN_DEPS("--strict_kotlin_deps"), + REDUCED_CLASSPATH_MODE("--reduced_classpath_mode"), + INSTRUMENT_COVERAGE("--instrument_coverage"), + KSP_GENERATED_JAVA_SRCJAR("--ksp_generated_java_srcjar"), } + } - fun build( - taskContext: WorkerContext.TaskContext, - args: List, - ): Int { - val (argMap, compileContext) = buildContext(taskContext, args) - var success = false - var status = 0 - try { - @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") - when (compileContext.info.platform) { - Platform.JVM -> executeJvmTask(compileContext, taskContext.directory, argMap) - Platform.UNRECOGNIZED -> throw IllegalStateException( - "unrecognized platform: ${compileContext.info}", - ) - } - success = true - } catch (ex: CompilationStatusException) { - taskContext.error { "Compilation failure: ${ex.message}" } - status = ex.status - } catch (throwable: Throwable) { - taskContext.error(throwable) { "Uncaught exception" } - } finally { - compileContext.finalize(success) + fun build( + taskContext: WorkerContext.TaskContext, + args: List, + ): Int { + val (argMap, compileContext) = buildContext(taskContext, args) + var success = false + var status = 0 + try { + @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") + when (compileContext.info.platform) { + Platform.JVM -> executeJvmTask(compileContext, taskContext.directory, argMap) + Platform.UNRECOGNIZED -> throw IllegalStateException( + "unrecognized platform: ${compileContext.info}", + ) } - return status + success = true + } catch (ex: CompilationStatusException) { + taskContext.error { "Compilation failure: ${ex.message}" } + status = ex.status + } catch (throwable: Throwable) { + taskContext.error(throwable) { "Uncaught exception" } + } finally { + compileContext.finalize(success) } + return status + } - private fun buildContext( - ctx: WorkerContext.TaskContext, - args: List, - ): Pair { - check(args.isNotEmpty()) { "expected at least a single arg got: ${args.joinToString(" ")}" } - val lines = - FLAGFILE_RE.matchEntire(args[0])?.groups?.get(1)?.let { - Files.readAllLines(FileSystems.getDefault().getPath(it.value), StandardCharsets.UTF_8) - } ?: args + private fun buildContext( + ctx: WorkerContext.TaskContext, + args: List, + ): Pair { + check(args.isNotEmpty()) { "expected at least a single arg got: ${args.joinToString(" ")}" } + val lines = + FLAGFILE_RE.matchEntire(args[0])?.groups?.get(1)?.let { + Files.readAllLines(FileSystems.getDefault().getPath(it.value), StandardCharsets.UTF_8) + } ?: args - val argMap = ArgMaps.from(lines) - val info = buildTaskInfo(argMap).build() - val context = - CompilationTaskContext(info, ctx.asPrintStream()) - return Pair(argMap, context) - } + val argMap = ArgMaps.from(lines) + val info = buildTaskInfo(argMap).build() + val context = + CompilationTaskContext(info, ctx.asPrintStream()) + return Pair(argMap, context) + } - private fun buildTaskInfo(argMap: ArgMap): CompilationTaskInfo.Builder = - with(CompilationTaskInfo.newBuilder()) { - addAllDebug(argMap.mandatory(KotlinBuilderFlags.DEBUG)) + private fun buildTaskInfo(argMap: ArgMap): CompilationTaskInfo.Builder = + with(CompilationTaskInfo.newBuilder()) { + addAllDebug(argMap.mandatory(KotlinBuilderFlags.DEBUG)) - label = argMap.mandatorySingle(KotlinBuilderFlags.TARGET_LABEL) - argMap.mandatorySingle(KotlinBuilderFlags.RULE_KIND).split("_").also { - check(it.size == 3 && it[0] == "kt") { "invalid rule kind $it" } - platform = - checkNotNull(Platform.valueOf(it[1].uppercase())) { - "unrecognized platform ${it[1]}" - } - ruleKind = - checkNotNull(RuleKind.valueOf(it[2].uppercase())) { - "unrecognized rule kind ${it[2]}" - } - } - moduleName = - argMap.mandatorySingle(KotlinBuilderFlags.MODULE_NAME).also { - check(it.isNotBlank()) { "--kotlin_module_name should not be blank" } + label = argMap.mandatorySingle(KotlinBuilderFlags.TARGET_LABEL) + argMap.mandatorySingle(KotlinBuilderFlags.RULE_KIND).split("_").also { + check(it.size == 3 && it[0] == "kt") { "invalid rule kind $it" } + platform = + checkNotNull(Platform.valueOf(it[1].uppercase())) { + "unrecognized platform ${it[1]}" + } + ruleKind = + checkNotNull(RuleKind.valueOf(it[2].uppercase())) { + "unrecognized rule kind ${it[2]}" } - addAllPassthroughFlags(argMap.optional(KotlinBuilderFlags.PASSTHROUGH_FLAGS) ?: emptyList()) - - argMap.optional(KotlinBuilderFlags.FRIEND_PATHS)?.let(::addAllFriendPaths) - toolchainInfoBuilder.commonBuilder.apiVersion = - argMap.mandatorySingle(KotlinBuilderFlags.API_VERSION) - toolchainInfoBuilder.commonBuilder.languageVersion = - argMap.mandatorySingle(KotlinBuilderFlags.LANGUAGE_VERSION) - strictKotlinDeps = argMap.mandatorySingle(KotlinBuilderFlags.STRICT_KOTLIN_DEPS) - reducedClasspathMode = argMap.mandatorySingle(KotlinBuilderFlags.REDUCED_CLASSPATH_MODE) - this } + moduleName = + argMap.mandatorySingle(KotlinBuilderFlags.MODULE_NAME).also { + check(it.isNotBlank()) { "--kotlin_module_name should not be blank" } + } + addAllPassthroughFlags(argMap.optional(KotlinBuilderFlags.PASSTHROUGH_FLAGS) ?: emptyList()) - private fun executeJvmTask( - context: CompilationTaskContext, - workingDir: Path, - argMap: ArgMap, - ) { - val task = buildJvmTask(context.info, workingDir, argMap) - context.whenTracing { - printProto("jvm task message:", task) - } - jvmTaskExecutor.execute(context, task) + argMap.optional(KotlinBuilderFlags.FRIEND_PATHS)?.let(::addAllFriendPaths) + toolchainInfoBuilder.commonBuilder.apiVersion = + argMap.mandatorySingle(KotlinBuilderFlags.API_VERSION) + toolchainInfoBuilder.commonBuilder.languageVersion = + argMap.mandatorySingle(KotlinBuilderFlags.LANGUAGE_VERSION) + strictKotlinDeps = argMap.mandatorySingle(KotlinBuilderFlags.STRICT_KOTLIN_DEPS) + reducedClasspathMode = argMap.mandatorySingle(KotlinBuilderFlags.REDUCED_CLASSPATH_MODE) + this } - private fun buildJvmTask( - info: CompilationTaskInfo, - workingDir: Path, - argMap: ArgMap, - ): JvmCompilationTask = - JvmCompilationTask.newBuilder().let { root -> - root.info = info + private fun executeJvmTask( + context: CompilationTaskContext, + workingDir: Path, + argMap: ArgMap, + ) { + val task = buildJvmTask(context.info, workingDir, argMap) + context.whenTracing { + printProto("jvm task message:", task) + } + jvmTaskExecutor.execute(context, task) + } - root.compileKotlin = argMap.mandatorySingle(KotlinBuilderFlags.BUILD_KOTLIN).toBoolean() - root.instrumentCoverage = - argMap - .mandatorySingle( - KotlinBuilderFlags.INSTRUMENT_COVERAGE, - ).toBoolean() + private fun buildJvmTask( + info: CompilationTaskInfo, + workingDir: Path, + argMap: ArgMap, + ): JvmCompilationTask = + JvmCompilationTask.newBuilder().let { root -> + root.info = info - with(root.outputsBuilder) { - argMap.optionalSingle(KotlinBuilderFlags.OUTPUT)?.let { jar = it } - argMap.optionalSingle(KotlinBuilderFlags.OUTPUT_SRCJAR)?.let { srcjar = it } + root.compileKotlin = argMap.mandatorySingle(KotlinBuilderFlags.BUILD_KOTLIN).toBoolean() + root.instrumentCoverage = + argMap + .mandatorySingle( + KotlinBuilderFlags.INSTRUMENT_COVERAGE, + ).toBoolean() - argMap.optionalSingle(KotlinBuilderFlags.OUTPUT_JDEPS)?.apply { jdeps = this } - argMap.optionalSingle(KotlinBuilderFlags.GENERATED_JAVA_SRC_JAR)?.apply { - generatedJavaSrcJar = this - } - argMap.optionalSingle(KotlinBuilderFlags.GENERATED_JAVA_STUB_JAR)?.apply { - generatedJavaStubJar = this - } - argMap.optionalSingle(KotlinBuilderFlags.ABI_JAR)?.let { abijar = it } - argMap.optionalSingle(KotlinBuilderFlags.GENERATED_CLASS_JAR)?.let { - generatedClassJar = it - } - argMap.optionalSingle(KotlinBuilderFlags.KSP_GENERATED_JAVA_SRCJAR)?.let { - generatedKspSrcJar = it - } + with(root.outputsBuilder) { + argMap.optionalSingle(KotlinBuilderFlags.OUTPUT)?.let { jar = it } + argMap.optionalSingle(KotlinBuilderFlags.OUTPUT_SRCJAR)?.let { srcjar = it } + + argMap.optionalSingle(KotlinBuilderFlags.OUTPUT_JDEPS)?.apply { jdeps = this } + argMap.optionalSingle(KotlinBuilderFlags.GENERATED_JAVA_SRC_JAR)?.apply { + generatedJavaSrcJar = this + } + argMap.optionalSingle(KotlinBuilderFlags.GENERATED_JAVA_STUB_JAR)?.apply { + generatedJavaStubJar = this + } + argMap.optionalSingle(KotlinBuilderFlags.ABI_JAR)?.let { abijar = it } + argMap.optionalSingle(KotlinBuilderFlags.GENERATED_CLASS_JAR)?.let { + generatedClassJar = it } + argMap.optionalSingle(KotlinBuilderFlags.KSP_GENERATED_JAVA_SRCJAR)?.let { + generatedKspSrcJar = it + } + } - with(root.directoriesBuilder) { - val moduleName = argMap.mandatorySingle(KotlinBuilderFlags.MODULE_NAME) - classes = - workingDir.resolveNewDirectories(getOutputDirPath(moduleName, "classes")).toString() - javaClasses = - workingDir - .resolveNewDirectories( - getOutputDirPath(moduleName, "java_classes"), - ).toString() - if (argMap.hasAll(KotlinBuilderFlags.ABI_JAR)) { - abiClasses = - workingDir - .resolveNewDirectories( - getOutputDirPath(moduleName, "abi_classes"), - ).toString() - } - generatedClasses = - workingDir - .resolveNewDirectories(getOutputDirPath(moduleName, "generated_classes")) - .toString() - temp = + with(root.directoriesBuilder) { + val moduleName = argMap.mandatorySingle(KotlinBuilderFlags.MODULE_NAME) + classes = + workingDir.resolveNewDirectories(getOutputDirPath(moduleName, "classes")).toString() + javaClasses = + workingDir + .resolveNewDirectories( + getOutputDirPath(moduleName, "java_classes"), + ).toString() + if (argMap.hasAll(KotlinBuilderFlags.ABI_JAR)) { + abiClasses = workingDir .resolveNewDirectories( - getOutputDirPath(moduleName, "temp"), + getOutputDirPath(moduleName, "abi_classes"), ).toString() - generatedSources = - workingDir - .resolveNewDirectories(getOutputDirPath(moduleName, "generated_sources")) - .toString() - generatedJavaSources = - workingDir - .resolveNewDirectories(getOutputDirPath(moduleName, "generated_java_sources")) - .toString() - generatedStubClasses = - workingDir.resolveNewDirectories(getOutputDirPath(moduleName, "stubs")).toString() - coverageMetadataClasses = - workingDir - .resolveNewDirectories(getOutputDirPath(moduleName, "coverage-metadata")) - .toString() } + generatedClasses = + workingDir + .resolveNewDirectories(getOutputDirPath(moduleName, "generated_classes")) + .toString() + temp = + workingDir + .resolveNewDirectories( + getOutputDirPath(moduleName, "temp"), + ).toString() + generatedSources = + workingDir + .resolveNewDirectories(getOutputDirPath(moduleName, "generated_sources")) + .toString() + generatedJavaSources = + workingDir + .resolveNewDirectories(getOutputDirPath(moduleName, "generated_java_sources")) + .toString() + generatedStubClasses = + workingDir.resolveNewDirectories(getOutputDirPath(moduleName, "stubs")).toString() + coverageMetadataClasses = + workingDir + .resolveNewDirectories(getOutputDirPath(moduleName, "coverage-metadata")) + .toString() + } - with(root.inputsBuilder) { - addAllClasspath(argMap.mandatory(KotlinBuilderFlags.CLASSPATH)) - addAllDepsArtifacts( - argMap.optional(KotlinBuilderFlags.DEPS_ARTIFACTS) ?: emptyList(), - ) - addAllDirectDependencies(argMap.mandatory(KotlinBuilderFlags.DIRECT_DEPENDENCIES)) + with(root.inputsBuilder) { + addAllClasspath(argMap.mandatory(KotlinBuilderFlags.CLASSPATH)) + addAllDepsArtifacts( + argMap.optional(KotlinBuilderFlags.DEPS_ARTIFACTS) ?: emptyList(), + ) + addAllDirectDependencies(argMap.mandatory(KotlinBuilderFlags.DIRECT_DEPENDENCIES)) - addAllProcessors(argMap.optional(KotlinBuilderFlags.PROCESSORS) ?: emptyList()) - addAllProcessorpaths(argMap.optional(KotlinBuilderFlags.PROCESSOR_PATH) ?: emptyList()) + addAllProcessors(argMap.optional(KotlinBuilderFlags.PROCESSORS) ?: emptyList()) + addAllProcessorpaths(argMap.optional(KotlinBuilderFlags.PROCESSOR_PATH) ?: emptyList()) - addAllStubsPluginOptions( - argMap.optional(KotlinBuilderFlags.STUBS_PLUGIN_OPTIONS) ?: emptyList(), - ) - addAllStubsPluginClasspath( - argMap.optional(KotlinBuilderFlags.STUBS_PLUGIN_CLASS_PATH) ?: emptyList(), - ) + addAllStubsPluginOptions( + argMap.optional(KotlinBuilderFlags.STUBS_PLUGIN_OPTIONS) ?: emptyList(), + ) + addAllStubsPluginClasspath( + argMap.optional(KotlinBuilderFlags.STUBS_PLUGIN_CLASS_PATH) ?: emptyList(), + ) - addAllCompilerPluginOptions( - argMap.optional(KotlinBuilderFlags.COMPILER_PLUGIN_OPTIONS) ?: emptyList(), - ) - addAllCompilerPluginClasspath( - argMap.optional(KotlinBuilderFlags.COMPILER_PLUGIN_CLASS_PATH) ?: emptyList(), - ) + addAllCompilerPluginOptions( + argMap.optional(KotlinBuilderFlags.COMPILER_PLUGIN_OPTIONS) ?: emptyList(), + ) + addAllCompilerPluginClasspath( + argMap.optional(KotlinBuilderFlags.COMPILER_PLUGIN_CLASS_PATH) ?: emptyList(), + ) - argMap - .optional(KotlinBuilderFlags.SOURCES) - ?.iterator() - ?.partitionJvmSources( - { addKotlinSources(it) }, - { addJavaSources(it) }, - ) - argMap - .optional(KotlinBuilderFlags.SOURCE_JARS) - ?.also { - addAllSourceJars(it) - } - } + argMap + .optional(KotlinBuilderFlags.SOURCES) + ?.iterator() + ?.partitionJvmSources( + { addKotlinSources(it) }, + { addJavaSources(it) }, + ) + argMap + .optional(KotlinBuilderFlags.SOURCE_JARS) + ?.also { + addAllSourceJars(it) + } + } - with(root.infoBuilder) { - toolchainInfoBuilder.jvmBuilder.jvmTarget = - argMap.mandatorySingle(KotlinBuilderFlags.JVM_TARGET) - } - root.build() + with(root.infoBuilder) { + toolchainInfoBuilder.jvmBuilder.jvmTarget = + argMap.mandatorySingle(KotlinBuilderFlags.JVM_TARGET) } + root.build() + } - private fun getOutputDirPath( - moduleName: String, - dirName: String, - ) = "_kotlinc/${moduleName}_jvm/$dirName" - } + private fun getOutputDirPath( + moduleName: String, + dirName: String, + ) = "_kotlinc/${moduleName}_jvm/$dirName" +} diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/MergeJdeps.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/MergeJdeps.kt index 30ada5576..bdef460fb 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/MergeJdeps.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/MergeJdeps.kt @@ -24,17 +24,17 @@ import io.bazel.worker.WorkerContext import javax.inject.Inject class MergeJdeps - @Inject - constructor( - private val merger: JdepsMerger, - ) : Work { - override fun invoke( - ctx: WorkerContext.TaskContext, - args: Iterable, - ): Status { - if (merger.execute(ctx, args.toList()) != 0) { - return Status.ERROR - } - return Status.SUCCESS +@Inject +constructor( + private val merger: JdepsMerger, +) : Work { + override fun invoke( + ctx: WorkerContext.TaskContext, + args: Iterable, + ): Status { + if (merger.execute(ctx, args.toList()) != 0) { + return Status.ERROR } + return Status.SUCCESS } +} diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt index a783c3828..306952041 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt @@ -242,7 +242,7 @@ internal fun JvmCompilationTask.runPlugins( ( inputs.processorsList.isEmpty() && inputs.stubsPluginClasspathList.isEmpty() - ) || + ) || inputs.kotlinSourcesList.isEmpty() ) { return this @@ -442,7 +442,7 @@ fun JvmCompilationTask.compileKotlin( options = inputs.compilerPluginOptionsList, classpath = inputs.compilerPluginClasspathList, ) - ).values(inputs.javaSourcesList) + ).values(inputs.javaSourcesList) .values(inputs.kotlinSourcesList) .flag("-d", directories.classes) .list() diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt index b1e07f733..934be5141 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt @@ -31,115 +31,115 @@ const val X_FRIENDS_PATH_SEPARATOR = "," @Singleton class KotlinJvmTaskExecutor - @Inject - internal constructor( - private val compiler: KotlinToolchain.KotlincInvoker, - private val plugins: InternalCompilerPlugins, - ) { - private fun combine( - one: Throwable?, - two: Throwable?, - ): Throwable? { - return when { - one != null && two != null -> { - one.addSuppressed(two) - return one - } - one != null -> one - else -> two +@Inject +internal constructor( + private val compiler: KotlinToolchain.KotlincInvoker, + private val plugins: InternalCompilerPlugins, +) { + private fun combine( + one: Throwable?, + two: Throwable?, + ): Throwable? { + return when { + one != null && two != null -> { + one.addSuppressed(two) + return one } + one != null -> one + else -> two } + } - fun execute( - context: CompilationTaskContext, - task: JvmCompilationTask, - ) { - val preprocessedTask = - task - .preProcessingSteps(context) - .runPlugins(context, plugins, compiler) + fun execute( + context: CompilationTaskContext, + task: JvmCompilationTask, + ) { + val preprocessedTask = + task + .preProcessingSteps(context) + .runPlugins(context, plugins, compiler) - context.execute("compile classes") { - preprocessedTask.apply { - sequenceOf( - runCatching { - context.execute("kotlinc") { - if (compileKotlin) { - compileKotlin( - context, - compiler, - args = - baseArgs() - .given(outputs.jdeps) - .notEmpty { - plugin(plugins.jdeps) { - flag("output", outputs.jdeps) - flag("target_label", info.label) - inputs.directDependenciesList.forEach { - flag("direct_dependencies", it) - } - flag("strict_kotlin_deps", info.strictKotlinDeps) - } - }.given(outputs.jar) - .notEmpty { - append(codeGenArgs()) - }.given(outputs.abijar) - .notEmpty { - plugin(plugins.jvmAbiGen) { - flag("outputDir", directories.abiClasses) - } - given(outputs.jar).empty { - plugin(plugins.skipCodeGen) - } - }, - printOnFail = false, - ) - } else { - emptyList() - } + context.execute("compile classes") { + preprocessedTask.apply { + sequenceOf( + runCatching { + context.execute("kotlinc") { + if (compileKotlin) { + compileKotlin( + context, + compiler, + args = + baseArgs() + .given(outputs.jdeps) + .notEmpty { + plugin(plugins.jdeps) { + flag("output", outputs.jdeps) + flag("target_label", info.label) + inputs.directDependenciesList.forEach { + flag("direct_dependencies", it) + } + flag("strict_kotlin_deps", info.strictKotlinDeps) + } + }.given(outputs.jar) + .notEmpty { + append(codeGenArgs()) + }.given(outputs.abijar) + .notEmpty { + plugin(plugins.jvmAbiGen) { + flag("outputDir", directories.abiClasses) + } + given(outputs.jar).empty { + plugin(plugins.skipCodeGen) + } + }, + printOnFail = false, + ) + } else { + emptyList() } - }, - ).map { - (it.getOrNull() ?: emptyList()) to it.exceptionOrNull() - }.map { - when (it.second) { - // TODO(issue/296): remove when the CompilationStatusException is unified. - is CompilationStatusException -> - (it.second as CompilationStatusException).lines + it.first to it.second - else -> it - } - }.fold(Pair, Throwable?>(emptyList(), null)) { acc, result -> - acc.first + result.first to combine(acc.second, result.second) - }.apply { - first.apply(context::printCompilerOutput) - second?.let { - throw it } + }, + ).map { + (it.getOrNull() ?: emptyList()) to it.exceptionOrNull() + }.map { + when (it.second) { + // TODO(issue/296): remove when the CompilationStatusException is unified. + is CompilationStatusException -> + (it.second as CompilationStatusException).lines + it.first to it.second + else -> it } - - if (outputs.jar.isNotEmpty()) { - if (instrumentCoverage) { - context.execute("create instrumented jar", ::createCoverageInstrumentedJar) - } else { - context.execute("create jar", ::createOutputJar) - } - } - if (outputs.abijar.isNotEmpty()) { - context.execute("create abi jar", ::createAbiJar) - } - if (outputs.generatedJavaSrcJar.isNotEmpty()) { - context.execute("creating KAPT generated Java source jar", ::createGeneratedJavaSrcJar) + }.fold(Pair, Throwable?>(emptyList(), null)) { acc, result -> + acc.first + result.first to combine(acc.second, result.second) + }.apply { + first.apply(context::printCompilerOutput) + second?.let { + throw it } - if (outputs.generatedJavaStubJar.isNotEmpty()) { - context.execute("creating KAPT generated Kotlin stubs jar", ::createGeneratedStubJar) - } - if (outputs.generatedClassJar.isNotEmpty()) { - context.execute("creating KAPT generated stub class jar", ::createGeneratedClassJar) - } - if (outputs.generatedKspSrcJar.isNotEmpty()) { - context.execute("creating KSP generated src jar", ::createGeneratedKspKotlinSrcJar) + } + + if (outputs.jar.isNotEmpty()) { + if (instrumentCoverage) { + context.execute("create instrumented jar", ::createCoverageInstrumentedJar) + } else { + context.execute("create jar", ::createOutputJar) } } + if (outputs.abijar.isNotEmpty()) { + context.execute("create abi jar", ::createAbiJar) + } + if (outputs.generatedJavaSrcJar.isNotEmpty()) { + context.execute("creating KAPT generated Java source jar", ::createGeneratedJavaSrcJar) + } + if (outputs.generatedJavaStubJar.isNotEmpty()) { + context.execute("creating KAPT generated Kotlin stubs jar", ::createGeneratedStubJar) + } + if (outputs.generatedClassJar.isNotEmpty()) { + context.execute("creating KAPT generated stub class jar", ::createGeneratedClassJar) + } + if (outputs.generatedKspSrcJar.isNotEmpty()) { + context.execute("creating KSP generated src jar", ::createGeneratedKspKotlinSrcJar) + } } } } +} diff --git a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt index 5f0d0f1af..0b29a9ff0 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt @@ -157,35 +157,35 @@ class KotlinToolchain private constructor( kspSymbolProcessingCommandLine, ), jvmAbiGen = - CompilerPlugin( - jvmAbiGenFile.path, - "org.jetbrains.kotlin.jvm.abi", - ), + CompilerPlugin( + jvmAbiGenFile.path, + "org.jetbrains.kotlin.jvm.abi", + ), skipCodeGen = - CompilerPlugin( - skipCodeGenFile.path, - "io.bazel.kotlin.plugin.SkipCodeGen", - ), + CompilerPlugin( + skipCodeGenFile.path, + "io.bazel.kotlin.plugin.SkipCodeGen", + ), jdepsGen = - CompilerPlugin( - jdepsGenFile.path, - "io.bazel.kotlin.plugin.jdeps.JDepsGen", - ), + CompilerPlugin( + jdepsGenFile.path, + "io.bazel.kotlin.plugin.jdeps.JDepsGen", + ), kapt3Plugin = - CompilerPlugin( - kaptFile.path, - "org.jetbrains.kotlin.kapt3", - ), + CompilerPlugin( + kaptFile.path, + "org.jetbrains.kotlin.kapt3", + ), kspSymbolProcessingApi = - CompilerPlugin( - kspSymbolProcessingApi.absolutePath, - "com.google.devtools.ksp.symbol-processing", - ), + CompilerPlugin( + kspSymbolProcessingApi.absolutePath, + "com.google.devtools.ksp.symbol-processing", + ), kspSymbolProcessingCommandLine = - CompilerPlugin( - kspSymbolProcessingCommandLine.absolutePath, - "com.google.devtools.ksp.symbol-processing", - ), + CompilerPlugin( + kspSymbolProcessingCommandLine.absolutePath, + "com.google.devtools.ksp.symbol-processing", + ), ) } @@ -267,11 +267,11 @@ class KotlinToolchain private constructor( @Singleton class KotlincInvoker - @Inject - constructor( - toolchain: KotlinToolchain, - ) : KotlinCliToolInvoker( - toolchain.toolchainWithReflect(), - "io.bazel.kotlin.compiler.BazelK2JVMCompiler", - ) + @Inject + constructor( + toolchain: KotlinToolchain, + ) : KotlinCliToolInvoker( + toolchain.toolchainWithReflect(), + "io.bazel.kotlin.compiler.BazelK2JVMCompiler", + ) } diff --git a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenExtension.kt b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenExtension.kt index ecbf61b59..c1bfb4be6 100644 --- a/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenExtension.kt +++ b/src/main/kotlin/io/bazel/kotlin/plugin/jdeps/JdepsGenExtension.kt @@ -185,7 +185,7 @@ class JdepsGenExtension( ( resultingDescriptor.getter ?.correspondingProperty as? SyntheticJavaPropertyDescriptor - )?.let { syntheticJavaPropertyDescriptor -> + )?.let { syntheticJavaPropertyDescriptor -> collectTypeReferences(syntheticJavaPropertyDescriptor.type, isExplicit = false) val functionDescriptor = syntheticJavaPropertyDescriptor.getMethod