Lazy task creation in konan plugin using Gradle Providers API.

Removes obsolete CMake task and IDE model.

Use Provider API for klib files. Replace findAll with matching to make tasks configure lazily

Make some test tasks use lazy configuration

Fix adding tasks to lists. Add it. to all fields to improve IDEA navigation

Remove obsolete IDE model from the task

Remove tooling model tests

Fix aggregate task dependency

Fix run tasks. Cleanup

Review fixes
This commit is contained in:
Pavel Punegov
2021-01-19 13:51:46 +03:00
committed by Vasily Levchenko
parent 716326a60f
commit 8a48609823
15 changed files with 218 additions and 777 deletions
+27 -24
View File
@@ -192,9 +192,9 @@ konanArtifacts {
}
}
task installTestLibrary(type: KlibInstall) {
dependsOn compileKonanTestLibraryHost
klib = konanArtifacts.testLibrary.getArtifactByTarget('host')
def installTestLib = tasks.register("installTestLibrary", KlibInstall) {
dependsOn "compileKonanTestLibraryHost"
klib = project.provider { konanArtifacts.testLibrary.getArtifactByTarget('host') }
repo = rootProject.file(testLibraryDir)
doLast {
@@ -2165,7 +2165,7 @@ standaloneTest("link_testLib_explicitly") {
// there are no testLibrary for cross targets yet.
enabled = project.target.name == project.hostName
dependsOn 'installTestLibrary'
dependsOn installTestLib
goldValue = "This is a side effect of a test library linked into the binary.\nYou should not be seeing this.\n\nHello\n"
source = "link/omit/hello.kt"
// We force library inclusion, so need to see the warning banner.
@@ -3535,12 +3535,12 @@ standaloneTest("testing_stacktrace") {
}
// Just check that the driver is able to produce runnable binaries.
task driver0(type: KonanDriverTest) {
tasks.register("driver0", KonanDriverTest) {
goldValue = "Hello, world!\n"
source = "runtime/basic/driver0.kt"
}
task driver_opt(type: KonanDriverTest) {
tasks.register("driver_opt", KonanDriverTest) {
disabled = (cacheTesting != null) // Cache is not compatible with -opt.
goldValue = "Hello, world!\n"
source = "runtime/basic/driver0.kt"
@@ -3728,14 +3728,16 @@ createInterop("embedStaticLibraries") {
// Note: also hardcoded in def file.
final String libDir = "$buildDir/embedStaticLibraries/"
it.getByTarget(target.name).doFirst {
1.upto(4) {
UtilsKt.buildStaticLibrary(
project,
[file("$projectDir/interop/embedStaticLibraries/${it}.c")],
file("$libDir/${it}.a"),
file("$libDir/${it}.objs"),
)
it.getByTarget(target.name).configure {
doFirst {
1.upto(4) {
UtilsKt.buildStaticLibrary(
project,
[file("$projectDir/interop/embedStaticLibraries/${it}.c")],
file("$libDir/${it}.a"),
file("$libDir/${it}.objs"),
)
}
}
}
@@ -4437,7 +4439,7 @@ dynamicTest("interop_memory_leaks") {
outputChecker = { s -> s.contains("Memory leaks detected, 1 objects leaked!") }
}
task library_mismatch(type: KonanDriverTest) {
tasks.register("library_mismatch", KonanDriverTest) {
// Does not work for cross targets yet.
enabled = !(project.testTarget != null && project.target.name != project.hostName)
@@ -4475,7 +4477,7 @@ task library_mismatch(type: KonanDriverTest) {
}
}
task library_ir_provider_mismatch(type: KonanDriverTest) {
tasks.register("library_ir_provider_mismatch", KonanDriverTest) {
def dir = buildDir.absolutePath
def lib = "$projectDir/link/ir_providers/library/empty.kt"
def invalidManifest = "$projectDir/link/ir_providers/library/manifest.properties"
@@ -4861,12 +4863,14 @@ if (UtilsKt.getTestTargetSupportsCodeCoverage(project)) {
}
}
task metadata_compare_typedefs(type: MetadataComparisonTest) {
tasks.register("metadata_compare_typedefs", MetadataComparisonTest) {
UtilsKt.dependsOnDist(it)
enabled = (project.testTarget != 'wasm32')
defFile = "interop/basics/typedefs.def"
}
task metadata_compare_unable_to_import(type: MetadataComparisonTest) {
tasks.register("metadata_compare_unable_to_import", MetadataComparisonTest) {
UtilsKt.dependsOnDist(it)
enabled = (project.testTarget != 'wasm32')
defFile = "interop/basics/unable_to_import.def"
}
@@ -4883,8 +4887,7 @@ standaloneTest("local_ea_arraysfieldwrite") {
source = "codegen/localEscapeAnalysis/arraysFieldWrite.kt"
}
task override_konan_properties0(type: KonanDriverTest) {
tasks.register("override_konan_properties0", KonanDriverTest) {
def overrides = PlatformInfo.isWindows()
? '-Xoverride-konan-properties="llvmInlineThreshold=76"'
: '-Xoverride-konan-properties=llvmInlineThreshold=76'
@@ -4989,7 +4992,7 @@ task buildKonanTests { t ->
project.tasks
.withType(KonanLocalTest.class)
.matching { !(it instanceof KonanStandaloneTest) }
.forEach { it.dependsOn(t) }
.configureEach { it.dependsOn(t) }
}
@@ -5007,7 +5010,7 @@ sourceSets {
}
Task pluginTest(String name, String pluginName, Closure configureClosure) {
def jarTask = project.tasks.create("jar-$pluginName", Jar).configure {
def jarTask = project.tasks.register("jar-$pluginName", Jar) {
it.dependsOn("compile${pluginName.capitalize()}Kotlin")
from {
sourceSets[pluginName].output
@@ -5062,7 +5065,7 @@ project.tasks.register("debugger_test", Test.class) {
if (target.family == Family.IOS && (target.architecture == ARM32 || target.architecture == ARM64)) {
project.tasks
.withType(KonanTestExecutable.class)
.forEach {
.configureEach {
ExecutorServiceKt.configureXcodeBuild((KonanTestExecutable) it)
}
// Exclude tasks that cannot be run on device
@@ -5074,5 +5077,5 @@ if (target.family == Family.IOS && (target.architecture == ARM32 || target.archi
it instanceof KonanDynamicTest ||
it instanceof KonanLinkTest
}
.forEach { it.enabled = false }
.configureEach { it.enabled = false }
}
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin
import groovy.lang.Closure
import org.gradle.api.Task
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
@@ -12,17 +13,17 @@ import java.io.File
// TODO: Implement as a part of the gradle plugin
open class KlibInstall: Exec() {
@InputFile
lateinit var klib: File
@Input
lateinit var klib: Provider<File>
@Input
var repo: File = project.rootDir
val installDir: File
val installDir: Provider<File>
@OutputDirectory
get() {
val klibName = klib.name.take(klib.name.lastIndexOf('.'))
return project.file("${repo.absolutePath}/$klibName")
get() = project.provider {
val klibName = klib.get().nameWithoutExtension
project.file("${repo.absolutePath}/$klibName")
}
@Input
@@ -38,7 +39,7 @@ open class KlibInstall: Exec() {
repo.mkdirs()
commandLine(klibProgram,
"install", klib.absolutePath,
"install", klib.get().absolutePath,
"-target", target,
"-repository", repo.absolutePath
)
+34 -34
View File
@@ -58,16 +58,16 @@ project.rootProject.ext.platformManager.enabled.each { target ->
def targetName = target.visibleName
ArrayList<Task> installTasks = []
ArrayList<Task> cacheTasks = []
ArrayList<TaskProvider> installTasks = []
ArrayList<TaskProvider> cacheTasks = []
if (target in cacheableTargets) {
task "${targetName}StdlibCache"(type: KonanCacheTask) {
tasks.register("${targetName}StdlibCache", KonanCacheTask) {
it.target = targetName
originalKlib = file("$konanHome/klib/common/stdlib")
cacheRoot = file("$konanHome/klib/cache")
it.originalKlib = file("$konanHome/klib/common/stdlib")
it.cacheRoot = file("$konanHome/klib/cache")
dependsOn ":${targetName}CrossDistRuntime"
it.dependsOn ":${targetName}CrossDistRuntime"
}
}
@@ -76,7 +76,7 @@ project.rootProject.ext.platformManager.enabled.each { target ->
def fileNamePrefix = PlatformLibsInfo.namePrefix
konanArtifacts {
interop (libName, targets: [targetName]) {
interop(libName, targets: [targetName]) {
defFile df.file
artifactName "${fileNamePrefix}${df.name}"
noDefaultLibs true
@@ -92,60 +92,60 @@ project.rootProject.ext.platformManager.enabled.each { target ->
}
def libTask = konanArtifacts."$libName"."$targetName"
libTask.dependsOn df.config.depends.collect{ defFileToLibName(targetName, it) }
libTask.dependsOn ":${targetName}CrossDist"
libTask.enableParallel = true
task "$libName"(type: KlibInstall) {
klib = libTask.artifact
repo = file("$konanHome/klib/platform/$targetName")
it.target = targetName
dependsOn libTask
libTask.configure {
it.dependsOn df.config.depends.collect { defFileToLibName(targetName, it) }
it.dependsOn ":${targetName}CrossDist"
it.enableParallel = true
}
installTasks.add(tasks[libName])
def klibInstallTask = tasks.register("$libName", KlibInstall) {
it.klib = project.provider { libTask.get().artifact }
it.repo = file("$konanHome/klib/platform/$targetName")
it.target = targetName
it.dependsOn libTask
}
installTasks.add(klibInstallTask)
if (target in cacheableTargets) {
task "${libName}Cache"(type: KonanCacheTask) {
def cacheTask = tasks.register("${libName}Cache", KonanCacheTask) {
it.target = targetName
originalKlib = tasks[libName].installDir
cacheRoot = file("$konanHome/klib/cache")
it.originalKlib = tasks[libName].installDir.get()
it.cacheRoot = file("$konanHome/klib/cache")
dependsOn ":${targetName}StdlibCache"
dependsOn tasks[libName]
dependsOn df.config.depends.collect {
it.dependsOn ":${targetName}StdlibCache"
it.dependsOn tasks[libName]
it.dependsOn df.config.depends.collect {
def depName = defFileToLibName(targetName, it)
"${depName}Cache"
}
cacheTasks.add(it)
}
cacheTasks.add(cacheTask)
}
}
task "${targetName}Install" {
dependsOn installTasks
tasks.register("${targetName}Install") {
it.dependsOn installTasks
}
if (target in cacheableTargets) {
task "${targetName}Cache" {
dependsOn cacheTasks
tasks.register("${targetName}Cache") {
it.dependsOn cacheTasks
group = BasePlugin.BUILD_GROUP
description = "Builds the compilation cache for platform: ${targetName}"
it.group = BasePlugin.BUILD_GROUP
it.description = "Builds the compilation cache for platform: ${targetName}"
}
}
}
// TODO: Don't install libraries here - copy them in the distPlatformLibs task
task hostInstall {
dependsOn tasks.withType(KlibInstall.class).findAll {
dependsOn tasks.withType(KlibInstall.class).matching {
it.target == HostManager.hostName
}
}
task hostCache {
dependsOn tasks.withType(KonanCacheTask.class).findAll {
dependsOn tasks.withType(KonanCacheTask.class).matching {
it.target == HostManager.hostName
}
}
@@ -17,30 +17,36 @@
package org.jetbrains.kotlin.gradle.plugin.konan
import groovy.lang.Closure
import org.gradle.api.*
import org.gradle.api.Action
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Named
import org.gradle.api.Task
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.publish.maven.MavenPom
import org.gradle.api.tasks.TaskProvider
import org.gradle.util.ConfigureUtil
import org.gradle.util.WrapUtil
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanBuildingTask
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
/** Base class for all Kotlin/Native artifacts. */
abstract class KonanBuildingConfig<T: KonanBuildingTask>(private val name_: String,
val type: Class<T>,
val project: ProjectInternal,
val targets: Iterable<String>)
: KonanBuildingSpec, Named, DomainObjectSet<T> by WrapUtil.toDomainObjectSet(type) {
abstract class KonanBuildingConfig<T : KonanBuildingTask>(
private val name_: String,
val type: Class<T>,
val project: ProjectInternal,
val targets: Iterable<String>
) : KonanBuildingSpec, Named {
internal val mainVariant = KonanSoftwareComponent(project)
override fun getName() = name_
protected val targetToTask = mutableMapOf<KonanTarget, T>()
protected val targetToTask = mutableMapOf<KonanTarget, TaskProvider<T>>()
internal val aggregateBuildTask: Task
fun tasks() = targetToTask.values
private val aggregateBuildTask: TaskProvider<Task>
internal var pomActions = mutableListOf<Action<MavenPom>>()
@@ -56,12 +62,11 @@ abstract class KonanBuildingConfig<T: KonanBuildingTask>(private val name_: Stri
continue
}
if (!targetIsSupported(konanTarget)) {
project.logger.info("The target ${targetName} is not supported by the artifact $name")
project.logger.info("The target $targetName is not supported by the artifact $name")
continue
}
if (this[konanTarget] == null) {
val task = createTask(konanTarget)
add(task)
targetToTask[konanTarget] = task
// Allow accessing targets just by their names in Groovy DSL.
(this as? ExtensionAware)?.extensions?.add(konanTarget.visibleName, task)
@@ -75,13 +80,13 @@ abstract class KonanBuildingConfig<T: KonanBuildingTask>(private val name_: Stri
}
protected open fun generateTaskName(target: KonanTarget) =
"compileKonan${name.capitalize()}${target.visibleName.capitalize()}"
"compileKonan${name.capitalize()}${target.visibleName.capitalize()}"
protected open fun generateAggregateTaskName() =
"compileKonan${name.capitalize()}"
"compileKonan${name.capitalize()}"
protected open fun generateTargetAliasTaskName(targetName: String) =
"compileKonan${name.capitalize()}${targetName.capitalize()}"
"compileKonan${name.capitalize()}${targetName.capitalize()}"
protected abstract fun generateTaskDescription(task: T): String
protected abstract fun generateAggregateTaskDescription(task: Task): String
@@ -109,31 +114,33 @@ abstract class KonanBuildingConfig<T: KonanBuildingTask>(private val name_: Stri
}
}
protected fun createTask(target: KonanTarget): T =
project.tasks.create(generateTaskName(target), type) {
val outputDescription = determineOutputPlacement(target)
it.init(this, outputDescription.destinationDir, outputDescription.artifactName, target)
it.group = BasePlugin.BUILD_GROUP
it.description = generateTaskDescription(it)
} ?: throw Exception("Cannot create task for target: ${target.visibleName}")
private fun createTask(target: KonanTarget): TaskProvider<T> =
project.tasks.register(generateTaskName(target), type) {
val outputDescription = determineOutputPlacement(target)
it.init(this, outputDescription.destinationDir, outputDescription.artifactName, target)
it.group = BasePlugin.BUILD_GROUP
it.description = generateTaskDescription(it)
} ?: throw Exception("Cannot create task for target: ${target.visibleName}")
protected fun createAggregateTask(): Task =
project.tasks.create(generateAggregateTaskName()) { task ->
task.group = BasePlugin.BUILD_GROUP
task.description = generateAggregateTaskDescription(task)
this.filter {
project.targetIsRequested(it.konanTarget)
}.forEach {
task.dependsOn(it)
}
project.compileAllTask.dependsOn(task)
private fun createAggregateTask(): TaskProvider<Task> =
project.tasks.register(generateAggregateTaskName()) { task ->
task.group = BasePlugin.BUILD_GROUP
task.description = generateAggregateTaskDescription(task)
targetToTask.filter {
project.targetIsRequested(it.key)
}.forEach {
task.dependsOn(it.value)
}
}.also {
project.compileAllTask.dependsOn(it)
}
protected fun createTargetAliasTaskIfDeclared(targetName: String): Task? {
protected fun createTargetAliasTaskIfDeclared(targetName: String): TaskProvider<Task>? {
val canonicalTarget = project.hostManager.targetByName(targetName)
return this[canonicalTarget]?.let { canonicalBuild ->
project.tasks.create(generateTargetAliasTaskName(targetName)) {
project.tasks.register(generateTargetAliasTaskName(targetName)) {
it.group = BasePlugin.BUILD_GROUP
it.description = generateTargetAliasTaskDescription(it, targetName)
it.dependsOn(canonicalBuild)
@@ -143,31 +150,53 @@ abstract class KonanBuildingConfig<T: KonanBuildingTask>(private val name_: Stri
internal operator fun get(target: KonanTarget) = targetToTask[target]
fun getByTarget(target: String) = findByTarget(target) ?: throw NoSuchElementException("No such target for artifact $name: ${target}")
fun getByTarget(target: String) =
findByTarget(target) ?: throw NoSuchElementException("No such target for artifact $name: $target")
fun findByTarget(target: String) = this[project.hostManager.targetByName(target)]
fun getArtifactByTarget(target: String) = getByTarget(target).artifact
fun findArtifactByTarget(target: String) = findByTarget(target)?.artifact
fun getArtifactByTarget(target: String) = getByTarget(target).get().artifact
fun findArtifactByTarget(target: String) = findByTarget(target)?.get()?.artifact
// Common building DSL.
override fun artifactName(name: String) = forEach { it.artifactName(name) }
override fun artifactName(name: String) = tasks().forEach { it.configure { t -> t.artifactName(name) } }
fun baseDir(dir: Any) = forEach { it.destinationDir(project.file(dir).targetSubdir(it.konanTarget)) }
fun baseDir(dir: Any) =
tasks().forEach {
it.configure { t ->
t.destinationDir(
project.file(dir).targetSubdir(t.konanTarget)
)
}
}
override fun libraries(closure: Closure<Unit>) = forEach { it.libraries(closure) }
override fun libraries(action: Action<KonanLibrariesSpec>) = forEach { it.libraries(action) }
override fun libraries(configure: KonanLibrariesSpec.() -> Unit) = forEach { it.libraries(configure) }
override fun libraries(closure: Closure<Unit>) =
tasks().forEach { it.configure { t -> t.libraries(closure) } }
override fun noDefaultLibs(flag: Boolean) = forEach { it.noDefaultLibs(flag) }
override fun noEndorsedLibs(flag: Boolean) = forEach { it.noEndorsedLibs(flag) }
override fun libraries(action: Action<KonanLibrariesSpec>) =
tasks().forEach { it.configure { t -> t.libraries(action) } }
override fun dumpParameters(flag: Boolean) = forEach { it.dumpParameters(flag) }
override fun libraries(configure: KonanLibrariesSpec.() -> Unit) =
tasks().forEach { it.configure { t -> t.libraries(configure) } }
override fun extraOpts(vararg values: Any) = forEach { it.extraOpts(*values) }
override fun extraOpts(values: List<Any>) = forEach { it.extraOpts(values) }
override fun noDefaultLibs(flag: Boolean) =
tasks().forEach { it.configure { t -> t.noDefaultLibs(flag) } }
fun dependsOn(vararg dependencies: Any?) = forEach { it.dependsOn(*dependencies) }
override fun noEndorsedLibs(flag: Boolean) =
tasks().forEach { it.configure { t -> t.noEndorsedLibs(flag) } }
override fun dumpParameters(flag: Boolean) =
tasks().forEach { it.configure { t -> t.dumpParameters(flag) } }
override fun extraOpts(vararg values: Any) =
tasks().forEach { it.configure { t -> t.extraOpts(*values) } }
override fun extraOpts(values: List<Any>) =
tasks().forEach { it.configure { t -> t.extraOpts(values) } }
fun dependsOn(vararg dependencies: Any?) =
tasks().forEach { it.configure { t -> t.dependsOn(*dependencies) } }
fun target(targetString: String, configureAction: T.() -> Unit) {
val target = project.hostManager.targetByName(targetString)
@@ -177,14 +206,16 @@ abstract class KonanBuildingConfig<T: KonanBuildingTask>(private val name_: Stri
return
}
val task = this[target] ?:
throw InvalidUserDataException("Target '$targetString' is not declared. Please add it into project.konanTasks list")
task.configureAction()
val task = this[target]
?: throw InvalidUserDataException("Target '$targetString' is not declared. Please add it into project.konanTasks list")
task.configure(configureAction)
}
fun target(targetString: String, configureAction: Action<T>) =
target(targetString) { configureAction.execute(this) }
target(targetString) { configureAction.execute(this) }
fun target(targetString: String, configureAction: Closure<Unit>) =
target(targetString, ConfigureUtil.configureUsing(configureAction))
target(targetString, ConfigureUtil.configureUsing(configureAction))
fun pom(action: Action<MavenPom>) = pomActions + action
}
@@ -43,33 +43,33 @@ abstract class KonanCompileConfig<T: KonanCompileTask>(name: String,
override fun generateTargetAliasTaskDescription(task: Task, targetName: String) =
"Build the Kotlin/Native $typeForDescription '${task.name}' for target '$targetName'"
override fun srcDir(dir: Any) = forEach { it.srcDir(dir) }
override fun srcFiles(vararg files: Any) = forEach { it.srcFiles(*files) }
override fun srcFiles(files: Collection<Any>) = forEach { it.srcFiles(files) }
override fun srcDir(dir: Any) = tasks().forEach { it.configure { t -> t.srcDir(dir) } }
override fun srcFiles(vararg files: Any) = tasks().forEach { it.configure { t -> t.srcFiles(*files) } }
override fun srcFiles(files: Collection<Any>) = tasks().forEach { it.configure { t -> t.srcFiles(files) } }
override fun nativeLibrary(lib: Any) = forEach { it.nativeLibrary(lib) }
override fun nativeLibraries(vararg libs: Any) = forEach { it.nativeLibraries(*libs) }
override fun nativeLibraries(libs: FileCollection) = forEach { it.nativeLibraries(libs) }
override fun nativeLibrary(lib: Any) = tasks().forEach { it.configure { t -> t.nativeLibrary(lib) } }
override fun nativeLibraries(vararg libs: Any) = tasks().forEach { it.configure { t -> t.nativeLibraries(*libs) } }
override fun nativeLibraries(libs: FileCollection) = tasks().forEach { it.configure { t -> t.nativeLibraries(libs) } }
@Deprecated("Use commonSourceSets instead", ReplaceWith("commonSourceSets(sourceSetName)"))
override fun commonSourceSet(sourceSetName: String) = forEach { it.commonSourceSets(sourceSetName) }
override fun commonSourceSets(vararg sourceSetNames: String) = forEach { it.commonSourceSets(*sourceSetNames) }
override fun enableMultiplatform(flag: Boolean) = forEach { it.enableMultiplatform(flag) }
override fun commonSourceSet(sourceSetName: String) = tasks().forEach { it.configure { t -> t.commonSourceSets(sourceSetName) } }
override fun commonSourceSets(vararg sourceSetNames: String) = tasks().forEach { it.configure { t -> t.commonSourceSets(*sourceSetNames) } }
override fun enableMultiplatform(flag: Boolean) = tasks().forEach { it.configure { t -> t.enableMultiplatform(flag) } }
override fun linkerOpts(values: List<String>) = forEach { it.linkerOpts(values) }
override fun linkerOpts(vararg values: String) = forEach { it.linkerOpts(*values) }
override fun linkerOpts(values: List<String>) = tasks().forEach { it.configure { t -> t.linkerOpts(values) } }
override fun linkerOpts(vararg values: String) = tasks().forEach { it.configure { t -> t.linkerOpts(*values) } }
override fun enableDebug(flag: Boolean) = forEach { it.enableDebug(flag) }
override fun noStdLib(flag: Boolean) = forEach { it.noStdLib(flag) }
override fun noMain(flag: Boolean) = forEach { it.noMain(flag) }
override fun enableOptimizations(flag: Boolean) = forEach { it.enableOptimizations(flag) }
override fun enableAssertions(flag: Boolean) = forEach { it.enableAssertions(flag) }
override fun enableDebug(flag: Boolean) = tasks().forEach { it.configure { t -> t.enableDebug(flag) } }
override fun noStdLib(flag: Boolean) = tasks().forEach { it.configure { t -> t.noStdLib(flag) } }
override fun noMain(flag: Boolean) = tasks().forEach { it.configure { t -> t.noMain(flag) } }
override fun enableOptimizations(flag: Boolean) = tasks().forEach { it.configure { t -> t.enableOptimizations(flag) } }
override fun enableAssertions(flag: Boolean) = tasks().forEach { it.configure { t -> t.enableAssertions(flag) } }
override fun entryPoint(entryPoint: String) = forEach { it.entryPoint(entryPoint) }
override fun entryPoint(entryPoint: String) = tasks().forEach { it.configure { t -> t.entryPoint(entryPoint) } }
override fun measureTime(flag: Boolean) = forEach { it.measureTime(flag) }
override fun measureTime(flag: Boolean) = tasks().forEach { it.configure { t -> t.measureTime(flag) } }
override fun dependencies(closure: Closure<Unit>) = forEach { it.dependencies(closure) }
override fun dependencies(closure: Closure<Unit>) = tasks().forEach { it.configure { t -> t.dependencies(closure) } }
}
open class KonanProgram(name: String,
@@ -49,38 +49,37 @@ open class KonanInteropLibrary(name: String,
inner class IncludeDirectoriesSpecImpl: IncludeDirectoriesSpec {
override fun allHeaders(vararg includeDirs: Any) = allHeaders(includeDirs.toList())
override fun allHeaders(includeDirs: Collection<Any>) = forEach {
it.includeDirs.allHeaders(includeDirs)
override fun allHeaders(includeDirs: Collection<Any>) = tasks().forEach {
it.configure { t -> t.includeDirs.allHeaders(includeDirs) }
}
override fun headerFilterOnly(vararg includeDirs: Any) = headerFilterOnly(includeDirs.toList())
override fun headerFilterOnly(includeDirs: Collection<Any>) = forEach {
it.includeDirs.headerFilterOnly(includeDirs)
override fun headerFilterOnly(includeDirs: Collection<Any>) = tasks().forEach {
it.configure { t -> t.includeDirs.headerFilterOnly(includeDirs) }
}
}
val includeDirs = IncludeDirectoriesSpecImpl()
override fun defFile(file: Any) = forEach { it.defFile(file) }
override fun defFile(file: Any) = tasks().forEach { it.configure { t -> t.defFile(file) } }
override fun packageName(value: String) = forEach { it.packageName(value) }
override fun packageName(value: String) = tasks().forEach { it.configure { t -> t.packageName(value) } }
override fun compilerOpts(vararg values: String) = forEach { it.compilerOpts(*values) }
override fun compilerOpts(vararg values: String) = tasks().forEach { it.configure { t -> t.compilerOpts(*values) } }
override fun headers(vararg files: Any) = forEach { it.headers(*files) }
override fun headers(vararg files: Any) = tasks().forEach { it.configure { t -> t.headers(*files) } }
override fun headers(files: FileCollection) = forEach { it.headers(files) }
override fun headers(files: FileCollection) = tasks().forEach { it.configure { t -> t.headers(files) } }
override fun includeDirs(vararg values: Any) = forEach { it.includeDirs(*values) }
override fun includeDirs(vararg values: Any) = tasks().forEach { it.configure { t -> t.includeDirs(*values) } }
override fun includeDirs(closure: Closure<Unit>) = includeDirs(ConfigureUtil.configureUsing(closure))
override fun includeDirs(action: Action<IncludeDirectoriesSpec>) = includeDirs { action.execute(this) }
override fun includeDirs(configure: IncludeDirectoriesSpec.() -> Unit) = includeDirs.configure()
override fun linkerOpts(values: List<String>) = forEach { it.linkerOpts(values) }
override fun linkerOpts(values: List<String>) = tasks().forEach { it.configure { t -> t.linkerOpts(values) } }
override fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
override fun link(vararg files: Any) = forEach { it.link(*files) }
override fun link(files: FileCollection) = forEach { it.link(files) }
override fun dependencies(closure: Closure<Unit>) = forEach { it.dependencies(closure) }
override fun link(vararg files: Any) = tasks().forEach { it.configure { t -> t.link(*files) } }
override fun link(files: FileCollection) = tasks().forEach { it.configure { t -> t.link(files) } }
override fun dependencies(closure: Closure<Unit>) = tasks().forEach { it.configure { t -> t.dependencies(closure) }}
}
@@ -86,7 +86,7 @@ open class KonanLibrariesSpec(
throw InvalidUserDataException("Config ${lib.name} is not a library")
}
val libraryTask = lib[target] ?:
val libraryTask = lib[target]?.get() ?:
throw InvalidUserDataException("Library ${lib.name} has no target ${target.visibleName}")
if (libraryTask == task) {
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.gradle.plugin.konan
import groovy.lang.Closure
import org.codehaus.groovy.runtime.GStringImpl
import org.gradle.api.*
import org.gradle.api.component.ComponentWithVariants
import org.gradle.api.component.SoftwareComponent
@@ -28,11 +30,11 @@ import org.gradle.api.plugins.BasePlugin
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal
import org.gradle.api.tasks.Exec
import org.gradle.language.cpp.internal.NativeVariantIdentity
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin.Companion.COMPILE_ALL_TASK_NAME
import org.jetbrains.kotlin.gradle.plugin.model.KonanToolingModelBuilder
import org.jetbrains.kotlin.gradle.plugin.tasks.*
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.konan.CompilerVersion
@@ -330,11 +332,9 @@ class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderR
return
}
checkGradleVersion()
registry.register(KonanToolingModelBuilder)
project.plugins.apply("base")
// Create necessary tasks and extensions.
project.tasks.create(KONAN_DOWNLOAD_TASK_NAME, KonanCompilerDownloadTask::class.java)
project.tasks.create(KONAN_GENERATE_CMAKE_TASK_NAME, KonanGenerateCMakeTask::class.java)
project.extensions.create(KONAN_EXTENSION_NAME, KonanExtension::class.java)
val container = project.extensions.create(
KonanArtifactContainer::class.java,
@@ -363,15 +363,36 @@ class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderR
doLast { project.cleanKonan() }
}
project.afterEvaluate {
project.tasks
.withType(KonanCompileProgramTask::class.java)
.forEach { task ->
val isCrossCompile = (task.target != HostManager.host.visibleName)
if (!isCrossCompile && !project.hasProperty("konanNoRun"))
task.runTask = project.tasks.register("run${task.artifactName.capitalize()}", Exec::class.java) {
it.group= "run"
it.dependsOn(task)
val artifactPathClosure = object : Closure<String>(this) {
override fun call() = task.artifactPath
}
// Use GString to evaluate a path to the artifact lazily thus allow changing it at configuration phase.
val lazyArtifactPath = GStringImpl(arrayOf(artifactPathClosure), arrayOf(""))
it.executable(lazyArtifactPath)
// Add values passed in the runArgs project property as arguments.
it.argumentProviders.add(task.RunArgumentProvider())
}
}
}
val runTask = project.getOrCreateTask("run")
project.afterEvaluate {
project.konanArtifactsContainer
.filterIsInstance(KonanProgram::class.java)
.forEach { program ->
program.forEach { compile ->
compile.runTask?.let { runTask.dependsOn(it) }
}
.filterIsInstance(KonanProgram::class.java)
.forEach { program ->
program.tasks().forEach { compile ->
compile.configure { it.runTask?.let { runTask.dependsOn(it) } }
}
}
}
// Enable multiplatform support
@@ -21,7 +21,6 @@ import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
@@ -30,7 +29,6 @@ abstract class KonanBuildingTask: KonanArtifactWithLibrariesTask(), KonanBuildin
@get:Internal
internal abstract val toolRunner: KonanToolRunner
internal abstract fun toModelArtifact(): KonanModelArtifact
override fun init(config: KonanBuildingConfig<*>, destinationDir: File, artifactName: String, target: KonanTarget) {
dependsOn(project.konanCompilerDownloadTask)
@@ -18,13 +18,12 @@ package org.jetbrains.kotlin.gradle.plugin.tasks
import groovy.lang.Closure
import org.codehaus.groovy.runtime.GStringImpl
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.*
import org.gradle.process.CommandLineArgumentProvider
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifactImpl
import org.jetbrains.kotlin.konan.library.defaultResolver
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.Distribution
@@ -323,29 +322,6 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
}
// endregion
// region IDE model
override fun toModelArtifact(): KonanModelArtifact {
val repos = libraries.repos
val resolver = defaultResolver(
repos.map { it.absolutePath },
konanTarget,
Distribution(project.konanHome)
)
return KonanModelArtifactImpl(
artifactName,
artifact,
produce,
konanTarget.name,
name,
allSources.filterIsInstance(ConfigurableFileTree::class.java).map { it.dir },
allSourceFiles,
libraries.asFiles(resolver),
repos.toList()
)
}
// endregion
override fun run() {
destinationDir.mkdirs()
if (dumpParameters) {
@@ -379,33 +355,13 @@ open class KonanCompileProgramTask: KonanCompileNativeBinary() {
override val produce: CompilerOutputKind get() = CompilerOutputKind.PROGRAM
@Internal
var runTask: Exec? = null
var runTask: TaskProvider<Exec>? = null
inner class RunArgumentProvider(): CommandLineArgumentProvider {
inner class RunArgumentProvider: CommandLineArgumentProvider {
override fun asArguments() = project.findProperty("runArgs")?.let {
it.toString().split(' ')
} ?: emptyList()
}
// Create tasks to run supported executables.
override fun init(config: KonanBuildingConfig<*>, destinationDir: File, artifactName: String, target: KonanTarget) {
super.init(config, destinationDir, artifactName, target)
if (!isCrossCompile && !project.hasProperty("konanNoRun")) {
runTask = project.tasks.create("run${artifactName.capitalize()}", Exec::class.java).apply {
group = "run"
dependsOn(this@KonanCompileProgramTask)
val artifactPathClosure = object : Closure<String>(this) {
override fun call() = artifactPath
}
// Use GString to evaluate a path to the artifact lazily thus allow changing it at configuration phase.
val lazyArtifactPath = GStringImpl(arrayOf(artifactPathClosure), arrayOf(""))
executable(lazyArtifactPath)
// Add values passed in the runArgs project property as arguments.
argumentProviders.add(RunArgumentProvider())
}
}
}
}
open class KonanCompileDynamicTask: KonanCompileNativeBinary() {
@@ -1,147 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.plugin.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.plugin.konan.KonanInteropLibrary
import org.jetbrains.kotlin.gradle.plugin.konan.KonanLibrary
import org.jetbrains.kotlin.gradle.plugin.konan.KonanProgram
import org.jetbrains.kotlin.gradle.plugin.konan.konanArtifactsContainer
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.HostManager
import java.io.File
open class KonanGenerateCMakeTask : DefaultTask() {
@Suppress("unused")
@TaskAction
fun generateCMake() {
val interops = project.konanArtifactsContainer.toList().filterIsInstance<KonanInteropLibrary>()
val libraries = project.konanArtifactsContainer.toList().filterIsInstance<KonanLibrary>()
val programs = project.konanArtifactsContainer.toList().filterIsInstance<KonanProgram>()
val cMakeLists = generateCMakeLists(
project.name,
interops,
libraries,
programs
)
File(project.projectDir, "CMakeLists.txt")
.writeText(cMakeLists)
// This directory is filled out by the IDE
File(project.projectDir, "KotlinCMakeModule")
.mkdir()
}
private val host = HostManager.host
private fun generateCMakeLists(
projectName: String,
interops: List<KonanInteropLibrary>,
libraries: List<KonanLibrary>,
programs: List<KonanProgram>
): String {
val cMakeCurrentListDir = "$" + "{CMAKE_CURRENT_LIST_DIR}"
return buildString {
appendln("""
cmake_minimum_required(VERSION 3.8)
set(CMAKE_MODULE_PATH $cMakeCurrentListDir/KotlinCMakeModule)
project($projectName Kotlin)
""".trimIndent())
appendln()
for (interop in interops) {
val task = interop[host] ?: continue
appendln(
Call("cinterop")
.arg("NAME", interop.name)
.arg("DEF_FILE", task.defFile.relativePath.toString().crossPlatformPath)
.arg("COMPILER_OPTS", task.cMakeCompilerOpts)
)
}
for (library in libraries) {
val task = library[host] ?: continue
appendln(
Call("konanc_library")
.arg("NAME", library.name)
.arg("SOURCES", task.cMakeSources)
.arg("LIBRARIES", task.cMakeLibraries)
.arg("LINKER_OPTS", task.cMakeLinkerOpts))
}
for (program in programs) {
val task = program[host] ?: continue
appendln(
Call("konanc_executable")
.arg("NAME", program.name)
.arg("SOURCES", task.cMakeSources)
.arg("LIBRARIES", task.cMakeLibraries)
.arg("LINKER_OPTS", task.cMakeLinkerOpts))
}
}
}
private val File.relativePath get() = relativeTo(project.projectDir)
private val String.crossPlatformPath get() =
if (host.family == Family.MINGW) replace('\\', '/') else this
private val FileCollection.asCMakeSourceList: List<String>
get() = files.map { it.relativePath.toString().crossPlatformPath }
private val KonanInteropTask.cMakeCompilerOpts: String
get() = (compilerOpts + includeDirs.allHeadersDirs.map { "-I${it.absolutePath.crossPlatformPath}" })
.joinToString(" ")
private val KonanCompileTask.cMakeSources: String
get() = allSources.flatMap { it.asCMakeSourceList }.joinToString(" ")
private val KonanCompileTask.cMakeLibraries: String
get() = mutableListOf<String>().apply {
addAll(libraries.artifacts.map { it.artifactName })
addAll(libraries.namedKlibs)
addAll(libraries.files.flatMap { it.files }.map { it.canonicalPath.crossPlatformPath })
}.joinToString(" ")
private val KonanCompileTask.cMakeLinkerOpts: String
get() = linkerOpts.joinToString(" ").replace('\\', '/')
}
private class Call(val name: String) {
private val args: MutableList<Pair<String, String>> = mutableListOf()
fun arg(key: String, value: String?): Call {
if (value != null && value.isNotBlank()) args += key to value
return this
}
override fun toString(): String =
buildString {
append(name)
append("(")
for ((key, value) in args) {
appendln()
append(" $key $value")
}
appendln(")")
}
}
@@ -27,8 +27,6 @@ import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.plugin.konan.KonanInteropSpec.IncludeDirectoriesSpec
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifactImpl
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.library.defaultResolver
@@ -170,29 +168,6 @@ open class KonanInteropTask @Inject constructor(@Internal val workerExecutor: Wo
// endregion
// region IDE model
override fun toModelArtifact(): KonanModelArtifact {
val repos = libraries.repos
val resolver = defaultResolver(
repos.map { it.absolutePath },
konanTarget,
Distribution(project.konanHome)
)
return KonanModelArtifactImpl(
artifactName,
artifact,
CompilerOutputKind.LIBRARY,
konanTarget.name,
name,
listOfNotNull(defFile.parentFile),
listOf(defFile),
libraries.asFiles(resolver),
repos.toList()
)
}
// endregion
internal interface RunToolParameters: WorkParameters {
var taskName: String
var args: List<String>
@@ -1,50 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.plugin.model
import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import java.io.File
import java.io.Serializable
/**
* An immutable representation of Konan's project model for gradle tooling API.
* This model is shared with the client processes such as an IDE.
*/
interface KonanModel : Serializable {
val konanHome: File
val konanVersion: CompilerVersion
val languageVersion: String?
val apiVersion: String?
val artifacts: List<KonanModelArtifact>
}
/**
* A representation of a single binary produced by Kotlin/Native for a cetrain target.
*/
interface KonanModelArtifact : Serializable {
val name: String
val type: CompilerOutputKind
val targetPlatform: String
val file: File
val buildTaskName: String
val srcDirs: List<File>
val srcFiles: List<File>
val libraries: List<File>
val searchPaths: List<File>
}
@@ -1,79 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.plugin.model
import org.gradle.api.Project
import org.gradle.tooling.provider.model.ToolingModelBuilder
import org.jetbrains.kotlin.gradle.plugin.konan.konanArtifactsContainer
import org.jetbrains.kotlin.gradle.plugin.konan.konanExtension
import org.jetbrains.kotlin.gradle.plugin.konan.konanHome
import org.jetbrains.kotlin.gradle.plugin.konan.konanVersion
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import java.io.File
object KonanToolingModelBuilder : ToolingModelBuilder {
override fun canBuild(modelName: String) = KonanModel::class.java.name == modelName
private fun buildModelKonan(project: Project): KonanModel {
val artifacts = project.konanArtifactsContainer.flatten().toList().map { it.toModelArtifact() }
return KonanModelImpl(
artifacts,
project.file(project.konanHome),
project.konanVersion,
// TODO: Provide defaults for these versions.
project.konanExtension.languageVersion,
project.konanExtension.apiVersion
)
}
private val Project.hasKonanPlugin: Boolean
get() = with(pluginManager) {
hasPlugin("konan") ||
hasPlugin("org.jetbrains.kotlin.konan")
}
override fun buildAll(modelName: String, project: Project): KonanModel =
when {
project.hasKonanPlugin -> buildModelKonan(project)
else -> throw IllegalStateException("The project '${project.path}' has no Kotlin/Native plugin")
}
}
internal data class KonanModelImpl(
override val artifacts: List<KonanModelArtifact>,
override val konanHome: File,
override val konanVersion: CompilerVersion,
override val languageVersion: String?,
override val apiVersion: String?
) : KonanModel
internal data class KonanModelArtifactImpl(
override val name: String,
override val file: File,
override val type: CompilerOutputKind,
override val targetPlatform: String,
override val buildTaskName: String,
override val srcDirs: List<File>,
override val srcFiles: List<File>,
override val libraries: List<File>,
override val searchPaths: List<File>
) : KonanModelArtifact
@@ -1,267 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.plugin.test
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.testfixtures.ProjectBuilder
import org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin
import org.jetbrains.kotlin.gradle.plugin.konan.konanArtifactsContainer
import org.jetbrains.kotlin.gradle.plugin.model.KonanToolingModelBuilder
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.konan.CompilerVersion
import org.junit.Ignore
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import java.io.File
import java.nio.file.Paths
import kotlin.test.Test
import kotlin.test.assertEquals
open class ToolingModelTests {
val tmpFolder = TemporaryFolder()
@Rule get
val projectDirectory: File
get() = tmpFolder.root
fun String.escapeBackSlashes() = KonanProject.escapeBackSlashes(this)
@Ignore
@Test
fun `The model should be serialized without exceptions`() {
val project = KonanProject.createEmpty(projectDirectory).apply {
buildFile.appendText("""
konanArtifacts {
library('foo') {
srcDir 'src/foo/kotlin'
}
interop('bar')
program('main') {
libraries {
artifact konanArtifacts.foo
artifact konanArtifacts.bar
klib 'posix'
}
}
}
import org.jetbrains.kotlin.gradle.plugin.model.*
task testSerialization {
doLast {
def model = KonanToolingModelBuilder.INSTANCE.buildAll("KonanModel", project)
file("model.bin").withObjectOutputStream {
it.writeObject(model)
}
KonanModelImpl deserializedModel
file("model.bin").withObjectInputStream(model.getClass().getClassLoader()) { stream ->
deserializedModel = (KonanModelImpl) stream.readObject()
}
if (!deserializedModel.equals(model)) {
throw new AssertionError("The deserialized model doesn't equal to the initial one")
}
}
}
""".trimIndent())
generateSrcFile("main.kt")
generateSrcFile(Paths.get("src", "foo", "kotlin"), "foo.kt", "fun foo() = 1")
generateSrcFile(Paths.get("src", "bar", "kotlin"), "bar.kt", "fun bar() = 1")
generateDefFile("baz.def", "")
generateDefFile("qux.def", "")
}
project.createRunner().withArguments("testSerialization").build()
}
@Ignore
@Test
fun `The model should contain the same data as the Gradle tasks`() {
val project = KonanProject.createEmpty(projectDirectory, listOf("host", "wasm32")).apply {
val konanVersion = CompilerVersion.CURRENT.toString()
generateSrcFile(listOf("src", "foo1"), "foo1.kt", "fun foo1() = 0")
generateSrcFile(listOf("src", "foo1"), "foo11.kt", "fun foo11() = 0")
generateSrcFile(listOf("src", "foo2"), "foo2.kt", "fun foo2() = 0")
generateSrcFile(listOf("defs_bar"), "bar.def", "")
generateSrcFile(listOf("src", "baz"), "baz.kt", "fun baz() = 0")
generateSrcFile("main.kt")
createSubDir("custom_repo")
buildFile.appendText("""
konanArtifacts {
library('foo') {
srcDir 'src/foo1'
srcDir 'src/foo2'
}
interop('bar') {
defFile 'defs_bar/bar.def'
}
library('baz') {
srcDir 'src/baz'
}
program('main') {
libraries {
artifact konanArtifacts.foo
artifact konanArtifacts.bar
klib 'baz'
useRepo 'custom_repo'
}
}
}
konan {
languageVersion = "1.2"
apiVersion = "1.2"
}
import org.jetbrains.kotlin.gradle.plugin.model.*
import shadow.org.jetbrains.kotlin.konan.target.CompilerOutputKind
public <T> void assertEquals(
T actual,
T expected,
String message = "${'$'}expected expected but ${'$'}actual found") {
if (expected != actual) {
throw new AssertionError(message)
}
}
public <T> void assertContentEquals(
Collection<T> actual,
Collection<T> expected,
String message = "${'$'}expected\nexpected but\n${'$'}actual\nfound") {
if (actual.size() != expected.size() || !actual.containsAll(expected)) {
throw new AssertionError(message)
}
}
task testModelData {
dependsOn('compileKonanBaz')
doLast {
def model = KonanToolingModelBuilder.INSTANCE.buildAll("KonanModel", project)
assertEquals(model.konanHome, file(project.getProperty('org.jetbrains.kotlin.native.home')))
assertEquals(model.konanVersion.toString(), "$konanVersion")
assertEquals(model.languageVersion, "1.2")
assertEquals(model.apiVersion, "1.2")
model.artifacts.each {
def konanArtifact = konanArtifacts[it.name]
def target = it.targetPlatform
def task = konanArtifact.getByTarget(target)
assertEquals(it.file, task.artifact)
assertEquals(it.buildTaskName, task.name)
switch(it.name) {
case 'foo':
assertEquals(it.type, CompilerOutputKind.valueOf('LIBRARY'))
assertContentEquals(it.srcDirs, [file('src/foo1'), file('src/foo2')])
assertContentEquals(it.srcFiles, [
file('src/foo1/foo1.kt'),
file('src/foo1/foo11.kt'),
file('src/foo2/foo2.kt')])
assertContentEquals(it.libraries, [])
assertContentEquals(it.searchPaths, [task.artifact.parentFile])
break
case 'bar':
assertEquals(it.type, CompilerOutputKind.valueOf('LIBRARY'))
assertContentEquals(it.srcDirs, [file('defs_bar')])
assertContentEquals(it.srcFiles, [file('defs_bar/bar.def')])
assertContentEquals(it.libraries, [])
assertContentEquals(it.searchPaths, [task.artifact.parentFile])
break
case 'main':
assertEquals(it.type, CompilerOutputKind.valueOf('PROGRAM'))
assertContentEquals(it.srcDirs, [file('src/main/kotlin')])
assertContentEquals(it.srcFiles, [file('src/main/kotlin/main.kt')])
assertContentEquals(it.libraries, [
konanArtifacts['foo'].getByTarget(target).artifact,
konanArtifacts['bar'].getByTarget(target).artifact,
konanArtifacts['baz'].getByTarget(target).artifact
])
assertContentEquals(it.searchPaths, [
file('custom_repo'),
task.artifact.parentFile,
konanArtifacts.foo.getByTarget(target).artifact.parentFile
])
break
}
}
}
}
""".trimIndent())
}
project.createRunner().withArguments("testModelData").build()
}
@Test
fun `The model should support maven libraries`() {
val repoDir = projectDirectory.resolve("repo").absolutePath.escapeBackSlashes()
val dependency = KonanProject.createEmpty(projectDirectory).apply {
buildFile.appendText("""
group 'test'
version '1.0'
konanArtifacts {
library('foo')
}
apply plugin: 'maven-publish'
publishing {
repositories {
maven {
url = '$repoDir'
}
}
}
""".trimIndent())
generateSrcFile("main.kt")
}
dependency.createRunner().withArguments("build", "publish").build()
val dependentDir = projectDirectory.resolve("dependent").apply {
mkdirs()
}
val dependent = ProjectBuilder.builder().withProjectDir(dependentDir).build() as ProjectInternal
with(dependent) {
pluginManager.apply(KonanPlugin::class.java)
konanArtifactsContainer.library("bar")
repositories.maven {
it.setUrl(repoDir)
}
dependencies.apply {
add("artifactbar", "test:foo:1.0")
}
}
val model = KonanToolingModelBuilder.buildAll("konanModel", dependent)
assertEquals(1, model.artifacts.size, "Incorrect number of artifacts.")
val libraries = model.artifacts[0].libraries
assertEquals(1, libraries.size, "Incorrect number of libraries.")
val library = libraries[0].name
assertEquals("foo.klib", library, "Incorrect library name.")
}
}