From d90b63d928bf69a49f8d101bad6ab1dd95eeefb6 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Mon, 22 Apr 2019 21:54:25 +0700 Subject: [PATCH] Improve logging in NativeCompilerDownloader Issue #KT-31158 fixed --- .../native/NativeCompilerDownloader.kt | 60 +++++++++++------ .../kotlin/gradle/utils/formattingUtils.kt | 66 +++++++++++++++++++ .../kotlin/gradle/utils/resourceUtils.kt | 33 +++++++++- 3 files changed, 137 insertions(+), 22 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/formattingUtils.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeCompilerDownloader.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeCompilerDownloader.kt index ada8ca877fa..9652cba26ac 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeCompilerDownloader.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeCompilerDownloader.kt @@ -22,22 +22,35 @@ import java.io.File class NativeCompilerDownloader( val project: Project, - val compilerVersion: KonanVersion = project.konanVersion + private val compilerVersion: KonanVersion = project.konanVersion ) { - internal companion object { - val DEFAULT_KONAN_VERSION: KonanVersion by lazy { + companion object { + internal val DEFAULT_KONAN_VERSION: KonanVersion by lazy { KonanVersion.fromString(loadPropertyFromResources("project.properties", "kotlin.native.version")) } - const val BASE_DOWNLOAD_URL = "https://download.jetbrains.com/kotlin/native/builds" + + private const val BASE_DOWNLOAD_URL = "https://download.jetbrains.com/kotlin/native/builds" } + val compilerDirectory: File + get() = DependencyDirectories.localKonanDir.resolve(dependencyNameWithVersion) + private val logger: Logger get() = project.logger private val simpleOsName: String get() = HostManager.simpleOsName() + private val dependencyName: String + get() = "kotlin-native-$simpleOsName" + + private val dependencyNameWithVersion: String + get() = "$dependencyName-$compilerVersion" + + private val dependencyFileName: String + get() = "$dependencyNameWithVersion.$archiveExtension" + private val useZip get() = HostManager.hostIsMingw @@ -55,9 +68,9 @@ class NativeCompilerDownloader( project.tarTree(archive) } - private fun setupRepo(url: String): ArtifactRepository { + private fun setupRepo(repoUrl: String): ArtifactRepository { return project.repositories.ivy { repo -> - repo.setUrl(url) + repo.setUrl(repoUrl) repo.layout("pattern") { val layout = it as IvyPatternRepositoryLayout layout.artifact("[artifact]-[revision].[ext]") @@ -73,41 +86,46 @@ class NativeCompilerDownloader( } private fun downloadAndExtract() { - val versionString = compilerVersion.toString() - - val url = buildString { + val repoUrl = buildString { append("$BASE_DOWNLOAD_URL/") append(if (compilerVersion.meta == MetaVersion.DEV) "dev/" else "releases/") - append("$versionString/") + append("$compilerVersion/") append(simpleOsName) } + val dependencyUrl = "$repoUrl/$dependencyFileName" - val repo = setupRepo(url) + val repo = setupRepo(repoUrl) val compilerDependency = project.dependencies.create( mapOf( - "name" to "kotlin-native-$simpleOsName", - "version" to versionString, + "name" to dependencyName, + "version" to compilerVersion.toString(), "ext" to archiveExtension ) ) val configuration = project.configurations.detachedConfiguration(compilerDependency) - val archive = configuration.files.single() + logger.lifecycle("\nPlease wait while Kotlin/Native compiler $compilerVersion is being installed.") + + val suffix = project.probeRemoteFileLength(dependencyUrl, probingTimeoutMs = 200)?.let { " (${formatContentLength(it)})" }.orEmpty() + logger.lifecycle("Download $dependencyUrl$suffix") + val archive = logger.lifecycleWithDuration("Download $dependencyUrl finished,") { + configuration.files.single() + } logger.kotlinInfo("Using Kotlin/Native compiler archive: ${archive.absolutePath}") - logger.lifecycle("Unpacking Kotlin/Native compiler (version $versionString)...") - project.copy { - it.from(archiveFileTree(archive)) - it.into(DependencyDirectories.localKonanDir) + + logger.lifecycle("Unpack Kotlin/Native compiler to $compilerDirectory") + logger.lifecycleWithDuration("Unpack Kotlin/Native compiler to $compilerDirectory finished,") { + project.copy { + it.from(archiveFileTree(archive)) + it.into(DependencyDirectories.localKonanDir) + } } removeRepo(repo) } - val compilerDirectory: File - get() = DependencyDirectories.localKonanDir.resolve("kotlin-native-$simpleOsName-$compilerVersion") - fun downloadIfNeeded() { if (KonanCompilerRunner(project).classpath.isEmpty) { downloadAndExtract() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/formattingUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/formattingUtils.kt new file mode 100644 index 00000000000..b7a7ff987e4 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/formattingUtils.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2019 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.logging.Logger + +internal fun formatDuration(milliseconds: Long): String { + var ms: Long = milliseconds + var s: Long = 0 + var m: Long = 0 + var h: Long = 0 + + if (ms >= 1000) { + s = ms / 1000 + ms %= 1000 + if (s >= 60) { + m = s / 60 + s %= 60 + if (m >= 60) { + h = m / 60 + m %= 60 + } + } + } + + return buildString { + if (h > 0) append(h).append(" h ") + if (m > 0 || isNotEmpty()) append(m).append(" m ") + if (s > 0 || isNotEmpty()) append(s).append(" s ") + if (ms > 0 || isNotEmpty()) append(ms).append(" ms") + } +} + +internal inline fun Logger.lifecycleWithDuration(messagePrefix: String, action: () -> T): T { + val startTime = System.currentTimeMillis() + val result = action() + val finishTime = System.currentTimeMillis() + + lifecycle("$messagePrefix took ${formatDuration(finishTime - startTime)}") + + return result +} + +internal fun formatContentLength(bytes: Long): String = when { + bytes < 0 -> "N/A" + bytes < 1024 -> "$bytes bytes" + else -> { + val kilobytes = bytes.toDouble() / 1024 + when { + kilobytes < 1024 -> String.format("%.2f KB", kilobytes) + else -> { + val megabytes = kilobytes / 1024 + when { + megabytes < 1024 -> String.format("%.2f MB", megabytes) + else -> { + val gigabytes = megabytes / 1024 + String.format("%.2f GB", gigabytes) + } + } + } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/resourceUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/resourceUtils.kt index 29735501c57..883e1d965df 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/resourceUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/resourceUtils.kt @@ -5,7 +5,13 @@ package org.jetbrains.kotlin.gradle.utils +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.logging.kotlinDebug import java.io.FileNotFoundException +import java.io.IOException +import java.net.HttpURLConnection +import java.net.SocketTimeoutException +import java.net.URL import java.util.* fun Any.loadPropertyFromResources(propFileName: String, property: String): String { @@ -15,4 +21,29 @@ fun Any.loadPropertyFromResources(propFileName: String, property: String): Strin inputStream.use { props.load(it) } return props[property] as String -} \ No newline at end of file +} + +@Throws(IOException::class) +fun Project.probeRemoteFileLength(url: String, probingTimeoutMs: Int = 0): Long? { + val connection = URL(url).openConnection() + if (connection !is HttpURLConnection) { + logger.kotlinDebug(::probeRemoteFileLength.name + "($url, $probingTimeoutMs): Failed to obtain content-length. Likely not an HTTP-based URL. URL connection class is ${connection::class.java}.") + return null + } + + return try { + connection.requestMethod = "HEAD" + connection.connectTimeout = probingTimeoutMs + connection.readTimeout = probingTimeoutMs + connection.contentLengthLong.takeIf { it >= 0 } + } catch (e: SocketTimeoutException) { + if (probingTimeoutMs == 0) + throw e + else { + logger.kotlinDebug(::probeRemoteFileLength.name + "($url, $probingTimeoutMs): Failed to obtain content-length during the probing timeout.") + null + } + } finally { + connection.disconnect() + } +}