From 4ccff3f1b133367b0debaa0922d746b1245d3f6c Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Sun, 2 Feb 2020 14:01:34 +0700 Subject: [PATCH] [Commonizer] Add extendable uniform multi-task CLI --- native/commonizer/build.gradle.kts | 2 +- .../commonizer/cli/BooleanOptionType.kt | 27 ++++ .../commonizer/cli/CliLoggerAdapter.kt | 26 ++++ .../cli/NativeDistributionOptionType.kt | 22 ++++ .../commonizer/cli/NativeTargetsOptionType.kt | 24 ++++ .../descriptors/commonizer/cli/Option.kt | 8 ++ .../descriptors/commonizer/cli/OptionType.kt | 14 ++ .../commonizer/cli/OutputOptionType.kt | 31 +++++ .../kotlin/descriptors/commonizer/cli/Task.kt | 61 +++++++++ .../descriptors/commonizer/cli/TaskType.kt | 48 +++++++ .../kotlin/descriptors/commonizer/cli/cli.kt | 123 ++++++++++++++++++ .../descriptors/commonizer/cli/cliUtils.kt | 35 ----- .../cli/nativeDistributionCommonizer.kt | 56 -------- .../descriptors/commonizer/cli/nativeTasks.kt | 79 +++++++++++ .../konan/NativeDistributionCommonizer.kt | 8 +- 15 files changed, 467 insertions(+), 97 deletions(-) create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/BooleanOptionType.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/CliLoggerAdapter.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/NativeDistributionOptionType.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/NativeTargetsOptionType.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/Option.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/OptionType.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/OutputOptionType.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/Task.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/TaskType.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cli.kt delete mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cliUtils.kt delete mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeDistributionCommonizer.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeTasks.kt diff --git a/native/commonizer/build.gradle.kts b/native/commonizer/build.gradle.kts index 1645d908187..09c8ae9ab61 100644 --- a/native/commonizer/build.gradle.kts +++ b/native/commonizer/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { val runCommonizer by tasks.registering(NoDebugJavaExec::class) { classpath(sourceSets.main.get().runtimeClasspath) - main = "org.jetbrains.kotlin.descriptors.commonizer.cli.NativeDistributionCommonizerKt" + main = "org.jetbrains.kotlin.descriptors.commonizer.cli.CommonizerCLI" } sourceSets { diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/BooleanOptionType.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/BooleanOptionType.kt new file mode 100644 index 00000000000..9d3297a3dcb --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/BooleanOptionType.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +internal class BooleanOptionType( + alias: String, + description: String, + mandatory: Boolean +) : OptionType(alias, description, mandatory) { + private val trueTokens = setOf("1", "on", "yes", "true") + private val falseTokens = setOf("0", "off", "no", "false") + + override fun parse(rawValue: String, onError: (reason: String) -> Nothing): Option { + val value = rawValue.toLowerCase().let { + when (it) { + in trueTokens -> true + in falseTokens -> false + else -> onError("Invalid boolean value: $it") + } + } + + return Option(this, value) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/CliLoggerAdapter.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/CliLoggerAdapter.kt new file mode 100644 index 00000000000..3a5d4e27cd4 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/CliLoggerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +import org.jetbrains.kotlin.util.Logger +import kotlin.system.exitProcess + +internal class CliLoggerAdapter(indentSize: Int) : Logger { + private val indent = " ".repeat(indentSize) + + override fun log(message: String) = printlnIndented(message) + override fun warning(message: String) = printlnIndented("Warning: $message") + + override fun error(message: String) = fatal(message) + override fun fatal(message: String): Nothing { + printlnIndented("Error: $message\n") + exitProcess(1) + } + + private fun printlnIndented(text: String) = + if (indent.isEmpty()) println(text) + else text.split('\n').forEach { println(indent + it) } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/NativeDistributionOptionType.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/NativeDistributionOptionType.kt new file mode 100644 index 00000000000..2354e12812b --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/NativeDistributionOptionType.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +import java.io.File + +internal object NativeDistributionOptionType : OptionType("distribution-path", "Path to the Kotlin/Native distribution") { + override fun parse(rawValue: String, onError: (reason: String) -> Nothing): Option { + val file = File(rawValue) + + try { + if (!file.isDirectory) onError("Kotlin/Native distribution directory does not exist: $rawValue") + } catch (_: Exception) { + onError("Access failure to the Kotlin/Native distribution directory: $rawValue") + } + + return Option(this, file) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/NativeTargetsOptionType.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/NativeTargetsOptionType.kt new file mode 100644 index 00000000000..e5233ee81f6 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/NativeTargetsOptionType.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.target.KonanTarget + +internal object NativeTargetsOptionType : OptionType>("targets", "Comma-separated list of hardware targets") { + private val hostManager = HostManager() + + override fun parse(rawValue: String, onError: (reason: String) -> Nothing): Option> { + val targetNames = rawValue.split(',') + if (targetNames.isEmpty()) onError("No hardware targets specified: $rawValue") + + val targets = targetNames.mapTo(HashSet()) { targetName -> + hostManager.targets[targetName] ?: onError("Unknown hardware target: $targetName") + }.toList() + + return Option(this, targets) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/Option.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/Option.kt new file mode 100644 index 00000000000..77489484b1d --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/Option.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +internal class Option(val type: OptionType, val value: T) diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/OptionType.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/OptionType.kt new file mode 100644 index 00000000000..b5ef3c5f1de --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/OptionType.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +internal abstract class OptionType( + val alias: String, + val description: String, + val mandatory: Boolean = true +) { + abstract fun parse(rawValue: String, onError: (reason: String) -> Nothing): Option +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/OutputOptionType.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/OutputOptionType.kt new file mode 100644 index 00000000000..f79851d663b --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/OutputOptionType.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +import java.io.File + +internal object OutputOptionType : OptionType("output-path", "Destination for commonized libraries") { + override fun parse(rawValue: String, onError: (reason: String) -> Nothing): Option { + val file = File(rawValue) + + try { + val valid = when { + file.isDirectory -> file.listFiles()?.isEmpty() != false + file.exists() -> false + else -> { + if (!file.mkdirs()) onError("Destination can't be created: $rawValue") + true + } + } + + if (!valid) onError("Destination is not empty: $rawValue") + } catch (_: Exception) { + onError("Access failure to the destination directory: $rawValue") + } + + return Option(this, file) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/Task.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/Task.kt new file mode 100644 index 00000000000..b434d900f53 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/Task.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +import java.util.concurrent.atomic.AtomicInteger + +internal abstract class Task(private val options: Collection>) : Comparable { + internal enum class Category( + open val prologue: String? = null, + open val epilogue: String? = null, + open val logEachStep: Boolean = false + ) { + // Important: the order of entries affects that order of tasks execution + INFORMATIONAL, + COMMONIZATION( + prologue = "Kotlin KLIB commonizer: Please wait while processing libraries.", + epilogue = "Kotlin KLIB commonizer: Done.\n", + logEachStep = true + ) + } + + abstract val category: Category + private val submissionOrder = SUBMISSION_ORDER_GENERATOR.getAndIncrement() + + abstract fun execute(logPrefix: String = "") + + protected inline fun > getMandatory(nameFilter: (String) -> Boolean = { true }): T { + val option = options.filter { it.type is O }.single { nameFilter(it.type.alias) } + check(option.type.mandatory) + + @Suppress("UNCHECKED_CAST") + return option.value as T + } + + protected inline fun > getOptional(nameFilter: (String) -> Boolean = { true }): T? { + val option = options.filter { it.type is O }.singleOrNull { nameFilter(it.type.alias) } + if (option != null) check(!option.type.mandatory) + + @Suppress("UNCHECKED_CAST") + return option?.value as T? + } + + override fun compareTo(other: Task): Int { + category.compareTo(other.category).let { + if (it != 0) return it + } + + this::class.java.name.compareTo(other::class.java.name).let { + if (it != 0) return it + } + + return submissionOrder - other.submissionOrder + } + + companion object { + private val SUBMISSION_ORDER_GENERATOR = AtomicInteger(0) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/TaskType.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/TaskType.kt new file mode 100644 index 00000000000..3fec1d4637f --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/TaskType.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +internal enum class TaskType( + val alias: String, + val description: String, + val optionTypes: List>, + val taskConstructor: (Collection>) -> Task +) { + NATIVE_DIST_COMMONIZE( + "native-dist-commonize", + "Commonize platform-specific libraries in Kotlin/Native distribution", + listOf( + NativeDistributionOptionType, + OutputOptionType, + NativeTargetsOptionType, + BooleanOptionType( + "copy-stdlib", + "Boolean (default false);\nwhether to copy Kotlin/Native endorsed libraries to the destination", + mandatory = false + ), + BooleanOptionType( + "copy-endorsed-libs", + "Boolean (default false);\nwhether to copy Kotlin/Native endorsed libraries to the destination", + mandatory = false + ), + BooleanOptionType("log-stats", "Boolean (default false); Log commonization stats", mandatory = false) + ), + ::NativeDistributionCommonize + ), + + NATIVE_DIST_LIST_TARGETS( + "native-dist-print-targets", + "Print all hardware targets inside of the Kotlin/Native distribution", + listOf( + NativeDistributionOptionType + ), + ::NativeDistributionListTargets + ); + + companion object { + fun getByAlias(alias: String) = values().firstOrNull { it.alias == alias } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cli.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cli.kt new file mode 100644 index 00000000000..6062752eff2 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cli.kt @@ -0,0 +1,123 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +@file:JvmName("CommonizerCLI") + +package org.jetbrains.kotlin.descriptors.commonizer.cli + +import org.jetbrains.kotlin.descriptors.commonizer.cli.Task.Category +import kotlin.system.exitProcess + +fun main(args: Array) { + if (args.isEmpty()) printUsageAndExit() + + val tokens = args.iterator() + val tasks = mutableListOf() + + var taskAlias: String? = tokens.next() + while (taskAlias != null) { + taskAlias = parseTask(taskAlias, tokens, tasks) + } + + // execute tasks in a specific order: + // - first, execute all informational tasks + // - then, all commonization tasks + Category.values().forEach { category -> + val sortedTasks = tasks.filter { it.category == category }.sorted() + if (sortedTasks.isNotEmpty()) { + category.prologue?.let(::println) + + sortedTasks.forEachIndexed { index, task -> + val logPrefix = if (category.logEachStep) "[Step ${index + 1} of ${sortedTasks.size}] " else "" + task.execute(logPrefix) + } + + category.epilogue?.let(::println) + } + } +} + +private fun parseTask( + taskAlias: String, + tokens: Iterator, + tasks: MutableList +): String? { + val taskType = TaskType.getByAlias(taskAlias) ?: printUsageAndExit("Unknown task $taskAlias") + val optionTypes = taskType.optionTypes.associateBy { it.alias } + val options = mutableMapOf>() + + fun buildOngoingTask() { + // check options completeness + val missedMandatoryOptions = optionTypes.filterKeys { it !in options }.filterValues { it.mandatory }.keys + if (missedMandatoryOptions.isNotEmpty()) + printUsageAndExit("Mandatory options not specified in task $taskAlias: " + missedMandatoryOptions.joinToString { "-$it" }) + + tasks += taskType.taskConstructor(options.values) + } + + while (tokens.hasNext()) { + val optionAlias = tokens.next().let { token -> + if (!token.startsWith('-')) { + buildOngoingTask() + + // proceed to the next task + return token + } + + token.trimStart('-') + } + + if (optionAlias in options) printUsageAndExit("Duplicated value for option -$optionAlias in task $taskAlias") + + val optionType = optionTypes[optionAlias] ?: printUsageAndExit("Unknown option -$optionAlias in task $taskAlias") + + val rawValue = if (tokens.hasNext()) tokens.next() else printUsageAndExit("No value for option -$optionAlias in task $taskAlias") + val option = optionType.parse(rawValue) { reason -> + printUsageAndExit("Failed to parse option -$optionAlias in task $taskAlias: $reason") + } + + options[optionAlias] = option + } + + buildOngoingTask() + return null // no next task +} + +private fun printUsageAndExit(errorMessage: String? = null): Nothing { + if (errorMessage != null) { + println("Error: $errorMessage") + println() + } + + fun formatLeft(indent: Int, left: String) = StringBuilder().apply { + repeat(indent) { append(" ") } + append(left) + } + + fun StringBuilder.formatRight(right: String): String { + val middleSpace = kotlin.math.max(38 - length, 1) + repeat(middleSpace) { append(" ") } + append(right) + return this.toString() + } + + fun formatBoth(indent: Int, left: String, right: String) = formatLeft(indent, left).formatRight(right) + + println("Usage: ${::printUsageAndExit.javaClass.`package`.name}.CommonizerCLI [ ...]") + println() + println("Tasks:") + for (taskType in TaskType.values()) { + println(formatBoth(1, taskType.alias, taskType.description)) + println(formatLeft(1, if (taskType.optionTypes.isNotEmpty()) "Options:" else "No options.")) + for (optionType in taskType.optionTypes) { + val lines = optionType.description.split('\n') + println(formatBoth(2, "-${optionType.alias}", lines.first())) + lines.drop(1).forEach { println(StringBuilder().formatRight(it)) } + } + println() + } + + exitProcess(if (errorMessage != null) 1 else 0) +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cliUtils.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cliUtils.kt deleted file mode 100644 index 4dc691f47da..00000000000 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cliUtils.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. - * 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.descriptors.commonizer.cli - -import org.jetbrains.kotlin.util.Logger -import kotlin.system.exitProcess - -internal fun parseArgs( - args: Array, - printUsageAndExit: (String) -> Nothing -): Map> { - val commandLine = mutableMapOf>() - for (index in args.indices step 2) { - val key = args[index] - if (key[0] != '-') printUsageAndExit("Expected a flag with initial dash: $key") - if (index + 1 == args.size) printUsageAndExit("Expected a value after $key") - val value = args[index + 1] - commandLine.computeIfAbsent(key) { mutableListOf() }.add(value) - } - return commandLine -} - -internal object CliLoggerAdapter : Logger { - override fun log(message: String) = println(message) - override fun warning(message: String) = println("Warning: $message") - - override fun error(message: String) = fatal(message) - override fun fatal(message: String): Nothing { - println("Error: $message\n") - exitProcess(1) - } -} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeDistributionCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeDistributionCommonizer.kt deleted file mode 100644 index 9940cc6cdab..00000000000 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeDistributionCommonizer.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. - * 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.descriptors.commonizer.cli - -import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommonizer -import org.jetbrains.kotlin.konan.target.HostManager -import java.io.File -import kotlin.system.exitProcess - -fun main(args: Array) { - if (args.isEmpty()) printUsageAndExit() - - val parsedArgs = parseArgs(args, ::printUsageAndExit) - - val repository = parsedArgs["-repository"]?.firstOrNull()?.let(::File) ?: printUsageAndExit("repository not specified") - - val targets = with(HostManager()) { - val targetNames = parsedArgs["-target"]?.toSet() ?: printUsageAndExit("no targets specified") - targetNames.map { targetName -> - targets[targetName] ?: printUsageAndExit("unknown target name: $targetName") - } - } - - val destination = parsedArgs["-output"]?.firstOrNull()?.let(::File) ?: printUsageAndExit("output not specified") - - val withStats = parsedArgs["-stats"]?.firstOrNull()?.toLowerCase() in setOf("1", "on", "yes", "true") - - NativeDistributionCommonizer( - repository = repository, - targets = targets, - destination = destination, - copyStdlib = true, - copyEndorsedLibs = true, - withStats = withStats, - logger = CliLoggerAdapter - ).run() -} - -private fun printUsageAndExit(errorMessage: String? = null): Nothing { - if (errorMessage != null) { - println("Error: $errorMessage") - println() - } - - println("Usage: commonizer ") - println("where possible options include:") - println("\t-repository \tWork with the specified Kotlin/Native repository") - println("\t-target \t\tAdd hardware target to commonization") - println("\t-output \t\tDestination of commonized KLIBs") - println() - - exitProcess(if (errorMessage != null) 1 else 0) -} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeTasks.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeTasks.kt new file mode 100644 index 00000000000..c68077344a6 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeTasks.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.commonizer.cli + +import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommonizer +import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_KLIB_DIR +import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR +import org.jetbrains.kotlin.konan.target.KonanTarget +import java.io.File + +internal class NativeDistributionListTargets(options: Collection>) : Task(options) { + override val category get() = Category.INFORMATIONAL + + override fun execute(logPrefix: String) { + val distributionPath = getMandatory() + + val targets = distributionPath.resolve(KONAN_DISTRIBUTION_KLIB_DIR) + .resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR) + .list() + ?.sorted() + ?: emptyList() + + println() + if (targets.isEmpty()) + println("No hardware targets found inside of the Kotlin/Native distribution \"$distributionPath\".") + else { + println("${targets.size} hardware targets found inside of the Kotlin/Native distribution \"$distributionPath\":") + targets.forEach(::println) + } + println() + } +} + +internal class NativeDistributionCommonize(options: Collection>) : Task(options) { + override val category get() = Category.COMMONIZATION + + override fun execute(logPrefix: String) { + val distribution = getMandatory() + val destination = getMandatory() + val targets = getMandatory, NativeTargetsOptionType>() + + val copyStdlib = getOptional { it == "copy-stdlib" } ?: false + val copyEndorsedLibs = getOptional { it == "copy-endorsed-libs" } ?: false + val withStats = getOptional { it == "log-stats" } ?: false + + val descriptionSuffix = estimateLibrariesCount(distribution, targets)?.let { " ($it items)" } ?: "" + val description = "${logPrefix}Preparing commonized Kotlin/Native libraries for targets $targets$descriptionSuffix" + + println(description) + + NativeDistributionCommonizer( + repository = distribution, + targets = targets, + destination = destination, + copyStdlib = copyStdlib, + copyEndorsedLibs = copyEndorsedLibs, + withStats = withStats, + logger = CliLoggerAdapter(2) + ).run() + + println("$description: Done") + } + + companion object { + private fun estimateLibrariesCount(distribution: File, targets: List): Int? { + val targetNames = targets.map { it.name } + return distribution.resolve(KONAN_DISTRIBUTION_KLIB_DIR) + .resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR) + .listFiles() + ?.filter { it.name in targetNames } + ?.mapNotNull { it.listFiles() } + ?.flatMap { it.toList() } + ?.size + } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt index 7227b5025f7..d8f85707848 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt @@ -47,20 +47,18 @@ class NativeDistributionCommonizer( with(ResettableClockMark()) { // 1. load modules val modulesByTargets = loadModules() - logger.log("Loaded lazy (uninitialized) libraries in ${elapsedSinceLast()}") + logger.log("* Loaded lazy (uninitialized) libraries in ${elapsedSinceLast()}") // 2. run commonization val result = commonize(modulesByTargets) - logger.log("Commonization performed in ${elapsedSinceLast()}") + logger.log("* Commonization performed in ${elapsedSinceLast()}") // 3. write new libraries saveModules(modulesByTargets, result) - logger.log("Written libraries in ${elapsedSinceLast()}") + logger.log("* Written libraries in ${elapsedSinceLast()}") logger.log("TOTAL: ${elapsedSinceStart()}") } - - logger.log("Done.\n") } private fun checkPreconditions() {