From 30a4911a51a968d08d4da1b505c6a8ff34127333 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Tue, 21 Mar 2023 13:31:44 +0100 Subject: [PATCH] [Gradle] KotlinNativeCompile: Implement KotlinCompilerArgumentsProducer KTIJ-24976 --- .../targets/native/tasks/KotlinNativeTasks.kt | 82 ++++++++++++++++++- .../KotlinNativeCompileArgumentsTest.kt | 77 +++++++++++++++++ 2 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/compilerArgumetns/KotlinNativeCompileArgumentsTest.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt index c0a75e16964..4d4287e7644 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt @@ -30,6 +30,9 @@ import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import org.jetbrains.kotlin.gradle.internal.isInIdeaSync import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationInfo +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext.Companion.create import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmMetadataCompilationData @@ -52,6 +55,7 @@ import org.jetbrains.kotlin.library.* import org.jetbrains.kotlin.project.model.LanguageSettings import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics import org.jetbrains.kotlin.statistics.metrics.StringMetrics +import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly import java.io.File import java.nio.file.Files import javax.inject.Inject @@ -319,6 +323,7 @@ internal constructor( private val execOperations: ExecOperations ) : AbstractKotlinNativeCompile(objectFactory), KotlinCompile, + KotlinCompilerArgumentsProducer, K2MultiplatformCompilationTask, KotlinCompilationTask { @@ -450,6 +455,72 @@ internal constructor( ) } + override fun createCompilerArguments(context: CreateCompilerArgumentsContext) = context.create { + val sharedCompilationData = createSharedCompilationDataOrNull() + + val compilerPlugins = listOfNotNull( + compilerPluginClasspath?.let { CompilerPluginData(it, compilerPluginOptions) }, + kotlinPluginData?.orNull?.let { CompilerPluginData(it.classpath, it.options) } + ) + + contribute(KotlinCompilerArgumentsProducer.ArgumentType.Primitive) { args -> + args.moduleName = compilerOptions.moduleName.get() + args.shortModuleName = shortModuleName + args.multiPlatform = true + args.noendorsedlibs = true + args.outputName = outputFile.get().absolutePath + args.optimization = optimized + args.debug = debuggable + args.enableAssertions = debuggable + args.target = konanTarget.name + args.produce = outputKind.name.toLowerCaseAsciiOnly() + args.expectActualLinker = sharedCompilationData != null + args.metadataKlib = sharedCompilationData != null + args.nodefaultlibs = sharedCompilationData != null + args.manifestFile = sharedCompilationData?.manifestFile?.absolutePath + + args.pluginOptions = compilerPlugins.flatMap { it.options.arguments }.toTypedArray() + + if (compilerOptions.usesK2.get()) { + args.fragments = multiplatformStructure.fragmentsCompilerArgs + args.fragmentRefines = multiplatformStructure.fragmentRefinesCompilerArgs + } + + KotlinNativeCompilerOptionsHelper.fillCompilerArguments(compilerOptions, args) + } + + contribute(KotlinCompilerArgumentsProducer.ArgumentType.PluginClasspath) { args -> + args.pluginClasspaths = compilerPlugins.flatMap { classpath -> tryLenient { classpath.files } ?: emptySet() }.toPathsArray() + } + + contribute(KotlinCompilerArgumentsProducer.ArgumentType.DependencyClasspath) { args -> + args.libraries = tryLenient { libraries.files.filterKlibsPassedToCompiler().toPathsArray() } + args.friendModules = tryLenient { + friendModule.files.takeIf { it.isNotEmpty() }?.map { it.absolutePath }?.joinToString(File.pathSeparator) + } + args.refinesPaths = tryLenient { + sharedCompilationData?.refinesPaths?.files?.takeIf { it.isNotEmpty() }?.toPathsArray() + } + } + + contribute(KotlinCompilerArgumentsProducer.ArgumentType.Sources) { args -> + if (compilerOptions.usesK2.get()) { + /* + For now, we only pass multiplatform structure to K2 for platform compilations + Metadata compilations will compile against pre-compiled klibs from their dependsOn + */ + if (sharedCompilationData == null) { + args.fragmentSources = multiplatformStructure.fragmentSourcesCompilerArgs + } + } else { + args.commonSources = commonSourcesTree.files.takeIf { it.isNotEmpty() }?.toPathsArray() + } + + args.freeArgs += sources.asFileTree.map { it.absolutePath } + } + } + + private val isMetadataCompilation: Boolean = when (compilation) { is KotlinCompilationInfo.KPM -> compilation.compilationData is GradleKpmMetadataCompilationData<*> is KotlinCompilationInfo.TCS -> compilation.compilation is KotlinMetadataCompilation<*> @@ -467,12 +538,14 @@ internal constructor( return SharedCompilationData(manifestFile, isAllowCommonizer, refinesModule) } - internal fun buildCompilerArgs(): List = buildKotlinNativeKlibCompilerArgs( + internal fun buildCompilerArgs(isLenient: Boolean = false): List = buildKotlinNativeKlibCompilerArgs( outFile = outputFile.get(), optimized = optimized, debuggable = debuggable, target = konanTarget, - libraries = libraries.files.filterKlibsPassedToCompiler(), + libraries = runCatching { libraries.files.filterKlibsPassedToCompiler() }.getOrElse { + if (isLenient) emptyList() else throw it + }, languageSettings = languageSettings, compilerOptions = compilerOptions, compilerPlugins = listOfNotNull( @@ -494,12 +567,13 @@ internal constructor( output.parentFile.mkdirs() collectCommonCompilerStats() - val buildArgs = buildCompilerArgs() + val arguments = createCompilerArguments() + val buildArguments = ArgumentUtils.convertArgumentsToStringList(arguments) KotlinNativeCompilerRunner( settings = runnerSettings, executionContext = KotlinToolRunner.GradleExecutionContext.fromTaskContext(objectFactory, execOperations, logger) - ).run(buildArgs) + ).run(buildArguments) } private fun collectCommonCompilerStats() { diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/compilerArgumetns/KotlinNativeCompileArgumentsTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/compilerArgumetns/KotlinNativeCompileArgumentsTest.kt new file mode 100644 index 00000000000..ca4825a29aa --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/compilerArgumetns/KotlinNativeCompileArgumentsTest.kt @@ -0,0 +1,77 @@ +/* + * 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:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle.unitTests.compilerArgumetns + +import org.jetbrains.kotlin.cli.common.arguments.K2NativeCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments +import org.jetbrains.kotlin.compilerRunner.ArgumentUtils +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext.Companion.default +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext.Companion.lenient +import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile +import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP +import org.jetbrains.kotlin.gradle.util.main +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFails +import kotlin.test.assertNull + +class KotlinNativeCompileArgumentsTest { + @Test + fun `test - simple project - old buildCompilerArgs and new CompilerArgumentsProducer - return same arguments`() { + val project = buildProjectWithMPP() + project.repositories.mavenLocal() + + val kotlin = project.multiplatformExtension + + kotlin.linuxArm64() + val linuxX64Target = kotlin.linuxX64() + + project.evaluate() + + /* Check linuxX64 main compilation as 'native platform compilation' */ + run { + val linuxX64MainCompilation = linuxX64Target.compilations.main + val linuxX64MainCompileTask = linuxX64MainCompilation.compileTaskProvider.get() + `assert buildCompilerArgs and createCompilerArgs are equal`(linuxX64MainCompileTask) + } + + /* Check commonMain compilation as 'shared native compilation' */ + run { + val commonMainCompilation = kotlin.metadata().compilations.getByName("commonMain") + val commonMainCompileTask = commonMainCompilation.compileTaskProvider.get() as KotlinNativeCompile + `assert buildCompilerArgs and createCompilerArgs are equal`(commonMainCompileTask) + } + } + + + private fun `assert buildCompilerArgs and createCompilerArgs are equal`(compile: KotlinNativeCompile) { + val argumentsFromCompilerArgumentsProducer = compile.createCompilerArguments(lenient) + val argumentsFromBuildCompilerArgs = K2NativeCompilerArguments().apply { + parseCommandLineArguments(compile.buildCompilerArgs(true), this) + } + + assertEquals( + ArgumentUtils.convertArgumentsToStringList(argumentsFromBuildCompilerArgs), + ArgumentUtils.convertArgumentsToStringList(argumentsFromCompilerArgumentsProducer) + ) + } + + @Test + fun `test - simple project - failing dependency - lenient`() { + val project = buildProjectWithMPP() + val kotlin = project.multiplatformExtension + val linuxX64Target = kotlin.linuxX64() + kotlin.sourceSets.getByName("commonMain").dependencies { implementation("not-a:dependency:1.0.0") } + project.evaluate() + + val commonMainCompileTask = linuxX64Target.compilations.main.compileTaskProvider.get() + assertNull(commonMainCompileTask.createCompilerArguments(lenient).libraries) + assertFails { commonMainCompileTask.createCompilerArguments(default) } + } +} \ No newline at end of file