diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Helper0.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Helper0.kt index 134968fcfd2..b09ea5831a2 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Helper0.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Helper0.kt @@ -15,8 +15,8 @@ */ // -// Example startup helper for Kotlin N compiler. Compile to JAR and add it to -// Kotlin N classpath - it will get loaded and executed automatically. +// Example startup helper for Kotlin/Native compiler. Compile to JAR and add it to +// Kotlin/Native classpath - it will get loaded and executed automatically. // package org.jetbrains.kotlin.konan.util diff --git a/dependencies/build.gradle b/dependencies/build.gradle index 073ba9306d5..7147141c46c 100644 --- a/dependencies/build.gradle +++ b/dependencies/build.gradle @@ -174,7 +174,9 @@ class HelperNativeDep extends TgzNativeDep { @TaskAction public void downloadAndExtract() { - new DependencyProcessor(baseOutDir, konanProperties, [baseName], baseUrl).run() + def downloader = new DependencyProcessor(baseOutDir, konanProperties, [baseName], baseUrl) + downloader.showInfo = false + downloader.run() } } diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/NoJavaUtil.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/NoJavaUtil.kt index d599c774b6c..ea505c10a16 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/NoJavaUtil.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/NoJavaUtil.kt @@ -186,9 +186,12 @@ fun Path.recursiveCopyTo(destPath: Path) { val newPath = destFs.getPath(destPath.toString(), relative.toString()) // File systems don't allow replacing an existing root. - if (newPath == newPath.getRoot()) return@next - Files.copy(oldPath, newPath, - StandardCopyOption.REPLACE_EXISTING) + if (newPath == newPath.getRoot()) return@next + if (Files.isDirectory(newPath)) { + Files.createDirectories(newPath) + } else { + Files.copy(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING) + } } } diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt index f59d4369f5b..6ba61e6e178 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt @@ -6,15 +6,13 @@ import java.net.URL import java.net.URLConnection import java.nio.file.Files import java.nio.file.StandardCopyOption -import java.util.concurrent.TimeUnit -import java.util.concurrent.locks.ReentrantLock -import kotlin.concurrent.thread -import kotlin.concurrent.withLock +import java.util.concurrent.* class DependencyDownloader( var maxAttempts: Int = DEFAULT_MAX_ATTEMPTS, var attemptIntervalMs: Long = DEFAULT_ATTEMPT_INTERVAL_MS ) { + val executor = ExecutorCompletionService(Executors.newSingleThreadExecutor()) enum class ReplacingMode { /** Redownload the file and replace the existing one. */ @@ -25,41 +23,8 @@ class DependencyDownloader( RETURN_EXISTING } - class DownloadingProgress(@Volatile var currentBytes: Long, val totalBytes: Long) { - @Volatile var done: Boolean = false - private set - @Volatile var exception: Throwable? = null - private set - - private val lock = ReentrantLock() - private val condition = lock.newCondition() - - val doneCorrectly: Boolean - get() = done && exception != null - + class DownloadingProgress(@Volatile var currentBytes: Long) { fun update(readBytes: Int) { currentBytes += readBytes } - - fun done() = lock.withLock { - done = true - condition.signalAll() - } - - fun done(e: Throwable) = lock.withLock { - done = true - exception = e - condition.signalAll() - } - - fun trackProgress(interval: Long, - intervalTimeUnit: TimeUnit = TimeUnit.MILLISECONDS, - action: (progress: DownloadingProgress) -> Unit) = - lock.withLock { - action(this) - while (!done) { - condition.await(interval, intervalTimeUnit) - action(this) - } - } } private fun doDownload(originalUrl: URL, @@ -68,33 +33,40 @@ class DependencyDownloader( currentBytes: Long, totalBytes: Long, append: Boolean) { - val progress = DownloadingProgress(currentBytes, totalBytes) + val progress = DownloadingProgress(currentBytes) - // TODO: Implement multi-thread downloading + use some sort of thread pool. - thread { - Thread.setDefaultUncaughtExceptionHandler { _, throwable -> progress.done(throwable) } + // TODO: Implement multi-thread downloading. + executor.submit { connection.getInputStream().use { from -> FileOutputStream(tmpFile, append).use { to -> val buffer = ByteArray(DEFAULT_BUFFER_SIZE) var read = from.read(buffer) while (read != -1) { + if (Thread.interrupted()) { + throw InterruptedException() + } to.write(buffer, 0, read) progress.update(read) read = from.read(buffer) } - if (currentBytes != totalBytes) { + if (progress.currentBytes != totalBytes) { throw EOFException("The stream closed before end of downloading.") } - progress.done() } } } - progress.trackProgress(1000) { - updateProgressMsg(originalUrl.toString(), it.currentBytes, it.totalBytes) - } - if (!progress.doneCorrectly) { - throw progress.exception!! + var result: Future? + do { + updateProgressMsg(originalUrl.toString(), progress.currentBytes, totalBytes) + result = executor.poll(1, TimeUnit.SECONDS) + } while(result == null) + updateProgressMsg(originalUrl.toString(), progress.currentBytes, totalBytes) + + try { + result.get() + } catch (e: ExecutionException) { + throw e.cause ?: e } } @@ -112,6 +84,7 @@ class DependencyDownloader( connection.setRequestProperty("range", "bytes=$currentBytes-") connection.connect() } else { + currentBytes = 0 tmpFile.delete() } } @@ -148,10 +121,10 @@ class DependencyDownloader( replace: ReplacingMode = ReplacingMode.RETURN_EXISTING): File { if (destination.exists()) { - @Suppress("NON_EXHAUSTIVE_WHEN") when (replace) { ReplacingMode.RETURN_EXISTING -> return destination ReplacingMode.THROW -> throw FileAlreadyExistsException(destination) + ReplacingMode.REPLACE -> Unit // Just continue with downloading. } } val tmpFile = File("${destination.canonicalPath}.$TMP_SUFFIX") @@ -160,7 +133,7 @@ class DependencyDownloader( "A temporary file is a directory: ${tmpFile.canonicalPath}. Remove it and try again." } check(!destination.isDirectory) { - "The destination file is a directory: ${tmpFile.canonicalPath}. Remove it and try again." + "The destination file is a directory: ${destination.canonicalPath}. Remove it and try again." } var attempt = 1 diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyProcessor.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyProcessor.kt index 5c399d773e9..0ae984a53da 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyProcessor.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyProcessor.kt @@ -73,6 +73,7 @@ class DependencyProcessor(dependenciesRoot: File, val lockFile = File(cacheDirectory, ".lock").apply { if (!exists()) createNewFile() } + var showInfo = true private var isInfoShown = false // TOOO: Rename pause -> interval @@ -135,7 +136,7 @@ class DependencyProcessor(dependenciesRoot: File, return } - if (!isInfoShown) { + if (showInfo && !isInfoShown) { println("Downloading native dependencies (LLVM, sysroot etc). This is a one-time action performed only on the first run of the compiler.") isInfoShown = true }