[KTIJ-25227] Use CompilerArgumentsAware to resolve CompilerArguments for KGP < 1.9

This commit will take care of removing the classpath of the Compile Task
from the resolved CompilerArguments as this classpath is undesired and can
take too much memory. JPS builds will restore the classpath from
the IJ project model. Classpath entries added as freeCompilerArgs are still
relevant.

This commit also ensures that the jvmTarget is present in the
List of argument Strings for older KGP plugins that omitted 1.6 targets
as it was their default value.

KTIJ-25227

GitOrigin-RevId: 721dee6ee0f490b7c06c5fd6af5e777cf5760466
This commit is contained in:
Sebastian Sellmair
2023-04-17 19:40:55 +02:00
committed by intellij-monorepo-bot
parent 72bde52b9b
commit 660b16d4ff
13 changed files with 160 additions and 61 deletions

View File

@@ -193,8 +193,8 @@ fun applyCompilerArgumentsToFacet(
K2NativeCompilerArguments::shortModuleName.name,
K2NativeCompilerArguments::noendorsedlibs.name,
K2JSCompilerArguments::main.name,
K2JSCompilerArguments::metaInfo.name,
K2JSCompilerArguments::outputFile.name,
)
fun exposeAsAdditionalArgument(property: KProperty1<CommonCompilerArguments, Any?>) =

View File

@@ -347,9 +347,6 @@ fun configureFacetWithCompilerArguments(
compilerArguments: CommonCompilerArguments,
) {
applyCompilerArgumentsToFacet(compilerArguments, kotlinFacet, modelsProvider)
if (compilerArguments is K2JVMCompilerArguments) run {
adjustClasspath(kotlinFacet, compilerArguments.classpathArray?.toSet() ?: return@run)
}
}
private fun getAdditionalVisibleModuleNames(moduleNode: DataNode<ModuleData>, sourceSetName: String): Set<String> {
@@ -364,12 +361,3 @@ private fun getAdditionalVisibleModuleNames(moduleNode: DataNode<ModuleData>, so
}.map { it.id }
.toSet()
}
internal fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: Set<String>) {
if (dependencyClasspath.isEmpty()) return
val arguments = kotlinFacet.configuration.settings.compilerArguments as? K2JVMCompilerArguments ?: return
val fullClasspath = arguments.classpathArray.orEmpty()
if (fullClasspath.isEmpty()) return
val newClasspath = fullClasspath.toSet() - dependencyClasspath.toSet()
arguments.classpathArray = if (newClasspath.isNotEmpty()) newClasspath.toTypedArray() else null
}

View File

