diff --git a/tools/kotlin-native-gradle-plugin/build.gradle b/tools/kotlin-native-gradle-plugin/build.gradle index be7efdbae11..3a498871501 100644 --- a/tools/kotlin-native-gradle-plugin/build.gradle +++ b/tools/kotlin-native-gradle-plugin/build.gradle @@ -92,7 +92,7 @@ dependencies { } test { - dependsOn ':cross_dist' + dependsOn ':dist' //testLogging.showStandardStreams = true systemProperty("konan.home", rootProject.file('dist').canonicalPath) } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanArtifactContainer.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanArtifactContainer.kt new file mode 100644 index 00000000000..efc4070e145 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanArtifactContainer.kt @@ -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::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) = + 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) = + 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) = + 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) = + 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)) + } + +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingConfig.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingConfig.kt new file mode 100644 index 00000000000..7962a93604d --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingConfig.kt @@ -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(private val name_: String, + type: Class, + val project: ProjectInternal, + instantiator: Instantiator) + : KonanBuildingSpec, Named, DefaultNamedDomainObjectSet(type, instantiator, { it.target.userName }) { + + override fun getName() = name_ + + protected val targetToTask = mutableMapOf() + + internal val aggregateBuildTask: Task + + val declaredTargets: Iterable = 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) = forEach { it.libraries(closure) } + override fun libraries(action: Action) = 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) = 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) = + target(targetString) { configureAction.execute(this) } + fun target(targetString: String, configureAction: Closure) = + target(targetString, ConfigureUtil.configureUsing(configureAction)) +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileConfig.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileConfig.kt new file mode 100644 index 00000000000..efa4f8353b1 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileConfig.kt @@ -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(name: String, + type: Class, + project: ProjectInternal, + instantiator: Instantiator) + : KonanBuildingConfig(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) = 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) = 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(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(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(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 +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropLibrary.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropLibrary.kt new file mode 100644 index 00000000000..3e050320283 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropLibrary.kt @@ -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(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) = forEach { it.linkerOpts(values) } + + override fun link(vararg files: Any) = forEach { it.link(*files) } + override fun link(files: FileCollection) = forEach { it.link(files) } +} \ No newline at end of file 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 index f426dd20345..6825c7fed4f 100644 --- 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 @@ -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() // TODO: Immutable interface? + @InputFiles val files = mutableSetOf() @Input val namedKlibs = mutableSetOf() - @Nested val artifacts = mutableSetOf() + @Internal val artifacts = mutableListOf() - // TODO: input? - val repos = mutableSetOf().apply { - add(project.file(project.konanCompilerOutputDir)) - add(project.file(project.konanInteropOutputDir)) - } + val artifactFiles: List + @InputFiles get() = artifacts.map { it.artifact } + + @Internal val explicitRepos = mutableSetOf() + + val repos: Set + @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) = 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, + 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) = 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) } } 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 f6dd7514e32..22081697108 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 @@ -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 - get() = extensions.getByName(KonanPlugin.ARTIFACTS_CONTAINER_NAME) as NamedDomainObjectContainer +internal val Project.konanArtifactsContainer: NamedDomainObjectContainer> + get() = extensions.getByName(KonanPlugin.ARTIFACTS_CONTAINER_NAME) + as NamedDomainObjectContainer> @Suppress("UNCHECKED_CAST") -internal val Project.konanInteropContainer: NamedDomainObjectContainer - get() = extensions.getByName(KonanPlugin.INTEROP_CONTAINER_NAME) as NamedDomainObjectContainer +internal val Project.konanTargets: MutableList + get() = extensions.extraProperties[KonanPlugin.TARGETS_EXTENSION_NAME] as MutableList -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 - get() = project.tasks.withType(KonanCompileTask::class.java).matching { - targetIsSupportedAndRequested(it) - } - -internal val Project.supportedInteropTasks: TaskCollection - 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 { - override fun create(name: String): KonanCompileConfig = KonanCompileConfig(name, project) -} - -internal class KonanInteropConfigFactory(val project: Project): NamedDomainObjectFactory { - override fun create(name: String): KonanInteropConfig = KonanInteropConfig(name, project) -} - -// Useful extensions and functions --------------------------------------- +// region Useful extensions and functions --------------------------------------- internal fun MutableList.addArg(parameter: String, value: String) { add(parameter) @@ -149,52 +149,58 @@ internal fun MutableList.addListArg(parameter: String, values: List.dump() = joinToString(prefix = "[", separator = ",\n${" ".repeat(22)}", postfix = "]") fun Collection.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 { + : Plugin { 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(' ')) } } } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanSpecs.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanSpecs.kt new file mode 100644 index 00000000000..c33236f3a73 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanSpecs.kt @@ -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) + fun libraries(action: Action) + 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) +} + +interface KonanCompileSpec: KonanBuildingSpec { + fun inputDir(dir: Any) + + fun inputFiles(vararg files: Any) + fun inputFiles(files: Collection) + + // DSL. Native libraries. + + fun nativeLibrary(lib: Any) + fun nativeLibraries(vararg libs: Any) + fun nativeLibraries(libs: FileCollection) + + // DSL. Other parameters. + + fun linkerOpts(args: List) + 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) + + fun link(vararg files: Any) + fun link(files: FileCollection) +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolRunner.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolRunner.kt index fac0c82fa72..4593eb3832c 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolRunner.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolRunner.kt @@ -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 diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolingModelBuilder.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolingModelBuilder.kt index 90dd02f2318..6884b2675ea 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolingModelBuilder.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolingModelBuilder.kt @@ -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) } } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanBaseTasks.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanBaseTasks.kt index b27292f94aa..52d76634398 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanBaseTasks.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanBaseTasks.kt @@ -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) = libraries(ConfigureUtil.configureUsing(closure)) + override fun libraries(action: Action) = libraries { action.execute(this) } + override fun libraries(configure: KonanLibrariesSpec.() -> Unit) { libraries.configure() } + + override fun noDefaultLibs(flag: Boolean) { + noDefaultLibs = flag + } +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanBuildingTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanBuildingTask.kt index b5e24539918..005d18a9eae 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanBuildingTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanBuildingTask.kt @@ -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() + val konanVersion @Input get() = project.konanVersion val konanHome @Input get() = project.konanHome - @Nested - val libraries = KonanLibrariesSpec(project) + protected abstract fun buildArgs(): List + + @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) { + 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) = 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/tasks/KonanCompileTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt index 280344a7905..52d63ed89df 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt @@ -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() - @Input var produce = "program" - internal set + @Input val linkerOpts = mutableListOf() - // 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() - - internal var _linkerOpts = mutableListOf() - val linkerOpts: List - @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().apply { - addArg("-output", artifactNamePath) + override fun buildArgs() = mutableListOf().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) { - 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) = 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) = 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) = linkerOpts(*args.toTypedArray()) - fun linkerOpts(vararg args: String) = with(compilationTask) { - _linkerOpts.addAll(args) + override fun linkerOpts(args: List) = 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) { - 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 +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompilerDownloadTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt similarity index 92% rename from tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompilerDownloadTask.kt rename to tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt index 939a3f34eb0..f5c5598777d 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompilerDownloadTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt @@ -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) diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanInteropTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanInteropTask.kt index a8bdd7d05ef..8209e7738a1 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanInteropTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanInteropTask.kt @@ -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() @Input val compilerOpts = mutableListOf() @Input val linkerOpts = mutableListOf() - @Input val extraOpts = mutableListOf() - // TODO: Check if we can use only one FileCollection instead of set. @InputFiles val headers = mutableSetOf() @InputFiles val linkFiles = mutableSetOf() - @TaskAction - fun exec() { - outputDir.mkdirs() - if (dumpParameters) dumpProperties(this@KonanInteropTask) - KonanInteropRunner(project).run(buildArgs()) - } - - protected fun buildArgs() = mutableListOf().apply { + override fun buildArgs() = mutableListOf().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) = with(interopProcessingTask) { + override fun linkerOpts(vararg values: String) = linkerOpts(values.toList()) + override fun linkerOpts(values: List) { 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) { - values.mapTo(interopProcessingTask.extraOpts) { it.toString() } - } + + // endregion } diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/DefaultSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/DefaultSpecification.groovy index 2a15704f576..f5ccaa0efa1 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/DefaultSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/DefaultSpecification.groovy @@ -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) } diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy index ca26913737d..726e73a64f7 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy @@ -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) { 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 } diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/KonanProject.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/KonanProject.groovy index de4abfa941a..92e70989b2d 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/KonanProject.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/KonanProject.groovy @@ -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 srcFiles = [] + Set defFiles = [] - List compilationTasks = [] - String downloadTask = ":downloadKonanCompiler" + List interopTasks = [] + List compilationTasks = [] + String downloadTask = ":checkKonanCompiler" - List getBuildingTasks() { return compilationTasks } + List targets + + List getBuildingTasks() { return compilationTasks + interopTasks } List 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) { + println(42) + } + """ + + static String DEFAULT_DEF_CONTENT = """ + headers = stdio.h + """.stripIndent() + + protected KonanProject(File projectDir){ + this(projectDir, [HOST]) + } + + protected KonanProject(File projectDir, List 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) { - 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 defFiles = [] - - List interopTasks = [] - - protected KonanInteropProject(File projectDir) { super(projectDir) } - - List 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 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 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 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 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 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 targets = [HOST], Closure config) { + def result = createEmpty(projectDir, targets) config(result) return result } + } \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/PathSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/PathSpecification.groovy index cc16b70dd5c..50ef3e246e8 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/PathSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/PathSpecification.groovy @@ -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 } } diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/RegressionSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/RegressionSpecification.groovy index 240a1bde32a..d81e541fab4 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/RegressionSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/RegressionSpecification.groovy @@ -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) { foo() } - """) + """, KonanProject.PROGRAM) } def result = project.createRunner().withArguments('build').buildAndFail() diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/TaskSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/TaskSpecification.groovy index c1decfeddfa..5561c2c6e15 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/TaskSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/TaskSpecification.groovy @@ -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) = 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) { 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) - } }