Merge commit 'ffdd43b8083c4fd7795799e351f23d997534a43e'; commit 'af3bf257ba1584fd2f3d690fe76c48778699e29b'; commit '83d22a3744df1b79545070fbb9ea13ee1182a614'; commit 'b7cf915e884caea8cabc2db5f611fa5d5a7536e8'; commit '522a5b8c3ffd84c11cb70efead9c152e8d62cd37'; commit '37a117574f314263360350964e80ce182c093e31' into move-packages-on-master

This commit is contained in:
Sergey Igushkin
2019-03-04 13:11:35 +03:00
23 changed files with 7426 additions and 7 deletions
-7
View File
@@ -1,7 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="idea.default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>
@@ -0,0 +1,788 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin
import org.gradle.api.Project
import org.gradle.api.Task
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
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.artifacts.ArtifactAttributes
import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.testing.Test
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.util.*
import java.util.concurrent.Callable
abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>(
protected val createDefaultSourceSets: Boolean,
protected val createTestCompilation: Boolean
) {
open fun configureTarget(
target: KotlinTargetType
) {
configureCompilationDefaults(target)
configureCompilations(target)
defineConfigurationsForTarget(target)
configureArchivesAndComponent(target)
configureTest(target)
configureBuild(target)
}
abstract protected fun configureArchivesAndComponent(target: KotlinTargetType)
abstract protected fun configureTest(target: KotlinTargetType)
private fun Project.registerOutputsForStaleOutputCleanup(kotlinCompilation: KotlinCompilation<*>) {
val cleanTask = tasks.getByName(LifecycleBasePlugin.CLEAN_TASK_NAME) as Delete
cleanTask.delete(kotlinCompilation.output.allOutputs)
}
protected fun configureCompilations(platformTarget: KotlinTargetType) {
val project = platformTarget.project
val main = platformTarget.compilations.create(KotlinCompilation.MAIN_COMPILATION_NAME)
platformTarget.compilations.all {
project.registerOutputsForStaleOutputCleanup(it)
it.compileDependencyFiles = project.configurations.maybeCreate(it.compileDependencyConfigurationName)
if (it is KotlinCompilationToRunnableFiles) {
it.runtimeDependencyFiles = project.configurations.maybeCreate(it.runtimeDependencyConfigurationName)
}
}
if (createTestCompilation) {
platformTarget.compilations.create(KotlinCompilation.TEST_COMPILATION_NAME).apply {
compileDependencyFiles = project.files(
main.output.allOutputs,
project.configurations.maybeCreate(compileDependencyConfigurationName)
)
if (this is KotlinCompilationToRunnableFiles) {
runtimeDependencyFiles = project.files(
output.allOutputs,
main.output.allOutputs,
project.configurations.maybeCreate(runtimeDependencyConfigurationName)
)
}
}
}
}
protected fun configureCompilationDefaults(target: KotlinTargetType) {
val project = target.project
target.compilations.all { compilation ->
defineConfigurationsForCompilation(compilation)
if (createDefaultSourceSets) {
project.kotlinExtension.sourceSets.maybeCreate(compilation.defaultSourceSetName).also { sourceSet ->
compilation.source(sourceSet) // also adds dependencies, requires the configurations for target and source set to exist at this point
}
}
if (compilation is KotlinCompilationWithResources) {
configureResourceProcessing(compilation, project.files(Callable { compilation.allKotlinSourceSets.map { it.resources } }))
}
createLifecycleTask(compilation)
}
}
protected fun configureResourceProcessing(
compilation: KotlinCompilationWithResources<*>,
resourceSet: FileCollection
) {
val project = compilation.target.project
val resourcesTask = project.tasks.maybeCreate(compilation.processResourcesTaskName, ProcessResources::class.java)
resourcesTask.description = "Processes $resourceSet."
DslObject(resourcesTask).conventionMapping.map("destinationDir") { project.file(compilation.output.resourcesDir) }
resourcesTask.from(resourceSet)
}
protected fun createLifecycleTask(compilation: KotlinCompilation<*>) {
val project = compilation.target.project
compilation.output.classesDirs.from(project.files().builtBy(compilation.compileAllTaskName))
project.tasks.create(compilation.compileAllTaskName).apply {
group = LifecycleBasePlugin.BUILD_GROUP
description = "Assembles outputs for compilation '${compilation.name}' of target '${compilation.target.name}'"
dependsOn(compilation.compileKotlinTaskName)
if (compilation is KotlinCompilationWithResources) {
dependsOn(compilation.processResourcesTaskName)
}
}
}
protected open fun defineConfigurationsForTarget(target: KotlinTargetType) {
val project = target.project
val configurations = project.configurations
val defaultConfiguration = configurations.maybeCreate(target.defaultConfigurationName).apply {
setupAsLocalTargetSpecificConfigurationIfSupported(target)
}
val mainCompilation = target.compilations.maybeCreate(KotlinCompilation.MAIN_COMPILATION_NAME)
val compileConfiguration = configurations.maybeCreate(mainCompilation.deprecatedCompileConfigurationName)
val implementationConfiguration = configurations.maybeCreate(mainCompilation.implementationConfigurationName)
val runtimeOnlyConfiguration = configurations.maybeCreate(mainCompilation.runtimeOnlyConfigurationName)
val apiElementsConfiguration = configurations.maybeCreate(target.apiElementsConfigurationName).apply {
description = "API elements for main."
isVisible = false
isCanBeResolved = false
isCanBeConsumed = true
attributes.attribute<Usage>(USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
extendsFrom(configurations.maybeCreate(mainCompilation.apiConfigurationName))
if (mainCompilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = configurations.maybeCreate(mainCompilation.deprecatedRuntimeConfigurationName)
extendsFrom(runtimeConfiguration)
}
usesPlatformOf(target)
setupAsPublicConfigurationIfSupported(target)
}
if (mainCompilation is KotlinCompilationToRunnableFiles<*>) {
val runtimeElementsConfiguration = configurations.maybeCreate(target.runtimeElementsConfigurationName).apply {
description = "Elements of runtime for main."
isVisible = false
isCanBeConsumed = true
isCanBeResolved = false
attributes.attribute<Usage>(USAGE_ATTRIBUTE, KotlinUsages.producerRuntimeUsage(target))
val runtimeConfiguration = configurations.maybeCreate(mainCompilation.deprecatedRuntimeConfigurationName)
extendsFrom(implementationConfiguration, runtimeOnlyConfiguration, runtimeConfiguration)
usesPlatformOf(target)
setupAsPublicConfigurationIfSupported(target)
}
defaultConfiguration.extendsFrom(runtimeElementsConfiguration)
} else {
defaultConfiguration.extendsFrom(apiElementsConfiguration)
}
if (createTestCompilation) {
val testCompilation = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME)
val compileTestsConfiguration = configurations.maybeCreate(testCompilation.deprecatedCompileConfigurationName)
val testImplementationConfiguration = configurations.maybeCreate(testCompilation.implementationConfigurationName)
val testRuntimeOnlyConfiguration = configurations.maybeCreate(testCompilation.runtimeOnlyConfigurationName)
compileTestsConfiguration.extendsFrom(compileConfiguration)
testImplementationConfiguration.extendsFrom(implementationConfiguration)
testRuntimeOnlyConfiguration.extendsFrom(runtimeOnlyConfiguration)
if (mainCompilation is KotlinCompilationToRunnableFiles && testCompilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = configurations.maybeCreate(mainCompilation.deprecatedRuntimeConfigurationName)
val testRuntimeConfiguration = configurations.maybeCreate(testCompilation.deprecatedRuntimeConfigurationName)
testRuntimeConfiguration.extendsFrom(runtimeConfiguration)
}
}
}
protected fun configureBuild(target: KotlinTargetType) {
val project = target.project
val buildNeeded = project.tasks.getByName(JavaBasePlugin.BUILD_NEEDED_TASK_NAME)
val buildDependent = project.tasks.getByName(JavaBasePlugin.BUILD_DEPENDENTS_TASK_NAME)
if (createTestCompilation) {
val testCompilation = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME)
if (testCompilation is KotlinCompilationToRunnableFiles) {
addDependsOnTaskInOtherProjects(buildNeeded, true, testCompilation.deprecatedRuntimeConfigurationName)
addDependsOnTaskInOtherProjects(buildDependent, false, testCompilation.deprecatedRuntimeConfigurationName)
}
}
}
private fun addDependsOnTaskInOtherProjects(task: Task, useDependedOn: Boolean, configurationName: String) {
val project = task.project
val configuration = project.configurations.getByName(configurationName)
task.dependsOn(configuration.getTaskDependencyFromProjectDependency(useDependedOn, task.name))
}
companion object {
const val testTaskNameSuffix = "test"
fun defineConfigurationsForCompilation(
compilation: KotlinCompilation<*>
) {
val target = compilation.target
val configurations = target.project.configurations
val compileConfiguration = configurations.maybeCreate(compilation.deprecatedCompileConfigurationName).apply {
setupAsLocalTargetSpecificConfigurationIfSupported(target)
isVisible = false
isCanBeResolved = true // Needed for IDE import
description = "Dependencies for $compilation (deprecated, use '${compilation.implementationConfigurationName} ' instead)."
}
val apiConfiguration = configurations.maybeCreate(compilation.apiConfigurationName).apply {
extendsFrom(compileConfiguration)
isVisible = false
isCanBeConsumed = false
isCanBeResolved = false
description = "API dependencies for $compilation."
}
val implementationConfiguration = configurations.maybeCreate(compilation.implementationConfigurationName).apply {
extendsFrom(compileConfiguration, apiConfiguration)
isVisible = false
isCanBeConsumed = false
isCanBeResolved = false
description = "Implementation only dependencies for $compilation."
}
val compileOnlyConfiguration = configurations.maybeCreate(compilation.compileOnlyConfigurationName).apply {
setupAsLocalTargetSpecificConfigurationIfSupported(target)
isVisible = false
isCanBeResolved = true // Needed for IDE import
description = "Compile only dependencies for $compilation."
}
val compileClasspathConfiguration = configurations.maybeCreate(compilation.compileDependencyConfigurationName).apply {
extendsFrom(compileOnlyConfiguration, implementationConfiguration)
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.consumerApiUsage(compilation.target))
description = "Compile classpath for $compilation."
}
if (compilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = configurations.maybeCreate(compilation.deprecatedRuntimeConfigurationName).apply {
setupAsLocalTargetSpecificConfigurationIfSupported(target)
extendsFrom(compileConfiguration)
isVisible = false
isCanBeResolved = true // Needed for IDE import
description =
"Runtime dependencies for $compilation (deprecated, use '${compilation.runtimeOnlyConfigurationName} ' instead)."
}
val runtimeOnlyConfiguration = configurations.maybeCreate(compilation.runtimeOnlyConfigurationName).apply {
isVisible = false
isCanBeConsumed = false
isCanBeResolved = false
description = "Runtime only dependencies for $compilation."
}
val runtimeClasspathConfiguration = configurations.maybeCreate(compilation.runtimeDependencyConfigurationName).apply {
extendsFrom(runtimeOnlyConfiguration, runtimeConfiguration, implementationConfiguration)
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
isCanBeResolved = true
attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.consumerRuntimeUsage(compilation.target))
description = "Runtime classpath of $compilation."
}
}
}
}
}
internal val KotlinCompilation<*>.deprecatedCompileConfigurationName: String
get() = disambiguateName("compile")
internal val KotlinCompilationToRunnableFiles<*>.deprecatedRuntimeConfigurationName: String
get() = disambiguateName("runtime")
open class KotlinTargetConfigurator<KotlinCompilationType : KotlinCompilation<*>>(
createDefaultSourceSets: Boolean,
createTestCompilation: Boolean
) : AbstractKotlinTargetConfigurator<KotlinOnlyTarget<KotlinCompilationType>>(
createDefaultSourceSets,
createTestCompilation
) {
override fun configureArchivesAndComponent(target: KotlinOnlyTarget<KotlinCompilationType>) {
val project = target.project
val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val jar = project.tasks.create(target.artifactsTaskName, Jar::class.java)
jar.description = "Assembles a jar archive containing the main classes."
jar.group = BasePlugin.BUILD_GROUP
jar.from(mainCompilation.output.allOutputs)
val apiElementsConfiguration = project.configurations.getByName(target.apiElementsConfigurationName)
target.disambiguationClassifier?.let { jar.appendix = it.toLowerCase() }
// Workaround: adding the artifact during configuration seems to interfere with the Java plugin, which results into missing
// task dependency 'assemble -> jar' if the Java plugin is applied after this steps
project.afterEvaluate {
project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, jar) { jarArtifact ->
jarArtifact.builtBy(jar)
jarArtifact.type = ArtifactTypeDefinition.JAR_TYPE
addJar(apiElementsConfiguration, jarArtifact)
if (mainCompilation is KotlinCompilationToRunnableFiles<*>) {
val runtimeConfiguration = project.configurations.getByName(mainCompilation.deprecatedRuntimeConfigurationName)
val runtimeElementsConfiguration = project.configurations.getByName(target.runtimeElementsConfigurationName)
addJar(runtimeConfiguration, jarArtifact)
addJar(runtimeElementsConfiguration, jarArtifact)
// TODO Check Gradle's special split into variants for classes & resources -- do we need that too?
}
}
}
}
override fun configureTest(target: KotlinOnlyTarget<KotlinCompilationType>) {
val testCompilation = target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME) as? KotlinCompilationToRunnableFiles<*>
?: return // Otherwise, there is no runtime classpath
target.project.tasks.create(lowerCamelCaseName(target.targetName, testTaskNameSuffix), Test::class.java).apply {
project.afterEvaluate {
// use afterEvaluate to override the JavaPlugin defaults for Test tasks
conventionMapping.map("testClassesDirs") { testCompilation.output.classesDirs }
conventionMapping.map("classpath") { testCompilation.runtimeDependencyFiles }
description = "Runs the unit tests."
group = JavaBasePlugin.VERIFICATION_GROUP
target.project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(this@apply)
}
}
}
private fun addJar(configuration: Configuration, jarArtifact: PublishArtifact) {
val publications = configuration.outgoing
// Configure an implicit variant
publications.artifacts.add(jarArtifact)
publications.attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.JAR_TYPE)
}
}
open class KotlinNativeTargetConfigurator(
private val kotlinPluginVersion: String
) : AbstractKotlinTargetConfigurator<KotlinNativeTarget>(
createDefaultSourceSets = true,
createTestCompilation = true
) {
private fun Project.klibOutputDirectory(
compilation: KotlinNativeCompilation
): File {
val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
return buildDir.resolve("classes/kotlin/$targetSubDirectory${compilation.name}")
}
private fun AbstractKotlinNativeCompile.addCompilerPlugins() {
SubpluginEnvironment
.loadSubplugins(project, kotlinPluginVersion)
.addSubpluginOptions(project, this, compilerPluginOptions)
compilerPluginClasspath = project.configurations.getByName(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME)
}
// region Artifact creation.
private fun Project.createKlibArtifact(
compilation: KotlinNativeCompilation,
artifactFile: File,
classifier: String?,
producingTask: Task,
copy: Boolean = false
) {
if (!compilation.target.konanTarget.enabledOnCurrentHost) {
return
}
val apiElements = configurations.getByName(compilation.target.apiElementsConfigurationName)
val realProducingTask: Task
// TODO: Someone remove this HACK PLEASE!
val realArtifactFile = if (copy) {
realProducingTask = project.tasks.create("copy${producingTask.name.capitalize()}", Copy::class.java) {
val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
it.destinationDir = project.buildDir.resolve("libs/$targetSubDirectory${compilation.name}")
it.from(artifactFile)
it.dependsOn(producingTask)
}
realProducingTask.destinationDir.resolve(artifactFile.name)
} else {
realProducingTask = producingTask
artifactFile
}
val klibArtifact = DefaultPublishArtifact(
compilation.name,
"klib",
"klib",
classifier,
Date(),
realArtifactFile,
realProducingTask
)
project.extensions.getByType(DefaultArtifactPublicationSet::class.java).addCandidate(klibArtifact)
with(apiElements.outgoing) {
artifacts.add(klibArtifact)
attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, NativeArtifactFormat.KLIB)
}
}
private fun Project.createRegularKlibArtifact(
compilation: KotlinNativeCompilation,
compileTask: KotlinNativeCompile
) = createKlibArtifact(compilation, compileTask.outputFile.get(), null, compileTask)
private fun Project.createCInteropKlibArtifact(
interop: DefaultCInteropSettings,
interopTask: CInteropProcess
) = createKlibArtifact(interop.compilation, interopTask.outputFile, "cinterop-${interop.name}", interopTask, copy = true)
// endregion.
// region Task creation.
private fun Project.createLinkTask(binary: NativeBinary) {
tasks.create(
binary.linkTaskName,
KotlinNativeLink::class.java
).apply {
val target = binary.target
this.binary = binary
group = BasePlugin.BUILD_GROUP
description = "Links ${binary.outputKind.description} '${binary.name}' for a target '${target.name}'."
enabled = target.konanTarget.enabledOnCurrentHost
destinationDir = binary.outputDirectory
addCompilerPlugins()
tasks.maybeCreate(target.artifactsTaskName).dependsOn(this)
tasks.maybeCreate(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(this)
}
}
private fun Project.createRunTask(binary: Executable) {
val taskName = binary.runTaskName ?: return
// TODO provide a special exec task for tests.
tasks.create(taskName, Exec::class.java).apply {
if (binary.isDefaultTestExecutable) {
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = "Executes Kotlin/Native unit tests for target ${binary.target.name}."
tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(this)
if (project.hasProperty("teamcity.version")) {
args("--ktest_logger=TEAMCITY")
}
} else {
group = RUN_GROUP
description = "Executes Kotlin/Native executable ${binary.name} for target ${binary.target.name}"
}
enabled = binary.target.konanTarget.isCurrentHost
executable = binary.outputFile.absolutePath
workingDir = project.projectDir
onlyIf { binary.outputFile.exists() }
dependsOn(binary.linkTaskName)
}
}
private fun Project.createKlibCompilationTask(compilation: KotlinNativeCompilation) {
val compileTask = tasks.create(
compilation.compileKotlinTaskName,
KotlinNativeCompile::class.java
).apply {
this.compilation = compilation
group = BasePlugin.BUILD_GROUP
description = "Compiles a klibrary from the '${compilation.name}' " +
"compilation for target '${compilation.platformType.name}'."
enabled = compilation.target.konanTarget.enabledOnCurrentHost
destinationDir = klibOutputDirectory(compilation)
addCompilerPlugins()
compilation.output.addClassesDir {
project.files(this.outputFile).builtBy(this)
}
}
project.tasks.getByName(compilation.compileAllTaskName).dependsOn(compileTask)
if (compilation.compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) {
project.tasks.getByName(compilation.target.artifactsTaskName).apply {
dependsOn(compileTask)
}
project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).apply {
dependsOn(compileTask)
}
createRegularKlibArtifact(compilation, compileTask)
}
}
private fun Project.createCInteropTasks(compilation: KotlinNativeCompilation) {
compilation.cinterops.all { interop ->
val interopTask = tasks.create(interop.interopProcessingTaskName, CInteropProcess::class.java).apply {
settings = interop
destinationDir = provider { klibOutputDirectory(compilation) }
group = INTEROP_GROUP
description = "Generates Kotlin/Native interop library '${interop.name}' " +
"for compilation '${compilation.name}'" +
"of target '${konanTarget.name}'."
enabled = compilation.target.konanTarget.enabledOnCurrentHost
val interopOutput = project.files(outputFileProvider).builtBy(this)
with(compilation) {
project.dependencies.add(compileDependencyConfigurationName, interopOutput)
if (isMainCompilation) {
target.compilations.findByName(TEST_COMPILATION_NAME)?.let {
project.dependencies.add(it.compileDependencyConfigurationName, interopOutput)
}
}
project.dependencies.add(target.apiElementsConfigurationName, interopOutput)
}
}
createCInteropKlibArtifact(interop, interopTask)
}
}
// endregion.
// region Configuration.
override fun configureTarget(target: KotlinNativeTarget) {
super.configureTarget(target)
configureBinaries(target)
configureFrameworkExport(target)
configureCInterops(target)
warnAboutIncorrectDependencies(target)
}
override fun configureArchivesAndComponent(target: KotlinNativeTarget): Unit = with(target.project) {
tasks.create(target.artifactsTaskName)
target.compilations.all {
createKlibCompilationTask(it)
}
with(configurations.getByName(target.apiElementsConfigurationName)) {
outgoing.attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, NativeArtifactFormat.KLIB)
}
}
override fun configureTest(target: KotlinNativeTarget) {
target.binaries.defaultTestExecutable {
compilation = target.compilations.maybeCreate(KotlinCompilation.TEST_COMPILATION_NAME)
// Allow accessing the test binary using the old getters (e.g. compilations.test.getBinary('EXECUTABLE', 'DEBUG'))
compilation.binaries[NativeOutputKind.EXECUTABLE to KotlinNativeBinaryContainer.DEFAULT_TEST_BUILD_TYPE] = this
}
}
protected fun configureCInterops(target: KotlinNativeTarget): Unit = with(target.project) {
target.compilations.all { compilation ->
createCInteropTasks(compilation)
compilation.cinterops.all {
defineConfigurationsForCInterop(compilation, it, target, configurations)
}
}
if (createTestCompilation) {
val mainCompilation = target.compilations.getByName(MAIN_COMPILATION_NAME)
target.compilations.findByName(TEST_COMPILATION_NAME)?.apply {
cinterops.all {
it.dependencyFiles += mainCompilation.output.allOutputs
}
}
}
}
protected fun configureBinaries(target: KotlinNativeTarget) {
val project = target.project
// Create link and run tasks.
target.binaries.all {
project.createLinkTask(it)
}
target.binaries.withType(Executable::class.java).all {
project.createRunTask(it)
}
target.binaries.prefixGroups.all { prefixGroup ->
val linkGroupTask = project.tasks.maybeCreate(prefixGroup.linkTaskName)
prefixGroup.binaries.all {
linkGroupTask.dependsOn(it.linkTaskName)
}
}
// Create an aggregate link task for each compilation.
target.compilations.all {
project.tasks.create(it.binariesTaskName)
}
project.whenEvaluated {
target.binaries.forEach {
project.tasks.getByName(it.compilation.binariesTaskName).dependsOn(it.linkTaskName)
}
}
// Create binaries for output kinds declared using the old DSL.
project.whenEvaluated {
target.compilations.all { compilation ->
val binaries = target.binaries
val konanTarget = compilation.target.konanTarget
val name = compilation.name
val buildTypes = compilation.buildTypes
val availableOutputKinds = compilation.outputKinds.filter { it.availableFor(konanTarget) }
val configure: NativeBinary.() -> Unit = {
this.compilation = compilation
linkerOpts.addAll(compilation.linkerOpts)
if (this is Executable) {
entryPoint = compilation.entryPoint
}
compilation.binaries[outputKind to buildType] = this
}
for (kind in availableOutputKinds) {
when (kind) {
NativeOutputKind.EXECUTABLE -> binaries.executable(name, buildTypes, configure)
NativeOutputKind.DYNAMIC -> binaries.sharedLib(name, buildTypes, configure)
NativeOutputKind.STATIC -> binaries.staticLib(name, buildTypes, configure)
NativeOutputKind.FRAMEWORK -> binaries.framework(name, buildTypes, configure)
}
}
}
// Allow setting linker options for the default test executable using the
// corresponding properties of the test compilation.
target.binaries.getDefaultTestExecutable().apply {
linkerOpts.addAll(target.compilations.getByName(TEST_COMPILATION_NAME).linkerOpts)
}
}
}
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
// The configuration and the main compilation are created by the base class.
val mainCompilation = target.compilations.getByName(MAIN_COMPILATION_NAME)
configurations.getByName(target.apiElementsConfigurationName).apply {
// K/N compiler doesn't divide libraries into implementation and api ones. So we need to add implementation
// dependencies into the outgoing configuration.
extendsFrom(configurations.getByName(mainCompilation.implementationConfigurationName))
}
}
private fun warnAboutIncorrectDependencies(target: KotlinNativeTarget) = target.project.whenEvaluated {
val compileOnlyDependencies = target.compilations.mapNotNull {
val dependencies = configurations.getByName(it.compileOnlyConfigurationName).allDependencies
if (dependencies.isNotEmpty()) {
it to dependencies
} else null
}
fun Dependency.stringCoordinates(): String = buildString {
group?.let { append(it).append(':') }
append(name)
version?.let { append(':').append(it) }
}
if (compileOnlyDependencies.isNotEmpty()) {
with(target.project.logger) {
warn("A compileOnly dependency is used in the Kotlin/Native target '${target.name}':")
compileOnlyDependencies.forEach {
warn(
"""
Compilation: ${it.first.name}
Dependencies:
${it.second.joinToString(separator = "\n") { it.stringCoordinates() }}
""".trimIndent()
)
}
warn("Such dependencies are not applicable for Kotlin/Native, consider changing the dependency type to 'implementation' or 'api'.")
}
}
}
// endregion.
object NativeArtifactFormat {
const val KLIB = "org.jetbrains.kotlin.klib"
}
companion object {
const val INTEROP_GROUP = "interop"
const val RUN_GROUP = "run"
protected fun defineConfigurationsForCInterop(
compilation: KotlinNativeCompilation,
cinterop: CInteropSettings,
target: KotlinTarget,
configurations: ConfigurationContainer
) {
val compileOnlyConfiguration = configurations.getByName(compilation.compileOnlyConfigurationName)
val implementationConfiguration = configurations.getByName(compilation.implementationConfigurationName)
cinterop.dependencyFiles = configurations.maybeCreate(cinterop.dependencyConfigurationName).apply {
extendsFrom(compileOnlyConfiguration, implementationConfiguration)
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.consumerApiUsage(target))
description = "Dependencies for cinterop '${cinterop.name}' (compilation '${compilation.name}')."
}
}
}
}
internal fun Project.usageByName(usageName: String): Usage =
project.objects.named(Usage::class.java, usageName)
fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
attributes.attribute(KotlinPlatformType.attribute, target.platformType)
// TODO: Provide an universal way to copy attributes from the target.
if (target is KotlinNativeTarget) {
attributes.attribute(KotlinNativeTarget.konanTargetAttribute, target.konanTarget.name)
}
return this
}
@@ -0,0 +1,437 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.*
import org.gradle.api.artifacts.ConfigurablePublishArtifact
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.attributes.Usage.JAVA_API
import org.gradle.api.attributes.Usage.JAVA_RUNTIME_JARS
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.util.ConfigureUtil
import org.gradle.util.WrapUtil
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget
abstract class AbstractKotlinTarget(
final override val project: Project
) : KotlinTarget {
private val attributeContainer = HierarchyAttributeContainer(parent = null)
override fun getAttributes(): AttributeContainer = attributeContainer
override val defaultConfigurationName: String
get() = disambiguateName("default")
override val apiElementsConfigurationName: String
get() = disambiguateName("apiElements")
override val runtimeElementsConfigurationName: String
get() = disambiguateName("runtimeElements")
override val artifactsTaskName: String
get() = disambiguateName("jar")
override fun toString(): String = "target $name ($platformType)"
override val publishable: Boolean
get() = true
override val components: Set<KotlinTargetComponent> by lazy {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val usageContexts = createUsageContexts(mainCompilation)
setOf(
if (isGradleVersionAtLeast(4, 7)) {
val componentName = mainCompilation.target.name
createKotlinVariant(componentName, mainCompilation, usageContexts).apply {
sourcesArtifacts = setOf(
sourcesJarArtifact(
mainCompilation, componentName,
dashSeparatedName(target.name.toLowerCase(), componentName.toLowerCase())
)
)
}
} else {
KotlinVariant(mainCompilation, usageContexts)
}
).also { project.components.addAll(it) }
}
protected fun createKotlinVariant(
componentName: String,
compilation: KotlinCompilation<*>,
usageContexts: Set<DefaultKotlinUsageContext>
): KotlinVariant {
val kotlinExtension = project.kotlinExtension as? KotlinMultiplatformExtension
?: return KotlinVariantWithCoordinates(compilation, usageContexts)
if (targetName == KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
return KotlinVariantWithCoordinates(compilation, usageContexts)
val separateMetadataTarget = kotlinExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
val result = if (kotlinExtension.isGradleMetadataAvailable) {
KotlinVariantWithMetadataVariant(compilation, usageContexts, separateMetadataTarget)
} else {
// we should only add the Kotlin metadata dependency if we publish no Gradle metadata related to Kotlin MPP;
// with metadata, such a dependency would get invalid, since a platform module should only depend on modules for that
// same platform, not Kotlin metadata modules
KotlinVariantWithMetadataDependency(compilation, usageContexts, separateMetadataTarget)
}
result.componentName = componentName
return result
}
private fun createUsageContexts(
producingCompilation: KotlinCompilation<*>
): Set<DefaultKotlinUsageContext> {
// Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for
// ordering of the usage contexts (prioritizing the dependencies) when merging them into the POM;
// These Java usages should not be replaced with the custom Kotlin usages.
return listOfNotNull(
JAVA_API to apiElementsConfigurationName,
(JAVA_RUNTIME_JARS to runtimeElementsConfigurationName).takeIf { producingCompilation is KotlinCompilationToRunnableFiles }
).mapTo(mutableSetOf()) { (usageName, dependenciesConfigurationName) ->
DefaultKotlinUsageContext(
producingCompilation,
project.usageByName(usageName),
dependenciesConfigurationName
)
}
}
protected fun sourcesJarArtifact(
producingCompilation: KotlinCompilation<*>,
componentName: String,
artifactNameAppendix: String,
classifierPrefix: String? = null
): PublishArtifact {
val sourcesJarTask = sourcesJarTask(producingCompilation, componentName, artifactNameAppendix)
val sourceArtifactConfigurationName = producingCompilation.disambiguateName("sourceArtifacts")
return producingCompilation.target.project.run {
(configurations.findByName(sourceArtifactConfigurationName) ?: run {
val configuration = configurations.create(sourceArtifactConfigurationName) {
it.isCanBeResolved = false
it.isCanBeConsumed = false
}
artifacts.add(sourceArtifactConfigurationName, sourcesJarTask)
configuration
}).artifacts.single().apply {
this as ConfigurablePublishArtifact
classifier = dashSeparatedName(classifierPrefix, "sources")
}
}
}
@Suppress("UNCHECKED_CAST")
internal val publicationConfigureActions =
WrapUtil.toDomainObjectSet(Action::class.java) as DomainObjectSet<Action<MavenPublication>>
override fun mavenPublication(action: Action<MavenPublication>) {
publicationConfigureActions.add(action)
}
override fun mavenPublication(action: Closure<Unit>) =
mavenPublication(ConfigureUtil.configureUsing(action))
override var preset: KotlinTargetPreset<out KotlinTarget>? = null
internal set
}
internal fun KotlinTarget.disambiguateName(simpleName: String) =
lowerCamelCaseName(targetName, simpleName)
open class KotlinAndroidTarget(
override val targetName: String,
project: Project
) : AbstractKotlinTarget(project) {
override var disambiguationClassifier: String? = null
internal set
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.androidJvm
private val compilationFactory = KotlinJvmAndroidCompilationFactory(project, this)
override val compilations: NamedDomainObjectContainer<out KotlinJvmAndroidCompilation> =
project.container(compilationFactory.itemClass, compilationFactory)
/** Names of the Android library variants that should be published from the target's project within the default publications which are
* set up if the `maven-publish` Gradle plugin is applied.
*
* Item examples:
* * 'release' (in case no product flavors were defined)
* * 'fooRelease' (for the release build type of a flavor 'foo')
* * 'fooBarRelease' (for the release build type multi-dimensional flavors 'foo' and 'bar').
*
* If set to null, which can also be done with [publishAllLibraryVariants],
* all library variants will be published, but not test or application variants. */
var publishLibraryVariants: List<String>? = listOf()
// Workaround for Groovy GString items in a list:
set(value) { field = value?.map(Any::toString) }
/** Add Android library variant names to [publishLibraryVariants]. */
fun publishLibraryVariants(vararg names: String) {
publishLibraryVariants = publishLibraryVariants.orEmpty() + names
}
/** Set up all of the Android library variants to be published from this target's project within the default publications, which are
* set up if the `maven-publish` Gradle plugin is applied. This overrides the variants chosen with [publishLibraryVariants] */
fun publishAllLibraryVariants() {
publishLibraryVariants = null
}
/** If true, a publication will be created per merged product flavor, with the build types used as classifiers for the artifacts
* published within each publication. If set to false, each Android variant will have a separate publication. */
var publishLibraryVariantsGroupedByFlavor = false
private fun checkPublishLibraryVariantsExist() {
// Capture type parameter T
fun <T> AbstractAndroidProjectHandler<T>.getLibraryVariantNames() =
mutableSetOf<String>().apply {
forEachVariant(project) {
if (getLibraryOutputTask(it) != null)
add(getVariantName(it))
}
}
val variantNames =
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this)
.getLibraryVariantNames()
val missingVariants =
publishLibraryVariants?.minus(variantNames).orEmpty()
if (missingVariants.isNotEmpty())
throw InvalidUserDataException(
"Kotlin target '$targetName' tried to set up publishing for Android build variants that are not library variants " +
"or do not exist:\n" + missingVariants.joinToString("\n") { "* $it" } +
"\nCheck the 'publishLibraryVariants' property, it should point to existing Android library variants. Publishing " +
"of application and test variants is not supported."
)
}
override val components by lazy {
checkPublishLibraryVariantsExist()
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this).doCreateComponents()
.also { project.components.addAll(it) }
}
// Capture the type parameter T for `AbstractAndroidProjectHandler`
private fun <T> AbstractAndroidProjectHandler<T>.doCreateComponents(): Set<KotlinTargetComponent> {
if (!isGradleVersionAtLeast(4, 7))
return emptySet()
val publishableVariants = mutableListOf<T>()
.apply { forEachVariant(project) { add(it) } }
.toList() // Defensive copy against unlikely modification by the lambda that captures the list above in forEachVariant { }
.filter { getLibraryOutputTask(it) != null && publishLibraryVariants?.contains(getVariantName(it)) ?: true }
val publishableVariantGroups = publishableVariants.groupBy { variant ->
val flavorNames = getFlavorNames(variant)
if (publishLibraryVariantsGroupedByFlavor) {
// For each flavor, we group its variants (which differ only in the build type) in a single component in order to publish
// all of the build types of the flavor as a single module with the build type as the classifier of the artifacts
flavorNames
} else {
flavorNames + getBuildTypeName(variant)
}
}
return publishableVariantGroups.map { (flavorGroupNameParts, androidVariants) ->
val nestedVariants = androidVariants.mapTo(mutableSetOf()) { androidVariant ->
val androidVariantName = getVariantName(androidVariant)
val compilation = compilations.getByName(androidVariantName)
val flavorNames = getFlavorNames(androidVariant)
val buildTypeName = getBuildTypeName(androidVariant)
val artifactClassifier = buildTypeName.takeIf { it != "release" && publishLibraryVariantsGroupedByFlavor }
val usageContexts = createAndroidUsageContexts(androidVariant, compilation, artifactClassifier)
createKotlinVariant(
lowerCamelCaseName(compilation.target.name, *flavorGroupNameParts.toTypedArray()),
compilation,
usageContexts
).apply {
sourcesArtifacts = setOf(
sourcesJarArtifact(
compilation, compilation.disambiguateName(""),
dashSeparatedName(
compilation.target.name.toLowerCase(),
*flavorNames.map { it.toLowerCase() }.toTypedArray(),
buildTypeName.takeIf { it != "release" }?.toLowerCase()
),
classifierPrefix = artifactClassifier
)
)
if (!publishLibraryVariantsGroupedByFlavor) {
defaultArtifactIdSuffix =
dashSeparatedName(
(getFlavorNames(androidVariant) + getBuildTypeName(androidVariant).takeIf { it != "release" })
.map { it?.toLowerCase() }
).takeIf { it.isNotEmpty() }
}
}
}
if (publishLibraryVariantsGroupedByFlavor) {
JointAndroidKotlinTargetComponent(
this@KotlinAndroidTarget,
nestedVariants,
flavorGroupNameParts,
nestedVariants.flatMap { it.sourcesArtifacts }.toSet()
)
} else {
nestedVariants.single()
} as KotlinTargetComponent
}.toSet()
}
private fun <T> AbstractAndroidProjectHandler<T>.createAndroidUsageContexts(
variant: T,
compilation: KotlinCompilation<*>,
artifactClassifier: String?
): Set<DefaultKotlinUsageContext> {
val variantName = getVariantName(variant)
val outputTask = getLibraryOutputTask(variant) ?: return emptySet()
val artifact = run {
val archivesConfigurationName = lowerCamelCaseName(targetName, variantName, "archives")
project.configurations.maybeCreate(archivesConfigurationName).apply {
isCanBeConsumed = false
isCanBeResolved = false
}
project.artifacts.add(archivesConfigurationName, outputTask) { artifact ->
artifact.classifier = artifactClassifier
}
}
val apiElementsConfigurationName = lowerCamelCaseName(variantName, "apiElements")
val runtimeElementsConfigurationName = lowerCamelCaseName(variantName, "runtimeElements")
// Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for
// ordering of the usage contexts (prioritizing the dependencies) when merging them into the POM;
// These Java usages should not be replaced with the custom Kotlin usages.
return listOf(
apiElementsConfigurationName to JAVA_API,
runtimeElementsConfigurationName to JAVA_RUNTIME_JARS
).mapTo(mutableSetOf()) { (dependencyConfigurationName, usageName) ->
DefaultKotlinUsageContext(
compilation,
project.usageByName(usageName),
dependencyConfigurationName,
overrideConfigurationArtifacts = setOf(artifact)
)
}
}
}
open class KotlinWithJavaTarget<KotlinOptionsType : KotlinCommonOptions>(
project: Project,
override val platformType: KotlinPlatformType,
override val targetName: String
) : AbstractKotlinTarget(project) {
override var disambiguationClassifier: String? = null
internal set
override val defaultConfigurationName: String
get() = Dependency.DEFAULT_CONFIGURATION
override val apiElementsConfigurationName: String
get() = JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME
override val runtimeElementsConfigurationName: String
get() = JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME
override val artifactsTaskName: String
get() = JavaPlugin.JAR_TASK_NAME
override val compilations: NamedDomainObjectContainer<KotlinWithJavaCompilation<KotlinOptionsType>> =
@Suppress("UNCHECKED_CAST")
project.container(
KotlinWithJavaCompilation::class.java as Class<KotlinWithJavaCompilation<KotlinOptionsType>>,
KotlinWithJavaCompilationFactory(project, this)
)
}
open class KotlinOnlyTarget<T : KotlinCompilation<*>>(
project: Project,
override val platformType: KotlinPlatformType
) : AbstractKotlinTarget(project) {
override lateinit var compilations: NamedDomainObjectContainer<T>
internal set
override lateinit var targetName: String
internal set
override var disambiguationClassifier: String? = null
internal set
}
class KotlinNativeTarget(
project: Project,
val konanTarget: KonanTarget
) : KotlinOnlyTarget<KotlinNativeCompilation>(project, KotlinPlatformType.native) {
init {
attributes.attribute(konanTargetAttribute, konanTarget.name)
}
val binaries = if(isGradleVersionAtLeast(4, 2)) {
// Use newInstance to allow accessing binaries by their names in Groovy using the extension mechanism.
project.objects.newInstance(KotlinNativeBinaryContainer::class.java, this, WrapUtil.toDomainObjectSet(NativeBinary::class.java))
} else {
KotlinNativeBinaryContainer(this, WrapUtil.toDomainObjectSet(NativeBinary::class.java))
}
fun binaries(configure: KotlinNativeBinaryContainer.() -> Unit) {
binaries.configure()
}
fun binaries(configure: Closure<*>) {
ConfigureUtil.configure(configure, binaries)
}
override val artifactsTaskName: String
get() = disambiguateName("binaries")
override val publishable: Boolean
get() = konanTarget.enabledOnCurrentHost
// User-visible constants
val DEBUG = NativeBuildType.DEBUG
val RELEASE = NativeBuildType.RELEASE
val EXECUTABLE = NativeOutputKind.EXECUTABLE
val FRAMEWORK = NativeOutputKind.FRAMEWORK
val DYNAMIC = NativeOutputKind.DYNAMIC
val STATIC = NativeOutputKind.STATIC
companion object {
val konanTargetAttribute = Attribute.of(
"org.jetbrains.kotlin.native.target",
String::class.java
)
}
}
@@ -0,0 +1,330 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.compilerRunner.KotlinNativeProjectProperty
import org.jetbrains.kotlin.compilerRunner.hasProperty
import org.jetbrains.kotlin.compilerRunner.konanHome
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import java.util.*
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation<*>>(
protected val project: Project,
private val instantiator: Instantiator,
private val fileResolver: FileResolver,
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
protected open fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T> =
KotlinTargetConfigurator(createDefaultSourceSets = true, createTestCompilation = true)
override fun createTarget(name: String): KotlinOnlyTarget<T> {
val result = KotlinOnlyTarget<T>(project, platformType).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinOnlyTargetPreset
val compilationFactory = createCompilationFactory(this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
createKotlinTargetConfigurator().configureTarget(result)
result.compilations.all { compilation ->
buildCompilationProcessor(compilation).run()
if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
sourcesJarTask(compilation, result.targetName, result.targetName.toLowerCase())
}
}
return result
}
protected abstract fun createCompilationFactory(forTarget: KotlinOnlyTarget<T>): KotlinCompilationFactory<T>
protected abstract val platformType: KotlinPlatformType
internal abstract fun buildCompilationProcessor(compilation: T): KotlinSourceSetProcessor<*>
}
class KotlinMetadataTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(
forTarget: KotlinOnlyTarget<KotlinCommonCompilation>
): KotlinCompilationFactory<KotlinCommonCompilation> =
KotlinCommonCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.common
override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "metadata"
}
override fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<KotlinCommonCompilation> =
KotlinTargetConfigurator(createDefaultSourceSets = false, createTestCompilation = false)
override fun createTarget(name: String): KotlinOnlyTarget<KotlinCommonCompilation> =
super.createTarget(name).apply {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val commonMainSourceSet = project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
mainCompilation.source(commonMainSourceSet)
project.afterEvaluate {
// Since there's no default source set, apply language settings from commonMain:
val compileKotlinMetadata = project.tasks.getByName(mainCompilation.compileKotlinTaskName) as KotlinCompile<*>
applyLanguageSettingsToKotlinTask(commonMainSourceSet.languageSettings, compileKotlinMetadata)
}
}
}
class KotlinJvmTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> =
KotlinJvmCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.jvm
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "jvm"
}
}
class KotlinJsTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJsCompilation>) =
KotlinJsCompilationFactory(project, forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JsSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "js"
}
}
class KotlinAndroidTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinAndroidTarget> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinAndroidTarget {
val result = KotlinAndroidTarget(name, project).apply {
disambiguationClassifier = name
preset = this@KotlinAndroidTargetPreset
}
KotlinAndroidPlugin.applyToTarget(kotlinPluginVersion, result)
return result
}
companion object {
const val PRESET_NAME = "android"
}
}
class KotlinJvmWithJavaTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinWithJavaTarget<KotlinJvmOptions>> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinWithJavaTarget<KotlinJvmOptions> {
project.plugins.apply(JavaPlugin::class.java)
val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, name).apply {
disambiguationClassifier = name
preset = this@KotlinJvmWithJavaTargetPreset
}
AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(name), compilation, kotlinPluginVersion)
}
target.compilations.getByName("test").run {
val main = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
compileDependencyFiles = project.files(
main.output.allOutputs,
project.configurations.maybeCreate(compileDependencyConfigurationName)
)
runtimeDependencyFiles = project.files(
output.allOutputs,
main.output.allOutputs,
project.configurations.maybeCreate(runtimeDependencyConfigurationName)
)
}
return target
}
companion object {
const val PRESET_NAME = "jvmWithJava"
}
}
class KotlinNativeTargetPreset(
private val name: String,
val project: Project,
val konanTarget: KonanTarget,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinNativeTarget> {
init {
// This is required to obtain Kotlin/Native home in CLion plugin:
setupNativeHomePrivateProperty()
}
override fun getName(): String = name
private fun setupNativeHomePrivateProperty() = with(project) {
if (!hasProperty(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY))
extensions.extraProperties.set(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY, konanHome)
}
private fun setupNativeCompiler() = with(project) {
if (!hasProperty(KotlinNativeProjectProperty.KONAN_HOME_OVERRIDE)) {
NativeCompilerDownloader(this).downloadIfNeeded()
logger.info("Kotlin/Native distribution: $konanHome")
} else {
logger.info("User-provided Kotlin/Native distribution: $konanHome")
}
}
// We declare default K/N dependencies as files to avoid searching them in remote repos (see KT-28128).
private fun defaultLibs(target: KonanTarget? = null): List<Dependency> = with(project) {
val relPath = if (target != null) "platform/${target.name}" else "common"
file("$konanHome/klib/$relPath")
.listFiles { file -> file.isDirectory }
?.sortedBy { dir -> dir.name.toLowerCase() }
?.map { dir -> dependencies.create(files(dir)) } ?: emptyList()
}
override fun createTarget(name: String): KotlinNativeTarget {
setupNativeCompiler()
val result = KotlinNativeTarget(project, konanTarget).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinNativeTargetPreset
val compilationFactory = KotlinNativeCompilationFactory(project, this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
KotlinNativeTargetConfigurator(kotlinPluginVersion).configureTarget(result)
// Allow IDE to resolve the libraries provided by the compiler by adding them into dependencies.
result.compilations.all { compilation ->
val target = compilation.target.konanTarget
// First, put common libs:
defaultLibs().forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
// Then, platform-specific libs:
defaultLibs(target).forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
}
if (!konanTarget.enabledOnCurrentHost) {
with(HostManager()) {
val supportedHosts = enabledTargetsByHost.filterValues { konanTarget in it }.keys
val supportedHostsString =
if (supportedHosts.size == 1)
"a ${supportedHosts.single()} host" else
"one of the host platforms: ${supportedHosts.joinToString(", ")}"
project.logger.warn(
"Target '$name' for platform ${konanTarget} is ignored during build on this ${HostManager.host} machine. " +
"You can build it with $supportedHostsString."
)
}
}
return result
}
companion object {
private const val KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY = "konanHome"
}
}
internal val KonanTarget.isCurrentHost: Boolean
get() = this == HostManager.host
internal val KonanTarget.enabledOnCurrentHost
get() = HostManager().isEnabled(this)
internal val KotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME
@@ -0,0 +1,471 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.SourceSet
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.util.*
import java.util.concurrent.Callable
internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: String? = null): String {
val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
val targetNamePart = target.disambiguationClassifier
return lowerCamelCaseName(prefix, targetNamePart, compilationNamePart, suffix)
}
internal val KotlinCompilation<*>.defaultSourceSetName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName)
abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
target: KotlinTarget,
override val compilationName: String
) : KotlinCompilation<T>, HasKotlinDependencies {
override val kotlinOptions: T
get() = compileKotlinTask.kotlinOptions
override fun kotlinOptions(configure: T.() -> Unit) =
configure(kotlinOptions)
@Suppress("UNCHECKED_CAST")
override val compileKotlinTask: KotlinCompile<T>
get() = (target.project.tasks.getByName(compileKotlinTaskName) as KotlinCompile<T>)
// Don't declare this property in the constructor to avoid NPE
// when an overriding property of a subclass is accessed instead.
override val target: KotlinTarget = target
private val attributeContainer = HierarchyAttributeContainer(target.attributes)
override fun getAttributes(): AttributeContainer = attributeContainer
override val kotlinSourceSets: MutableSet<KotlinSourceSet> = mutableSetOf()
override val allKotlinSourceSets: Set<KotlinSourceSet>
get() = kotlinSourceSets.flatMapTo(mutableSetOf()) { it.getSourceSetHierarchy() }
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(defaultSourceSetName)
override fun defaultSourceSet(configure: KotlinSourceSet.() -> Unit) =
configure(defaultSourceSet)
override val output: KotlinCompilationOutput by lazy {
DefaultKotlinCompilationOutput(
target.project,
Callable { target.project.buildDir.resolve("processedResources/${target.targetName}/$name") })
}
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
fun AbstractKotlinCompile<*>.configureAction() {
source(sourceSet.kotlin)
sourceFilesExtensions(sourceSet.customSourceFilesExtensions)
if (addAsCommonSources) {
commonSourceSet += sourceSet.kotlin
}
}
// Note! Invocation of withType-all results in preliminary task instantiation.
// After fix of this issue the following code should be uncommented:
// if (useLazyTaskConfiguration) {
// (target.project.tasks.named(compileKotlinTaskName) as TaskProvider<AbstractKotlinCompile<*>>).configure {
// it.configureAction()
// }
// }
target.project.tasks
// To configure a task that may have not yet been created at this point, use 'withType-matching-all`:
.withType(AbstractKotlinCompile::class.java)
.matching { it.name == compileKotlinTaskName }
.all { compileKotlinTask ->
compileKotlinTask.configureAction()
}
}
override fun source(sourceSet: KotlinSourceSet) {
if (kotlinSourceSets.add(sourceSet)) {
//TODO possibly issue with forced instantiation
target.project.whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
val isCommonSource =
CompilationSourceSetUtil.sourceSetsInMultipleCompilations(project)?.contains(sourceSet) ?: false
addSourcesToCompileTask(sourceSet, addAsCommonSources = isCommonSource)
// Use `forced = false` since `api`, `implementation`, and `compileOnly` may be missing in some cases like
// old Java & Android projects:
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName, forced = false)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName, forced = false)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName, forced = false)
if (this@AbstractKotlinCompilation is KotlinCompilationToRunnableFiles<*>) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName, forced = false)
}
if (sourceSet.name != defaultSourceSetName) {
kotlinExtension.sourceSets.findByName(defaultSourceSetName)?.let { defaultSourceSet ->
// Temporary solution for checking consistency across source sets participating in a compilation that may
// not be interconnected with the dependsOn relation: check the settings as if the default source set of
// the compilation depends on the one added to the compilation:
defaultSourceSetLanguageSettingsChecker.runAllChecks(
defaultSourceSet,
sourceSet
)
}
}
}
}
}
}
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileClasspath"
)
override val compileKotlinTaskName: String
get() = lowerCamelCaseName(
"compile",
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"Kotlin",
target.targetName
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "classes")
override lateinit var compileDependencyFiles: FileCollection
override val apiConfigurationName: String
get() = disambiguateName("api")
override val implementationConfigurationName: String
get() = disambiguateName("implementation")
override val compileOnlyConfigurationName: String
get() = disambiguateName("compileOnly")
override val runtimeOnlyConfigurationName: String
get() = disambiguateName("runtimeOnly")
override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit =
DefaultKotlinDependencyHandler(this, target.project).run(configure)
override fun dependencies(configureClosure: Closure<Any?>) =
dependencies f@{ ConfigureUtil.configure(configureClosure, this@f) }
override fun toString(): String = "compilation '$compilationName' ($target)"
}
abstract class AbstractKotlinCompilationToRunnableFiles<T : KotlinCommonOptions>(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<T>(target, name), KotlinCompilationToRunnableFiles<T> {
override val runtimeDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"runtimeClasspath"
)
override lateinit var runtimeDependencyFiles: FileCollection
}
internal fun KotlinCompilation<*>.disambiguateName(simpleName: String): String {
return lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
simpleName
)
}
open class KotlinJvmCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name), KotlinCompilationWithResources<KotlinJvmOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
}
class KotlinWithJavaCompilation<KotlinOptionsType : KotlinCommonOptions>(
target: KotlinWithJavaTarget<KotlinOptionsType>,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinOptionsType>(target, name), KotlinCompilationWithResources<KotlinOptionsType> {
lateinit var javaSourceSet: SourceSet
override val output: KotlinCompilationOutput by lazy { KotlinWithJavaCompilationOutput(this) }
override val processResourcesTaskName: String
get() = javaSourceSet.processResourcesTaskName
override var runtimeDependencyFiles: FileCollection
get() = javaSourceSet.runtimeClasspath
set(value) {
javaSourceSet.runtimeClasspath = value
}
override val runtimeDependencyConfigurationName: String
get() = javaSourceSet.runtimeClasspathConfigurationName
override val compileDependencyConfigurationName: String
get() = javaSourceSet.compileClasspathConfigurationName
override val runtimeOnlyConfigurationName: String
get() = javaSourceSet.runtimeOnlyConfigurationName
override val implementationConfigurationName: String
get() = javaSourceSet.implementationConfigurationName
override val apiConfigurationName: String
get() = javaSourceSet.apiConfigurationName
override val compileOnlyConfigurationName: String
get() = javaSourceSet.compileOnlyConfigurationName
override val compileAllTaskName: String
get() = javaSourceSet.classesTaskName
override var compileDependencyFiles: FileCollection
get() = javaSourceSet.compileClasspath
set(value) {
javaSourceSet.compileClasspath = value
}
fun source(javaSourceSet: SourceSet) {
with(target.project) {
afterEvaluate {
(tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(javaSourceSet.java)
}
}
}
}
class KotlinJvmAndroidCompilation(
target: KotlinAndroidTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name) {
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
override val relatedConfigurationNames: List<String>
get() = super.relatedConfigurationNames + listOf("${name}ApiElements", "${name}RuntimeElements")
}
class KotlinJsCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJsOptions>(target, name), KotlinCompilationWithResources<KotlinJsOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: Kotlin2JsCompile
get() = super.compileKotlinTask as Kotlin2JsCompile
}
class KotlinCommonCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<KotlinMultiplatformCommonOptions>(target, name) {
override val compileKotlinTask: KotlinCompileCommon
get() = super.compileKotlinTask as KotlinCompileCommon
// TODO once we properly compile metadata for each source set, the default source sets will likely become just the source sets
// which are transformed to metadata
private val commonSourceSetName = when (compilationName) {
KotlinCompilation.MAIN_COMPILATION_NAME -> KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
else -> error("Custom metadata compilations are not supported yet")
}
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(commonSourceSetName)
}
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
name: String
) : AbstractKotlinCompilation<KotlinCommonOptions>(target, name), KotlinCompilationWithResources<KotlinCommonOptions> {
override val kotlinOptions: KotlinCommonOptions
get() = compileKotlinTask.kotlinOptions
override val compileKotlinTask: KotlinNativeCompile
get() = super.compileKotlinTask as KotlinNativeCompile
private val project: Project
get() = target.project
// A collection containing all source sets used by this compilation
// (taking into account dependencies between source sets). Used by both compilation
// and linking tasks. Unlike kotlinSourceSets, includes dependency source sets.
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val allSources: MutableSet<SourceDirectorySet> = mutableSetOf()
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val commonSources: MutableSet<SourceDirectorySet> = mutableSetOf()
var isTestCompilation = false
var friendCompilationName: String? = null
internal val friendCompilation: KotlinNativeCompilation?
get() = friendCompilationName?.let {
target.compilations.getByName(it)
}
// Used only to support the old APIs. TODO: Remove when the old APIs are removed.
internal val binaries = mutableMapOf<Pair<NativeOutputKind, NativeBuildType>, NativeBinary>()
// Native-specific DSL.
var extraOpts = mutableListOf<String>()
fun extraOpts(vararg values: Any) = extraOpts(values.toList())
fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
var buildTypes = mutableListOf<NativeBuildType>()
var outputKinds = mutableListOf<NativeOutputKind>()
fun outputKind(kind: NativeOutputKind) = outputKinds.add(kind)
fun outputKinds(vararg kinds: NativeOutputKind) {
outputKinds = kinds.toMutableList()
}
fun outputKinds(vararg kinds: String) {
outputKinds = kinds.map { NativeOutputKind.valueOf(it.toUpperCase()) }.toMutableList()
}
fun outputKinds(kinds: List<Any>) {
outputKinds = kinds.map {
when (it) {
is NativeOutputKind -> it
is String -> NativeOutputKind.valueOf(it.toUpperCase())
else -> error("Cannot use $it as an output kind")
}
}.toMutableList()
}
var entryPoint: String? = null
fun entryPoint(value: String) {
entryPoint = value
}
// Interop DSL.
val cinterops = project.container(DefaultCInteropSettings::class.java) { cinteropName ->
DefaultCInteropSettings(project, cinteropName, this)
}
var linkerOpts = mutableListOf<String>()
fun cinterops(action: NamedDomainObjectContainer<DefaultCInteropSettings>.() -> Unit) = cinterops.action()
fun cinterops(action: Closure<Unit>) = cinterops(ConfigureUtil.configureUsing(action))
fun cinterops(action: Action<NamedDomainObjectContainer<DefaultCInteropSettings>>) = action.execute(cinterops)
fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
fun linkerOpts(values: List<String>) {
linkerOpts.addAll(values)
}
// Task accessors.
fun findLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink? = binaries[kind to buildType]?.linkTask
fun getLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink =
findLinkTask(kind, buildType)
?: throw IllegalArgumentException("Cannot find a link task for the binary kind '$kind' and the build type '$buildType'")
fun findLinkTask(kind: String, buildType: String) =
findLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getLinkTask(kind: String, buildType: String) =
getLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun findBinary(kind: NativeOutputKind, buildType: NativeBuildType): File? = findLinkTask(kind, buildType)?.outputFile?.get()
fun getBinary(kind: NativeOutputKind, buildType: NativeBuildType): File = getLinkTask(kind, buildType).outputFile.get()
fun findBinary(kind: String, buildType: String) =
findBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getBinary(kind: String, buildType: String) =
getBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
// Naming
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
fun linkTaskName(kind: NativeOutputKind, buildType: NativeBuildType): String =
lowerCamelCaseName(
"link",
KotlinNativeBinaryContainer.generateBinaryName(compilationName, buildType, kind.taskNameClassifier),
target.targetName
)
fun linkTaskName(kind: String, buildType: String) =
linkTaskName(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileKlibraries"
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "klibrary")
val binariesTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "binaries")
override fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
allSources.add(sourceSet.kotlin)
if (addAsCommonSources) {
commonSources.add(sourceSet.kotlin)
}
}
}
private object CompilationSourceSetUtil {
// Cache the results per project
private val projectSourceSetsInMultipleCompilationsCache = WeakHashMap<Project, Set<KotlinSourceSet>>()
fun sourceSetsInMultipleCompilations(project: Project) =
projectSourceSetsInMultipleCompilationsCache.computeIfAbsent(project) { _ ->
check(project.state.executed) { "Should only be computed after the project is evaluated" }
val compilations = (project.kotlinExtension as? KotlinMultiplatformExtension)?.targets?.flatMap { it.compilations }
?: return@computeIfAbsent null
compilations
.flatMap { compilation -> compilation.allKotlinSourceSets.map { sourceSet -> compilation to sourceSet } }
.groupingBy { (_, sourceSet) -> sourceSet }
.eachCount()
.filterValues { it > 1 }
.keys
}
}
@@ -0,0 +1,471 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.SourceSet
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.util.*
import java.util.concurrent.Callable
internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: String? = null): String {
val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
val targetNamePart = target.disambiguationClassifier
return lowerCamelCaseName(prefix, targetNamePart, compilationNamePart, suffix)
}
internal val KotlinCompilation<*>.defaultSourceSetName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName)
abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
target: KotlinTarget,
override val compilationName: String
) : KotlinCompilation<T>, HasKotlinDependencies {
override val kotlinOptions: T
get() = compileKotlinTask.kotlinOptions
override fun kotlinOptions(configure: T.() -> Unit) =
configure(kotlinOptions)
@Suppress("UNCHECKED_CAST")
override val compileKotlinTask: KotlinCompile<T>
get() = (target.project.tasks.getByName(compileKotlinTaskName) as KotlinCompile<T>)
// Don't declare this property in the constructor to avoid NPE
// when an overriding property of a subclass is accessed instead.
override val target: KotlinTarget = target
private val attributeContainer = HierarchyAttributeContainer(target.attributes)
override fun getAttributes(): AttributeContainer = attributeContainer
override val kotlinSourceSets: MutableSet<KotlinSourceSet> = mutableSetOf()
override val allKotlinSourceSets: Set<KotlinSourceSet>
get() = kotlinSourceSets.flatMapTo(mutableSetOf()) { it.getSourceSetHierarchy() }
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(defaultSourceSetName)
override fun defaultSourceSet(configure: KotlinSourceSet.() -> Unit) =
configure(defaultSourceSet)
override val output: KotlinCompilationOutput by lazy {
DefaultKotlinCompilationOutput(
target.project,
Callable { target.project.buildDir.resolve("processedResources/${target.targetName}/$name") })
}
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
fun AbstractKotlinCompile<*>.configureAction() {
source(sourceSet.kotlin)
sourceFilesExtensions(sourceSet.customSourceFilesExtensions)
if (addAsCommonSources) {
commonSourceSet += sourceSet.kotlin
}
}
// Note! Invocation of withType-all results in preliminary task instantiation.
// After fix of this issue the following code should be uncommented:
// if (useLazyTaskConfiguration) {
// (target.project.tasks.named(compileKotlinTaskName) as TaskProvider<AbstractKotlinCompile<*>>).configure {
// it.configureAction()
// }
// }
target.project.tasks
// To configure a task that may have not yet been created at this point, use 'withType-matching-all`:
.withType(AbstractKotlinCompile::class.java)
.matching { it.name == compileKotlinTaskName }
.all { compileKotlinTask ->
compileKotlinTask.configureAction()
}
}
override fun source(sourceSet: KotlinSourceSet) {
if (kotlinSourceSets.add(sourceSet)) {
//TODO possibly issue with forced instantiation
target.project.whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
val isCommonSource =
CompilationSourceSetUtil.sourceSetsInMultipleCompilations(project)?.contains(sourceSet) ?: false
addSourcesToCompileTask(sourceSet, addAsCommonSources = isCommonSource)
// Use `forced = false` since `api`, `implementation`, and `compileOnly` may be missing in some cases like
// old Java & Android projects:
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName, forced = false)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName, forced = false)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName, forced = false)
if (this@AbstractKotlinCompilation is KotlinCompilationToRunnableFiles<*>) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName, forced = false)
}
if (sourceSet.name != defaultSourceSetName) {
kotlinExtension.sourceSets.findByName(defaultSourceSetName)?.let { defaultSourceSet ->
// Temporary solution for checking consistency across source sets participating in a compilation that may
// not be interconnected with the dependsOn relation: check the settings as if the default source set of
// the compilation depends on the one added to the compilation:
defaultSourceSetLanguageSettingsChecker.runAllChecks(
defaultSourceSet,
sourceSet
)
}
}
}
}
}
}
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileClasspath"
)
override val compileKotlinTaskName: String
get() = lowerCamelCaseName(
"compile",
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"Kotlin",
target.targetName
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "classes")
override lateinit var compileDependencyFiles: FileCollection
override val apiConfigurationName: String
get() = disambiguateName("api")
override val implementationConfigurationName: String
get() = disambiguateName("implementation")
override val compileOnlyConfigurationName: String
get() = disambiguateName("compileOnly")
override val runtimeOnlyConfigurationName: String
get() = disambiguateName("runtimeOnly")
override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit =
DefaultKotlinDependencyHandler(this, target.project).run(configure)
override fun dependencies(configureClosure: Closure<Any?>) =
dependencies f@{ ConfigureUtil.configure(configureClosure, this@f) }
override fun toString(): String = "compilation '$compilationName' ($target)"
}
abstract class AbstractKotlinCompilationToRunnableFiles<T : KotlinCommonOptions>(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<T>(target, name), KotlinCompilationToRunnableFiles<T> {
override val runtimeDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"runtimeClasspath"
)
override lateinit var runtimeDependencyFiles: FileCollection
}
internal fun KotlinCompilation<*>.disambiguateName(simpleName: String): String {
return lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
simpleName
)
}
open class KotlinJvmCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name), KotlinCompilationWithResources<KotlinJvmOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
}
class KotlinWithJavaCompilation<KotlinOptionsType : KotlinCommonOptions>(
target: KotlinWithJavaTarget<KotlinOptionsType>,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinOptionsType>(target, name), KotlinCompilationWithResources<KotlinOptionsType> {
lateinit var javaSourceSet: SourceSet
override val output: KotlinCompilationOutput by lazy { KotlinWithJavaCompilationOutput(this) }
override val processResourcesTaskName: String
get() = javaSourceSet.processResourcesTaskName
override var runtimeDependencyFiles: FileCollection
get() = javaSourceSet.runtimeClasspath
set(value) {
javaSourceSet.runtimeClasspath = value
}
override val runtimeDependencyConfigurationName: String
get() = javaSourceSet.runtimeClasspathConfigurationName
override val compileDependencyConfigurationName: String
get() = javaSourceSet.compileClasspathConfigurationName
override val runtimeOnlyConfigurationName: String
get() = javaSourceSet.runtimeOnlyConfigurationName
override val implementationConfigurationName: String
get() = javaSourceSet.implementationConfigurationName
override val apiConfigurationName: String
get() = javaSourceSet.apiConfigurationName
override val compileOnlyConfigurationName: String
get() = javaSourceSet.compileOnlyConfigurationName
override val compileAllTaskName: String
get() = javaSourceSet.classesTaskName
override var compileDependencyFiles: FileCollection
get() = javaSourceSet.compileClasspath
set(value) {
javaSourceSet.compileClasspath = value
}
fun source(javaSourceSet: SourceSet) {
with(target.project) {
afterEvaluate {
(tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(javaSourceSet.java)
}
}
}
}
class KotlinJvmAndroidCompilation(
target: KotlinAndroidTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name) {
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
override val relatedConfigurationNames: List<String>
get() = super.relatedConfigurationNames + listOf("${name}ApiElements", "${name}RuntimeElements")
}
class KotlinJsCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJsOptions>(target, name), KotlinCompilationWithResources<KotlinJsOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: Kotlin2JsCompile
get() = super.compileKotlinTask as Kotlin2JsCompile
}
class KotlinCommonCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<KotlinMultiplatformCommonOptions>(target, name) {
override val compileKotlinTask: KotlinCompileCommon
get() = super.compileKotlinTask as KotlinCompileCommon
// TODO once we properly compile metadata for each source set, the default source sets will likely become just the source sets
// which are transformed to metadata
private val commonSourceSetName = when (compilationName) {
KotlinCompilation.MAIN_COMPILATION_NAME -> KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
else -> error("Custom metadata compilations are not supported yet")
}
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(commonSourceSetName)
}
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
name: String
) : AbstractKotlinCompilation<KotlinCommonOptions>(target, name), KotlinCompilationWithResources<KotlinCommonOptions> {
override val kotlinOptions: KotlinCommonOptions
get() = compileKotlinTask.kotlinOptions
override val compileKotlinTask: KotlinNativeCompile
get() = super.compileKotlinTask as KotlinNativeCompile
private val project: Project
get() = target.project
// A collection containing all source sets used by this compilation
// (taking into account dependencies between source sets). Used by both compilation
// and linking tasks. Unlike kotlinSourceSets, includes dependency source sets.
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val allSources: MutableSet<SourceDirectorySet> = mutableSetOf()
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val commonSources: MutableSet<SourceDirectorySet> = mutableSetOf()
var isTestCompilation = false
var friendCompilationName: String? = null
internal val friendCompilation: KotlinNativeCompilation?
get() = friendCompilationName?.let {
target.compilations.getByName(it)
}
// Used only to support the old APIs. TODO: Remove when the old APIs are removed.
internal val binaries = mutableMapOf<Pair<NativeOutputKind, NativeBuildType>, NativeBinary>()
// Native-specific DSL.
var extraOpts = mutableListOf<String>()
fun extraOpts(vararg values: Any) = extraOpts(values.toList())
fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
var buildTypes = mutableListOf<NativeBuildType>()
var outputKinds = mutableListOf<NativeOutputKind>()
fun outputKind(kind: NativeOutputKind) = outputKinds.add(kind)
fun outputKinds(vararg kinds: NativeOutputKind) {
outputKinds = kinds.toMutableList()
}
fun outputKinds(vararg kinds: String) {
outputKinds = kinds.map { NativeOutputKind.valueOf(it.toUpperCase()) }.toMutableList()
}
fun outputKinds(kinds: List<Any>) {
outputKinds = kinds.map {
when (it) {
is NativeOutputKind -> it
is String -> NativeOutputKind.valueOf(it.toUpperCase())
else -> error("Cannot use $it as an output kind")
}
}.toMutableList()
}
var entryPoint: String? = null
fun entryPoint(value: String) {
entryPoint = value
}
// Interop DSL.
val cinterops = project.container(DefaultCInteropSettings::class.java) { cinteropName ->
DefaultCInteropSettings(project, cinteropName, this)
}
var linkerOpts = mutableListOf<String>()
fun cinterops(action: NamedDomainObjectContainer<DefaultCInteropSettings>.() -> Unit) = cinterops.action()
fun cinterops(action: Closure<Unit>) = cinterops(ConfigureUtil.configureUsing(action))
fun cinterops(action: Action<NamedDomainObjectContainer<DefaultCInteropSettings>>) = action.execute(cinterops)
fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
fun linkerOpts(values: List<String>) {
linkerOpts.addAll(values)
}
// Task accessors.
fun findLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink? = binaries[kind to buildType]?.linkTask
fun getLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink =
findLinkTask(kind, buildType)
?: throw IllegalArgumentException("Cannot find a link task for the binary kind '$kind' and the build type '$buildType'")
fun findLinkTask(kind: String, buildType: String) =
findLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getLinkTask(kind: String, buildType: String) =
getLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun findBinary(kind: NativeOutputKind, buildType: NativeBuildType): File? = findLinkTask(kind, buildType)?.outputFile?.get()
fun getBinary(kind: NativeOutputKind, buildType: NativeBuildType): File = getLinkTask(kind, buildType).outputFile.get()
fun findBinary(kind: String, buildType: String) =
findBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getBinary(kind: String, buildType: String) =
getBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
// Naming
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
fun linkTaskName(kind: NativeOutputKind, buildType: NativeBuildType): String =
lowerCamelCaseName(
"link",
KotlinNativeBinaryContainer.generateBinaryName(compilationName, buildType, kind.taskNameClassifier),
target.targetName
)
fun linkTaskName(kind: String, buildType: String) =
linkTaskName(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileKlibraries"
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "klibrary")
val binariesTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "binaries")
override fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
allSources.add(sourceSet.kotlin)
if (addAsCommonSources) {
commonSources.add(sourceSet.kotlin)
}
}
}
private object CompilationSourceSetUtil {
// Cache the results per project
private val projectSourceSetsInMultipleCompilationsCache = WeakHashMap<Project, Set<KotlinSourceSet>>()
fun sourceSetsInMultipleCompilations(project: Project) =
projectSourceSetsInMultipleCompilationsCache.computeIfAbsent(project) { _ ->
check(project.state.executed) { "Should only be computed after the project is evaluated" }
val compilations = (project.kotlinExtension as? KotlinMultiplatformExtension)?.targets?.flatMap { it.compilations }
?: return@computeIfAbsent null
compilations
.flatMap { compilation -> compilation.allKotlinSourceSets.map { sourceSet -> compilation to sourceSet } }
.groupingBy { (_, sourceSet) -> sourceSet }
.eachCount()
.filterValues { it > 1 }
.keys
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
interface KotlinCompilationFactory<T : KotlinCompilation<*>> : NamedDomainObjectFactory<T> {
val itemClass: Class<T>
}
class KotlinCommonCompilationFactory(
val target: KotlinOnlyTarget<KotlinCommonCompilation>
) : KotlinCompilationFactory<KotlinCommonCompilation> {
override val itemClass: Class<KotlinCommonCompilation>
get() = KotlinCommonCompilation::class.java
override fun create(name: String): KotlinCommonCompilation =
KotlinCommonCompilation(target, name)
}
class KotlinJvmCompilationFactory(
val target: KotlinOnlyTarget<KotlinJvmCompilation>
) : KotlinCompilationFactory<KotlinJvmCompilation> {
override val itemClass: Class<KotlinJvmCompilation>
get() = KotlinJvmCompilation::class.java
override fun create(name: String): KotlinJvmCompilation =
KotlinJvmCompilation(target, name)
}
class KotlinWithJavaCompilationFactory<KotlinOptionsType : KotlinCommonOptions>(
val project: Project,
val target: KotlinWithJavaTarget<KotlinOptionsType>
) : KotlinCompilationFactory<KotlinWithJavaCompilation<KotlinOptionsType>> {
override val itemClass: Class<KotlinWithJavaCompilation<KotlinOptionsType>>
@Suppress("UNCHECKED_CAST")
get() = KotlinWithJavaCompilation::class.java as Class<KotlinWithJavaCompilation<KotlinOptionsType>>
override fun create(name: String): KotlinWithJavaCompilation<KotlinOptionsType> {
val result = KotlinWithJavaCompilation(target, name)
return result
}
}
class KotlinJvmAndroidCompilationFactory(
val project: Project,
val target: KotlinAndroidTarget
) : KotlinCompilationFactory<KotlinJvmAndroidCompilation> {
override val itemClass: Class<KotlinJvmAndroidCompilation>
get() = KotlinJvmAndroidCompilation::class.java
override fun create(name: String): KotlinJvmAndroidCompilation =
KotlinJvmAndroidCompilation(target, name)
}
class KotlinJsCompilationFactory(
val project: Project,
val target: KotlinOnlyTarget<KotlinJsCompilation>
) : KotlinCompilationFactory<KotlinJsCompilation> {
override val itemClass: Class<KotlinJsCompilation>
get() = KotlinJsCompilation::class.java
override fun create(name: String): KotlinJsCompilation =
KotlinJsCompilation(target, name)
}
class KotlinNativeCompilationFactory(
val project: Project,
val target: KotlinNativeTarget
) : KotlinCompilationFactory<KotlinNativeCompilation> {
override val itemClass: Class<KotlinNativeCompilation>
get() = KotlinNativeCompilation::class.java
override fun create(name: String): KotlinNativeCompilation =
KotlinNativeCompilation(target, name).apply {
if (name == KotlinCompilation.TEST_COMPILATION_NAME) {
friendCompilationName = KotlinCompilation.MAIN_COMPILATION_NAME
isTestCompilation = true
}
buildTypes = mutableListOf(NativeBuildType.DEBUG, NativeBuildType.RELEASE)
}
}
@@ -0,0 +1,330 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.compilerRunner.KotlinNativeProjectProperty
import org.jetbrains.kotlin.compilerRunner.hasProperty
import org.jetbrains.kotlin.compilerRunner.konanHome
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import java.util.*
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation<*>>(
protected val project: Project,
private val instantiator: Instantiator,
private val fileResolver: FileResolver,
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
protected open fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T> =
KotlinTargetConfigurator(createDefaultSourceSets = true, createTestCompilation = true)
override fun createTarget(name: String): KotlinOnlyTarget<T> {
val result = KotlinOnlyTarget<T>(project, platformType).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinOnlyTargetPreset
val compilationFactory = createCompilationFactory(this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
createKotlinTargetConfigurator().configureTarget(result)
result.compilations.all { compilation ->
buildCompilationProcessor(compilation).run()
if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
sourcesJarTask(compilation, result.targetName, result.targetName.toLowerCase())
}
}
return result
}
protected abstract fun createCompilationFactory(forTarget: KotlinOnlyTarget<T>): KotlinCompilationFactory<T>
protected abstract val platformType: KotlinPlatformType
internal abstract fun buildCompilationProcessor(compilation: T): KotlinSourceSetProcessor<*>
}
class KotlinMetadataTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(
forTarget: KotlinOnlyTarget<KotlinCommonCompilation>
): KotlinCompilationFactory<KotlinCommonCompilation> =
KotlinCommonCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.common
override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "metadata"
}
override fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<KotlinCommonCompilation> =
KotlinTargetConfigurator(createDefaultSourceSets = false, createTestCompilation = false)
override fun createTarget(name: String): KotlinOnlyTarget<KotlinCommonCompilation> =
super.createTarget(name).apply {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val commonMainSourceSet = project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
mainCompilation.source(commonMainSourceSet)
project.afterEvaluate {
// Since there's no default source set, apply language settings from commonMain:
val compileKotlinMetadata = project.tasks.getByName(mainCompilation.compileKotlinTaskName) as KotlinCompile<*>
applyLanguageSettingsToKotlinTask(commonMainSourceSet.languageSettings, compileKotlinMetadata)
}
}
}
class KotlinJvmTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> =
KotlinJvmCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.jvm
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "jvm"
}
}
class KotlinJsTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJsCompilation>) =
KotlinJsCompilationFactory(project, forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JsSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "js"
}
}
class KotlinAndroidTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinAndroidTarget> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinAndroidTarget {
val result = KotlinAndroidTarget(name, project).apply {
disambiguationClassifier = name
preset = this@KotlinAndroidTargetPreset
}
KotlinAndroidPlugin.applyToTarget(kotlinPluginVersion, result)
return result
}
companion object {
const val PRESET_NAME = "android"
}
}
class KotlinJvmWithJavaTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinWithJavaTarget<KotlinJvmOptions>> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinWithJavaTarget<KotlinJvmOptions> {
project.plugins.apply(JavaPlugin::class.java)
val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, name).apply {
disambiguationClassifier = name
preset = this@KotlinJvmWithJavaTargetPreset
}
AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(name), compilation, kotlinPluginVersion)
}
target.compilations.getByName("test").run {
val main = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
compileDependencyFiles = project.files(
main.output.allOutputs,
project.configurations.maybeCreate(compileDependencyConfigurationName)
)
runtimeDependencyFiles = project.files(
output.allOutputs,
main.output.allOutputs,
project.configurations.maybeCreate(runtimeDependencyConfigurationName)
)
}
return target
}
companion object {
const val PRESET_NAME = "jvmWithJava"
}
}
class KotlinNativeTargetPreset(
private val name: String,
val project: Project,
val konanTarget: KonanTarget,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinNativeTarget> {
init {
// This is required to obtain Kotlin/Native home in CLion plugin:
setupNativeHomePrivateProperty()
}
override fun getName(): String = name
private fun setupNativeHomePrivateProperty() = with(project) {
if (!hasProperty(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY))
extensions.extraProperties.set(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY, konanHome)
}
private fun setupNativeCompiler() = with(project) {
if (!hasProperty(KotlinNativeProjectProperty.KONAN_HOME_OVERRIDE)) {
NativeCompilerDownloader(this).downloadIfNeeded()
logger.info("Kotlin/Native distribution: $konanHome")
} else {
logger.info("User-provided Kotlin/Native distribution: $konanHome")
}
}
// We declare default K/N dependencies as files to avoid searching them in remote repos (see KT-28128).
private fun defaultLibs(target: KonanTarget? = null): List<Dependency> = with(project) {
val relPath = if (target != null) "platform/${target.name}" else "common"
file("$konanHome/klib/$relPath")
.listFiles { file -> file.isDirectory }
?.sortedBy { dir -> dir.name.toLowerCase() }
?.map { dir -> dependencies.create(files(dir)) } ?: emptyList()
}
override fun createTarget(name: String): KotlinNativeTarget {
setupNativeCompiler()
val result = KotlinNativeTarget(project, konanTarget).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinNativeTargetPreset
val compilationFactory = KotlinNativeCompilationFactory(project, this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
KotlinNativeTargetConfigurator(kotlinPluginVersion).configureTarget(result)
// Allow IDE to resolve the libraries provided by the compiler by adding them into dependencies.
result.compilations.all { compilation ->
val target = compilation.target.konanTarget
// First, put common libs:
defaultLibs().forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
// Then, platform-specific libs:
defaultLibs(target).forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
}
if (!konanTarget.enabledOnCurrentHost) {
with(HostManager()) {
val supportedHosts = enabledTargetsByHost.filterValues { konanTarget in it }.keys
val supportedHostsString =
if (supportedHosts.size == 1)
"a ${supportedHosts.single()} host" else
"one of the host platforms: ${supportedHosts.joinToString(", ")}"
project.logger.warn(
"Target '$name' for platform ${konanTarget} is ignored during build on this ${HostManager.host} machine. " +
"You can build it with $supportedHostsString."
)
}
}
return result
}
companion object {
private const val KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY = "konanHome"
}
}
internal val KonanTarget.isCurrentHost: Boolean
get() = this == HostManager.host
internal val KonanTarget.enabledOnCurrentHost
get() = HostManager().isEnabled(this)
internal val KotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME
@@ -0,0 +1,471 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.SourceSet
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.util.*
import java.util.concurrent.Callable
internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: String? = null): String {
val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
val targetNamePart = target.disambiguationClassifier
return lowerCamelCaseName(prefix, targetNamePart, compilationNamePart, suffix)
}
internal val KotlinCompilation<*>.defaultSourceSetName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName)
abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
target: KotlinTarget,
override val compilationName: String
) : KotlinCompilation<T>, HasKotlinDependencies {
override val kotlinOptions: T
get() = compileKotlinTask.kotlinOptions
override fun kotlinOptions(configure: T.() -> Unit) =
configure(kotlinOptions)
@Suppress("UNCHECKED_CAST")
override val compileKotlinTask: KotlinCompile<T>
get() = (target.project.tasks.getByName(compileKotlinTaskName) as KotlinCompile<T>)
// Don't declare this property in the constructor to avoid NPE
// when an overriding property of a subclass is accessed instead.
override val target: KotlinTarget = target
private val attributeContainer = HierarchyAttributeContainer(target.attributes)
override fun getAttributes(): AttributeContainer = attributeContainer
override val kotlinSourceSets: MutableSet<KotlinSourceSet> = mutableSetOf()
override val allKotlinSourceSets: Set<KotlinSourceSet>
get() = kotlinSourceSets.flatMapTo(mutableSetOf()) { it.getSourceSetHierarchy() }
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(defaultSourceSetName)
override fun defaultSourceSet(configure: KotlinSourceSet.() -> Unit) =
configure(defaultSourceSet)
override val output: KotlinCompilationOutput by lazy {
DefaultKotlinCompilationOutput(
target.project,
Callable { target.project.buildDir.resolve("processedResources/${target.targetName}/$name") })
}
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
fun AbstractKotlinCompile<*>.configureAction() {
source(sourceSet.kotlin)
sourceFilesExtensions(sourceSet.customSourceFilesExtensions)
if (addAsCommonSources) {
commonSourceSet += sourceSet.kotlin
}
}
// Note! Invocation of withType-all results in preliminary task instantiation.
// After fix of this issue the following code should be uncommented:
// if (useLazyTaskConfiguration) {
// (target.project.tasks.named(compileKotlinTaskName) as TaskProvider<AbstractKotlinCompile<*>>).configure {
// it.configureAction()
// }
// }
target.project.tasks
// To configure a task that may have not yet been created at this point, use 'withType-matching-all`:
.withType(AbstractKotlinCompile::class.java)
.matching { it.name == compileKotlinTaskName }
.all { compileKotlinTask ->
compileKotlinTask.configureAction()
}
}
override fun source(sourceSet: KotlinSourceSet) {
if (kotlinSourceSets.add(sourceSet)) {
//TODO possibly issue with forced instantiation
target.project.whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
val isCommonSource =
CompilationSourceSetUtil.sourceSetsInMultipleCompilations(project)?.contains(sourceSet) ?: false
addSourcesToCompileTask(sourceSet, addAsCommonSources = isCommonSource)
// Use `forced = false` since `api`, `implementation`, and `compileOnly` may be missing in some cases like
// old Java & Android projects:
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName, forced = false)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName, forced = false)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName, forced = false)
if (this@AbstractKotlinCompilation is KotlinCompilationToRunnableFiles<*>) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName, forced = false)
}
if (sourceSet.name != defaultSourceSetName) {
kotlinExtension.sourceSets.findByName(defaultSourceSetName)?.let { defaultSourceSet ->
// Temporary solution for checking consistency across source sets participating in a compilation that may
// not be interconnected with the dependsOn relation: check the settings as if the default source set of
// the compilation depends on the one added to the compilation:
defaultSourceSetLanguageSettingsChecker.runAllChecks(
defaultSourceSet,
sourceSet
)
}
}
}
}
}
}
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileClasspath"
)
override val compileKotlinTaskName: String
get() = lowerCamelCaseName(
"compile",
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"Kotlin",
target.targetName
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "classes")
override lateinit var compileDependencyFiles: FileCollection
override val apiConfigurationName: String
get() = disambiguateName("api")
override val implementationConfigurationName: String
get() = disambiguateName("implementation")
override val compileOnlyConfigurationName: String
get() = disambiguateName("compileOnly")
override val runtimeOnlyConfigurationName: String
get() = disambiguateName("runtimeOnly")
override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit =
DefaultKotlinDependencyHandler(this, target.project).run(configure)
override fun dependencies(configureClosure: Closure<Any?>) =
dependencies f@{ ConfigureUtil.configure(configureClosure, this@f) }
override fun toString(): String = "compilation '$compilationName' ($target)"
}
abstract class AbstractKotlinCompilationToRunnableFiles<T : KotlinCommonOptions>(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<T>(target, name), KotlinCompilationToRunnableFiles<T> {
override val runtimeDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"runtimeClasspath"
)
override lateinit var runtimeDependencyFiles: FileCollection
}
internal fun KotlinCompilation<*>.disambiguateName(simpleName: String): String {
return lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
simpleName
)
}
open class KotlinJvmCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name), KotlinCompilationWithResources<KotlinJvmOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
}
class KotlinWithJavaCompilation<KotlinOptionsType : KotlinCommonOptions>(
target: KotlinWithJavaTarget<KotlinOptionsType>,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinOptionsType>(target, name), KotlinCompilationWithResources<KotlinOptionsType> {
lateinit var javaSourceSet: SourceSet
override val output: KotlinCompilationOutput by lazy { KotlinWithJavaCompilationOutput(this) }
override val processResourcesTaskName: String
get() = javaSourceSet.processResourcesTaskName
override var runtimeDependencyFiles: FileCollection
get() = javaSourceSet.runtimeClasspath
set(value) {
javaSourceSet.runtimeClasspath = value
}
override val runtimeDependencyConfigurationName: String
get() = javaSourceSet.runtimeClasspathConfigurationName
override val compileDependencyConfigurationName: String
get() = javaSourceSet.compileClasspathConfigurationName
override val runtimeOnlyConfigurationName: String
get() = javaSourceSet.runtimeOnlyConfigurationName
override val implementationConfigurationName: String
get() = javaSourceSet.implementationConfigurationName
override val apiConfigurationName: String
get() = javaSourceSet.apiConfigurationName
override val compileOnlyConfigurationName: String
get() = javaSourceSet.compileOnlyConfigurationName
override val compileAllTaskName: String
get() = javaSourceSet.classesTaskName
override var compileDependencyFiles: FileCollection
get() = javaSourceSet.compileClasspath
set(value) {
javaSourceSet.compileClasspath = value
}
fun source(javaSourceSet: SourceSet) {
with(target.project) {
afterEvaluate {
(tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(javaSourceSet.java)
}
}
}
}
class KotlinJvmAndroidCompilation(
target: KotlinAndroidTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name) {
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
override val relatedConfigurationNames: List<String>
get() = super.relatedConfigurationNames + listOf("${name}ApiElements", "${name}RuntimeElements")
}
class KotlinJsCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJsOptions>(target, name), KotlinCompilationWithResources<KotlinJsOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: Kotlin2JsCompile
get() = super.compileKotlinTask as Kotlin2JsCompile
}
class KotlinCommonCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<KotlinMultiplatformCommonOptions>(target, name) {
override val compileKotlinTask: KotlinCompileCommon
get() = super.compileKotlinTask as KotlinCompileCommon
// TODO once we properly compile metadata for each source set, the default source sets will likely become just the source sets
// which are transformed to metadata
private val commonSourceSetName = when (compilationName) {
KotlinCompilation.MAIN_COMPILATION_NAME -> KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
else -> error("Custom metadata compilations are not supported yet")
}
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(commonSourceSetName)
}
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
name: String
) : AbstractKotlinCompilation<KotlinCommonOptions>(target, name), KotlinCompilationWithResources<KotlinCommonOptions> {
override val kotlinOptions: KotlinCommonOptions
get() = compileKotlinTask.kotlinOptions
override val compileKotlinTask: KotlinNativeCompile
get() = super.compileKotlinTask as KotlinNativeCompile
private val project: Project
get() = target.project
// A collection containing all source sets used by this compilation
// (taking into account dependencies between source sets). Used by both compilation
// and linking tasks. Unlike kotlinSourceSets, includes dependency source sets.
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val allSources: MutableSet<SourceDirectorySet> = mutableSetOf()
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val commonSources: MutableSet<SourceDirectorySet> = mutableSetOf()
var isTestCompilation = false
var friendCompilationName: String? = null
internal val friendCompilation: KotlinNativeCompilation?
get() = friendCompilationName?.let {
target.compilations.getByName(it)
}
// Used only to support the old APIs. TODO: Remove when the old APIs are removed.
internal val binaries = mutableMapOf<Pair<NativeOutputKind, NativeBuildType>, NativeBinary>()
// Native-specific DSL.
var extraOpts = mutableListOf<String>()
fun extraOpts(vararg values: Any) = extraOpts(values.toList())
fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
var buildTypes = mutableListOf<NativeBuildType>()
var outputKinds = mutableListOf<NativeOutputKind>()
fun outputKind(kind: NativeOutputKind) = outputKinds.add(kind)
fun outputKinds(vararg kinds: NativeOutputKind) {
outputKinds = kinds.toMutableList()
}
fun outputKinds(vararg kinds: String) {
outputKinds = kinds.map { NativeOutputKind.valueOf(it.toUpperCase()) }.toMutableList()
}
fun outputKinds(kinds: List<Any>) {
outputKinds = kinds.map {
when (it) {
is NativeOutputKind -> it
is String -> NativeOutputKind.valueOf(it.toUpperCase())
else -> error("Cannot use $it as an output kind")
}
}.toMutableList()
}
var entryPoint: String? = null
fun entryPoint(value: String) {
entryPoint = value
}
// Interop DSL.
val cinterops = project.container(DefaultCInteropSettings::class.java) { cinteropName ->
DefaultCInteropSettings(project, cinteropName, this)
}
var linkerOpts = mutableListOf<String>()
fun cinterops(action: NamedDomainObjectContainer<DefaultCInteropSettings>.() -> Unit) = cinterops.action()
fun cinterops(action: Closure<Unit>) = cinterops(ConfigureUtil.configureUsing(action))
fun cinterops(action: Action<NamedDomainObjectContainer<DefaultCInteropSettings>>) = action.execute(cinterops)
fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
fun linkerOpts(values: List<String>) {
linkerOpts.addAll(values)
}
// Task accessors.
fun findLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink? = binaries[kind to buildType]?.linkTask
fun getLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink =
findLinkTask(kind, buildType)
?: throw IllegalArgumentException("Cannot find a link task for the binary kind '$kind' and the build type '$buildType'")
fun findLinkTask(kind: String, buildType: String) =
findLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getLinkTask(kind: String, buildType: String) =
getLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun findBinary(kind: NativeOutputKind, buildType: NativeBuildType): File? = findLinkTask(kind, buildType)?.outputFile?.get()
fun getBinary(kind: NativeOutputKind, buildType: NativeBuildType): File = getLinkTask(kind, buildType).outputFile.get()
fun findBinary(kind: String, buildType: String) =
findBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getBinary(kind: String, buildType: String) =
getBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
// Naming
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
fun linkTaskName(kind: NativeOutputKind, buildType: NativeBuildType): String =
lowerCamelCaseName(
"link",
KotlinNativeBinaryContainer.generateBinaryName(compilationName, buildType, kind.taskNameClassifier),
target.targetName
)
fun linkTaskName(kind: String, buildType: String) =
linkTaskName(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileKlibraries"
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "klibrary")
val binariesTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "binaries")
override fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
allSources.add(sourceSet.kotlin)
if (addAsCommonSources) {
commonSources.add(sourceSet.kotlin)
}
}
}
private object CompilationSourceSetUtil {
// Cache the results per project
private val projectSourceSetsInMultipleCompilationsCache = WeakHashMap<Project, Set<KotlinSourceSet>>()
fun sourceSetsInMultipleCompilations(project: Project) =
projectSourceSetsInMultipleCompilationsCache.computeIfAbsent(project) { _ ->
check(project.state.executed) { "Should only be computed after the project is evaluated" }
val compilations = (project.kotlinExtension as? KotlinMultiplatformExtension)?.targets?.flatMap { it.compilations }
?: return@computeIfAbsent null
compilations
.flatMap { compilation -> compilation.allKotlinSourceSets.map { sourceSet -> compilation to sourceSet } }
.groupingBy { (_, sourceSet) -> sourceSet }
.eachCount()
.filterValues { it > 1 }
.keys
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
interface KotlinCompilationFactory<T : KotlinCompilation<*>> : NamedDomainObjectFactory<T> {
val itemClass: Class<T>
}
class KotlinCommonCompilationFactory(
val target: KotlinOnlyTarget<KotlinCommonCompilation>
) : KotlinCompilationFactory<KotlinCommonCompilation> {
override val itemClass: Class<KotlinCommonCompilation>
get() = KotlinCommonCompilation::class.java
override fun create(name: String): KotlinCommonCompilation =
KotlinCommonCompilation(target, name)
}
class KotlinJvmCompilationFactory(
val target: KotlinOnlyTarget<KotlinJvmCompilation>
) : KotlinCompilationFactory<KotlinJvmCompilation> {
override val itemClass: Class<KotlinJvmCompilation>
get() = KotlinJvmCompilation::class.java
override fun create(name: String): KotlinJvmCompilation =
KotlinJvmCompilation(target, name)
}
class KotlinWithJavaCompilationFactory<KotlinOptionsType : KotlinCommonOptions>(
val project: Project,
val target: KotlinWithJavaTarget<KotlinOptionsType>
) : KotlinCompilationFactory<KotlinWithJavaCompilation<KotlinOptionsType>> {
override val itemClass: Class<KotlinWithJavaCompilation<KotlinOptionsType>>
@Suppress("UNCHECKED_CAST")
get() = KotlinWithJavaCompilation::class.java as Class<KotlinWithJavaCompilation<KotlinOptionsType>>
override fun create(name: String): KotlinWithJavaCompilation<KotlinOptionsType> {
val result = KotlinWithJavaCompilation(target, name)
return result
}
}
class KotlinJvmAndroidCompilationFactory(
val project: Project,
val target: KotlinAndroidTarget
) : KotlinCompilationFactory<KotlinJvmAndroidCompilation> {
override val itemClass: Class<KotlinJvmAndroidCompilation>
get() = KotlinJvmAndroidCompilation::class.java
override fun create(name: String): KotlinJvmAndroidCompilation =
KotlinJvmAndroidCompilation(target, name)
}
class KotlinJsCompilationFactory(
val project: Project,
val target: KotlinOnlyTarget<KotlinJsCompilation>
) : KotlinCompilationFactory<KotlinJsCompilation> {
override val itemClass: Class<KotlinJsCompilation>
get() = KotlinJsCompilation::class.java
override fun create(name: String): KotlinJsCompilation =
KotlinJsCompilation(target, name)
}
class KotlinNativeCompilationFactory(
val project: Project,
val target: KotlinNativeTarget
) : KotlinCompilationFactory<KotlinNativeCompilation> {
override val itemClass: Class<KotlinNativeCompilation>
get() = KotlinNativeCompilation::class.java
override fun create(name: String): KotlinNativeCompilation =
KotlinNativeCompilation(target, name).apply {
if (name == KotlinCompilation.TEST_COMPILATION_NAME) {
friendCompilationName = KotlinCompilation.MAIN_COMPILATION_NAME
isTestCompilation = true
}
buildTypes = mutableListOf(NativeBuildType.DEBUG, NativeBuildType.RELEASE)
}
}
@@ -0,0 +1,471 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.SourceSet
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.util.*
import java.util.concurrent.Callable
internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: String? = null): String {
val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
val targetNamePart = target.disambiguationClassifier
return lowerCamelCaseName(prefix, targetNamePart, compilationNamePart, suffix)
}
internal val KotlinCompilation<*>.defaultSourceSetName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName)
abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
target: KotlinTarget,
override val compilationName: String
) : KotlinCompilation<T>, HasKotlinDependencies {
override val kotlinOptions: T
get() = compileKotlinTask.kotlinOptions
override fun kotlinOptions(configure: T.() -> Unit) =
configure(kotlinOptions)
@Suppress("UNCHECKED_CAST")
override val compileKotlinTask: KotlinCompile<T>
get() = (target.project.tasks.getByName(compileKotlinTaskName) as KotlinCompile<T>)
// Don't declare this property in the constructor to avoid NPE
// when an overriding property of a subclass is accessed instead.
override val target: KotlinTarget = target
private val attributeContainer = HierarchyAttributeContainer(target.attributes)
override fun getAttributes(): AttributeContainer = attributeContainer
override val kotlinSourceSets: MutableSet<KotlinSourceSet> = mutableSetOf()
override val allKotlinSourceSets: Set<KotlinSourceSet>
get() = kotlinSourceSets.flatMapTo(mutableSetOf()) { it.getSourceSetHierarchy() }
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(defaultSourceSetName)
override fun defaultSourceSet(configure: KotlinSourceSet.() -> Unit) =
configure(defaultSourceSet)
override val output: KotlinCompilationOutput by lazy {
DefaultKotlinCompilationOutput(
target.project,
Callable { target.project.buildDir.resolve("processedResources/${target.targetName}/$name") })
}
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
fun AbstractKotlinCompile<*>.configureAction() {
source(sourceSet.kotlin)
sourceFilesExtensions(sourceSet.customSourceFilesExtensions)
if (addAsCommonSources) {
commonSourceSet += sourceSet.kotlin
}
}
// Note! Invocation of withType-all results in preliminary task instantiation.
// After fix of this issue the following code should be uncommented:
// if (useLazyTaskConfiguration) {
// (target.project.tasks.named(compileKotlinTaskName) as TaskProvider<AbstractKotlinCompile<*>>).configure {
// it.configureAction()
// }
// }
target.project.tasks
// To configure a task that may have not yet been created at this point, use 'withType-matching-all`:
.withType(AbstractKotlinCompile::class.java)
.matching { it.name == compileKotlinTaskName }
.all { compileKotlinTask ->
compileKotlinTask.configureAction()
}
}
override fun source(sourceSet: KotlinSourceSet) {
if (kotlinSourceSets.add(sourceSet)) {
//TODO possibly issue with forced instantiation
target.project.whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
val isCommonSource =
CompilationSourceSetUtil.sourceSetsInMultipleCompilations(project)?.contains(sourceSet) ?: false
addSourcesToCompileTask(sourceSet, addAsCommonSources = isCommonSource)
// Use `forced = false` since `api`, `implementation`, and `compileOnly` may be missing in some cases like
// old Java & Android projects:
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName, forced = false)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName, forced = false)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName, forced = false)
if (this@AbstractKotlinCompilation is KotlinCompilationToRunnableFiles<*>) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName, forced = false)
}
if (sourceSet.name != defaultSourceSetName) {
kotlinExtension.sourceSets.findByName(defaultSourceSetName)?.let { defaultSourceSet ->
// Temporary solution for checking consistency across source sets participating in a compilation that may
// not be interconnected with the dependsOn relation: check the settings as if the default source set of
// the compilation depends on the one added to the compilation:
defaultSourceSetLanguageSettingsChecker.runAllChecks(
defaultSourceSet,
sourceSet
)
}
}
}
}
}
}
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileClasspath"
)
override val compileKotlinTaskName: String
get() = lowerCamelCaseName(
"compile",
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"Kotlin",
target.targetName
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "classes")
override lateinit var compileDependencyFiles: FileCollection
override val apiConfigurationName: String
get() = disambiguateName("api")
override val implementationConfigurationName: String
get() = disambiguateName("implementation")
override val compileOnlyConfigurationName: String
get() = disambiguateName("compileOnly")
override val runtimeOnlyConfigurationName: String
get() = disambiguateName("runtimeOnly")
override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit =
DefaultKotlinDependencyHandler(this, target.project).run(configure)
override fun dependencies(configureClosure: Closure<Any?>) =
dependencies f@{ ConfigureUtil.configure(configureClosure, this@f) }
override fun toString(): String = "compilation '$compilationName' ($target)"
}
abstract class AbstractKotlinCompilationToRunnableFiles<T : KotlinCommonOptions>(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<T>(target, name), KotlinCompilationToRunnableFiles<T> {
override val runtimeDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"runtimeClasspath"
)
override lateinit var runtimeDependencyFiles: FileCollection
}
internal fun KotlinCompilation<*>.disambiguateName(simpleName: String): String {
return lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
simpleName
)
}
open class KotlinJvmCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name), KotlinCompilationWithResources<KotlinJvmOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
}
class KotlinWithJavaCompilation<KotlinOptionsType : KotlinCommonOptions>(
target: KotlinWithJavaTarget<KotlinOptionsType>,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinOptionsType>(target, name), KotlinCompilationWithResources<KotlinOptionsType> {
lateinit var javaSourceSet: SourceSet
override val output: KotlinCompilationOutput by lazy { KotlinWithJavaCompilationOutput(this) }
override val processResourcesTaskName: String
get() = javaSourceSet.processResourcesTaskName
override var runtimeDependencyFiles: FileCollection
get() = javaSourceSet.runtimeClasspath
set(value) {
javaSourceSet.runtimeClasspath = value
}
override val runtimeDependencyConfigurationName: String
get() = javaSourceSet.runtimeClasspathConfigurationName
override val compileDependencyConfigurationName: String
get() = javaSourceSet.compileClasspathConfigurationName
override val runtimeOnlyConfigurationName: String
get() = javaSourceSet.runtimeOnlyConfigurationName
override val implementationConfigurationName: String
get() = javaSourceSet.implementationConfigurationName
override val apiConfigurationName: String
get() = javaSourceSet.apiConfigurationName
override val compileOnlyConfigurationName: String
get() = javaSourceSet.compileOnlyConfigurationName
override val compileAllTaskName: String
get() = javaSourceSet.classesTaskName
override var compileDependencyFiles: FileCollection
get() = javaSourceSet.compileClasspath
set(value) {
javaSourceSet.compileClasspath = value
}
fun source(javaSourceSet: SourceSet) {
with(target.project) {
afterEvaluate {
(tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(javaSourceSet.java)
}
}
}
}
class KotlinJvmAndroidCompilation(
target: KotlinAndroidTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name) {
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
override val relatedConfigurationNames: List<String>
get() = super.relatedConfigurationNames + listOf("${name}ApiElements", "${name}RuntimeElements")
}
class KotlinJsCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJsOptions>(target, name), KotlinCompilationWithResources<KotlinJsOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: Kotlin2JsCompile
get() = super.compileKotlinTask as Kotlin2JsCompile
}
class KotlinCommonCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<KotlinMultiplatformCommonOptions>(target, name) {
override val compileKotlinTask: KotlinCompileCommon
get() = super.compileKotlinTask as KotlinCompileCommon
// TODO once we properly compile metadata for each source set, the default source sets will likely become just the source sets
// which are transformed to metadata
private val commonSourceSetName = when (compilationName) {
KotlinCompilation.MAIN_COMPILATION_NAME -> KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
else -> error("Custom metadata compilations are not supported yet")
}
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(commonSourceSetName)
}
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
name: String
) : AbstractKotlinCompilation<KotlinCommonOptions>(target, name), KotlinCompilationWithResources<KotlinCommonOptions> {
override val kotlinOptions: KotlinCommonOptions
get() = compileKotlinTask.kotlinOptions
override val compileKotlinTask: KotlinNativeCompile
get() = super.compileKotlinTask as KotlinNativeCompile
private val project: Project
get() = target.project
// A collection containing all source sets used by this compilation
// (taking into account dependencies between source sets). Used by both compilation
// and linking tasks. Unlike kotlinSourceSets, includes dependency source sets.
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val allSources: MutableSet<SourceDirectorySet> = mutableSetOf()
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val commonSources: MutableSet<SourceDirectorySet> = mutableSetOf()
var isTestCompilation = false
var friendCompilationName: String? = null
internal val friendCompilation: KotlinNativeCompilation?
get() = friendCompilationName?.let {
target.compilations.getByName(it)
}
// Used only to support the old APIs. TODO: Remove when the old APIs are removed.
internal val binaries = mutableMapOf<Pair<NativeOutputKind, NativeBuildType>, NativeBinary>()
// Native-specific DSL.
var extraOpts = mutableListOf<String>()
fun extraOpts(vararg values: Any) = extraOpts(values.toList())
fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
var buildTypes = mutableListOf<NativeBuildType>()
var outputKinds = mutableListOf<NativeOutputKind>()
fun outputKind(kind: NativeOutputKind) = outputKinds.add(kind)
fun outputKinds(vararg kinds: NativeOutputKind) {
outputKinds = kinds.toMutableList()
}
fun outputKinds(vararg kinds: String) {
outputKinds = kinds.map { NativeOutputKind.valueOf(it.toUpperCase()) }.toMutableList()
}
fun outputKinds(kinds: List<Any>) {
outputKinds = kinds.map {
when (it) {
is NativeOutputKind -> it
is String -> NativeOutputKind.valueOf(it.toUpperCase())
else -> error("Cannot use $it as an output kind")
}
}.toMutableList()
}
var entryPoint: String? = null
fun entryPoint(value: String) {
entryPoint = value
}
// Interop DSL.
val cinterops = project.container(DefaultCInteropSettings::class.java) { cinteropName ->
DefaultCInteropSettings(project, cinteropName, this)
}
var linkerOpts = mutableListOf<String>()
fun cinterops(action: NamedDomainObjectContainer<DefaultCInteropSettings>.() -> Unit) = cinterops.action()
fun cinterops(action: Closure<Unit>) = cinterops(ConfigureUtil.configureUsing(action))
fun cinterops(action: Action<NamedDomainObjectContainer<DefaultCInteropSettings>>) = action.execute(cinterops)
fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
fun linkerOpts(values: List<String>) {
linkerOpts.addAll(values)
}
// Task accessors.
fun findLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink? = binaries[kind to buildType]?.linkTask
fun getLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink =
findLinkTask(kind, buildType)
?: throw IllegalArgumentException("Cannot find a link task for the binary kind '$kind' and the build type '$buildType'")
fun findLinkTask(kind: String, buildType: String) =
findLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getLinkTask(kind: String, buildType: String) =
getLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun findBinary(kind: NativeOutputKind, buildType: NativeBuildType): File? = findLinkTask(kind, buildType)?.outputFile?.get()
fun getBinary(kind: NativeOutputKind, buildType: NativeBuildType): File = getLinkTask(kind, buildType).outputFile.get()
fun findBinary(kind: String, buildType: String) =
findBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getBinary(kind: String, buildType: String) =
getBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
// Naming
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
fun linkTaskName(kind: NativeOutputKind, buildType: NativeBuildType): String =
lowerCamelCaseName(
"link",
KotlinNativeBinaryContainer.generateBinaryName(compilationName, buildType, kind.taskNameClassifier),
target.targetName
)
fun linkTaskName(kind: String, buildType: String) =
linkTaskName(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileKlibraries"
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "klibrary")
val binariesTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "binaries")
override fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
allSources.add(sourceSet.kotlin)
if (addAsCommonSources) {
commonSources.add(sourceSet.kotlin)
}
}
}
private object CompilationSourceSetUtil {
// Cache the results per project
private val projectSourceSetsInMultipleCompilationsCache = WeakHashMap<Project, Set<KotlinSourceSet>>()
fun sourceSetsInMultipleCompilations(project: Project) =
projectSourceSetsInMultipleCompilationsCache.computeIfAbsent(project) { _ ->
check(project.state.executed) { "Should only be computed after the project is evaluated" }
val compilations = (project.kotlinExtension as? KotlinMultiplatformExtension)?.targets?.flatMap { it.compilations }
?: return@computeIfAbsent null
compilations
.flatMap { compilation -> compilation.allKotlinSourceSets.map { sourceSet -> compilation to sourceSet } }
.groupingBy { (_, sourceSet) -> sourceSet }
.eachCount()
.filterValues { it > 1 }
.keys
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
interface KotlinCompilationFactory<T : KotlinCompilation<*>> : NamedDomainObjectFactory<T> {
val itemClass: Class<T>
}
class KotlinCommonCompilationFactory(
val target: KotlinOnlyTarget<KotlinCommonCompilation>
) : KotlinCompilationFactory<KotlinCommonCompilation> {
override val itemClass: Class<KotlinCommonCompilation>
get() = KotlinCommonCompilation::class.java
override fun create(name: String): KotlinCommonCompilation =
KotlinCommonCompilation(target, name)
}
class KotlinJvmCompilationFactory(
val target: KotlinOnlyTarget<KotlinJvmCompilation>
) : KotlinCompilationFactory<KotlinJvmCompilation> {
override val itemClass: Class<KotlinJvmCompilation>
get() = KotlinJvmCompilation::class.java
override fun create(name: String): KotlinJvmCompilation =
KotlinJvmCompilation(target, name)
}
class KotlinWithJavaCompilationFactory<KotlinOptionsType : KotlinCommonOptions>(
val project: Project,
val target: KotlinWithJavaTarget<KotlinOptionsType>
) : KotlinCompilationFactory<KotlinWithJavaCompilation<KotlinOptionsType>> {
override val itemClass: Class<KotlinWithJavaCompilation<KotlinOptionsType>>
@Suppress("UNCHECKED_CAST")
get() = KotlinWithJavaCompilation::class.java as Class<KotlinWithJavaCompilation<KotlinOptionsType>>
override fun create(name: String): KotlinWithJavaCompilation<KotlinOptionsType> {
val result = KotlinWithJavaCompilation(target, name)
return result
}
}
class KotlinJvmAndroidCompilationFactory(
val project: Project,
val target: KotlinAndroidTarget
) : KotlinCompilationFactory<KotlinJvmAndroidCompilation> {
override val itemClass: Class<KotlinJvmAndroidCompilation>
get() = KotlinJvmAndroidCompilation::class.java
override fun create(name: String): KotlinJvmAndroidCompilation =
KotlinJvmAndroidCompilation(target, name)
}
class KotlinJsCompilationFactory(
val project: Project,
val target: KotlinOnlyTarget<KotlinJsCompilation>
) : KotlinCompilationFactory<KotlinJsCompilation> {
override val itemClass: Class<KotlinJsCompilation>
get() = KotlinJsCompilation::class.java
override fun create(name: String): KotlinJsCompilation =
KotlinJsCompilation(target, name)
}
class KotlinNativeCompilationFactory(
val project: Project,
val target: KotlinNativeTarget
) : KotlinCompilationFactory<KotlinNativeCompilation> {
override val itemClass: Class<KotlinNativeCompilation>
get() = KotlinNativeCompilation::class.java
override fun create(name: String): KotlinNativeCompilation =
KotlinNativeCompilation(target, name).apply {
if (name == KotlinCompilation.TEST_COMPILATION_NAME) {
friendCompilationName = KotlinCompilation.MAIN_COMPILATION_NAME
isTestCompilation = true
}
buildTypes = mutableListOf(NativeBuildType.DEBUG, NativeBuildType.RELEASE)
}
}
@@ -0,0 +1,330 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.compilerRunner.KotlinNativeProjectProperty
import org.jetbrains.kotlin.compilerRunner.hasProperty
import org.jetbrains.kotlin.compilerRunner.konanHome
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import java.util.*
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation<*>>(
protected val project: Project,
private val instantiator: Instantiator,
private val fileResolver: FileResolver,
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
protected open fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T> =
KotlinTargetConfigurator(createDefaultSourceSets = true, createTestCompilation = true)
override fun createTarget(name: String): KotlinOnlyTarget<T> {
val result = KotlinOnlyTarget<T>(project, platformType).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinOnlyTargetPreset
val compilationFactory = createCompilationFactory(this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
createKotlinTargetConfigurator().configureTarget(result)
result.compilations.all { compilation ->
buildCompilationProcessor(compilation).run()
if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
sourcesJarTask(compilation, result.targetName, result.targetName.toLowerCase())
}
}
return result
}
protected abstract fun createCompilationFactory(forTarget: KotlinOnlyTarget<T>): KotlinCompilationFactory<T>
protected abstract val platformType: KotlinPlatformType
internal abstract fun buildCompilationProcessor(compilation: T): KotlinSourceSetProcessor<*>
}
class KotlinMetadataTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(
forTarget: KotlinOnlyTarget<KotlinCommonCompilation>
): KotlinCompilationFactory<KotlinCommonCompilation> =
KotlinCommonCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.common
override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "metadata"
}
override fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<KotlinCommonCompilation> =
KotlinTargetConfigurator(createDefaultSourceSets = false, createTestCompilation = false)
override fun createTarget(name: String): KotlinOnlyTarget<KotlinCommonCompilation> =
super.createTarget(name).apply {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val commonMainSourceSet = project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
mainCompilation.source(commonMainSourceSet)
project.afterEvaluate {
// Since there's no default source set, apply language settings from commonMain:
val compileKotlinMetadata = project.tasks.getByName(mainCompilation.compileKotlinTaskName) as KotlinCompile<*>
applyLanguageSettingsToKotlinTask(commonMainSourceSet.languageSettings, compileKotlinMetadata)
}
}
}
class KotlinJvmTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> =
KotlinJvmCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.jvm
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "jvm"
}
}
class KotlinJsTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJsCompilation>) =
KotlinJsCompilationFactory(project, forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JsSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "js"
}
}
class KotlinAndroidTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinAndroidTarget> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinAndroidTarget {
val result = KotlinAndroidTarget(name, project).apply {
disambiguationClassifier = name
preset = this@KotlinAndroidTargetPreset
}
KotlinAndroidPlugin.applyToTarget(kotlinPluginVersion, result)
return result
}
companion object {
const val PRESET_NAME = "android"
}
}
class KotlinJvmWithJavaTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinWithJavaTarget<KotlinJvmOptions>> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinWithJavaTarget<KotlinJvmOptions> {
project.plugins.apply(JavaPlugin::class.java)
val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, name).apply {
disambiguationClassifier = name
preset = this@KotlinJvmWithJavaTargetPreset
}
AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(name), compilation, kotlinPluginVersion)
}
target.compilations.getByName("test").run {
val main = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
compileDependencyFiles = project.files(
main.output.allOutputs,
project.configurations.maybeCreate(compileDependencyConfigurationName)
)
runtimeDependencyFiles = project.files(
output.allOutputs,
main.output.allOutputs,
project.configurations.maybeCreate(runtimeDependencyConfigurationName)
)
}
return target
}
companion object {
const val PRESET_NAME = "jvmWithJava"
}
}
class KotlinNativeTargetPreset(
private val name: String,
val project: Project,
val konanTarget: KonanTarget,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinNativeTarget> {
init {
// This is required to obtain Kotlin/Native home in CLion plugin:
setupNativeHomePrivateProperty()
}
override fun getName(): String = name
private fun setupNativeHomePrivateProperty() = with(project) {
if (!hasProperty(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY))
extensions.extraProperties.set(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY, konanHome)
}
private fun setupNativeCompiler() = with(project) {
if (!hasProperty(KotlinNativeProjectProperty.KONAN_HOME_OVERRIDE)) {
NativeCompilerDownloader(this).downloadIfNeeded()
logger.info("Kotlin/Native distribution: $konanHome")
} else {
logger.info("User-provided Kotlin/Native distribution: $konanHome")
}
}
// We declare default K/N dependencies as files to avoid searching them in remote repos (see KT-28128).
private fun defaultLibs(target: KonanTarget? = null): List<Dependency> = with(project) {
val relPath = if (target != null) "platform/${target.name}" else "common"
file("$konanHome/klib/$relPath")
.listFiles { file -> file.isDirectory }
?.sortedBy { dir -> dir.name.toLowerCase() }
?.map { dir -> dependencies.create(files(dir)) } ?: emptyList()
}
override fun createTarget(name: String): KotlinNativeTarget {
setupNativeCompiler()
val result = KotlinNativeTarget(project, konanTarget).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinNativeTargetPreset
val compilationFactory = KotlinNativeCompilationFactory(project, this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
KotlinNativeTargetConfigurator(kotlinPluginVersion).configureTarget(result)
// Allow IDE to resolve the libraries provided by the compiler by adding them into dependencies.
result.compilations.all { compilation ->
val target = compilation.target.konanTarget
// First, put common libs:
defaultLibs().forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
// Then, platform-specific libs:
defaultLibs(target).forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
}
if (!konanTarget.enabledOnCurrentHost) {
with(HostManager()) {
val supportedHosts = enabledTargetsByHost.filterValues { konanTarget in it }.keys
val supportedHostsString =
if (supportedHosts.size == 1)
"a ${supportedHosts.single()} host" else
"one of the host platforms: ${supportedHosts.joinToString(", ")}"
project.logger.warn(
"Target '$name' for platform ${konanTarget} is ignored during build on this ${HostManager.host} machine. " +
"You can build it with $supportedHostsString."
)
}
}
return result
}
companion object {
private const val KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY = "konanHome"
}
}
internal val KonanTarget.isCurrentHost: Boolean
get() = this == HostManager.host
internal val KonanTarget.enabledOnCurrentHost
get() = HostManager().isEnabled(this)
internal val KotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME
@@ -0,0 +1,330 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.compilerRunner.KotlinNativeProjectProperty
import org.jetbrains.kotlin.compilerRunner.hasProperty
import org.jetbrains.kotlin.compilerRunner.konanHome
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import java.util.*
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation<*>>(
protected val project: Project,
private val instantiator: Instantiator,
private val fileResolver: FileResolver,
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
protected open fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T> =
KotlinTargetConfigurator(createDefaultSourceSets = true, createTestCompilation = true)
override fun createTarget(name: String): KotlinOnlyTarget<T> {
val result = KotlinOnlyTarget<T>(project, platformType).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinOnlyTargetPreset
val compilationFactory = createCompilationFactory(this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
createKotlinTargetConfigurator().configureTarget(result)
result.compilations.all { compilation ->
buildCompilationProcessor(compilation).run()
if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
sourcesJarTask(compilation, result.targetName, result.targetName.toLowerCase())
}
}
return result
}
protected abstract fun createCompilationFactory(forTarget: KotlinOnlyTarget<T>): KotlinCompilationFactory<T>
protected abstract val platformType: KotlinPlatformType
internal abstract fun buildCompilationProcessor(compilation: T): KotlinSourceSetProcessor<*>
}
class KotlinMetadataTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(
forTarget: KotlinOnlyTarget<KotlinCommonCompilation>
): KotlinCompilationFactory<KotlinCommonCompilation> =
KotlinCommonCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.common
override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "metadata"
}
override fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<KotlinCommonCompilation> =
KotlinTargetConfigurator(createDefaultSourceSets = false, createTestCompilation = false)
override fun createTarget(name: String): KotlinOnlyTarget<KotlinCommonCompilation> =
super.createTarget(name).apply {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val commonMainSourceSet = project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
mainCompilation.source(commonMainSourceSet)
project.afterEvaluate {
// Since there's no default source set, apply language settings from commonMain:
val compileKotlinMetadata = project.tasks.getByName(mainCompilation.compileKotlinTaskName) as KotlinCompile<*>
applyLanguageSettingsToKotlinTask(commonMainSourceSet.languageSettings, compileKotlinMetadata)
}
}
}
class KotlinJvmTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> =
KotlinJvmCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.jvm
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "jvm"
}
}
class KotlinJsTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJsCompilation>) =
KotlinJsCompilationFactory(project, forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JsSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "js"
}
}
class KotlinAndroidTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinAndroidTarget> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinAndroidTarget {
val result = KotlinAndroidTarget(name, project).apply {
disambiguationClassifier = name
preset = this@KotlinAndroidTargetPreset
}
KotlinAndroidPlugin.applyToTarget(kotlinPluginVersion, result)
return result
}
companion object {
const val PRESET_NAME = "android"
}
}
class KotlinJvmWithJavaTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinWithJavaTarget<KotlinJvmOptions>> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinWithJavaTarget<KotlinJvmOptions> {
project.plugins.apply(JavaPlugin::class.java)
val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, name).apply {
disambiguationClassifier = name
preset = this@KotlinJvmWithJavaTargetPreset
}
AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(name), compilation, kotlinPluginVersion)
}
target.compilations.getByName("test").run {
val main = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
compileDependencyFiles = project.files(
main.output.allOutputs,
project.configurations.maybeCreate(compileDependencyConfigurationName)
)
runtimeDependencyFiles = project.files(
output.allOutputs,
main.output.allOutputs,
project.configurations.maybeCreate(runtimeDependencyConfigurationName)
)
}
return target
}
companion object {
const val PRESET_NAME = "jvmWithJava"
}
}
class KotlinNativeTargetPreset(
private val name: String,
val project: Project,
val konanTarget: KonanTarget,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinNativeTarget> {
init {
// This is required to obtain Kotlin/Native home in CLion plugin:
setupNativeHomePrivateProperty()
}
override fun getName(): String = name
private fun setupNativeHomePrivateProperty() = with(project) {
if (!hasProperty(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY))
extensions.extraProperties.set(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY, konanHome)
}
private fun setupNativeCompiler() = with(project) {
if (!hasProperty(KotlinNativeProjectProperty.KONAN_HOME_OVERRIDE)) {
NativeCompilerDownloader(this).downloadIfNeeded()
logger.info("Kotlin/Native distribution: $konanHome")
} else {
logger.info("User-provided Kotlin/Native distribution: $konanHome")
}
}
// We declare default K/N dependencies as files to avoid searching them in remote repos (see KT-28128).
private fun defaultLibs(target: KonanTarget? = null): List<Dependency> = with(project) {
val relPath = if (target != null) "platform/${target.name}" else "common"
file("$konanHome/klib/$relPath")
.listFiles { file -> file.isDirectory }
?.sortedBy { dir -> dir.name.toLowerCase() }
?.map { dir -> dependencies.create(files(dir)) } ?: emptyList()
}
override fun createTarget(name: String): KotlinNativeTarget {
setupNativeCompiler()
val result = KotlinNativeTarget(project, konanTarget).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinNativeTargetPreset
val compilationFactory = KotlinNativeCompilationFactory(project, this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
KotlinNativeTargetConfigurator(kotlinPluginVersion).configureTarget(result)
// Allow IDE to resolve the libraries provided by the compiler by adding them into dependencies.
result.compilations.all { compilation ->
val target = compilation.target.konanTarget
// First, put common libs:
defaultLibs().forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
// Then, platform-specific libs:
defaultLibs(target).forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
}
if (!konanTarget.enabledOnCurrentHost) {
with(HostManager()) {
val supportedHosts = enabledTargetsByHost.filterValues { konanTarget in it }.keys
val supportedHostsString =
if (supportedHosts.size == 1)
"a ${supportedHosts.single()} host" else
"one of the host platforms: ${supportedHosts.joinToString(", ")}"
project.logger.warn(
"Target '$name' for platform ${konanTarget} is ignored during build on this ${HostManager.host} machine. " +
"You can build it with $supportedHostsString."
)
}
}
return result
}
companion object {
private const val KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY = "konanHome"
}
}
internal val KonanTarget.isCurrentHost: Boolean
get() = this == HostManager.host
internal val KonanTarget.enabledOnCurrentHost
get() = HostManager().isEnabled(this)
internal val KotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME
@@ -0,0 +1,330 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.compilerRunner.KotlinNativeProjectProperty
import org.jetbrains.kotlin.compilerRunner.hasProperty
import org.jetbrains.kotlin.compilerRunner.konanHome
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import java.util.*
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation<*>>(
protected val project: Project,
private val instantiator: Instantiator,
private val fileResolver: FileResolver,
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
protected open fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T> =
KotlinTargetConfigurator(createDefaultSourceSets = true, createTestCompilation = true)
override fun createTarget(name: String): KotlinOnlyTarget<T> {
val result = KotlinOnlyTarget<T>(project, platformType).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinOnlyTargetPreset
val compilationFactory = createCompilationFactory(this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
createKotlinTargetConfigurator().configureTarget(result)
result.compilations.all { compilation ->
buildCompilationProcessor(compilation).run()
if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
sourcesJarTask(compilation, result.targetName, result.targetName.toLowerCase())
}
}
return result
}
protected abstract fun createCompilationFactory(forTarget: KotlinOnlyTarget<T>): KotlinCompilationFactory<T>
protected abstract val platformType: KotlinPlatformType
internal abstract fun buildCompilationProcessor(compilation: T): KotlinSourceSetProcessor<*>
}
class KotlinMetadataTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(
forTarget: KotlinOnlyTarget<KotlinCommonCompilation>
): KotlinCompilationFactory<KotlinCommonCompilation> =
KotlinCommonCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.common
override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "metadata"
}
override fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<KotlinCommonCompilation> =
KotlinTargetConfigurator(createDefaultSourceSets = false, createTestCompilation = false)
override fun createTarget(name: String): KotlinOnlyTarget<KotlinCommonCompilation> =
super.createTarget(name).apply {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val commonMainSourceSet = project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
mainCompilation.source(commonMainSourceSet)
project.afterEvaluate {
// Since there's no default source set, apply language settings from commonMain:
val compileKotlinMetadata = project.tasks.getByName(mainCompilation.compileKotlinTaskName) as KotlinCompile<*>
applyLanguageSettingsToKotlinTask(commonMainSourceSet.languageSettings, compileKotlinMetadata)
}
}
}
class KotlinJvmTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> =
KotlinJvmCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.jvm
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "jvm"
}
}
class KotlinJsTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJsCompilation>) =
KotlinJsCompilationFactory(project, forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JsSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "js"
}
}
class KotlinAndroidTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinAndroidTarget> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinAndroidTarget {
val result = KotlinAndroidTarget(name, project).apply {
disambiguationClassifier = name
preset = this@KotlinAndroidTargetPreset
}
KotlinAndroidPlugin.applyToTarget(kotlinPluginVersion, result)
return result
}
companion object {
const val PRESET_NAME = "android"
}
}
class KotlinJvmWithJavaTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinWithJavaTarget<KotlinJvmOptions>> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinWithJavaTarget<KotlinJvmOptions> {
project.plugins.apply(JavaPlugin::class.java)
val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, name).apply {
disambiguationClassifier = name
preset = this@KotlinJvmWithJavaTargetPreset
}
AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(name), compilation, kotlinPluginVersion)
}
target.compilations.getByName("test").run {
val main = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
compileDependencyFiles = project.files(
main.output.allOutputs,
project.configurations.maybeCreate(compileDependencyConfigurationName)
)
runtimeDependencyFiles = project.files(
output.allOutputs,
main.output.allOutputs,
project.configurations.maybeCreate(runtimeDependencyConfigurationName)
)
}
return target
}
companion object {
const val PRESET_NAME = "jvmWithJava"
}
}
class KotlinNativeTargetPreset(
private val name: String,
val project: Project,
val konanTarget: KonanTarget,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinNativeTarget> {
init {
// This is required to obtain Kotlin/Native home in CLion plugin:
setupNativeHomePrivateProperty()
}
override fun getName(): String = name
private fun setupNativeHomePrivateProperty() = with(project) {
if (!hasProperty(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY))
extensions.extraProperties.set(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY, konanHome)
}
private fun setupNativeCompiler() = with(project) {
if (!hasProperty(KotlinNativeProjectProperty.KONAN_HOME_OVERRIDE)) {
NativeCompilerDownloader(this).downloadIfNeeded()
logger.info("Kotlin/Native distribution: $konanHome")
} else {
logger.info("User-provided Kotlin/Native distribution: $konanHome")
}
}
// We declare default K/N dependencies as files to avoid searching them in remote repos (see KT-28128).
private fun defaultLibs(target: KonanTarget? = null): List<Dependency> = with(project) {
val relPath = if (target != null) "platform/${target.name}" else "common"
file("$konanHome/klib/$relPath")
.listFiles { file -> file.isDirectory }
?.sortedBy { dir -> dir.name.toLowerCase() }
?.map { dir -> dependencies.create(files(dir)) } ?: emptyList()
}
override fun createTarget(name: String): KotlinNativeTarget {
setupNativeCompiler()
val result = KotlinNativeTarget(project, konanTarget).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinNativeTargetPreset
val compilationFactory = KotlinNativeCompilationFactory(project, this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
KotlinNativeTargetConfigurator(kotlinPluginVersion).configureTarget(result)
// Allow IDE to resolve the libraries provided by the compiler by adding them into dependencies.
result.compilations.all { compilation ->
val target = compilation.target.konanTarget
// First, put common libs:
defaultLibs().forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
// Then, platform-specific libs:
defaultLibs(target).forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
}
if (!konanTarget.enabledOnCurrentHost) {
with(HostManager()) {
val supportedHosts = enabledTargetsByHost.filterValues { konanTarget in it }.keys
val supportedHostsString =
if (supportedHosts.size == 1)
"a ${supportedHosts.single()} host" else
"one of the host platforms: ${supportedHosts.joinToString(", ")}"
project.logger.warn(
"Target '$name' for platform ${konanTarget} is ignored during build on this ${HostManager.host} machine. " +
"You can build it with $supportedHostsString."
)
}
}
return result
}
companion object {
private const val KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY = "konanHome"
}
}
internal val KonanTarget.isCurrentHost: Boolean
get() = this == HostManager.host
internal val KonanTarget.enabledOnCurrentHost
get() = HostManager().isEnabled(this)
internal val KotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME
@@ -0,0 +1,471 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.SourceSet
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.util.*
import java.util.concurrent.Callable
internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: String? = null): String {
val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
val targetNamePart = target.disambiguationClassifier
return lowerCamelCaseName(prefix, targetNamePart, compilationNamePart, suffix)
}
internal val KotlinCompilation<*>.defaultSourceSetName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName)
abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
target: KotlinTarget,
override val compilationName: String
) : KotlinCompilation<T>, HasKotlinDependencies {
override val kotlinOptions: T
get() = compileKotlinTask.kotlinOptions
override fun kotlinOptions(configure: T.() -> Unit) =
configure(kotlinOptions)
@Suppress("UNCHECKED_CAST")
override val compileKotlinTask: KotlinCompile<T>
get() = (target.project.tasks.getByName(compileKotlinTaskName) as KotlinCompile<T>)
// Don't declare this property in the constructor to avoid NPE
// when an overriding property of a subclass is accessed instead.
override val target: KotlinTarget = target
private val attributeContainer = HierarchyAttributeContainer(target.attributes)
override fun getAttributes(): AttributeContainer = attributeContainer
override val kotlinSourceSets: MutableSet<KotlinSourceSet> = mutableSetOf()
override val allKotlinSourceSets: Set<KotlinSourceSet>
get() = kotlinSourceSets.flatMapTo(mutableSetOf()) { it.getSourceSetHierarchy() }
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(defaultSourceSetName)
override fun defaultSourceSet(configure: KotlinSourceSet.() -> Unit) =
configure(defaultSourceSet)
override val output: KotlinCompilationOutput by lazy {
DefaultKotlinCompilationOutput(
target.project,
Callable { target.project.buildDir.resolve("processedResources/${target.targetName}/$name") })
}
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
fun AbstractKotlinCompile<*>.configureAction() {
source(sourceSet.kotlin)
sourceFilesExtensions(sourceSet.customSourceFilesExtensions)
if (addAsCommonSources) {
commonSourceSet += sourceSet.kotlin
}
}
// Note! Invocation of withType-all results in preliminary task instantiation.
// After fix of this issue the following code should be uncommented:
// if (useLazyTaskConfiguration) {
// (target.project.tasks.named(compileKotlinTaskName) as TaskProvider<AbstractKotlinCompile<*>>).configure {
// it.configureAction()
// }
// }
target.project.tasks
// To configure a task that may have not yet been created at this point, use 'withType-matching-all`:
.withType(AbstractKotlinCompile::class.java)
.matching { it.name == compileKotlinTaskName }
.all { compileKotlinTask ->
compileKotlinTask.configureAction()
}
}
override fun source(sourceSet: KotlinSourceSet) {
if (kotlinSourceSets.add(sourceSet)) {
//TODO possibly issue with forced instantiation
target.project.whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
val isCommonSource =
CompilationSourceSetUtil.sourceSetsInMultipleCompilations(project)?.contains(sourceSet) ?: false
addSourcesToCompileTask(sourceSet, addAsCommonSources = isCommonSource)
// Use `forced = false` since `api`, `implementation`, and `compileOnly` may be missing in some cases like
// old Java & Android projects:
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName, forced = false)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName, forced = false)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName, forced = false)
if (this@AbstractKotlinCompilation is KotlinCompilationToRunnableFiles<*>) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName, forced = false)
}
if (sourceSet.name != defaultSourceSetName) {
kotlinExtension.sourceSets.findByName(defaultSourceSetName)?.let { defaultSourceSet ->
// Temporary solution for checking consistency across source sets participating in a compilation that may
// not be interconnected with the dependsOn relation: check the settings as if the default source set of
// the compilation depends on the one added to the compilation:
defaultSourceSetLanguageSettingsChecker.runAllChecks(
defaultSourceSet,
sourceSet
)
}
}
}
}
}
}
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileClasspath"
)
override val compileKotlinTaskName: String
get() = lowerCamelCaseName(
"compile",
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"Kotlin",
target.targetName
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "classes")
override lateinit var compileDependencyFiles: FileCollection
override val apiConfigurationName: String
get() = disambiguateName("api")
override val implementationConfigurationName: String
get() = disambiguateName("implementation")
override val compileOnlyConfigurationName: String
get() = disambiguateName("compileOnly")
override val runtimeOnlyConfigurationName: String
get() = disambiguateName("runtimeOnly")
override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit =
DefaultKotlinDependencyHandler(this, target.project).run(configure)
override fun dependencies(configureClosure: Closure<Any?>) =
dependencies f@{ ConfigureUtil.configure(configureClosure, this@f) }
override fun toString(): String = "compilation '$compilationName' ($target)"
}
abstract class AbstractKotlinCompilationToRunnableFiles<T : KotlinCommonOptions>(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<T>(target, name), KotlinCompilationToRunnableFiles<T> {
override val runtimeDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"runtimeClasspath"
)
override lateinit var runtimeDependencyFiles: FileCollection
}
internal fun KotlinCompilation<*>.disambiguateName(simpleName: String): String {
return lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
simpleName
)
}
open class KotlinJvmCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name), KotlinCompilationWithResources<KotlinJvmOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
}
class KotlinWithJavaCompilation<KotlinOptionsType : KotlinCommonOptions>(
target: KotlinWithJavaTarget<KotlinOptionsType>,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinOptionsType>(target, name), KotlinCompilationWithResources<KotlinOptionsType> {
lateinit var javaSourceSet: SourceSet
override val output: KotlinCompilationOutput by lazy { KotlinWithJavaCompilationOutput(this) }
override val processResourcesTaskName: String
get() = javaSourceSet.processResourcesTaskName
override var runtimeDependencyFiles: FileCollection
get() = javaSourceSet.runtimeClasspath
set(value) {
javaSourceSet.runtimeClasspath = value
}
override val runtimeDependencyConfigurationName: String
get() = javaSourceSet.runtimeClasspathConfigurationName
override val compileDependencyConfigurationName: String
get() = javaSourceSet.compileClasspathConfigurationName
override val runtimeOnlyConfigurationName: String
get() = javaSourceSet.runtimeOnlyConfigurationName
override val implementationConfigurationName: String
get() = javaSourceSet.implementationConfigurationName
override val apiConfigurationName: String
get() = javaSourceSet.apiConfigurationName
override val compileOnlyConfigurationName: String
get() = javaSourceSet.compileOnlyConfigurationName
override val compileAllTaskName: String
get() = javaSourceSet.classesTaskName
override var compileDependencyFiles: FileCollection
get() = javaSourceSet.compileClasspath
set(value) {
javaSourceSet.compileClasspath = value
}
fun source(javaSourceSet: SourceSet) {
with(target.project) {
afterEvaluate {
(tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(javaSourceSet.java)
}
}
}
}
class KotlinJvmAndroidCompilation(
target: KotlinAndroidTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name) {
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
override val relatedConfigurationNames: List<String>
get() = super.relatedConfigurationNames + listOf("${name}ApiElements", "${name}RuntimeElements")
}
class KotlinJsCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJsOptions>(target, name), KotlinCompilationWithResources<KotlinJsOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: Kotlin2JsCompile
get() = super.compileKotlinTask as Kotlin2JsCompile
}
class KotlinCommonCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<KotlinMultiplatformCommonOptions>(target, name) {
override val compileKotlinTask: KotlinCompileCommon
get() = super.compileKotlinTask as KotlinCompileCommon
// TODO once we properly compile metadata for each source set, the default source sets will likely become just the source sets
// which are transformed to metadata
private val commonSourceSetName = when (compilationName) {
KotlinCompilation.MAIN_COMPILATION_NAME -> KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
else -> error("Custom metadata compilations are not supported yet")
}
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(commonSourceSetName)
}
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
name: String
) : AbstractKotlinCompilation<KotlinCommonOptions>(target, name), KotlinCompilationWithResources<KotlinCommonOptions> {
override val kotlinOptions: KotlinCommonOptions
get() = compileKotlinTask.kotlinOptions
override val compileKotlinTask: KotlinNativeCompile
get() = super.compileKotlinTask as KotlinNativeCompile
private val project: Project
get() = target.project
// A collection containing all source sets used by this compilation
// (taking into account dependencies between source sets). Used by both compilation
// and linking tasks. Unlike kotlinSourceSets, includes dependency source sets.
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val allSources: MutableSet<SourceDirectorySet> = mutableSetOf()
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val commonSources: MutableSet<SourceDirectorySet> = mutableSetOf()
var isTestCompilation = false
var friendCompilationName: String? = null
internal val friendCompilation: KotlinNativeCompilation?
get() = friendCompilationName?.let {
target.compilations.getByName(it)
}
// Used only to support the old APIs. TODO: Remove when the old APIs are removed.
internal val binaries = mutableMapOf<Pair<NativeOutputKind, NativeBuildType>, NativeBinary>()
// Native-specific DSL.
var extraOpts = mutableListOf<String>()
fun extraOpts(vararg values: Any) = extraOpts(values.toList())
fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
var buildTypes = mutableListOf<NativeBuildType>()
var outputKinds = mutableListOf<NativeOutputKind>()
fun outputKind(kind: NativeOutputKind) = outputKinds.add(kind)
fun outputKinds(vararg kinds: NativeOutputKind) {
outputKinds = kinds.toMutableList()
}
fun outputKinds(vararg kinds: String) {
outputKinds = kinds.map { NativeOutputKind.valueOf(it.toUpperCase()) }.toMutableList()
}
fun outputKinds(kinds: List<Any>) {
outputKinds = kinds.map {
when (it) {
is NativeOutputKind -> it
is String -> NativeOutputKind.valueOf(it.toUpperCase())
else -> error("Cannot use $it as an output kind")
}
}.toMutableList()
}
var entryPoint: String? = null
fun entryPoint(value: String) {
entryPoint = value
}
// Interop DSL.
val cinterops = project.container(DefaultCInteropSettings::class.java) { cinteropName ->
DefaultCInteropSettings(project, cinteropName, this)
}
var linkerOpts = mutableListOf<String>()
fun cinterops(action: NamedDomainObjectContainer<DefaultCInteropSettings>.() -> Unit) = cinterops.action()
fun cinterops(action: Closure<Unit>) = cinterops(ConfigureUtil.configureUsing(action))
fun cinterops(action: Action<NamedDomainObjectContainer<DefaultCInteropSettings>>) = action.execute(cinterops)
fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
fun linkerOpts(values: List<String>) {
linkerOpts.addAll(values)
}
// Task accessors.
fun findLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink? = binaries[kind to buildType]?.linkTask
fun getLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink =
findLinkTask(kind, buildType)
?: throw IllegalArgumentException("Cannot find a link task for the binary kind '$kind' and the build type '$buildType'")
fun findLinkTask(kind: String, buildType: String) =
findLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getLinkTask(kind: String, buildType: String) =
getLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun findBinary(kind: NativeOutputKind, buildType: NativeBuildType): File? = findLinkTask(kind, buildType)?.outputFile?.get()
fun getBinary(kind: NativeOutputKind, buildType: NativeBuildType): File = getLinkTask(kind, buildType).outputFile.get()
fun findBinary(kind: String, buildType: String) =
findBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getBinary(kind: String, buildType: String) =
getBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
// Naming
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
fun linkTaskName(kind: NativeOutputKind, buildType: NativeBuildType): String =
lowerCamelCaseName(
"link",
KotlinNativeBinaryContainer.generateBinaryName(compilationName, buildType, kind.taskNameClassifier),
target.targetName
)
fun linkTaskName(kind: String, buildType: String) =
linkTaskName(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileKlibraries"
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "klibrary")
val binariesTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "binaries")
override fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
allSources.add(sourceSet.kotlin)
if (addAsCommonSources) {
commonSources.add(sourceSet.kotlin)
}
}
}
private object CompilationSourceSetUtil {
// Cache the results per project
private val projectSourceSetsInMultipleCompilationsCache = WeakHashMap<Project, Set<KotlinSourceSet>>()
fun sourceSetsInMultipleCompilations(project: Project) =
projectSourceSetsInMultipleCompilationsCache.computeIfAbsent(project) { _ ->
check(project.state.executed) { "Should only be computed after the project is evaluated" }
val compilations = (project.kotlinExtension as? KotlinMultiplatformExtension)?.targets?.flatMap { it.compilations }
?: return@computeIfAbsent null
compilations
.flatMap { compilation -> compilation.allKotlinSourceSets.map { sourceSet -> compilation to sourceSet } }
.groupingBy { (_, sourceSet) -> sourceSet }
.eachCount()
.filterValues { it > 1 }
.keys
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
interface KotlinCompilationFactory<T : KotlinCompilation<*>> : NamedDomainObjectFactory<T> {
val itemClass: Class<T>
}
class KotlinCommonCompilationFactory(
val target: KotlinOnlyTarget<KotlinCommonCompilation>
) : KotlinCompilationFactory<KotlinCommonCompilation> {
override val itemClass: Class<KotlinCommonCompilation>
get() = KotlinCommonCompilation::class.java
override fun create(name: String): KotlinCommonCompilation =
KotlinCommonCompilation(target, name)
}
class KotlinJvmCompilationFactory(
val target: KotlinOnlyTarget<KotlinJvmCompilation>
) : KotlinCompilationFactory<KotlinJvmCompilation> {
override val itemClass: Class<KotlinJvmCompilation>
get() = KotlinJvmCompilation::class.java
override fun create(name: String): KotlinJvmCompilation =
KotlinJvmCompilation(target, name)
}
class KotlinWithJavaCompilationFactory<KotlinOptionsType : KotlinCommonOptions>(
val project: Project,
val target: KotlinWithJavaTarget<KotlinOptionsType>
) : KotlinCompilationFactory<KotlinWithJavaCompilation<KotlinOptionsType>> {
override val itemClass: Class<KotlinWithJavaCompilation<KotlinOptionsType>>
@Suppress("UNCHECKED_CAST")
get() = KotlinWithJavaCompilation::class.java as Class<KotlinWithJavaCompilation<KotlinOptionsType>>
override fun create(name: String): KotlinWithJavaCompilation<KotlinOptionsType> {
val result = KotlinWithJavaCompilation(target, name)
return result
}
}
class KotlinJvmAndroidCompilationFactory(
val project: Project,
val target: KotlinAndroidTarget
) : KotlinCompilationFactory<KotlinJvmAndroidCompilation> {
override val itemClass: Class<KotlinJvmAndroidCompilation>
get() = KotlinJvmAndroidCompilation::class.java
override fun create(name: String): KotlinJvmAndroidCompilation =
KotlinJvmAndroidCompilation(target, name)
}
class KotlinJsCompilationFactory(
val project: Project,
val target: KotlinOnlyTarget<KotlinJsCompilation>
) : KotlinCompilationFactory<KotlinJsCompilation> {
override val itemClass: Class<KotlinJsCompilation>
get() = KotlinJsCompilation::class.java
override fun create(name: String): KotlinJsCompilation =
KotlinJsCompilation(target, name)
}
class KotlinNativeCompilationFactory(
val project: Project,
val target: KotlinNativeTarget
) : KotlinCompilationFactory<KotlinNativeCompilation> {
override val itemClass: Class<KotlinNativeCompilation>
get() = KotlinNativeCompilation::class.java
override fun create(name: String): KotlinNativeCompilation =
KotlinNativeCompilation(target, name).apply {
if (name == KotlinCompilation.TEST_COMPILATION_NAME) {
friendCompilationName = KotlinCompilation.MAIN_COMPILATION_NAME
isTestCompilation = true
}
buildTypes = mutableListOf(NativeBuildType.DEBUG, NativeBuildType.RELEASE)
}
}
@@ -0,0 +1,437 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.*
import org.gradle.api.artifacts.ConfigurablePublishArtifact
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.attributes.Usage.JAVA_API
import org.gradle.api.attributes.Usage.JAVA_RUNTIME_JARS
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.util.ConfigureUtil
import org.gradle.util.WrapUtil
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget
abstract class AbstractKotlinTarget(
final override val project: Project
) : KotlinTarget {
private val attributeContainer = HierarchyAttributeContainer(parent = null)
override fun getAttributes(): AttributeContainer = attributeContainer
override val defaultConfigurationName: String
get() = disambiguateName("default")
override val apiElementsConfigurationName: String
get() = disambiguateName("apiElements")
override val runtimeElementsConfigurationName: String
get() = disambiguateName("runtimeElements")
override val artifactsTaskName: String
get() = disambiguateName("jar")
override fun toString(): String = "target $name ($platformType)"
override val publishable: Boolean
get() = true
override val components: Set<KotlinTargetComponent> by lazy {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val usageContexts = createUsageContexts(mainCompilation)
setOf(
if (isGradleVersionAtLeast(4, 7)) {
val componentName = mainCompilation.target.name
createKotlinVariant(componentName, mainCompilation, usageContexts).apply {
sourcesArtifacts = setOf(
sourcesJarArtifact(
mainCompilation, componentName,
dashSeparatedName(target.name.toLowerCase(), componentName.toLowerCase())
)
)
}
} else {
KotlinVariant(mainCompilation, usageContexts)
}
).also { project.components.addAll(it) }
}
protected fun createKotlinVariant(
componentName: String,
compilation: KotlinCompilation<*>,
usageContexts: Set<DefaultKotlinUsageContext>
): KotlinVariant {
val kotlinExtension = project.kotlinExtension as? KotlinMultiplatformExtension
?: return KotlinVariantWithCoordinates(compilation, usageContexts)
if (targetName == KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
return KotlinVariantWithCoordinates(compilation, usageContexts)
val separateMetadataTarget = kotlinExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
val result = if (kotlinExtension.isGradleMetadataAvailable) {
KotlinVariantWithMetadataVariant(compilation, usageContexts, separateMetadataTarget)
} else {
// we should only add the Kotlin metadata dependency if we publish no Gradle metadata related to Kotlin MPP;
// with metadata, such a dependency would get invalid, since a platform module should only depend on modules for that
// same platform, not Kotlin metadata modules
KotlinVariantWithMetadataDependency(compilation, usageContexts, separateMetadataTarget)
}
result.componentName = componentName
return result
}
private fun createUsageContexts(
producingCompilation: KotlinCompilation<*>
): Set<DefaultKotlinUsageContext> {
// Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for
// ordering of the usage contexts (prioritizing the dependencies) when merging them into the POM;
// These Java usages should not be replaced with the custom Kotlin usages.
return listOfNotNull(
JAVA_API to apiElementsConfigurationName,
(JAVA_RUNTIME_JARS to runtimeElementsConfigurationName).takeIf { producingCompilation is KotlinCompilationToRunnableFiles }
).mapTo(mutableSetOf()) { (usageName, dependenciesConfigurationName) ->
DefaultKotlinUsageContext(
producingCompilation,
project.usageByName(usageName),
dependenciesConfigurationName
)
}
}
protected fun sourcesJarArtifact(
producingCompilation: KotlinCompilation<*>,
componentName: String,
artifactNameAppendix: String,
classifierPrefix: String? = null
): PublishArtifact {
val sourcesJarTask = sourcesJarTask(producingCompilation, componentName, artifactNameAppendix)
val sourceArtifactConfigurationName = producingCompilation.disambiguateName("sourceArtifacts")
return producingCompilation.target.project.run {
(configurations.findByName(sourceArtifactConfigurationName) ?: run {
val configuration = configurations.create(sourceArtifactConfigurationName) {
it.isCanBeResolved = false
it.isCanBeConsumed = false
}
artifacts.add(sourceArtifactConfigurationName, sourcesJarTask)
configuration
}).artifacts.single().apply {
this as ConfigurablePublishArtifact
classifier = dashSeparatedName(classifierPrefix, "sources")
}
}
}
@Suppress("UNCHECKED_CAST")
internal val publicationConfigureActions =
WrapUtil.toDomainObjectSet(Action::class.java) as DomainObjectSet<Action<MavenPublication>>
override fun mavenPublication(action: Action<MavenPublication>) {
publicationConfigureActions.add(action)
}
override fun mavenPublication(action: Closure<Unit>) =
mavenPublication(ConfigureUtil.configureUsing(action))
override var preset: KotlinTargetPreset<out KotlinTarget>? = null
internal set
}
internal fun KotlinTarget.disambiguateName(simpleName: String) =
lowerCamelCaseName(targetName, simpleName)
open class KotlinAndroidTarget(
override val targetName: String,
project: Project
) : AbstractKotlinTarget(project) {
override var disambiguationClassifier: String? = null
internal set
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.androidJvm
private val compilationFactory = KotlinJvmAndroidCompilationFactory(project, this)
override val compilations: NamedDomainObjectContainer<out KotlinJvmAndroidCompilation> =
project.container(compilationFactory.itemClass, compilationFactory)
/** Names of the Android library variants that should be published from the target's project within the default publications which are
* set up if the `maven-publish` Gradle plugin is applied.
*
* Item examples:
* * 'release' (in case no product flavors were defined)
* * 'fooRelease' (for the release build type of a flavor 'foo')
* * 'fooBarRelease' (for the release build type multi-dimensional flavors 'foo' and 'bar').
*
* If set to null, which can also be done with [publishAllLibraryVariants],
* all library variants will be published, but not test or application variants. */
var publishLibraryVariants: List<String>? = listOf()
// Workaround for Groovy GString items in a list:
set(value) { field = value?.map(Any::toString) }
/** Add Android library variant names to [publishLibraryVariants]. */
fun publishLibraryVariants(vararg names: String) {
publishLibraryVariants = publishLibraryVariants.orEmpty() + names
}
/** Set up all of the Android library variants to be published from this target's project within the default publications, which are
* set up if the `maven-publish` Gradle plugin is applied. This overrides the variants chosen with [publishLibraryVariants] */
fun publishAllLibraryVariants() {
publishLibraryVariants = null
}
/** If true, a publication will be created per merged product flavor, with the build types used as classifiers for the artifacts
* published within each publication. If set to false, each Android variant will have a separate publication. */
var publishLibraryVariantsGroupedByFlavor = false
private fun checkPublishLibraryVariantsExist() {
// Capture type parameter T
fun <T> AbstractAndroidProjectHandler<T>.getLibraryVariantNames() =
mutableSetOf<String>().apply {
forEachVariant(project) {
if (getLibraryOutputTask(it) != null)
add(getVariantName(it))
}
}
val variantNames =
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this)
.getLibraryVariantNames()
val missingVariants =
publishLibraryVariants?.minus(variantNames).orEmpty()
if (missingVariants.isNotEmpty())
throw InvalidUserDataException(
"Kotlin target '$targetName' tried to set up publishing for Android build variants that are not library variants " +
"or do not exist:\n" + missingVariants.joinToString("\n") { "* $it" } +
"\nCheck the 'publishLibraryVariants' property, it should point to existing Android library variants. Publishing " +
"of application and test variants is not supported."
)
}
override val components by lazy {
checkPublishLibraryVariantsExist()
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this).doCreateComponents()
.also { project.components.addAll(it) }
}
// Capture the type parameter T for `AbstractAndroidProjectHandler`
private fun <T> AbstractAndroidProjectHandler<T>.doCreateComponents(): Set<KotlinTargetComponent> {
if (!isGradleVersionAtLeast(4, 7))
return emptySet()
val publishableVariants = mutableListOf<T>()
.apply { forEachVariant(project) { add(it) } }
.toList() // Defensive copy against unlikely modification by the lambda that captures the list above in forEachVariant { }
.filter { getLibraryOutputTask(it) != null && publishLibraryVariants?.contains(getVariantName(it)) ?: true }
val publishableVariantGroups = publishableVariants.groupBy { variant ->
val flavorNames = getFlavorNames(variant)
if (publishLibraryVariantsGroupedByFlavor) {
// For each flavor, we group its variants (which differ only in the build type) in a single component in order to publish
// all of the build types of the flavor as a single module with the build type as the classifier of the artifacts
flavorNames
} else {
flavorNames + getBuildTypeName(variant)
}
}
return publishableVariantGroups.map { (flavorGroupNameParts, androidVariants) ->
val nestedVariants = androidVariants.mapTo(mutableSetOf()) { androidVariant ->
val androidVariantName = getVariantName(androidVariant)
val compilation = compilations.getByName(androidVariantName)
val flavorNames = getFlavorNames(androidVariant)
val buildTypeName = getBuildTypeName(androidVariant)
val artifactClassifier = buildTypeName.takeIf { it != "release" && publishLibraryVariantsGroupedByFlavor }
val usageContexts = createAndroidUsageContexts(androidVariant, compilation, artifactClassifier)
createKotlinVariant(
lowerCamelCaseName(compilation.target.name, *flavorGroupNameParts.toTypedArray()),
compilation,
usageContexts
).apply {
sourcesArtifacts = setOf(
sourcesJarArtifact(
compilation, compilation.disambiguateName(""),
dashSeparatedName(
compilation.target.name.toLowerCase(),
*flavorNames.map { it.toLowerCase() }.toTypedArray(),
buildTypeName.takeIf { it != "release" }?.toLowerCase()
),
classifierPrefix = artifactClassifier
)
)
if (!publishLibraryVariantsGroupedByFlavor) {
defaultArtifactIdSuffix =
dashSeparatedName(
(getFlavorNames(androidVariant) + getBuildTypeName(androidVariant).takeIf { it != "release" })
.map { it?.toLowerCase() }
).takeIf { it.isNotEmpty() }
}
}
}
if (publishLibraryVariantsGroupedByFlavor) {
JointAndroidKotlinTargetComponent(
this@KotlinAndroidTarget,
nestedVariants,
flavorGroupNameParts,
nestedVariants.flatMap { it.sourcesArtifacts }.toSet()
)
} else {
nestedVariants.single()
} as KotlinTargetComponent
}.toSet()
}
private fun <T> AbstractAndroidProjectHandler<T>.createAndroidUsageContexts(
variant: T,
compilation: KotlinCompilation<*>,
artifactClassifier: String?
): Set<DefaultKotlinUsageContext> {
val variantName = getVariantName(variant)
val outputTask = getLibraryOutputTask(variant) ?: return emptySet()
val artifact = run {
val archivesConfigurationName = lowerCamelCaseName(targetName, variantName, "archives")
project.configurations.maybeCreate(archivesConfigurationName).apply {
isCanBeConsumed = false
isCanBeResolved = false
}
project.artifacts.add(archivesConfigurationName, outputTask) { artifact ->
artifact.classifier = artifactClassifier
}
}
val apiElementsConfigurationName = lowerCamelCaseName(variantName, "apiElements")
val runtimeElementsConfigurationName = lowerCamelCaseName(variantName, "runtimeElements")
// Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for
// ordering of the usage contexts (prioritizing the dependencies) when merging them into the POM;
// These Java usages should not be replaced with the custom Kotlin usages.
return listOf(
apiElementsConfigurationName to JAVA_API,
runtimeElementsConfigurationName to JAVA_RUNTIME_JARS
).mapTo(mutableSetOf()) { (dependencyConfigurationName, usageName) ->
DefaultKotlinUsageContext(
compilation,
project.usageByName(usageName),
dependencyConfigurationName,
overrideConfigurationArtifacts = setOf(artifact)
)
}
}
}
open class KotlinWithJavaTarget<KotlinOptionsType : KotlinCommonOptions>(
project: Project,
override val platformType: KotlinPlatformType,
override val targetName: String
) : AbstractKotlinTarget(project) {
override var disambiguationClassifier: String? = null
internal set
override val defaultConfigurationName: String
get() = Dependency.DEFAULT_CONFIGURATION
override val apiElementsConfigurationName: String
get() = JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME
override val runtimeElementsConfigurationName: String
get() = JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME
override val artifactsTaskName: String
get() = JavaPlugin.JAR_TASK_NAME
override val compilations: NamedDomainObjectContainer<KotlinWithJavaCompilation<KotlinOptionsType>> =
@Suppress("UNCHECKED_CAST")
project.container(
KotlinWithJavaCompilation::class.java as Class<KotlinWithJavaCompilation<KotlinOptionsType>>,
KotlinWithJavaCompilationFactory(project, this)
)
}
open class KotlinOnlyTarget<T : KotlinCompilation<*>>(
project: Project,
override val platformType: KotlinPlatformType
) : AbstractKotlinTarget(project) {
override lateinit var compilations: NamedDomainObjectContainer<T>
internal set
override lateinit var targetName: String
internal set
override var disambiguationClassifier: String? = null
internal set
}
class KotlinNativeTarget(
project: Project,
val konanTarget: KonanTarget
) : KotlinOnlyTarget<KotlinNativeCompilation>(project, KotlinPlatformType.native) {
init {
attributes.attribute(konanTargetAttribute, konanTarget.name)
}
val binaries = if(isGradleVersionAtLeast(4, 2)) {
// Use newInstance to allow accessing binaries by their names in Groovy using the extension mechanism.
project.objects.newInstance(KotlinNativeBinaryContainer::class.java, this, WrapUtil.toDomainObjectSet(NativeBinary::class.java))
} else {
KotlinNativeBinaryContainer(this, WrapUtil.toDomainObjectSet(NativeBinary::class.java))
}
fun binaries(configure: KotlinNativeBinaryContainer.() -> Unit) {
binaries.configure()
}
fun binaries(configure: Closure<*>) {
ConfigureUtil.configure(configure, binaries)
}
override val artifactsTaskName: String
get() = disambiguateName("binaries")
override val publishable: Boolean
get() = konanTarget.enabledOnCurrentHost
// User-visible constants
val DEBUG = NativeBuildType.DEBUG
val RELEASE = NativeBuildType.RELEASE
val EXECUTABLE = NativeOutputKind.EXECUTABLE
val FRAMEWORK = NativeOutputKind.FRAMEWORK
val DYNAMIC = NativeOutputKind.DYNAMIC
val STATIC = NativeOutputKind.STATIC
companion object {
val konanTargetAttribute = Attribute.of(
"org.jetbrains.kotlin.native.target",
String::class.java
)
}
}
@@ -0,0 +1,330 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.compilerRunner.KotlinNativeProjectProperty
import org.jetbrains.kotlin.compilerRunner.hasProperty
import org.jetbrains.kotlin.compilerRunner.konanHome
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import java.util.*
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation<*>>(
protected val project: Project,
private val instantiator: Instantiator,
private val fileResolver: FileResolver,
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
protected open fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T> =
KotlinTargetConfigurator(createDefaultSourceSets = true, createTestCompilation = true)
override fun createTarget(name: String): KotlinOnlyTarget<T> {
val result = KotlinOnlyTarget<T>(project, platformType).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinOnlyTargetPreset
val compilationFactory = createCompilationFactory(this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
createKotlinTargetConfigurator().configureTarget(result)
result.compilations.all { compilation ->
buildCompilationProcessor(compilation).run()
if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
sourcesJarTask(compilation, result.targetName, result.targetName.toLowerCase())
}
}
return result
}
protected abstract fun createCompilationFactory(forTarget: KotlinOnlyTarget<T>): KotlinCompilationFactory<T>
protected abstract val platformType: KotlinPlatformType
internal abstract fun buildCompilationProcessor(compilation: T): KotlinSourceSetProcessor<*>
}
class KotlinMetadataTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(
forTarget: KotlinOnlyTarget<KotlinCommonCompilation>
): KotlinCompilationFactory<KotlinCommonCompilation> =
KotlinCommonCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.common
override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "metadata"
}
override fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<KotlinCommonCompilation> =
KotlinTargetConfigurator(createDefaultSourceSets = false, createTestCompilation = false)
override fun createTarget(name: String): KotlinOnlyTarget<KotlinCommonCompilation> =
super.createTarget(name).apply {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val commonMainSourceSet = project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
mainCompilation.source(commonMainSourceSet)
project.afterEvaluate {
// Since there's no default source set, apply language settings from commonMain:
val compileKotlinMetadata = project.tasks.getByName(mainCompilation.compileKotlinTaskName) as KotlinCompile<*>
applyLanguageSettingsToKotlinTask(commonMainSourceSet.languageSettings, compileKotlinMetadata)
}
}
}
class KotlinJvmTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> =
KotlinJvmCompilationFactory(forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.jvm
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "jvm"
}
}
class KotlinJsTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJsCompilation>) =
KotlinJsCompilationFactory(project, forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JsSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
}
companion object {
const val PRESET_NAME = "js"
}
}
class KotlinAndroidTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinAndroidTarget> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinAndroidTarget {
val result = KotlinAndroidTarget(name, project).apply {
disambiguationClassifier = name
preset = this@KotlinAndroidTargetPreset
}
KotlinAndroidPlugin.applyToTarget(kotlinPluginVersion, result)
return result
}
companion object {
const val PRESET_NAME = "android"
}
}
class KotlinJvmWithJavaTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinWithJavaTarget<KotlinJvmOptions>> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinWithJavaTarget<KotlinJvmOptions> {
project.plugins.apply(JavaPlugin::class.java)
val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, name).apply {
disambiguationClassifier = name
preset = this@KotlinJvmWithJavaTargetPreset
}
AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(name), compilation, kotlinPluginVersion)
}
target.compilations.getByName("test").run {
val main = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
compileDependencyFiles = project.files(
main.output.allOutputs,
project.configurations.maybeCreate(compileDependencyConfigurationName)
)
runtimeDependencyFiles = project.files(
output.allOutputs,
main.output.allOutputs,
project.configurations.maybeCreate(runtimeDependencyConfigurationName)
)
}
return target
}
companion object {
const val PRESET_NAME = "jvmWithJava"
}
}
class KotlinNativeTargetPreset(
private val name: String,
val project: Project,
val konanTarget: KonanTarget,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinNativeTarget> {
init {
// This is required to obtain Kotlin/Native home in CLion plugin:
setupNativeHomePrivateProperty()
}
override fun getName(): String = name
private fun setupNativeHomePrivateProperty() = with(project) {
if (!hasProperty(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY))
extensions.extraProperties.set(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY, konanHome)
}
private fun setupNativeCompiler() = with(project) {
if (!hasProperty(KotlinNativeProjectProperty.KONAN_HOME_OVERRIDE)) {
NativeCompilerDownloader(this).downloadIfNeeded()
logger.info("Kotlin/Native distribution: $konanHome")
} else {
logger.info("User-provided Kotlin/Native distribution: $konanHome")
}
}
// We declare default K/N dependencies as files to avoid searching them in remote repos (see KT-28128).
private fun defaultLibs(target: KonanTarget? = null): List<Dependency> = with(project) {
val relPath = if (target != null) "platform/${target.name}" else "common"
file("$konanHome/klib/$relPath")
.listFiles { file -> file.isDirectory }
?.sortedBy { dir -> dir.name.toLowerCase() }
?.map { dir -> dependencies.create(files(dir)) } ?: emptyList()
}
override fun createTarget(name: String): KotlinNativeTarget {
setupNativeCompiler()
val result = KotlinNativeTarget(project, konanTarget).apply {
targetName = name
disambiguationClassifier = name
preset = this@KotlinNativeTargetPreset
val compilationFactory = KotlinNativeCompilationFactory(project, this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
KotlinNativeTargetConfigurator(kotlinPluginVersion).configureTarget(result)
// Allow IDE to resolve the libraries provided by the compiler by adding them into dependencies.
result.compilations.all { compilation ->
val target = compilation.target.konanTarget
// First, put common libs:
defaultLibs().forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
// Then, platform-specific libs:
defaultLibs(target).forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
}
if (!konanTarget.enabledOnCurrentHost) {
with(HostManager()) {
val supportedHosts = enabledTargetsByHost.filterValues { konanTarget in it }.keys
val supportedHostsString =
if (supportedHosts.size == 1)
"a ${supportedHosts.single()} host" else
"one of the host platforms: ${supportedHosts.joinToString(", ")}"
project.logger.warn(
"Target '$name' for platform ${konanTarget} is ignored during build on this ${HostManager.host} machine. " +
"You can build it with $supportedHostsString."
)
}
}
return result
}
companion object {
private const val KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY = "konanHome"
}
}
internal val KonanTarget.isCurrentHost: Boolean
get() = this == HostManager.host
internal val KonanTarget.enabledOnCurrentHost
get() = HostManager().isEnabled(this)
internal val KotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME
@@ -0,0 +1,471 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.SourceSet
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.util.*
import java.util.concurrent.Callable
internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: String? = null): String {
val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
val targetNamePart = target.disambiguationClassifier
return lowerCamelCaseName(prefix, targetNamePart, compilationNamePart, suffix)
}
internal val KotlinCompilation<*>.defaultSourceSetName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName)
abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
target: KotlinTarget,
override val compilationName: String
) : KotlinCompilation<T>, HasKotlinDependencies {
override val kotlinOptions: T
get() = compileKotlinTask.kotlinOptions
override fun kotlinOptions(configure: T.() -> Unit) =
configure(kotlinOptions)
@Suppress("UNCHECKED_CAST")
override val compileKotlinTask: KotlinCompile<T>
get() = (target.project.tasks.getByName(compileKotlinTaskName) as KotlinCompile<T>)
// Don't declare this property in the constructor to avoid NPE
// when an overriding property of a subclass is accessed instead.
override val target: KotlinTarget = target
private val attributeContainer = HierarchyAttributeContainer(target.attributes)
override fun getAttributes(): AttributeContainer = attributeContainer
override val kotlinSourceSets: MutableSet<KotlinSourceSet> = mutableSetOf()
override val allKotlinSourceSets: Set<KotlinSourceSet>
get() = kotlinSourceSets.flatMapTo(mutableSetOf()) { it.getSourceSetHierarchy() }
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(defaultSourceSetName)
override fun defaultSourceSet(configure: KotlinSourceSet.() -> Unit) =
configure(defaultSourceSet)
override val output: KotlinCompilationOutput by lazy {
DefaultKotlinCompilationOutput(
target.project,
Callable { target.project.buildDir.resolve("processedResources/${target.targetName}/$name") })
}
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
fun AbstractKotlinCompile<*>.configureAction() {
source(sourceSet.kotlin)
sourceFilesExtensions(sourceSet.customSourceFilesExtensions)
if (addAsCommonSources) {
commonSourceSet += sourceSet.kotlin
}
}
// Note! Invocation of withType-all results in preliminary task instantiation.
// After fix of this issue the following code should be uncommented:
// if (useLazyTaskConfiguration) {
// (target.project.tasks.named(compileKotlinTaskName) as TaskProvider<AbstractKotlinCompile<*>>).configure {
// it.configureAction()
// }
// }
target.project.tasks
// To configure a task that may have not yet been created at this point, use 'withType-matching-all`:
.withType(AbstractKotlinCompile::class.java)
.matching { it.name == compileKotlinTaskName }
.all { compileKotlinTask ->
compileKotlinTask.configureAction()
}
}
override fun source(sourceSet: KotlinSourceSet) {
if (kotlinSourceSets.add(sourceSet)) {
//TODO possibly issue with forced instantiation
target.project.whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
val isCommonSource =
CompilationSourceSetUtil.sourceSetsInMultipleCompilations(project)?.contains(sourceSet) ?: false
addSourcesToCompileTask(sourceSet, addAsCommonSources = isCommonSource)
// Use `forced = false` since `api`, `implementation`, and `compileOnly` may be missing in some cases like
// old Java & Android projects:
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName, forced = false)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName, forced = false)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName, forced = false)
if (this@AbstractKotlinCompilation is KotlinCompilationToRunnableFiles<*>) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName, forced = false)
}
if (sourceSet.name != defaultSourceSetName) {
kotlinExtension.sourceSets.findByName(defaultSourceSetName)?.let { defaultSourceSet ->
// Temporary solution for checking consistency across source sets participating in a compilation that may
// not be interconnected with the dependsOn relation: check the settings as if the default source set of
// the compilation depends on the one added to the compilation:
defaultSourceSetLanguageSettingsChecker.runAllChecks(
defaultSourceSet,
sourceSet
)
}
}
}
}
}
}
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileClasspath"
)
override val compileKotlinTaskName: String
get() = lowerCamelCaseName(
"compile",
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"Kotlin",
target.targetName
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "classes")
override lateinit var compileDependencyFiles: FileCollection
override val apiConfigurationName: String
get() = disambiguateName("api")
override val implementationConfigurationName: String
get() = disambiguateName("implementation")
override val compileOnlyConfigurationName: String
get() = disambiguateName("compileOnly")
override val runtimeOnlyConfigurationName: String
get() = disambiguateName("runtimeOnly")
override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit =
DefaultKotlinDependencyHandler(this, target.project).run(configure)
override fun dependencies(configureClosure: Closure<Any?>) =
dependencies f@{ ConfigureUtil.configure(configureClosure, this@f) }
override fun toString(): String = "compilation '$compilationName' ($target)"
}
abstract class AbstractKotlinCompilationToRunnableFiles<T : KotlinCommonOptions>(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<T>(target, name), KotlinCompilationToRunnableFiles<T> {
override val runtimeDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
"runtimeClasspath"
)
override lateinit var runtimeDependencyFiles: FileCollection
}
internal fun KotlinCompilation<*>.disambiguateName(simpleName: String): String {
return lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
simpleName
)
}
open class KotlinJvmCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name), KotlinCompilationWithResources<KotlinJvmOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
}
class KotlinWithJavaCompilation<KotlinOptionsType : KotlinCommonOptions>(
target: KotlinWithJavaTarget<KotlinOptionsType>,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinOptionsType>(target, name), KotlinCompilationWithResources<KotlinOptionsType> {
lateinit var javaSourceSet: SourceSet
override val output: KotlinCompilationOutput by lazy { KotlinWithJavaCompilationOutput(this) }
override val processResourcesTaskName: String
get() = javaSourceSet.processResourcesTaskName
override var runtimeDependencyFiles: FileCollection
get() = javaSourceSet.runtimeClasspath
set(value) {
javaSourceSet.runtimeClasspath = value
}
override val runtimeDependencyConfigurationName: String
get() = javaSourceSet.runtimeClasspathConfigurationName
override val compileDependencyConfigurationName: String
get() = javaSourceSet.compileClasspathConfigurationName
override val runtimeOnlyConfigurationName: String
get() = javaSourceSet.runtimeOnlyConfigurationName
override val implementationConfigurationName: String
get() = javaSourceSet.implementationConfigurationName
override val apiConfigurationName: String
get() = javaSourceSet.apiConfigurationName
override val compileOnlyConfigurationName: String
get() = javaSourceSet.compileOnlyConfigurationName
override val compileAllTaskName: String
get() = javaSourceSet.classesTaskName
override var compileDependencyFiles: FileCollection
get() = javaSourceSet.compileClasspath
set(value) {
javaSourceSet.compileClasspath = value
}
fun source(javaSourceSet: SourceSet) {
with(target.project) {
afterEvaluate {
(tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(javaSourceSet.java)
}
}
}
}
class KotlinJvmAndroidCompilation(
target: KotlinAndroidTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name) {
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
override val relatedConfigurationNames: List<String>
get() = super.relatedConfigurationNames + listOf("${name}ApiElements", "${name}RuntimeElements")
}
class KotlinJsCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilationToRunnableFiles<KotlinJsOptions>(target, name), KotlinCompilationWithResources<KotlinJsOptions> {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
override val compileKotlinTask: Kotlin2JsCompile
get() = super.compileKotlinTask as Kotlin2JsCompile
}
class KotlinCommonCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<KotlinMultiplatformCommonOptions>(target, name) {
override val compileKotlinTask: KotlinCompileCommon
get() = super.compileKotlinTask as KotlinCompileCommon
// TODO once we properly compile metadata for each source set, the default source sets will likely become just the source sets
// which are transformed to metadata
private val commonSourceSetName = when (compilationName) {
KotlinCompilation.MAIN_COMPILATION_NAME -> KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
else -> error("Custom metadata compilations are not supported yet")
}
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(commonSourceSetName)
}
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
name: String
) : AbstractKotlinCompilation<KotlinCommonOptions>(target, name), KotlinCompilationWithResources<KotlinCommonOptions> {
override val kotlinOptions: KotlinCommonOptions
get() = compileKotlinTask.kotlinOptions
override val compileKotlinTask: KotlinNativeCompile
get() = super.compileKotlinTask as KotlinNativeCompile
private val project: Project
get() = target.project
// A collection containing all source sets used by this compilation
// (taking into account dependencies between source sets). Used by both compilation
// and linking tasks. Unlike kotlinSourceSets, includes dependency source sets.
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val allSources: MutableSet<SourceDirectorySet> = mutableSetOf()
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val commonSources: MutableSet<SourceDirectorySet> = mutableSetOf()
var isTestCompilation = false
var friendCompilationName: String? = null
internal val friendCompilation: KotlinNativeCompilation?
get() = friendCompilationName?.let {
target.compilations.getByName(it)
}
// Used only to support the old APIs. TODO: Remove when the old APIs are removed.
internal val binaries = mutableMapOf<Pair<NativeOutputKind, NativeBuildType>, NativeBinary>()
// Native-specific DSL.
var extraOpts = mutableListOf<String>()
fun extraOpts(vararg values: Any) = extraOpts(values.toList())
fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
var buildTypes = mutableListOf<NativeBuildType>()
var outputKinds = mutableListOf<NativeOutputKind>()
fun outputKind(kind: NativeOutputKind) = outputKinds.add(kind)
fun outputKinds(vararg kinds: NativeOutputKind) {
outputKinds = kinds.toMutableList()
}
fun outputKinds(vararg kinds: String) {
outputKinds = kinds.map { NativeOutputKind.valueOf(it.toUpperCase()) }.toMutableList()
}
fun outputKinds(kinds: List<Any>) {
outputKinds = kinds.map {
when (it) {
is NativeOutputKind -> it
is String -> NativeOutputKind.valueOf(it.toUpperCase())
else -> error("Cannot use $it as an output kind")
}
}.toMutableList()
}
var entryPoint: String? = null
fun entryPoint(value: String) {
entryPoint = value
}
// Interop DSL.
val cinterops = project.container(DefaultCInteropSettings::class.java) { cinteropName ->
DefaultCInteropSettings(project, cinteropName, this)
}
var linkerOpts = mutableListOf<String>()
fun cinterops(action: NamedDomainObjectContainer<DefaultCInteropSettings>.() -> Unit) = cinterops.action()
fun cinterops(action: Closure<Unit>) = cinterops(ConfigureUtil.configureUsing(action))
fun cinterops(action: Action<NamedDomainObjectContainer<DefaultCInteropSettings>>) = action.execute(cinterops)
fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
fun linkerOpts(values: List<String>) {
linkerOpts.addAll(values)
}
// Task accessors.
fun findLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink? = binaries[kind to buildType]?.linkTask
fun getLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink =
findLinkTask(kind, buildType)
?: throw IllegalArgumentException("Cannot find a link task for the binary kind '$kind' and the build type '$buildType'")
fun findLinkTask(kind: String, buildType: String) =
findLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getLinkTask(kind: String, buildType: String) =
getLinkTask(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun findBinary(kind: NativeOutputKind, buildType: NativeBuildType): File? = findLinkTask(kind, buildType)?.outputFile?.get()
fun getBinary(kind: NativeOutputKind, buildType: NativeBuildType): File = getLinkTask(kind, buildType).outputFile.get()
fun findBinary(kind: String, buildType: String) =
findBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
fun getBinary(kind: String, buildType: String) =
getBinary(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
// Naming
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
fun linkTaskName(kind: NativeOutputKind, buildType: NativeBuildType): String =
lowerCamelCaseName(
"link",
KotlinNativeBinaryContainer.generateBinaryName(compilationName, buildType, kind.taskNameClassifier),
target.targetName
)
fun linkTaskName(kind: String, buildType: String) =
linkTaskName(NativeOutputKind.valueOf(kind.toUpperCase()), NativeBuildType.valueOf(buildType.toUpperCase()))
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileKlibraries"
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "klibrary")
val binariesTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "binaries")
override fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
allSources.add(sourceSet.kotlin)
if (addAsCommonSources) {
commonSources.add(sourceSet.kotlin)
}
}
}
private object CompilationSourceSetUtil {
// Cache the results per project
private val projectSourceSetsInMultipleCompilationsCache = WeakHashMap<Project, Set<KotlinSourceSet>>()
fun sourceSetsInMultipleCompilations(project: Project) =
projectSourceSetsInMultipleCompilationsCache.computeIfAbsent(project) { _ ->
check(project.state.executed) { "Should only be computed after the project is evaluated" }
val compilations = (project.kotlinExtension as? KotlinMultiplatformExtension)?.targets?.flatMap { it.compilations }
?: return@computeIfAbsent null
compilations
.flatMap { compilation -> compilation.allKotlinSourceSets.map { sourceSet -> compilation to sourceSet } }
.groupingBy { (_, sourceSet) -> sourceSet }
.eachCount()
.filterValues { it > 1 }
.keys
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
interface KotlinCompilationFactory<T : KotlinCompilation<*>> : NamedDomainObjectFactory<T> {
val itemClass: Class<T>
}
class KotlinCommonCompilationFactory(
val target: KotlinOnlyTarget<KotlinCommonCompilation>
) : KotlinCompilationFactory<KotlinCommonCompilation> {
override val itemClass: Class<KotlinCommonCompilation>
get() = KotlinCommonCompilation::class.java
override fun create(name: String): KotlinCommonCompilation =
KotlinCommonCompilation(target, name)
}
class KotlinJvmCompilationFactory(
val target: KotlinOnlyTarget<KotlinJvmCompilation>
) : KotlinCompilationFactory<KotlinJvmCompilation> {
override val itemClass: Class<KotlinJvmCompilation>
get() = KotlinJvmCompilation::class.java
override fun create(name: String): KotlinJvmCompilation =
KotlinJvmCompilation(target, name)
}
class KotlinWithJavaCompilationFactory<KotlinOptionsType : KotlinCommonOptions>(
val project: Project,
val target: KotlinWithJavaTarget<KotlinOptionsType>
) : KotlinCompilationFactory<KotlinWithJavaCompilation<KotlinOptionsType>> {
override val itemClass: Class<KotlinWithJavaCompilation<KotlinOptionsType>>
@Suppress("UNCHECKED_CAST")
get() = KotlinWithJavaCompilation::class.java as Class<KotlinWithJavaCompilation<KotlinOptionsType>>
override fun create(name: String): KotlinWithJavaCompilation<KotlinOptionsType> {
val result = KotlinWithJavaCompilation(target, name)
return result
}
}
class KotlinJvmAndroidCompilationFactory(
val project: Project,
val target: KotlinAndroidTarget
) : KotlinCompilationFactory<KotlinJvmAndroidCompilation> {
override val itemClass: Class<KotlinJvmAndroidCompilation>
get() = KotlinJvmAndroidCompilation::class.java
override fun create(name: String): KotlinJvmAndroidCompilation =
KotlinJvmAndroidCompilation(target, name)
}
class KotlinJsCompilationFactory(
val project: Project,
val target: KotlinOnlyTarget<KotlinJsCompilation>
) : KotlinCompilationFactory<KotlinJsCompilation> {
override val itemClass: Class<KotlinJsCompilation>
get() = KotlinJsCompilation::class.java
override fun create(name: String): KotlinJsCompilation =
KotlinJsCompilation(target, name)
}
class KotlinNativeCompilationFactory(
val project: Project,
val target: KotlinNativeTarget
) : KotlinCompilationFactory<KotlinNativeCompilation> {
override val itemClass: Class<KotlinNativeCompilation>
get() = KotlinNativeCompilation::class.java
override fun create(name: String): KotlinNativeCompilation =
KotlinNativeCompilation(target, name).apply {
if (name == KotlinCompilation.TEST_COMPILATION_NAME) {
friendCompilationName = KotlinCompilation.MAIN_COMPILATION_NAME
isTestCompilation = true
}
buildTypes = mutableListOf(NativeBuildType.DEBUG, NativeBuildType.RELEASE)
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationOutput
import java.io.File
import java.util.concurrent.Callable
class DefaultKotlinCompilationOutput(
private val project: Project,
override var resourcesDirProvider: Any
) : KotlinCompilationOutput, Callable<FileCollection> {
override val classesDirs: ConfigurableFileCollection = project.files()
override val allOutputs = project.files().apply {
from(classesDirs)
from(Callable { resourcesDir })
}
override val resourcesDir: File
get() = project.file(resourcesDirProvider)
override fun call(): FileCollection = allOutputs
}
class KotlinWithJavaCompilationOutput(
internal val compilation: KotlinWithJavaCompilation<*>
) : KotlinCompilationOutput, Callable<FileCollection> {
private val javaSourceSetOutput
get() = compilation.javaSourceSet.output
override val resourcesDir: File
get() = javaSourceSetOutput.resourcesDir!!
override var resourcesDirProvider: Any
get() = javaSourceSetOutput.resourcesDir!!
set(value) {
javaSourceSetOutput.setResourcesDir(value)
}
override val classesDirs: ConfigurableFileCollection =
javaSourceSetOutput.classesDirs as ConfigurableFileCollection
override val allOutputs: FileCollection
get() = javaSourceSetOutput
override fun call(): FileCollection = allOutputs
}
@@ -0,0 +1,437 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.*
import org.gradle.api.artifacts.ConfigurablePublishArtifact
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.attributes.Usage.JAVA_API
import org.gradle.api.attributes.Usage.JAVA_RUNTIME_JARS
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.util.ConfigureUtil
import org.gradle.util.WrapUtil
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget
abstract class AbstractKotlinTarget(
final override val project: Project
) : KotlinTarget {
private val attributeContainer = HierarchyAttributeContainer(parent = null)
override fun getAttributes(): AttributeContainer = attributeContainer
override val defaultConfigurationName: String
get() = disambiguateName("default")
override val apiElementsConfigurationName: String
get() = disambiguateName("apiElements")
override val runtimeElementsConfigurationName: String
get() = disambiguateName("runtimeElements")
override val artifactsTaskName: String
get() = disambiguateName("jar")
override fun toString(): String = "target $name ($platformType)"
override val publishable: Boolean
get() = true
override val components: Set<KotlinTargetComponent> by lazy {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val usageContexts = createUsageContexts(mainCompilation)
setOf(
if (isGradleVersionAtLeast(4, 7)) {
val componentName = mainCompilation.target.name
createKotlinVariant(componentName, mainCompilation, usageContexts).apply {
sourcesArtifacts = setOf(
sourcesJarArtifact(
mainCompilation, componentName,
dashSeparatedName(target.name.toLowerCase(), componentName.toLowerCase())
)
)
}
} else {
KotlinVariant(mainCompilation, usageContexts)
}
).also { project.components.addAll(it) }
}
protected fun createKotlinVariant(
componentName: String,
compilation: KotlinCompilation<*>,
usageContexts: Set<DefaultKotlinUsageContext>
): KotlinVariant {
val kotlinExtension = project.kotlinExtension as? KotlinMultiplatformExtension
?: return KotlinVariantWithCoordinates(compilation, usageContexts)
if (targetName == KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
return KotlinVariantWithCoordinates(compilation, usageContexts)
val separateMetadataTarget = kotlinExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME)
val result = if (kotlinExtension.isGradleMetadataAvailable) {
KotlinVariantWithMetadataVariant(compilation, usageContexts, separateMetadataTarget)
} else {
// we should only add the Kotlin metadata dependency if we publish no Gradle metadata related to Kotlin MPP;
// with metadata, such a dependency would get invalid, since a platform module should only depend on modules for that
// same platform, not Kotlin metadata modules
KotlinVariantWithMetadataDependency(compilation, usageContexts, separateMetadataTarget)
}
result.componentName = componentName
return result
}
private fun createUsageContexts(
producingCompilation: KotlinCompilation<*>
): Set<DefaultKotlinUsageContext> {
// Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for
// ordering of the usage contexts (prioritizing the dependencies) when merging them into the POM;
// These Java usages should not be replaced with the custom Kotlin usages.
return listOfNotNull(
JAVA_API to apiElementsConfigurationName,
(JAVA_RUNTIME_JARS to runtimeElementsConfigurationName).takeIf { producingCompilation is KotlinCompilationToRunnableFiles }
).mapTo(mutableSetOf()) { (usageName, dependenciesConfigurationName) ->
DefaultKotlinUsageContext(
producingCompilation,
project.usageByName(usageName),
dependenciesConfigurationName
)
}
}
protected fun sourcesJarArtifact(
producingCompilation: KotlinCompilation<*>,
componentName: String,
artifactNameAppendix: String,
classifierPrefix: String? = null
): PublishArtifact {
val sourcesJarTask = sourcesJarTask(producingCompilation, componentName, artifactNameAppendix)
val sourceArtifactConfigurationName = producingCompilation.disambiguateName("sourceArtifacts")
return producingCompilation.target.project.run {
(configurations.findByName(sourceArtifactConfigurationName) ?: run {
val configuration = configurations.create(sourceArtifactConfigurationName) {
it.isCanBeResolved = false
it.isCanBeConsumed = false
}
artifacts.add(sourceArtifactConfigurationName, sourcesJarTask)
configuration
}).artifacts.single().apply {
this as ConfigurablePublishArtifact
classifier = dashSeparatedName(classifierPrefix, "sources")
}
}
}
@Suppress("UNCHECKED_CAST")
internal val publicationConfigureActions =
WrapUtil.toDomainObjectSet(Action::class.java) as DomainObjectSet<Action<MavenPublication>>
override fun mavenPublication(action: Action<MavenPublication>) {
publicationConfigureActions.add(action)
}
override fun mavenPublication(action: Closure<Unit>) =
mavenPublication(ConfigureUtil.configureUsing(action))
override var preset: KotlinTargetPreset<out KotlinTarget>? = null
internal set
}
internal fun KotlinTarget.disambiguateName(simpleName: String) =
lowerCamelCaseName(targetName, simpleName)
open class KotlinAndroidTarget(
override val targetName: String,
project: Project
) : AbstractKotlinTarget(project) {
override var disambiguationClassifier: String? = null
internal set
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.androidJvm
private val compilationFactory = KotlinJvmAndroidCompilationFactory(project, this)
override val compilations: NamedDomainObjectContainer<out KotlinJvmAndroidCompilation> =
project.container(compilationFactory.itemClass, compilationFactory)
/** Names of the Android library variants that should be published from the target's project within the default publications which are
* set up if the `maven-publish` Gradle plugin is applied.
*
* Item examples:
* * 'release' (in case no product flavors were defined)
* * 'fooRelease' (for the release build type of a flavor 'foo')
* * 'fooBarRelease' (for the release build type multi-dimensional flavors 'foo' and 'bar').
*
* If set to null, which can also be done with [publishAllLibraryVariants],
* all library variants will be published, but not test or application variants. */
var publishLibraryVariants: List<String>? = listOf()
// Workaround for Groovy GString items in a list:
set(value) { field = value?.map(Any::toString) }
/** Add Android library variant names to [publishLibraryVariants]. */
fun publishLibraryVariants(vararg names: String) {
publishLibraryVariants = publishLibraryVariants.orEmpty() + names
}
/** Set up all of the Android library variants to be published from this target's project within the default publications, which are
* set up if the `maven-publish` Gradle plugin is applied. This overrides the variants chosen with [publishLibraryVariants] */
fun publishAllLibraryVariants() {
publishLibraryVariants = null
}
/** If true, a publication will be created per merged product flavor, with the build types used as classifiers for the artifacts
* published within each publication. If set to false, each Android variant will have a separate publication. */
var publishLibraryVariantsGroupedByFlavor = false
private fun checkPublishLibraryVariantsExist() {
// Capture type parameter T
fun <T> AbstractAndroidProjectHandler<T>.getLibraryVariantNames() =
mutableSetOf<String>().apply {
forEachVariant(project) {
if (getLibraryOutputTask(it) != null)
add(getVariantName(it))
}
}
val variantNames =
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this)
.getLibraryVariantNames()
val missingVariants =
publishLibraryVariants?.minus(variantNames).orEmpty()
if (missingVariants.isNotEmpty())
throw InvalidUserDataException(
"Kotlin target '$targetName' tried to set up publishing for Android build variants that are not library variants " +
"or do not exist:\n" + missingVariants.joinToString("\n") { "* $it" } +
"\nCheck the 'publishLibraryVariants' property, it should point to existing Android library variants. Publishing " +
"of application and test variants is not supported."
)
}
override val components by lazy {
checkPublishLibraryVariantsExist()
KotlinAndroidPlugin.androidTargetHandler(project.getKotlinPluginVersion()!!, this).doCreateComponents()
.also { project.components.addAll(it) }
}
// Capture the type parameter T for `AbstractAndroidProjectHandler`
private fun <T> AbstractAndroidProjectHandler<T>.doCreateComponents(): Set<KotlinTargetComponent> {
if (!isGradleVersionAtLeast(4, 7))
return emptySet()
val publishableVariants = mutableListOf<T>()
.apply { forEachVariant(project) { add(it) } }
.toList() // Defensive copy against unlikely modification by the lambda that captures the list above in forEachVariant { }
.filter { getLibraryOutputTask(it) != null && publishLibraryVariants?.contains(getVariantName(it)) ?: true }
val publishableVariantGroups = publishableVariants.groupBy { variant ->
val flavorNames = getFlavorNames(variant)
if (publishLibraryVariantsGroupedByFlavor) {
// For each flavor, we group its variants (which differ only in the build type) in a single component in order to publish
// all of the build types of the flavor as a single module with the build type as the classifier of the artifacts
flavorNames
} else {
flavorNames + getBuildTypeName(variant)
}
}
return publishableVariantGroups.map { (flavorGroupNameParts, androidVariants) ->
val nestedVariants = androidVariants.mapTo(mutableSetOf()) { androidVariant ->
val androidVariantName = getVariantName(androidVariant)
val compilation = compilations.getByName(androidVariantName)
val flavorNames = getFlavorNames(androidVariant)
val buildTypeName = getBuildTypeName(androidVariant)
val artifactClassifier = buildTypeName.takeIf { it != "release" && publishLibraryVariantsGroupedByFlavor }
val usageContexts = createAndroidUsageContexts(androidVariant, compilation, artifactClassifier)
createKotlinVariant(
lowerCamelCaseName(compilation.target.name, *flavorGroupNameParts.toTypedArray()),
compilation,
usageContexts
).apply {
sourcesArtifacts = setOf(
sourcesJarArtifact(
compilation, compilation.disambiguateName(""),
dashSeparatedName(
compilation.target.name.toLowerCase(),
*flavorNames.map { it.toLowerCase() }.toTypedArray(),
buildTypeName.takeIf { it != "release" }?.toLowerCase()
),
classifierPrefix = artifactClassifier
)
)
if (!publishLibraryVariantsGroupedByFlavor) {
defaultArtifactIdSuffix =
dashSeparatedName(
(getFlavorNames(androidVariant) + getBuildTypeName(androidVariant).takeIf { it != "release" })
.map { it?.toLowerCase() }
).takeIf { it.isNotEmpty() }
}
}
}
if (publishLibraryVariantsGroupedByFlavor) {
JointAndroidKotlinTargetComponent(
this@KotlinAndroidTarget,
nestedVariants,
flavorGroupNameParts,
nestedVariants.flatMap { it.sourcesArtifacts }.toSet()
)
} else {
nestedVariants.single()
} as KotlinTargetComponent
}.toSet()
}
private fun <T> AbstractAndroidProjectHandler<T>.createAndroidUsageContexts(
variant: T,
compilation: KotlinCompilation<*>,
artifactClassifier: String?
): Set<DefaultKotlinUsageContext> {
val variantName = getVariantName(variant)
val outputTask = getLibraryOutputTask(variant) ?: return emptySet()
val artifact = run {
val archivesConfigurationName = lowerCamelCaseName(targetName, variantName, "archives")
project.configurations.maybeCreate(archivesConfigurationName).apply {
isCanBeConsumed = false
isCanBeResolved = false
}
project.artifacts.add(archivesConfigurationName, outputTask) { artifact ->
artifact.classifier = artifactClassifier
}
}
val apiElementsConfigurationName = lowerCamelCaseName(variantName, "apiElements")
val runtimeElementsConfigurationName = lowerCamelCaseName(variantName, "runtimeElements")
// Here, `JAVA_API` and `JAVA_RUNTIME_JARS` are used intentionally as Gradle needs this for
// ordering of the usage contexts (prioritizing the dependencies) when merging them into the POM;
// These Java usages should not be replaced with the custom Kotlin usages.
return listOf(
apiElementsConfigurationName to JAVA_API,
runtimeElementsConfigurationName to JAVA_RUNTIME_JARS
).mapTo(mutableSetOf()) { (dependencyConfigurationName, usageName) ->
DefaultKotlinUsageContext(
compilation,
project.usageByName(usageName),
dependencyConfigurationName,
overrideConfigurationArtifacts = setOf(artifact)
)
}
}
}
open class KotlinWithJavaTarget<KotlinOptionsType : KotlinCommonOptions>(
project: Project,
override val platformType: KotlinPlatformType,
override val targetName: String
) : AbstractKotlinTarget(project) {
override var disambiguationClassifier: String? = null
internal set
override val defaultConfigurationName: String
get() = Dependency.DEFAULT_CONFIGURATION
override val apiElementsConfigurationName: String
get() = JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME
override val runtimeElementsConfigurationName: String
get() = JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME
override val artifactsTaskName: String
get() = JavaPlugin.JAR_TASK_NAME
override val compilations: NamedDomainObjectContainer<KotlinWithJavaCompilation<KotlinOptionsType>> =
@Suppress("UNCHECKED_CAST")
project.container(
KotlinWithJavaCompilation::class.java as Class<KotlinWithJavaCompilation<KotlinOptionsType>>,
KotlinWithJavaCompilationFactory(project, this)
)
}
open class KotlinOnlyTarget<T : KotlinCompilation<*>>(
project: Project,
override val platformType: KotlinPlatformType
) : AbstractKotlinTarget(project) {
override lateinit var compilations: NamedDomainObjectContainer<T>
internal set
override lateinit var targetName: String
internal set
override var disambiguationClassifier: String? = null
internal set
}
class KotlinNativeTarget(
project: Project,
val konanTarget: KonanTarget
) : KotlinOnlyTarget<KotlinNativeCompilation>(project, KotlinPlatformType.native) {
init {
attributes.attribute(konanTargetAttribute, konanTarget.name)
}
val binaries = if(isGradleVersionAtLeast(4, 2)) {
// Use newInstance to allow accessing binaries by their names in Groovy using the extension mechanism.
project.objects.newInstance(KotlinNativeBinaryContainer::class.java, this, WrapUtil.toDomainObjectSet(NativeBinary::class.java))
} else {
KotlinNativeBinaryContainer(this, WrapUtil.toDomainObjectSet(NativeBinary::class.java))
}
fun binaries(configure: KotlinNativeBinaryContainer.() -> Unit) {
binaries.configure()
}
fun binaries(configure: Closure<*>) {
ConfigureUtil.configure(configure, binaries)
}
override val artifactsTaskName: String
get() = disambiguateName("binaries")
override val publishable: Boolean
get() = konanTarget.enabledOnCurrentHost
// User-visible constants
val DEBUG = NativeBuildType.DEBUG
val RELEASE = NativeBuildType.RELEASE
val EXECUTABLE = NativeOutputKind.EXECUTABLE
val FRAMEWORK = NativeOutputKind.FRAMEWORK
val DYNAMIC = NativeOutputKind.DYNAMIC
val STATIC = NativeOutputKind.STATIC
companion object {
val konanTargetAttribute = Attribute.of(
"org.jetbrains.kotlin.native.target",
String::class.java
)
}
}