gradle-plugin: Implement library block

This commit is contained in:
Ilya Matveev
2017-09-27 17:02:45 +03:00
committed by ilmat192
parent 86ee37b7e8
commit edfe593ea7
6 changed files with 319 additions and 106 deletions
+5
View File
@@ -298,6 +298,11 @@ For this project the task graph will be the following:
// Add the `anotherTask` to the stub generation task dependencies.
dependsOn anotherTask
// Add dependency on 'library' in the output klib (analogue of 'depends' parameter in a def-file)
// where 'library' is a konan interop or konan interop name.
klibDependsOn 'konanInteropName'
klibDependsOn konanInterop['foo']
// Pass additional command line options to the cinterop tool.
extraOpts '-shims', 'true'
}
@@ -0,0 +1,66 @@
package org.jetbrains.kotlin.gradle.plugin
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 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
abstract val isLibrary: Boolean
@Internal get
@Input
var dumpParameters: Boolean = false
val konanVersion
@Input get() = project.konanVersion
val konanHome
@Input get() = project.konanHome
@Nested
val libraries = KonanLibrariesSpec(project)
}
// 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)
}
}
}
@@ -16,24 +16,27 @@
package org.jetbrains.kotlin.gradle.plugin
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
/**
* A task compiling the target executable/library using Kotlin/Native compiler
*/
open class KonanCompileTask: KonanTargetableTask() {
open class KonanCompileTask: KonanBuildingTask() {
// Output artifact --------------------------------------------------------
internal lateinit var artifactName: String
@Internal get
@Internal lateinit var outputDir: File
@Internal override lateinit var outputDir: File
internal set
internal fun init(artifactName: String) {
@@ -42,16 +45,16 @@ open class KonanCompileTask: KonanTargetableTask() {
outputDir = project.file(project.konanCompilerOutputDir)
}
val artifactNamePath: String
protected val artifactNamePath: String
@Internal get() = "${outputDir.absolutePath}/$artifactName"
val artifactSuffix: String
protected val artifactSuffix: String
@Internal get() = produceSuffix(produce)
val artifactPath: String
override val artifactPath: String
@Internal get() = "$artifactNamePath$artifactSuffix"
val artifact: File
override val artifact: File
@OutputFile get() = project.file(artifactPath)
// Other compilation parameters -------------------------------------------
@@ -60,22 +63,20 @@ open class KonanCompileTask: KonanTargetableTask() {
val inputFiles: Collection<FileCollection>
@InputFiles get() = _inputFiles.takeIf { !it.isEmpty() } ?: listOf(project.konanDefaultSrcFiles)
@InputFiles val libraries = mutableSetOf<FileCollection>()
@InputFiles val nativeLibraries = mutableSetOf<FileCollection>()
@Input var produce = "program"
internal set
@Internal val interops = mutableSetOf<KonanInteropConfig>()
// TODO: Replace produce string with an enum.
override val isLibrary: Boolean
@Internal get() = produce == "library"
@Input val extraOpts = mutableListOf<String>()
internal var _linkerOpts = mutableListOf<String>()
val linkerOpts: List<String>
@Input get() = mutableListOf<String>().apply {
addAll(_linkerOpts)
interops.flatMapTo(this) { it.interopProcessingTask.linkerOpts }
}
@Input get() = _linkerOpts // TODO: use the original linkerOpts prop.
@Input var enableDebug = project.properties.containsKey("enableDebug") && project.properties["enableDebug"].toString().toBoolean()
internal set
@@ -97,19 +98,19 @@ open class KonanCompileTask: KonanTargetableTask() {
@Optional @Input var apiVersion : String? = null
internal set
@Input var dumpParameters: Boolean = false
val konanVersion
@Input get() = project.konanVersion
val konanHome
@Input get() = project.konanHome
// Task action ------------------------------------------------------------
protected fun buildArgs() = mutableListOf<String>().apply {
addArg("-output", artifactNamePath)
addFileArgs("-library", libraries)
// TODO: remove this repo
addArg("-repo", outputDir.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 })
addFileArgs("-nativelibrary", nativeLibraries)
addArg("-produce", produce)
@@ -143,12 +144,11 @@ open class KonanCompileTask: KonanTargetableTask() {
}
}
// TODO: check debug outputs
// TODO: Use +=/-= syntax for libraries and inputFiles
open class KonanCompileConfig(
val configName: String,
val project: Project,
taskNamePrefix: String = "compileKonan"): Named {
configName: String,
project: Project,
taskNamePrefix: String = "compileKonan"): KonanBuildingConfig(configName, project) {
override fun getName() = configName
@@ -160,21 +160,24 @@ open class KonanCompileConfig(
it.description = "Compiles the Kotlin/Native artifact '${this@KonanCompileConfig.name}'"
}
// DSL methods. Interop. --------------------------------------------------
override val task: KonanCompileTask
get() = compilationTask
fun useInterop(interop: KonanInteropConfig) = with(compilationTask) {
val generateStubsTask = interop.interopProcessingTask
dependsOn(generateStubsTask)
library(project.files(generateStubsTask.klib))
interops.add(interop)
private fun evaluationDependsOn(anotherProject: Project) {
if (anotherProject != project) {
project.evaluationDependsOn(anotherProject.path)
}
}
fun useInterop(interop: String) {
useInterop(project.konanInteropContainer.getByName(interop))
}
// 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) {
@@ -185,10 +188,13 @@ open class KonanCompileConfig(
}
}
// DSL. Input/output files
// DSL. Input/output files.
fun inputDir(dir: Any) = with(compilationTask) {
_inputFiles.add(project.fileTree(dir))
_inputFiles.add(project.fileTree(dir).apply {
include("**/*.kt")
exclude { it.file.startsWith(project.buildDir) }
})
}
fun inputFiles(vararg files: Any) = with(compilationTask) {
_inputFiles.add(project.files(files))
@@ -196,7 +202,7 @@ open class KonanCompileConfig(
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)
}
@@ -207,34 +213,22 @@ open class KonanCompileConfig(
// DSL. Libraries.
fun library(project: Project) {
this.project.evaluationDependsOn(project.path)
project.konanArtifactsContainer.asSequence()
.map { it.compilationTask }
.filter { it.produce == "library" }
.forEach {
compilationTask.dependsOn(it)
library(it.artifact)
}
}
@Deprecated("Use `libraries` block instead")
fun library(project: Project) = libraries { allArtifactsFrom(project) }
fun library(project: Project, artifactName: String) {
this.project.evaluationDependsOn(project.path)
val libraryCompilationTask = project.konanArtifactsContainer.getByName(artifactName).compilationTask
if (libraryCompilationTask.produce != "library") {
throw IllegalArgumentException("Artifact is not a library: $artifactName (in project: ${project.path})")
}
compilationTask.dependsOn(libraryCompilationTask)
library(libraryCompilationTask.artifact)
}
@Deprecated("Use `libraries` block instead")
fun library(project: Project, artifactName: String) = libraries { this.artifact(project, artifactName) }
fun library(lib: Any) = libraries(lib)
fun libraries(vararg libs: Any) = with(compilationTask) {
libraries.add(project.files(*libs))
}
fun libraries(libs: FileCollection) = with(compilationTask) {
libraries.add(libs)
}
@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) }
// DSL. Native libraries.
fun nativeLibrary(lib: Any) = nativeLibraries(lib)
fun nativeLibraries(vararg libs: Any) = with(compilationTask) {
@@ -251,10 +245,6 @@ open class KonanCompileConfig(
_linkerOpts.addAll(args)
}
fun target(tgt: String) = with(compilationTask) {
target = tgt
}
fun languageVersion(version: String) = with(compilationTask) {
languageVersion = version
}
@@ -295,12 +285,6 @@ open class KonanCompileConfig(
measureTime = value
}
fun dumpParameters(value: Boolean) = with(compilationTask) {
dumpParameters = value
}
fun dependsOn(dependency: Any) = compilationTask.dependsOn(dependency)
fun extraOpts(vararg values: Any) = extraOpts(values.asList())
fun extraOpts(values: List<Any>) {
values.mapTo(compilationTask.extraOpts) { it.toString() }
@@ -27,7 +27,7 @@ import java.io.File
/**
* A task executing cinterop tool with the given args and compiling the stubs produced by this tool.
*/
open class KonanInteropTask: KonanTargetableTask() {
open class KonanInteropTask: KonanBuildingTask() {
internal fun init(libName: String) {
dependsOn(project.konanCompilerDownloadTask)
@@ -38,10 +38,22 @@ open class KonanInteropTask: KonanTargetableTask() {
// Output directories -----------------------------------------------------
@Internal val outputDir = project.file(project.konanInteropOutputDir)
@Internal override val outputDir = project.file(project.konanInteropOutputDir)
// TODO: Mark as deprecated
lateinit var klib: File
@OutputFile get
@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
// Interop stub generator parameters -------------------------------------
@@ -54,10 +66,7 @@ open class KonanInteropTask: KonanTargetableTask() {
@Input lateinit var libName: String
internal set
@Input var dumpParameters = false
internal set
@Input var noDefaultLibs = false
internal set
@Internal internal val klibDependencies_ = mutableListOf<KonanInteropConfig>()
@Input val compilerOpts = mutableListOf<String>()
@Input val linkerOpts = mutableListOf<String>()
@@ -67,11 +76,6 @@ open class KonanInteropTask: KonanTargetableTask() {
@InputFiles val headers = mutableSetOf<FileCollection>()
@InputFiles val linkFiles = mutableSetOf<FileCollection>()
val konanVersion
@Input get() = project.konanVersion
val konanHome
@Input get() = project.konanHome
@TaskAction
fun exec() {
outputDir.mkdirs()
@@ -104,15 +108,19 @@ open class KonanInteropTask: KonanTargetableTask() {
addArg("-lopt", it)
}
addFileArgs("-library", libraries.files)
addArgs("-library", libraries.namedKlibs)
addArgs("-library", libraries.artifacts.map { it.task.artifact.canonicalPath })
addArgs("-repo", libraries.repos.map { it.canonicalPath })
addAll(extraOpts)
}
}
open class KonanInteropConfig(
val configName: String,
val project: Project
): Named {
configName: String,
project: Project
): KonanBuildingConfig(configName, project) {
override fun getName() = configName
@@ -142,6 +150,10 @@ open class KonanInteropConfig(
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",
@@ -161,10 +173,6 @@ open class KonanInteropConfig(
pkg = value
}
fun target(value: String) = with(interopProcessingTask) {
interopProcessingTask.target = value
}
fun compilerOpts(vararg values: String) = with(interopProcessingTask) {
compilerOpts.addAll(values)
}
@@ -193,15 +201,8 @@ open class KonanInteropConfig(
linkFiles.add(files)
}
fun dumpParameters(value: Boolean) = with(interopProcessingTask) {
dumpParameters = value
}
fun noDefaultLibs(flag: Boolean) = with(interopProcessingTask) {
noDefaultLibs = flag
}
fun dependsOn(dependency: Any) = interopProcessingTask.dependsOn(dependency)
// Other.
fun extraOpts(vararg values: Any) = extraOpts(values.asList())
fun extraOpts(values: List<Any>) {
@@ -0,0 +1,147 @@
package org.jetbrains.kotlin.gradle.plugin
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 java.io.File
/**
* 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?
@Input val namedKlibs = mutableSetOf<String>()
@Nested val artifacts = mutableSetOf<KonanBuildingConfig>()
// TODO: input?
val repos = mutableSetOf<File>().apply {
add(project.file(project.konanCompilerOutputDir))
add(project.file(project.konanInteropOutputDir))
}
// DSL Methods
/** Absolute path */
fun file(file: Any) = files.add(project.files(file))
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)
}
/** 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))
}
/** 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)
/** 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) }
}
/** 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 */
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))
/** 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) }
}
}
@@ -113,6 +113,12 @@ internal fun MutableList<String>.addArg(parameter: String, value: String) {
add(value)
}
internal fun MutableList<String>.addArgs(parameter: String, values: Iterable<String>) {
values.forEach {
addArg(parameter, it)
}
}
internal fun MutableList<String>.addArgIfNotNull(parameter: String, value: String?) {
if (value != null) {
addArg(parameter, value)
@@ -144,8 +150,8 @@ internal fun MutableList<String>.addListArg(parameter: String, values: List<Stri
}
internal fun dumpProperties(task: Task) {
fun Collection<FileCollection>.dump() = flatMap { it.files }.joinToString(prefix = "[",
separator = ",\n${" ".repeat(22)}", postfix = "]")
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 -> {
@@ -156,7 +162,11 @@ internal fun dumpProperties(task: Task) {
println("artifactPath : ${task.artifactPath}")
println("inputFiles : ${task.inputFiles.dump()}")
println("produce : ${task.produce}")
println("libraries : ${task.libraries.dump()}")
println("libraries : ${task.libraries.files.dump()}")
println(" : ${task.libraries.artifacts.map {
it.task.artifact.canonicalPath
}.dump()}")
println(" : ${task.libraries.namedKlibs.dump()}")
println("nativeLibraries : ${task.nativeLibraries.dump()}")
println("linkerOpts : ${task.linkerOpts}")
println("enableDebug : ${task.enableDebug}")
@@ -206,7 +216,7 @@ 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 = "downloadKonanCompiler"
internal const val KONAN_DOWNLOAD_TASK_NAME = "checkKonanCompiler"
internal val DEFAULT_KONAN_VERSION = Properties().apply {
load(KonanPlugin::class.java.getResourceAsStream("/META-INF/gradle-plugins/konan.properties") ?: