gradle-plugin: Rework DSL
* Remove konanInterop block
* Use konanArtifact block for both compiler and interop artifacts
* Specify targets in a project-wide manner
* Autogenerate tasks for all targets specified for a project
* Add an ability to configure each target for an artifact separately
With this patch the following old DSL:
konanInterop {
fooMacbook { target 'macbook' ... }
fooLinux { target 'linux' ... }
}
konanArtifacts {
barMacbook { target 'macbook' }
barLinux { target 'linux' }
}
Transforms into:
konanTargets = ['macbook', 'linux']
konanArtifacts {
interop('foo') { ... }
program('bar') { ... }
}
This commit is contained in:
@@ -92,7 +92,7 @@ dependencies {
|
||||
}
|
||||
|
||||
test {
|
||||
dependsOn ':cross_dist'
|
||||
dependsOn ':dist'
|
||||
//testLogging.showStandardStreams = true
|
||||
systemProperty("konan.home", rootProject.file('dist').canonicalPath)
|
||||
}
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.internal.reflect.Instantiator
|
||||
import org.gradle.util.ConfigureUtil
|
||||
|
||||
|
||||
open class KonanArtifactContainer(val project: ProjectInternal)
|
||||
: DefaultPolymorphicDomainObjectContainer<KonanBuildingConfig<*>>(
|
||||
KonanBuildingConfig::class.java,
|
||||
project.services.get(Instantiator::class.java)
|
||||
) {
|
||||
|
||||
init {
|
||||
registerFactory(KonanProgram::class.java) { name ->
|
||||
instantiator.newInstance(KonanProgram::class.java, name, project, instantiator)
|
||||
}
|
||||
registerFactory(KonanLibrary::class.java) { name ->
|
||||
instantiator.newInstance(KonanLibrary::class.java, name, project, instantiator)
|
||||
}
|
||||
registerFactory(KonanBitcode::class.java) { name ->
|
||||
instantiator.newInstance(KonanBitcode::class.java, name, project, instantiator)
|
||||
}
|
||||
registerFactory(KonanInteropLibrary::class.java) { name ->
|
||||
instantiator.newInstance(KonanInteropLibrary::class.java, name, project, instantiator)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createConfigureDelegate(configureClosure: Closure<*>?): ConfigureDelegate {
|
||||
return ConfigureDelegate(configureClosure)
|
||||
}
|
||||
|
||||
// TODO: Investigate if we can support the same DSL as project.task:
|
||||
// program foo(targets: [target1, target2, target3]) {
|
||||
// ...
|
||||
// }
|
||||
open inner class ConfigureDelegate(configureClosure: Closure<*>?)
|
||||
: org.gradle.internal.metaobject.ConfigureDelegate(configureClosure, this@KonanArtifactContainer) {
|
||||
|
||||
fun program(name: String) = create(name, KonanProgram::class.java)
|
||||
fun program(name: String, configureAction: Action<KonanProgram>) =
|
||||
create(name, KonanProgram::class.java, configureAction)
|
||||
fun program(name: String, configureAction: KonanProgram.() -> Unit) =
|
||||
create(name, KonanProgram::class.java, configureAction)
|
||||
fun program(name: String, configureAction: Closure<*>) =
|
||||
program(name, ConfigureUtil.configureUsing(configureAction))
|
||||
|
||||
fun library(name: String) = create(name, KonanLibrary::class.java)
|
||||
fun library(name: String, configureAction: Action<KonanLibrary>) =
|
||||
create(name, KonanLibrary::class.java, configureAction)
|
||||
fun library(name: String, configureAction: KonanLibrary.() -> Unit) =
|
||||
create(name, KonanLibrary::class.java, configureAction)
|
||||
fun library(name: String, configureAction: Closure<*>) =
|
||||
library(name, ConfigureUtil.configureUsing(configureAction))
|
||||
|
||||
fun bitcode(name: String) = create(name, KonanBitcode::class.java)
|
||||
fun bitcode(name: String, configureAction: Action<KonanBitcode>) =
|
||||
create(name, KonanBitcode::class.java, configureAction)
|
||||
fun bitcode(name: String, configureAction: KonanBitcode.() -> Unit) =
|
||||
create(name, KonanBitcode::class.java, configureAction)
|
||||
fun bitcode(name: String, configureAction: Closure<*>) =
|
||||
bitcode(name, ConfigureUtil.configureUsing(configureAction))
|
||||
|
||||
fun interop(name: String) = create(name, KonanInteropLibrary::class.java)
|
||||
fun interop(name: String, configureAction: Action<KonanInteropLibrary>) =
|
||||
create(name, KonanInteropLibrary::class.java, configureAction)
|
||||
fun interop(name: String, configureAction: KonanInteropLibrary.() -> Unit) =
|
||||
create(name, KonanInteropLibrary::class.java, configureAction)
|
||||
fun interop(name: String, configureAction: Closure<*>) =
|
||||
interop(name, ConfigureUtil.configureUsing(configureAction))
|
||||
}
|
||||
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import groovy.lang.Closure
|
||||
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.DefaultNamedDomainObjectSet
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.internal.reflect.Instantiator
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanBuildingTask
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.TargetManager
|
||||
import java.io.File
|
||||
|
||||
/** Base class for all Kotlin/Native artifacts. */
|
||||
abstract class KonanBuildingConfig<T: KonanBuildingTask>(private val name_: String,
|
||||
type: Class<T>,
|
||||
val project: ProjectInternal,
|
||||
instantiator: Instantiator)
|
||||
: KonanBuildingSpec, Named, DefaultNamedDomainObjectSet<T>(type, instantiator, { it.target.userName }) {
|
||||
|
||||
override fun getName() = name_
|
||||
|
||||
protected val targetToTask = mutableMapOf<KonanTarget, T>()
|
||||
|
||||
internal val aggregateBuildTask: Task
|
||||
|
||||
val declaredTargets: Iterable<KonanTarget> = project.konanTargets.map { TargetManager(it).target }
|
||||
|
||||
init {
|
||||
declaredTargets.forEach {
|
||||
if (it.enabled) {
|
||||
super.add(createTask(it))
|
||||
} else {
|
||||
project.logger.warn("The target is not enabled on the current host: ${it.userName}")
|
||||
}
|
||||
}
|
||||
aggregateBuildTask = createAggregateTask()
|
||||
createHostTaskIfDeclared()
|
||||
}
|
||||
|
||||
protected open fun generateTaskName(target: KonanTarget) =
|
||||
"compileKonan${name.capitalize()}${target.userName.capitalize()}"
|
||||
|
||||
protected open fun generateAggregateTaskName() =
|
||||
"compileKonan${name.capitalize()}"
|
||||
|
||||
protected open fun generateHostTaskName() =
|
||||
"compileKonan${name.capitalize()}Host"
|
||||
|
||||
protected abstract fun generateTaskDescription(task: T): String
|
||||
protected abstract fun generateAggregateTaskDescription(task: Task): String
|
||||
protected abstract fun generateHostTaskDescription(task: Task, hostTarget: KonanTarget): String
|
||||
|
||||
protected abstract val defaultOutputDir: File
|
||||
|
||||
override fun didAdd(toAdd: T) {
|
||||
super.didAdd(toAdd)
|
||||
|
||||
assert(toAdd.target !in targetToTask)
|
||||
targetToTask[toAdd.target] = toAdd
|
||||
}
|
||||
|
||||
protected fun createTask(target: KonanTarget): T =
|
||||
project.tasks.create(generateTaskName(target), type) {
|
||||
it.init(defaultOutputDir, name, target)
|
||||
it.group = BasePlugin.BUILD_GROUP
|
||||
it.description = generateTaskDescription(it)
|
||||
} ?: throw Exception("Cannot create task for target: ${target.userName}")
|
||||
|
||||
protected fun createAggregateTask(): Task =
|
||||
project.tasks.create(generateAggregateTaskName()) { task ->
|
||||
task.group = BasePlugin.BUILD_GROUP
|
||||
task.description = generateAggregateTaskDescription(task)
|
||||
this.filter {
|
||||
project.targetIsRequested(it.target)
|
||||
}.forEach {
|
||||
task.dependsOn(it)
|
||||
}
|
||||
project.compileAllTask.dependsOn(task)
|
||||
}
|
||||
|
||||
protected fun createHostTaskIfDeclared(): Task? =
|
||||
this[TargetManager.host]?.let { hostBuild ->
|
||||
project.tasks.create(generateHostTaskName()) {
|
||||
it.group = BasePlugin.BUILD_GROUP
|
||||
it.description = generateHostTaskDescription(it, hostBuild.target)
|
||||
it.dependsOn(hostBuild)
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(target: KonanTarget) = targetToTask[target]
|
||||
|
||||
// Common building DSL.
|
||||
|
||||
override fun outputName(name: String) = forEach { it.outputName(name) }
|
||||
override fun baseDir(dir: Any) = forEach { it.baseDir(dir) }
|
||||
|
||||
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 noDefaultLibs(flag: Boolean) = forEach { it.noDefaultLibs(flag) }
|
||||
|
||||
override fun dumpParameters(flag: Boolean) = forEach { it.dumpParameters(flag) }
|
||||
|
||||
override fun extraOpts(vararg values: Any) = forEach { it.extraOpts(*values) }
|
||||
override fun extraOpts(values: List<Any>) = forEach { it.extraOpts(values) }
|
||||
|
||||
fun dependsOn(vararg dependencies: Any?) = forEach { it.dependsOn(*dependencies) }
|
||||
|
||||
fun target(targetString: String, configureAction: T.() -> Unit) {
|
||||
val target = TargetManager(targetString).target
|
||||
|
||||
if (!target.enabled) {
|
||||
project.logger.info("Target '$targetString' of artifact '$name' is not supported on the current host")
|
||||
return
|
||||
}
|
||||
|
||||
val task = this[target] ?:
|
||||
throw InvalidUserDataException("Target '$targetString' is not declared. Please add it into project.konanTasks list")
|
||||
task.configureAction()
|
||||
}
|
||||
fun target(targetString: String, configureAction: Action<T>) =
|
||||
target(targetString) { configureAction.execute(this) }
|
||||
fun target(targetString: String, configureAction: Closure<Unit>) =
|
||||
target(targetString, ConfigureUtil.configureUsing(configureAction))
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.internal.reflect.Instantiator
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompileBitcodeTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompileLibraryTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompileProgramTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompileTask
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.io.File
|
||||
|
||||
abstract class KonanCompileConfig<T: KonanCompileTask>(name: String,
|
||||
type: Class<T>,
|
||||
project: ProjectInternal,
|
||||
instantiator: Instantiator)
|
||||
: KonanBuildingConfig<T>(name, type, project, instantiator), KonanCompileSpec {
|
||||
|
||||
protected abstract val typeForDescription: String
|
||||
|
||||
override fun generateTaskDescription(task: T) =
|
||||
"Build the Kotlin/Native $typeForDescription '${task.name}' for target '${task.target}'"
|
||||
|
||||
override fun generateAggregateTaskDescription(task: Task) =
|
||||
"Build the Kotlin/Native $typeForDescription '${task.name}' for all supported and declared targets"
|
||||
|
||||
override fun generateHostTaskDescription(task: Task, hostTarget: KonanTarget) =
|
||||
"Build the Kotlin/Native $typeForDescription '${task.name}' for current host"
|
||||
|
||||
override fun inputDir(dir: Any) = forEach { it.inputDir(dir) }
|
||||
override fun inputFiles(vararg files: Any) = forEach { it.inputFiles(*files) }
|
||||
override fun inputFiles(files: Collection<Any>) = forEach { it.inputFiles(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 linkerOpts(args: List<String>) = forEach { it.linkerOpts(args) }
|
||||
override fun linkerOpts(vararg args: String) = forEach { it.linkerOpts(*args) }
|
||||
|
||||
override fun languageVersion(version: String) = forEach { it.languageVersion(version) }
|
||||
override fun apiVersion(version: String) = forEach { it.apiVersion(version) }
|
||||
|
||||
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 measureTime(flag: Boolean) = forEach { it.measureTime(flag) }
|
||||
}
|
||||
|
||||
open class KonanProgram(name: String, project: ProjectInternal, instantiator: Instantiator)
|
||||
: KonanCompileConfig<KonanCompileProgramTask>(name, KonanCompileProgramTask::class.java, project, instantiator) {
|
||||
|
||||
override val typeForDescription: String
|
||||
get() = "executable"
|
||||
|
||||
override val defaultOutputDir: File
|
||||
get() = project.konanBinOutputDir
|
||||
}
|
||||
|
||||
open class KonanLibrary(name: String, project: ProjectInternal, instantiator: Instantiator)
|
||||
: KonanCompileConfig<KonanCompileLibraryTask>(name, KonanCompileLibraryTask::class.java, project, instantiator) {
|
||||
|
||||
override val typeForDescription: String
|
||||
get() = "library"
|
||||
|
||||
override val defaultOutputDir: File
|
||||
get() = project.konanLibsOutputDir
|
||||
}
|
||||
|
||||
open class KonanBitcode(name: String, project: ProjectInternal, instantiator: Instantiator)
|
||||
: KonanCompileConfig<KonanCompileBitcodeTask>(name, KonanCompileBitcodeTask::class.java, project, instantiator) {
|
||||
override val typeForDescription: String
|
||||
get() = "bitcode"
|
||||
|
||||
override fun generateTaskDescription(task: KonanCompileBitcodeTask) =
|
||||
"Generates bitcode for the artifact '${task.name}' and target '${task.target}'"
|
||||
|
||||
override fun generateAggregateTaskDescription(task: Task) =
|
||||
"Generates bitcode for the artifact '${task.name}' for all supported and declared targets'"
|
||||
|
||||
override fun generateHostTaskDescription(task: Task, hostTarget: KonanTarget) =
|
||||
"Generates bitcode for the artifact '${task.name}' for current host"
|
||||
|
||||
override val defaultOutputDir: File
|
||||
get() = project.konanBitcodeOutputDir
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.internal.reflect.Instantiator
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanInteropTask
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.io.File
|
||||
|
||||
open class KonanInteropLibrary(name: String, project: ProjectInternal, instantiator: Instantiator)
|
||||
: KonanBuildingConfig<KonanInteropTask>(name, KonanInteropTask::class.java, project, instantiator), KonanInteropSpec {
|
||||
|
||||
override fun generateTaskDescription(task: KonanInteropTask) =
|
||||
"Build the Kotlin/Native interop library '${task.name}' for target '${task.target}'"
|
||||
|
||||
override fun generateAggregateTaskDescription(task: Task) =
|
||||
"Build the Kotlin/Native interop library '${task.name}' for all supported and declared targets'"
|
||||
|
||||
override fun generateHostTaskDescription(task: Task, hostTarget: KonanTarget) =
|
||||
"Build the Kotlin/Native interop library '${task.name}' for current host"
|
||||
|
||||
override val defaultOutputDir: File
|
||||
get() = project.konanLibsOutputDir
|
||||
|
||||
// DSL
|
||||
|
||||
override fun defFile(file: Any) = forEach { it.defFile(file) }
|
||||
|
||||
override fun pkg(value: String) = forEach { it.pkg(value) }
|
||||
|
||||
override fun compilerOpts(vararg values: String) = forEach { it.compilerOpts(*values) }
|
||||
|
||||
override fun headers(vararg files: Any) = forEach { it.headers(*files) }
|
||||
|
||||
override fun headers(files: FileCollection) = forEach { it.headers(files) }
|
||||
|
||||
override fun includeDirs(vararg values: Any) = forEach { it.includeDirs(*values) }
|
||||
|
||||
override fun linkerOpts(values: List<String>) = forEach { it.linkerOpts(values) }
|
||||
|
||||
override fun link(vararg files: Any) = forEach { it.link(*files) }
|
||||
override fun link(files: FileCollection) = forEach { it.link(files) }
|
||||
}
|
||||
+65
-95
@@ -1,72 +1,37 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import org.gradle.api.InvalidUserDataException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Nested
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanArtifactWithLibrariesTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanBuildingTask
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.io.File
|
||||
|
||||
open class KonanLibrariesSpec(val task: KonanArtifactWithLibrariesTask, val project: Project) {
|
||||
|
||||
/**
|
||||
* libraries {
|
||||
file 'sdfsdf'
|
||||
file project.files(sdfsd) // == file: Any
|
||||
|
||||
klib 'posix' // no changes
|
||||
klib config
|
||||
|
||||
|
||||
|
||||
artifact 'myLib' // in the project
|
||||
interop 'microhttpd'
|
||||
|
||||
artifact project, 'myLib'
|
||||
interop project, 'name'
|
||||
|
||||
artifact config
|
||||
interop config
|
||||
|
||||
|
||||
|
||||
allArtifactsFrom project
|
||||
allInteropsFrom project
|
||||
|
||||
allArtifactsFrom 'project'
|
||||
allInteropsFrom 'project'
|
||||
|
||||
useRepo Any (-> Project.files)
|
||||
}
|
||||
|
||||
|
||||
val task = libConfig.task
|
||||
|
||||
if (config == libConfig) {
|
||||
throw IllegalArgumentException("Attempt to use a library as its own dependency: " +
|
||||
"${libConfig.name} (in project: ${project.path})")
|
||||
}
|
||||
|
||||
project.evaluationDependsOn(libConfig.project.path)
|
||||
config.dependsOn(task)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: Get rid of two things: Interop and artifact. Replace with one KonanArtifact.
|
||||
class KonanLibrariesSpec(val project: Project) {
|
||||
|
||||
@InputFiles val files = mutableSetOf<FileCollection>() // TODO: Immutable interface?
|
||||
@InputFiles val files = mutableSetOf<FileCollection>()
|
||||
|
||||
@Input val namedKlibs = mutableSetOf<String>()
|
||||
|
||||
@Nested val artifacts = mutableSetOf<KonanBuildingConfig>()
|
||||
@Internal val artifacts = mutableListOf<KonanBuildingTask>()
|
||||
|
||||
// TODO: input?
|
||||
val repos = mutableSetOf<File>().apply {
|
||||
add(project.file(project.konanCompilerOutputDir))
|
||||
add(project.file(project.konanInteropOutputDir))
|
||||
}
|
||||
val artifactFiles: List<File>
|
||||
@InputFiles get() = artifacts.map { it.artifact }
|
||||
|
||||
@Internal val explicitRepos = mutableSetOf<File>()
|
||||
|
||||
val repos: Set<File>
|
||||
@Input get() = explicitRepos +
|
||||
task.outputDir +
|
||||
task.project.konanLibsOutputDir +
|
||||
artifacts.flatMap { it.libraries.repos }.toSet()
|
||||
|
||||
val target: KonanTarget
|
||||
@Internal get() = task.target
|
||||
|
||||
// DSL Methods
|
||||
|
||||
@@ -75,72 +40,77 @@ class KonanLibrariesSpec(val project: Project) {
|
||||
fun files(vararg files: Any) = this.files.addAll(files.map { project.files(it) })
|
||||
fun files(collection: FileCollection) = this.files.add(collection)
|
||||
|
||||
// TODO: doc.
|
||||
/** The compiler with search the library in repos */
|
||||
fun klib(lib: String) = namedKlibs.add(lib)
|
||||
fun klibs(vararg libs: String) = namedKlibs.addAll(libs)
|
||||
fun klibs(libs: Iterable<String>) = namedKlibs.addAll(libs)
|
||||
|
||||
/** Direct link to a config */
|
||||
fun klib(libConfig: KonanBuildingConfig) {
|
||||
useReposOf(libConfig)
|
||||
artifacts.add(libConfig)
|
||||
private fun klibInternal(lib: KonanBuildingConfig<*>) {
|
||||
if (!(lib is KonanLibrary || lib is KonanInteropLibrary)) {
|
||||
throw InvalidUserDataException("Config ${lib.name} is not a library")
|
||||
}
|
||||
|
||||
val libraryTask = lib[target] ?:
|
||||
throw InvalidUserDataException("Library ${lib.name} has no target ${target.userName}")
|
||||
|
||||
if (libraryTask == task) {
|
||||
throw InvalidUserDataException("Attempt to use a library as its own dependency: " +
|
||||
"${task.name} (in project: ${project.path})")
|
||||
}
|
||||
artifacts.add(libraryTask)
|
||||
task.dependsOn(libraryTask)
|
||||
}
|
||||
|
||||
/** Direct link to a config */
|
||||
fun klib(lib: KonanLibrary) = klibInternal(lib)
|
||||
/** Direct link to a config */
|
||||
fun klib(lib: KonanInteropLibrary) = klibInternal(lib)
|
||||
|
||||
/** Artifact in the specified project by name */
|
||||
fun artifact(libraryProject: Project, name: String) {
|
||||
project.evaluationDependsOn(libraryProject)
|
||||
klib(libraryProject.konanArtifactsContainer.getByName(name))
|
||||
}
|
||||
/** Interop in the specified project by name */
|
||||
fun interop(libraryProject: Project, name: String) {
|
||||
project.evaluationDependsOn(libraryProject)
|
||||
klib(libraryProject.konanInteropContainer.getByName(name))
|
||||
klibInternal(libraryProject.konanArtifactsContainer.getByName(name))
|
||||
}
|
||||
|
||||
/** Artifact in the current project by name */
|
||||
fun artifact(name: String) = artifact(project, name)
|
||||
/** Interop library in the current project by name */
|
||||
fun interop(name: String) = interop(project, name)
|
||||
|
||||
/** Artifact by direct link */
|
||||
fun artifact(artifact: KonanCompileConfig) = klib(artifact)
|
||||
/** Interop library by direct link */
|
||||
fun interop(artifact: KonanInteropConfig) = klib(artifact)
|
||||
fun artifact(artifact: KonanLibrary) = klib(artifact)
|
||||
/** Direct link to a config */
|
||||
fun artifact(artifact: KonanInteropLibrary) = klib(artifact)
|
||||
|
||||
/** All artifacts from the projects by direct link */
|
||||
fun allArtifactsFrom(vararg libraryProjects: Project) = libraryProjects.forEach {
|
||||
project.evaluationDependsOn(it)
|
||||
it.konanArtifactsContainer
|
||||
.filter { it.task.isLibrary }
|
||||
.forEach { klib(it) }
|
||||
}
|
||||
/** All interop libraries from the projects by direct link */
|
||||
fun allInteropsFrom(vararg libraryProjects: Project) = libraryProjects.forEach {
|
||||
project.evaluationDependsOn(it)
|
||||
it.konanInteropContainer
|
||||
.forEach { klib(it) }
|
||||
private fun allArtifactsFromInternal(libraryProjects: Array<out Project>,
|
||||
filter: (KonanBuildingConfig<*>) -> Boolean) {
|
||||
libraryProjects.forEach { prj ->
|
||||
project.evaluationDependsOn(prj)
|
||||
prj.konanArtifactsContainer.filter(filter).forEach {
|
||||
klibInternal(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** All artifacts from the project by its relative path */
|
||||
fun allArtifactsFrom(vararg paths: String) = allArtifactsFrom(*paths.map { project.project(it) }.toTypedArray())
|
||||
/** All interop libraries from the project by its name */
|
||||
/** All libraries (both interop and non-interop ones) from the projects by direct references */
|
||||
fun allLibrariesFrom(vararg libraryProjects: Project) = allArtifactsFromInternal(libraryProjects) {
|
||||
it is KonanLibrary || it is KonanInteropLibrary
|
||||
}
|
||||
/** All libraries (both interop and non-interop ones) from the projects by paths */
|
||||
fun allLibrariesFrom(vararg paths: String) = allLibrariesFrom(*paths.map { project.project(it) }.toTypedArray())
|
||||
|
||||
/** All interop libraries from the projects by direct references */
|
||||
fun allInteropsFrom(vararg libraryProjects: Project) = allArtifactsFromInternal(libraryProjects) {
|
||||
it is KonanInteropLibrary
|
||||
}
|
||||
/** All interop libraries from the projects by paths */
|
||||
fun allInteropsFrom(vararg paths: String) = allInteropsFrom(*paths.map { project.project(it) }.toTypedArray())
|
||||
|
||||
/** Add repo for library search */
|
||||
fun useRepo(directory: Any) = repos.add(project.file(directory))
|
||||
fun useRepo(directory: Any) = explicitRepos.add(project.file(directory))
|
||||
/** Add repos for library search */
|
||||
fun useRepos(vararg directories: Any) = directories.forEach { useRepo(it) }
|
||||
/** Add repos for library search */
|
||||
fun useRepos(directories: Iterable<Any>) = directories.forEach { useRepo(it) }
|
||||
|
||||
/** Add the output directory of the config and default output directories of its project as repos */
|
||||
private fun useReposOf(config: KonanBuildingConfig) {
|
||||
useRepo(config.task.outputDir)
|
||||
useRepo(config.project.konanCompilerOutputDir)
|
||||
useRepo(config.project.konanInteropOutputDir)
|
||||
}
|
||||
|
||||
fun Project.evaluationDependsOn(another: Project) {
|
||||
if (this != another) { evaluationDependsOn(another.path) }
|
||||
}
|
||||
|
||||
+98
-107
@@ -18,9 +18,13 @@ package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.tasks.TaskCollection
|
||||
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
|
||||
import org.jetbrains.kotlin.gradle.plugin.KonanPlugin.Companion.COMPILE_ALL_TASK_NAME
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.*
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -31,15 +35,15 @@ import javax.inject.Inject
|
||||
*/
|
||||
|
||||
internal fun Project.hasProperty(property: KonanPlugin.ProjectProperty) = hasProperty(property.propertyName)
|
||||
internal fun Project.findProperty(property: KonanPlugin.ProjectProperty) = findProperty(property.propertyName)
|
||||
internal fun Project.findProperty(property: KonanPlugin.ProjectProperty): Any? = findProperty(property.propertyName)
|
||||
|
||||
internal fun Project.getProperty(property: KonanPlugin.ProjectProperty) = findProperty(property)
|
||||
?: throw IllegalArgumentException("No such property in the project: ${property.propertyName}")
|
||||
|
||||
internal fun Project.getProperty(property: KonanPlugin.ProjectProperty, defaultValue: Any?) =
|
||||
internal fun Project.getProperty(property: KonanPlugin.ProjectProperty, defaultValue: Any) =
|
||||
findProperty(property) ?: defaultValue
|
||||
|
||||
internal fun Project.setProperty(property: KonanPlugin.ProjectProperty, value: Any?) {
|
||||
internal fun Project.setProperty(property: KonanPlugin.ProjectProperty, value: Any) {
|
||||
extensions.extraProperties.set(property.propertyName, value)
|
||||
}
|
||||
|
||||
@@ -50,47 +54,51 @@ internal val Project.konanHome: String
|
||||
return project.file(getProperty(KonanPlugin.ProjectProperty.KONAN_HOME)).canonicalPath
|
||||
}
|
||||
|
||||
internal val Project.konanBuildRoot get() = "${buildDir.canonicalPath}/konan"
|
||||
internal val Project.konanCompilerOutputDir get() = "${konanBuildRoot}/bin"
|
||||
internal val Project.konanInteropOutputDir get() = "${konanBuildRoot}/c_interop"
|
||||
internal val Project.konanBuildRoot get() = buildDir.resolve("konan")
|
||||
internal val Project.konanBinOutputDir get() = konanBuildRoot.resolve("bin")
|
||||
internal val Project.konanLibsOutputDir get() = konanBuildRoot.resolve("libs")
|
||||
internal val Project.konanBitcodeOutputDir get() = konanBuildRoot.resolve("bitcode")
|
||||
|
||||
internal val Project.konanDefaultSrcFiles get() = fileTree("${projectDir.canonicalPath}/src/main/kotlin")
|
||||
internal fun Project.konanDefaultDefFile(libName: String)
|
||||
= file("${projectDir.canonicalPath}/src/main/c_interop/$libName.def")
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal val Project.konanArtifactsContainer: NamedDomainObjectContainer<KonanCompileConfig>
|
||||
get() = extensions.getByName(KonanPlugin.ARTIFACTS_CONTAINER_NAME) as NamedDomainObjectContainer<KonanCompileConfig>
|
||||
internal val Project.konanArtifactsContainer: NamedDomainObjectContainer<KonanBuildingConfig<*>>
|
||||
get() = extensions.getByName(KonanPlugin.ARTIFACTS_CONTAINER_NAME)
|
||||
as NamedDomainObjectContainer<KonanBuildingConfig<*>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal val Project.konanInteropContainer: NamedDomainObjectContainer<KonanInteropConfig>
|
||||
get() = extensions.getByName(KonanPlugin.INTEROP_CONTAINER_NAME) as NamedDomainObjectContainer<KonanInteropConfig>
|
||||
internal val Project.konanTargets: MutableList<String>
|
||||
get() = extensions.extraProperties[KonanPlugin.TARGETS_EXTENSION_NAME] as MutableList<String>
|
||||
|
||||
internal val Project.konanCompilerDownloadTask get() = tasks.getByName(KonanPlugin.KONAN_DOWNLOAD_TASK_NAME)
|
||||
internal val Project.konanCompilerDownloadTask
|
||||
get() = tasks.getByName(KonanPlugin.KONAN_DOWNLOAD_TASK_NAME)
|
||||
|
||||
internal val Project.konanVersion
|
||||
get() = getProperty(KonanPlugin.ProjectProperty.KONAN_VERSION, KonanPlugin.DEFAULT_KONAN_VERSION) as String
|
||||
|
||||
internal fun Project.targetIsRequested(target: String?): Boolean {
|
||||
val targets = getProperty(KonanPlugin.ProjectProperty.KONAN_BUILD_TARGETS).toString().trim().split(' ')
|
||||
internal val Project.requestedTargets
|
||||
get() = findProperty(KonanPlugin.ProjectProperty.KONAN_BUILD_TARGETS)?.toString()?.trim()?.split(' ') ?: emptyList()
|
||||
|
||||
return (targets.contains(target) ||
|
||||
targets.contains("all") ||
|
||||
target == null)
|
||||
internal val Project.compileAllTask
|
||||
get() = getOrCreateTask(COMPILE_ALL_TASK_NAME)
|
||||
|
||||
internal fun Project.targetIsRequested(target: KonanTarget): Boolean {
|
||||
val targets = requestedTargets
|
||||
return (targets.isEmpty() || targets.contains(target.userName) || targets.contains("all"))
|
||||
}
|
||||
|
||||
internal fun Project.targetIsSupportedAndRequested(task: KonanTargetableTask)
|
||||
= task.targetIsSupported && this.targetIsRequested(task.target)
|
||||
/** Looks for task with given name in the given project. Throws [UnknownTaskException] if there's not such task. */
|
||||
private fun Project.getTask(name: String): Task = tasks.getByPath(name)
|
||||
|
||||
internal val Project.supportedCompileTasks: TaskCollection<KonanCompileTask>
|
||||
get() = project.tasks.withType(KonanCompileTask::class.java).matching {
|
||||
targetIsSupportedAndRequested(it)
|
||||
}
|
||||
|
||||
internal val Project.supportedInteropTasks: TaskCollection<KonanInteropTask>
|
||||
get() = project.tasks.withType(KonanInteropTask::class.java).matching {
|
||||
targetIsSupportedAndRequested(it)
|
||||
}
|
||||
/**
|
||||
* Looks for task with given name in the given project.
|
||||
* If such task isn't found, will create it. Returns created/found task.
|
||||
*/
|
||||
private fun Project.getOrCreateTask(name: String): Task = with(tasks) {
|
||||
findByPath(name) ?: create(name, DefaultTask::class.java)
|
||||
}
|
||||
|
||||
internal fun Project.konanCompilerName(): String =
|
||||
"kotlin-native-${project.simpleOsName}-${this.konanVersion}"
|
||||
@@ -98,15 +106,7 @@ internal fun Project.konanCompilerName(): String =
|
||||
internal fun Project.konanCompilerDownloadDir(): String =
|
||||
KonanCompilerDownloadTask.KONAN_PARENT_DIR + "/" + project.konanCompilerName()
|
||||
|
||||
internal class KonanCompileConfigFactory(val project: Project): NamedDomainObjectFactory<KonanCompileConfig> {
|
||||
override fun create(name: String): KonanCompileConfig = KonanCompileConfig(name, project)
|
||||
}
|
||||
|
||||
internal class KonanInteropConfigFactory(val project: Project): NamedDomainObjectFactory<KonanInteropConfig> {
|
||||
override fun create(name: String): KonanInteropConfig = KonanInteropConfig(name, project)
|
||||
}
|
||||
|
||||
// Useful extensions and functions ---------------------------------------
|
||||
// region Useful extensions and functions ---------------------------------------
|
||||
|
||||
internal fun MutableList<String>.addArg(parameter: String, value: String) {
|
||||
add(parameter)
|
||||
@@ -149,52 +149,58 @@ internal fun MutableList<String>.addListArg(parameter: String, values: List<Stri
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
internal fun dumpProperties(task: Task) {
|
||||
fun Iterable<String>.dump() = joinToString(prefix = "[", separator = ",\n${" ".repeat(22)}", postfix = "]")
|
||||
fun Collection<FileCollection>.dump() = flatMap { it.files }.map { it.canonicalPath }.dump()
|
||||
|
||||
when (task) {
|
||||
is KonanCompileTask -> {
|
||||
is KonanCompileTask -> with(task) {
|
||||
println()
|
||||
println("Compilation task: ${task.name}")
|
||||
println("outputDir : ${task.outputDir}")
|
||||
println("artifact : ${task.artifact}")
|
||||
println("artifactPath : ${task.artifactPath}")
|
||||
println("inputFiles : ${task.inputFiles.dump()}")
|
||||
println("produce : ${task.produce}")
|
||||
println("libraries : ${task.libraries.files.dump()}")
|
||||
println(" : ${task.libraries.artifacts.map {
|
||||
it.task.artifact.canonicalPath
|
||||
println("Compilation task: ${name}")
|
||||
println("outputDir : ${outputDir}")
|
||||
println("artifact : ${artifact.canonicalPath}")
|
||||
println("inputFiles : ${inputFiles.dump()}")
|
||||
println("produce : ${produce}")
|
||||
println("libraries : ${libraries.files.dump()}")
|
||||
println(" : ${libraries.artifacts.map {
|
||||
it.artifact.canonicalPath
|
||||
}.dump()}")
|
||||
println(" : ${task.libraries.namedKlibs.dump()}")
|
||||
println("nativeLibraries : ${task.nativeLibraries.dump()}")
|
||||
println("linkerOpts : ${task.linkerOpts}")
|
||||
println("enableDebug : ${task.enableDebug}")
|
||||
println("noStdLib : ${task.noStdLib}")
|
||||
println("noMain : ${task.noMain}")
|
||||
println("enableOptimization : ${task.enableOptimization}")
|
||||
println("enableAssertions : ${task.enableAssertions}")
|
||||
println("target : ${task.target}")
|
||||
println("languageVersion : ${task.languageVersion}")
|
||||
println("apiVersion : ${task.apiVersion}")
|
||||
println("konanVersion : ${task.konanVersion}")
|
||||
println("konanHome : ${task.konanHome}")
|
||||
println(" : ${libraries.namedKlibs.dump()}")
|
||||
println("nativeLibraries : ${nativeLibraries.dump()}")
|
||||
println("linkerOpts : ${linkerOpts}")
|
||||
println("enableDebug : ${enableDebug}")
|
||||
println("noStdLib : ${noStdLib}")
|
||||
println("noMain : ${noMain}")
|
||||
println("enableOptimization : ${enableOptimizations}")
|
||||
println("enableAssertions : ${enableAssertions}")
|
||||
println("noDefaultLibs : ${noDefaultLibs}")
|
||||
println("target : ${target}")
|
||||
println("languageVersion : ${languageVersion}")
|
||||
println("apiVersion : ${apiVersion}")
|
||||
println("konanVersion : ${konanVersion}")
|
||||
println("konanHome : ${konanHome}")
|
||||
println()
|
||||
}
|
||||
is KonanInteropTask -> {
|
||||
is KonanInteropTask -> with(task) {
|
||||
println()
|
||||
println("Stub generation task: ${task.name}")
|
||||
println("outputDir : ${task.outputDir}")
|
||||
println("klib : ${task.klib}")
|
||||
println("defFile : ${task.defFile}")
|
||||
println("target : ${task.target}")
|
||||
println("pkg : ${task.pkg}")
|
||||
println("compilerOpts : ${task.compilerOpts}")
|
||||
println("linkerOpts : ${task.linkerOpts}")
|
||||
println("headers : ${task.headers.dump()}")
|
||||
println("linkFiles : ${task.linkFiles.dump()}")
|
||||
println("konanVersion : ${task.konanVersion}")
|
||||
println("konanHome : ${task.konanHome}")
|
||||
println("Stub generation task: ${name}")
|
||||
println("outputDir : ${outputDir}")
|
||||
println("artifact : ${artifact}")
|
||||
println("libraries : ${libraries.files.dump()}")
|
||||
println(" : ${libraries.artifacts.map {
|
||||
it.artifact.canonicalPath
|
||||
}.dump()}")
|
||||
println(" : ${libraries.namedKlibs.dump()}")
|
||||
println("defFile : ${defFile}")
|
||||
println("target : ${target}")
|
||||
println("pkg : ${pkg}")
|
||||
println("compilerOpts : ${compilerOpts}")
|
||||
println("linkerOpts : ${linkerOpts}")
|
||||
println("headers : ${headers.dump()}")
|
||||
println("linkFiles : ${linkFiles.dump()}")
|
||||
println("konanVersion : ${konanVersion}")
|
||||
println("konanHome : ${konanHome}")
|
||||
println()
|
||||
}
|
||||
else -> {
|
||||
@@ -204,7 +210,7 @@ internal fun dumpProperties(task: Task) {
|
||||
}
|
||||
|
||||
class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderRegistry)
|
||||
: Plugin<Project> {
|
||||
: Plugin<ProjectInternal> {
|
||||
|
||||
enum class ProjectProperty(val propertyName: String) {
|
||||
KONAN_HOME ("konan.home"),
|
||||
@@ -215,8 +221,10 @@ class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderR
|
||||
|
||||
companion object {
|
||||
internal const val ARTIFACTS_CONTAINER_NAME = "konanArtifacts"
|
||||
internal const val INTEROP_CONTAINER_NAME = "konanInterop"
|
||||
internal const val KONAN_DOWNLOAD_TASK_NAME = "checkKonanCompiler"
|
||||
internal const val COMPILE_ALL_TASK_NAME = "compileKonan"
|
||||
|
||||
internal const val TARGETS_EXTENSION_NAME = "konanTargets"
|
||||
|
||||
internal val DEFAULT_KONAN_VERSION = Properties().apply {
|
||||
load(KonanPlugin::class.java.getResourceAsStream("/META-INF/gradle-plugins/konan.properties") ?:
|
||||
@@ -224,46 +232,29 @@ class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderR
|
||||
}.getProperty("default-konan-version") ?: throw RuntimeException("Cannot read the default compiler version")
|
||||
}
|
||||
|
||||
/** Looks for task with given name in the given project. Throws [UnknownTaskException] if there's not such task. */
|
||||
private fun Project.getTask(name: String): Task = tasks.getByPath(name)
|
||||
|
||||
/**
|
||||
* Looks for task with given name in the given project.
|
||||
* If such task isn't found, will create it. Returns created/found task.
|
||||
*/
|
||||
private fun Project.getOrCreateTask(name: String): Task = with(tasks) {
|
||||
findByPath(name) ?: create(name, DefaultTask::class.java)
|
||||
private fun Project.cleanKonan() = project.tasks.withType(KonanBuildingTask::class.java).forEach {
|
||||
project.delete(it.artifact)
|
||||
}
|
||||
|
||||
private fun Project.cleanKonan() = project.konanArtifactsContainer.forEach {
|
||||
project.delete(it.compilationTask.outputDir)
|
||||
}
|
||||
|
||||
override fun apply(project: Project?) {
|
||||
override fun apply(project: ProjectInternal?) {
|
||||
if (project == null) { return }
|
||||
registry.register(KonanToolingModelBuilder)
|
||||
project.plugins.apply("base")
|
||||
// Create necessary tasks and extensions.
|
||||
project.tasks.create(KONAN_DOWNLOAD_TASK_NAME, KonanCompilerDownloadTask::class.java)
|
||||
project.extensions.add(ARTIFACTS_CONTAINER_NAME,
|
||||
project.container(KonanCompileConfig::class.java, KonanCompileConfigFactory(project)))
|
||||
project.extensions.add(INTEROP_CONTAINER_NAME,
|
||||
project.container(KonanInteropConfig::class.java, KonanInteropConfigFactory(project)))
|
||||
project.extensions.extraProperties.set(TARGETS_EXTENSION_NAME, mutableListOf("host"))
|
||||
project.extensions.create(ARTIFACTS_CONTAINER_NAME, KonanArtifactContainer::class.java, project)
|
||||
|
||||
// Set additional project properties like konan.home, konan.build.targets etc.
|
||||
if (!project.hasProperty(ProjectProperty.KONAN_HOME)) {
|
||||
project.setProperty(ProjectProperty.KONAN_HOME, project.konanCompilerDownloadDir())
|
||||
project.setProperty(ProjectProperty.DOWNLOAD_COMPILER, true)
|
||||
}
|
||||
if (!project.hasProperty(ProjectProperty.KONAN_BUILD_TARGETS)) {
|
||||
project.setProperty(ProjectProperty.KONAN_BUILD_TARGETS, project.host)
|
||||
}
|
||||
|
||||
// Create and set up aggregate building tasks.
|
||||
val compileKonanTask = project.getOrCreateTask("compileKonan").apply {
|
||||
dependsOn(project.supportedCompileTasks)
|
||||
val compileKonanTask = project.getOrCreateTask(COMPILE_ALL_TASK_NAME).apply {
|
||||
group = BasePlugin.BUILD_GROUP
|
||||
description = "Compiles all the Kotlin/Native artifacts supported"
|
||||
description = "Compiles all the Kotlin/Native artifacts"
|
||||
}
|
||||
project.getTask("build").apply {
|
||||
dependsOn(compileKonanTask)
|
||||
@@ -276,14 +267,14 @@ class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderR
|
||||
project.getOrCreateTask("run").apply {
|
||||
dependsOn(project.getTask("build"))
|
||||
doLast {
|
||||
for (task in project.tasks.withType(KonanCompileTask::class.java).matching { !it.isCrossCompile}) {
|
||||
if (task?.produce == "program") {
|
||||
project.exec {
|
||||
with(it) {
|
||||
commandLine(task.artifactPath)
|
||||
if (project.extensions.extraProperties.has("runArgs")) {
|
||||
args(project.extensions.extraProperties.get("runArgs").toString().split(' '))
|
||||
}
|
||||
for (task in project.tasks
|
||||
.withType(KonanCompileProgramTask::class.java)
|
||||
.matching { !it.isCrossCompile}) {
|
||||
project.exec {
|
||||
with(it) {
|
||||
commandLine(task.artifact.canonicalPath)
|
||||
if (project.extensions.extraProperties.has("runArgs")) {
|
||||
args(project.extensions.extraProperties.get("runArgs").toString().split(' '))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.file.FileCollection
|
||||
|
||||
interface KonanArtifactSpec {
|
||||
fun outputName(name: String)
|
||||
fun baseDir(dir: Any)
|
||||
}
|
||||
|
||||
interface KonanArtifactWithLibrariesSpec: KonanArtifactSpec {
|
||||
fun libraries(closure: Closure<Unit>)
|
||||
fun libraries(action: Action<KonanLibrariesSpec>)
|
||||
fun libraries(configure: KonanLibrariesSpec.() -> Unit)
|
||||
|
||||
fun noDefaultLibs(flag: Boolean)
|
||||
}
|
||||
|
||||
interface KonanBuildingSpec: KonanArtifactWithLibrariesSpec {
|
||||
fun dumpParameters(flag: Boolean)
|
||||
|
||||
fun extraOpts(vararg values: Any)
|
||||
fun extraOpts(values: List<Any>)
|
||||
}
|
||||
|
||||
interface KonanCompileSpec: KonanBuildingSpec {
|
||||
fun inputDir(dir: Any)
|
||||
|
||||
fun inputFiles(vararg files: Any)
|
||||
fun inputFiles(files: Collection<Any>)
|
||||
|
||||
// DSL. Native libraries.
|
||||
|
||||
fun nativeLibrary(lib: Any)
|
||||
fun nativeLibraries(vararg libs: Any)
|
||||
fun nativeLibraries(libs: FileCollection)
|
||||
|
||||
// DSL. Other parameters.
|
||||
|
||||
fun linkerOpts(args: List<String>)
|
||||
fun linkerOpts(vararg args: String)
|
||||
|
||||
fun languageVersion(version: String)
|
||||
fun apiVersion(version: String)
|
||||
|
||||
fun enableDebug(flag: Boolean)
|
||||
fun noStdLib(flag: Boolean)
|
||||
fun noMain(flag: Boolean)
|
||||
fun enableOptimizations(flag: Boolean)
|
||||
fun enableAssertions(flag: Boolean)
|
||||
|
||||
fun measureTime(flag: Boolean)
|
||||
}
|
||||
|
||||
interface KonanInteropSpec: KonanBuildingSpec {
|
||||
|
||||
fun defFile(file: Any)
|
||||
|
||||
fun pkg(value: String)
|
||||
|
||||
fun compilerOpts(vararg values: String)
|
||||
|
||||
fun header(file: Any) = headers(file)
|
||||
fun headers(vararg files: Any)
|
||||
fun headers(files: FileCollection)
|
||||
|
||||
fun includeDirs(vararg values: Any)
|
||||
|
||||
fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
|
||||
fun linkerOpts(values: List<String>)
|
||||
|
||||
fun link(vararg files: Any)
|
||||
fun link(files: FileCollection)
|
||||
}
|
||||
+1
@@ -3,6 +3,7 @@ package org.jetbrains.kotlin.gradle.plugin
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.jetbrains.kotlin.gradle.plugin.tasks.host
|
||||
|
||||
internal interface KonanToolRunner: Named {
|
||||
val mainClass: String
|
||||
|
||||
+2
-2
@@ -25,9 +25,9 @@ object KonanToolingModelBuilder : ToolingModelBuilder {
|
||||
override fun canBuild(modelName: String): Boolean = KonanModel::class.java.name == modelName
|
||||
|
||||
override fun buildAll(modelName: String, project: Project): KonanModel {
|
||||
val artifacts = project.supportedCompileTasks
|
||||
val artifacts = project.konanArtifactsContainer.flatten()
|
||||
.toList()
|
||||
.map { KonanArtifactImpl(it.artifactName, it.artifactPath) }
|
||||
.map { KonanArtifactImpl("${it.artifact.name}-${it.target.userName}", it.artifact.canonicalPath) }
|
||||
return KonanModelImpl(project.konanVersion, artifacts)
|
||||
}
|
||||
}
|
||||
|
||||
+88
-25
@@ -14,37 +14,100 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
package org.jetbrains.kotlin.gradle.plugin.tasks
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.konan.target.*
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget.*
|
||||
|
||||
abstract class KonanTargetableTask: DefaultTask() {
|
||||
|
||||
@Optional @Input var target : String? = null
|
||||
internal set
|
||||
|
||||
internal val targetManager: TargetManager by lazy {
|
||||
TargetManager(target ?: "host")
|
||||
} @Internal get
|
||||
|
||||
|
||||
val targetIsSupported: Boolean
|
||||
@Internal get() = targetManager.target.enabled
|
||||
|
||||
val isCrossCompile: Boolean
|
||||
@Internal get() = (targetManager.target != TargetManager.host)
|
||||
|
||||
@Internal fun produceSuffix(produce: String): String
|
||||
= CompilerOutputKind.valueOf(produce.toUpperCase())
|
||||
.suffix(targetManager.target)
|
||||
}
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.Nested
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import org.jetbrains.kotlin.gradle.plugin.KonanArtifactSpec
|
||||
import org.jetbrains.kotlin.gradle.plugin.KonanArtifactWithLibrariesSpec
|
||||
import org.jetbrains.kotlin.gradle.plugin.KonanLibrariesSpec
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.TargetManager
|
||||
import java.io.File
|
||||
|
||||
internal val Project.host
|
||||
get() = TargetManager.host.name.toLowerCase()
|
||||
|
||||
internal val Project.simpleOsName
|
||||
get() = TargetManager.simpleOsName()
|
||||
|
||||
/** A task with a KonanTarget specified. */
|
||||
abstract class KonanTargetableTask: DefaultTask() {
|
||||
|
||||
@Input lateinit var target: KonanTarget
|
||||
internal set
|
||||
|
||||
internal open fun init(target: KonanTarget) {
|
||||
this.target = target
|
||||
}
|
||||
|
||||
val targetIsSupported: Boolean
|
||||
@Internal get() = target.enabled
|
||||
|
||||
val isCrossCompile: Boolean
|
||||
@Internal get() = (target != TargetManager.host)
|
||||
}
|
||||
|
||||
/** A task building an artifact. */
|
||||
abstract class KonanArtifactTask: KonanTargetableTask(), KonanArtifactSpec {
|
||||
|
||||
val artifact: File
|
||||
@OutputFile get() = outputDir.resolve(artifactNameWithSuffix)
|
||||
|
||||
@Internal lateinit var baseDir: File
|
||||
@Internal lateinit var outputName: String
|
||||
|
||||
val outputDir
|
||||
@Internal get() = baseDir.resolve(target.userName)
|
||||
|
||||
protected val artifactNameWithSuffix: String
|
||||
@Internal get() = "$outputName$artifactSuffix"
|
||||
|
||||
protected val artifactPath: String
|
||||
@Internal get() = artifact.canonicalPath
|
||||
|
||||
protected abstract val artifactSuffix: String
|
||||
@Internal get
|
||||
|
||||
internal open fun init(baseDir: File, outputName: String, target: KonanTarget) {
|
||||
super.init(target)
|
||||
this.baseDir = baseDir
|
||||
this.outputName = outputName
|
||||
}
|
||||
|
||||
// DSL.
|
||||
|
||||
override fun outputName(name: String) {
|
||||
outputName = name
|
||||
}
|
||||
|
||||
override fun baseDir(dir: Any) {
|
||||
baseDir = project.file(dir)
|
||||
}
|
||||
}
|
||||
|
||||
/** Task building an artifact with libraries */
|
||||
abstract class KonanArtifactWithLibrariesTask: KonanArtifactTask(), KonanArtifactWithLibrariesSpec {
|
||||
@Nested
|
||||
val libraries = KonanLibrariesSpec(this, project)
|
||||
|
||||
@Input
|
||||
var noDefaultLibs = false
|
||||
|
||||
// DSL
|
||||
|
||||
override fun libraries(closure: Closure<Unit>) = libraries(ConfigureUtil.configureUsing(closure))
|
||||
override fun libraries(action: Action<KonanLibrariesSpec>) = libraries { action.execute(this) }
|
||||
override fun libraries(configure: KonanLibrariesSpec.() -> Unit) { libraries.configure() }
|
||||
|
||||
override fun noDefaultLibs(flag: Boolean) {
|
||||
noDefaultLibs = flag
|
||||
}
|
||||
}
|
||||
|
||||
+34
-51
@@ -1,66 +1,49 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
package org.jetbrains.kotlin.gradle.plugin.tasks
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.Nested
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.io.File
|
||||
|
||||
abstract class KonanBuildingTask: KonanTargetableTask() {
|
||||
abstract val artifactPath: String
|
||||
@Internal get
|
||||
abstract val artifact: File
|
||||
@OutputFile get
|
||||
abstract val outputDir: File
|
||||
@Internal get
|
||||
/** Base class for both interop and compiler tasks. */
|
||||
abstract class KonanBuildingTask: KonanArtifactWithLibrariesTask(), KonanBuildingSpec {
|
||||
|
||||
abstract val isLibrary: Boolean
|
||||
@Internal get
|
||||
internal abstract val toolRunner: KonanToolRunner
|
||||
|
||||
override fun init(baseDir: File, outputName: String, target: KonanTarget) {
|
||||
dependsOn(project.konanCompilerDownloadTask)
|
||||
super.init(baseDir, outputName, target)
|
||||
}
|
||||
|
||||
@Input
|
||||
var dumpParameters: Boolean = false
|
||||
|
||||
@Input
|
||||
val extraOpts = mutableListOf<String>()
|
||||
|
||||
val konanVersion
|
||||
@Input get() = project.konanVersion
|
||||
val konanHome
|
||||
@Input get() = project.konanHome
|
||||
|
||||
@Nested
|
||||
val libraries = KonanLibrariesSpec(project)
|
||||
protected abstract fun buildArgs(): List<String>
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
outputDir.mkdirs()
|
||||
if (dumpParameters) { dumpProperties(this) }
|
||||
toolRunner.run(buildArgs())
|
||||
}
|
||||
|
||||
// DSL.
|
||||
|
||||
override fun dumpParameters(flag: Boolean) {
|
||||
dumpParameters = flag
|
||||
}
|
||||
|
||||
override fun extraOpts(vararg values: Any) = extraOpts(values.toList())
|
||||
override fun extraOpts(values: List<Any>) {
|
||||
extraOpts.addAll(values.map { it.toString() })
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement the target properties here.
|
||||
abstract class KonanBuildingConfig(val configName: String,
|
||||
val project: Project): Named {
|
||||
|
||||
override fun getName(): String = configName
|
||||
|
||||
fun dumpParameters(value: Boolean) {
|
||||
task.dumpParameters = value
|
||||
}
|
||||
|
||||
// TODO: Replace with target enum
|
||||
fun target(target: String) {
|
||||
task.target = target
|
||||
}
|
||||
|
||||
// TODO: Replace with a collection for target support
|
||||
abstract internal val task: KonanBuildingTask
|
||||
|
||||
fun dependsOn(dependency: Any) = task.dependsOn(dependency)
|
||||
|
||||
fun libraries(closure: Closure<Unit>) = libraries(ConfigureUtil.configureUsing(closure))
|
||||
fun libraries(action: Action<KonanLibrariesSpec>) = libraries { action.execute(this) }
|
||||
fun libraries(configure: KonanLibrariesSpec.() -> Unit) {
|
||||
task.libraries.configure()
|
||||
// TODO: May be rework.
|
||||
task.libraries.artifacts.forEach {
|
||||
dependsOn(it.task)
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
-184
@@ -14,48 +14,34 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
package org.jetbrains.kotlin.gradle.plugin.tasks
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import java.io.File
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
|
||||
enum class Produce(val cliOption: String, val kind: CompilerOutputKind) {
|
||||
PROGRAM("program", CompilerOutputKind.PROGRAM),
|
||||
LIBRARY("library", CompilerOutputKind.LIBRARY),
|
||||
BITCODE("bitcode", CompilerOutputKind.BITCODE)
|
||||
}
|
||||
|
||||
/**
|
||||
* A task compiling the target executable/library using Kotlin/Native compiler
|
||||
*/
|
||||
open class KonanCompileTask: KonanBuildingTask() {
|
||||
abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
|
||||
|
||||
// TODO: Support custom runner options (java options)
|
||||
@Internal override val toolRunner = KonanCompilerRunner(project)
|
||||
|
||||
abstract val produce: Produce
|
||||
@Internal get
|
||||
|
||||
// Output artifact --------------------------------------------------------
|
||||
|
||||
internal lateinit var artifactName: String
|
||||
@Internal get
|
||||
|
||||
@Internal override lateinit var outputDir: File
|
||||
internal set
|
||||
|
||||
internal fun init(artifactName: String) {
|
||||
dependsOn(project.konanCompilerDownloadTask)
|
||||
this.artifactName = artifactName
|
||||
outputDir = project.file(project.konanCompilerOutputDir)
|
||||
}
|
||||
|
||||
protected val artifactNamePath: String
|
||||
@Internal get() = "${outputDir.absolutePath}/$artifactName"
|
||||
|
||||
protected val artifactSuffix: String
|
||||
@Internal get() = produceSuffix(produce)
|
||||
|
||||
override val artifactPath: String
|
||||
@Internal get() = "$artifactNamePath$artifactSuffix"
|
||||
|
||||
override val artifact: File
|
||||
@OutputFile get() = project.file(artifactPath)
|
||||
override val artifactSuffix: String
|
||||
@Internal get() = produce.kind.suffix(target)
|
||||
|
||||
// Other compilation parameters -------------------------------------------
|
||||
|
||||
@@ -65,228 +51,130 @@ open class KonanCompileTask: KonanBuildingTask() {
|
||||
|
||||
@InputFiles val nativeLibraries = mutableSetOf<FileCollection>()
|
||||
|
||||
@Input var produce = "program"
|
||||
internal set
|
||||
@Input val linkerOpts = mutableListOf<String>()
|
||||
|
||||
// TODO: Replace produce string with an enum.
|
||||
override val isLibrary: Boolean
|
||||
@Internal get() = produce == "library"
|
||||
@Input var enableDebug =
|
||||
project.properties.containsKey("enableDebug") &&
|
||||
project.properties["enableDebug"].toString().toBoolean()
|
||||
|
||||
@Input val extraOpts = mutableListOf<String>()
|
||||
|
||||
internal var _linkerOpts = mutableListOf<String>()
|
||||
val linkerOpts: List<String>
|
||||
@Input get() = _linkerOpts // TODO: use the original linkerOpts prop.
|
||||
|
||||
@Input var enableDebug = project.properties.containsKey("enableDebug") && project.properties["enableDebug"].toString().toBoolean()
|
||||
internal set
|
||||
@Input var noStdLib = false
|
||||
internal set
|
||||
@Input var noMain = false
|
||||
internal set
|
||||
@Input var enableOptimization = false
|
||||
internal set
|
||||
@Input var enableAssertions = false
|
||||
internal set
|
||||
@Input var noDefaultLibs = false
|
||||
internal set
|
||||
@Console var measureTime = false
|
||||
internal set
|
||||
@Input var noStdLib = false
|
||||
@Input var noMain = false
|
||||
@Input var enableOptimizations = false
|
||||
@Input var enableAssertions = false
|
||||
@Console var measureTime = false
|
||||
|
||||
@Optional @Input var languageVersion : String? = null
|
||||
internal set
|
||||
@Optional @Input var apiVersion : String? = null
|
||||
internal set
|
||||
|
||||
// Task action ------------------------------------------------------------
|
||||
// Command line ------------------------------------------------------------
|
||||
|
||||
protected fun buildArgs() = mutableListOf<String>().apply {
|
||||
addArg("-output", artifactNamePath)
|
||||
override fun buildArgs() = mutableListOf<String>().apply {
|
||||
addArg("-output", artifact.canonicalPath)
|
||||
|
||||
// TODO: remove this repo
|
||||
addArg("-repo", outputDir.canonicalPath)
|
||||
addArgs("-repo", libraries.repos.map { it.canonicalPath })
|
||||
|
||||
addFileArgs("-library", libraries.files)
|
||||
addArgs("-library", libraries.namedKlibs)
|
||||
addArgs("-library", libraries.artifacts.map { it.task.artifact.canonicalPath })
|
||||
addArgs("-repo", libraries.repos.map { it.canonicalPath })
|
||||
addArgs("-library", libraries.artifacts.map { it.artifact.canonicalPath })
|
||||
|
||||
addFileArgs("-nativelibrary", nativeLibraries)
|
||||
addArg("-produce", produce)
|
||||
addArg("-produce", produce.cliOption)
|
||||
|
||||
addListArg("-linkerOpts", linkerOpts)
|
||||
|
||||
addArgIfNotNull("-target", target)
|
||||
addArgIfNotNull("-target", target.userName)
|
||||
addArgIfNotNull("-language-version", languageVersion)
|
||||
addArgIfNotNull("-api-version", apiVersion)
|
||||
|
||||
addKey("-g", enableDebug)
|
||||
addKey("-nostdlib", noStdLib)
|
||||
addKey("-nomain", noMain)
|
||||
addKey("-opt", enableOptimization)
|
||||
addKey("-opt", enableOptimizations)
|
||||
addKey("-ea", enableAssertions)
|
||||
addKey("-nodefaultlibs", noDefaultLibs)
|
||||
addKey("--time", measureTime)
|
||||
addKey("-nodefaultlibs", noDefaultLibs)
|
||||
|
||||
addAll(extraOpts)
|
||||
|
||||
inputFiles.flatMap { it.files }.filter { it.name.endsWith(".kt") }.mapTo(this) { it.canonicalPath }
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun compile() {
|
||||
outputDir.mkdirs()
|
||||
|
||||
if (dumpParameters) dumpProperties(this@KonanCompileTask)
|
||||
|
||||
// TODO: Use compiler service.
|
||||
KonanCompilerRunner(project).run(buildArgs())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use +=/-= syntax for libraries and inputFiles
|
||||
open class KonanCompileConfig(
|
||||
configName: String,
|
||||
project: Project,
|
||||
taskNamePrefix: String = "compileKonan"): KonanBuildingConfig(configName, project) {
|
||||
|
||||
override fun getName() = configName
|
||||
|
||||
val compilationTask: KonanCompileTask = project.tasks.create(
|
||||
"$taskNamePrefix${configName.capitalize()}",
|
||||
KonanCompileTask::class.java) {
|
||||
it.init(this@KonanCompileConfig.name)
|
||||
it.group = BasePlugin.BUILD_GROUP
|
||||
it.description = "Compiles the Kotlin/Native artifact '${this@KonanCompileConfig.name}'"
|
||||
}
|
||||
|
||||
override val task: KonanCompileTask
|
||||
get() = compilationTask
|
||||
|
||||
private fun evaluationDependsOn(anotherProject: Project) {
|
||||
if (anotherProject != project) {
|
||||
project.evaluationDependsOn(anotherProject.path)
|
||||
}
|
||||
}
|
||||
|
||||
// DSL methods. --------------------------------------------------
|
||||
|
||||
@Deprecated("Use `libraries` block instead")
|
||||
fun useInterop(interop: KonanInteropConfig) = libraries { interop(interop) }
|
||||
|
||||
@Deprecated("Use `libraries` block instead")
|
||||
fun useInterop(interop: String) = libraries { interop(interop) }
|
||||
|
||||
@Deprecated("Use `libraries` block instead")
|
||||
fun useInterops(interops: Collection<Any>) {
|
||||
interops.forEach {
|
||||
when(it) {
|
||||
is String -> useInterop(it)
|
||||
is KonanInteropConfig -> useInterop(it)
|
||||
else -> throw IllegalArgumentException("Cannot convert the object to an interop description: $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
// region DSL.
|
||||
|
||||
// DSL. Input/output files.
|
||||
|
||||
fun inputDir(dir: Any) = with(compilationTask) {
|
||||
override fun inputDir(dir: Any) {
|
||||
_inputFiles.add(project.fileTree(dir).apply {
|
||||
include("**/*.kt")
|
||||
exclude { it.file.startsWith(project.buildDir) }
|
||||
})
|
||||
}
|
||||
fun inputFiles(vararg files: Any) = with(compilationTask) {
|
||||
override fun inputFiles(vararg files: Any) {
|
||||
_inputFiles.add(project.files(files))
|
||||
}
|
||||
fun inputFiles(files: FileCollection) = compilationTask._inputFiles.add(files)
|
||||
fun inputFiles(files: Collection<FileCollection>) = compilationTask._inputFiles.addAll(files)
|
||||
|
||||
// TODO: Remove this functional.
|
||||
fun outputDir(dir: Any) = with(compilationTask) {
|
||||
outputDir = project.file(dir)
|
||||
}
|
||||
|
||||
fun outputName(name: String) = with(compilationTask) {
|
||||
artifactName = name
|
||||
}
|
||||
|
||||
// DSL. Libraries.
|
||||
|
||||
@Deprecated("Use `libraries` block instead")
|
||||
fun library(project: Project) = libraries { allArtifactsFrom(project) }
|
||||
|
||||
@Deprecated("Use `libraries` block instead")
|
||||
fun library(project: Project, artifactName: String) = libraries { this.artifact(project, artifactName) }
|
||||
|
||||
@Deprecated("Use `libraries` block instead")
|
||||
fun library(lib: Any) = libraries { file(lib) }
|
||||
|
||||
@Deprecated("Use `libraries` block instead")
|
||||
fun libraries(vararg libs: Any) = libraries { files(*libs) }
|
||||
|
||||
@Deprecated("Use `libraries` block instead")
|
||||
fun libraries(libs: FileCollection) = libraries { files(libs) }
|
||||
override fun inputFiles(files: Collection<Any>) = inputFiles(*files.toTypedArray())
|
||||
|
||||
// DSL. Native libraries.
|
||||
|
||||
fun nativeLibrary(lib: Any) = nativeLibraries(lib)
|
||||
fun nativeLibraries(vararg libs: Any) = with(compilationTask) {
|
||||
override fun nativeLibrary(lib: Any) = nativeLibraries(lib)
|
||||
override fun nativeLibraries(vararg libs: Any) {
|
||||
nativeLibraries.add(project.files(*libs))
|
||||
}
|
||||
fun nativeLibraries(libs: FileCollection) = with(compilationTask) {
|
||||
override fun nativeLibraries(libs: FileCollection) {
|
||||
nativeLibraries.add(libs)
|
||||
}
|
||||
|
||||
// DSL. Other parameters.
|
||||
|
||||
fun linkerOpts(args: List<String>) = linkerOpts(*args.toTypedArray())
|
||||
fun linkerOpts(vararg args: String) = with(compilationTask) {
|
||||
_linkerOpts.addAll(args)
|
||||
override fun linkerOpts(args: List<String>) = linkerOpts(*args.toTypedArray())
|
||||
override fun linkerOpts(vararg args: String) {
|
||||
linkerOpts.addAll(args)
|
||||
}
|
||||
|
||||
fun languageVersion(version: String) = with(compilationTask) {
|
||||
override fun languageVersion(version: String) {
|
||||
languageVersion = version
|
||||
}
|
||||
|
||||
fun apiVersion(version: String) = with(compilationTask) {
|
||||
override fun apiVersion(version: String) {
|
||||
apiVersion = version
|
||||
}
|
||||
|
||||
fun enableDebug(flag: Boolean) = with(compilationTask) {
|
||||
override fun enableDebug(flag: Boolean) {
|
||||
enableDebug = flag
|
||||
}
|
||||
|
||||
fun noStdLib() = with(compilationTask) {
|
||||
noStdLib = true
|
||||
override fun noStdLib(flag: Boolean) {
|
||||
noStdLib = flag
|
||||
}
|
||||
|
||||
fun produce(prod: String) = with(compilationTask) {
|
||||
produce = prod
|
||||
override fun noMain(flag: Boolean) {
|
||||
noMain = flag
|
||||
}
|
||||
|
||||
fun noMain() = with(compilationTask) {
|
||||
noMain = true
|
||||
override fun enableOptimizations(flag: Boolean) {
|
||||
enableOptimizations = true
|
||||
}
|
||||
|
||||
fun enableOptimization() = with(compilationTask) {
|
||||
enableOptimization = true
|
||||
override fun enableAssertions(flag: Boolean) {
|
||||
enableAssertions = flag
|
||||
}
|
||||
|
||||
fun enableAssertions() = with(compilationTask) {
|
||||
enableAssertions = true
|
||||
}
|
||||
|
||||
fun noDefaultLibs(flag: Boolean) = with(compilationTask) {
|
||||
noDefaultLibs = flag
|
||||
}
|
||||
|
||||
fun measureTime(value: Boolean) = with(compilationTask) {
|
||||
measureTime = value
|
||||
}
|
||||
|
||||
fun extraOpts(vararg values: Any) = extraOpts(values.asList())
|
||||
fun extraOpts(values: List<Any>) {
|
||||
values.mapTo(compilationTask.extraOpts) { it.toString() }
|
||||
override fun measureTime(flag: Boolean) {
|
||||
measureTime = flag
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
open class KonanCompileProgramTask: KonanCompileTask() {
|
||||
override val produce: Produce get() = Produce.PROGRAM
|
||||
}
|
||||
|
||||
open class KonanCompileLibraryTask: KonanCompileTask() {
|
||||
override val produce: Produce get() = Produce.LIBRARY
|
||||
}
|
||||
|
||||
open class KonanCompileBitcodeTask: KonanCompileTask() {
|
||||
override val produce: Produce get() = Produce.BITCODE
|
||||
}
|
||||
+5
-3
@@ -14,11 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
package org.jetbrains.kotlin.gradle.plugin.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleScriptException
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.konan.util.DependencyProcessor
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@@ -54,7 +56,7 @@ open class KonanCompilerDownloadTask : DefaultTask() {
|
||||
} else {
|
||||
try {
|
||||
val konanCompiler = project.konanCompilerName()
|
||||
logger.info("Downloading Kotlin/Native compiler from $DOWNLOAD_URL/$konanCompiler into $KONAN_PARENT_DIR")
|
||||
logger.info("Downloading Kotlin/Native compiler from ${DOWNLOAD_URL}/$konanCompiler into ${KONAN_PARENT_DIR}")
|
||||
DependencyProcessor(File(KONAN_PARENT_DIR), DOWNLOAD_URL, listOf(konanCompiler)).run()
|
||||
} catch (e: IOException) {
|
||||
throw GradleScriptException("Cannot download Kotlin/Native compiler", e)
|
||||
+33
-117
@@ -14,88 +14,54 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
package org.jetbrains.kotlin.gradle.plugin.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* A task executing cinterop tool with the given args and compiling the stubs produced by this tool.
|
||||
*/
|
||||
open class KonanInteropTask: KonanBuildingTask() {
|
||||
open class KonanInteropTask: KonanBuildingTask(), KonanInteropSpec {
|
||||
|
||||
internal fun init(libName: String) {
|
||||
dependsOn(project.konanCompilerDownloadTask)
|
||||
this.libName = libName
|
||||
this.defFile = project.konanDefaultDefFile(libName)
|
||||
this.klib = outputDir.resolve("$libName.klib")
|
||||
@Internal override val toolRunner: KonanToolRunner = KonanInteropRunner(project)
|
||||
|
||||
override fun init(baseDir: File, outputName: String, target: KonanTarget) {
|
||||
super.init(baseDir, outputName, target)
|
||||
this.defFile = project.konanDefaultDefFile(outputName)
|
||||
}
|
||||
|
||||
// Output directories -----------------------------------------------------
|
||||
|
||||
@Internal override val outputDir = project.file(project.konanInteropOutputDir)
|
||||
|
||||
// TODO: Mark as deprecated
|
||||
lateinit var klib: File
|
||||
@Internal get
|
||||
internal set
|
||||
|
||||
override val artifact: File
|
||||
@OutputFile get() = klib
|
||||
|
||||
// TODO Annotations are not inherited
|
||||
override val artifactPath: String
|
||||
@Internal get() = artifact.canonicalPath
|
||||
|
||||
override val isLibrary: Boolean
|
||||
@Internal get() = true
|
||||
override val artifactSuffix: String
|
||||
@Internal get() = ".klib"
|
||||
|
||||
// Interop stub generator parameters -------------------------------------
|
||||
|
||||
@InputFile lateinit var defFile: File
|
||||
internal set
|
||||
|
||||
@Optional @Input var pkg: String? = null
|
||||
internal set
|
||||
|
||||
@Input lateinit var libName: String
|
||||
internal set
|
||||
|
||||
@Internal internal val klibDependencies_ = mutableListOf<KonanInteropConfig>()
|
||||
|
||||
@Input val compilerOpts = mutableListOf<String>()
|
||||
@Input val linkerOpts = mutableListOf<String>()
|
||||
@Input val extraOpts = mutableListOf<String>()
|
||||
|
||||
// TODO: Check if we can use only one FileCollection instead of set.
|
||||
@InputFiles val headers = mutableSetOf<FileCollection>()
|
||||
@InputFiles val linkFiles = mutableSetOf<FileCollection>()
|
||||
|
||||
@TaskAction
|
||||
fun exec() {
|
||||
outputDir.mkdirs()
|
||||
if (dumpParameters) dumpProperties(this@KonanInteropTask)
|
||||
KonanInteropRunner(project).run(buildArgs())
|
||||
}
|
||||
|
||||
protected fun buildArgs() = mutableListOf<String>().apply {
|
||||
override fun buildArgs() = mutableListOf<String>().apply {
|
||||
addArg("-properties", "${project.konanHome}/konan/konan.properties")
|
||||
|
||||
addArg("-o", klib.canonicalPath)
|
||||
addArg("-o", artifact.canonicalPath)
|
||||
|
||||
addArgIfNotNull("-target", target)
|
||||
addArgIfNotNull("-target", target.userName)
|
||||
addArgIfNotNull("-def", defFile.canonicalPath)
|
||||
addArgIfNotNull("-pkg", pkg)
|
||||
|
||||
addFileArgs("-h", headers)
|
||||
|
||||
addKey("-nodefaultlibs", noDefaultLibs)
|
||||
|
||||
compilerOpts.forEach {
|
||||
addArg("-copt", it)
|
||||
}
|
||||
@@ -108,105 +74,55 @@ open class KonanInteropTask: KonanBuildingTask() {
|
||||
addArg("-lopt", it)
|
||||
}
|
||||
|
||||
addArgs("-repo", libraries.repos.map { it.canonicalPath })
|
||||
|
||||
addFileArgs("-library", libraries.files)
|
||||
addArgs("-library", libraries.namedKlibs)
|
||||
addArgs("-library", libraries.artifacts.map { it.task.artifact.canonicalPath })
|
||||
addArgs("-repo", libraries.repos.map { it.canonicalPath })
|
||||
addArgs("-library", libraries.artifacts.map { it.artifact.canonicalPath })
|
||||
|
||||
addKey("-nodefaultlibs", noDefaultLibs)
|
||||
|
||||
addAll(extraOpts)
|
||||
}
|
||||
}
|
||||
|
||||
open class KonanInteropConfig(
|
||||
configName: String,
|
||||
project: Project
|
||||
): KonanBuildingConfig(configName, project) {
|
||||
// region DSL.
|
||||
|
||||
override fun getName() = configName
|
||||
|
||||
// Child tasks ------------------------------------------------------------
|
||||
|
||||
// TODO: Remove dummy tasks and properties in 0.5
|
||||
val generateStubsTask: KonanInteropTask
|
||||
get() = throw NotImplementedError("This property is not supported now. Use interopProcessingTask instead.")
|
||||
val compileStubsTask: KonanCompileTask
|
||||
get() = throw NotImplementedError("This property is not supported now. Use interopProcessingTask instead.")
|
||||
val compileStubsConfig: KonanCompileConfig
|
||||
get() = throw NotImplementedError("This property is not supported now.")
|
||||
|
||||
open class DummyTask: DefaultTask() {
|
||||
var replacementName: String = ""
|
||||
|
||||
@TaskAction
|
||||
fun doAction(): Unit = throw NotImplementedError("This task is not supported now. Use $replacementName instead.")
|
||||
}
|
||||
|
||||
// Task to process the library and generate stubs
|
||||
val interopProcessingTask: KonanInteropTask = project.tasks.create(
|
||||
"process${name.capitalize()}Interop",
|
||||
KonanInteropTask::class.java) {
|
||||
it.init(name)
|
||||
it.group = BasePlugin.BUILD_GROUP
|
||||
it.description = "Generates a klib for the Kotlin/Native interop '$name'"
|
||||
}
|
||||
|
||||
// TODO: Rename tasks
|
||||
override val task: KonanInteropTask
|
||||
get() = interopProcessingTask
|
||||
|
||||
init {
|
||||
val dummyGenerateStubsTask = project.tasks.create(
|
||||
"gen${name.capitalize()}InteropStubs",
|
||||
DummyTask::class.java) { it.replacementName = interopProcessingTask.name }
|
||||
val dummyCompileStubsTask = project.tasks.create(
|
||||
"compile${name.capitalize()}InteropStubs",
|
||||
DummyTask::class.java) { it.replacementName = interopProcessingTask.name }
|
||||
}
|
||||
|
||||
// DSL methods ------------------------------------------------------------
|
||||
|
||||
fun defFile(file: Any) = with(interopProcessingTask) {
|
||||
override fun defFile(file: Any) {
|
||||
defFile = project.file(file)
|
||||
}
|
||||
|
||||
fun pkg(value: String) = with(interopProcessingTask) {
|
||||
override fun pkg(value: String) {
|
||||
pkg = value
|
||||
}
|
||||
|
||||
fun compilerOpts(vararg values: String) = with(interopProcessingTask) {
|
||||
override fun compilerOpts(vararg values: String) {
|
||||
compilerOpts.addAll(values)
|
||||
}
|
||||
|
||||
fun header(file: Any) = headers(file)
|
||||
fun headers(vararg files: Any) = with(interopProcessingTask) {
|
||||
override fun header(file: Any) = headers(file)
|
||||
override fun headers(vararg files: Any) {
|
||||
headers.add(project.files(files))
|
||||
}
|
||||
fun headers(files: FileCollection) = with(interopProcessingTask) {
|
||||
override fun headers(files: FileCollection) {
|
||||
headers.add(files)
|
||||
}
|
||||
|
||||
fun includeDirs(vararg values: Any) = with(interopProcessingTask) {
|
||||
override fun includeDirs(vararg values: Any) {
|
||||
compilerOpts.addAll(values.map { "-I${project.file(it).canonicalPath}" })
|
||||
}
|
||||
|
||||
fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
|
||||
fun linkerOpts(values: List<String>) = with(interopProcessingTask) {
|
||||
override fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
|
||||
override fun linkerOpts(values: List<String>) {
|
||||
linkerOpts.addAll(values)
|
||||
}
|
||||
|
||||
fun link(vararg files: Any) = with(interopProcessingTask) {
|
||||
override fun link(vararg files: Any) {
|
||||
linkFiles.add(project.files(files))
|
||||
}
|
||||
fun link(files: FileCollection) = with(interopProcessingTask) {
|
||||
override fun link(files: FileCollection) {
|
||||
linkFiles.add(files)
|
||||
}
|
||||
|
||||
fun dependsOn(dependency: Any) = interopProcessingTask.dependsOn(dependency)
|
||||
// Other.
|
||||
|
||||
fun extraOpts(vararg values: Any) = extraOpts(values.asList())
|
||||
fun extraOpts(values: List<Any>) {
|
||||
values.mapTo(interopProcessingTask.extraOpts) { it.toString() }
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
|
||||
+8
-7
@@ -1,24 +1,25 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin.test
|
||||
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import spock.lang.Specification
|
||||
|
||||
|
||||
class DefaultSpecification extends BaseKonanSpecification {
|
||||
|
||||
def 'Plugin should build a project without additional settings'() {
|
||||
when:
|
||||
def project = KonanInteropProject.create(projectDirectory) { KonanInteropProject it ->
|
||||
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
|
||||
it.buildFile.write("""
|
||||
plugins { id 'konan' }
|
||||
konanInterop { stdio {} }
|
||||
konanArtifacts { main {} }
|
||||
konanArtifacts {
|
||||
interop('stdio')
|
||||
program('main')
|
||||
}
|
||||
""".stripIndent())
|
||||
it.generateDefFile("stdio.def", "")
|
||||
it.generateSrcFile("main.kt")
|
||||
}
|
||||
def result = project.createRunner().withArguments('build').build()
|
||||
|
||||
|
||||
then:
|
||||
!result.tasks.collect { it.outcome }.contains(TaskOutcome.FAILED)
|
||||
}
|
||||
|
||||
+59
-101
@@ -2,15 +2,12 @@ package org.jetbrains.kotlin.gradle.plugin.test
|
||||
|
||||
import org.gradle.testkit.runner.BuildResult
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import spock.lang.IgnoreIf
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
class IncrementalSpecification extends BaseKonanSpecification {
|
||||
|
||||
Tuple buildTwice(KonanInteropProject project, Closure change) {
|
||||
Tuple buildTwice(KonanProject project, Closure change) {
|
||||
def runner = project.createRunner().withArguments('build')
|
||||
def firstResult = runner.build()
|
||||
change(project)
|
||||
@@ -19,16 +16,10 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
}
|
||||
|
||||
Tuple buildTwice(Closure change) {
|
||||
return buildTwice(KonanInteropProject.create(projectDirectory), change)
|
||||
return buildTwice(KonanProject.createWithInterop(projectDirectory), change)
|
||||
}
|
||||
|
||||
Tuple buildTwiceEmpty(Closure change) {
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory)
|
||||
project.generateSrcFile("main.kt")
|
||||
return buildTwice(project, change)
|
||||
}
|
||||
|
||||
Boolean noRecompilationHappened(KonanInteropProject project, BuildResult firstResult, BuildResult secondResult) {
|
||||
Boolean noRecompilationHappened(KonanProject project, BuildResult firstResult, BuildResult secondResult) {
|
||||
return project.with {
|
||||
firstResult.tasks.collect { it.path }.containsAll(buildingTasks) &&
|
||||
firstResult.taskPaths(TaskOutcome.SUCCESS).containsAll(buildingTasks) &&
|
||||
@@ -38,7 +29,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
}
|
||||
}
|
||||
|
||||
Boolean onlyRecompilationHappened(KonanInteropProject project, BuildResult firstResult, BuildResult secondResult) {
|
||||
Boolean onlyRecompilationHappened(KonanProject project, BuildResult firstResult, BuildResult secondResult) {
|
||||
return project.with {
|
||||
firstResult.taskPaths(TaskOutcome.SUCCESS).containsAll(buildingTasks) &&
|
||||
secondResult.taskPaths(TaskOutcome.SUCCESS).containsAll(compilationTasks) &&
|
||||
@@ -46,7 +37,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
}
|
||||
}
|
||||
|
||||
Boolean recompilationAndInteropProcessingHappened(KonanInteropProject project, BuildResult firstResult, BuildResult secondResult) {
|
||||
Boolean recompilationAndInteropProcessingHappened(KonanProject project, BuildResult firstResult, BuildResult secondResult) {
|
||||
return project.with {
|
||||
firstResult.taskPaths(TaskOutcome.SUCCESS).containsAll(buildingTasks) &&
|
||||
secondResult.taskPaths(TaskOutcome.SUCCESS).containsAll(buildingTasks)
|
||||
@@ -64,7 +55,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
|
||||
def 'Source change should cause only recompilation'() {
|
||||
when:
|
||||
def results = buildTwice { KonanInteropProject project ->
|
||||
def results = buildTwice { KonanProject project ->
|
||||
project.srcFiles[0].append("\n // Some change in the source file")
|
||||
}
|
||||
|
||||
@@ -75,7 +66,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
|
||||
def 'Def-file change should cause recompilation and interop reprocessing'() {
|
||||
when:
|
||||
def results = buildTwice { KonanInteropProject project ->
|
||||
def results = buildTwice { KonanProject project ->
|
||||
project.defFiles[0].append("\n # Some change in the def-file")
|
||||
}
|
||||
|
||||
@@ -85,7 +76,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
|
||||
def 'Compilation is up-to-date if there is no changes in empty project'() {
|
||||
when:
|
||||
def results = buildTwiceEmpty {}
|
||||
def results = buildTwice {}
|
||||
|
||||
then:
|
||||
noRecompilationHappened(*results)
|
||||
@@ -94,8 +85,8 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
@Unroll("#parameter change for a compilation task should cause only recompilation")
|
||||
def 'Parameter changes should cause only recompilaton'() {
|
||||
when:
|
||||
def results = buildTwiceEmpty { KonanInteropProject project ->
|
||||
project.addCompilationSetting("main", parameter, value)
|
||||
def results = buildTwice { KonanProject project ->
|
||||
project.addSetting("main", parameter, value)
|
||||
}
|
||||
|
||||
then:
|
||||
@@ -103,83 +94,81 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
|
||||
|
||||
where:
|
||||
parameter | value
|
||||
"outputDir" | "'build/new/outputDir'"
|
||||
"produce" | "'library'"
|
||||
"enableOptimization" | "()"
|
||||
"linkerOpts" | "'--help'"
|
||||
"languageVersion" | "'1.2'"
|
||||
"apiVersion" | "'1.0'"
|
||||
"enableAssertions" | "()"
|
||||
"enableDebug" | "true"
|
||||
"outputName" | "'foo'"
|
||||
"extraOpts" | "'--time'"
|
||||
parameter | value
|
||||
"baseDir" | "'build/new/outputDir'"
|
||||
"enableOptimizations" | "true"
|
||||
"linkerOpts" | "'--help'"
|
||||
"languageVersion" | "'1.2'"
|
||||
"apiVersion" | "'1.0'"
|
||||
"enableAssertions" | "true"
|
||||
"enableDebug" | "true"
|
||||
"outputName" | "'foo'"
|
||||
"extraOpts" | "'--time'"
|
||||
"noDefaultLibs" | "true"
|
||||
}
|
||||
|
||||
def 'inputFiles change for a compilation task should cause only recompilation'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory) { KonanInteropProject it ->
|
||||
it.generateSrcFile('main.kt')
|
||||
def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it ->
|
||||
it.generateSrcFile(["src", "foo", "kotlin"], 'bar.kt', """
|
||||
fun main(args: Array<String>) { println("Hello!") }
|
||||
""".stripIndent())
|
||||
}
|
||||
def results = buildTwice(project) { KonanInteropProject it ->
|
||||
it.addCompilationSetting("main", "inputFiles", "project.fileTree('src/foo/kotlin')")
|
||||
def results = buildTwice(project) { KonanProject it ->
|
||||
it.addSetting("main", "inputFiles", "project.fileTree('src/foo/kotlin')")
|
||||
}
|
||||
|
||||
then:
|
||||
onlyRecompilationHappened(*results)
|
||||
}
|
||||
|
||||
@Unroll("#parameter change for a compilation task should cause only recompilation")
|
||||
def 'Library changes should cause only recompilaton'() {
|
||||
def 'Library change for a compilation task should cause only recompilation'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory) { KonanInteropProject it ->
|
||||
it.generateSrcFile('main.kt')
|
||||
def project = KonanProject.create(projectDirectory) { KonanProject it ->
|
||||
it.generateSrcFile(["src", "lib", "kotlin"], "lib.kt", "fun bar() { println(\"Hello!\") }")
|
||||
it.buildFile.append("""
|
||||
konanArtifacts {
|
||||
lib {
|
||||
library('lib') {
|
||||
inputFiles fileTree('src/lib/kotlin')
|
||||
produce '$produce'
|
||||
}
|
||||
}
|
||||
""".stripIndent())
|
||||
}
|
||||
def results = buildTwice(project) { KonanInteropProject it ->
|
||||
it.addCompilationSetting("main", parameter, "konanArtifacts['lib'].compilationTask.artifactPath")
|
||||
def results = buildTwice(project) { KonanProject it ->
|
||||
it.addLibraryToArtifact("main", 'lib')
|
||||
}
|
||||
|
||||
then:
|
||||
onlyRecompilationHappened(*results)
|
||||
|
||||
where:
|
||||
parameter | produce
|
||||
"library" | "library"
|
||||
"nativeLibrary" | "bitcode"
|
||||
}
|
||||
|
||||
def 'useInterop change for a compilation task should cause only recompilation'() {
|
||||
def 'Library changes should cause only recompilaton'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory) { KonanInteropProject it ->
|
||||
it.generateSrcFile('main.kt')
|
||||
it.buildFile.append("konanInterop { foo {} }\n")
|
||||
it.generateDefFile("foo.def", "")
|
||||
def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it ->
|
||||
it.generateSrcFile(["src", "lib", "kotlin"], "lib.kt", "fun bar() { println(\"Hello!\") }")
|
||||
it.buildFile.append("""
|
||||
konanArtifacts {
|
||||
bitcode('lib') {
|
||||
inputFiles fileTree('src/lib/kotlin')
|
||||
}
|
||||
}
|
||||
""".stripIndent())
|
||||
}
|
||||
def results = buildTwice(project) { KonanInteropProject it ->
|
||||
it.addCompilationSetting("main", "useInterop", "'foo'")
|
||||
def results = buildTwice(project) { KonanProject it ->
|
||||
it.addSetting("main", "nativeLibrary", "compileKonanLib${KonanProject.HOST.capitalize()}.artifact")
|
||||
}
|
||||
|
||||
then:
|
||||
onlyRecompilationHappened(*results)
|
||||
}
|
||||
|
||||
// TODO: Test library for incremental compilation.
|
||||
|
||||
@Unroll("#parameter change for an interop task should cause recompilation and interop reprocessing")
|
||||
def 'Parameter change for an interop task should cause recompilation and interop reprocessing'() {
|
||||
when:
|
||||
def results = buildTwiceEmpty { KonanInteropProject project ->
|
||||
project.addInteropSetting("stdio", parameter, value)
|
||||
def results = buildTwice { KonanProject project ->
|
||||
project.addSetting("stdio", parameter, value)
|
||||
}
|
||||
|
||||
then:
|
||||
@@ -192,15 +181,15 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
"linkerOpts" | "'--help'"
|
||||
"includeDirs" | "'src'"
|
||||
"extraOpts" | "'-shims', 'false'"
|
||||
"noDefaultLibs" | "true"
|
||||
}
|
||||
|
||||
def 'defFile change for an interop task should cause recompilation and interop reprocessing'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory)
|
||||
project.generateSrcFile('main.kt')
|
||||
def project = KonanProject.createWithInterop(projectDirectory)
|
||||
def defFile = project.generateDefFile("foo.def", "#some content")
|
||||
def results = buildTwice(project) { KonanInteropProject it ->
|
||||
it.addInteropSetting("stdio", "defFile", defFile)
|
||||
def results = buildTwice(project) { KonanProject it ->
|
||||
it.addSetting("stdio", "defFile", defFile)
|
||||
}
|
||||
|
||||
then:
|
||||
@@ -209,11 +198,10 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
|
||||
def 'header change for an interop task should cause recompilation and interop reprocessing'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory)
|
||||
project.generateSrcFile('main.kt')
|
||||
def project = KonanProject.createWithInterop(projectDirectory)
|
||||
def header = project.generateSrcFile('header.h', "#define CONST 1")
|
||||
def results = buildTwice(project) { KonanInteropProject it ->
|
||||
it.addInteropSetting("stdio", "headers", header)
|
||||
def results = buildTwice(project) { KonanProject it ->
|
||||
it.addSetting("stdio", "headers", header)
|
||||
}
|
||||
|
||||
then:
|
||||
@@ -222,22 +210,19 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
|
||||
def 'link change for an interop task should cause recompilation and interop reprocessing'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory) { KonanInteropProject it ->
|
||||
it.generateSrcFile('main.kt')
|
||||
def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it ->
|
||||
it.generateSrcFile(["src", "lib", "kotlin"], 'lib.kt', 'fun foo() { println(42) }')
|
||||
it.buildFile.append("""
|
||||
konanArtifacts {
|
||||
lib {
|
||||
bitcode('lib') {
|
||||
inputFiles fileTree('src/lib/kotlin')
|
||||
produce 'bitcode'
|
||||
noMain()
|
||||
}
|
||||
}
|
||||
""".stripIndent())
|
||||
}
|
||||
def results = buildTwice(project) { KonanInteropProject it ->
|
||||
it.addInteropSetting("stdio", "interopProcessingTask.dependsOn", "konanArtifacts['lib'].compilationTask")
|
||||
it.addInteropSetting("stdio", "link", "files(konanArtifacts['lib'].compilationTask.artifactPath)")
|
||||
def results = buildTwice(project) { KonanProject it ->
|
||||
it.addSetting("stdio", "dependsOn", "konanArtifacts.lib.${KonanProject.HOST}")
|
||||
it.addSetting("stdio", "link", "files(konanArtifacts.lib.${KonanProject.HOST}.artifactPath)")
|
||||
}
|
||||
|
||||
then:
|
||||
@@ -246,11 +231,10 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
|
||||
def 'konan version change should cause recompilation and interop reprocessing'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory) { KonanInteropProject it ->
|
||||
it.generateSrcFile('main.kt')
|
||||
def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it ->
|
||||
it.propertiesFile.append("konan.version=0.3\n")
|
||||
}
|
||||
def results = buildTwice(project) { KonanInteropProject it ->
|
||||
def results = buildTwice(project) { KonanProject it ->
|
||||
def newText = it.propertiesFile.text.replace('konan.version=0.3', 'konan.version=0.4')
|
||||
it.propertiesFile.write(newText)
|
||||
}
|
||||
@@ -259,31 +243,5 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
recompilationAndInteropProcessingHappened(*results)
|
||||
}
|
||||
|
||||
@IgnoreIf({ System.getProperty('os.name').toLowerCase().contains('windows') })
|
||||
def 'target change should cause recompilation and interop reprocessing'() {
|
||||
when:
|
||||
def newTarget
|
||||
if (System.getProperty('os.name').toLowerCase().contains('linux')) {
|
||||
newTarget = "raspberrypi"
|
||||
} else if (System.getProperty('os.name').toLowerCase().contains('mac')) {
|
||||
newTarget = "iphone"
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown host platform")
|
||||
}
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory) { KonanInteropProject it ->
|
||||
it.generateSrcFile('main.kt')
|
||||
it.propertiesFile.append("konan.build.targets=all\n")
|
||||
}
|
||||
|
||||
|
||||
def results = buildTwice(project) { KonanInteropProject it ->
|
||||
project.addCompilationSetting("main", "target", "'$newTarget'")
|
||||
project.addInteropSetting("stdio", "target", "'$newTarget'")
|
||||
}
|
||||
|
||||
then:
|
||||
recompilationAndInteropProcessingHappened(*results)
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
||||
+142
-189
@@ -1,6 +1,8 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin.test
|
||||
|
||||
import org.gradle.testkit.runner.GradleRunner
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.konan.target.TargetManager
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
@@ -9,6 +11,9 @@ import java.nio.file.Paths
|
||||
class KonanProject {
|
||||
|
||||
static String DEFAULT_ARTIFACT_NAME = 'main'
|
||||
static String DEFAULT_INTEROP_NAME = "stdio"
|
||||
|
||||
static String HOST = TargetManager.host.userName
|
||||
|
||||
File projectDir
|
||||
Path projectPath
|
||||
@@ -21,15 +26,40 @@ class KonanProject {
|
||||
File settingsFile
|
||||
|
||||
Set<File> srcFiles = []
|
||||
Set<File> defFiles = []
|
||||
|
||||
List<String> compilationTasks = []
|
||||
String downloadTask = ":downloadKonanCompiler"
|
||||
List<String> interopTasks = []
|
||||
List<String> compilationTasks = []
|
||||
String downloadTask = ":checkKonanCompiler"
|
||||
|
||||
List<String> getBuildingTasks() { return compilationTasks }
|
||||
List<String> targets
|
||||
|
||||
List<String> getBuildingTasks() { return compilationTasks + interopTasks }
|
||||
List<String> getKonanTasks() { return getBuildingTasks() + downloadTask }
|
||||
|
||||
protected KonanProject(File projectDir) {
|
||||
// TODO: Must be an enum.
|
||||
static String PROGRAM = "program"
|
||||
static String LIBRARY = "library"
|
||||
static String BITCODE = "bitcode"
|
||||
static String INTEROP = "interop"
|
||||
|
||||
static String DEFAULT_SRC_CONTENT = """
|
||||
fun main(args: Array<String>) {
|
||||
println(42)
|
||||
}
|
||||
"""
|
||||
|
||||
static String DEFAULT_DEF_CONTENT = """
|
||||
headers = stdio.h
|
||||
""".stripIndent()
|
||||
|
||||
protected KonanProject(File projectDir){
|
||||
this(projectDir, [HOST])
|
||||
}
|
||||
|
||||
protected KonanProject(File projectDir, List<String> targets) {
|
||||
this.projectDir = projectDir
|
||||
this.targets = targets
|
||||
projectPath = projectDir.toPath()
|
||||
konanBuildDir = projectPath.resolve('build/konan').toFile()
|
||||
def konanHome = System.getProperty("konan.home")
|
||||
@@ -76,6 +106,7 @@ class KonanProject {
|
||||
/** Creates a folder for project source files (src/main/kotlin). */
|
||||
void generateFolders() {
|
||||
createSubDir("src", "main", "kotlin")
|
||||
createSubDir("src", "main", "c_interop")
|
||||
}
|
||||
|
||||
/** Generates a build.gradle file in the root project directory with the given content. */
|
||||
@@ -94,22 +125,20 @@ class KonanProject {
|
||||
* Generates a build.gradle file in root project directory with the default content (see below)
|
||||
* and fills the compilationTasks array.
|
||||
*
|
||||
* plugins { id 'konan' }
|
||||
* plugins { id 'konan' }
|
||||
*
|
||||
* konanArtifacts {
|
||||
* $DEFAULT_ARTIFACT_NAME { }
|
||||
* }
|
||||
* konanArtifacts {
|
||||
* program('$DEFAULT_ARTIFACT_NAME')
|
||||
* }
|
||||
*/
|
||||
File generateBuildFile() {
|
||||
def result = generateBuildFile("""
|
||||
plugins { id 'konan' }
|
||||
|
||||
konanArtifacts {
|
||||
$DEFAULT_ARTIFACT_NAME { }
|
||||
}
|
||||
konanTargets = [${targets.collect { "'$it'" }.join(", ")}]
|
||||
""".stripIndent()
|
||||
)
|
||||
compilationTasks = [defaultCompilationTask(), ":compileKonan", ":build"]
|
||||
compilationTasks = [":compileKonan", ":build"]
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -139,151 +168,7 @@ class KonanProject {
|
||||
* }
|
||||
*/
|
||||
File generateSrcFile(String fileName) {
|
||||
return generateSrcFile(fileName, """
|
||||
fun main(args: Array<String>) {
|
||||
println(42)
|
||||
}
|
||||
""".stripIndent()
|
||||
)
|
||||
}
|
||||
|
||||
/** Generates gradle.properties file with the konan.home property set. */
|
||||
File generatePropertiesFile(String konanHome) {
|
||||
propertiesFile = createFile(projectPath, "gradle.properties", "konan.home=$konanHome\n")
|
||||
return propertiesFile
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given setting of the given project extension.
|
||||
* In other words adds the following string in the build file:
|
||||
*
|
||||
* $container['$section'].$parameter $value
|
||||
*/
|
||||
protected void addSetting(String container, String section, String parameter, String value) {
|
||||
buildFile.append("$container['$section'].$parameter $value\n")
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given setting of the given project extension using the path of the file as a value.
|
||||
* In other words adds the following string in the build file:
|
||||
*
|
||||
* $container['$section'].$parameter ${value.canonicalPath.replace(\, \\)}
|
||||
*/
|
||||
protected void addSetting(String container, String section, String parameter, File value) {
|
||||
addSetting(container, section, parameter, "'${value.canonicalPath.replace('\\', '\\\\')}'")
|
||||
}
|
||||
|
||||
/** Sets the given setting of the given konanArtifact */
|
||||
void addCompilationSetting(String artifactName = DEFAULT_ARTIFACT_NAME, String parameter, String value) {
|
||||
addSetting("konanArtifacts", artifactName, parameter, value)
|
||||
}
|
||||
|
||||
/** Sets the given setting of the given konanArtifact using the path of the file as a value. */
|
||||
void addCompilationSetting(String artifactName = DEFAULT_ARTIFACT_NAME, String parameter, File value) {
|
||||
addSetting("konanArtifacts", artifactName, parameter, value)
|
||||
}
|
||||
|
||||
/** Returns the path of compileKonan... task for the default artifact. */
|
||||
String defaultCompilationTask() {
|
||||
return compilationTask(DEFAULT_ARTIFACT_NAME)
|
||||
}
|
||||
|
||||
/** Returns the path of compileKonan... task for the artifact specified. */
|
||||
String compilationTask(String artifactName) {
|
||||
return ":compileKonan${artifactName.capitalize()}"
|
||||
}
|
||||
|
||||
String defaultArtifactConfig() {
|
||||
return artifactConfig(DEFAULT_ARTIFACT_NAME)
|
||||
}
|
||||
|
||||
String artifactConfig(String artifactName) {
|
||||
return "konanArtifacts['$artifactName']"
|
||||
}
|
||||
|
||||
/** Creates a project with default build and source files. */
|
||||
static KonanProject create(File projectDir) {
|
||||
return createEmpty(projectDir) {
|
||||
it.generateSrcFile("main.kt")
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a project with default build and source files. */
|
||||
static KonanProject create(File projectDir, Closure config) {
|
||||
def result = create(projectDir)
|
||||
config(result)
|
||||
return result
|
||||
}
|
||||
|
||||
/** Creates a project with the default build file and without any source files. */
|
||||
static KonanProject createEmpty(File projectDir) {
|
||||
def result = new KonanProject(projectDir)
|
||||
result.with {
|
||||
generateFolders()
|
||||
generateBuildFile()
|
||||
generatePropertiesFile(konanHome)
|
||||
generateSettingsFile("")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** Creates a project with the default build file and without any source files. */
|
||||
static KonanProject createEmpty(File projectDir, Closure config) {
|
||||
def result = createEmpty(projectDir)
|
||||
config(result)
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class KonanInteropProject extends KonanProject {
|
||||
|
||||
static String DEFAULT_INTEROP_NAME = "stdio"
|
||||
|
||||
Set<File> defFiles = []
|
||||
|
||||
List<String> interopTasks = []
|
||||
|
||||
protected KonanInteropProject(File projectDir) { super(projectDir) }
|
||||
|
||||
List<String> getBuildingTasks() { return interopTasks + compilationTasks }
|
||||
|
||||
/** Creates a folder for project source files (src/main/kotlin) and a folder for def-files (src/main/c_interop). */
|
||||
void generateFolders() {
|
||||
super.generateFolders()
|
||||
createSubDir("src", "main", "c_interop")
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a build.gradle file in root project directory with the default content (see below)
|
||||
* and fills compilationTasks and interopTasks arrays.
|
||||
*
|
||||
* plugins { id 'konan' }
|
||||
*
|
||||
* konanInterop {
|
||||
* $DEFAULT_INTEROP_NAME { }
|
||||
* }
|
||||
*
|
||||
* konanArtifacts {
|
||||
* $DEFAULT_ARTIFACT_NAME { useInterop '$DEFAULT_INTEROP_NAME' }
|
||||
}
|
||||
*/
|
||||
File generateBuildFile() {
|
||||
def result = generateBuildFile("""
|
||||
plugins { id 'konan' }
|
||||
|
||||
konanInterop {
|
||||
$DEFAULT_INTEROP_NAME { }
|
||||
}
|
||||
|
||||
konanArtifacts {
|
||||
$DEFAULT_ARTIFACT_NAME { useInterop '$DEFAULT_INTEROP_NAME' }
|
||||
}
|
||||
""".stripIndent()
|
||||
)
|
||||
interopTasks = [defaultInteropProcessingTask()]
|
||||
compilationTasks = [defaultCompilationTask(), ":compileKonan", ":build"]
|
||||
return result
|
||||
return generateSrcFile(fileName, DEFAULT_SRC_CONTENT)
|
||||
}
|
||||
|
||||
/** Creates a def-file with the given name and content in src/main/c_interop directory and adds it to defFiles. */
|
||||
@@ -300,69 +185,137 @@ class KonanInteropProject extends KonanProject {
|
||||
* headers = stdio.h stdlib.h string.h
|
||||
*/
|
||||
File generateDefFile(String fileName = "${DEFAULT_INTEROP_NAME}.def") {
|
||||
return generateDefFile(fileName, """
|
||||
headers = stdio.h stdlib.h string.h
|
||||
""".stripIndent()
|
||||
)
|
||||
return generateDefFile(fileName, DEFAULT_DEF_CONTENT)
|
||||
}
|
||||
|
||||
/** Sets the given setting of the given konanInterop */
|
||||
void addInteropSetting(String interopName = DEFAULT_INTEROP_NAME, String parameter, String value) {
|
||||
addSetting("konanInterop", interopName, parameter, value)
|
||||
/** Generates gradle.properties file with the konan.home property set. */
|
||||
File generatePropertiesFile(String konanHome) {
|
||||
propertiesFile = createFile(projectPath, "gradle.properties", "konan.home=$konanHome\n")
|
||||
return propertiesFile
|
||||
}
|
||||
|
||||
/** Sets the given setting of the given konanInterop using the path of the file as a value. */
|
||||
void addInteropSetting(String interopName = DEFAULT_INTEROP_NAME, String parameter, File value) {
|
||||
addSetting("konanInterop", interopName, parameter, value)
|
||||
/**
|
||||
* Sets the given setting of the given project extension.
|
||||
* In other words adds the following string in the build file:
|
||||
*
|
||||
* $container.$section.$parameter $value
|
||||
*/
|
||||
protected void addSetting(String container, String section, String parameter, String value) {
|
||||
buildFile.append("$container.$section.$parameter $value\n")
|
||||
}
|
||||
|
||||
String defaultInteropProcessingTask() {
|
||||
return interopProcessingTask(DEFAULT_INTEROP_NAME)
|
||||
/**
|
||||
* Sets the given setting of the given project extension using the path of the file as a value.
|
||||
* In other words adds the following string in the build file:
|
||||
*
|
||||
* $container.$section.$parameter ${value.canonicalPath.replace(\, \\)}
|
||||
*/
|
||||
protected void addSetting(String container, String section, String parameter, File value) {
|
||||
addSetting(container, section, parameter, "'${value.canonicalPath.replace('\\', '\\\\')}'")
|
||||
}
|
||||
|
||||
String interopProcessingTask(String interopName) {
|
||||
return ":process${interopName.capitalize()}Interop"
|
||||
/** Sets the given setting of the given konanArtifact */
|
||||
void addSetting(String artifactName = DEFAULT_ARTIFACT_NAME, String parameter, String value) {
|
||||
addSetting("konanArtifacts", artifactName, parameter, value)
|
||||
}
|
||||
|
||||
/** Sets the given setting of the given konanArtifact using the path of the file as a value. */
|
||||
void addSetting(String artifactName = DEFAULT_ARTIFACT_NAME, String parameter, File value) {
|
||||
addSetting("konanArtifacts", artifactName, parameter, value)
|
||||
}
|
||||
|
||||
void addLibraryToArtifact(String artifactName = DEFAULT_ARTIFACT_NAME, String library = DEFAULT_INTEROP_NAME) {
|
||||
addLibraryToArtifactCustom(artifactName, "artifact '$library'")
|
||||
}
|
||||
|
||||
void addLibraryToArtifactCustom(String artifactName = DEFAULT_ARTIFACT_NAME, String closureContent) {
|
||||
buildFile.append("konanArtifacts.${artifactName}.libraries { $closureContent }\n")
|
||||
}
|
||||
|
||||
/** Returns the path of compileKonan... task for the default artifact. */
|
||||
String defaultCompilationTask(String target = HOST) {
|
||||
return compilationTask(DEFAULT_ARTIFACT_NAME, target)
|
||||
}
|
||||
|
||||
String defaultInteropTask(String target = HOST) {
|
||||
return compilationTask(DEFAULT_INTEROP_NAME, target)
|
||||
}
|
||||
|
||||
/** Returns the path of compileKonan... task for the artifact specified. */
|
||||
String compilationTask(String artifactName, String target = HOST) {
|
||||
return ":compileKonan${artifactName.capitalize()}${target.capitalize()}"
|
||||
}
|
||||
|
||||
String defaultCompilationConfig() {
|
||||
return artifactConfig(DEFAULT_ARTIFACT_NAME)
|
||||
}
|
||||
|
||||
String defaultInteropConfig() {
|
||||
return interopConfig(DEFAULT_INTEROP_NAME)
|
||||
return artifactConfig(DEFAULT_INTEROP_NAME)
|
||||
}
|
||||
|
||||
String interopConfig(String interopName) {
|
||||
return "konanInterop[$interopName]"
|
||||
String artifactConfig(String artifactName) {
|
||||
return "konanArtifacts.$artifactName"
|
||||
}
|
||||
|
||||
/** Creates a project with default build, source and def files. */
|
||||
static KonanInteropProject create(File projectDir) {
|
||||
return createEmpty(projectDir) {
|
||||
it.generateSrcFile("main.kt")
|
||||
it.generateDefFile("${DEFAULT_INTEROP_NAME}.def")
|
||||
void addCompilerArtifact(String name, String content = "", String type = PROGRAM) {
|
||||
def newTasks = targets.collect { compilationTask(name, it) } + ":compileKonan${name.capitalize()}".toString()
|
||||
if (type == INTEROP) {
|
||||
defFiles += generateDefFile("${name}.def", content)
|
||||
interopTasks += newTasks
|
||||
} else {
|
||||
srcFiles += generateSrcFile(projectPath.resolve("src/$name/kotlin"), "source.kt", content)
|
||||
compilationTasks += newTasks
|
||||
}
|
||||
buildFile.append("konanArtifacts { $type('$name') }\n")
|
||||
}
|
||||
|
||||
/** Creates a project with default build and source files. */
|
||||
static KonanProject create(File projectDir, List<String> targets = [HOST]) {
|
||||
return createEmpty(projectDir, targets) { KonanProject p ->
|
||||
p.addCompilerArtifact(DEFAULT_ARTIFACT_NAME, DEFAULT_SRC_CONTENT)
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a project with default build, source and def files. */
|
||||
static KonanInteropProject create(File projectDir, Closure config) {
|
||||
def result = create(projectDir)
|
||||
// TODO: Add 'type parameter'
|
||||
|
||||
/** Creates a project with default build and source files. */
|
||||
static KonanProject create(File projectDir, List<String> targets = [HOST], Closure config) {
|
||||
def result = create(projectDir, targets)
|
||||
config(result)
|
||||
return result
|
||||
}
|
||||
|
||||
/** Creates a project with the default build file, without any source and with an empty def file. */
|
||||
static KonanInteropProject createEmpty(File projectDir) {
|
||||
def result = new KonanInteropProject(projectDir)
|
||||
static KonanProject createWithInterop(File projectDir, List<String> targets = [HOST]) {
|
||||
return create(projectDir, targets) { KonanProject p ->
|
||||
p.addCompilerArtifact(DEFAULT_INTEROP_NAME, DEFAULT_DEF_CONTENT, INTEROP)
|
||||
p.addLibraryToArtifact()
|
||||
}
|
||||
}
|
||||
|
||||
static KonanProject createWithInterop(File projectDir, List<String> targets = [HOST], Closure config) {
|
||||
def result = createWithInterop(projectDir, targets)
|
||||
config(result)
|
||||
return result
|
||||
}
|
||||
|
||||
/** Creates a project with the default build file and without any source files. */
|
||||
static KonanProject createEmpty(File projectDir, List<String> targets = [HOST]) {
|
||||
def result = new KonanProject(projectDir, targets)
|
||||
result.with {
|
||||
generateFolders()
|
||||
generateBuildFile()
|
||||
generatePropertiesFile(konanHome)
|
||||
generateDefFile("${DEFAULT_INTEROP_NAME}.def", "")
|
||||
generateSettingsFile("")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** Creates a project with the default build file, without any source and with an empty def file. */
|
||||
static KonanInteropProject createEmpty(File projectDir, Closure config) {
|
||||
def result = createEmpty(projectDir)
|
||||
/** Creates a project with the default build file and without any source files. */
|
||||
static KonanProject createEmpty(File projectDir, List<String> targets = [HOST], Closure config) {
|
||||
def result = createEmpty(projectDir, targets)
|
||||
config(result)
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
+33
-15
@@ -1,26 +1,36 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin.test
|
||||
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.konan.target.TargetManager
|
||||
|
||||
class PathSpecification extends BaseKonanSpecification {
|
||||
|
||||
boolean fileExists(KonanProject project, String path) {
|
||||
project.konanBuildDir.toPath().resolve(path).toFile().exists()
|
||||
}
|
||||
|
||||
def 'Plugin should create all necessary directories'() {
|
||||
when:
|
||||
def project = KonanInteropProject.create(projectDirectory)
|
||||
def project = KonanProject.createWithInterop(projectDirectory)
|
||||
project.addCompilerArtifact("lib", "fun foo() {}", KonanProject.LIBRARY)
|
||||
project.addCompilerArtifact("bit", "fun bar() {}", KonanProject.BITCODE)
|
||||
println(project.buildFile.text)
|
||||
def result = project.createRunner().withArguments('build').build()
|
||||
|
||||
then:
|
||||
def konan = project.konanBuildDir
|
||||
new File("$konan/bin").listFiles().findAll {
|
||||
File it -> it.file && it.name.matches('^main\\.[^.]+')
|
||||
project.konanBuildDir.toPath().resolve("bin/$KonanProject.HOST").toFile().listFiles().findAll {
|
||||
File it -> it.file && it.name.matches("^${KonanProject.DEFAULT_ARTIFACT_NAME}\\.[^.]+")
|
||||
}.size() > 0
|
||||
def klib = new File("$konan/c_interop/stdio.klib")
|
||||
klib.exists() && klib.file
|
||||
|
||||
fileExists(project, "libs/$KonanProject.HOST/${KonanProject.DEFAULT_INTEROP_NAME}.klib")
|
||||
fileExists(project, "libs/$KonanProject.HOST/lib.klib")
|
||||
fileExists(project, "bitcode/$KonanProject.HOST/bit.bc")
|
||||
}
|
||||
|
||||
def 'Plugin should stop building if the compiler classpath is empty'() {
|
||||
when:
|
||||
def project = KonanProject.createEmpty(projectDirectory)
|
||||
def project = KonanProject.create(projectDirectory)
|
||||
project.propertiesFile.write("konan.home=${projectDirectory.canonicalPath}}")
|
||||
def result = project.createRunner().withArguments('build').buildAndFail()
|
||||
|
||||
@@ -30,29 +40,37 @@ class PathSpecification extends BaseKonanSpecification {
|
||||
|
||||
def 'Plugin should stop building if the stub generator classpath is empty'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory)
|
||||
def project = KonanProject.createWithInterop(projectDirectory)
|
||||
project.propertiesFile.write("konan.home=${projectDirectory.canonicalPath}}")
|
||||
def result = project.createRunner().withArguments('build').buildAndFail()
|
||||
|
||||
then:
|
||||
result.task(project.defaultInteropProcessingTask()).outcome == TaskOutcome.FAILED
|
||||
result.task(project.compilationTask(KonanProject.DEFAULT_INTEROP_NAME)).outcome == TaskOutcome.FAILED
|
||||
}
|
||||
|
||||
def 'Plugin should remove custom output directories'() {
|
||||
when:
|
||||
def customOutputDir = projectDirectory.toPath().resolve("foo").toFile()
|
||||
def project = KonanInteropProject.create(projectDirectory) { KonanProject it ->
|
||||
it.addCompilationSetting("outputDir", customOutputDir)
|
||||
def project = KonanProject.create(projectDirectory) { KonanProject it ->
|
||||
it.addSetting("baseDir", customOutputDir)
|
||||
}
|
||||
|
||||
def res1 = project.createRunner().withArguments("build").build()
|
||||
def customDirExistsAfterBuild = customOutputDir.exists()
|
||||
def artifactExistsAfterBuild = customOutputDir.toPath()
|
||||
.resolve("${KonanProject.HOST}").toFile()
|
||||
.listFiles()
|
||||
.findAll { it.name.matches("^${KonanProject.DEFAULT_ARTIFACT_NAME}\\.[^.]+") }.size() > 0
|
||||
|
||||
def res2 = project.createRunner().withArguments("clean").build()
|
||||
def customDirDoesntNotExistAfterClean = !customOutputDir.exists()
|
||||
def artifactDoesntNotExistAfterClean = customOutputDir.toPath()
|
||||
.resolve("${KonanProject.HOST}").toFile()
|
||||
.listFiles()
|
||||
.findAll { it.name.matches("^${KonanProject.DEFAULT_ARTIFACT_NAME}\\.[^.]+") }.isEmpty()
|
||||
|
||||
then:
|
||||
res1.taskPaths(TaskOutcome.SUCCESS).containsAll(project.buildingTasks)
|
||||
res2.taskPaths(TaskOutcome.SUCCESS).contains(":clean")
|
||||
customDirExistsAfterBuild
|
||||
customDirDoesntNotExistAfterClean
|
||||
artifactExistsAfterBuild
|
||||
artifactDoesntNotExistAfterClean
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,13 +40,13 @@ class RegressionSpecification extends BaseKonanSpecification {
|
||||
def 'KT-20192'() {
|
||||
when:
|
||||
def project = KonanProject.createEmpty(getProjectDirectory()) { KonanProject prj ->
|
||||
prj.generateSrcFile("main.kt", """
|
||||
prj.addCompilerArtifact(KonanProject.DEFAULT_ARTIFACT_NAME,"""
|
||||
external fun foo()
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
foo()
|
||||
}
|
||||
""")
|
||||
""", KonanProject.PROGRAM)
|
||||
}
|
||||
def result = project.createRunner().withArguments('build').buildAndFail()
|
||||
|
||||
|
||||
+7
-151
@@ -9,15 +9,13 @@ class TaskSpecification extends BaseKonanSpecification {
|
||||
|
||||
def 'Configs should allow user to add dependencies to them'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory) { KonanInteropProject it ->
|
||||
it.generateSrcFile("main.kt")
|
||||
}
|
||||
def project = KonanProject.createWithInterop(projectDirectory)
|
||||
project.buildFile.append("""
|
||||
task beforeInterop(type: DefaultTask) { doLast { println("Before Interop") } }
|
||||
task beforeCompilation(type: DefaultTask) { doLast { println("Before compilation") } }
|
||||
""".stripIndent())
|
||||
project.addInteropSetting("dependsOn", "beforeInterop")
|
||||
project.addCompilationSetting("dependsOn", "beforeCompilation")
|
||||
project.addSetting(KonanProject.DEFAULT_INTEROP_NAME,"dependsOn", "beforeInterop")
|
||||
project.addSetting("dependsOn", "beforeCompilation")
|
||||
def result = project.createRunner().withArguments('build').build()
|
||||
|
||||
then:
|
||||
@@ -27,62 +25,10 @@ class TaskSpecification extends BaseKonanSpecification {
|
||||
beforeCompilation != null && beforeCompilation.outcome == TaskOutcome.SUCCESS
|
||||
}
|
||||
|
||||
def 'Compilation config should work with konanInterop from another project'() {
|
||||
when:
|
||||
def rootProject = KonanProject.create(projectDirectory) { KonanProject it ->
|
||||
it.buildFile.append("evaluationDependsOn(':interop')\n")
|
||||
it.createFile("settings.gradle", "include ':interop'")
|
||||
it.addCompilationSetting("useInterop", "project(':interop').konanInterop['interop']")
|
||||
}
|
||||
def interopProjectDir = rootProject.createSubDir("interop")
|
||||
def interopProject = KonanInteropProject.createEmpty(interopProjectDir) { KonanInteropProject it ->
|
||||
it.generateBuildFile("""
|
||||
apply plugin: 'konan'
|
||||
|
||||
konanInterop {
|
||||
interop { }
|
||||
}
|
||||
""".stripIndent())
|
||||
it.interopTasks = [":interop:processInteropInterop"]
|
||||
it.generateDefFile("interop.def")
|
||||
}
|
||||
def result = rootProject.createRunner().withArguments("build").build()
|
||||
|
||||
then:
|
||||
result.taskPaths(TaskOutcome.SUCCESS).containsAll(rootProject.compilationTasks + interopProject.interopTasks)
|
||||
}
|
||||
|
||||
def 'Compilation should support interop parameters changing after `useInterop` call'() {
|
||||
when:
|
||||
def project = KonanInteropProject.create(projectDirectory)
|
||||
project.addInteropSetting("linkerOpts", "'-lpthread'")
|
||||
project.buildFile.append("""
|
||||
task printArgs {
|
||||
dependsOn 'build'
|
||||
doLast {
|
||||
println(konanArtifacts['$project.DEFAULT_ARTIFACT_NAME'].compilationTask.linkerOpts)
|
||||
konanArtifacts['$project.DEFAULT_ARTIFACT_NAME'].compilationTask.libraries.each { println it.files }
|
||||
konanArtifacts['$project.DEFAULT_ARTIFACT_NAME'].compilationTask.nativeLibraries.each { println it.files }
|
||||
}
|
||||
}
|
||||
""".stripIndent())
|
||||
def result = project.createRunner().withArguments('printArgs').build()
|
||||
|
||||
then:
|
||||
result.task(":printArgs") != null
|
||||
result.task(":printArgs").outcome == TaskOutcome.SUCCESS
|
||||
def expectedKlibPath = project.konanBuildDir.toPath()
|
||||
.resolve("c_interop${File.separator}stdio.klib")
|
||||
.toFile().canonicalPath
|
||||
def ls = System.lineSeparator()
|
||||
result.output.contains("[-lpthread]$ls[$expectedKlibPath]".stripIndent().trim())
|
||||
}
|
||||
|
||||
def 'Compiler should print time measurements if measureTime flag is set'() {
|
||||
when:
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory)
|
||||
project.generateSrcFile("main.kt")
|
||||
project.addCompilationSetting("measureTime", "true")
|
||||
def project = KonanProject.create(projectDirectory)
|
||||
project.addSetting("measureTime", "true")
|
||||
def result = project.createRunner().withArguments('build').build()
|
||||
|
||||
then:
|
||||
@@ -92,7 +38,7 @@ class TaskSpecification extends BaseKonanSpecification {
|
||||
}
|
||||
|
||||
BuildResult failOnPropertyAccess(String property) {
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory)
|
||||
def project = KonanProject.createWithInterop(projectDirectory)
|
||||
project.buildFile.append("""
|
||||
task testTask(type: DefaultTask) {
|
||||
doLast {
|
||||
@@ -104,7 +50,7 @@ class TaskSpecification extends BaseKonanSpecification {
|
||||
}
|
||||
|
||||
BuildResult failOnTaskAccess(String task) {
|
||||
def project = KonanInteropProject.createEmpty(projectDirectory)
|
||||
def project = KonanProject.createWithInterop(projectDirectory)
|
||||
project.buildFile.append("""
|
||||
task testTask(type: DefaultTask) {
|
||||
dependsOn $task
|
||||
@@ -112,94 +58,4 @@ class TaskSpecification extends BaseKonanSpecification {
|
||||
""".stripIndent())
|
||||
return project.createRunner().withArguments("testTask").buildAndFail()
|
||||
}
|
||||
|
||||
def 'Deprecated properties/tasks should generate exceptions'() {
|
||||
expect:
|
||||
failOnPropertyAccess("generateStubsTask")
|
||||
failOnPropertyAccess("compileStubsTask")
|
||||
failOnPropertyAccess("compileStubsConfig")
|
||||
failOnTaskAccess("gen${KonanInteropProject.DEFAULT_INTEROP_NAME.capitalize()}InteropStubs")
|
||||
failOnTaskAccess("compile${KonanInteropProject.DEFAULT_INTEROP_NAME.capitalize()}InteropStubs")
|
||||
}
|
||||
|
||||
def 'Compilation task should be able to use another project as a library (with artifact name)'() {
|
||||
when:
|
||||
def project = KonanProject.createEmpty(projectDirectory) { KonanProject prj ->
|
||||
prj.generateSrcFile("main.kt", """\
|
||||
external fun foo()
|
||||
fun main(args: Array<String>) = foo()
|
||||
""")
|
||||
prj.settingsFile.append("include ':foo'")
|
||||
}
|
||||
def subproject = KonanProject.createEmpty(project.projectPath.resolve("foo").toFile()) {
|
||||
KonanProject prj ->
|
||||
prj.generateSrcFile("foo.kt", "fun foo() { println(42) } ")
|
||||
prj.addCompilationSetting("produce", "'library'")
|
||||
def buildScript = prj.buildFile.text.replace("plugins { id 'konan' }", "apply plugin: 'konan'")
|
||||
prj.buildFile.write(buildScript)
|
||||
}
|
||||
project.addCompilationSetting("library", "findProject(':foo'), 'main'")
|
||||
|
||||
def result = project.createRunner().withArguments("build").build()
|
||||
def successfulTasks = project.buildingTasks + subproject.buildingTasks.collect { ":foo$it".toString() }
|
||||
|
||||
subproject.addCompilationSetting("produce", "'program'")
|
||||
def failedResult = project.createRunner().withArguments("build").buildAndFail()
|
||||
|
||||
|
||||
then:
|
||||
result.taskPaths(TaskOutcome.SUCCESS).containsAll(successfulTasks)
|
||||
}
|
||||
|
||||
def 'Compilation task should be able to use another project as a library (all klibs in the project)'() {
|
||||
when:
|
||||
def project = KonanProject.createEmpty(projectDirectory) { KonanProject prj ->
|
||||
prj.generateSrcFile("main.kt", """\
|
||||
external fun foo()
|
||||
external fun bar()
|
||||
fun baz() { println("baz") }
|
||||
fun main(args: Array<String>) { foo(); bar(); baz() }
|
||||
""".stripIndent())
|
||||
prj.settingsFile.append("include ':foo'")
|
||||
}
|
||||
def subproject = KonanProject.createEmpty(project.projectPath.resolve("foo").toFile()) {
|
||||
KonanProject prj ->
|
||||
def buildScript = prj.buildFile.text.replace("plugins { id 'konan' }", "apply plugin: 'konan'")
|
||||
prj.buildFile.write(buildScript)
|
||||
|
||||
// Default artifact: main, should be recognized as a library with foo function.
|
||||
prj.generateSrcFile("foo.kt", "fun foo() { println(42) } ")
|
||||
prj.addCompilationSetting("produce", "'library'")
|
||||
|
||||
prj.generateSrcFile(Paths.get("src", "bar" , "kotlin"),
|
||||
"bar.kt", "fun bar() { println(\"bar\") }")
|
||||
|
||||
prj.generateSrcFile(Paths.get("src", "baz", "kotlin"),
|
||||
"baz.kt", "fun baz() { println(\"another baz\") }")
|
||||
|
||||
prj.buildFile.append("""\
|
||||
konanArtifacts {
|
||||
// Should be recognized as a library.
|
||||
bar {
|
||||
inputDir "src/bar/kotlin"
|
||||
produce "library"
|
||||
}
|
||||
|
||||
// Should not be recognized as a library.
|
||||
baz {
|
||||
inputDir "src/baz/kotlin"
|
||||
produce "bitcode"
|
||||
}
|
||||
}
|
||||
""".stripIndent())
|
||||
|
||||
}
|
||||
project.addCompilationSetting("library", "findProject(':foo')")
|
||||
|
||||
def result = project.createRunner().withArguments("build").build()
|
||||
def successfulTasks = project.buildingTasks + subproject.buildingTasks.collect { ":foo$it".toString() }
|
||||
|
||||
then:
|
||||
result.taskPaths(TaskOutcome.SUCCESS).containsAll(successfulTasks)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user