Skip to content

Commit

Permalink
dependencies: Move dependency downloader into shared project
Browse files Browse the repository at this point in the history
  • Loading branch information
ilmat192 committed Aug 23, 2017
1 parent fb15992 commit 60ed6f6
Show file tree
Hide file tree
Showing 23 changed files with 370 additions and 587 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ private fun runCmd(command: Array<String>, workDir: File, verbose: Boolean = fal

private fun maybeExecuteHelper(dependenciesRoot: String, properties: Properties, dependencies: List<String>) {
try {
val kClass = Class.forName("org.jetbrains.kotlin.konan.Helper0").kotlin
val kClass = Class.forName("org.jetbrains.kotlin.konan.util.Helper0").kotlin
@Suppress("UNCHECKED_CAST")
val ctor = kClass.constructors.single() as KFunction<Runnable>
val result = ctor.call(dependenciesRoot, properties, dependencies)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,9 @@ import org.jetbrains.kotlin.config.addKotlinSourceRoots
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.TargetManager
import org.jetbrains.kotlin.utils.KotlinPaths
import java.io.File
import kotlin.reflect.KFunction

// TODO: Don't use reflection?
private fun maybeExecuteHelper(targetName: String) {
try {
val kClass = Class.forName("org.jetbrains.kotlin.konan.Helper0").kotlin
@Suppress("UNCHECKED_CAST")
val ctor = kClass.constructors.single() as KFunction<Runnable>
val distribution = Distribution(TargetManager(targetName))
val result = ctor.call(
distribution.dependenciesDir,
distribution.properties,
distribution.dependencies
)
result.run()
} catch (notFound: ClassNotFoundException) {
// Just ignore, no helper.
} catch (e: Throwable) {
throw IllegalStateException("Cannot download dependencies.", e)
}
}

class K2Native : CLICompiler<K2NativeCompilerArguments>() {

override fun doExecute(@NotNull arguments: K2NativeCompilerArguments, @NotNull configuration: CompilerConfiguration, @NotNull rootDisposable: Disposable, @Nullable paths: KotlinPaths?): ExitCode {
Expand Down Expand Up @@ -85,7 +66,6 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
return this?.asList<String>() ?: listOf<String>()
}


// It is executed before doExecute().
override fun setupPlatformSpecificArgumentsAndServices(
configuration: CompilerConfiguration,
Expand Down Expand Up @@ -152,8 +132,6 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
put(ENABLE_ASSERTIONS, arguments.enableAssertions)
}
}

maybeExecuteHelper(arguments.target ?: "host")
}

override fun createArguments(): K2NativeCompilerArguments {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
// Example startup helper for Kotlin N compiler. Compile to JAR and add it to
// Kotlin N classpath - it will get loaded and executed automatically.
//
package org.jetbrains.kotlin.konan
package org.jetbrains.kotlin.konan.util

import java.util.*

// Name it CompilerHelper0 so that it get loaded.
// Name it Helper0 so that it get loaded.
class Helper0Disabled(val dependenciesDir: String,
val properties: Properties,
val dependencies: List<String>) : Runnable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.konan.target.TargetManager.*
import org.jetbrains.kotlin.konan.target.CompilerOutputKind.*
import org.jetbrains.kotlin.konan.util.DependencyProcessor

class KonanConfig(val project: Project, val configuration: CompilerConfiguration) {

Expand All @@ -47,9 +48,16 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
error("Target $target is not available on the ${TargetManager.host} host")
}
}

private fun Distribution.prepareDependencies() {
DependencyProcessor(java.io.File(dependenciesDir), targetProperties).run()
}

internal val distribution = Distribution(targetManager,
configuration.get(KonanConfigKeys.PROPERTY_FILE),
configuration.get(KonanConfigKeys.RUNTIME_FILE))
configuration.get(KonanConfigKeys.PROPERTY_FILE),
configuration.get(KonanConfigKeys.RUNTIME_FILE)).apply {
prepareDependencies()
}

private val produce = configuration.get(KonanConfigKeys.PRODUCE)!!
private val suffix = produce.suffix(targetManager.target)
Expand Down
3 changes: 2 additions & 1 deletion backend.native/konan.properties
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ dependenciesUrl = http://download.jetbrains.com/kotlin/native
airplaneMode = false

downloadingAttempts = 10
downloadingAttemptPauseMs = 3000
downloadingAttemptIntervalMs = 3000
homeDependencyCache = .konan/cache

llvmDebugOptFlags = -O0

Expand Down
6 changes: 0 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ task list_dist(dependsOn: "listDist")

task distCompiler(type: Copy) {
dependsOn ':backend.native:jars'
dependsOn ':tools:helpers:jar'
dependsOn ':utilities:jar'
dependsOn ':klib:jar'
dependsOn ':shared:jar'
Expand Down Expand Up @@ -212,11 +211,6 @@ task distCompiler(type: Copy) {
from(konanPropertiesFile) {
into('konan')
}

// TODO: check if we use native libraries copied to dist (see above) or not.
from(project(':tools:helpers').file('build/libs')) {
into('konan/lib')
}
}

task listDist(type: Exec) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,6 @@ abstract class KonanTest extends JavaExec {
try {
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
classpath = project.configurations.cli_bc
// TODO: Remove helpers classpath when downloading is moved into 'shared' project.
classpath += project.project(":tools:helpers").sourceSets.main.runtimeClasspath
enableAssertions = true
args = ["-output", output,
*filesToCompile,
Expand Down
3 changes: 1 addition & 2 deletions cmd/run_konan
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,10 @@ KOTLIN_JAR="${KONAN_HOME}/konan/lib/kotlin-compiler.jar"
STUB_GENERATOR_JAR="${KONAN_HOME}/konan/lib/StubGenerator.jar"
INTEROP_INDEXER_JAR="${KONAN_HOME}/konan/lib/Indexer.jar"
INTEROP_JAR="${KONAN_HOME}/konan/lib/Runtime.jar"
HELPERS_JAR="${KONAN_HOME}/konan/lib/helpers.jar"
SHARED_JAR="${KONAN_HOME}/konan/lib/shared.jar"
KLIB_JAR="${KONAN_HOME}/konan/lib/klib.jar"
UTILITIES_JAR="${KONAN_HOME}/konan/lib/utilities.jar"
KONAN_CLASSPATH="$KOTLIN_JAR:$INTEROP_JAR:$STUB_GENERATOR_JAR:$INTEROP_INDEXER_JAR:$KONAN_JAR:$HELPERS_JAR:$KLIB_JAR:$UTILITIES_JAR:$SHARED_JAR"
KONAN_CLASSPATH="$KOTLIN_JAR:$INTEROP_JAR:$STUB_GENERATOR_JAR:$INTEROP_INDEXER_JAR:$KONAN_JAR:$KLIB_JAR:$UTILITIES_JAR:$SHARED_JAR"
TOOL_CLASS=org.jetbrains.kotlin.cli.utilities.MainKt

LIBCLANG_DISABLE_CRASH_RECOVERY=1 \
Expand Down
32 changes: 14 additions & 18 deletions dependencies/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@


import groovy.transform.stc.ClosureParams
import groovy.transform.stc.ClosureSignatureHint
import groovy.transform.stc.FirstParam
import groovy.transform.stc.FromString
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.konan.util.DependencyProcessor

import static org.jetbrains.kotlin.konan.target.KonanTarget.*
import org.jetbrains.kotlin.konan.properties.KonanProperties
import org.jetbrains.kotlin.konan.properties.*
Expand Down Expand Up @@ -166,23 +171,11 @@ class TgzNativeDep extends NativeDep {

class HelperNativeDep extends TgzNativeDep {

int maxAttempts = Integer.parseInt(
project.rootProject.ext.konanProperties['downloadingAttempts'].toString())
long attemptPauseMs = Long.parseLong(
project.rootProject.ext.konanProperties['downloadingAttemptPauseMs'].toString())

public HelperNativeDep() {
dependsOn(':tools:helpers:jar')
}
Properties konanProperties = new Properties()

@TaskAction
public void downloadAndExtract() {
project.javaexec {
main = "org.jetbrains.kotlin.konan.MainKt"
classpath += project.findProject(':tools:helpers').getConfigurations().getByName("runtime")
classpath += project.findProject(':tools:helpers').getConfigurations().getByName("runtime").artifacts.files
args baseOutDir.canonicalPath, baseUrl, maxAttempts, attemptPauseMs, baseName
}
new DependencyProcessor(baseOutDir, konanProperties, [baseName], baseUrl).run()
}
}

Expand All @@ -195,8 +188,8 @@ enum DependencyKind {


DependencyKind(String name,
@ClosureParams(KonanProperties.class) Closure<String> dirGetter,
@ClosureParams(KonanTarget.class) Closure<String> propertyNameGetter =
@ClosureParams(value = FromString.class, options = "KonanProperties") Closure<String> dirGetter,
@ClosureParams(value = FromString.class, options = "KonanTarget") Closure<String> propertyNameGetter =
{target -> "${target.userName}${name.capitalize()}Dir"}) {
this.name = name
this.dirGetter = dirGetter
Expand Down Expand Up @@ -225,8 +218,11 @@ TargetManager.enabled.each { target ->
)

konanProperties.dependencies.each { dependency ->
task "${target.userName}-${dependency}"(type: HelperNativeDep) {
baseName = dependency
if (tasks.findByName(dependency) == null) {
task "${dependency}"(type: HelperNativeDep) {
baseName = dependency
it.konanProperties = rootProject.ext.konanProperties
}
}
}

Expand Down
1 change: 0 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@ include ':runtime'
include ':common'
include ':backend.native:tests'
include ':shared'
include ':tools:helpers'
include ':tools:kotlin-native-gradle-plugin'
include ':utilities'
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ fun Path.recursiveCopyTo(destPath: Path) {
val relative = sourcePath.relativize(oldPath)
val destFs = destPath.getFileSystem()
// We are copying files between file systems,
// so pass the relative path through the Sting.
// so pass the relative path through the String.
val newPath = destFs.getPath(destPath.toString(), relative.toString())

// File systems don't allow replacing an existing root.
Expand All @@ -195,7 +195,7 @@ fun Path.recursiveCopyTo(destPath: Path) {
fun bufferedReader(errorStream: InputStream?) = BufferedReader(InputStreamReader(errorStream))

// stdlib `use` function adapted for AutoCloseable.
private inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R {
inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R {
var closed = false
try {
return block(this)
Expand All @@ -212,3 +212,11 @@ private inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R {
}
}
}

fun Path.unzipTo(directory: Path) {
val zipUri = URI.create("jar:" + this.toUri())
FileSystems.newFileSystem(zipUri, emptyMap<String, Any?>(), null).use { zipfs ->
val zipPath = zipfs.getPath("/")
zipPath.recursiveCopyTo(directory)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package org.jetbrains.kotlin.konan.util

import java.io.EOFException
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.net.URL
import java.nio.file.Files
import java.nio.file.StandardCopyOption

class DependencyDownloader(
var maxAttempts: Int = DEFAULT_MAX_ATTEMPTS,
var attemptIntervalMs: Long = DEFAULT_ATTEMPT_INTERVAL_MS) {

enum class ReplacingMode {
/** Redownload the file and replace the existing one. */
REPLACE,
/** Throw FileAlreadyExistsException */
THROW,
/** Don't download the file and return the existing one*/
RETURN_EXISTING
}

/** Performs an attempt to download a specified file into the specified location */
fun tryDownload(url: URL, dstFile: File) {
val connection = url.openConnection()
val totalBytes = connection.contentLengthLong
var currentBytes = 0L

try {
url.openStream().use { from ->
FileOutputStream(dstFile, false).use { to ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var read = from.read(buffer)
while (read != -1) {
to.write(buffer, 0, read)
currentBytes += read
updateProgressMsg(url.toString(), currentBytes, totalBytes)
read = from.read(buffer)
}
if (currentBytes != totalBytes) {
throw EOFException("The stream closed before end of downloading.")
}
}
}
} catch (e: Throwable) {
dstFile.delete()
throw e
}
}

/** Downloads a file from [source] url to [destination]. Returns [destination]. */
fun download(source: URL,
destination: File,
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)
// TODO: What if dst is a directory?
}
}
// TODO: resuming must be here
val tmpFile = File("${destination.canonicalPath}.$TMP_SUFFIX")

var attempt = 1
var waitTime = 0L
while (true) {
try {
tryDownload(source, tmpFile)
break
} catch (e: IOException) {
if (attempt >= maxAttempts) {
throw e
}
attempt++
waitTime += attemptIntervalMs
println("Cannot download a dependency: $e\n" +
"Wait ${waitTime.toDouble() / 1000} sec and try again (attempt: $attempt/$maxAttempts).")
// TODO: Wait better
Thread.sleep(waitTime)
}
}

Files.move(tmpFile.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING)
println("Done.")
return destination
}

private val Long.humanReadable: String
get() {
if (this < 0) {
return "-"
}
if (this < 1024) {
return "$this bytes"
}
val exp = (Math.log(this.toDouble()) / Math.log(1024.0)).toInt()
val prefix = "kMGTPE"[exp-1]
return "%.1f %siB".format(this / Math.pow(1024.0, exp.toDouble()), prefix)
}

private fun updateProgressMsg(url: String, currentBytes: Long, totalBytes: Long) {
print("\rDownload dependency: $url (${currentBytes.humanReadable}/${totalBytes.humanReadable}). ")
}

companion object {
const val DEFAULT_MAX_ATTEMPTS = 10
const val DEFAULT_ATTEMPT_INTERVAL_MS = 3000L

const val TMP_SUFFIX = "part"
}
}
Loading

0 comments on commit 60ed6f6

Please sign in to comment.