[K/N] Use configurations for interproject compdb deps ^KT-53776

Merge-request: KT-MR-7158
Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
Alexander Shabalin
2022-12-09 09:42:08 +00:00
committed by Space Team
parent 76997edebe
commit 01cabefea7
9 changed files with 207 additions and 148 deletions
@@ -22,10 +22,9 @@ import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.ExecClang import org.jetbrains.kotlin.ExecClang
import org.jetbrains.kotlin.execLlvmUtility import org.jetbrains.kotlin.execLlvmUtility
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.PlatformManager import org.jetbrains.kotlin.konan.target.PlatformManager
import org.jetbrains.kotlin.konan.target.SanitizerKind import org.jetbrains.kotlin.konan.target.SanitizerKind
import org.jetbrains.kotlin.utils.Maybe import org.jetbrains.kotlin.konan.target.TargetWithSanitizer
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
@@ -83,21 +82,23 @@ private abstract class CompileToBitcodeJob : WorkAction<CompileToBitcodeJob.Para
* *
* Compiles [inputFiles] into [outputFile] with [compiler] and `llvm-link`. * Compiles [inputFiles] into [outputFile] with [compiler] and `llvm-link`.
* *
* @property target target for which to compile
* @see CompileToBitcodePlugin * @see CompileToBitcodePlugin
*/ */
abstract class CompileToBitcode @Inject constructor( abstract class CompileToBitcode @Inject constructor(
@Input val target: KonanTarget, private val _target: TargetWithSanitizer,
private val _sanitizer: Maybe<SanitizerKind>,
) : DefaultTask() { ) : DefaultTask() {
/**
* Target for which to compile
*/
@get:Input
val target by _target::target
/** /**
* Compile with sanitizer enabled. * Optional [sanitizer][SanitizerKind] for [target].
*/ */
@get:Input @get:Input
@get:Optional @get:Optional
val sanitizer val sanitizer by _target::sanitizer
get() = _sanitizer.orNull
/** /**
* Final output file. * Final output file.
@@ -17,20 +17,12 @@ import org.gradle.kotlin.dsl.*
import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.ExecClang import org.jetbrains.kotlin.ExecClang
import org.jetbrains.kotlin.cpp.* import org.jetbrains.kotlin.cpp.*
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.konan.target.PlatformManager
import org.jetbrains.kotlin.konan.target.SanitizerKind
import org.jetbrains.kotlin.konan.target.TargetDomainObjectContainer
import org.jetbrains.kotlin.testing.native.GoogleTestExtension import org.jetbrains.kotlin.testing.native.GoogleTestExtension
import org.jetbrains.kotlin.utils.Maybe import org.jetbrains.kotlin.utils.capitalized
import org.jetbrains.kotlin.utils.asMaybe
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
@OptIn(ExperimentalStdlibApi::class)
private val String.capitalized: String
get() = replaceFirstChar { it.uppercase() }
private fun String.snakeCaseToUpperCamelCase() = split('_').joinToString(separator = "") { it.capitalized } private fun String.snakeCaseToUpperCamelCase() = split('_').joinToString(separator = "") { it.capitalized }
private fun fullTaskName(name: String, targetName: String, sanitizer: SanitizerKind?) = "${targetName}${name.snakeCaseToUpperCamelCase()}${sanitizer.taskSuffix}" private fun fullTaskName(name: String, targetName: String, sanitizer: SanitizerKind?) = "${targetName}${name.snakeCaseToUpperCamelCase()}${sanitizer.taskSuffix}"
@@ -76,8 +68,8 @@ open class CompileToBitcodePlugin : Plugin<Project> {
open class CompileToBitcodeExtension @Inject constructor(val project: Project) : TargetDomainObjectContainer<CompileToBitcodeExtension.Target>(project) { open class CompileToBitcodeExtension @Inject constructor(val project: Project) : TargetDomainObjectContainer<CompileToBitcodeExtension.Target>(project) {
init { init {
this.factory = { target, sanitizer -> this.factory = { target ->
project.objects.newInstance<Target>(this, target, sanitizer.asMaybe) project.objects.newInstance<Target>(this, target)
} }
} }
@@ -118,11 +110,10 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) :
} }
abstract class TestsGroup @Inject constructor( abstract class TestsGroup @Inject constructor(
val target: KonanTarget, private val _target: TargetWithSanitizer,
private val _sanitizer: Maybe<SanitizerKind>,
) { ) {
val sanitizer val target by _target::target
get() = _sanitizer.orNull val sanitizer by _target::sanitizer
abstract val testedModules: ListProperty<String> abstract val testedModules: ListProperty<String>
abstract val testSupportModules: ListProperty<String> abstract val testSupportModules: ListProperty<String>
abstract val testFrameworkModules: ListProperty<String> abstract val testFrameworkModules: ListProperty<String>
@@ -131,10 +122,10 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) :
abstract class Target @Inject constructor( abstract class Target @Inject constructor(
private val owner: CompileToBitcodeExtension, private val owner: CompileToBitcodeExtension,
val target: KonanTarget, private val _target: TargetWithSanitizer,
_sanitizer: Maybe<SanitizerKind>,
) { ) {
val sanitizer = _sanitizer.orNull val target by _target::target
val sanitizer by _target::sanitizer
private val project by owner::project private val project by owner::project
@@ -157,7 +148,7 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) :
} }
private fun addToCompdb(compileTask: CompileToBitcode) { private fun addToCompdb(compileTask: CompileToBitcode) {
compilationDatabase.target(target, sanitizer) { compilationDatabase.target(_target) {
entry { entry {
val args = listOf(execClang.resolveExecutable(compileTask.compiler.get())) + compileTask.compilerFlags.get() + execClang.clangArgsForCppRuntime(target.name) val args = listOf(execClang.resolveExecutable(compileTask.compiler.get())) + compileTask.compilerFlags.get() + execClang.clangArgsForCppRuntime(target.name)
directory.set(compileTask.compilerWorkingDirectory) directory.set(compileTask.compilerWorkingDirectory)
@@ -166,6 +157,13 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) :
// Only the location of output file matters, compdb does not depend on the compilation result. // Only the location of output file matters, compdb does not depend on the compilation result.
output.set(compileTask.outputFile.locationOnly.map { it.asFile.absolutePath }) output.set(compileTask.outputFile.locationOnly.map { it.asFile.absolutePath })
} }
// Compile task depends on the toolchain (including headers) and on the source code (e.g. googletest).
// compdb task should also have these dependencies. This way the generated database will point to the
// code that actually exists.
// TODO: Probably module should know dependencies on its own.
task.configure {
dependsOn(compileTask.dependsOn)
}
} }
} }
@@ -173,7 +171,7 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) :
val targetName = target.name val targetName = target.name
val allMainModulesTask = owner.allMainModulesTasks[targetName]!! val allMainModulesTask = owner.allMainModulesTasks[targetName]!!
val taskName = fullTaskName(name, targetName, sanitizer) val taskName = fullTaskName(name, targetName, sanitizer)
val task = project.tasks.create(taskName, CompileToBitcode::class.java, target, sanitizer.asMaybe).apply { val task = project.tasks.create(taskName, CompileToBitcode::class.java, _target).apply {
this.moduleName.set(name) this.moduleName.set(name)
this.outputFile.convention(moduleName.flatMap { project.layout.buildDirectory.file("bitcode/$outputGroup/$target${sanitizer.dirSuffix}/$it.bc") }) this.outputFile.convention(moduleName.flatMap { project.layout.buildDirectory.file("bitcode/$outputGroup/$target${sanitizer.dirSuffix}/$it.bc") })
this.outputDirectory.convention(moduleName.flatMap { project.layout.buildDirectory.dir("bitcode/$outputGroup/$target${sanitizer.dirSuffix}/$it") }) this.outputDirectory.convention(moduleName.flatMap { project.layout.buildDirectory.dir("bitcode/$outputGroup/$target${sanitizer.dirSuffix}/$it") })
@@ -204,7 +202,7 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) :
testTaskName: String, testTaskName: String,
action: Action<in TestsGroup>, action: Action<in TestsGroup>,
) { ) {
val testsGroup = project.objects.newInstance(TestsGroup::class.java, target, sanitizer.asMaybe).apply { val testsGroup = project.objects.newInstance(TestsGroup::class.java, _target).apply {
testFrameworkModules.convention(listOf("googletest", "googlemock")) testFrameworkModules.convention(listOf("googletest", "googlemock"))
testLauncherModule.convention("test_support") testLauncherModule.convention("test_support")
action.execute(this) action.execute(this)
@@ -219,7 +217,7 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) :
val testedModulesTestTasks = testedModulesMainTasks.mapNotNull { val testedModulesTestTasks = testedModulesMainTasks.mapNotNull {
val name = "${it.name}TestBitcode" val name = "${it.name}TestBitcode"
val task = project.tasks.findByName(name) as? CompileToBitcode val task = project.tasks.findByName(name) as? CompileToBitcode
?: project.tasks.create(name, CompileToBitcode::class.java, it.target, it.sanitizer.asMaybe).apply { ?: project.tasks.create(name, CompileToBitcode::class.java, _target).apply {
this.moduleName.set(it.moduleName) this.moduleName.set(it.moduleName)
this.outputFile.convention(moduleName.flatMap { project.layout.buildDirectory.file("bitcode/test/$target${sanitizer.dirSuffix}/${it}Tests.bc") }) this.outputFile.convention(moduleName.flatMap { project.layout.buildDirectory.file("bitcode/test/$target${sanitizer.dirSuffix}/${it}Tests.bc") })
this.outputDirectory.convention(moduleName.flatMap { project.layout.buildDirectory.dir("bitcode/test/$target${sanitizer.dirSuffix}/${it}Tests") }) this.outputDirectory.convention(moduleName.flatMap { project.layout.buildDirectory.dir("bitcode/test/$target${sanitizer.dirSuffix}/${it}Tests") })
@@ -247,7 +245,7 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) :
val testedAndTestSupportModulesTestSupportTasks = (testedModulesMainTasks + testSupportModulesMainTasks).mapNotNull { val testedAndTestSupportModulesTestSupportTasks = (testedModulesMainTasks + testSupportModulesMainTasks).mapNotNull {
val name = "${it.name}TestSupportBitcode" val name = "${it.name}TestSupportBitcode"
val task = project.tasks.findByName(name) as? CompileToBitcode val task = project.tasks.findByName(name) as? CompileToBitcode
?: project.tasks.create(name, CompileToBitcode::class.java, it.target, it.sanitizer.asMaybe).apply { ?: project.tasks.create(name, CompileToBitcode::class.java, _target).apply {
this.moduleName.set(it.moduleName) this.moduleName.set(it.moduleName)
this.outputFile.convention(moduleName.flatMap { project.layout.buildDirectory.file("bitcode/test/$target${sanitizer.dirSuffix}/${it}TestSupport.bc") }) this.outputFile.convention(moduleName.flatMap { project.layout.buildDirectory.file("bitcode/test/$target${sanitizer.dirSuffix}/${it}TestSupport.bc") })
this.outputDirectory.convention(moduleName.flatMap { project.layout.buildDirectory.dir("bitcode/test/$target${sanitizer.dirSuffix}/${it}TestSupport") }) this.outputDirectory.convention(moduleName.flatMap { project.layout.buildDirectory.dir("bitcode/test/$target${sanitizer.dirSuffix}/${it}TestSupport") })
@@ -8,21 +8,16 @@ package org.jetbrains.kotlin.cpp
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Usage
import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.ListProperty import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.*
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.newInstance
import org.gradle.kotlin.dsl.register
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.SanitizerKind
import org.jetbrains.kotlin.konan.target.TargetDomainObjectContainer import org.jetbrains.kotlin.konan.target.TargetDomainObjectContainer
import org.jetbrains.kotlin.konan.target.targetSuffix import org.jetbrains.kotlin.konan.target.TargetWithSanitizer
import org.jetbrains.kotlin.utils.Maybe import org.jetbrains.kotlin.utils.capitalized
import org.jetbrains.kotlin.utils.asMaybe
import javax.inject.Inject import javax.inject.Inject
/** /**
@@ -30,25 +25,37 @@ import javax.inject.Inject
* *
* Allows generating compilation databases for different targets. For example: * Allows generating compilation databases for different targets. For example:
* ``` * ```
* dependencies {
* compilationDatabase(project(":some:other:project"))
* }
*
* compilationDatabase { * compilationDatabase {
* target(someTarget1) { * target(someTarget1) {
* entry { ... } * entry { ... }
* } * }
* allTargets { * allTargets {
* mergeFrom(provider { project("subproject") }) * mergeFrom(compilationDatabase)
* } * }
* } * }
* ``` * ```
* Adds an entry for target `someTarget1`, and for each known target merges in databases generated * Adds an entry for target `someTarget1`, and for each known target merges in databases from `:some:other:project`.
* by this plugin in `subproject`.
* The task that generates the database can be found via `compilationDatabase.target(someTarget1).task`. * The task that generates the database can be found via `compilationDatabase.target(someTarget1).task`.
* *
* @see CompilationDatabasePlugin gradle plugin that creates this extension. * @see CompilationDatabasePlugin gradle plugin that creates this extension.
*/ */
abstract class CompilationDatabaseExtension @Inject constructor(private val project: Project) : TargetDomainObjectContainer<CompilationDatabaseExtension.Target>(project) { abstract class CompilationDatabaseExtension @Inject constructor(private val project: Project) : TargetDomainObjectContainer<CompilationDatabaseExtension.Target>(project) {
init { init {
this.factory = { target, sanitizer -> this.factory = { target ->
project.objects.newInstance<Target>(project, target, sanitizer.asMaybe) project.objects.newInstance<Target>(this, target)
}
}
private val outgoingConfiguration = project.configurations.create("generateCompilationDatabase") {
description = "Generate Compilation Database"
isCanBeConsumed = true
isCanBeResolved = false
attributes {
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, USAGE))
} }
} }
@@ -61,10 +68,10 @@ abstract class CompilationDatabaseExtension @Inject constructor(private val proj
* @property sanitizer optional sanitizer for [target]. * @property sanitizer optional sanitizer for [target].
*/ */
abstract class Entry @Inject constructor( abstract class Entry @Inject constructor(
val target: KonanTarget, private val _target: TargetWithSanitizer,
_sanitizer: Maybe<SanitizerKind>,
) { ) {
val sanitizer = _sanitizer.orNull val target by _target::target
val sanitizer by _target::sanitizer
/** /**
* **directory** from the [JSON Compilation Database](https://clang.llvm.org/docs/JSONCompilationDatabase.html#format). * **directory** from the [JSON Compilation Database](https://clang.llvm.org/docs/JSONCompilationDatabase.html#format).
@@ -101,31 +108,19 @@ abstract class CompilationDatabaseExtension @Inject constructor(private val proj
* Configure compilation database generation for [target]. * Configure compilation database generation for [target].
* *
* [entry] to add new entries. * [entry] to add new entries.
* [mergeFrom] to merge databases from other projects with [CompilationDatabasePlugin]s.
* [task] is the gradle task for compilation database generation. * [task] is the gradle task for compilation database generation.
* *
* @property target target for which compilation database is generated. * @property target target for which compilation database is generated.
* @property sanitizer optional sanitizer for which compilation database is generated. * @property sanitizer optional sanitizer for which compilation database is generated.
*/ */
abstract class Target @Inject constructor( abstract class Target @Inject constructor(
private val project: Project, private val owner: CompilationDatabaseExtension,
val target: KonanTarget, private val _target: TargetWithSanitizer,
_sanitizer: Maybe<SanitizerKind>,
) { ) {
val sanitizer = _sanitizer.orNull val target by _target::target
val sanitizer by _target::sanitizer
protected abstract val mergeFrom: ListProperty<GenerateCompilationDatabase> private val project by owner::project
/**
* Merge compilation database generated for [from] project for [target] with optional [sanitizer].
*
* @param from project with applied [CompilationDatabasePlugin] to merge compilation database from.
*/
fun mergeFrom(from: Provider<Project>) {
mergeFrom.add(from.flatMap { project ->
project.extensions.getByType<CompilationDatabaseExtension>().target(target, sanitizer).task
})
}
protected abstract val entries: ListProperty<GenerateCompilationDatabase.Entry> protected abstract val entries: ListProperty<GenerateCompilationDatabase.Entry>
@@ -136,7 +131,7 @@ abstract class CompilationDatabaseExtension @Inject constructor(private val proj
*/ */
fun entry(action: Action<in Entry>) { fun entry(action: Action<in Entry>) {
entries.add(project.provider { entries.add(project.provider {
val instance = project.objects.newInstance<Entry>(target, sanitizer.asMaybe).apply { val instance = project.objects.newInstance<Entry>(_target).apply {
action.execute(this) action.execute(this)
} }
project.objects.newInstance<GenerateCompilationDatabase.Entry>().apply { project.objects.newInstance<GenerateCompilationDatabase.Entry>().apply {
@@ -148,21 +143,56 @@ abstract class CompilationDatabaseExtension @Inject constructor(private val proj
}) })
} }
protected abstract val mergeFrom: ListProperty<Configuration>
/**
* Merge compilation database entries from given [configuration].
*
* @param configuration provider of additional compilation database entries
*/
fun mergeFrom(configuration: Configuration) {
mergeFrom.add(configuration)
}
/** /**
* Gradle task that generates compilation database for [target] with optional [sanitizer]. * Gradle task that generates compilation database for [target] with optional [sanitizer].
*/ */
val task = project.tasks.register<GenerateCompilationDatabase>("${target}${sanitizer.targetSuffix}CompilationDatabase") { val task = project.tasks.register<GenerateCompilationDatabase>("compilationDatabase${_target.name.capitalized}") {
description = "Generate compilation database for $target${sanitizer.targetSuffix}" description = "Generate compilation database for $_target"
group = TASK_GROUP group = TASK_GROUP
mergeFiles.from(mergeFrom) mergeFiles.from(mergeFrom.map { configurations ->
configurations.map {
it.incoming.artifactView {
attributes {
attribute(TargetWithSanitizer.TARGET_ATTRIBUTE, _target)
}
}.files
}
})
entries.set(this@Target.entries) entries.set(this@Target.entries)
outputFile.set(project.layout.buildDirectory.file("${target}${sanitizer.targetSuffix}/compile_commands.json")) outputFile.set(project.layout.buildDirectory.file("$_target/compile_commands.json"))
}
init {
owner.outgoingConfiguration.outgoing {
variants {
create("$_target") {
attributes {
attribute(TargetWithSanitizer.TARGET_ATTRIBUTE, _target)
}
artifact(task)
}
}
}
} }
} }
companion object { companion object {
@JvmStatic @JvmStatic
val TASK_GROUP = "development support" val TASK_GROUP = "development support"
@JvmStatic
val USAGE = "compilationDatabase"
} }
} }
@@ -170,11 +200,23 @@ abstract class CompilationDatabaseExtension @Inject constructor(private val proj
* Generating [JSON Compilation Database](https://clang.llvm.org/docs/JSONCompilationDatabase.html). * Generating [JSON Compilation Database](https://clang.llvm.org/docs/JSONCompilationDatabase.html).
* *
* Creates [CompilationDatabaseExtension] extension named `compilationDatabase`. * Creates [CompilationDatabaseExtension] extension named `compilationDatabase`.
* Creates [Configuration][org.gradle.api.artifacts.Configuration] named `compilationDatabase`.
* *
* @see CompilationDatabaseExtension extension that this plugin creates. * @see CompilationDatabaseExtension extension that this plugin creates.
*/ */
open class CompilationDatabasePlugin : Plugin<Project> { open class CompilationDatabasePlugin : Plugin<Project> {
override fun apply(project: Project) { override fun apply(project: Project) {
project.extensions.create<CompilationDatabaseExtension>("compilationDatabase", project) project.extensions.create<CompilationDatabaseExtension>("compilationDatabase", project)
project.dependencies.attributesSchema {
attribute(TargetWithSanitizer.TARGET_ATTRIBUTE)
}
project.configurations.create("compilationDatabase") {
description = "Compilation Database"
isCanBeConsumed = false
isCanBeResolved = true
attributes {
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(CompilationDatabaseExtension.USAGE))
}
}
} }
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.cpp package org.jetbrains.kotlin.cpp
import com.google.gson.JsonSyntaxException
import com.google.gson.annotations.Expose import com.google.gson.annotations.Expose
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.ConfigurableFileCollection
@@ -126,7 +127,11 @@ abstract class GenerateCompilationDatabase : DefaultTask() {
val serialized = mutableListOf<SerializedEntry>() val serialized = mutableListOf<SerializedEntry>()
mergeFiles.files.forEach { file -> mergeFiles.files.forEach { file ->
FileReader(file).use { FileReader(file).use {
serialized.addAll(gson.fromJson(it, Array<SerializedEntry>::class.java)) try {
serialized.addAll(gson.fromJson(it, Array<SerializedEntry>::class.java))
} catch (e: JsonSyntaxException) {
throw IllegalStateException("Failed to parse $file as compilation database", e)
}
} }
} }
entries.get().forEach { entries.get().forEach {
@@ -16,7 +16,7 @@ import org.gradle.kotlin.dsl.getByType
* Associative container from a [KonanTarget] with optional [SanitizerKind] to [T]. * Associative container from a [KonanTarget] with optional [SanitizerKind] to [T].
* *
* Serves similar purpose to [NamedDomainObjectContainer][org.gradle.api.NamedDomainObjectContainer] * Serves similar purpose to [NamedDomainObjectContainer][org.gradle.api.NamedDomainObjectContainer]
* except this is keyed on a target instead of a name. Also this implementation does not support lazy * except this is keyed on a target instead of a name. Also, this implementation does not support lazy
* creation. * creation.
* *
* Plugin extensions can inherit from this to automatically get API suitable for `build.gradle.kts`. * Plugin extensions can inherit from this to automatically get API suitable for `build.gradle.kts`.
@@ -31,15 +31,12 @@ import org.gradle.kotlin.dsl.getByType
* target(someTarget) { * target(someTarget) {
* // This is a lambda inside of a T scope. Called for `someTarget` without any sanitizer. * // This is a lambda inside of a T scope. Called for `someTarget` without any sanitizer.
* } * }
* target(someTarget, someSanitizer) { * target(someTarget.withSanitizer(someSanitizer)) {
* // This is a lambda inside of a T scope. Called for `someTarget` with `someSanitizer`. * // This is a lambda inside of a T scope. Called for `someTarget` with `someSanitizer`.
* } * }
* hostTarget { * hostTarget {
* // This is a lambda inside of a T scope. Called for the host target without any sanitizer. * // This is a lambda inside of a T scope. Called for the host target without any sanitizer.
* } * }
* hostTarget(someSanitizer) {
* // This is a lambda inside of a T scope. Called for the host target with `someSanitizer`.
* }
* } * }
* *
* someExtension.target(someTarget) // returns T if `someTarget` was configured. Otherwise fails with UnknownDomainObjectException. * someExtension.target(someTarget) // returns T if `someTarget` was configured. Otherwise fails with UnknownDomainObjectException.
@@ -57,36 +54,32 @@ open class TargetDomainObjectContainer<T : Any> constructor(
/** /**
* How to create [T]. Must be set before using the rest of API. * How to create [T]. Must be set before using the rest of API.
*/ */
lateinit var factory: (KonanTarget, SanitizerKind?) -> T lateinit var factory: (TargetWithSanitizer) -> T
private val targets: MutableMap<Pair<KonanTarget, SanitizerKind?>, T> = mutableMapOf() private val targets: MutableMap<TargetWithSanitizer, T> = mutableMapOf()
/** /**
* Create or update configuration [T] for [target] with optional [sanitizer] and apply [action] to it. * Create or update configuration [T] for [target] and apply [action] to it.
* *
* @param target target of the configuration * @param target target of the configuration
* @param sanitizer optional sanitizer for [target]
* @param action action to apply to the configuration * @param action action to apply to the configuration
* @return resulting configuration * @return resulting configuration
*/ */
fun target(target: KonanTarget, sanitizer: SanitizerKind? = null, action: Action<in T>): T { fun target(target: TargetWithSanitizer, action: Action<in T>): T {
val key = target to sanitizer val element = targets.getOrPut(target) { factory(target) }
val element = targets.getOrPut(key) { factory(target, sanitizer) }
action.execute(element) action.execute(element)
return element return element
} }
/** /**
* Get configuration [T] for [target] with optional [sanitizer]. * Get configuration [T] for [target].
* *
* @param target target of the configuration * @param target target of the configuration
* @param sanitizer optional sanitizer for [target]
* @return resulting configuration * @return resulting configuration
* @throws UnknownDomainObjectException if configuration for [target] and [sanitizer] does not exist * @throws UnknownDomainObjectException if configuration for [target] does not exist
*/ */
fun target(target: KonanTarget, sanitizer: SanitizerKind? = null): T { fun target(target: TargetWithSanitizer): T {
val key = target to sanitizer return targets[target] ?: throw UnknownDomainObjectException("Configuration for $target does not exists")
return targets.get(key) ?: throw UnknownDomainObjectException("Configuration for $target${sanitizer.targetSuffix} does not exists")
} }
/** /**
@@ -95,13 +88,8 @@ open class TargetDomainObjectContainer<T : Any> constructor(
* @param action action to apply to the configuration * @param action action to apply to the configuration
* @return list of configurations * @return list of configurations
*/ */
fun allTargets(action: Action<in T>): List<T> { fun allTargets(action: Action<in T>): List<T> = platformManager.allTargetsWithSanitizers.map {
return platformManager.enabled.flatMap { target -> this.target(it, action)
val sanitizers = target.supportedSanitizers() + listOf(null)
sanitizers.map { sanitizer ->
this.target(target, sanitizer, action)
}
}
} }
/** /**
@@ -114,33 +102,21 @@ open class TargetDomainObjectContainer<T : Any> constructor(
} }
/** /**
* Create or update configuration [T] for [host target][HostManager.host] with optional [sanitizer] and apply [action] to it. * Create or update configuration [T] for [host target][HostManager.host] and apply [action] to it.
* *
* @param sanitizer optional sanitizer for [host target][HostManager.host]
* @param action action to apply to the configuration * @param action action to apply to the configuration
* @return resulting configuration * @return resulting configuration
*/ */
fun hostTarget(sanitizer: SanitizerKind? = null, action: Action<in T>): T { fun hostTarget(action: Action<in T>): T {
return target(HostManager.host, sanitizer, action) return target(HostManager.host.withSanitizer(), action)
} }
/** /**
* Get configuration [T] for [host target][HostManager.host] with optional [sanitizer]. * Get configuration [T] for [host target][HostManager.host].
*
* @param sanitizer optional sanitizer for [host target][HostManager.host]
* @return resulting configuration
* @throws UnknownDomainObjectException if configuration for [host target][HostManager.host] and [sanitizer] does not exist
*/
fun hostTarget(sanitizer: SanitizerKind? = null): T {
return target(HostManager.host, sanitizer)
}
/**
* Get configuration [T] for [host target][HostManager.host] without sanitizer.
* *
* @return resulting configuration * @return resulting configuration
* @throws UnknownDomainObjectException if configuration for [host target][HostManager.host] without sanitizer does not exist * @throws UnknownDomainObjectException if configuration for [host target][HostManager.host] does not exist
*/ */
val hostTarget: T val hostTarget: T
get() = hostTarget() get() = target(HostManager.host.withSanitizer())
} }
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.konan.target
import org.gradle.api.Named
import org.gradle.api.attributes.Attribute
import java.io.Serializable
/**
* [Target][KonanTarget] with optional [sanitizer][SanitizerKind].
*
* Can be used as a gradle attribute: `attribute(TargetWithSanitizer.TARGET_ATTRIBUTE, target.withSanitizer())`
*/
class TargetWithSanitizer(
val target: KonanTarget,
val sanitizer: SanitizerKind?,
) : Named, Serializable {
override fun getName(): String = "$target${sanitizer.targetSuffix}"
override fun toString(): String = name
override fun hashCode(): Int {
return name.hashCode()
}
override fun equals(other: Any?): Boolean {
val otherTarget = other as? TargetWithSanitizer ?: return false
return name == otherTarget.name
}
companion object {
@JvmStatic
val TARGET_ATTRIBUTE = Attribute.of("org.jetbrains.kotlin.target", TargetWithSanitizer::class.java)
}
}
/**
* Construct [TargetWithSanitizer] from [target][KonanTarget] and optional [sanitizer][SanitizerKind].
*/
fun KonanTarget.withSanitizer(sanitizer: SanitizerKind? = null) = TargetWithSanitizer(this, sanitizer)
/**
* All known targets with their sanitizers.
*/
val PlatformManager.allTargetsWithSanitizers
get() = this.enabled.flatMap { target ->
listOf(target.withSanitizer()) + target.supportedSanitizers().map {
target.withSanitizer(it)
}
}
@@ -1,23 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.utils
/**
* Wrapper over [T]?.
*
* Useful for some generalized functions that you want to pass [T]? into, but they forbid nullable types.
* For example, [ObjectFactory.newInstance][org.gradle.api.model.ObjectFactory.newInstance] cannot be used
* to construct a type that has [T]? as a constructor argument.
*
* @property orNull get underlying value.
*/
data class Maybe<T>(val orNull: T?)
/**
* Construct [Maybe]<[T]> from [T]?.
*/
inline val <T> T?.asMaybe
get() = Maybe(this)
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.utils
@OptIn(ExperimentalStdlibApi::class)
val String.capitalized: String
get() = replaceFirstChar { it.uppercase() }
+6 -9
View File
@@ -142,6 +142,8 @@ configurations {
commonSources commonSources
} }
apply plugin: CompilationDatabasePlugin
dependencies { dependencies {
ftpAntTask 'org.apache.ant:ant-commons-net:1.9.9' ftpAntTask 'org.apache.ant:ant-commons-net:1.9.9'
distPack project(':kotlin-native:Interop:Runtime') distPack project(':kotlin-native:Interop:Runtime')
@@ -157,9 +159,10 @@ dependencies {
commonSources project(path: ':kotlin-stdlib-common', configuration: 'sources') commonSources project(path: ':kotlin-stdlib-common', configuration: 'sources')
commonSources project(path: ':kotlin-test:kotlin-test-common', configuration: 'sources') commonSources project(path: ':kotlin-test:kotlin-test-common', configuration: 'sources')
commonSources project(path: ':kotlin-test:kotlin-test-annotations-common', configuration: 'sources') commonSources project(path: ':kotlin-test:kotlin-test-annotations-common', configuration: 'sources')
compilationDatabase project(":kotlin-native:common")
compilationDatabase project(":kotlin-native:runtime")
} }
apply plugin: CompilationDatabasePlugin
apply plugin: GitClangFormatPlugin apply plugin: GitClangFormatPlugin
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
apply plugin: BasePlugin apply plugin: BasePlugin
@@ -725,13 +728,7 @@ task pusher(type: KotlinBuildPusher){
compilationDatabase { compilationDatabase {
allTargets { allTargets {
mergeFrom(provider { project("common") }) mergeFrom(configurations.compilationDatabase)
mergeFrom(provider { project("runtime") })
// TODO: Reconsider this
task.configure {
dependsOn(":kotlin-native:runtime:downloadGoogleTest")
}
} }
} }
@@ -886,4 +883,4 @@ publishing {
) )
} }
} }
} }