@@ -70,10 +70,7 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
assertFalse(compilerArguments!!.autoAdvanceApiVersion)
assertEquals(JvmPlatforms.jvm8, targetPlatform)
assertEquals("1.7", (compilerArguments as K2JVMCompilerArguments).jvmTarget)
assertEquals(
"-Xallow-no-source-files -Xdump-declarations-to=tmp",
compilerSettings!!.additionalArguments
)
assertEquals("-Xdump-declarations-to=tmp", compilerSettings!!.additionalArguments)
}
with(testFacetSettings) {
@@ -84,7 +81,7 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
assertEquals(JvmPlatforms.jvm6, targetPlatform)
assertEquals("1.6", (compilerArguments as K2JVMCompilerArguments).jvmTarget)
assertEquals(
"-Xallow-no-source-files -Xdump-declarations-to=tmpTest",
"-Xdump-declarations-to=tmpTest",
compilerSettings!!.additionalArguments
)
}
@@ -130,10 +127,7 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
assertEquals("1.3", apiLevel!!.versionString)
assertEquals(JvmPlatforms.jvm8, targetPlatform)
assertEquals("1.7", (compilerArguments as K2JVMCompilerArguments).jvmTarget)
assertEquals(
"-Xallow-no-source-files -Xdump-declarations-to=tmp",
compilerSettings!!.additionalArguments
)
assertEquals("-Xdump-declarations-to=tmp", compilerSettings!!.additionalArguments)
}
with(facetSettings("project.myTest")) {
@@ -141,10 +135,7 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
assertEquals("1.0", apiLevel!!.versionString)
assertEquals(JvmPlatforms.jvm6, targetPlatform)
assertEquals("1.6", (compilerArguments as K2JVMCompilerArguments).jvmTarget)
assertEquals(
"-Xallow-no-source-files -Xdump-declarations-to=tmpTest",
compilerSettings!!.additionalArguments
)
assertEquals("-Xdump-declarations-to=tmpTest", compilerSettings!!.additionalArguments)
}
assertAllModulesConfigured()
@@ -522,7 +513,7 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
with(facetSettings) {
assertEquals(
listOf("-Xallow-no-source-files", "-Xbuild-file=module with spaces"),
listOf("-Xbuild-file=module with spaces"),
compilerSettings!!.additionalArgumentsAsList
)
}
@@ -639,9 +630,9 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
fun testJDKImport() {
val mockJdkPath = FileUtil.toSystemDependentName("${PathManager.getHomePath()}/community/java/mockJDK-1.8")
runWriteActionAndWait {
val jdk = JavaSdk.getInstance().createJdk("myJDK", mockJdkPath)
runReadAction<ProjectJdkTable> { ProjectJdkTable.getInstance() }.addJdk(jdk)
ProjectRootManager.getInstance(myProject).projectSdk = jdk
val jdk = JavaSdk.getInstance().createJdk("myJDK", mockJdkPath)
runReadAction<ProjectJdkTable> { ProjectJdkTable.getInstance() }.addJdk(jdk)
ProjectRootManager.getInstance(myProject).projectSdk = jdk
}
try {
@@ -654,9 +645,9 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
assertEquals(mockJdkPath, moduleSDK.homePath?.let(FileUtil::toSystemDependentName))
} finally {
runWriteActionAndWait {
val jdkTable = runReadAction<ProjectJdkTable> { ProjectJdkTable.getInstance() }
jdkTable.removeJdk(jdkTable.findJdk("myJDK")!!)
ProjectRootManager.getInstance(myProject).projectSdk = null
val jdkTable = runReadAction<ProjectJdkTable> { ProjectJdkTable.getInstance() }
jdkTable.removeJdk(jdkTable.findJdk("myJDK")!!)
ProjectRootManager.getInstance(myProject).projectSdk = null
}
}
}
@@ -775,7 +766,7 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
assertEquals(LanguageVersion.KOTLIN_1_3, facetSettings.languageLevel)
// We haven't lost internal argument during importing to facet
assertEquals("-Xallow-no-source-files -XXLanguage:+InlineClasses", facetSettings.compilerSettings?.additionalArguments)
assertEquals("-XXLanguage:+InlineClasses", facetSettings.compilerSettings?.additionalArguments)
// Inline classes are enabled even though LV = 1.3
assertEquals(
@@ -814,11 +805,7 @@ class GradleFacetImportTest8 : KotlinGradleImportingTestCase() {
configureByFiles()
importProject()
assertEquals(
"-Xallow-no-source-files",
testFacetSettings.compilerSettings!!.additionalArguments
)
assertEquals("", testFacetSettings.compilerSettings!!.additionalArguments)
assertAllModulesConfigured()
}

View File

@@ -9,18 +9,18 @@ fun KotlinCompileTaskReflection(task: Task): KotlinCompileTaskReflection {
interface KotlinCompileTaskReflection {
val task: Task
val legacyCompilerArguments: List<String>
val serializedCompilerArguments: List<String>
}
private class KotlinCompileTaskReflectionImpl(override val task: Task) : KotlinCompileTaskReflection {
override val legacyCompilerArguments: List<String>
override val serializedCompilerArguments: List<String>
get() = runCatching {
task.callReflectiveGetter<List<String>>("getSerializedCompilerArguments", logger)
}.recoverCatching {
logger.logIssue("Failed calling 'getSerializedCompilerArguments'", it)
task.callReflectiveGetter<List<String>>("getSerializedCompilerArgumentsIgnoreClasspathIssues", logger)
}.onFailure {
logger.logIssue("Failed getting 'legacyCompilerArguments' from '${task.path}'")
logger.logIssue("Failed getting 'serializedCompilerArguments' from '${task.path}'")
}.getOrNull().orEmpty()

View File

@@ -4,13 +4,135 @@ package org.jetbrains.kotlin.idea.gradleTooling
import org.gradle.api.Task
import org.jetbrains.kotlin.idea.gradleTooling.reflect.KotlinCompileTaskReflection
import org.jetbrains.kotlin.idea.gradleTooling.reflect.KotlinCompilerArgumentsResolverReflection
import org.jetbrains.kotlin.idea.gradleTooling.reflect.ReflectionLogger
import java.lang.reflect.Method
internal fun resolveCompilerArguments(compileTask: Task): List<String>? {
/* Preferred approach in Kotlin 1.9+ */
KotlinCompilerArgumentsResolverReflection(compileTask.project, compileTask::class.java.classLoader)?.let { resolver ->
return resolver.resolveCompilerArguments(compileTask)
/* Service bundled inside Kotlin Gradle Plugin */
val compilerArgumentsResolver = KotlinCompilerArgumentsResolverReflection(compileTask.project, compileTask::class.java.classLoader)
return when {
/*
Preferred approach in Kotlin 1.9+:
Using a service inside KGP that is capable of resolving the arguments properly
*/
compilerArgumentsResolver != null -> compilerArgumentsResolver.resolveCompilerArguments(compileTask)
/*
Fallback for Kotlin Gradle Plugins < 1.9:
Using the 'CompilerArgumentsAware' infrastructure in KGP, calling into it via reflection and
fixing up known issues in KGP reflectively.
Native Compile Tasks however do only support requesting the arguments in serialised format.
Other compile tasks will allow the createCompilerArgs + setupCompilerArgs approach
*/
compileTask.isKotlinNativeCompileTask -> resolveCompilerArgumentsForKGPLower19ForNativeCompileTask(compileTask)
else -> resolveCompilerArgumentsForKGPLower19(compileTask)
}
}
fun resolveCompilerArgumentsForKGPLower19ForNativeCompileTask(compileTask: Task): List<String> {
/*
Resolve arguments using 'serializedCompilerArguments' as native tasks do not support createCompilerArgs&setupCompilerArgs methods
*/
return KotlinCompileTaskReflection(compileTask).serializedCompilerArguments
}
fun resolveCompilerArgumentsForKGPLower19(compileTask: Task): List<String>? {
val logger = ReflectionLogger(compileTask.javaClass)
/*
Create compiler arguments by using the CompilerArgumentsAware methods
(createCompilerArgs followed by setupCompilerArgs)
*/
val compileTaskClass = compileTask.javaClass
val compilerArguments = compileTask["createCompilerArgs"] ?: run {
logger.logIssue("Failed creating compiler arguments from ${compileTask.path}")
return null
}
/* Less preferred approach w/o IdeCompilerArgumentsResolver on KGP */
return KotlinCompileTaskReflection(compileTask).legacyCompilerArguments
val setupCompilerArgsMethod = compileTaskClass.getMethodOrNull(
"setupCompilerArgs", compilerArguments::class.java, Boolean::class.java, Boolean::class.java
) ?: run {
logger.logIssue("No 'setupCompilerArgs' method found on '${compileTask.path}'")
return null
}
setupCompilerArgsMethod.doSetupCompilerArgs(compileTask, compilerArguments)
/*
Previous Kotlin Gradle Plugin versions (<1.9) are resolving the classpath of the compile task, adding it
directly to the arguments .classpath property. This classpath is undesired as it potentially takes too much memory.
JPS builds will be able to infer the classpath from the IJ project model.
We therefore will remove the classpath manually from the received compiler arguments and then convert the arguments
to their List<String> representation.
*/
val compilerArgumentsClass = compilerArguments.javaClass
compilerArgumentsClass.getMethodOrNull("setClasspath", String::class.java)?.invoke(compilerArguments, null)
/*
Use the 'ArgumentUtils' list bundled within the Kotlin Gradle Plugin to convert
the arguments to List<String>
*/
return runCatching {
val commonToolArgumentsClass = compileTaskClass.classLoader.loadClass(
"org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments"
)
val argumentUtilsClass = compileTaskClass.classLoader.loadClass(
"org.jetbrains.kotlin.compilerRunner.ArgumentUtils"
)
val toStringListFunction = argumentUtilsClass.getMethodOrNull("convertArgumentsToStringList", commonToolArgumentsClass) ?: run {
logger.logIssue("Missing 'convertArgumentsToStringList' function")
return null
}
/*
Kotlin Gradle Plugin versions before:
```
Switch `-jvm-target` default to null Mikhael Bogdanov* 14.11.21, 14:23
f5da166d7c5efe34541bee0cf207f6b394aca5e5
```
Did set a default jvmTarget value, basically omitting the argument on String conversion (since it's the default value)
This however is undesirable, since there are Kotlin Gradle Plugins build with jvmTarget=1.6 as default.
When omitting the String argument, then the IDE will fill the absent argument with its default (which might be 1.8 or higher)
Therefore, we detect this Situation and support older Kotlin Gradle Plugins by explicitly adding this argument again.
*/
val additionalExplicitArguments: List<String> = runCatching resolveExplicitArguments@{
val emptyArgumentsInstance = compilerArgumentsClass.getConstructor().newInstance()
val getJvmTargetMethod = compilerArgumentsClass.getMethodOrNull("getJvmTarget") ?: return@resolveExplicitArguments emptyList()
val jvmTarget = getJvmTargetMethod.invoke(compilerArguments)
val jvmTargetDefault = getJvmTargetMethod.invoke(emptyArgumentsInstance)
if (jvmTargetDefault != null && jvmTargetDefault == jvmTarget) {
listOf("-jvm-target", jvmTarget.toString())
} else emptyList()
}.getOrNull().orEmpty()
@Suppress("UNCHECKED_CAST")
val argumentsAsStrings = (toStringListFunction.invoke(null, compilerArguments) as? List<String>) ?: return null
additionalExplicitArguments + argumentsAsStrings
}
.onFailure { failure -> logger.logIssue("Failed converting $compilerArguments", failure) }
.getOrNull()
}
private fun Method.doSetupCompilerArgs(compileTask: Task, compilerArgs: Any) {
runCatching {
invoke(compileTask, compilerArgs, /*defaults only */ false, /* ignore classpath issues */ false)
}.recoverCatching {
invoke(compileTask, compilerArgs, /* defaults only */ false, /* ignore classpath issues */ true)
}
}
private const val KOTLIN_NATIVE_COMPILE_CLASS = "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile"
private val Task.isKotlinNativeCompileTask: Boolean
get() = javaClass.classLoader.loadClassOrNull(KOTLIN_NATIVE_COMPILE_CLASS)?.isAssignableFrom(javaClass) ?: false

View File

@@ -53,12 +53,12 @@ project.iosX64Test
project.jsMain
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation
compilerSettings = -main call -opt-in OptInAnnotation
project.jsTest
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation
compilerSettings = -main call -opt-in OptInAnnotation
project.jvmAndroidMain
languageLevel = {{LATEST_STABLE}}

View File

@@ -53,12 +53,12 @@ project.iosX64Test
project.jsMain
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation
compilerSettings = -main call -opt-in OptInAnnotation
project.jsTest
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation
compilerSettings = -main call -opt-in OptInAnnotation
project.jvmAndroidMain
languageLevel = {{LATEST_STABLE}}

View File

@@ -53,12 +53,12 @@ project.iosX64Test
project.jsMain
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation
compilerSettings = -main call -opt-in OptInAnnotation
project.jsTest
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation
compilerSettings = -main call -opt-in OptInAnnotation
project.jvmAndroidMain
languageLevel = {{LATEST_STABLE}}

View File

@@ -53,12 +53,12 @@ project.iosX64Test
project.jsMain
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation
compilerSettings = -main call -opt-in OptInAnnotation
project.jsTest
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation
compilerSettings = -main call -opt-in OptInAnnotation
project.jvmAndroidMain
languageLevel = {{LATEST_STABLE}}

View File

@@ -59,12 +59,12 @@ project.iosX64Test
project.jsMain
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation -progressive
compilerSettings = -main call -opt-in OptInAnnotation -progressive
project.jsTest
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in OptInAnnotation -progressive
compilerSettings = -main call -opt-in OptInAnnotation -progressive
project.jvmAndroidMain
languageLevel = 1.7

View File

@@ -59,12 +59,12 @@ project.iosX64Test
project.jsMain
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in LangSettingsOptInAnnotation,CompilationOptInAnnotation -progressive
compilerSettings = -main call -opt-in LangSettingsOptInAnnotation,CompilationOptInAnnotation -progressive
project.jsTest
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in LangSettingsOptInAnnotation,CompilationOptInAnnotation -progressive
compilerSettings = -main call -opt-in LangSettingsOptInAnnotation,CompilationOptInAnnotation -progressive
project.jvmAndroidMain
languageLevel = 1.8

View File

@@ -53,12 +53,12 @@ project.iosX64Test
project.jsMain
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in CompilationOptInAnnotation
compilerSettings = -main call -opt-in CompilationOptInAnnotation
project.jsTest
languageLevel = 1.7
apiLevel = 1.7
compilerSettings = -opt-in CompilationOptInAnnotation
compilerSettings = -main call -opt-in CompilationOptInAnnotation
project.jvmAndroidMain
languageLevel = {{LATEST_STABLE}}

View File

@@ -46,10 +46,12 @@ project.iosX64Test
project.jsMain
languageLevel = {{LATEST_STABLE}}
apiLevel = {{LATEST_STABLE}}
compilerSettings = -main call
project.jsTest
languageLevel = {{LATEST_STABLE}}
apiLevel = {{LATEST_STABLE}}
compilerSettings = -main call
project.jvmMain
languageLevel = 1.7