From 80bec0898fc88e01fc1ef0be04fee177c781d3dd Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Wed, 12 Sep 2018 13:48:58 +0300 Subject: [PATCH] Download native compiler in configuration stage --- .../kotlin/gradle/NewMultiplatformIT.kt | 18 +++ .../compilerRunner/KotlinNativeToolRunner.kt | 14 +-- .../gradle/plugin/KotlinTargetConfigurator.kt | 8 -- .../gradle/plugin/mpp/kotlinCompilations.kt | 2 +- .../gradle/plugin/mpp/kotlinTargetPresets.kt | 22 ++-- .../kotlin/gradle/tasks/KotlinNativeTasks.kt | 102 +--------------- .../gradle/utils/NativeCompilerDownloader.kt | 111 ++++++++++++++++++ 7 files changed, 149 insertions(+), 128 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/NativeCompilerDownloader.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt index ff3c4241ec1..0857382fcb9 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt @@ -17,6 +17,7 @@ import org.junit.Test import java.util.jar.JarFile import java.util.zip.ZipFile import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertTrue class NewMultiplatformIT : BaseGradleIT() { @@ -607,4 +608,21 @@ class NewMultiplatformIT : BaseGradleIT() { assertTrue(output.contains("Dependent: Published print"), "No test output found") } } + + @Test + fun testNativeCompilerDownloading() { + // The plugin shouldn't download the K/N compiler if there is no corresponding targets in the project. + with(Project("new-mpp-lib-with-tests", gradleVersion)) { + build("tasks") { + assertSuccessful() + assertFalse(output.contains("Kotlin/Native distribution: ")) + } + } + with(Project("new-mpp-native-libraries", gradleVersion)) { + build("tasks") { + assertSuccessful() + assertTrue(output.contains("Kotlin/Native distribution: ")) + } + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinNativeToolRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinNativeToolRunner.kt index 65a98b85d4c..8fc1f15a976 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinNativeToolRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinNativeToolRunner.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.compilerRunner import org.gradle.api.Named import org.gradle.api.Project import org.gradle.api.file.FileCollection +import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.util.DependencyDirectories @@ -26,10 +27,9 @@ import org.jetbrains.kotlin.konan.util.DependencyDirectories /** Copied from Kotlin/Native repository. */ internal enum class KotlinNativeProjectProperty(val propertyName: String) { - KONAN_HOME ("org.jetbrains.kotlin.native.home"), + KONAN_HOME_OVERRIDE ("org.jetbrains.kotlin.native.home"), KONAN_JVM_ARGS ("org.jetbrains.kotlin.native.jvmArgs"), - KONAN_USE_ENVIRONMENT_VARIABLES("org.jetbrains.kotlin.native.useEnvironmentVariables"), - DOWNLOAD_COMPILER ("org.jetbrains.kotlin.native.download.compiler"), + KONAN_USE_ENVIRONMENT_VARIABLES("org.jetbrains.kotlin.native.useEnvironmentVariables") } internal fun Project.hasProperty(property: KotlinNativeProjectProperty) = hasProperty(property.propertyName) @@ -44,12 +44,10 @@ internal fun Project.getProperty(property: KotlinNativeProjectProperty) = findPr internal val Project.jvmArgs get() = (findProperty(KotlinNativeProjectProperty.KONAN_JVM_ARGS) as String?)?.split("\\s+".toRegex()).orEmpty() -// konanHome extension is set by downloadKonanCompiler task. internal val Project.konanHome: String - get() { - assert(hasProperty(KotlinNativeProjectProperty.KONAN_HOME)) - return project.file(getProperty(KotlinNativeProjectProperty.KONAN_HOME)).canonicalPath - } + get() = findProperty(KotlinNativeProjectProperty.KONAN_HOME_OVERRIDE)?.let { + file(it).absolutePath + } ?: NativeCompilerDownloader(project).compilerDirectory.absolutePath internal interface KonanToolRunner: Named { val mainClass: String diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt index 6da3784d499..1984f4f9255 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt @@ -36,7 +36,6 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile import org.jetbrains.kotlin.gradle.tasks.CInteropProcess -import org.jetbrains.kotlin.gradle.tasks.KonanCompilerDownloadTask import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName @@ -412,7 +411,6 @@ open class KotlinNativeTargetConfigurator( onlyIf { testExecutableProperty.get().exists() } inputs.file(testExecutableProperty) dependsOn(testExecutableLinkTask) - dependsOnCompilerDownloading() } tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).apply { dependsOn(testTask) @@ -507,7 +505,6 @@ open class KotlinNativeTargetConfigurator( addCompilerPlugins() dependsOn(compilation.compileKotlinTaskName) - dependsOnCompilerDownloading() linkAll.dependsOn(this) } @@ -577,7 +574,6 @@ open class KotlinNativeTargetConfigurator( registerOutputFiles(klibOutputDirectory(compilation)) addCompilerPlugins() - dependsOnCompilerDownloading() compilation.output.tryAddClassesDir { project.files(this.outputFile).builtBy(this) } @@ -609,7 +605,6 @@ open class KotlinNativeTargetConfigurator( "of target '${konanTarget.name}'." enabled = compilation.target.konanTarget.enabledOnCurrentHost - dependsOnCompilerDownloading() val interopOutput = project.files(outputFileProvider).builtBy(this) with(compilation) { project.dependencies.add(compileDependencyConfigurationName, interopOutput) @@ -655,9 +650,6 @@ open class KotlinNativeTargetConfigurator( configureCInterops(target) } - private fun Task.dependsOnCompilerDownloading() = - dependsOn(KonanCompilerDownloadTask.KONAN_DOWNLOAD_TASK_NAME) - object NativeArtifactFormat { const val KLIB = "org.jetbrains.kotlin.klib" } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt index 2120c5bd385..3b4ee7c1271 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt @@ -290,7 +290,7 @@ class KotlinNativeCompilation( internal val binaryTasks = mutableMapOf, KotlinNativeCompile>() // Native-specific DSL. - val extraOpts = mutableListOf() + var extraOpts = mutableListOf() fun extraOpts(vararg values: Any) = extraOpts(values.toList()) fun extraOpts(values: List) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt index 9f7b1f2eec3..6119e0e8bb3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt @@ -20,9 +20,8 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask import org.jetbrains.kotlin.gradle.tasks.AndroidTasksProvider -import org.jetbrains.kotlin.gradle.tasks.KonanCompilerDownloadTask -import org.jetbrains.kotlin.gradle.tasks.KonanCompilerDownloadTask.Companion.KONAN_DOWNLOAD_TASK_NAME import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider +import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget @@ -245,26 +244,27 @@ class KotlinNativeTargetPreset( override fun getName(): String = name - private fun createCompilerDownloadingTask() = with(project) { - if (!hasProperty(KotlinNativeProjectProperty.KONAN_HOME)) { - setProperty(KotlinNativeProjectProperty.KONAN_HOME, KonanCompilerDownloadTask.compilerDirectory) - setProperty(KotlinNativeProjectProperty.DOWNLOAD_COMPILER, true) + private fun setupNativeCompiler() = with(project) { + if (!hasProperty(KotlinNativeProjectProperty.KONAN_HOME_OVERRIDE)) { + NativeCompilerDownloader(this).downloadIfNeeded() + logger.info("Kotlin/Native distribution: $konanHome") + } else { + logger.info("User-provided Kotlin/Native distribution: $konanHome") } - tasks.maybeCreate(KONAN_DOWNLOAD_TASK_NAME, KonanCompilerDownloadTask::class.java) } private fun stdlib(target: KonanTarget): FileCollection = with(project) { - files("${konanHome}/klib/common/stdlib").builtBy(createCompilerDownloadingTask()) + files("${konanHome}/klib/common/stdlib") } private fun platformLibs(target: KonanTarget): FileCollection = with(project) { files(provider { - file("${project.konanHome}/klib/platform/${target.name}").listFiles { file -> file.isDirectory } ?: emptyArray() - }).builtBy(createCompilerDownloadingTask()) + file("${konanHome}/klib/platform/${target.name}").listFiles { file -> file.isDirectory } ?: emptyArray() + }) } override fun createTarget(name: String): KotlinNativeTarget { - createCompilerDownloadingTask() + setupNativeCompiler() val result = KotlinNativeTarget(project, konanTarget).apply { targetName = name diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt index e660140f211..5c532b57e7f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultCInteropSettings import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.defaultSourceSetName import org.jetbrains.kotlin.gradle.plugin.mpp.isMainCompilation +import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.KonanVersionImpl import org.jetbrains.kotlin.konan.MetaVersion @@ -77,8 +78,6 @@ private fun File.providedByCompiler(project: Project): Boolean = open class KotlinNativeCompile : AbstractCompile() { init { - dependsOn(KonanCompilerDownloadTask.KONAN_DOWNLOAD_TASK_NAME) - @Suppress("LeakingThis") setDestinationDir(project.provider { val output = outputFile.get() @@ -155,7 +154,7 @@ open class KotlinNativeCompile : AbstractCompile() { // endregion. val kotlinNativeVersion: String - @Input get() = KonanCompilerDownloadTask.compilerVersion.toString() + @Input get() = NativeCompilerDownloader(project).compilerVersion.toString() // We manually register this property as output file or directory depending on output kind. @Internal @@ -333,100 +332,3 @@ open class CInteropProcess: DefaultTask() { KonanInteropRunner(project).run(args) } } - -open class KonanCompilerDownloadTask : DefaultTask() { - - internal companion object { - - val simpleOsName: String = HostManager.simpleOsName() - - val compilerDirectory: File - get() = DependencyDirectories.localKonanDir.resolve("kotlin-native-$simpleOsName-$compilerVersion") - - // TODO: Support project property for Kotlin/Native compiler version - val compilerVersion: KonanVersion = KonanVersionImpl(MetaVersion.RELEASE, 0, 9, 0) - - internal const val BASE_DOWNLOAD_URL = "https://download.jetbrains.com/kotlin/native/builds" - const val KONAN_DOWNLOAD_TASK_NAME = "checkNativeCompiler" - } - - private val useZip = HostManager.hostIsMingw - - private val archiveExtension - get() = if (useZip) { - "zip" - } else { - "tar.gz" - } - - private fun archiveFileTree(archive: File): FileTree = - if (useZip) { - project.zipTree(archive) - } else { - project.tarTree(archive) - } - - private fun setupRepo(url: String): ArtifactRepository { - return project.repositories.ivy { repo -> - repo.setUrl(url) - repo.layout("pattern") { - val layout = it as IvyPatternRepositoryLayout - layout.artifact("[artifact]-[revision].[ext]") - } - repo.metadataSources { - it.artifact() - } - } - } - - private fun removeRepo(repo: ArtifactRepository) { - project.repositories.remove(repo) - } - - private fun downloadAndExtract() { - val versionString = compilerVersion.toString() - - val url = buildString { - append("$BASE_DOWNLOAD_URL/") - append(if (compilerVersion.meta == MetaVersion.DEV) "dev/" else "releases/") - append("$versionString/") - append(simpleOsName) - } - - val repo = setupRepo(url) - - val compilerDependency = project.dependencies.create( - mapOf( - "name" to "kotlin-native-$simpleOsName", - "version" to versionString, - "ext" to archiveExtension - ) - ) - - val configuration = project.configurations.detachedConfiguration(compilerDependency) - val archive = configuration.files.single() - - logger.info("Use Kotlin/Native compiler archive: ${archive.absolutePath}") - logger.lifecycle("Unpacking Kotlin/Native compiler (version $versionString)...") - project.copy { - it.from(archiveFileTree(archive)) - it.into(DependencyDirectories.localKonanDir) - } - - removeRepo(repo) - } - - @TaskAction - fun checkCompiler() { - if (!project.hasProperty(KotlinNativeProjectProperty.DOWNLOAD_COMPILER)) { - val konanHome = project.getProperty(KotlinNativeProjectProperty.KONAN_HOME) - logger.info("Use a user-defined compiler path: $konanHome") - } else { - // TODO: Move Kotlin/Native Distribution class in the Big Kotlin repo and use it here. - if (KonanCompilerRunner(project).classpath.isEmpty) { - downloadAndExtract() - } - logger.info("Use Kotlin/Native distribution: $compilerDirectory") - } - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/NativeCompilerDownloader.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/NativeCompilerDownloader.kt new file mode 100644 index 00000000000..025b42708d2 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/NativeCompilerDownloader.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.utils + +import org.gradle.api.Project +import org.gradle.api.artifacts.repositories.ArtifactRepository +import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout +import org.gradle.api.file.FileTree +import org.gradle.api.logging.Logger +import org.jetbrains.kotlin.compilerRunner.KonanCompilerRunner +import org.jetbrains.kotlin.konan.KonanVersion +import org.jetbrains.kotlin.konan.KonanVersionImpl +import org.jetbrains.kotlin.konan.MetaVersion +import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.util.DependencyDirectories +import java.io.File + +class NativeCompilerDownloader(val project: Project) { + + internal companion object { + const val BASE_DOWNLOAD_URL = "https://download.jetbrains.com/kotlin/native/builds" + } + + private val logger: Logger + get() = project.logger + + private val simpleOsName: String + get() = HostManager.simpleOsName() + + private val useZip + get() = HostManager.hostIsMingw + + private val archiveExtension + get() = if (useZip) { + "zip" + } else { + "tar.gz" + } + + private fun archiveFileTree(archive: File): FileTree = + if (useZip) { + project.zipTree(archive) + } else { + project.tarTree(archive) + } + + private fun setupRepo(url: String): ArtifactRepository { + return project.repositories.ivy { repo -> + repo.setUrl(url) + repo.layout("pattern") { + val layout = it as IvyPatternRepositoryLayout + layout.artifact("[artifact]-[revision].[ext]") + } + repo.metadataSources { + it.artifact() + } + } + } + + private fun removeRepo(repo: ArtifactRepository) { + project.repositories.remove(repo) + } + + private fun downloadAndExtract() { + val versionString = compilerVersion.toString() + + val url = buildString { + append("$BASE_DOWNLOAD_URL/") + append(if (compilerVersion.meta == MetaVersion.DEV) "dev/" else "releases/") + append("$versionString/") + append(simpleOsName) + } + + val repo = setupRepo(url) + + val compilerDependency = project.dependencies.create( + mapOf( + "name" to "kotlin-native-$simpleOsName", + "version" to versionString, + "ext" to archiveExtension + ) + ) + + val configuration = project.configurations.detachedConfiguration(compilerDependency) + val archive = configuration.files.single() + + logger.info("Use Kotlin/Native compiler archive: ${archive.absolutePath}") + logger.lifecycle("Unpacking Kotlin/Native compiler (version $versionString)...") + project.copy { + it.from(archiveFileTree(archive)) + it.into(DependencyDirectories.localKonanDir) + } + + removeRepo(repo) + } + + val compilerDirectory: File + get() = DependencyDirectories.localKonanDir.resolve("kotlin-native-$simpleOsName-$compilerVersion") + + // TODO: Support project property for Kotlin/Native compiler version + val compilerVersion: KonanVersion = KonanVersionImpl(MetaVersion.RELEASE, 0, 9, 0) + + fun downloadIfNeeded() { + if (KonanCompilerRunner(project).classpath.isEmpty) { + downloadAndExtract() + } + } +} \ No newline at end of file