CLI library rework (#3215)

This commit is contained in:
LepilkinaElena
2019-09-06 17:49:12 +03:00
committed by GitHub
parent 51d03ece0a
commit 8e0e346492
66 changed files with 1494 additions and 1099 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-compiler:$kotlinVersion" compile "org.jetbrains.kotlin:kotlin-compiler:$kotlinVersion"
compile project(':Interop:Indexer') compile project(':Interop:Indexer')
compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion"
compile project(path: ":endorsedLibraries:kliopt", configuration: "jvmRuntimeElements") compile project(path: ":endorsedLibraries:kotlinx.cli", configuration: "jvmRuntimeElements")
} }
compileKotlin { compileKotlin {
@@ -7,7 +7,7 @@ import org.jetbrains.kotlin.native.interop.gen.jvm.prepareTool
import org.jetbrains.kotlin.native.interop.indexer.NativeLibraryHeaders import org.jetbrains.kotlin.native.interop.indexer.NativeLibraryHeaders
import org.jetbrains.kotlin.native.interop.indexer.getHeaderPaths import org.jetbrains.kotlin.native.interop.indexer.getHeaderPaths
import org.jetbrains.kotlin.native.interop.tool.CInteropArguments import org.jetbrains.kotlin.native.interop.tool.CInteropArguments
import org.jetbrains.kliopt.ArgParser import kotlinx.cli.ArgParser
import java.io.File import java.io.File
fun defFileDependencies(args: Array<String>) { fun defFileDependencies(args: Array<String>) {
@@ -16,85 +16,83 @@
package org.jetbrains.kotlin.native.interop.tool package org.jetbrains.kotlin.native.interop.tool
import org.jetbrains.kliopt.* import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlinx.cli.*
const val HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX = "headerFilterAdditionalSearchPrefix" const val HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX = "headerFilterAdditionalSearchPrefix"
const val NODEFAULTLIBS = "nodefaultlibs" const val NODEFAULTLIBS_DEPRECATED = "nodefaultlibs"
const val NODEFAULTLIBS = "no-default-libs"
const val NOENDORSEDLIBS = "no-endorsed-libs"
const val PURGE_USER_LIBS = "Xpurge-user-libs" const val PURGE_USER_LIBS = "Xpurge-user-libs"
const val TEMP_DIR = "Xtemporary-files-dir" const val TEMP_DIR = "Xtemporary-files-dir"
// TODO: unify camel and snake cases. // TODO: unify camel and snake cases.
// Possible solution is to accept both cases // Possible solution is to accept both cases
open class CommonInteropArguments(val argParser: ArgParser) { open class CommonInteropArguments(val argParser: ArgParser) {
val verbose by argParser.option(ArgType.Boolean, description = "Enable verbose logging output", defaultValue = false) val verbose by argParser.option(ArgType.Boolean, description = "Enable verbose logging output").default(false)
val flavor by argParser.option(ArgType.Choice(listOf("jvm", "native", "wasm")), description = "Interop target", val flavor by argParser.option(ArgType.Choice(listOf("jvm", "native", "wasm")), description = "Interop target")
defaultValue = "jvm") .default("jvm")
val pkg by argParser.option(ArgType.String, description = "place generated bindings to the package") val pkg by argParser.option(ArgType.String, description = "place generated bindings to the package")
val output by argParser.option(ArgType.String, shortName = "o", description = "specifies the resulting library file", val output by argParser.option(ArgType.String, shortName = "o", description = "specifies the resulting library file")
defaultValue = "nativelib") .default("nativelib")
val libraryPath by argParser.options(ArgType.String, description = "add a library search path", val libraryPath by argParser.option(ArgType.String, description = "add a library search path")
multiple = true, delimiter = ",") .multiple().delimiter(",")
val staticLibrary by argParser.options(ArgType.String, description = "embed static library to the result", val staticLibrary by argParser.option(ArgType.String, description = "embed static library to the result")
multiple = true, delimiter = ",") .multiple().delimiter(",")
val generated by argParser.option(ArgType.String, description = "place generated bindings to the directory", val generated by argParser.option(ArgType.String, description = "place generated bindings to the directory")
defaultValue = System.getProperty("user.dir")) .default(System.getProperty("user.dir"))
val natives by argParser.option(ArgType.String, description = "where to put the built native files", val natives by argParser.option(ArgType.String, description = "where to put the built native files")
defaultValue = System.getProperty("user.dir")) .default(System.getProperty("user.dir"))
val library by argParser.options(ArgType.String, shortName = "l", description = "library to use for building", val library by argParser.option(ArgType.String, shortName = "l", description = "library to use for building")
multiple = true) .multiple()
val repo by argParser.options(ArgType.String, shortName = "r", description = "repository to resolve dependencies", val repo by argParser.option(ArgType.String, shortName = "r", description = "repository to resolve dependencies")
multiple = true) .multiple()
val nodefaultlibs by argParser.option(ArgType.Boolean, NODEFAULTLIBS, val nodefaultlibs by argParser.option(ArgType.Boolean, NODEFAULTLIBS,
description = "don't link the libraries from dist/klib automatically").default(false)
val nodefaultlibsDeprecated by argParser.option(ArgType.Boolean, NODEFAULTLIBS_DEPRECATED,
description = "don't link the libraries from dist/klib automatically", description = "don't link the libraries from dist/klib automatically",
defaultValue = false) deprecatedWarning = "Old form of flag. Please, use $NODEFAULTLIBS.").default(false)
val noendorsedlibs by argParser.option(ArgType.Boolean, NOENDORSEDLIBS,
description = "don't link the endorsed libraries from dist automatically").default(false)
val purgeUserLibs by argParser.option(ArgType.Boolean, PURGE_USER_LIBS, val purgeUserLibs by argParser.option(ArgType.Boolean, PURGE_USER_LIBS,
description = "don't link unused libraries even explicitly specified", defaultValue = false) description = "don't link unused libraries even explicitly specified").default(false)
val tempDir by argParser.option(ArgType.String, TEMP_DIR, val tempDir by argParser.option(ArgType.String, TEMP_DIR,
description = "save temporary files to the given directory") description = "save temporary files to the given directory")
} }
class CInteropArguments(argParser: ArgParser = class CInteropArguments(argParser: ArgParser =
ArgParser("cinterop", useDefaultHelpShortName = false, ArgParser("cinterop",
prefixStyle = ArgParser.OPTION_PREFIX_STYLE.JVM)): CommonInteropArguments(argParser) { prefixStyle = ArgParser.OPTION_PREFIX_STYLE.JVM)): CommonInteropArguments(argParser) {
val target by argParser.option(ArgType.String, description = "native target to compile to", defaultValue = "host") val target by argParser.option(ArgType.String, description = "native target to compile to").default("host")
val def by argParser.option(ArgType.String, description = "the library definition file") val def by argParser.option(ArgType.String, description = "the library definition file")
val header by argParser.options(ArgType.String, description = "header file to produce kotlin bindings for", val header by argParser.option(ArgType.String, description = "header file to produce kotlin bindings for")
multiple = true, delimiter = ",") .multiple().delimiter(",")
val shortHeaderForm by argParser.options(ArgType.String, "h", description = "header file to produce kotlin bindings for", val headerFilterPrefix by argParser.option(ArgType.String, HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, "hfasp",
multiple = true, delimiter = ",", deprecatedWarning = "Option -h is deprecated. Please use -header.") "header file to produce kotlin bindings for").multiple().delimiter(",")
val headerFilterPrefix by argParser.options(ArgType.String, HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, "hfasp", val compilerOpts by argParser.option(ArgType.String,
"header file to produce kotlin bindings for", multiple = true, delimiter = ",") description = "additional compiler options (allows to add several options separated by spaces)")
val compilerOpts by argParser.options(ArgType.String, .multiple().delimiter(" ")
description = "additional compiler options (allows to add several options separated by spaces)", val compilerOptions by argParser.option(ArgType.String, "compiler-options",
multiple = true, delimiter = " ") description = "additional compiler options (allows to add several options separated by spaces)")
val compilerOptions by argParser.options(ArgType.String, "compiler-options", .multiple().delimiter(" ")
description = "additional compiler options (allows to add several options separated by spaces)", val linkerOpts = argParser.option(ArgType.String, "linkerOpts",
multiple = true, delimiter = " ") description = "additional linker options (allows to add several options separated by spaces)")
val linkerOpts by argParser.options(ArgType.String, "linkerOpts", .multiple().delimiter(" ")
description = "additional linker options (allows to add several options separated by spaces)", val linkerOptions = argParser.option(ArgType.String, "linker-options",
multiple = true, delimiter = " ") description = "additional linker options (allows to add several options separated by spaces)")
val linkerOptions by argParser.options(ArgType.String, "linker-options", .multiple().delimiter(" ")
description = "additional linker options (allows to add several options separated by spaces)", val compilerOption by argParser.option(ArgType.String, "compiler-option",
multiple = true, delimiter = " ") description = "additional compiler option").multiple()
val compilerOption by argParser.options(ArgType.String, "compiler-option", val linkerOption = argParser.option(ArgType.String, "linker-option",
description = "additional compiler option", multiple = true) description = "additional linker option").multiple()
val linkerOption by argParser.options(ArgType.String, "linker-option",
description = "additional linker option", multiple = true)
val copt by argParser.options(ArgType.String,
description = "additional compiler options (allows to add several options separated by spaces)",
multiple = true, delimiter = " ",
deprecatedWarning = "Option -copt is deprecated. Please use -compiler-options.")
val lopt by argParser.options(ArgType.String,
description = "additional linker options (allows to add several options separated by spaces)",
multiple = true, delimiter = " ",
deprecatedWarning = "Option -lopt is deprecated. Please use -linker-options.")
val linker by argParser.option(ArgType.String, description = "use specified linker") val linker by argParser.option(ArgType.String, description = "use specified linker")
} }
class JSInteropArguments(argParser: ArgParser = ArgParser("jsinterop", useDefaultHelpShortName = false, class JSInteropArguments(argParser: ArgParser = ArgParser("jsinterop",
prefixStyle = ArgParser.OPTION_PREFIX_STYLE.JVM)): CommonInteropArguments(argParser) { prefixStyle = ArgParser.OPTION_PREFIX_STYLE.JVM)): CommonInteropArguments(argParser) {
val target by argParser.option(ArgType.Choice(listOf("wasm32")), val target by argParser.option(ArgType.Choice(listOf("wasm32")),
description = "wasm target to compile to", defaultValue = "wasm32") description = "wasm target to compile to").default("wasm32")
} }
internal fun warn(msg: String) { internal fun warn(msg: String) {
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.native.interop.gen.*
import org.jetbrains.kotlin.native.interop.gen.wasm.processIdlLib import org.jetbrains.kotlin.native.interop.gen.wasm.processIdlLib
import org.jetbrains.kotlin.native.interop.indexer.* import org.jetbrains.kotlin.native.interop.indexer.*
import org.jetbrains.kotlin.native.interop.tool.* import org.jetbrains.kotlin.native.interop.tool.*
import org.jetbrains.kliopt.ArgParser import kotlinx.cli.ArgParser
import java.io.File import java.io.File
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import java.nio.file.* import java.nio.file.*
@@ -175,16 +175,15 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
val tool = prepareTool(cinteropArguments.target, flavor) val tool = prepareTool(cinteropArguments.target, flavor)
val def = DefFile(defFile, tool.substitutions) val def = DefFile(defFile, tool.substitutions)
val isLinkerOptsSetByUser = (cinteropArguments.argParser.getOrigin("linkerOpts") == ArgParser.ValueOrigin.SET_BY_USER) || val isLinkerOptsSetByUser = (cinteropArguments.linkerOpts.valueOrigin == ArgParser.ValueOrigin.SET_BY_USER) ||
(cinteropArguments.argParser.getOrigin("linker-option") == ArgParser.ValueOrigin.SET_BY_USER) || (cinteropArguments.linkerOptions.valueOrigin == ArgParser.ValueOrigin.SET_BY_USER) ||
(cinteropArguments.argParser.getOrigin("linker-options") == ArgParser.ValueOrigin.SET_BY_USER) || (cinteropArguments.linkerOption.valueOrigin == ArgParser.ValueOrigin.SET_BY_USER)
(cinteropArguments.argParser.getOrigin("lopt") == ArgParser.ValueOrigin.SET_BY_USER)
if (flavorName == "native" && isLinkerOptsSetByUser) { if (flavorName == "native" && isLinkerOptsSetByUser) {
warn("-linker-option(s)/-linkerOpts/-lopt option is not supported by cinterop. Please add linker options to .def file or binary compilation instead.") warn("-linker-option(s)/-linkerOpts option is not supported by cinterop. Please add linker options to .def file or binary compilation instead.")
} }
val additionalLinkerOpts = cinteropArguments.linkerOpts.toTypedArray() + cinteropArguments.linkerOption.toTypedArray() + val additionalLinkerOpts = cinteropArguments.linkerOpts.value.toTypedArray() + cinteropArguments.linkerOption.value.toTypedArray() +
cinteropArguments.linkerOptions.toTypedArray() + cinteropArguments.lopt.toTypedArray() cinteropArguments.linkerOptions.value.toTypedArray()
val verbose = cinteropArguments.verbose val verbose = cinteropArguments.verbose
val language = selectNativeLanguage(def.config) val language = selectNativeLanguage(def.config)
@@ -305,10 +304,9 @@ internal fun buildNativeLibrary(
arguments: CInteropArguments, arguments: CInteropArguments,
imports: ImportsImpl imports: ImportsImpl
): NativeLibrary { ): NativeLibrary {
val additionalHeaders = (arguments.header + arguments.shortHeaderForm).toTypedArray() val additionalHeaders = (arguments.header).toTypedArray()
val additionalCompilerOpts = (arguments.compilerOpts + val additionalCompilerOpts = (arguments.compilerOpts +
arguments.compilerOptions + arguments.compilerOption + arguments.compilerOptions + arguments.compilerOption).toTypedArray()
arguments.copt).toTypedArray()
val headerFiles = def.config.headers + additionalHeaders val headerFiles = def.config.headers + additionalHeaders
val language = selectNativeLanguage(def.config) val language = selectNativeLanguage(def.config)
@@ -3,7 +3,7 @@ package org.jetbrains.kotlin.native.interop.gen.wasm
import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.native.interop.gen.argsToCompiler import org.jetbrains.kotlin.native.interop.gen.argsToCompiler
import org.jetbrains.kotlin.native.interop.gen.wasm.idl.* import org.jetbrains.kotlin.native.interop.gen.wasm.idl.*
import org.jetbrains.kliopt.ArgParser import kotlinx.cli.ArgParser
import org.jetbrains.kotlin.native.interop.tool.JSInteropArguments import org.jetbrains.kotlin.native.interop.tool.JSInteropArguments
fun kotlinHeader(packageName: String): String { fun kotlinHeader(packageName: String): String {
+1 -1
View File
@@ -191,7 +191,7 @@ targetList.each { target ->
"-Djava.library.path=${project.buildDir}/nativelibs/$hostName", "-Djava.library.path=${project.buildDir}/nativelibs/$hostName",
] ]
def defaultArgs = ['-nopack', '-nostdlib', '-nodefaultlibs'] def defaultArgs = ['-nopack', '-nostdlib', '-no-default-libs', '-no-endorsed-libs']
if (target != "wasm32") defaultArgs += '-g' if (target != "wasm32") defaultArgs += '-g'
def konanArgs = [*defaultArgs, def konanArgs = [*defaultArgs,
'-target', target, '-target', target,
@@ -111,6 +111,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
with(configuration) { with(configuration) {
put(NODEFAULTLIBS, arguments.nodefaultlibs) put(NODEFAULTLIBS, arguments.nodefaultlibs)
put(NOENDORSEDLIBS, arguments.noendorsedlibs)
put(NOSTDLIB, arguments.nostdlib) put(NOSTDLIB, arguments.nostdlib)
put(NOPACK, arguments.nopack) put(NOPACK, arguments.nopack)
put(NOMAIN, arguments.nomain) put(NOMAIN, arguments.nomain)
@@ -57,9 +57,12 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
valueDescription = "<path>", description = "Include the native bitcode library", delimiter = "") valueDescription = "<path>", description = "Include the native bitcode library", delimiter = "")
var nativeLibraries: Array<String>? = null var nativeLibraries: Array<String>? = null
@Argument(value = "-nodefaultlibs", description = "Don't link the libraries from dist/klib automatically") @Argument(value = "-no-default-libs", deprecatedName = "-nodefaultlibs", description = "Don't link the libraries from dist/klib automatically")
var nodefaultlibs: Boolean = false var nodefaultlibs: Boolean = false
@Argument(value = "-no-endorsed-libs", description = "Don't link the endorsed libraries from dist automatically")
var noendorsedlibs: Boolean = false
@Argument(value = "-nomain", description = "Assume 'main' entry point to be provided by external libraries") @Argument(value = "-nomain", description = "Assume 'main' entry point to be provided by external libraries")
var nomain: Boolean = false var nomain: Boolean = false
@@ -128,7 +128,8 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
resolver.resolveWithDependencies( resolver.resolveWithDependencies(
unresolvedLibraries + includedLibraryFiles.map { UnresolvedLibrary(it.absolutePath, null) }, unresolvedLibraries + includedLibraryFiles.map { UnresolvedLibrary(it.absolutePath, null) },
noStdLib = configuration.getBoolean(KonanConfigKeys.NOSTDLIB), noStdLib = configuration.getBoolean(KonanConfigKeys.NOSTDLIB),
noDefaultLibs = configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS) noDefaultLibs = configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS),
noEndorsedLibs = configuration.getBoolean(KonanConfigKeys.NOENDORSEDLIBS)
) )
} }
@@ -66,6 +66,8 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("native library file paths") = CompilerConfigurationKey.create("native library file paths")
val NODEFAULTLIBS: CompilerConfigurationKey<Boolean> val NODEFAULTLIBS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link with the default libraries") = CompilerConfigurationKey.create("don't link with the default libraries")
val NOENDORSEDLIBS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link with the endorsed libraries")
val NOMAIN: CompilerConfigurationKey<Boolean> val NOMAIN: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("assume 'main' entry point to be provided by external libraries") = CompilerConfigurationKey.create("assume 'main' entry point to be provided by external libraries")
val NOSTDLIB: CompilerConfigurationKey<Boolean> val NOSTDLIB: CompilerConfigurationKey<Boolean>
@@ -826,7 +826,7 @@ internal val ModuleDescriptor.namePrefix: String get() {
internal fun abbreviate(name: String): String { internal fun abbreviate(name: String): String {
val normalizedName = name val normalizedName = name
.capitalize() .capitalize()
.replace('-', '_') .replace("-|\\.".toRegex(), "_")
val uppers = normalizedName.filterIndexed { index, character -> index == 0 || character.isUpperCase() } val uppers = normalizedName.filterIndexed { index, character -> index == 0 || character.isUpperCase() }
if (uppers.length >= 3) return uppers if (uppers.length >= 3) return uppers
+1
View File
@@ -3225,6 +3225,7 @@ void createInterop(String name, Closure conf) {
interop(name, targets: [target.name]) { interop(name, targets: [target.name]) {
conf(it) conf(it)
noDefaultLibs(true) noDefaultLibs(true)
noEndorsedLibs(true)
baseDir "$testOutputLocal/$name" baseDir "$testOutputLocal/$name"
} }
} }
@@ -301,7 +301,7 @@ class NativeInteropPlugin implements Plugin<Project> {
prj.dependencies { prj.dependencies {
interopStubGenerator project(path: ":Interop:StubGenerator") interopStubGenerator project(path: ":Interop:StubGenerator")
interopStubGenerator project(path: ":endorsedLibraries:kliopt", configuration: "jvmRuntimeElements") interopStubGenerator project(path: ":endorsedLibraries:kotlinx.cli", configuration: "jvmRuntimeElements")
} }
// FIXME: choose tasks more wisely // FIXME: choose tasks more wisely
@@ -137,7 +137,7 @@ open class BenchmarkingPlugin: Plugin<Project> {
project.configurations.getByName(nativeMain.implementationConfigurationName).apply { project.configurations.getByName(nativeMain.implementationConfigurationName).apply {
// Exclude dependencies already included into K/N distribution (aka endorsed libraries). // Exclude dependencies already included into K/N distribution (aka endorsed libraries).
exclude(mapOf("module" to "kliopt")) exclude(mapOf("module" to "kotlinx.cli"))
} }
repositories.maven { repositories.maven {
+1 -1
View File
@@ -286,7 +286,7 @@ task distCompiler(type: Copy) {
into('konan/lib') into('konan/lib')
} }
from(project(':endorsedLibraries:kliopt').file('build/libs')) { from(project(':endorsedLibraries:kotlinx.cli').file('build/libs')) {
into('konan/lib') into('konan/lib')
} }
+2 -2
View File
@@ -89,13 +89,13 @@ SHARED_JAR="${KONAN_HOME}/konan/lib/shared.jar"
EXTRACTED_METADATA_JAR="${KONAN_HOME}/konan/lib/konan.metadata.jar" EXTRACTED_METADATA_JAR="${KONAN_HOME}/konan/lib/konan.metadata.jar"
EXTRACTED_SERIALIZER_JAR="${KONAN_HOME}/konan/lib/konan.serializer.jar" EXTRACTED_SERIALIZER_JAR="${KONAN_HOME}/konan/lib/konan.serializer.jar"
KLIB_JAR="${KONAN_HOME}/konan/lib/klib.jar" KLIB_JAR="${KONAN_HOME}/konan/lib/klib.jar"
KLIOPT_JAR="${KONAN_HOME}/konan/lib/kliopt-jvm.jar" KOTLINX_CLI_JAR="${KONAN_HOME}/konan/lib/kotlinx.cli-jvm.jar"
UTILITIES_JAR="${KONAN_HOME}/konan/lib/utilities.jar" UTILITIES_JAR="${KONAN_HOME}/konan/lib/utilities.jar"
NATIVE_UTILS_JAR="${KONAN_HOME}/konan/lib/kotlin-native-utils.jar" NATIVE_UTILS_JAR="${KONAN_HOME}/konan/lib/kotlin-native-utils.jar"
UTIL_IO_JAR="${KONAN_HOME}/konan/lib/kotlin-util-io.jar" UTIL_IO_JAR="${KONAN_HOME}/konan/lib/kotlin-util-io.jar"
UTIL_KLIB_JAR="${KONAN_HOME}/konan/lib/kotlin-util-klib.jar" UTIL_KLIB_JAR="${KONAN_HOME}/konan/lib/kotlin-util-klib.jar"
TROVE_JAR="${KONAN_HOME}/konan/lib/trove4j.jar" TROVE_JAR="${KONAN_HOME}/konan/lib/trove4j.jar"
KONAN_CLASSPATH="$KOTLIN_JAR:$KOTLIN_STDLIB_JAR:$KOTLIN_REFLECT_JAR:$KOTLIN_SCRIPT_RUNTIME_JAR:$INTEROP_JAR:$STUB_GENERATOR_JAR:$INTEROP_INDEXER_JAR:$KONAN_JAR:$KLIB_JAR:$UTILITIES_JAR:$SHARED_JAR:$EXTRACTED_METADATA_JAR:$EXTRACTED_SERIALIZER_JAR:$NATIVE_UTILS_JAR:$TROVE_JAR:$UTIL_IO_JAR:$UTIL_KLIB_JAR:$KLIOPT_JAR" KONAN_CLASSPATH="$KOTLIN_JAR:$KOTLIN_STDLIB_JAR:$KOTLIN_REFLECT_JAR:$KOTLIN_SCRIPT_RUNTIME_JAR:$INTEROP_JAR:$STUB_GENERATOR_JAR:$INTEROP_INDEXER_JAR:$KONAN_JAR:$KLIB_JAR:$UTILITIES_JAR:$SHARED_JAR:$EXTRACTED_METADATA_JAR:$EXTRACTED_SERIALIZER_JAR:$NATIVE_UTILS_JAR:$TROVE_JAR:$UTIL_IO_JAR:$UTIL_KLIB_JAR:$KOTLINX_CLI_JAR"
TOOL_CLASS=org.jetbrains.kotlin.cli.utilities.MainKt TOOL_CLASS=org.jetbrains.kotlin.cli.utilities.MainKt
LIBCLANG_DISABLE_CRASH_RECOVERY=1 \ LIBCLANG_DISABLE_CRASH_RECOVERY=1 \
+2 -2
View File
@@ -55,7 +55,7 @@ set "NATIVE_LIB=%_KONAN_HOME%\konan\nativelib"
set "KONAN_LIB=%_KONAN_HOME%\konan\lib" set "KONAN_LIB=%_KONAN_HOME%\konan\lib"
set "SHARED_JAR=%KONAN_LIB%\shared.jar" set "SHARED_JAR=%KONAN_LIB%\shared.jar"
set "KLIOPT_JAR=%KONAN_LIB%\kliopt-jvm.jar" set "KOTLINX_CLI_JAR=%KONAN_LIB%\kotlinx.cli-jvm.jar"
set "EXTRACTED_METADATA_JAR=%KONAN_LIB%\konan.metadata.jar" set "EXTRACTED_METADATA_JAR=%KONAN_LIB%\konan.metadata.jar"
set "EXTRACTED_SERIALIZER_JAR=%KONAN_LIB%\konan.serializer.jar" set "EXTRACTED_SERIALIZER_JAR=%KONAN_LIB%\konan.serializer.jar"
set "INTEROP_INDEXER_JAR=%KONAN_LIB%\Indexer.jar" set "INTEROP_INDEXER_JAR=%KONAN_LIB%\Indexer.jar"
@@ -73,7 +73,7 @@ set "UTIL_IO_JAR=%KONAN_LIB%\kotlin-util-io.jar"
set "UTIL_KLIB_JAR=%KONAN_LIB%\kotlin-util-klib.jar" set "UTIL_KLIB_JAR=%KONAN_LIB%\kotlin-util-klib.jar"
set TROVE_JAR="%KONAN_LIB%\lib\trove4j.jar" set TROVE_JAR="%KONAN_LIB%\lib\trove4j.jar"
set "KONAN_CLASSPATH=%KOTLIN_JAR%;%KOTLIN_STDLIB_JAR%;%KOTLIN_REFLECT_JAR%;%KOTLIN_SCRIPT_RUNTIME_JAR%;%INTEROP_RUNTIME_JAR%;%KONAN_JAR%;%STUB_GENERATOR_JAR%;%INTEROP_INDEXER_JAR%;%SHARED_JAR%;%EXTRACTED_METADATA_JAR%;%EXTRACTED_SERIALIZER_JAR%;%KLIB_JAR%;%UTILITIES_JAR%;%NATIVE_UTILS_JAR%;%UTIL_IO_JAR%;%UTIL_KLIB_JAR%;%TROVE_JAR%;%KLIOPT_JAR%" set "KONAN_CLASSPATH=%KOTLIN_JAR%;%KOTLIN_STDLIB_JAR%;%KOTLIN_REFLECT_JAR%;%KOTLIN_SCRIPT_RUNTIME_JAR%;%INTEROP_RUNTIME_JAR%;%KONAN_JAR%;%STUB_GENERATOR_JAR%;%INTEROP_INDEXER_JAR%;%SHARED_JAR%;%EXTRACTED_METADATA_JAR%;%EXTRACTED_SERIALIZER_JAR%;%KLIB_JAR%;%UTILITIES_JAR%;%NATIVE_UTILS_JAR%;%UTIL_IO_JAR%;%UTIL_KLIB_JAR%;%TROVE_JAR%;%KOTLINX_CLI_JAR%"
set JAVA_OPTS=-ea ^ set JAVA_OPTS=-ea ^
-Xmx3G ^ -Xmx3G ^
+9 -3
View File
@@ -1,4 +1,10 @@
def endorsedLibrariesList = ['kliopt'] def endorsedLibrariesList = ['kotlinx.cli']
def toTaskName(library) {
def name = ""
library.split("\\.").each { word -> name += word.capitalize() }
return name
}
task clean { task clean {
doLast { doLast {
@@ -16,11 +22,11 @@ task jvmJar {
targetList.each { target -> targetList.each { target ->
task("${target}EndorsedLibraries", type: Copy) { task("${target}EndorsedLibraries", type: Copy) {
endorsedLibrariesList.each { library -> endorsedLibrariesList.each { library ->
dependsOn "$library:${target}${library.capitalize()}" dependsOn "$library:${target}${ toTaskName(library) }"
} }
destinationDir project.buildDir destinationDir project.buildDir
endorsedLibrariesList.each { library -> endorsedLibrariesList.each { library ->
from(project("$library").file("build/${target}${library.capitalize()}")) { from(project("$library").file("build/${target}${ toTaskName(library) }")) {
include('**') include('**')
into("$library") into("$library")
} }
@@ -1,863 +0,0 @@
/*
* 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 file.
*/
package org.jetbrains.kliopt
import kotlin.reflect.KProperty
internal expect fun exitProcess(status: Int): Nothing
/**
* Queue of arguments descriptors.
* Arguments can have several values, so one descriptor can be returned several times.
*/
internal class ArgumentsQueue(argumentsDescriptors: List<ArgParser.ArgDescriptor<*>>) {
/**
* Map of arguments descriptors and their current usage number.
*/
private val argumentsUsageNumber = linkedMapOf(*argumentsDescriptors.map { it to 0 }.toTypedArray())
/**
* Get next descriptor from queue.
*/
fun pop(): String? {
if (argumentsUsageNumber.isEmpty())
return null
val (currentDescriptor, usageNumber) = argumentsUsageNumber.iterator().next()
currentDescriptor.number?.let {
// Parse all arguments for current argument description.
if (usageNumber + 1 >= currentDescriptor.number) {
// All needed arguments were provided.
argumentsUsageNumber.remove(currentDescriptor)
} else {
argumentsUsageNumber[currentDescriptor] = usageNumber + 1
}
}
return currentDescriptor.fullName
}
}
/**
* Abstract base class for subcommands.
*/
@SinceKotlin("1.3")
@ExperimentalCli
abstract class Subcommand(val name: String): ArgParser(name) {
/**
* Execute action if subcommand was provided.
*/
abstract fun execute()
}
/**
* Common descriptor both for options and positional arguments.
*
* @property type option/argument type, one of [ArgType].
* @property fullName option/argument full name.
* @property description text descrition of option/argument.
* @property defaultValue default value for option/argument.
* @property required if option/argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @property deprecatedWarning text message with information in case if option is deprecated.
*/
abstract class Descriptor<T : Any>(val type: ArgType<T>,
val fullName: String,
val description: String? = null,
val defaultValue: List<T> = emptyList(),
val required: Boolean = false,
val deprecatedWarning: String? = null) {
/**
* Text description for help message.
*/
abstract val textDescription: String
/**
* Help message for descriptor.
*/
abstract val helpMessage: String
}
/**
* Argument parsing result.
* Contains name of subcommand which was called.
*
* @property commandName name of command which was called.
*/
class ArgParserResult(val commandName: String)
/**
* Arguments parser.
*
* @property programName name of current program.
* @property useDefaultHelpShortName add or not -h flag for hrlp message.
* @property prefixStyle style of expected options prefix.
* @property skipExtraArguments just skip extra arhuments in command line string without producing error message.
*/
open class ArgParser(val programName: String, var useDefaultHelpShortName: Boolean = true,
var prefixStyle: OPTION_PREFIX_STYLE = OPTION_PREFIX_STYLE.LINUX,
var skipExtraArguments: Boolean = false) {
/**
* Map of options: key - fullname of option, value - pair of descriptor and parsed values.
*/
protected val options = mutableMapOf<String, ParsingValue<*, *>>()
/**
* Map of arguments: key - fullname of argument, value - pair of descriptor and parsed values.
*/
protected val arguments = mutableMapOf<String, ParsingValue<*, *>>()
/**
* Map of subcommands.
*/
@UseExperimental(ExperimentalCli::class)
protected val subcommands = mutableMapOf<String, Subcommand>()
/**
* Mapping for short options names for quick search.
*/
private lateinit var shortNames: Map<String, ParsingValue<*, *>>
/**
* Used prefix form for full option form.
*/
protected val optionFullFormPrefix = if (prefixStyle == OPTION_PREFIX_STYLE.LINUX) "--" else "-"
/**
* Used prefix form for short option form.
*/
protected val optionShortFromPrefix = "-"
/**
* Name with all commands that should be executed.
*/
protected val fullCommandName = mutableListOf<String>(programName)
/**
* Origin of option/argument value.
*
* Possible values:
* SET_BY_USER - value of option was provided in command line string;
* SET_DEFAULT_VALUE - value of option wasn't provided in command line, but set using default value;
* UNSET - value of option is unset
* REDEFINED - value of option was redefined in source code after parsing.
*/
enum class ValueOrigin { SET_BY_USER, SET_DEFAULT_VALUE, UNSET, REDEFINED }
/**
* Options prefix style.
*
* Possible values:
* LINUX - Linux style, for full forms of options "--", for short form - "-"
* JVM - JVM style, both for full and short forms of options "-"
*/
enum class OPTION_PREFIX_STYLE { LINUX, JVM }
/**
* Option descriptor.
*
* Command line entity started with some prefix (-/—) and can have value as next entity in command line string.
*
* @property type option type, one of [ArgType].
* @property fullName option full name.
* @property shortName option short name.
* @property description text descrition of option.
* @property defaultValue default value for option.
* @property required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @property multiple if option can be repeated several times in command line with different values. All values are stored.
* @property delimiter delimiter that separate option provided as one string to several values.
* @property deprecatedWarning text message with information in case if option is deprecated.
*/
inner class OptionDescriptor<T : Any>(
type: ArgType<T>,
fullName: String,
val shortName: String ? = null,
description: String? = null,
defaultValue: List<T> = emptyList(),
required: Boolean = false,
val multiple: Boolean = false,
val delimiter: String? = null,
deprecatedWarning: String? = null) : Descriptor<T> (type, fullName, description, defaultValue,
required, deprecatedWarning) {
override val textDescription: String
get() = "option $optionFullFormPrefix$fullName"
override val helpMessage: String
get() {
val result = StringBuilder()
result.append(" $optionFullFormPrefix$fullName")
shortName?.let { result.append(", $optionShortFromPrefix$it") }
(defaultValue.joinToString(",") { it.toString() }).also { if (!it.isEmpty()) result.append(" [$it]") }
description?.let {result.append(" -> ${it}")}
if (required) result.append(" (always required)")
result.append(" ${type.description}")
deprecatedWarning?.let { result.append(" Warning: $it") }
result.append("\n")
return result.toString()
}
}
/**
* Argument descriptor.
*
* Command line entity which role is connected only with its position.
*
* @property type argument type, one of [ArgType].
* @property fullName argument full name.
* @property number expected number of values. Null means any possible number of values.
* @property description text descrition of argument.
* @property defaultValue default value for argument.
* @property required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @property deprecatedWarning text message with information in case if argument is deprecated.
*/
inner class ArgDescriptor<T : Any>(
type: ArgType<T>,
fullName: String,
val number: Int? = null,
description: String? = null,
defaultValue: List<T> = emptyList(),
required: Boolean = true,
deprecatedWarning: String? = null) : Descriptor<T> (type, fullName, description, defaultValue,
required, deprecatedWarning) {
init {
// Check arguments number correctness.
number?.let {
if (it < 0)
printError("Number of arguments for argument description $fullName should be greater than zero.")
}
}
override val textDescription: String
get() = "argument $fullName"
override val helpMessage: String
get() {
val result = StringBuilder()
result.append(" ${fullName}")
(defaultValue.joinToString(",") { it.toString() }).also { if (!it.isEmpty()) result.append(" [$it]") }
description?.let { result.append(" -> ${it}") }
if (!required) result.append(" (optional)")
result.append(" ${type.description}")
deprecatedWarning?.let { result.append(" Warning: $it") }
result.append("\n")
return result.toString()
}
}
/**
* Loader for option with single possible value which is nullable.
*/
inner class SingleNullableOptionLoader<T : Any>(val type: ArgType<T>,
val fullName: String? = null,
val shortName: String ? = null,
val description: String? = null,
val required: Boolean = false,
val deprecatedWarning: String? = null) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<T?> {
val name = fullName ?: prop.name
val descriptor = OptionDescriptor(type, name, shortName, description, emptyList(),
required, deprecatedWarning = deprecatedWarning)
val cliElement = ArgumentSingleNullableValue(type.conversion)
options[name] = ParsingValue(descriptor, cliElement)
return cliElement
}
}
/**
* Loader for option with single possible value which has default value.
*/
inner class SingleOptionWithDefaultLoader<T : Any>(val type: ArgType<T>,
val fullName: String? = null,
val shortName: String ? = null,
val description: String? = null,
val defaultValue: T,
val required: Boolean = false,
val deprecatedWarning: String? = null) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<T> {
val name = fullName ?: prop.name
val descriptor = OptionDescriptor(type, name, shortName, description, listOf(defaultValue),
required, deprecatedWarning = deprecatedWarning)
val cliElement = ArgumentSingleValueWithDefault(type.conversion)
options[name] = ParsingValue(descriptor, cliElement)
return cliElement
}
}
/**
* Loader for option with multiple possible values.
*/
inner class MultipleOptionsLoader<T : Any>(val type: ArgType<T>,
val fullName: String? = null,
val shortName: String ? = null,
val description: String? = null,
val defaultValue: List<T> = emptyList(),
val required: Boolean = false,
val multiple: Boolean = false,
val delimiter: String? = null,
val deprecatedWarning: String? = null) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<MutableList<T>> {
val name = fullName ?: prop.name
val descriptor = OptionDescriptor(type, name, shortName, description, defaultValue,
required, multiple, delimiter, deprecatedWarning)
if (!multiple && delimiter == null)
printError("Several values are expected for option $name. " +
"Option must be used multiple times or split with delimiter.")
val cliElement = ArgumentMultipleValues(type.conversion)
options[name] = ParsingValue(descriptor, cliElement)
return cliElement
}
}
/**
* Add option with single possible value and get delegator to its value.
*
* @param type argument type, one of [ArgType].
* @param fullName argument full name.
* @param shortName option short name.
* @param description text descrition of option.
* @param required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @param deprecatedWarning text message with information in case if option is deprecated.
*/
fun <T : Any>option(type: ArgType<T>,
fullName: String? = null,
shortName: String ? = null,
description: String? = null,
required: Boolean = false,
deprecatedWarning: String? = null) = SingleNullableOptionLoader(type, fullName, shortName,
description, required, deprecatedWarning)
/**
* Add option with single possible value with default and get delegator to its value.
*
* @param type option type, one of [ArgType].
* @param fullName option full name.
* @param shortName option short name.
* @param description text descrition of option.
* @param defaultValue default value for option.
* @param required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @param deprecatedWarning text message with information in case if option is deprecated.
*/
fun <T : Any>option(type: ArgType<T>,
fullName: String? = null,
shortName: String ? = null,
description: String? = null,
defaultValue: T,
required: Boolean = false,
deprecatedWarning: String? = null) = SingleOptionWithDefaultLoader(type, fullName, shortName,
description, defaultValue, required, deprecatedWarning)
/**
* Add option with multiple possible values and get delegator to its values.
*
* @param type option type, one of [ArgType].
* @param fullName option full name.
* @param shortName option short name.
* @param description text descrition of option.
* @param defaultValue default value for option.
* @param required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @param multiple if option can be repeated several times in command line with different values. All values are stored.
* @param delimiter delimiter that separate option provided as one string to several values.
* @param deprecatedWarning text message with information in case if option is deprecated.
*/
fun <T : Any>options(type: ArgType<T>,
fullName: String? = null,
shortName: String ? = null,
description: String? = null,
defaultValue: List<T> = emptyList(),
required: Boolean = false,
multiple: Boolean = false,
delimiter: String? = null,
deprecatedWarning: String? = null) = MultipleOptionsLoader(type, fullName, shortName,
description, defaultValue, required, multiple, delimiter, deprecatedWarning)
/**
* Loader for argument with single possible value which is nullable.
*/
inner class SingleNullableArgumentLoader<T : Any>(val type: ArgType<T>,
val fullName: String? = null,
val description: String? = null,
val required: Boolean = true,
val deprecatedWarning: String? = null) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<T?> {
val name = fullName ?: prop.name
val descriptor = ArgDescriptor(type, name, 1, description,
emptyList(), required, deprecatedWarning)
val cliElement = ArgumentSingleNullableValue(type.conversion)
arguments[name] = ParsingValue(descriptor, cliElement)
return cliElement
}
}
/**
* Loader for argument with single possible value which has default one.
*/
inner class SingleArgumentWithDefaultLoader<T : Any>(val type: ArgType<T>,
val fullName: String? = null,
val description: String? = null,
val defaultValue: T,
val required: Boolean = true,
val deprecatedWarning: String? = null) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<T> {
val name = fullName ?: prop.name
val descriptor = ArgDescriptor(type, name, 1, description,
listOf(defaultValue), required, deprecatedWarning)
val cliElement = ArgumentSingleValueWithDefault(type.conversion)
arguments[name] = ParsingValue(descriptor, cliElement)
return cliElement
}
}
/**
* Loader for option with multiple possible values.
*/
inner class MultipleArgumentsLoader<T : Any>(val type: ArgType<T>,
val fullName: String? = null,
val number: Int? = null,
val description: String? = null,
val defaultValue: List<T> = emptyList(),
val required: Boolean = true,
val deprecatedWarning: String? = null) {
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<MutableList<T>> {
val name = fullName ?: prop.name
val descriptor = ArgDescriptor(type, name, number, description,
defaultValue, required, deprecatedWarning)
val cliElement = ArgumentMultipleValues(type.conversion)
arguments[name] = ParsingValue(descriptor, cliElement)
return cliElement
}
}
/**
* Add argument with single nullable value and get delegator to its value.
*
* @param type argument type, one of [ArgType].
* @param fullName argument full name.
* @param description text descrition of argument.
* @param required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @param deprecatedWarning text message with information in case if argument is deprecated.
*/
fun <T : Any>argument(type: ArgType<T>,
fullName: String? = null,
description: String? = null,
required: Boolean = true,
deprecatedWarning: String? = null) = SingleNullableArgumentLoader(type, fullName, description,
required, deprecatedWarning)
/**
* Add argument with single value with default and get delegator to its value.
*
* @param type argument type, one of [ArgType].
* @param fullName argument full name.
* @param description text descrition of argument.
* @param defaultValue default value for argument.
* @param required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @param deprecatedWarning text message with information in case if argument is deprecated.
*/
fun <T : Any>argument(type: ArgType<T>,
fullName: String? = null,
description: String? = null,
defaultValue: T,
required: Boolean = true,
deprecatedWarning: String? = null) = SingleArgumentWithDefaultLoader(type, fullName,
description, defaultValue, required, deprecatedWarning )
/**
* Add argument with [number] possible values and get delegator to its value.
*
* @param type argument type, one of [ArgType].
* @param fullName argument full name.
* @param number expected number of values. Null means any possible number of values.
* @param description text descrition of argument.
* @param defaultValue default value for argument.
* @param required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @param deprecatedWarning text message with information in case if argument is deprecated.
*/
fun <T : Any>arguments(type: ArgType<T>,
fullName: String? = null,
number: Int? = null,
description: String? = null,
defaultValue: List<T> = emptyList(),
required: Boolean = true,
deprecatedWarning: String? = null) = MultipleArgumentsLoader(type, fullName, number,
description, defaultValue, required, deprecatedWarning)
/**
* Add subcommands.
*
* @param subcommandsList subcommands to add.
*/
@SinceKotlin("1.3")
@ExperimentalCli
fun subcommands(vararg subcommandsList: Subcommand) {
subcommandsList.forEach {
if (it.name in subcommands) {
printError("Subcommand with name ${it.name} was already defined.")
}
// Set same settings as main parser.
it.prefixStyle = prefixStyle
it.useDefaultHelpShortName = useDefaultHelpShortName
fullCommandName.forEachIndexed { index, namePart ->
it.fullCommandName.add(index, namePart)
}
subcommands[it.name] = it
}
}
/**
* Get all free arguments as unnamed list.
*
* @param type argument type, one of [ArgType].
* @param description text descrition of argument.
* @param defaultValue default value for argument.
* @param required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @param deprecatedWarning text message with information in case if argument is deprecated.
*/
fun <T : Any>arguments(type: ArgType<T>,
description: String? = null,
defaultValue: List<T> = emptyList(),
required: Boolean = true,
deprecatedWarning: String? = null): ArgumentValueInterface<MutableList<T>> {
val descriptor = ArgDescriptor(type, "", null, description,
defaultValue, required, deprecatedWarning)
val cliElement = ArgumentMultipleValues(type.conversion)
if ("" in arguments) {
printError("You can have only one unnamed list with positional arguments.")
}
arguments[""] = ParsingValue(descriptor, cliElement)
return cliElement
}
/**
* Parsing value of option/argument.
*/
protected inner class ParsingValue<T: Any, U: Any>(val descriptor: Descriptor<T>, val argumentValue: ArgumentValue<U>) {
/**
* Add parsed value from command line.
*/
fun addValue(stringValue: String,
setValue: ArgumentValue<U>.(String, String) -> Unit = ArgumentValue<U>::addValue) {
// Check of possibility to set several values to one option/argument.
if (descriptor is OptionDescriptor<*> && !descriptor.multiple &&
!argumentValue.isEmpty() && descriptor.delimiter == null) {
printError("Try to provide more than one value for ${descriptor.fullName}.")
}
// Show deprecated warning only first time of using option/argument.
descriptor.deprecatedWarning?.let {
if (argumentValue.isEmpty())
println ("Warning: $it")
}
// Split value if needed.
if (descriptor is OptionDescriptor<*> && descriptor.delimiter != null) {
stringValue.split(descriptor.delimiter).forEach {
argumentValue.setValue(it, descriptor.fullName)
}
} else {
argumentValue.setValue(stringValue, descriptor.fullName)
}
}
/**
* Set default value to option.
*/
fun addDefaultValue() {
descriptor.defaultValue.forEach {
addValue(it.toString(), ArgumentValue<U>::addDefaultValue)
}
if (descriptor.defaultValue.isEmpty() && descriptor.required) {
printError("Please, provide value for ${descriptor.textDescription}. It should be always set.")
}
}
}
/**
* Interface of argument value.
*/
interface ArgumentValueInterface<T> {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T
}
/**
* Argument/option value.
*/
abstract class ArgumentValue<T : Any>(val conversion: (value: String, name: String, helpMessage: String)->T) {
/**
* Values of arguments.
*/
protected lateinit var values: T
/**
* Value origin.
*/
var valueOrigin = ValueOrigin.UNSET
protected set
/**
* Add value from command line.
*
* @param stringValue value from command line.
* @param argumentName name of argument value is added for.
*/
abstract fun addValue(stringValue: String, argumentName: String)
/**
* Add default value.
*
* @param stringValue value from command line.
* @param argumentName name of argument value is added for.
*/
fun addDefaultValue(stringValue: String, argumentName: String) {
addValue(stringValue, argumentName)
valueOrigin = ValueOrigin.SET_DEFAULT_VALUE
}
/**
* Check if values of argument are empty.
*/
abstract fun isEmpty(): Boolean
/**
* Check if value of argument was initialized.
*/
protected fun valuesAreInitialized() = ::values.isInitialized
/**
* Set value from delegated property.
*/
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
values = value
valueOrigin = ValueOrigin.REDEFINED
}
}
/**
* Single argument value.
*
* @property conversion conversion function from string value from command line to expected type.
*/
inner abstract class ArgumentSingleValue<T : Any>(conversion: (value: String, name: String, helpMessage: String)->T):
ArgumentValue<T>(conversion) {
override fun addValue(stringValue: String, argumentName: String) {
if (!valuesAreInitialized()) {
values = conversion(stringValue, argumentName, makeUsage())
valueOrigin = ValueOrigin.SET_BY_USER
} else {
printError("Try to provide more than one value $values and $stringValue for $argumentName.")
}
}
override fun isEmpty(): Boolean = !valuesAreInitialized()
}
/**
* Single nullable argument value.
*
* @property conversion conversion function from string value from command line to expected type.
*/
inner class ArgumentSingleNullableValue<T : Any>(conversion: (value: String, name: String, helpMessage: String)->T):
ArgumentSingleValue<T>(conversion), ArgumentValueInterface<T?> {
override operator fun getValue(thisRef: Any?, property: KProperty<*>): T? = if (!isEmpty()) values else null
}
/**
* Single argument value with default.
*
* @property conversion conversion function from string value from command line to expected type.
*/
inner class ArgumentSingleValueWithDefault<T : Any>(conversion: (value: String, name: String, helpMessage: String)->T):
ArgumentSingleValue<T>(conversion), ArgumentValueInterface<T> {
override operator fun getValue(thisRef: Any?, property: KProperty<*>): T = values
}
/**
* Multiple argument values.
*
* @property conversion conversion function from string value from command line to expected type.
*/
inner class ArgumentMultipleValues<T : Any>(conversion: (value: String, name: String, helpMessage: String)->T):
ArgumentValue<MutableList<T>> (
{ value, name, _ -> mutableListOf(conversion(value, name, makeUsage())) }
), ArgumentValueInterface<MutableList<T>> {
init {
values = mutableListOf()
}
override operator fun getValue(thisRef: Any?, property: KProperty<*>): MutableList<T> = values
override fun addValue(stringValue: String, argumentName: String) {
values.addAll(conversion(stringValue, argumentName, makeUsage()))
valueOrigin = ValueOrigin.SET_BY_USER
}
override fun isEmpty() = values.isEmpty()
}
/**
* Output error. Also adds help usage information for easy understanding of problem.
*
* @param message error message.
*/
fun printError(message: String): Nothing {
error("$message\n${makeUsage()}")
}
/**
* Get origin of option value.
*
* @param name name of argument/option.
*/
fun getOrigin(name: String) = options[name]?.argumentValue?.valueOrigin ?:
arguments[name]?.argumentValue?.valueOrigin ?: printError("No option/argument $name in list of avaliable options")
/**
* Save value as argument value.
*
* @param arg string with argument value.
* @param argumentsQueue queue with active argument descriptors.
*/
private fun saveAsArg(arg: String, argumentsQueue: ArgumentsQueue): Boolean {
// Find next uninitialized arguments.
val name = argumentsQueue.pop()
name?.let {
val argumentValue = arguments[name]!!
argumentValue.descriptor.deprecatedWarning?.let { println ("Warning: $it") }
argumentValue.addValue(arg)
return true
}
return false
}
/**
* Save value as option value.
*/
private fun <T : Any, U: Any> saveAsOption(parsingValue: ParsingValue<T, U>, value: String) {
parsingValue.addValue(value)
}
/**
* Try to recognize command line element as full form of option.
*
* @param candidate string with candidate in options.
*/
protected fun recognizeOptionFullForm(candidate: String) =
if (candidate.startsWith(optionFullFormPrefix))
options[candidate.substring(optionFullFormPrefix.length)]
else null
/**
* Try to recognize command line element as short form of option.
*
* @param candidate string with candidate in options.
*/
protected fun recognizeOptionShortForm(candidate: String) =
if (candidate.startsWith(optionShortFromPrefix))
shortNames[candidate.substring(optionShortFromPrefix.length)]
else null
/**
* Parse arguments.
*
* @param args array with command line arguments.
*
* @return true if all arguments were parsed successfully, otherwise return false and print help message.
*/
fun parse(args: Array<String>): ArgParserResult {
// Add help option.
val helpDescriptor = if (useDefaultHelpShortName) OptionDescriptor(ArgType.Boolean,
"help", "h", "Usage info")
else OptionDescriptor(ArgType.Boolean, "help", description = "Usage info")
options["help"] = ParsingValue(helpDescriptor, ArgumentSingleNullableValue(helpDescriptor.type.conversion))
// Add default list with arguments if there can be extra free arguments.
if (skipExtraArguments) {
arguments(ArgType.String)
}
val argumentsQueue = ArgumentsQueue(arguments.map { it.value.descriptor as ArgDescriptor<*> })
// Fill map with short names of options.
shortNames = options.filter { (it.value.descriptor as? OptionDescriptor<*>)?.shortName != null }.
map { (it.value.descriptor as OptionDescriptor<*>).shortName!! to it.value }.toMap()
var index = 0
while (index < args.size) {
val arg = args[index]
// Check for subcommands.
@UseExperimental(ExperimentalCli::class)
subcommands.forEach { (name, subcommand) ->
if (arg == name) {
// Use parser for this subcommand.
subcommand.parse(args.slice(index + 1..args.size - 1).toTypedArray())
subcommand.execute()
return ArgParserResult(name)
}
}
// Parse argumnets from command line.
if (arg.startsWith('-')) {
// Candidate in being option.
// Option is found.
val argValue = recognizeOptionShortForm(arg) ?: recognizeOptionFullForm(arg)
argValue?.descriptor?.let {
if (argValue.descriptor.type.hasParameter) {
if (index < args.size - 1) {
saveAsOption(argValue, args[index + 1])
index++
} else {
// An error, option with value without value.
printError("No value for ${argValue.descriptor.textDescription}")
}
} else {
// Boolean flags.
if (argValue.descriptor.fullName == "help") {
println(makeUsage())
exitProcess(0)
}
saveAsOption(argValue, "true")
}
} ?: run {
// Try save as argument.
if (!saveAsArg(arg, argumentsQueue)) {
printError("Unknown option $arg")
}
}
} else {
// Argument is found.
if (!saveAsArg(arg, argumentsQueue)) {
printError("Too many arguments! Couldn't proccess argument $arg!")
}
}
index++
}
// Postprocess results of parsing.
options.values.union(arguments.values).forEach { value ->
// Not inited, append default value if needed.
if (value.argumentValue.isEmpty()) {
value.addDefaultValue()
}
}
return ArgParserResult(programName)
}
/**
* Create message with usage description.
*/
internal fun makeUsage(): String {
val result = StringBuilder()
result.append("Usage: ${fullCommandName.joinToString(" ")} options_list\n")
if (!arguments.isEmpty()) {
result.append("Arguments: \n")
arguments.forEach {
result.append(it.value.descriptor.helpMessage)
}
}
result.append("Options: \n")
options.forEach {
result.append(it.value.descriptor.helpMessage)
}
return result.toString()
}
}
@@ -60,7 +60,7 @@ kotlin {
jvm().compilations.all { jvm().compilations.all {
kotlinOptions { kotlinOptions {
freeCompilerArgs = ["-Xuse-experimental=org.jetbrains.kliopt.ExperimentalCli"] freeCompilerArgs = ["-Xuse-experimental=kotlinx.cli.ExperimentalCli"]
suppressWarnings = true suppressWarnings = true
} }
} }
@@ -76,23 +76,23 @@ targetList.each { target ->
"-Djava.library.path=${project.buildDir}/nativelibs/$hostName", "-Djava.library.path=${project.buildDir}/nativelibs/$hostName",
] ]
def defaultArgs = ['-nopack', '-nodefaultlibs'] def defaultArgs = ['-nopack', '-no-default-libs', '-no-endorsed-libs']
if (target != "wasm32") defaultArgs += '-g' if (target != "wasm32") defaultArgs += '-g'
def konanArgs = [*defaultArgs, def konanArgs = [*defaultArgs,
'-target', target, '-target', target,
"-Xruntime=${project(':runtime').file('build/' + target + '/runtime.bc')}", "-Xruntime=${project(':runtime').file('build/' + target + '/runtime.bc')}",
*project.globalBuildArgs] *project.globalBuildArgs]
task("${target}Kliopt", type: JavaExec) { task("${target}KotlinxCli", type: JavaExec) {
dependsOn ":${target}CrossDistRuntime" dependsOn ":${target}CrossDistRuntime"
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt' main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
classpath = project(":backend.native").configurations.cli_bc classpath = project(":backend.native").configurations.cli_bc
jvmArgs = konanJvmArgs jvmArgs = konanJvmArgs
args = [*konanArgs, args = [*konanArgs,
'-output', project.file("build/${target}Kliopt"), '-output', project.file("build/${target}KotlinxCli"),
'-produce', 'library', '-module-name', 'kliopt', '-XXLanguage:+AllowContractsForCustomFunctions', '-produce', 'library', '-module-name', 'kotlinx-cli', '-XXLanguage:+AllowContractsForCustomFunctions',
'-Xmulti-platform', '-Xuse-experimental=org.jetbrains.kliopt.ExperimentalCli', '-Xmulti-platform', '-Xuse-experimental=kotlinx.cli.ExperimentalCli',
'-Xuse-experimental=kotlin.ExperimentalMultiplatform', '-Xuse-experimental=kotlin.ExperimentalMultiplatform',
'-Xallow-result-return-type', '-Xallow-result-return-type',
commonSrc.absolutePath, commonSrc.absolutePath,
@@ -100,6 +100,6 @@ targetList.each { target ->
nativeSrc] nativeSrc]
inputs.dir(nativeSrc) inputs.dir(nativeSrc)
inputs.dir(commonSrc) inputs.dir(commonSrc)
outputs.dir(project.file("build/${target}Kliopt")) outputs.dir(project.file("build/${target}KotlinxCli"))
} }
} }
@@ -3,7 +3,7 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
package org.jetbrains.kliopt package kotlinx.cli
internal actual fun exitProcess(status: Int): Nothing { internal actual fun exitProcess(status: Int): Nothing {
error("Not implemented for JS!") error("Not implemented for JS!")
@@ -3,7 +3,7 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
package org.jetbrains.kliopt package kotlinx.cli
internal actual fun exitProcess(status: Int): Nothing { internal actual fun exitProcess(status: Int): Nothing {
kotlin.system.exitProcess(0) kotlin.system.exitProcess(0)
@@ -3,7 +3,7 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
package org.jetbrains.kliopt package kotlinx.cli
internal actual fun exitProcess(status: Int): Nothing { internal actual fun exitProcess(status: Int): Nothing {
kotlin.system.exitProcess(0) kotlin.system.exitProcess(0)
@@ -0,0 +1,449 @@
/*
* 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 file.
*/
package kotlinx.cli
import kotlin.reflect.KProperty
internal expect fun exitProcess(status: Int): Nothing
/**
* Queue of arguments descriptors.
* Arguments can have several values, so one descriptor can be returned several times.
*/
internal class ArgumentsQueue(argumentsDescriptors: List<ArgDescriptor<*, *>>) {
/**
* Map of arguments descriptors and their current usage number.
*/
private val argumentsUsageNumber = linkedMapOf(*argumentsDescriptors.map { it to 0 }.toTypedArray())
/**
* Get next descriptor from queue.
*/
fun pop(): String? {
if (argumentsUsageNumber.isEmpty())
return null
val (currentDescriptor, usageNumber) = argumentsUsageNumber.iterator().next()
currentDescriptor.number?.let {
// Parse all arguments for current argument description.
if (usageNumber + 1 >= currentDescriptor.number) {
// All needed arguments were provided.
argumentsUsageNumber.remove(currentDescriptor)
} else {
argumentsUsageNumber[currentDescriptor] = usageNumber + 1
}
}
return currentDescriptor.fullName
}
}
/**
* Interface of argument value.
*/
interface ArgumentValueDelegate<T> {
var value: T
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
/**
* Abstract base class for subcommands.
*/
@SinceKotlin("1.3")
@ExperimentalCli
abstract class Subcommand(val name: String): ArgParser(name) {
/**
* Execute action if subcommand was provided.
*/
abstract fun execute()
}
/**
* Argument parsing result.
* Contains name of subcommand which was called.
*
* @property commandName name of command which was called.
*/
class ArgParserResult(val commandName: String)
/**
* Arguments parser.
*
* @property programName name of current program.
* @property useDefaultHelpShortName add or not -h flag for help message.
* @property prefixStyle style of expected options prefix.
* @property skipExtraArguments just skip extra arguments in command line string without producing error message.
*/
open class ArgParser(val programName: String, var useDefaultHelpShortName: Boolean = true,
var prefixStyle: OPTION_PREFIX_STYLE = OPTION_PREFIX_STYLE.LINUX,
var skipExtraArguments: Boolean = false) {
/**
* Map of options: key - full name of option, value - pair of descriptor and parsed values.
*/
private val options = mutableMapOf<String, ParsingValue<*, *>>()
/**
* Map of arguments: key - full name of argument, value - pair of descriptor and parsed values.
*/
private val arguments = mutableMapOf<String, ParsingValue<*, *>>()
/**
* Map with declared options.
*/
private val declaredOptions = mutableListOf<CLIEntityWrapper>()
/**
* Map with declared arguments.
*/
private val declaredArguments = mutableListOf<CLIEntityWrapper>()
/**
* Map of subcommands.
*/
@UseExperimental(ExperimentalCli::class)
protected val subcommands = mutableMapOf<String, Subcommand>()
/**
* Mapping for short options names for quick search.
*/
private val shortNames = mutableMapOf<String, ParsingValue<*, *>>()
/**
* Used prefix form for full option form.
*/
private val optionFullFormPrefix = if (prefixStyle == OPTION_PREFIX_STYLE.LINUX) "--" else "-"
/**
* Used prefix form for short option form.
*/
private val optionShortFromPrefix = "-"
/**
* Name with all commands that should be executed.
*/
protected val fullCommandName = mutableListOf(programName)
/**
* Origin of option/argument value.
*
* Possible values:
* SET_BY_USER - value of option was provided in command line string;
* SET_DEFAULT_VALUE - value of option wasn't provided in command line, but set using default value;
* UNSET - value of option is unset
* REDEFINED - value of option was redefined in source code after parsing.
*/
enum class ValueOrigin { SET_BY_USER, SET_DEFAULT_VALUE, UNSET, REDEFINED }
/**
* Options prefix style.
*
* Possible values:
* LINUX - Linux style, for full forms of options "--", for short form - "-"
* JVM - JVM style, both for full and short forms of options "-"
*/
enum class OPTION_PREFIX_STYLE { LINUX, JVM }
/**
* Add option with single possible value and get delegator to its value.
*
* @param type argument type, one of [ArgType].
* @param fullName argument full name.
* @param shortName option short name.
* @param description text description of Argument.
* @param deprecatedWarning text message with information in case if option is deprecated.
*/
fun <T : Any>option(type: ArgType<T>,
fullName: String? = null,
shortName: String ? = null,
description: String? = null,
deprecatedWarning: String? = null): SingleNullableOption<T> {
val option = SingleNullableOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type,
fullName, shortName, description, deprecatedWarning = deprecatedWarning), CLIEntityWrapper())
option.owner.entity = option
declaredOptions.add(option.owner)
return option
}
/**
* Check usage of required property for arguments.
* Make sense only for several last arguments.
*/
private fun inspectRequiredAndDefaultUsage() {
var previousArgument: ParsingValue<*, *>? = null
arguments.forEach { (_, currentArgument) ->
previousArgument?.let {
// Previous argument has default value.
it.descriptor.defaultValue?.let {
if (currentArgument.descriptor.defaultValue == null && currentArgument.descriptor.required) {
printWarning("Default value of argument ${previousArgument.descriptor.fullName} will be unused, " +
"because next argument ${currentArgument.descriptor.fullName} is always required and has no default value.")
}
}
// Previous argument is optional.
if (!it.descriptor.required) {
if (currentArgument.descriptor.defaultValue == null && currentArgument.descriptor.required) {
printWarning("Argument ${previousArgument.descriptor.fullName} will be always required, " +
"because next argument ${currentArgument.descriptor.fullName} is always required.")
}
}
}
}
}
/**
* Add argument with single nullable value and get delegator to its value.
*
* @param type argument type, one of [ArgType].
* @param fullName argument full name.
* @param description text description of argument.
* @param deprecatedWarning text message with information in case if argument is deprecated.
*/
fun <T : Any>argument(type: ArgType<T>,
fullName: String? = null,
description: String? = null,
deprecatedWarning: String? = null) : SingleArgument<T> {
val argument = SingleArgument(ArgDescriptor(type, fullName, 1,
description, deprecatedWarning = deprecatedWarning), CLIEntityWrapper())
argument.owner.entity = argument
declaredArguments.add(argument.owner)
return argument
}
/**
* Add subcommands.
*
* @param subcommandsList subcommands to add.
*/
@SinceKotlin("1.3")
@ExperimentalCli
fun subcommands(vararg subcommandsList: Subcommand) {
subcommandsList.forEach {
if (it.name in subcommands) {
printError("Subcommand with name ${it.name} was already defined.")
}
// Set same settings as main parser.
it.prefixStyle = prefixStyle
it.useDefaultHelpShortName = useDefaultHelpShortName
fullCommandName.forEachIndexed { index, namePart ->
it.fullCommandName.add(index, namePart)
}
subcommands[it.name] = it
}
}
/**
* Output error. Also adds help usage information for easy understanding of problem.
*
* @param message error message.
*/
fun printError(message: String): Nothing {
error("$message\n${makeUsage()}")
}
/**
* Save value as argument value.
*
* @param arg string with argument value.
* @param argumentsQueue queue with active argument descriptors.
*/
private fun saveAsArg(arg: String, argumentsQueue: ArgumentsQueue): Boolean {
// Find next uninitialized arguments.
val name = argumentsQueue.pop()
name?.let {
val argumentValue = arguments[name]!!
argumentValue.descriptor.deprecatedWarning?.let { printWarning(it) }
argumentValue.addValue(arg)
return true
}
return false
}
/**
* Save value as option value.
*/
private fun <T : Any, U: Any> saveAsOption(parsingValue: ParsingValue<T, U>, value: String) {
parsingValue.addValue(value)
}
/**
* Try to recognize command line element as full form of option.
*
* @param candidate string with candidate in options.
*/
private fun recognizeOptionFullForm(candidate: String) =
if (candidate.startsWith(optionFullFormPrefix))
options[candidate.substring(optionFullFormPrefix.length)]
else null
/**
* Try to recognize command line element as short form of option.
*
* @param candidate string with candidate in options.
*/
private fun recognizeOptionShortForm(candidate: String) =
if (candidate.startsWith(optionShortFromPrefix))
shortNames[candidate.substring(optionShortFromPrefix.length)]
else null
/**
* Parse arguments.
*
* @param args array with command line arguments.
*
* @return true if all arguments were parsed successfully, otherwise return false and print help message.
*/
fun parse(args: Array<String>) = parse(args.asList())
protected fun parse(args: List<String>): ArgParserResult {
// Add help option.
val helpDescriptor = if (useDefaultHelpShortName) OptionDescriptor<Boolean, Boolean>(optionFullFormPrefix,
optionShortFromPrefix, ArgType.Boolean,
"help", "h", "Usage info")
else OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix,
ArgType.Boolean, "help", description = "Usage info")
val helpOption = SingleNullableOption(helpDescriptor, CLIEntityWrapper())
helpOption.owner.entity = helpOption
declaredOptions.add(helpOption.owner)
// Add default list with arguments if there can be extra free arguments.
if (skipExtraArguments) {
argument(ArgType.String, "").number()
}
// Map declared options and arguments to maps.
declaredOptions.forEachIndexed { index, option ->
val value = option.entity?.delegate as ParsingValue<*, *>
value.descriptor.fullName?.let {
// Add option.
if (options.containsKey(it)) {
error("Option with full name $it was already added.")
}
with(value.descriptor as OptionDescriptor) {
if (shortName != null && shortNames.containsKey(shortName)) {
error("Option with short name ${shortName} was already added.")
}
shortName?.let {
shortNames[it] = value
}
}
options[it] = value
} ?: error("Option was added, but unnamed. Added option under №${index + 1}")
}
declaredArguments.forEachIndexed { index, argument ->
val value = argument.entity?.delegate as ParsingValue<*, *>
value.descriptor.fullName?.let {
// Add option.
if (arguments.containsKey(it)) {
error("Argument with full name $it was already added.")
}
arguments[it] = value
} ?: error("Argument was added, but unnamed. Added argument under №${index + 1}")
}
// Make inspections for arguments.
inspectRequiredAndDefaultUsage()
val argumentsQueue = ArgumentsQueue(arguments.map { it.value.descriptor as ArgDescriptor<*, *> })
var index = 0
try {
while (index < args.size) {
val arg = args[index]
// Check for subcommands.
@UseExperimental(ExperimentalCli::class)
subcommands.forEach { (name, subcommand) ->
if (arg == name) {
// Use parser for this subcommand.
subcommand.parse(args.slice(index + 1..args.size - 1))
subcommand.execute()
return ArgParserResult(name)
}
}
// Parse arguments from command line.
if (arg.startsWith('-')) {
// Candidate in being option.
// Option is found.
val argValue = recognizeOptionShortForm(arg) ?: recognizeOptionFullForm(arg)
argValue?.descriptor?.let {
if (argValue.descriptor.type.hasParameter) {
if (index < args.size - 1) {
saveAsOption(argValue, args[index + 1])
index++
} else {
// An error, option with value without value.
printError("No value for ${argValue.descriptor.textDescription}")
}
} else {
// Boolean flags.
if (argValue.descriptor.fullName == "help") {
println(makeUsage())
exitProcess(0)
}
saveAsOption(argValue, "true")
}
} ?: run {
// Try save as argument.
if (!saveAsArg(arg, argumentsQueue)) {
printError("Unknown option $arg")
}
}
} else {
// Argument is found.
if (!saveAsArg(arg, argumentsQueue)) {
printError("Too many arguments! Couldn't process argument $arg!")
}
}
index++
}
// Postprocess results of parsing.
options.values.union(arguments.values).forEach { value ->
// Not inited, append default value if needed.
if (value.isEmpty()) {
value.addDefaultValue()
}
}
} catch (exception: ParsingException) {
printError(exception.message!!)
}
return ArgParserResult(programName)
}
/**
* Create message with usage description.
*/
internal fun makeUsage(): String {
val result = StringBuilder()
result.append("Usage: ${fullCommandName.joinToString(" ")} options_list\n")
if (arguments.isNotEmpty()) {
result.append("Arguments: \n")
arguments.forEach {
result.append(it.value.descriptor.helpMessage)
}
}
if (options.isNotEmpty()) {
result.append("Options: \n")
options.forEach {
result.append(it.value.descriptor.helpMessage)
}
}
return result.toString()
}
}
/**
* Output warning.
*
* @param message warning message.
*/
internal fun printWarning(message: String) {
println("WARNING $message")
}
@@ -3,7 +3,7 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
package org.jetbrains.kliopt package kotlinx.cli
/** /**
* Possible types of arguments. * Possible types of arguments.
@@ -23,7 +23,7 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
* *
* @param value value * @param value value
*/ */
abstract val conversion: (value: kotlin.String, name: kotlin.String, helpMessage: kotlin.String)->T abstract val conversion: (value: kotlin.String, name: kotlin.String)->T
/** /**
* Argument type for flags that can be only set/unset. * Argument type for flags that can be only set/unset.
@@ -32,8 +32,8 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String override val description: kotlin.String
get() = "" get() = ""
override val conversion: (value: kotlin.String, name: kotlin.String, _: kotlin.String) -> kotlin.Boolean override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.Boolean
get() = { value, _ , _ -> if (value == "false") false else true } get() = { value, _ -> if (value == "false") false else true }
} }
/** /**
@@ -43,8 +43,8 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String override val description: kotlin.String
get() = "{ String }" get() = "{ String }"
override val conversion: (value: kotlin.String, name: kotlin.String, _: kotlin.String) -> kotlin.String override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.String
get() = { value, _, _ -> value } get() = { value, _ -> value }
} }
/** /**
@@ -54,9 +54,9 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String override val description: kotlin.String
get() = "{ Int }" get() = "{ Int }"
override val conversion: (value: kotlin.String, name: kotlin.String, helpMessage: kotlin.String) -> kotlin.Int override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.Int
get() = { value, name, helpMessage -> value.toIntOrNull() get() = { value, name -> value.toIntOrNull()
?: error("Option $name is expected to be integer number. $value is provided.\n$helpMessage") } ?: throw ParsingException("Option $name is expected to be integer number. $value is provided.") }
} }
/** /**
@@ -66,10 +66,9 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String override val description: kotlin.String
get() = "{ Double }" get() = "{ Double }"
override val conversion: (value: kotlin.String, name: kotlin.String, override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.Double
helpMessage: kotlin.String) -> kotlin.Double get() = { value, name -> value.toDoubleOrNull()
get() = { value, name, helpMessage -> value.toDoubleOrNull() ?: throw ParsingException("Option $name is expected to be double number. $value is provided.") }
?: error("Option $name is expected to be double number. $value is provided.\n$helpMessage") }
} }
/** /**
@@ -79,9 +78,10 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
override val description: kotlin.String override val description: kotlin.String
get() = "{ Value should be one of $values }" get() = "{ Value should be one of $values }"
override val conversion: (value: kotlin.String, name: kotlin.String, override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.String
helpMessage: kotlin.String) -> kotlin.String get() = { value, name -> if (value in values) value
get() = { value, name, helpMessage -> if (value in values) value else throw ParsingException("Option $name is expected to be one of $values. $value is provided.") }
else error("Option $name is expected to be one of $values. $value is provided.\n$helpMessage") }
} }
} }
internal class ParsingException(message: String) : Exception(message)
@@ -0,0 +1,172 @@
/*
* 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 file.
*/
package kotlinx.cli
import kotlin.reflect.KProperty
/**
* Parsing value of option/argument.
*/
internal abstract class ParsingValue<T: Any, TResult: Any>(val descriptor: Descriptor<T, TResult>) {
/**
* Values of arguments.
*/
protected lateinit var parsedValue: TResult
/**
* Value origin.
*/
var valueOrigin = ArgParser.ValueOrigin.UNSET
protected set
/**
* Check if values of argument are empty.
*/
abstract fun isEmpty(): Boolean
/**
* Check if value of argument was initialized.
*/
protected fun valueIsInitialized() = ::parsedValue.isInitialized
/**
* Sace value from command line.
*
* @param stringValue value from command line.
*/
protected abstract fun saveValue(stringValue: String)
/**
* Set value of delegated property.
*/
fun setDelegatedValue(providedValue: TResult) {
parsedValue = providedValue
valueOrigin = ArgParser.ValueOrigin.REDEFINED
}
/**
* Add parsed value from command line.
*
* @param stringValue value from command line.
*/
internal fun addValue(stringValue: String) {
// Check of possibility to set several values to one option/argument.
if (descriptor is OptionDescriptor<*, *> && !descriptor.multiple &&
!isEmpty() && descriptor.delimiter == null) {
throw ParsingException("Try to provide more than one value for ${descriptor.fullName}.")
}
// Show deprecated warning only first time of using option/argument.
descriptor.deprecatedWarning?.let {
if (isEmpty())
println ("Warning: $it")
}
// Split value if needed.
if (descriptor is OptionDescriptor<*, *> && descriptor.delimiter != null) {
stringValue.split(descriptor.delimiter).forEach {
saveValue(it)
}
} else {
saveValue(stringValue)
}
}
/**
* Set default value to option.
*/
fun addDefaultValue() {
if (!descriptor.defaultValueSet && descriptor.required) {
throw ParsingException("Please, provide value for ${descriptor.textDescription}. It should be always set.")
}
if (descriptor.defaultValueSet) {
parsedValue = descriptor.defaultValue!!
valueOrigin = ArgParser.ValueOrigin.SET_DEFAULT_VALUE
}
}
/**
* Provide name for CLI entity.
*
* @param name name for CLI entity.
*/
fun provideName(name: String) {
descriptor.fullName ?: run { descriptor.fullName = name }
}
}
/**
* Single argument value.
*
* @property descriptor descriptor of option/argument.
*/
internal abstract class AbstractArgumentSingleValue<T: Any>(descriptor: Descriptor<T, T>):
ParsingValue<T, T>(descriptor) {
override fun saveValue(stringValue: String) {
if (!valueIsInitialized()) {
parsedValue = descriptor.type.conversion(stringValue, descriptor.fullName!!)
valueOrigin = ArgParser.ValueOrigin.SET_BY_USER
} else {
throw ParsingException("Try to provide more than one value $parsedValue and $stringValue for ${descriptor.fullName}.")
}
}
override fun isEmpty(): Boolean = !valueIsInitialized()
}
/**
* Single argument value.
*
* @property descriptor descriptor of option/argument.
*/
internal class ArgumentSingleValue<T: Any>(descriptor: Descriptor<T, T>): AbstractArgumentSingleValue<T>(descriptor),
ArgumentValueDelegate<T> {
override var value: T
get() = parsedValue
set(value) = setDelegatedValue(value)
}
/**
* Single nullable argument value.
*
* @property descriptor descriptor of option/argument.
*/
internal class ArgumentSingleNullableValue<T : Any>(descriptor: Descriptor<T, T>):
AbstractArgumentSingleValue<T>(descriptor), ArgumentValueDelegate<T?> {
private var setToNull = false
override var value: T?
get() = if (!isEmpty() && !setToNull) parsedValue else null
set(providedValue) = providedValue?.let {
setDelegatedValue(it)
setToNull = false
} ?: run {
setToNull = true
valueOrigin = ArgParser.ValueOrigin.REDEFINED
}
}
/**
* Multiple argument values.
*
* @property descriptor descriptor of option/argument.
*/
internal class ArgumentMultipleValues<T : Any>(descriptor: Descriptor<T, List<T>>):
ParsingValue<T, List<T>>(descriptor), ArgumentValueDelegate<List<T>> {
private val addedValue = mutableListOf<T>()
init {
parsedValue = addedValue
}
override var value: List<T>
get() = parsedValue
set(value) = setDelegatedValue(value)
override fun saveValue(stringValue: String) {
addedValue.add(descriptor.type.conversion(stringValue, descriptor.fullName!!))
valueOrigin = ArgParser.ValueOrigin.SET_BY_USER
}
override fun isEmpty() = parsedValue.isEmpty()
}
@@ -0,0 +1,165 @@
/*
* 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 file.
*/
package kotlinx.cli
import kotlin.reflect.KProperty
internal data class CLIEntityWrapper(var entity: CLIEntity<*>? = null)
/**
* Command line entity.
*
* @property owner parser which owns current entity.
*/
abstract class CLIEntity<TResult> internal constructor(internal val owner: CLIEntityWrapper) {
/**
* Wrapper for element - read only property.
* Needed to close set of variable [cliElement].
*/
lateinit var delegate: ArgumentValueDelegate<TResult>
internal set
/**
* Value of entity.
*/
var value: TResult
get() = delegate.value
set(value) { delegate.value = value }
/**
* Origin of argument value.
*/
val valueOrigin: ArgParser.ValueOrigin
get() = (delegate as ParsingValue<*, *>).valueOrigin
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueDelegate<TResult> {
(delegate as ParsingValue<*, *>).provideName(prop.name)
return delegate
}
}
/**
* Argument instance.
*/
abstract class Argument<TResult> internal constructor(owner: CLIEntityWrapper): CLIEntity<TResult>(owner)
/**
* Common single argument instance.
*/
abstract class AbstractSingleArgument<T: Any, TResult> internal constructor(owner: CLIEntityWrapper): Argument<TResult>(owner) {
/**
* Check descriptor for this kind of argument.
*/
internal fun checkDescriptor(descriptor: ArgDescriptor<*, *>) {
if (descriptor.number == null || descriptor.number > 1) {
failAssertion("Argument with single value can't be initialized with descriptor for multiple values.")
}
}
}
/**
* Argument with single non-nullable value.
*/
class SingleArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T, T>, owner: CLIEntityWrapper):
AbstractSingleArgument<T, T>(owner) {
init {
checkDescriptor(descriptor)
delegate = ArgumentSingleValue(descriptor)
}
}
/**
* Argument with single nullable value.
*/
class SingleNullableArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T, T>, owner: CLIEntityWrapper):
AbstractSingleArgument<T, T?>(owner){
init {
checkDescriptor(descriptor)
delegate = ArgumentSingleNullableValue(descriptor)
}
}
/**
* Argument with multiple values.
*/
class MultipleArgument<T : Any> internal constructor(descriptor: ArgDescriptor<T, List<T>>, owner: CLIEntityWrapper):
Argument<List<T>>(owner) {
init {
if (descriptor.number != null && descriptor.number == 1) {
failAssertion("Argument with multiple values can't be initialized with descriptor for single one.")
}
delegate = ArgumentMultipleValues(descriptor)
}
}
/**
* Allow argument have several values.
*
* @param number number of arguments are expected. In case of null value any number of arguments can be set.
*/
fun <T : Any, TResult> AbstractSingleArgument<T, TResult>.number(value: Int? = null): MultipleArgument<T> {
if (value != null && value == 1) {
error("number() modifier with value 1 is unavailable. It's already set to 1.")
}
val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
MultipleArgument(ArgDescriptor(type, fullName, value, description, listOfNotNull(defaultValue),
required, deprecatedWarning), owner)
}
owner.entity = newArgument
return newArgument
}
/**
* Set default value for argument.
*
* @param value default value.
*/
fun <T: Any, TResult> AbstractSingleArgument<T, TResult>.default(value: T): SingleArgument<T> {
val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
SingleArgument(ArgDescriptor(type, fullName, number, description, value, required, deprecatedWarning), owner)
}
owner.entity = newArgument
return newArgument
}
/**
* Set default value for argument.
*
* @param value default value.
*/
fun <T: Any> MultipleArgument<T>.default(value: Collection<T>): MultipleArgument<T> {
val newArgument = with((delegate as ParsingValue<T, List<T>>).descriptor as ArgDescriptor) {
MultipleArgument(ArgDescriptor(type, fullName, number, description, value.toList(),
required, deprecatedWarning), owner)
}
owner.entity = newArgument
return newArgument
}
/**
* Allow argument be unprovided in command line.
*/
fun <T: Any, TResult> AbstractSingleArgument<T, TResult>.optional(): SingleNullableArgument<T> {
val newArgument = with((delegate as ParsingValue<T, T>).descriptor as ArgDescriptor) {
SingleNullableArgument(ArgDescriptor(type, fullName, number, description, defaultValue,
false, deprecatedWarning), owner)
}
owner.entity = newArgument
return newArgument
}
/**
* Allow argument be unprovided in command line.
*/
fun <T: Any> MultipleArgument<T>.optional(): MultipleArgument<T> {
val newArgument = with((delegate as ParsingValue<T, List<T>>).descriptor as ArgDescriptor) {
MultipleArgument(ArgDescriptor(type, fullName, number, description,
defaultValue?.toList() ?: listOf(), false, deprecatedWarning), owner)
}
owner.entity = newArgument
return newArgument
}
fun failAssertion(message: String): Nothing = throw AssertionError(message)
@@ -0,0 +1,152 @@
/*
* 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 file.
*/
package kotlinx.cli
/**
* Common descriptor both for options and positional arguments.
*
* @property type option/argument type, one of [ArgType].
* @property fullName option/argument full name.
* @property description text description of option/argument.
* @property defaultValue default value for option/argument.
* @property required if option/argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @property deprecatedWarning text message with information in case if option is deprecated.
*/
internal abstract class Descriptor<T : Any, TResult>(val type: ArgType<T>,
var fullName: String? = null,
val description: String? = null,
val defaultValue: TResult? = null,
val required: Boolean = false,
val deprecatedWarning: String? = null) {
/**
* Text description for help message.
*/
abstract val textDescription: String
/**
* Help message for descriptor.
*/
abstract val helpMessage: String
/**
* Provide text description of value.
*
* @param value value got getting text description for.
*/
fun valueDescription(value: TResult?) = value?.let {
if (it is List<*> && it.isNotEmpty())
" [${it.joinToString { it.toString() }}]"
else if (it !is List<*>)
" [$it]"
else null
}
/**
* Flag to check if descriptor has set default value for option/argument.
*/
val defaultValueSet by lazy {
defaultValue != null && (defaultValue is List<*> && defaultValue.isNotEmpty() || defaultValue !is List<*>)
}
}
/**
* Option descriptor.
*
* Command line entity started with some prefix (-/--) and can have value as next entity in command line string.
*
* @property optionFullFormPrefix prefix used before full form of option.
* @property optionShortFromPrefix prefix used before short form of option.
* @property type option type, one of [ArgType].
* @property fullName option full name.
* @property shortName option short name.
* @property description text description of option.
* @property defaultValue default value for option.
* @property required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @property multiple if option can be repeated several times in command line with different values. All values are stored.
* @property delimiter delimiter that separate option provided as one string to several values.
* @property deprecatedWarning text message with information in case if option is deprecated.
*/
internal class OptionDescriptor<T : Any, TResult>(
val optionFullFormPrefix: String,
val optionShortFromPrefix: String,
type: ArgType<T>,
fullName: String? = null,
val shortName: String ? = null,
description: String? = null,
defaultValue: TResult? = null,
required: Boolean = false,
val multiple: Boolean = false,
val delimiter: String? = null,
deprecatedWarning: String? = null) : Descriptor<T, TResult>(type, fullName, description, defaultValue,
required, deprecatedWarning) {
override val textDescription: String
get() = "option $optionFullFormPrefix$fullName"
override val helpMessage: String
get() {
val result = StringBuilder()
result.append(" $optionFullFormPrefix$fullName")
shortName?.let { result.append(", $optionShortFromPrefix$it") }
valueDescription(defaultValue)?.let {
result.append("$it")
}
description?.let {result.append(" -> ${it}")}
if (required) result.append(" (always required)")
result.append(" ${type.description}")
deprecatedWarning?.let { result.append(" Warning: $it") }
result.append("\n")
return result.toString()
}
}
/**
* Argument descriptor.
*
* Command line entity which role is connected only with its position.
*
* @property type argument type, one of [ArgType].
* @property fullName argument full name.
* @property number expected number of values. Null means any possible number of values.
* @property description text description of argument.
* @property defaultValue default value for argument.
* @property required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
* @property deprecatedWarning text message with information in case if argument is deprecated.
*/
internal class ArgDescriptor<T : Any, TResult>(
type: ArgType<T>,
fullName: String?,
val number: Int? = null,
description: String? = null,
defaultValue: TResult? = null,
required: Boolean = true,
deprecatedWarning: String? = null) : Descriptor<T, TResult>(type, fullName, description, defaultValue,
required, deprecatedWarning) {
init {
// Check arguments number correctness.
number?.let {
if (it < 0)
error("Number of arguments for argument description $fullName should be greater than zero.")
}
}
override val textDescription: String
get() = "argument $fullName"
override val helpMessage: String
get() {
val result = StringBuilder()
result.append(" ${fullName}")
valueDescription(defaultValue)?.let {
result.append("$it")
}
description?.let { result.append(" -> ${it}") }
if (!required) result.append(" (optional)")
result.append(" ${type.description}")
deprecatedWarning?.let { result.append(" Warning: $it") }
result.append("\n")
return result.toString()
}
}
@@ -3,7 +3,7 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
package org.jetbrains.kliopt package kotlinx.cli
import kotlin.annotation.AnnotationTarget.* import kotlin.annotation.AnnotationTarget.*
@@ -15,7 +15,7 @@ import kotlin.annotation.AnnotationTarget.*
* *
* Any usage of a declaration annotated with `@ExperimentalCli` must be accepted either by * Any usage of a declaration annotated with `@ExperimentalCli` must be accepted either by
* annotating that usage with the [UseExperimental] annotation, e.g. `@UseExperimental(ExperimentalCli::class)`, * annotating that usage with the [UseExperimental] annotation, e.g. `@UseExperimental(ExperimentalCli::class)`,
* or by using the compiler argument `-Xuse-experimental=org.jetbrains.kliopt.ExperimentalCli`. * or by using the compiler argument `-Xuse-experimental=kotlinx.cli.ExperimentalCli`.
*/ */
@Experimental(level = Experimental.Level.WARNING) @Experimental(level = Experimental.Level.WARNING)
@Retention(AnnotationRetention.BINARY) @Retention(AnnotationRetention.BINARY)
@@ -0,0 +1,209 @@
/*
* 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 file.
*/
package kotlinx.cli
import kotlin.reflect.KProperty
/**
* Base interface for all possible types of options with multiple values.
*/
interface MultipleOptionType
/**
* Type of option with multiple values that can be provided several times in command line.
*/
class RepeatedOption: MultipleOptionType
/**
* Type of option with multiple values that are provided using delimiter.
*/
class DelimitedOption: MultipleOptionType
/**
* Type of option with multiple values that can be both provided several times in command line and using delimiter.
*/
class RepeatedDelimitedOption: MultipleOptionType
/**
* Option instance.
*/
abstract class Option<TResult> internal constructor(owner: CLIEntityWrapper): CLIEntity<TResult>(owner)
/**
* Common single option instance.
*/
abstract class AbstractSingleOption<T: Any, TResult> internal constructor(owner: CLIEntityWrapper): Option<TResult>(owner) {
/**
* Check descriptor for this kind of option.
*/
internal fun checkDescriptor(descriptor: OptionDescriptor<*, *>) {
if (descriptor.multiple || descriptor.delimiter != null) {
failAssertion("Option with single value can't be initialized with descriptor for multiple values.")
}
}
}
/**
* Option wit single non-nullable value.
*/
class SingleOption<T : Any> internal constructor(descriptor: OptionDescriptor<T, T>, owner: CLIEntityWrapper):
AbstractSingleOption<T, T>(owner) {
init {
checkDescriptor(descriptor)
delegate = ArgumentSingleValue(descriptor)
}
}
/**
* Option with single nullable value.
*/
class SingleNullableOption<T : Any> internal constructor(descriptor: OptionDescriptor<T, T>, owner: CLIEntityWrapper):
AbstractSingleOption<T, T?>(owner) {
init {
checkDescriptor(descriptor)
delegate = ArgumentSingleNullableValue(descriptor)
}
}
/**
* Option with multiple values.
*/
class MultipleOption<T : Any, OptionType: MultipleOptionType> internal constructor(descriptor: OptionDescriptor<T, List<T>>, owner: CLIEntityWrapper):
Option<List<T>>(owner) {
init {
if (!descriptor.multiple && descriptor.delimiter == null) {
failAssertion("Option with multiple values can't be initialized with descriptor for single one.")
}
delegate = ArgumentMultipleValues(descriptor)
}
}
/**
* Allow option have several values.
*/
fun <T : Any, TResult> AbstractSingleOption<T, TResult>.multiple(): MultipleOption<T, RepeatedOption> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
MultipleOption<T, RepeatedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, listOfNotNull(defaultValue),
required, true, delimiter, deprecatedWarning), owner)
}
owner.entity = newOption
return newOption
}
/**
* Allow option have several values.
*/
fun <T : Any> MultipleOption<T, DelimitedOption>.multiple(): MultipleOption<T, RepeatedDelimitedOption> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
if (multiple) {
error("Try to use modifier multiple() twice on option ${fullName ?: ""}")
}
MultipleOption<T, RepeatedDelimitedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(),
required, true, delimiter, deprecatedWarning), owner)
}
owner.entity = newOption
return newOption
}
/**
* Set default option value.
*
* @param value default value.
*/
fun <T: Any, TResult> AbstractSingleOption<T, TResult>.default(value: T): SingleOption<T> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
if (required) {
printWarning("required() is unneeded, because option with default value is defined.")
}
SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, value, required, multiple, delimiter, deprecatedWarning), owner)
}
owner.entity = newOption
return newOption
}
/**
* Set default option value.
*
* @param value default value.
*/
fun <T: Any, OptionType: MultipleOptionType>
MultipleOption<T, OptionType>.default(value: Collection<T>): MultipleOption<T, OptionType> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
if (required) {
printWarning("required() is unneeded, because option with default value is defined.")
}
MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
shortName, description, value.toList(),
required, multiple, delimiter, deprecatedWarning), owner)
}
owner.entity = newOption
return newOption
}
/**
* Require option to be always provided in command line.
*/
fun <T: Any, TResult> AbstractSingleOption<T, TResult>.required(): SingleOption<T> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
defaultValue?.let {
printWarning("required() is unneeded, because option with default value is defined.")
}
SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName,
shortName, description, defaultValue,
true, multiple, delimiter, deprecatedWarning), owner)
}
owner.entity = newOption
return newOption
}
/**
* Require option to be always provided in command line.
*/
fun <T: Any, OptionType: MultipleOptionType>
MultipleOption<T, OptionType>.required(): MultipleOption<T, OptionType> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
if (required) {
printWarning("required() is unneeded, because option with default value is defined.")
}
MultipleOption<T, OptionType>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(),
true, multiple, delimiter, deprecatedWarning), owner)
}
owner.entity = newOption
return newOption
}
/**
* Allow provide several options using [delimiter].
*
* @param delimiterValue delimiter used to separate string value to option values.
*/
fun <T : Any, TResult> AbstractSingleOption<T, TResult>.delimiter(delimiterValue: String): MultipleOption<T, DelimitedOption> {
val newOption = with((delegate as ParsingValue<T, T>).descriptor as OptionDescriptor) {
MultipleOption<T, DelimitedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, listOfNotNull(defaultValue),
required, multiple, delimiterValue, deprecatedWarning), owner)
}
owner.entity = newOption
return newOption
}
/**
* Allow provide several options using [delimiter].
*
* @param delimiterValue delimiter used to separate string value to option values.
*/
fun <T : Any> MultipleOption<T, RepeatedOption>.delimiter(delimiterValue: String): MultipleOption<T, RepeatedDelimitedOption> {
val newOption = with((delegate as ParsingValue<T, List<T>>).descriptor as OptionDescriptor) {
MultipleOption<T, RepeatedDelimitedOption>(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName,
description, defaultValue?.toList() ?: listOf(),
required, multiple, delimiterValue, deprecatedWarning), owner)
}
owner.entity = newOption
return newOption
}
@@ -3,8 +3,10 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
package org.jetbrains.kliopt package kotlinx.cli
import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlin.test.* import kotlin.test.*
class ArgumentsTests { class ArgumentsTests {
@@ -24,7 +26,7 @@ class ArgumentsTests {
fun testArgumetsWithAnyNumberOfValues() { fun testArgumetsWithAnyNumberOfValues() {
val argParser = ArgParser("testParser") val argParser = ArgParser("testParser")
val output by argParser.argument(ArgType.String, "output", "Output file") val output by argParser.argument(ArgType.String, "output", "Output file")
val inputs by argParser.arguments(ArgType.String, description = "Input files") val inputs by argParser.argument(ArgType.String, description = "Input files").number()
argParser.parse(arrayOf("out.txt", "input1.txt", "input2.txt", "input3.txt", argParser.parse(arrayOf("out.txt", "input1.txt", "input2.txt", "input3.txt",
"input4.txt")) "input4.txt"))
assertEquals("out.txt", output) assertEquals("out.txt", output)
@@ -34,7 +36,7 @@ class ArgumentsTests {
@Test @Test
fun testArgumetsWithSeveralValues() { fun testArgumetsWithSeveralValues() {
val argParser = ArgParser("testParser") val argParser = ArgParser("testParser")
val addendums by argParser.arguments(ArgType.Int, "addendums", 2, description = "Addendums") val addendums by argParser.argument(ArgType.Int, "addendums", description = "Addendums").number(2)
val output by argParser.argument(ArgType.String, "output", "Output file") val output by argParser.argument(ArgType.String, "output", "Output file")
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode") val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
argParser.parse(arrayOf("2", "-d", "3", "out.txt")) argParser.parse(arrayOf("2", "-d", "3", "out.txt"))
@@ -48,7 +50,7 @@ class ArgumentsTests {
@Test @Test
fun testSkippingExtraArguments() { fun testSkippingExtraArguments() {
val argParser = ArgParser("testParser", skipExtraArguments = true) val argParser = ArgParser("testParser", skipExtraArguments = true)
val addendums by argParser.arguments(ArgType.Int, "addendums", 2, description = "Addendums") val addendums by argParser.argument(ArgType.Int, "addendums", description = "Addendums").number(2)
val output by argParser.argument(ArgType.String, "output", "Output file") val output by argParser.argument(ArgType.String, "output", "Output file")
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode") val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
argParser.parse(arrayOf("2", "-d", "3", "out.txt", "something", "else", "in", "string")) argParser.parse(arrayOf("2", "-d", "3", "out.txt", "something", "else", "in", "string"))
@@ -3,20 +3,22 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
package org.jetbrains.kliopt package kotlinx.cli
import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlin.test.* import kotlin.test.*
class ErrorTests { class ErrorTests {
@Test @Test
fun testExtraArguments() { fun testExtraArguments() {
val argParser = ArgParser("testParser") val argParser = ArgParser("testParser")
val addendums by argParser.arguments(ArgType.Int, "addendums", 2, description = "Addendums") val addendums by argParser.argument(ArgType.Int, "addendums", description = "Addendums").number(2)
val output by argParser.argument(ArgType.String, "output", "Output file") val output by argParser.argument(ArgType.String, "output", "Output file")
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode") val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
val exception = assertFailsWith<IllegalStateException> { argParser.parse( val exception = assertFailsWith<IllegalStateException> { argParser.parse(
arrayOf("2", "-d", "3", "out.txt", "something", "else", "in", "string")) } arrayOf("2", "-d", "3", "out.txt", "something", "else", "in", "string")) }
assertTrue("Too many arguments! Couldn't proccess argument something" in exception.message!!) assertTrue("Too many arguments! Couldn't process argument something" in exception.message!!)
} }
@Test @Test
@@ -43,10 +45,9 @@ class ErrorTests {
@Test @Test
fun testWrongChoice() { fun testWrongChoice() {
val argParser = ArgParser("testParser") val argParser = ArgParser("testParser")
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report", val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report").default(false)
defaultValue = false) val renders by argParser.option(ArgType.Choice(listOf("text", "html")),
val renders by argParser.options(ArgType.Choice(listOf("text", "html")), "renders", "r", "Renders for showing information").multiple().default(listOf("text"))
"renders", "r", "Renders for showing information", listOf("text"), multiple = true)
val exception = assertFailsWith<IllegalStateException> { val exception = assertFailsWith<IllegalStateException> {
argParser.parse(arrayOf("-r", "xml")) argParser.parse(arrayOf("-r", "xml"))
} }
@@ -3,8 +3,12 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
@file:UseExperimental(ExperimentalCli::class) @file:UseExperimental(ExperimentalCli::class)
package org.jetbrains.kliopt package kotlinx.cli
import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlinx.cli.ExperimentalCli
import kotlinx.cli.Subcommand
import kotlin.math.exp import kotlin.math.exp
import kotlin.test.* import kotlin.test.*
@@ -13,19 +17,19 @@ class HelpTests {
fun testHelpMessage() { fun testHelpMessage() {
val argParser = ArgParser("test") val argParser = ArgParser("test")
val mainReport by argParser.argument(ArgType.String, description = "Main report for analysis") val mainReport by argParser.argument(ArgType.String, description = "Main report for analysis")
val compareToReport by argParser.argument(ArgType.String, description = "Report to compare to", required = false) val compareToReport by argParser.argument(ArgType.String, description = "Report to compare to").optional()
val output by argParser.option(ArgType.String, shortName = "o", description = "Output file") val output by argParser.option(ArgType.String, shortName = "o", description = "Output file")
val epsValue by argParser.option(ArgType.Double, "eps", "e", "Meaningful performance changes", 1.0) val epsValue by argParser.option(ArgType.Double, "eps", "e", "Meaningful performance changes").default(1.0)
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", val useShortForm by argParser.option(ArgType.Boolean, "short", "s",
"Show short version of report", defaultValue = false) "Show short version of report").default(false)
val renders by argParser.options(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")), val renders by argParser.option(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")),
shortName = "r", description = "Renders for showing information", defaultValue = listOf("text"), multiple = true) shortName = "r", description = "Renders for showing information").multiple().default(listOf("text"))
val user by argParser.option(ArgType.String, shortName = "u", description = "User access information for authorization") val user by argParser.option(ArgType.String, shortName = "u", description = "User access information for authorization")
argParser.parse(arrayOf("-h")) argParser.parse(arrayOf("main.txt"))
val helpOutput = argParser.makeUsage().trimIndent() val helpOutput = argParser.makeUsage().trimIndent()
val expectedOutput = """ val expectedOutput = """
Usage: test options_list Usage: test options_list
Arguments: Arguments:
mainReport -> Main report for analysis { String } mainReport -> Main report for analysis { String }
compareToReport -> Report to compare to (optional) { String } compareToReport -> Report to compare to (optional) { String }
@@ -37,31 +41,28 @@ Options:
--user, -u -> User access information for authorization { String } --user, -u -> User access information for authorization { String }
--help, -h -> Usage info --help, -h -> Usage info
""".trimIndent() """.trimIndent()
assertEquals(helpOutput, expectedOutput) assertEquals(expectedOutput, helpOutput)
} }
@Test @Test
fun testHelpForSubcommands() { fun testHelpForSubcommands() {
class Summary: Subcommand("summary") { class Summary: Subcommand("summary") {
val exec by option(ArgType.Choice(listOf("samples", "geomean")), val exec by option(ArgType.Choice(listOf("samples", "geomean")),
description = "Execution time way of calculation", defaultValue = "geomean") description = "Execution time way of calculation").default("geomean")
val execSamples by options(ArgType.String, "exec-samples", val execSamples by option(ArgType.String, "exec-samples",
description = "Samples used for execution time metric (value 'all' allows use all samples)", description = "Samples used for execution time metric (value 'all' allows use all samples)").delimiter(",")
delimiter = ",")
val execNormalize by option(ArgType.String, "exec-normalize", val execNormalize by option(ArgType.String, "exec-normalize",
description = "File with golden results which should be used for normalization") description = "File with golden results which should be used for normalization")
val compile by option(ArgType.Choice(listOf("samples", "geomean")), val compile by option(ArgType.Choice(listOf("samples", "geomean")),
description = "Compile time way of calculation", defaultValue = "geomean") description = "Compile time way of calculation").default("geomean")
val compileSamples by options(ArgType.String, "compile-samples", val compileSamples by option(ArgType.String, "compile-samples",
description = "Samples used for compile time metric (value 'all' allows use all samples)", description = "Samples used for compile time metric (value 'all' allows use all samples)").delimiter(",")
delimiter = ",")
val compileNormalize by option(ArgType.String, "compile-normalize", val compileNormalize by option(ArgType.String, "compile-normalize",
description = "File with golden results which should be used for normalization") description = "File with golden results which should be used for normalization")
val codesize by option(ArgType.Choice(listOf("samples", "geomean")), val codesize by option(ArgType.Choice(listOf("samples", "geomean")),
description = "Code size way of calculation", defaultValue = "geomean") description = "Code size way of calculation").default("geomean")
val codesizeSamples by options(ArgType.String, "codesize-samples", val codesizeSamples by option(ArgType.String, "codesize-samples",
description = "Samples used for code size metric (value 'all' allows use all samples)", description = "Samples used for code size metric (value 'all' allows use all samples)").delimiter(",")
delimiter = ",")
val codesizeNormalize by option(ArgType.String, "codesize-normalize", val codesizeNormalize by option(ArgType.String, "codesize-normalize",
description = "File with golden results which should be used for normalization") description = "File with golden results which should be used for normalization")
val user by option(ArgType.String, shortName = "u", description = "User access information for authorization") val user by option(ArgType.String, shortName = "u", description = "User access information for authorization")
@@ -75,21 +76,25 @@ Options:
// Parse args. // Parse args.
val argParser = ArgParser("test") val argParser = ArgParser("test")
argParser.subcommands(action) argParser.subcommands(action)
argParser.parse(arrayOf("summary", "-h")) argParser.parse(arrayOf("summary", "out.txt"))
val helpOutput = argParser.makeUsage().trimIndent() val helpOutput = action.makeUsage().trimIndent()
val expectedOutput = """ val expectedOutput = """
Usage: test summary options_list Usage: test summary options_list
Arguments: Arguments:
mainReport -> Main report for analysis { String } mainReport -> Main report for analysis { String }
compareToReport -> Report to compare to (optional) { String }
Options: Options:
--output, -o -> Output file { String } --exec [geomean] -> Execution time way of calculation { Value should be one of [samples, geomean] }
--eps, -e [1.0] -> Meaningful performance changes { Double } --exec-samples -> Samples used for execution time metric (value 'all' allows use all samples) { String }
--short, -s [false] -> Show short version of report --exec-normalize -> File with golden results which should be used for normalization { String }
--renders, -r [text] -> Renders for showing information { Value should be one of [text, html, teamcity, statistics, metrics] } --compile [geomean] -> Compile time way of calculation { Value should be one of [samples, geomean] }
--compile-samples -> Samples used for compile time metric (value 'all' allows use all samples) { String }
--compile-normalize -> File with golden results which should be used for normalization { String }
--codesize [geomean] -> Code size way of calculation { Value should be one of [samples, geomean] }
--codesize-samples -> Samples used for code size metric (value 'all' allows use all samples) { String }
--codesize-normalize -> File with golden results which should be used for normalization { String }
--user, -u -> User access information for authorization { String } --user, -u -> User access information for authorization { String }
--help, -h -> Usage info --help, -h -> Usage info
""".trimIndent() """.trimIndent()
assertEquals(helpOutput, expectedOutput) assertEquals(expectedOutput, helpOutput)
} }
} }
@@ -3,8 +3,10 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
package org.jetbrains.kliopt package kotlinx.cli
import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlin.test.* import kotlin.test.*
class OptionsTests { class OptionsTests {
@@ -41,9 +43,9 @@ class OptionsTests {
@Test @Test
fun testMultipleOptions() { fun testMultipleOptions() {
val argParser = ArgParser("testParser") val argParser = ArgParser("testParser")
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report", defaultValue = false) val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report").default(false)
val renders by argParser.options(ArgType.Choice(listOf("text", "html", "xml", "json")), val renders by argParser.option(ArgType.Choice(listOf("text", "html", "xml", "json")),
"renders", "r", "Renders for showing information", listOf("text"), multiple = true) "renders", "r", "Renders for showing information").multiple().default(listOf("text"))
argParser.parse(arrayOf("-s", "-r", "text", "-r", "json")) argParser.parse(arrayOf("-s", "-r", "text", "-r", "json"))
assertEquals(true, useShortForm) assertEquals(true, useShortForm)
assertEquals(2, renders.size) assertEquals(2, renders.size)
@@ -55,12 +57,34 @@ class OptionsTests {
@Test @Test
fun testDefaultOptions() { fun testDefaultOptions() {
val argParser = ArgParser("testParser") val argParser = ArgParser("testParser")
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report", defaultValue = false) val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report").default(false)
val renders by argParser.options(ArgType.Choice(listOf("text", "html", "xml", "json")), val renders by argParser.option(ArgType.Choice(listOf("text", "html", "xml", "json")),
"renders", "r", "Renders for showing information", listOf("text"), multiple = true) "renders", "r", "Renders for showing information").multiple().default(listOf("text"))
val output by argParser.option(ArgType.String, "output", "o", "Output file") val output by argParser.option(ArgType.String, "output", "o", "Output file")
argParser.parse(arrayOf("-o", "out.txt")) argParser.parse(arrayOf("-o", "out.txt"))
assertEquals(false, useShortForm) assertEquals(false, useShortForm)
assertEquals("text", renders[0]) assertEquals("text", renders[0])
} }
@Test
fun testResetOptionsValues() {
val argParser = ArgParser("testParser")
val useShortFormOption = argParser.option(ArgType.Boolean, "short", "s", "Show short version of report").default(false)
var useShortForm by useShortFormOption
val rendersOption = argParser.option(ArgType.Choice(listOf("text", "html", "xml", "json")),
"renders", "r", "Renders for showing information").multiple().default(listOf("text"))
var renders by rendersOption
val outputOption = argParser.option(ArgType.String, "output", "o", "Output file")
var output by outputOption
argParser.parse(arrayOf("-o", "out.txt"))
output = null
useShortForm = true
renders = listOf()
assertEquals(true, useShortForm)
assertEquals(null, output)
assertEquals(0, renders.size)
assertEquals(ArgParser.ValueOrigin.REDEFINED, outputOption.valueOrigin)
assertEquals(ArgParser.ValueOrigin.REDEFINED, useShortFormOption.valueOrigin)
assertEquals(ArgParser.ValueOrigin.REDEFINED, rendersOption.valueOrigin)
}
} }
@@ -3,8 +3,12 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
@file:UseExperimental(ExperimentalCli::class) @file:UseExperimental(ExperimentalCli::class)
package org.jetbrains.kliopt package kotlinx.cli
import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlinx.cli.ExperimentalCli
import kotlinx.cli.Subcommand
import kotlin.test.* import kotlin.test.*
class SubcommandsTests { class SubcommandsTests {
@@ -14,7 +18,7 @@ class SubcommandsTests {
val output by argParser.option(ArgType.String, "output", "o", "Output file") val output by argParser.option(ArgType.String, "output", "o", "Output file")
class Summary: Subcommand("summary") { class Summary: Subcommand("summary") {
val invert by option(ArgType.Boolean, "invert", "i", "Invert results") val invert by option(ArgType.Boolean, "invert", "i", "Invert results")
val addendums by arguments(ArgType.Int, "addendums", description = "Addendums") val addendums by argument(ArgType.Int, "addendums", description = "Addendums").number()
var result: Int = 0 var result: Int = 0
override fun execute() { override fun execute() {
@@ -32,7 +36,7 @@ class SubcommandsTests {
@Test @Test
fun testCommonOptions() { fun testCommonOptions() {
abstract class CommonOptions(name: String): Subcommand(name) { abstract class CommonOptions(name: String): Subcommand(name) {
val numbers by arguments(ArgType.Int, "numbers", description = "Numbers") val numbers by argument(ArgType.Int, "numbers", description = "Numbers").number()
} }
class Summary: CommonOptions("summary") { class Summary: CommonOptions("summary") {
val invert by option(ArgType.Boolean, "invert", "i", "Invert results") val invert by option(ArgType.Boolean, "invert", "i", "Invert results")
@@ -70,7 +74,7 @@ class SubcommandsTests {
val argParser = ArgParser("testParser") val argParser = ArgParser("testParser")
class Summary: Subcommand("summary") { class Summary: Subcommand("summary") {
val addendums by arguments(ArgType.Int, "addendums", description = "Addendums") val addendums by argument(ArgType.Int, "addendums", description = "Addendums").number()
var result: Int = 0 var result: Int = 0
override fun execute() { override fun execute() {
@@ -15,7 +15,8 @@ interface KonanLibraryResolver {
fun resolveWithDependencies( fun resolveWithDependencies(
unresolvedLibraries: List<UnresolvedLibrary>, unresolvedLibraries: List<UnresolvedLibrary>,
noStdLib: Boolean = false, noStdLib: Boolean = false,
noDefaultLibs: Boolean = false noDefaultLibs: Boolean = false,
noEndorsedLibs: Boolean = false
): KonanLibraryResolveResult ): KonanLibraryResolveResult
} }
@@ -30,13 +30,14 @@ internal class KonanLibraryResolverImpl(
override fun resolveWithDependencies( override fun resolveWithDependencies(
unresolvedLibraries: List<UnresolvedLibrary>, unresolvedLibraries: List<UnresolvedLibrary>,
noStdLib: Boolean, noStdLib: Boolean,
noDefaultLibs: Boolean noDefaultLibs: Boolean,
) = findLibraries(unresolvedLibraries, noStdLib, noDefaultLibs) noEndorsedLibs: Boolean
) = findLibraries(unresolvedLibraries, noStdLib, noDefaultLibs, noEndorsedLibs)
.leaveDistinct() .leaveDistinct()
.resolveDependencies() .resolveDependencies()
/** /**
* Returns the list of libraries based on [libraryNames], [noStdLib] and [noDefaultLibs] criteria. * Returns the list of libraries based on [libraryNames], [noStdLib], [noDefaultLibs] and [noEndorsedLibs] criteria.
* *
* This method does not return any libraries that might be available via transitive dependencies * This method does not return any libraries that might be available via transitive dependencies
* from the original library set (root set). * from the original library set (root set).
@@ -44,14 +45,15 @@ internal class KonanLibraryResolverImpl(
private fun findLibraries( private fun findLibraries(
unresolvedLibraries: List<UnresolvedLibrary>, unresolvedLibraries: List<UnresolvedLibrary>,
noStdLib: Boolean, noStdLib: Boolean,
noDefaultLibs: Boolean noDefaultLibs: Boolean,
noEndorsedLibs: Boolean
): List<KonanLibrary> { ): List<KonanLibrary> {
val userProvidedLibraries = unresolvedLibraries.asSequence() val userProvidedLibraries = unresolvedLibraries.asSequence()
.map { searchPathResolver.resolve(it) } .map { searchPathResolver.resolve(it) }
.toList() .toList()
val defaultLibraries = searchPathResolver.defaultLinks(noStdLib, noDefaultLibs) val defaultLibraries = searchPathResolver.defaultLinks(noStdLib, noDefaultLibs, noEndorsedLibs)
// Make sure the user provided ones appear first, so that // Make sure the user provided ones appear first, so that
// they have precedence over defaults when duplicates are eliminated. // they have precedence over defaults when duplicates are eliminated.
@@ -160,7 +160,7 @@ class Library(val name: String, val requestedRepository: String?, val target: St
distributionKlib = Distribution().klib, distributionKlib = Distribution().klib,
skipCurrentDir = true, skipCurrentDir = true,
logger = KlibToolLogger) logger = KlibToolLogger)
resolver.defaultLinks(false, true) resolver.defaultLinks(false, true, true)
.mapTo(defaultModules) { .mapTo(defaultModules) {
DefaultDeserializedDescriptorFactory.createDescriptor( DefaultDeserializedDescriptorFactory.createDescriptor(
it, versionSpec, storageManager, module.builtIns) it, versionSpec, storageManager, module.builtIns)
+1 -1
View File
@@ -17,7 +17,7 @@ benchmark {
mingwSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/mingw") mingwSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/mingw")
posixSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/posix") posixSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/posix")
dependencies.common(project(":endorsedLibraries:kliopt")) dependencies.common(project(":endorsedLibraries:kotlinx.cli"))
} }
val native = kotlin.targets.getByName("native") as KotlinNativeTarget val native = kotlin.targets.getByName("native") as KotlinNativeTarget
+1 -1
View File
@@ -19,7 +19,7 @@ import org.jetbrains.structsProducedByMacrosBenchmarks.*
import org.jetbrains.benchmarksLauncher.* import org.jetbrains.benchmarksLauncher.*
import org.jetbrains.structsBenchmarks.* import org.jetbrains.structsBenchmarks.*
import org.jetbrains.typesBenchmarks.* import org.jetbrains.typesBenchmarks.*
import org.jetbrains.kliopt.* import kotlinx.cli.*
class CinteropLauncher : Launcher() { class CinteropLauncher : Launcher() {
override val benchmarks = BenchmarksCollection( override val benchmarks = BenchmarksCollection(
+2
View File
@@ -37,6 +37,8 @@ kotlin {
} }
kotlin.srcDir "$toolsPath/benchmarks/shared/src" kotlin.srcDir "$toolsPath/benchmarks/shared/src"
kotlin.srcDir "$toolsPath/benchmarksAnalyzer/src/main/kotlin" kotlin.srcDir "$toolsPath/benchmarksAnalyzer/src/main/kotlin"
kotlin.srcDir "$rootProject.projectDir/endorsedLibraries/kotlinx.cli/src/main/kotlin"
kotlin.srcDir "$rootProject.projectDir/endorsedLibraries/kotlinx.cli/src/main/kotlin-native"
kotlin.srcDir "$toolsPath/benchmarksAnalyzer/src/main/kotlin-native" kotlin.srcDir "$toolsPath/benchmarksAnalyzer/src/main/kotlin-native"
} }
} }
+1 -1
View File
@@ -21,7 +21,7 @@ benchmark {
posixSrcDirs = listOf("../shared/src/main/kotlin-native/posix") posixSrcDirs = listOf("../shared/src/main/kotlin-native/posix")
linkerOpts = listOf("$buildDir/pi.o") linkerOpts = listOf("$buildDir/pi.o")
dependencies.common(project(":endorsedLibraries:kliopt")) dependencies.common(project(":endorsedLibraries:kotlinx.cli"))
} }
val compileLibary by tasks.creating { val compileLibary by tasks.creating {
@@ -4,7 +4,7 @@
*/ */
import org.jetbrains.benchmarksLauncher.* import org.jetbrains.benchmarksLauncher.*
import org.jetbrains.kliopt.* import kotlinx.cli.*
expect class NumericalLauncher() : Launcher { expect class NumericalLauncher() : Launcher {
} }
+1 -1
View File
@@ -21,7 +21,7 @@ benchmark {
posixSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/posix") posixSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/posix")
linkerOpts = listOf("-L$buildDir", "-lcomplexnumbers") linkerOpts = listOf("-L$buildDir", "-lcomplexnumbers")
dependencies.common(project(":endorsedLibraries:kliopt")) dependencies.common(project(":endorsedLibraries:kotlinx.cli"))
} }
val compileLibary by tasks.creating { val compileLibary by tasks.creating {
@@ -6,7 +6,7 @@
import org.jetbrains.benchmarksLauncher.* import org.jetbrains.benchmarksLauncher.*
import org.jetbrains.complexNumbers.* import org.jetbrains.complexNumbers.*
import org.jetbrains.kliopt.* import kotlinx.cli.*
class ObjCInteropLauncher: Launcher() { class ObjCInteropLauncher: Launcher() {
override val benchmarks = BenchmarksCollection( override val benchmarks = BenchmarksCollection(
+1 -1
View File
@@ -15,5 +15,5 @@ benchmark {
mingwSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/mingw") mingwSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/mingw")
posixSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/posix") posixSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/posix")
dependencies.common(project(":endorsedLibraries:kliopt")) dependencies.common(project(":endorsedLibraries:kotlinx.cli"))
} }
+1 -1
View File
@@ -18,7 +18,7 @@
import org.jetbrains.ring.* import org.jetbrains.ring.*
import octoTest import octoTest
import org.jetbrains.benchmarksLauncher.* import org.jetbrains.benchmarksLauncher.*
import org.jetbrains.kliopt.* import kotlinx.cli.*
class RingLauncher : Launcher() { class RingLauncher : Launcher() {
@@ -17,7 +17,7 @@
package org.jetbrains.benchmarksLauncher package org.jetbrains.benchmarksLauncher
import org.jetbrains.report.BenchmarkResult import org.jetbrains.report.BenchmarkResult
import org.jetbrains.kliopt.* import kotlinx.cli.*
abstract class Launcher { abstract class Launcher {
@@ -118,17 +118,18 @@ abstract class Launcher {
abstract class BenchmarkArguments(argParser: ArgParser) abstract class BenchmarkArguments(argParser: ArgParser)
class BaseBenchmarkArguments(argParser: ArgParser): BenchmarkArguments(argParser) { class BaseBenchmarkArguments(argParser: ArgParser): BenchmarkArguments(argParser) {
val warmup by argParser.option(ArgType.Int, shortName = "w", description = "Number of warm up iterations", val warmup by argParser.option(ArgType.Int, shortName = "w", description = "Number of warm up iterations")
defaultValue = 20) .default(20)
val repeat by argParser.option(ArgType.Int, shortName = "r", description = "Number of each benchmark run", val repeat by argParser.option(ArgType.Int, shortName = "r", description = "Number of each benchmark run").
defaultValue = 60) default(60)
val prefix by argParser.option(ArgType.String, shortName = "p", description = "Prefix added to benchmark name", val prefix by argParser.option(ArgType.String, shortName = "p", description = "Prefix added to benchmark name")
defaultValue = "") .default("")
val output by argParser.option(ArgType.String, shortName = "o", description = "Output file") val output by argParser.option(ArgType.String, shortName = "o", description = "Output file")
val filter by argParser.options(ArgType.String, shortName = "f", description = "Benchmark to run", multiple = true) val filter by argParser.option(ArgType.String, shortName = "f", description = "Benchmark to run").multiple()
val filterRegex by argParser.options(ArgType.String, shortName = "fr", val filterRegex by argParser.option(ArgType.String, shortName = "fr",
description = "Benchmark to run, described by a regular expression", multiple = true) description = "Benchmark to run, described by a regular expression").multiple()
val verbose by argParser.option(ArgType.Boolean, shortName = "v", description = "Verbose mode of running", defaultValue = false) val verbose by argParser.option(ArgType.Boolean, shortName = "v", description = "Verbose mode of running")
.default(false)
} }
object BenchmarksRunner { object BenchmarksRunner {
+3 -3
View File
@@ -36,7 +36,7 @@ kotlin {
commonMain { commonMain {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion"
implementation project(":endorsedLibraries:kliopt") implementation project(":endorsedLibraries:kotlinx.cli")
} }
kotlin.srcDir "src" kotlin.srcDir "src"
kotlin.srcDir "../shared/src/main/kotlin" kotlin.srcDir "../shared/src/main/kotlin"
@@ -47,9 +47,9 @@ kotlin {
kotlin.srcDir "../shared/src/main/kotlin-native/common" kotlin.srcDir "../shared/src/main/kotlin-native/common"
kotlin.srcDir "../shared/src/main/kotlin-native/posix" kotlin.srcDir "../shared/src/main/kotlin-native/posix"
// Exclude kliopt added in commonMain dependencies and inherited by macosMain. // Exclude kotlinx.cli added in commonMain dependencies and inherited by macosMain.
configurations.getByName(implementationConfigurationName) { configurations.getByName(implementationConfigurationName) {
exclude module: "kliopt" exclude module: "kotlinx.cli"
} }
} }
} }
@@ -4,7 +4,7 @@
*/ */
import org.jetbrains.benchmarksLauncher.* import org.jetbrains.benchmarksLauncher.*
import org.jetbrains.kliopt.* import kotlinx.cli.*
class SwiftLauncher: Launcher() { class SwiftLauncher: Launcher() {
override val benchmarks = BenchmarksCollection( override val benchmarks = BenchmarksCollection(
+1
View File
@@ -67,6 +67,7 @@ project.rootProject.ext.platformManager.enabled.each { target ->
defFile df.file defFile df.file
artifactName df.name artifactName df.name
noDefaultLibs true noDefaultLibs true
noEndorsedLibs true
libraries { libraries {
klibs df.config.depends klibs df.config.depends
} }
+1 -1
View File
@@ -45,7 +45,7 @@ if (System.getProperty("os.name") == "Mac OS X") {
include ':platformLibs' include ':platformLibs'
include ':endorsedLibraries' include ':endorsedLibraries'
include ':endorsedLibraries:kliopt' include ':endorsedLibraries:kotlinx.cli'
if (hasProperty("kotlinProjectPath")) { if (hasProperty("kotlinProjectPath")) {
include ':runtime:generator' include ':runtime:generator'
@@ -22,7 +22,7 @@ interface SearchPathResolver : WithLogger {
fun resolutionSequence(givenPath: String): Sequence<File> fun resolutionSequence(givenPath: String): Sequence<File>
fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean = false): KonanLibraryImpl fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean = false): KonanLibraryImpl
fun resolve(givenPath: String): KonanLibraryImpl fun resolve(givenPath: String): KonanLibraryImpl
fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<KonanLibraryImpl> fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean, noEndorsedLibs: Boolean): List<KonanLibraryImpl>
} }
// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface! // FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface!
@@ -157,21 +157,34 @@ internal open class KonanLibrarySearchPathResolver(
val defaultRoots: List<File> val defaultRoots: List<File>
get() = listOfNotNull(distHead, distPlatformHead).filter { it.exists } get() = listOfNotNull(distHead, distPlatformHead).filter { it.exists }
override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<KonanLibraryImpl> { private fun getDefaultLibrariesFromDir(directory: File) =
if (directory.exists) {
directory.listFiles
.asSequence()
.filterNot { it.name.startsWith('.') }
.filterNot { it.name.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT) == KONAN_STDLIB_NAME }
.map { UnresolvedLibrary(it.absolutePath, null) }
.map { resolve(it, isDefaultLink = true) }
} else emptySequence()
override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean, noEndorsedLibs: Boolean): List<KonanLibraryImpl> {
val result = mutableListOf<KonanLibraryImpl>() val result = mutableListOf<KonanLibraryImpl>()
if (!noStdLib) { if (!noStdLib) {
result.add(resolve(UnresolvedLibrary(KONAN_STDLIB_NAME, null), true)) result.add(resolve(UnresolvedLibrary(KONAN_STDLIB_NAME, null), true))
} }
// Endorsed libraries in distHead.
if (!noEndorsedLibs) {
distHead?.let {
result.addAll(getDefaultLibrariesFromDir(it))
}
}
// Platform libraries resolve.
if (!noDefaultLibs) { if (!noDefaultLibs) {
val defaultLibs = defaultRoots.flatMap { it.listFiles } distPlatformHead?.let {
.asSequence() result.addAll(getDefaultLibrariesFromDir(it))
.filterNot { it.name.startsWith('.') } }
.filterNot { it.name.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT) == KONAN_STDLIB_NAME }
.map { UnresolvedLibrary(it.absolutePath, null) }
.map { resolve(it, isDefaultLink = true) }
result.addAll(defaultLibs)
} }
return result return result
+4 -4
View File
@@ -48,7 +48,7 @@ kotlin {
} }
kotlin.srcDir '../benchmarks/shared/src' kotlin.srcDir '../benchmarks/shared/src'
kotlin.srcDir 'src/main/kotlin' kotlin.srcDir 'src/main/kotlin'
kotlin.srcDir '../../endorsedLibraries/kliopt/src/main/kotlin' kotlin.srcDir '../../endorsedLibraries/kotlinx.cli/src/main/kotlin'
} }
commonTest { commonTest {
dependencies { dependencies {
@@ -71,21 +71,21 @@ kotlin {
nativeMain { nativeMain {
dependsOn commonMain dependsOn commonMain
kotlin.srcDir 'src/main/kotlin-native' kotlin.srcDir 'src/main/kotlin-native'
kotlin.srcDir '../../endorsedLibraries/kliopt/src/main/kotlin-native' kotlin.srcDir '../../endorsedLibraries/kotlinx.cli/src/main/kotlin-native'
} }
jvmMain { jvmMain {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
} }
kotlin.srcDir 'src/main/kotlin-jvm' kotlin.srcDir 'src/main/kotlin-jvm'
kotlin.srcDir '../../endorsedLibraries/kliopt/src/main/kotlin-jvm' kotlin.srcDir '../../endorsedLibraries/kotlinx.cli/src/main/kotlin-jvm'
} }
jsMain { jsMain {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlinVersion"
} }
kotlin.srcDir 'src/main/kotlin-js' kotlin.srcDir 'src/main/kotlin-js'
kotlin.srcDir '../../endorsedLibraries/kliopt/src/main/kotlin-js' kotlin.srcDir '../../endorsedLibraries/kotlinx.cli/src/main/kotlin-js'
} }
linuxMain { dependsOn nativeMain } linuxMain { dependsOn nativeMain }
windowsMain { dependsOn nativeMain } windowsMain { dependsOn nativeMain }
@@ -17,7 +17,7 @@
import org.jetbrains.analyzer.sendGetRequest import org.jetbrains.analyzer.sendGetRequest
import org.jetbrains.analyzer.readFile import org.jetbrains.analyzer.readFile
import org.jetbrains.analyzer.SummaryBenchmarksReport import org.jetbrains.analyzer.SummaryBenchmarksReport
import org.jetbrains.kliopt.* import kotlinx.cli.*
import org.jetbrains.renders.* import org.jetbrains.renders.*
import org.jetbrains.report.BenchmarksReport import org.jetbrains.report.BenchmarksReport
import org.jetbrains.report.BenchmarkResult import org.jetbrains.report.BenchmarkResult
@@ -106,31 +106,30 @@ fun parseNormalizeResults(results: String): Map<String, Map<String, Double>> {
fun main(args: Array<String>) { fun main(args: Array<String>) {
class Summary: Subcommand("summary") { class Summary: Subcommand("summary") {
val exec by option(ArgType.Choice(listOf("samples", "geomean")), val exec by option(ArgType.Choice(listOf("samples", "geomean")),
description = "Execution time way of calculation", defaultValue = "geomean") description = "Execution time way of calculation").default("geomean")
val execSamples by options(ArgType.String, "exec-samples", val execSamples by option(ArgType.String, "exec-samples",
description = "Samples used for execution time metric (value 'all' allows use all samples)", description = "Samples used for execution time metric (value 'all' allows use all samples)")
delimiter = ",") .delimiter(",")
val execNormalize by option(ArgType.String, "exec-normalize", val execNormalize by option(ArgType.String, "exec-normalize",
description = "File with golden results which should be used for normalization") description = "File with golden results which should be used for normalization")
val compile by option(ArgType.Choice(listOf("samples", "geomean")), val compile by option(ArgType.Choice(listOf("samples", "geomean")),
description = "Compile time way of calculation", defaultValue = "geomean") description = "Compile time way of calculation").default("geomean")
val compileSamples by options(ArgType.String, "compile-samples", val compileSamples by option(ArgType.String, "compile-samples",
description = "Samples used for compile time metric (value 'all' allows use all samples)", description = "Samples used for compile time metric (value 'all' allows use all samples)")
delimiter = ",") .delimiter(",")
val compileNormalize by option(ArgType.String, "compile-normalize", val compileNormalize by option(ArgType.String, "compile-normalize",
description = "File with golden results which should be used for normalization") description = "File with golden results which should be used for normalization")
val codesize by option(ArgType.Choice(listOf("samples", "geomean")), val codesize by option(ArgType.Choice(listOf("samples", "geomean")),
description = "Code size way of calculation", defaultValue = "geomean") description = "Code size way of calculation").default("geomean")
val codesizeSamples by options(ArgType.String, "codesize-samples", val codesizeSamples by option(ArgType.String, "codesize-samples",
description = "Samples used for code size metric (value 'all' allows use all samples)", description = "Samples used for code size metric (value 'all' allows use all samples)").delimiter(",")
delimiter = ",")
val codesizeNormalize by option(ArgType.String, "codesize-normalize", val codesizeNormalize by option(ArgType.String, "codesize-normalize",
description = "File with golden results which should be used for normalization") description = "File with golden results which should be used for normalization")
val user by option(ArgType.String, shortName = "u", description = "User access information for authorization") val user by option(ArgType.String, shortName = "u", description = "User access information for authorization")
val mainReport by argument(ArgType.String, description = "Main report for analysis") val mainReport by argument(ArgType.String, description = "Main report for analysis")
override fun execute() { override fun execute() {
val benchsReport = SummaryBenchmarksReport(getBenchmarkReport(mainReport!!, user)) val benchsReport = SummaryBenchmarksReport(getBenchmarkReport(mainReport, user))
val results = mutableListOf<String>() val results = mutableListOf<String>()
val executionNormalize = execNormalize?.let { val executionNormalize = execNormalize?.let {
parseNormalizeResults(getFileContent(it)) parseNormalizeResults(getFileContent(it))
@@ -171,19 +170,20 @@ fun main(args: Array<String>) {
val argParser = ArgParser("benchmarksAnalyzer") val argParser = ArgParser("benchmarksAnalyzer")
argParser.subcommands(action) argParser.subcommands(action)
val mainReport by argParser.argument(ArgType.String, description = "Main report for analysis") val mainReport by argParser.argument(ArgType.String, description = "Main report for analysis")
val compareToReport by argParser.argument(ArgType.String, description = "Report to compare to", required = false) val compareToReport by argParser.argument(ArgType.String, description = "Report to compare to").optional()
val output by argParser.option(ArgType.String, shortName = "o", description = "Output file") val output by argParser.option(ArgType.String, shortName = "o", description = "Output file")
val epsValue by argParser.option(ArgType.Double, "eps", "e", "Meaningful performance changes", 1.0) val epsValue by argParser.option(ArgType.Double, "eps", "e",
"Meaningful performance changes").default(1.0)
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", val useShortForm by argParser.option(ArgType.Boolean, "short", "s",
"Show short version of report", defaultValue = false) "Show short version of report").default(false)
val renders by argParser.options(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")), val renders by argParser.option(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")),
shortName = "r", description = "Renders for showing information", defaultValue = listOf("text"), multiple = true) shortName = "r", description = "Renders for showing information").multiple().default(listOf("text"))
val user by argParser.option(ArgType.String, shortName = "u", description = "User access information for authorization") val user by argParser.option(ArgType.String, shortName = "u", description = "User access information for authorization")
if (argParser.parse(args).commandName == "benchmarksAnalyzer") { if (argParser.parse(args).commandName == "benchmarksAnalyzer") {
// Read contents of file. // Read contents of file.
val mainBenchsReport = getBenchmarkReport(mainReport!!, user) val mainBenchsReport = getBenchmarkReport(mainReport, user)
var compareToBenchsReport = compareToReport?.let { var compareToBenchsReport = compareToReport?.let {
getBenchmarkReport(it, user) getBenchmarkReport(it, user)
} }
@@ -194,7 +194,7 @@ fun main(args: Array<String>) {
epsValue) epsValue)
var outputFile = output var outputFile = output
renders?.forEach { renders.forEach {
Render.getRenderByName(it).print(summaryReport, useShortForm, outputFile) Render.getRenderByName(it).print(summaryReport, useShortForm, outputFile)
outputFile = null outputFile = null
} }
@@ -160,6 +160,7 @@ abstract class KonanBuildingConfig<T: KonanBuildingTask>(private val name_: Stri
override fun libraries(configure: KonanLibrariesSpec.() -> Unit) = forEach { it.libraries(configure) } override fun libraries(configure: KonanLibrariesSpec.() -> Unit) = forEach { it.libraries(configure) }
override fun noDefaultLibs(flag: Boolean) = forEach { it.noDefaultLibs(flag) } override fun noDefaultLibs(flag: Boolean) = forEach { it.noDefaultLibs(flag) }
override fun noEndorsedLibs(flag: Boolean) = forEach { it.noEndorsedLibs(flag) }
override fun dumpParameters(flag: Boolean) = forEach { it.dumpParameters(flag) } override fun dumpParameters(flag: Boolean) = forEach { it.dumpParameters(flag) }
@@ -229,6 +229,7 @@ internal fun dumpProperties(task: Task) {
println("enableOptimization : $enableOptimizations") println("enableOptimization : $enableOptimizations")
println("enableAssertions : $enableAssertions") println("enableAssertions : $enableAssertions")
println("noDefaultLibs : $noDefaultLibs") println("noDefaultLibs : $noDefaultLibs")
println("noEndorsedLibs : $noEndorsedLibs")
println("target : $target") println("target : $target")
println("languageVersion : $languageVersion") println("languageVersion : $languageVersion")
println("apiVersion : $apiVersion") println("apiVersion : $apiVersion")
@@ -30,6 +30,7 @@ interface KonanArtifactWithLibrariesSpec: KonanArtifactSpec {
fun libraries(configure: KonanLibrariesSpec.() -> Unit) fun libraries(configure: KonanLibrariesSpec.() -> Unit)
fun noDefaultLibs(flag: Boolean) fun noDefaultLibs(flag: Boolean)
fun noEndorsedLibs(flag: Boolean)
fun dependencies(closure: Closure<Unit>) fun dependencies(closure: Closure<Unit>)
} }
@@ -167,6 +167,9 @@ abstract class KonanArtifactWithLibrariesTask: KonanArtifactTask(), KonanArtifac
@Input @Input
var noDefaultLibs = false var noDefaultLibs = false
@Input
var noEndorsedLibs = false
// DSL // DSL
override fun libraries(closure: Closure<Unit>) = libraries(ConfigureUtil.configureUsing(closure)) override fun libraries(closure: Closure<Unit>) = libraries(ConfigureUtil.configureUsing(closure))
@@ -176,4 +179,8 @@ abstract class KonanArtifactWithLibrariesTask: KonanArtifactTask(), KonanArtifac
override fun noDefaultLibs(flag: Boolean) { override fun noDefaultLibs(flag: Boolean) {
noDefaultLibs = flag noDefaultLibs = flag
} }
override fun noEndorsedLibs(flag: Boolean) {
noEndorsedLibs = flag
}
} }
@@ -213,7 +213,8 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
addKey("-ea", enableAssertions) addKey("-ea", enableAssertions)
addKey("-Xtime", measureTime) addKey("-Xtime", measureTime)
addKey("-Xprofile-phases", measureTime) addKey("-Xprofile-phases", measureTime)
addKey("-nodefaultlibs", noDefaultLibs) addKey("-no-default-libs", noDefaultLibs)
addKey("-no-endorsed-libs", noEndorsedLibs)
addKey("-Xmulti-platform", enableMultiplatform) addKey("-Xmulti-platform", enableMultiplatform)
if (libraries.friends.isNotEmpty()) if (libraries.friends.isNotEmpty())
@@ -105,7 +105,8 @@ open class KonanInteropTask @Inject constructor(val workerExecutor: WorkerExecut
addArgs("-library", libraries.namedKlibs) addArgs("-library", libraries.namedKlibs)
addArgs("-library", libraries.artifacts.map { it.artifact.canonicalPath }) addArgs("-library", libraries.artifacts.map { it.artifact.canonicalPath })
addKey("-nodefaultlibs", noDefaultLibs) addKey("-no-default-libs", noDefaultLibs)
addKey("-no-endorsed-libs", noEndorsedLibs)
addAll(extraOpts) addAll(extraOpts)
} }
@@ -110,6 +110,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
"artifactName" | "'foo'" "artifactName" | "'foo'"
"extraOpts" | "'-Xtime'" "extraOpts" | "'-Xtime'"
"noDefaultLibs" | "true" "noDefaultLibs" | "true"
"noEndorsedLibs" | "true"
} }
def 'Plugin should support a custom entry point and recompile an artifact if it changes'() { def 'Plugin should support a custom entry point and recompile an artifact if it changes'() {
@@ -210,6 +211,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
"includeDirs.allHeaders" | "'src'" "includeDirs.allHeaders" | "'src'"
"extraOpts" | "'-verbose'" "extraOpts" | "'-verbose'"
"noDefaultLibs" | "true" "noDefaultLibs" | "true"
"noEndorsedLibs" | "true"
} }
def 'includeDirs.headerFilterOnly change should cause recompilation and interop reprocessing'() { def 'includeDirs.headerFilterOnly change should cause recompilation and interop reprocessing'() {
@@ -57,11 +57,13 @@ class LibrarySpecification extends BaseKonanSpecification {
|} |}
""".stripMargin(), ArtifactType.LIBRARY) """.stripMargin(), ArtifactType.LIBRARY)
project.addSetting(name, "noDefaultLibs", "true") project.addSetting(name, "noDefaultLibs", "true")
project.addSetting(name, "noEndorsedLibs", "true")
} }
void createInteropLibrary(KonanProject project, String name) { void createInteropLibrary(KonanProject project, String name) {
project.addCompilerArtifact(name, "headers = math.h", ArtifactType.INTEROP) project.addCompilerArtifact(name, "headers = math.h", ArtifactType.INTEROP)
project.addSetting(name, "noDefaultLibs", "true") project.addSetting(name, "noDefaultLibs", "true")
project.addSetting(name, "noEndorsedLibs", "true")
} }
KonanProject createProjectWithLibraries(Closure createLibraryFunction, Closure callBuilder) { KonanProject createProjectWithLibraries(Closure createLibraryFunction, Closure callBuilder) {
@@ -79,6 +81,7 @@ class LibrarySpecification extends BaseKonanSpecification {
result.addCompilerArtifact("main", createMainWithCalls(libraryNames, callBuilder)) result.addCompilerArtifact("main", createMainWithCalls(libraryNames, callBuilder))
result.addSetting("main", "noDefaultLibs", "true") result.addSetting("main", "noDefaultLibs", "true")
result.addSetting("main", "noEndorsedLibs", "true")
for (int i = 0; i < libraries.size(); i++) { for (int i = 0; i < libraries.size(); i++) {
def foo = libraryNames[i].first def foo = libraryNames[i].first
@@ -130,12 +133,15 @@ class LibrarySpecification extends BaseKonanSpecification {
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it -> def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY) it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
it.addSetting("foo", "noDefaultLibs", "true") it.addSetting("foo", "noDefaultLibs", "true")
it.addSetting("foo", "noEndorsedLibs", "true")
it.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY) it.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
it.addSetting("bar", "noDefaultLibs", "true") it.addSetting("bar", "noDefaultLibs", "true")
it.addSetting("bar", "noEndorsedLibs", "true")
it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }") it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
it.addSetting("main", "noDefaultLibs", "true" ) it.addSetting("main", "noDefaultLibs", "true" )
it.addSetting("main", "noEndorsedLibs", "true" )
it.addLibraryToArtifactCustom("main", "allLibrariesFrom project") it.addLibraryToArtifactCustom("main", "allLibrariesFrom project")
} }
project.createRunner() project.createRunner()
@@ -153,15 +159,19 @@ class LibrarySpecification extends BaseKonanSpecification {
project.addCompilerArtifact("wrongFoo","fun foo() { println(24) }", ArtifactType.LIBRARY) project.addCompilerArtifact("wrongFoo","fun foo() { println(24) }", ArtifactType.LIBRARY)
project.addSetting("wrongFoo", "noDefaultLibs", "true") project.addSetting("wrongFoo", "noDefaultLibs", "true")
project.addSetting("wrongFoo", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY) subproject.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
subproject.addSetting("foo", "noDefaultLibs", "true") subproject.addSetting("foo", "noDefaultLibs", "true")
subproject.addSetting("foo", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY) subproject.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
subproject.addSetting("bar", "noDefaultLibs", "true") subproject.addSetting("bar", "noDefaultLibs", "true")
subproject.addSetting("bar", "noEndorsedLibs", "true")
project.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }") project.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
project.addSetting("main", "noDefaultLibs", "true" ) project.addSetting("main", "noDefaultLibs", "true" )
project.addSetting("main", "noEndorsedLibs", "true" )
project.addLibraryToArtifactCustom("main", "allLibrariesFrom project('subproject')") project.addLibraryToArtifactCustom("main", "allLibrariesFrom project('subproject')")
project.createRunner() project.createRunner()
@@ -180,15 +190,19 @@ class LibrarySpecification extends BaseKonanSpecification {
project.addCompilerArtifact("wrongFoo1", "fun foo() { println(42) }", ArtifactType.LIBRARY) project.addCompilerArtifact("wrongFoo1", "fun foo() { println(42) }", ArtifactType.LIBRARY)
project.addSetting("wrongFoo1", "noDefaultLibs", "true") project.addSetting("wrongFoo1", "noDefaultLibs", "true")
project.addSetting("wrongFoo1", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("wrongFoo2", "fun foo() { println(42) }", ArtifactType.LIBRARY) subproject.addCompilerArtifact("wrongFoo2", "fun foo() { println(42) }", ArtifactType.LIBRARY)
subproject.addSetting("wrongFoo2", "noDefaultLibs", "true") subproject.addSetting("wrongFoo2", "noDefaultLibs", "true")
subproject.addSetting("wrongFoo2", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("math1", "headers = math.h", ArtifactType.INTEROP) subproject.addCompilerArtifact("math1", "headers = math.h", ArtifactType.INTEROP)
subproject.addSetting("math1", "noDefaultLibs", "true") subproject.addSetting("math1", "noDefaultLibs", "true")
subproject.addSetting("math1", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("math2", "headers = math.h", ArtifactType.INTEROP) subproject.addCompilerArtifact("math2", "headers = math.h", ArtifactType.INTEROP)
subproject.addSetting("math2", "noDefaultLibs", "true") subproject.addSetting("math2", "noDefaultLibs", "true")
subproject.addSetting("math2", "noEndorsedLibs", "true")
project.addCompilerArtifact("main" ,""" project.addCompilerArtifact("main" ,"""
|fun foo() {} |fun foo() {}
@@ -196,6 +210,7 @@ class LibrarySpecification extends BaseKonanSpecification {
|fun main(args: Array<String>) { foo(); math1.cos(0.0); math2.cos(0.0) } |fun main(args: Array<String>) { foo(); math1.cos(0.0); math2.cos(0.0) }
""".stripMargin()) """.stripMargin())
project.addSetting("main", "noDefaultLibs", "true" ) project.addSetting("main", "noDefaultLibs", "true" )
project.addSetting("main", "noEndorsedLibs", "true" )
project.addLibraryToArtifactCustom("main", "allInteropLibrariesFrom project('subproject')") project.addLibraryToArtifactCustom("main", "allInteropLibrariesFrom project('subproject')")
@@ -209,12 +224,15 @@ class LibrarySpecification extends BaseKonanSpecification {
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it -> def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("wrongFoo", "fun foo() { println(42) }", ArtifactType.LIBRARY) it.addCompilerArtifact("wrongFoo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
it.addSetting("wrongFoo", "noDefaultLibs", "true") it.addSetting("wrongFoo", "noDefaultLibs", "true")
it.addSetting("wrongFoo", "noEndorsedLibs", "true")
it.addCompilerArtifact("math1", "headers = math.h", ArtifactType.INTEROP) it.addCompilerArtifact("math1", "headers = math.h", ArtifactType.INTEROP)
it.addSetting("math1", "noDefaultLibs", "true") it.addSetting("math1", "noDefaultLibs", "true")
it.addSetting("math1", "noEndorsedLibs", "true")
it.addCompilerArtifact("math2", "headers = math.h", ArtifactType.INTEROP) it.addCompilerArtifact("math2", "headers = math.h", ArtifactType.INTEROP)
it.addSetting("math2", "noDefaultLibs", "true") it.addSetting("math2", "noDefaultLibs", "true")
it.addSetting("math2", "noEndorsedLibs", "true")
it.addCompilerArtifact("main" ,""" it.addCompilerArtifact("main" ,"""
|fun foo() {} |fun foo() {}
@@ -222,6 +240,7 @@ class LibrarySpecification extends BaseKonanSpecification {
|fun main(args: Array<String>) { foo(); math1.cos(0.0); math2.cos(0.0) } |fun main(args: Array<String>) { foo(); math1.cos(0.0); math2.cos(0.0) }
""".stripMargin()) """.stripMargin())
it.addSetting("main", "noDefaultLibs", "true" ) it.addSetting("main", "noDefaultLibs", "true" )
it.addSetting("main", "noEndorsedLibs", "true" )
it.addLibraryToArtifactCustom("main", "allInteropLibrariesFrom project") it.addLibraryToArtifactCustom("main", "allInteropLibrariesFrom project")
} }
project.createRunner() project.createRunner()
@@ -234,11 +253,13 @@ class LibrarySpecification extends BaseKonanSpecification {
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it -> def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY) it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
it.addSetting("foo", "noDefaultLibs", "true") it.addSetting("foo", "noDefaultLibs", "true")
it.addSetting("foo", "noEndorsedLibs", "true")
it.addSetting("foo", "baseDir", "file('out')") it.addSetting("foo", "baseDir", "file('out')")
it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo() }") it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo() }")
it.addSetting("main", "noDefaultLibs", "true" ) it.addSetting("main", "noDefaultLibs", "true" )
it.addSetting("main", "noEndorsedLibs", "true" )
it.addSetting("main", "dependsOn", "konanArtifacts.foo.$KonanProject.HOST") it.addSetting("main", "dependsOn", "konanArtifacts.foo.$KonanProject.HOST")
it.addLibraryToArtifactCustom("main", "klib 'foo'") it.addLibraryToArtifactCustom("main", "klib 'foo'")
it.addLibraryToArtifactCustom("main", "useRepo 'out/$KonanProject.HOST'") it.addLibraryToArtifactCustom("main", "useRepo 'out/$KonanProject.HOST'")
@@ -253,13 +274,16 @@ class LibrarySpecification extends BaseKonanSpecification {
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it -> def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY) it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
it.addSetting("foo", "noDefaultLibs", "true") it.addSetting("foo", "noDefaultLibs", "true")
it.addSetting("foo", "noEndorsedLibs", "true")
it.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY) it.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
it.addSetting("bar", "noDefaultLibs", "true") it.addSetting("bar", "noDefaultLibs", "true")
it.addSetting("bar", "noEndorsedLibs", "true")
it.addLibraryToArtifact("bar", "foo") it.addLibraryToArtifact("bar", "foo")
it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }") it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
it.addSetting("main", "noDefaultLibs", "true" ) it.addSetting("main", "noDefaultLibs", "true" )
it.addSetting("main", "noEndorsedLibs", "true" )
it.addLibraryToArtifact("main", "bar") it.addLibraryToArtifact("main", "bar")
} }
@@ -281,15 +305,18 @@ class LibrarySpecification extends BaseKonanSpecification {
subproject1.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY) subproject1.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
subproject1.addSetting("foo", "noDefaultLibs", "true") subproject1.addSetting("foo", "noDefaultLibs", "true")
subproject1.addSetting("foo", "noEndorsedLibs", "true")
subproject2.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY) subproject2.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
subproject2.addSetting("bar", "noDefaultLibs", "true") subproject2.addSetting("bar", "noDefaultLibs", "true")
subproject2.addSetting("bar", "noEndorsedLibs", "true")
subproject2.addLibraryToArtifactCustom( subproject2.addLibraryToArtifactCustom(
"bar", "artifact rootProject.project('subproject1'), 'foo'" "bar", "artifact rootProject.project('subproject1'), 'foo'"
) )
project.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }") project.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
project.addSetting("main", "noDefaultLibs", "true" ) project.addSetting("main", "noDefaultLibs", "true" )
project.addSetting("main", "noEndorsedLibs", "true" )
project.addLibraryToArtifactCustom( project.addLibraryToArtifactCustom(
"main", "artifact project('subproject2'), 'bar'" "main", "artifact project('subproject2'), 'bar'"
) )
@@ -629,7 +629,8 @@ class ExperimentalPluginTests {
dependencies { dependencies {
implementation project(':libBar') implementation project(':libBar')
cinterop('mystdio') { cinterop('mystdio') {
extraOpts '-nodefaultlibs' extraOpts '-no-default-libs'
extraOpts '-no-endorsed-libs'
} }
} }
} }
@@ -19,7 +19,8 @@ fun invokeInterop(flavor: String, args: Array<String>): Array<String> {
val arguments = if (flavor == "native") CInteropArguments() else JSInteropArguments() val arguments = if (flavor == "native") CInteropArguments() else JSInteropArguments()
arguments.argParser.parse(args) arguments.argParser.parse(args)
val outputFileName = arguments.output val outputFileName = arguments.output
val noDefaultLibs = arguments.nodefaultlibs val noDefaultLibs = arguments.nodefaultlibs || arguments.nodefaultlibsDeprecated
val noEndorsedLibs = arguments.noendorsedlibs
val purgeUserLibs = arguments.purgeUserLibs val purgeUserLibs = arguments.purgeUserLibs
val temporaryFilesDir = arguments.tempDir val temporaryFilesDir = arguments.tempDir
@@ -49,7 +50,8 @@ fun invokeInterop(flavor: String, args: Array<String>): Array<String> {
Distribution() Distribution()
).libraryResolver() ).libraryResolver()
val allLibraries = resolver.resolveWithDependencies( val allLibraries = resolver.resolveWithDependencies(
libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs,
noEndorsedLibs = noEndorsedLibs
).getFullList() ).getFullList()
val imports = allLibraries.map { library -> val imports = allLibraries.map { library ->
@@ -82,6 +84,7 @@ fun invokeInterop(flavor: String, args: Array<String>): Array<String> {
libraries.flatMap { listOf("-library", it) } + libraries.flatMap { listOf("-library", it) } +
repos.flatMap { listOf("-repo", it) } + repos.flatMap { listOf("-repo", it) } +
(if (noDefaultLibs) arrayOf("-$NODEFAULTLIBS") else emptyArray()) + (if (noDefaultLibs) arrayOf("-$NODEFAULTLIBS") else emptyArray()) +
(if (noEndorsedLibs) arrayOf("-$NOENDORSEDLIBS") else emptyArray()) +
(if (purgeUserLibs) arrayOf("-$PURGE_USER_LIBS") else emptyArray()) (if (purgeUserLibs) arrayOf("-$PURGE_USER_LIBS") else emptyArray())
return konancArgs return konancArgs