diff --git a/GRADLE_PLUGIN.md b/GRADLE_PLUGIN.md index 8d2bbd912c0..b93809e3b79 100644 --- a/GRADLE_PLUGIN.md +++ b/GRADLE_PLUGIN.md @@ -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' } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingTask.kt new file mode 100644 index 00000000000..b5e24539918 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingTask.kt @@ -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) = libraries(ConfigureUtil.configureUsing(closure)) + fun libraries(action: Action) = libraries { action.execute(this) } + fun libraries(configure: KonanLibrariesSpec.() -> Unit) { + task.libraries.configure() + // TODO: May be rework. + task.libraries.artifacts.forEach { + dependsOn(it.task) + } + } +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileTask.kt index 6a512842f8b..280344a7905 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileTask.kt @@ -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 @InputFiles get() = _inputFiles.takeIf { !it.isEmpty() } ?: listOf(project.konanDefaultSrcFiles) - @InputFiles val libraries = mutableSetOf() @InputFiles val nativeLibraries = mutableSetOf() @Input var produce = "program" internal set - @Internal val interops = mutableSetOf() + // TODO: Replace produce string with an enum. + override val isLibrary: Boolean + @Internal get() = produce == "library" @Input val extraOpts = mutableListOf() internal var _linkerOpts = mutableListOf() val linkerOpts: List - @Input get() = mutableListOf().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().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) { 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) = 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) { values.mapTo(compilationTask.extraOpts) { it.toString() } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropTask.kt index 33940e54487..a8bdd7d05ef 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropTask.kt @@ -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() @Input val compilerOpts = mutableListOf() @Input val linkerOpts = mutableListOf() @@ -67,11 +76,6 @@ open class KonanInteropTask: KonanTargetableTask() { @InputFiles val headers = mutableSetOf() @InputFiles val linkFiles = mutableSetOf() - 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) { diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanLibrariesSpec.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanLibrariesSpec.kt new file mode 100644 index 00000000000..f426dd20345 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanLibrariesSpec.kt @@ -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() // TODO: Immutable interface? + + @Input val namedKlibs = mutableSetOf() + + @Nested val artifacts = mutableSetOf() + + // TODO: input? + val repos = mutableSetOf().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) = 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) = 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) } + } +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanPlugin.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanPlugin.kt index f8923a0af87..f6dd7514e32 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanPlugin.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanPlugin.kt @@ -113,6 +113,12 @@ internal fun MutableList.addArg(parameter: String, value: String) { add(value) } +internal fun MutableList.addArgs(parameter: String, values: Iterable) { + values.forEach { + addArg(parameter, it) + } +} + internal fun MutableList.addArgIfNotNull(parameter: String, value: String?) { if (value != null) { addArg(parameter, value) @@ -144,8 +150,8 @@ internal fun MutableList.addListArg(parameter: String, values: List.dump() = flatMap { it.files }.joinToString(prefix = "[", - separator = ",\n${" ".repeat(22)}", postfix = "]") + fun Iterable.dump() = joinToString(prefix = "[", separator = ",\n${" ".repeat(22)}", postfix = "]") + fun Collection.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") ?: