From 0727ee5a4f8e532ab728b30d6c45290cf50dadd1 Mon Sep 17 00:00:00 2001 From: "Alexander.Likhachev" Date: Mon, 20 Mar 2023 22:40:27 +0100 Subject: [PATCH] [Gradle] Use custom classloader and ServiceLoader to load bt-api implementation #KT-57397 Fixed --- .../build-tools-api/api/build-tools-api.api | 12 ++++- .../buildtools/api/CompilationService.kt | 5 +++ .../api/SharedApiClassesClassLoader.kt | 44 +++++++++++++++++++ .../btapi/BuildToolsApiCompilationWork.kt | 17 ++++++- 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 compiler/build-tools/build-tools-api/src/main/kotlin/org/jetbrains/kotlin/buildtools/api/SharedApiClassesClassLoader.kt diff --git a/compiler/build-tools/build-tools-api/api/build-tools-api.api b/compiler/build-tools/build-tools-api/api/build-tools-api.api index bde4bd2063d..57bc109683e 100644 --- a/compiler/build-tools/build-tools-api/api/build-tools-api.api +++ b/compiler/build-tools/build-tools-api/api/build-tools-api.api @@ -1,4 +1,14 @@ -public abstract interface class org/jetbrains/kotlin/buildtools/api/CompilationFacade { +public abstract interface class org/jetbrains/kotlin/buildtools/api/CompilationService { + public static final field Companion Lorg/jetbrains/kotlin/buildtools/api/CompilationService$Companion; public abstract fun compile ()V + public static fun loadImplementation (Ljava/lang/ClassLoader;)Lorg/jetbrains/kotlin/buildtools/api/CompilationService; +} + +public final class org/jetbrains/kotlin/buildtools/api/CompilationService$Companion { + public final fun loadImplementation (Ljava/lang/ClassLoader;)Lorg/jetbrains/kotlin/buildtools/api/CompilationService; +} + +public final class org/jetbrains/kotlin/buildtools/api/SharedApiClassesClassLoader { + public static final fun newInstance ()Ljava/lang/ClassLoader; } diff --git a/compiler/build-tools/build-tools-api/src/main/kotlin/org/jetbrains/kotlin/buildtools/api/CompilationService.kt b/compiler/build-tools/build-tools-api/src/main/kotlin/org/jetbrains/kotlin/buildtools/api/CompilationService.kt index ed6da9c9fa3..93a8d15a50a 100644 --- a/compiler/build-tools/build-tools-api/src/main/kotlin/org/jetbrains/kotlin/buildtools/api/CompilationService.kt +++ b/compiler/build-tools/build-tools-api/src/main/kotlin/org/jetbrains/kotlin/buildtools/api/CompilationService.kt @@ -11,4 +11,9 @@ package org.jetbrains.kotlin.buildtools.api */ interface CompilationService { fun compile() + + companion object { + @JvmStatic + fun loadImplementation(classLoader: ClassLoader) = loadImplementation(CompilationService::class, classLoader) + } } \ No newline at end of file diff --git a/compiler/build-tools/build-tools-api/src/main/kotlin/org/jetbrains/kotlin/buildtools/api/SharedApiClassesClassLoader.kt b/compiler/build-tools/build-tools-api/src/main/kotlin/org/jetbrains/kotlin/buildtools/api/SharedApiClassesClassLoader.kt new file mode 100644 index 00000000000..4dca4917a34 --- /dev/null +++ b/compiler/build-tools/build-tools-api/src/main/kotlin/org/jetbrains/kotlin/buildtools/api/SharedApiClassesClassLoader.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +@file:JvmName("SharedApiClassesClassLoader") + +package org.jetbrains.kotlin.buildtools.api + +import java.util.* +import kotlin.reflect.KClass + +/** + * Creates a [ClassLoader] which reuses the API classes from the ClassLoader which loaded the API. + * This way an API implementation can be loaded with almost fully isolated classpath, sharing only the classes from `org.jetbrains.kotlin.buildtools.api`, + * so a caller still able to pass API parameters in a compatible way. + */ +@Suppress("FunctionName") +@JvmName("newInstance") +fun SharedApiClassesClassLoader(): ClassLoader = SharedApiClassesClassLoaderImpl( + SharedApiClassesClassLoaderImpl::class.java.classLoader, + ClassLoader.getSystemClassLoader(), + SharedApiClassesClassLoaderImpl::class.java.`package`.name, +) + +internal fun loadImplementation(cls: KClass, classLoader: ClassLoader): T { + val implementations = ServiceLoader.load(cls.java, classLoader) + implementations.firstOrNull() ?: error("The classpath contains no implementation for ${cls.qualifiedName}") + return implementations.singleOrNull() + ?: error("The classpath contains more than one implementation for ${cls.qualifiedName}") +} + +private class SharedApiClassesClassLoaderImpl( + private val parent: ClassLoader, + fallback: ClassLoader, + private val allowedPackage: String, +) : ClassLoader(fallback) { + override fun loadClass(name: String, resolve: Boolean): Class<*> { + return if (name.startsWith(allowedPackage)) { + parent.loadClass(name) + } else { + super.loadClass(name, resolve) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/btapi/BuildToolsApiCompilationWork.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/btapi/BuildToolsApiCompilationWork.kt index 50072440558..e110ffae0af 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/btapi/BuildToolsApiCompilationWork.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/btapi/BuildToolsApiCompilationWork.kt @@ -11,9 +11,13 @@ import org.gradle.api.provider.Property import org.gradle.workers.WorkAction import org.gradle.workers.WorkParameters import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter +import org.jetbrains.kotlin.buildtools.api.CompilationService +import org.jetbrains.kotlin.buildtools.api.SharedApiClassesClassLoader import org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWorkArguments import org.jetbrains.kotlin.gradle.internal.ClassLoadersCachingBuildService +import org.jetbrains.kotlin.gradle.internal.ParentClassLoaderProvider import java.io.File +import java.util.* internal abstract class BuildToolsApiCompilationWork : WorkAction { internal interface BuildToolsApiCompilationParameters : WorkParameters { @@ -29,6 +33,17 @@ internal abstract class BuildToolsApiCompilationWork : WorkAction