[Gradle] Implement MppCompilerOptionsDslIT

^KT-61636 Verification Pending
^KT-61355 Verification Pending
^KT-61368 Verification Pending
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppCompilerOptionsDslIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppCompilerOptionsDslIT.kt
new file mode 100644
index 0000000..519b61b
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppCompilerOptionsDslIT.kt
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+package org.jetbrains.kotlin.gradle.mpp
+
+import org.gradle.util.GradleVersion
+import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
+import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
+import org.jetbrains.kotlin.cli.common.arguments.K2NativeCompilerArguments
+import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
+import org.jetbrains.kotlin.gradle.mpp.MppCompilerOptionsDslIT.TestUtils.buildCompilerArguments
+import org.jetbrains.kotlin.gradle.testbase.*
+import kotlin.io.path.exists
+import kotlin.io.path.readText
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+import kotlin.test.fail
+
+@MppGradlePluginTests
+@GradleTestVersions(maxVersion = TestVersions.Gradle.G_8_2)
+class MppCompilerOptionsDslIT : KGPBaseTest() {
+
+
+    /**
+     * Will configure `apiVersion` on three different levels.
+     * It is expected that target level configuration overwrites the top-level configuration
+     * It is expected that compilation level configuration overwrites the target level configuration.
+     */
+    @GradleTest
+    fun `test - top level - target level - compilation level`(gradleVersion: GradleVersion) {
+        project("mppCompilerOptionsDsl", gradleVersion) {
+            buildGradleKts.append(
+                """
+                kotlin {
+                    jvm()
+                    linuxArm64()
+                    linuxX64()
+                
+                    compilerOptions {
+                        apiVersion.set(KotlinVersion.KOTLIN_1_7)
+                    }
+                
+                    linuxX64 {
+                        compilerOptions {
+                            apiVersion.set(KotlinVersion.KOTLIN_1_8)
+                        }
+                    }
+                
+                    linuxX64().compilations.getByName("test") {
+                        compilerOptions {
+                            apiVersion.set(KotlinVersion.KOTLIN_1_9)
+                        }
+                    }
+                }
+                """.trimIndent()
+            )
+
+            val arguments = buildCompilerArguments()
+            val jvmMainArguments = arguments.get<K2JVMCompilerArguments>("jvm", "main")
+            assertEquals("1.7", jvmMainArguments.apiVersion)
+
+            val linuxX64MainArguments = arguments.get<K2NativeCompilerArguments>("linuxX64", "main")
+            assertEquals("1.8", linuxX64MainArguments.apiVersion)
+
+            val linuxX64TestArguments = arguments.get<K2NativeCompilerArguments>("linuxX64", "test")
+            assertEquals("1.9", linuxX64TestArguments.apiVersion)
+        }
+    }
+
+    /**
+     * See puzzler posted: https://youtrack.jetbrains.com/issue/KT-61636/Wrong-scope-of-compiler-options-could-be-used-in-the-build-script#focus=Comments-27-8127182.0-0
+     */
+    @GradleTest
+    fun `test - compilations all`(gradleVersion: GradleVersion) {
+        project("mppCompilerOptionsDsl", gradleVersion) {
+            buildGradleKts.append(
+                """
+                kotlin {
+                    jvm()
+                
+                    compilerOptions {
+                        apiVersion.set(KotlinVersion.KOTLIN_1_8)
+                    }
+                
+                    targets.all {
+                        compilations.all {
+                            if (compilationName == "test") {
+                                compilerOptions {
+                                    apiVersion.set(KotlinVersion.KOTLIN_1_9)
+                                }
+                            }
+                        }
+                    }
+                }
+            """.trimIndent()
+            )
+
+            val arguments = buildCompilerArguments()
+            val jvmMainArguments = arguments.get<K2JVMCompilerArguments>("jvm", "main")
+            assertEquals("1.8", jvmMainArguments.apiVersion)
+
+            val jvmTestArguments = arguments.get<K2JVMCompilerArguments>("jvm", "test")
+            assertEquals("1.9", jvmTestArguments.apiVersion)
+        }
+    }
+
+    @GradleTest
+    fun `test - KT-61368 - Native compiler option 'module-name' isn't available within the compilerOptions`(gradleVersion: GradleVersion) {
+        project("mppCompilerOptionsDsl", gradleVersion) {
+            buildGradleKts.append(
+                """
+                    kotlin {
+                        linuxArm64()
+                        linuxX64()
+                        
+                        linuxX64().compilations.all {
+                            compilerOptions {
+                                moduleName.set("foo") 
+                            }
+                        }
+                    }
+                """.trimIndent()
+            )
+
+            val arguments = buildCompilerArguments()
+            val linuxArm64MainArguments = arguments.get<K2NativeCompilerArguments>("linuxArm64", "main")
+            assertNotEquals("foo", linuxArm64MainArguments.moduleName)
+
+            val linuxX64MainArguments = arguments.get<K2NativeCompilerArguments>("linuxX64", "main")
+            assertEquals("foo", linuxX64MainArguments.moduleName)
+
+            val linuxX64TestArguments = arguments.get<K2JVMCompilerArguments>("linuxX64", "test")
+            assertEquals("foo", linuxX64TestArguments.moduleName)
+        }
+    }
+
+    @GradleTest
+    fun `test - KT-61355 - freeCompilerArgs`(gradleVersion: GradleVersion) {
+        project("mppCompilerOptionsDsl", gradleVersion) {
+            buildGradleKts.append(
+                """
+                kotlin {
+                    linuxX64()
+                
+                    jvm().compilations.all {
+                        compilerOptions {
+                            freeCompilerArgs.add("foo")
+                        }
+                    }
+                }
+                """.trimIndent()
+            )
+
+            val arguments = buildCompilerArguments()
+
+            val linuxX64MainArguments = arguments.get<K2NativeCompilerArguments>("linuxX64", "main")
+            assertEquals(emptyList(), linuxX64MainArguments.freeArgs)
+
+            val jvmMainArguments = arguments.get<K2JVMCompilerArguments>("jvm", "main")
+            assertEquals(listOf("foo"), jvmMainArguments.freeArgs)
+
+            val jvmTestArguments = arguments.get<K2JVMCompilerArguments>("jvm", "test")
+            assertEquals(listOf("foo"), jvmTestArguments.freeArgs)
+        }
+    }
+
+    @Suppress("IdentifierGrammar")
+    private object TestUtils {
+        class CompilerArgumentsContainer(val project: TestProject) {
+            inline fun <reified T : CommonCompilerArguments> get(target: String, compilation: String): T {
+                val argumentsFile = project.projectPath.resolve("build/args/$target-$compilation.args")
+                if (!argumentsFile.exists()) fail("Missing arguments for target $target and compilation $compilation")
+                return parseCommandLineArguments<T>(argumentsFile.readText().lines())
+            }
+        }
+
+        fun TestProject.buildCompilerArguments(): CompilerArgumentsContainer {
+            build("buildCompilerArguments")
+            return CompilerArgumentsContainer(this)
+        }
+    }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/mppCompilerOptionsDsl/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/mppCompilerOptionsDsl/build.gradle.kts
new file mode 100644
index 0000000..847fa03
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/mppCompilerOptionsDsl/build.gradle.kts
@@ -0,0 +1,30 @@
+import org.jetbrains.kotlin.compilerRunner.toArgumentStrings
+import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi
+import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
+import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer
+
+
+plugins {
+    kotlin("multiplatform")
+}
+
+
+kotlin {
+    tasks.create("buildCompilerArguments") {
+        doFirst {
+            targets.all {
+                compilations.all {
+                    @OptIn(InternalKotlinGradlePluginApi::class)
+                    val arguments = (compileTaskProvider.get() as KotlinCompilerArgumentsProducer)
+                        .createCompilerArguments(KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext.lenient)
+                        .toArgumentStrings()
+
+                    buildDir.resolve("args/${target.name}-$compilationName.args")
+                        .also { it.parentFile.mkdirs() }
+                        .writeText(arguments.joinToString("\n"))
+                }
+            }
+        }
+    }
+}
+