diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt index 582abc9b4a2..e509703661c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt @@ -14,22 +14,10 @@ import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import javax.inject.Inject /* -Naming: - -executable('foo', [debug, release]) -> fooDebugExecutable + fooReleaseExecutable -executable -> debugExecutable, releaseExecutable -executable([debug]) -> debugExecutable - -// Tests: -1. Cannot add a second binary with the same parameters. -2. Can access a binary by name -3. Access linkTask -4. Access runTask -5. Final binaries have correct names -6. Old APIs work fine: - - creating binaries, - - getting binaries and link tasks, - - setting binary parameters using compilation DSL: linker opts and entry points +Use the following naming scheme: + executable('foo', [debug, release]) -> fooDebugExecutable + fooReleaseExecutable + executable() -> debugExecutable, releaseExecutable + executable([debug]) -> debugExecutable */ open class KotlinNativeBinaryContainer @Inject constructor( @@ -131,7 +119,7 @@ open class KotlinNativeBinaryContainer @Inject constructor( } require(outputKind.availableFor(target.konanTarget)) { - "Cannot create binary $name: ${outputKind.taskNameClassifier.decapitalize()} binaries are not available for target ${target.name}" + "Cannot create ${outputKind.description}: $name. Binaries of this kind are not available for target ${target.name}" } val binary = create(name, baseName, buildType, defaultCompilation) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt index c1d19444eaf..9ded1509519 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt @@ -7,10 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ConfigurationContainer -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.PublishArtifact +import org.gradle.api.artifacts.* import org.gradle.api.artifacts.type.ArtifactTypeDefinition import org.gradle.api.attributes.Usage import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE @@ -29,6 +26,7 @@ import org.gradle.api.tasks.testing.Test import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.jvm.tasks.ProcessResources import org.gradle.nativeplatform.test.tasks.RunTestExecutable +import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME @@ -464,7 +462,7 @@ open class KotlinNativeTargetConfigurator( // endregion. // region Task creation. - private fun Project.createLinkTask(target: KotlinNativeTarget, binary: NativeBinary) { + private fun Project.createLinkTask(binary: NativeBinary) { tasks.create( binary.linkTaskName, KotlinNativeLink::class.java @@ -569,6 +567,7 @@ open class KotlinNativeTargetConfigurator( override fun configureTarget(target: KotlinNativeTarget) { super.configureTarget(target) configureBinaries(target) + configureFrameworkExport(target) configureCInterops(target) warnAboutIncorrectDependencies(target) } @@ -610,8 +609,9 @@ open class KotlinNativeTargetConfigurator( protected fun configureBinaries(target: KotlinNativeTarget) { val project = target.project + // Create link and run tasks. target.binaries.all { - project.createLinkTask(target, it) + project.createLinkTask(it) } target.binaries.withType(Executable::class.java).all { @@ -655,6 +655,33 @@ open class KotlinNativeTargetConfigurator( } } + fun configureFrameworkExport(target: KotlinNativeTarget) { + val project = target.project + + target.compilations.all { + // Allow resolving api configurations directly to be able to check that + // all exported dependency are also added in the corresponding api configurations. + // The check is performed during a link task execution. + project.configurations.maybeCreate(it.apiConfigurationName).apply { + isCanBeResolved = true + usesPlatformOf(target) + attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.consumerApiUsage(target)) + } + } + + target.binaries.withType(Framework::class.java).all { framework -> + val exportConfiguration = project.configurations.maybeCreate(framework.exportConfigurationName).apply { + isVisible = false + isTransitive = false + isCanBeConsumed = false + isCanBeResolved = true + usesPlatformOf(target) + attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.consumerApiUsage(target)) + description = "Dependenceis to be exported in framework ${framework.name} for target ${target.targetName}" + } + } + } + override fun defineConfigurationsForTarget(target: KotlinNativeTarget) { super.defineConfigurationsForTarget(target) val configurations = target.project.configurations @@ -725,7 +752,7 @@ open class KotlinNativeTargetConfigurator( usesPlatformOf(target) isVisible = false isCanBeConsumed = false - attributes.attribute(USAGE_ATTRIBUTE, compilation.target.project.usageByName(KotlinUsages.KOTLIN_API)) + attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.consumerApiUsage(target)) description = "Dependencies for cinterop '${cinterop.name}' (compilation '${compilation.name}')." } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaries.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaries.kt index 6974f47805f..7de11172ecd 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaries.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaries.kt @@ -149,7 +149,22 @@ class Framework( override val outputKind: NativeOutputKind get() = NativeOutputKind.FRAMEWORK - // TODO: Pack task configuration. + val exportConfigurationName: String + get() = target.disambiguateName(lowerCamelCaseName(name, "export")) + + var transitiveExport: Boolean + get() = project.configurations.maybeCreate(exportConfigurationName).isTransitive + set(value) { + project.configurations.maybeCreate(exportConfigurationName).isTransitive = value + } + + fun export(dependency: Any) { + project.dependencies.add(exportConfigurationName, dependency) + } + + fun export(dependency: Any, configure: Closure<*>) { + project.dependencies.add(exportConfigurationName, dependency, configure) + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt index 2d1f85f800f..076a466afd4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt @@ -3,6 +3,7 @@ package org.jetbrains.kotlin.gradle.tasks import groovy.lang.Closure import org.gradle.api.DefaultTask import org.gradle.api.Project +import org.gradle.api.artifacts.* import org.gradle.api.file.FileCollection import org.gradle.api.file.FileTree import org.gradle.api.provider.Provider @@ -77,6 +78,11 @@ private fun FileCollection.filterOutPublishableInteropLibs(project: Project): Fi } } +private fun Collection.filterExternalKlibs(project: Project) = filter { + // Support only klib files for now. + it.extension == "klib" && !it.providedByCompiler(project) +} + // endregion abstract class AbstractKotlinNativeCompile : AbstractCompile(), KotlinCompile { @@ -270,10 +276,7 @@ abstract class AbstractKotlinNativeCompile : AbstractCompile(), KotlinCompile + libraries.files.filterExternalKlibs(project).forEach { library -> addArg("-l", library.absolutePath) } } @@ -357,14 +360,64 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile() { val linkerOpts: List get() = binary.linkerOpts + @get:InputFiles + val exportLibraries: FileCollection + get() = binary.let { + if (it is Framework) { + project.configurations.getByName(it.exportConfigurationName) + } else { + project.files() + } + } + override fun buildArgs(defaultsOnly: Boolean): List { val superArgs = super.buildArgs(defaultsOnly) return mutableListOf().apply { addAll(superArgs) addArgIfNotNull("-entry", entryPoint) addListArg("-linker-options", linkerOpts) + exportLibraries.files.filterExternalKlibs(project).forEach { + add("-Xexport-library=${it.absolutePath}") + } } } + + private fun validatedExportedLibraries() { + val exportConfiguration = exportLibraries as? Configuration ?: return + val apiFiles = project.configurations.getByName(compilation.apiConfigurationName).files.filterExternalKlibs(project) + + val failed = mutableSetOf() + exportConfiguration.allDependencies.forEach { + val dependencyFiles = exportConfiguration.files(it).filterExternalKlibs(project) + if (!apiFiles.containsAll(dependencyFiles)) { + failed.add(it) + } + } + + check(failed.isEmpty()) { + val failedDependenciesList = failed.joinToString(separator = "\n") { + when (it) { + is FileCollectionDependency -> "|Files: ${it.files.files}" + is ProjectDependency -> "|Project ${it.dependencyProject.path}" + else -> "|${it.group}:${it.name}:${it.version}" + } + } + + """ + |Following dependencies exported in the ${binary.name} binary are not specified as API-dependencies of a corresponding source set: + | + $failedDependenciesList + | + |Please add them in the API-dependencies and rerun the build. + """.trimMargin() + } + } + + @TaskAction + override fun compile() { + validatedExportedLibraries() + super.compile() + } } open class CInteropProcess : DefaultTask() { @@ -450,10 +503,7 @@ open class CInteropProcess : DefaultTask() { addArg("-lopt", it) } - libraries.files.filter { - // Support only klib files for now. - it.extension == "klib" && !it.providedByCompiler(project) - }.forEach { library -> + libraries.files.filterExternalKlibs(project).forEach { library -> addArg("-l", library.absolutePath) }