CLI library rework (#3215)
This commit is contained in:
@@ -34,7 +34,7 @@ dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-compiler:$kotlinVersion"
|
||||
compile project(':Interop:Indexer')
|
||||
compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion"
|
||||
compile project(path: ":endorsedLibraries:kliopt", configuration: "jvmRuntimeElements")
|
||||
compile project(path: ":endorsedLibraries:kotlinx.cli", configuration: "jvmRuntimeElements")
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
|
||||
+1
-1
@@ -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.getHeaderPaths
|
||||
import org.jetbrains.kotlin.native.interop.tool.CInteropArguments
|
||||
import org.jetbrains.kliopt.ArgParser
|
||||
import kotlinx.cli.ArgParser
|
||||
import java.io.File
|
||||
|
||||
fun defFileDependencies(args: Array<String>) {
|
||||
|
||||
+53
-55
@@ -16,85 +16,83 @@
|
||||
|
||||
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 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 TEMP_DIR = "Xtemporary-files-dir"
|
||||
|
||||
// TODO: unify camel and snake cases.
|
||||
// Possible solution is to accept both cases
|
||||
open class CommonInteropArguments(val argParser: ArgParser) {
|
||||
val verbose by argParser.option(ArgType.Boolean, description = "Enable verbose logging output", defaultValue = false)
|
||||
val flavor by argParser.option(ArgType.Choice(listOf("jvm", "native", "wasm")), description = "Interop target",
|
||||
defaultValue = "jvm")
|
||||
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")
|
||||
.default("jvm")
|
||||
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",
|
||||
defaultValue = "nativelib")
|
||||
val libraryPath by argParser.options(ArgType.String, description = "add a library search path",
|
||||
multiple = true, delimiter = ",")
|
||||
val staticLibrary by argParser.options(ArgType.String, description = "embed static library to the result",
|
||||
multiple = true, delimiter = ",")
|
||||
val generated by argParser.option(ArgType.String, description = "place generated bindings to the directory",
|
||||
defaultValue = System.getProperty("user.dir"))
|
||||
val natives by argParser.option(ArgType.String, description = "where to put the built native files",
|
||||
defaultValue = System.getProperty("user.dir"))
|
||||
val library by argParser.options(ArgType.String, shortName = "l", description = "library to use for building",
|
||||
multiple = true)
|
||||
val repo by argParser.options(ArgType.String, shortName = "r", description = "repository to resolve dependencies",
|
||||
multiple = true)
|
||||
val output by argParser.option(ArgType.String, shortName = "o", description = "specifies the resulting library file")
|
||||
.default("nativelib")
|
||||
val libraryPath by argParser.option(ArgType.String, description = "add a library search path")
|
||||
.multiple().delimiter(",")
|
||||
val staticLibrary by argParser.option(ArgType.String, description = "embed static library to the result")
|
||||
.multiple().delimiter(",")
|
||||
val generated by argParser.option(ArgType.String, description = "place generated bindings to the directory")
|
||||
.default(System.getProperty("user.dir"))
|
||||
val natives by argParser.option(ArgType.String, description = "where to put the built native files")
|
||||
.default(System.getProperty("user.dir"))
|
||||
val library by argParser.option(ArgType.String, shortName = "l", description = "library to use for building")
|
||||
.multiple()
|
||||
val repo by argParser.option(ArgType.String, shortName = "r", description = "repository to resolve dependencies")
|
||||
.multiple()
|
||||
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",
|
||||
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,
|
||||
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,
|
||||
description = "save temporary files to the given directory")
|
||||
}
|
||||
|
||||
class CInteropArguments(argParser: ArgParser =
|
||||
ArgParser("cinterop", useDefaultHelpShortName = false,
|
||||
ArgParser("cinterop",
|
||||
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 header by argParser.options(ArgType.String, description = "header file to produce kotlin bindings for",
|
||||
multiple = true, delimiter = ",")
|
||||
val shortHeaderForm by argParser.options(ArgType.String, "h", description = "header file to produce kotlin bindings for",
|
||||
multiple = true, delimiter = ",", deprecatedWarning = "Option -h is deprecated. Please use -header.")
|
||||
val headerFilterPrefix by argParser.options(ArgType.String, HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, "hfasp",
|
||||
"header file to produce kotlin bindings for", multiple = true, delimiter = ",")
|
||||
val compilerOpts by argParser.options(ArgType.String,
|
||||
description = "additional compiler options (allows to add several options separated by spaces)",
|
||||
multiple = true, delimiter = " ")
|
||||
val compilerOptions by argParser.options(ArgType.String, "compiler-options",
|
||||
description = "additional compiler options (allows to add several options separated by spaces)",
|
||||
multiple = true, delimiter = " ")
|
||||
val linkerOpts by argParser.options(ArgType.String, "linkerOpts",
|
||||
description = "additional linker options (allows to add several options separated by spaces)",
|
||||
multiple = true, delimiter = " ")
|
||||
val linkerOptions by argParser.options(ArgType.String, "linker-options",
|
||||
description = "additional linker options (allows to add several options separated by spaces)",
|
||||
multiple = true, delimiter = " ")
|
||||
val compilerOption by argParser.options(ArgType.String, "compiler-option",
|
||||
description = "additional compiler option", multiple = true)
|
||||
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 header by argParser.option(ArgType.String, description = "header file to produce kotlin bindings for")
|
||||
.multiple().delimiter(",")
|
||||
val headerFilterPrefix by argParser.option(ArgType.String, HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, "hfasp",
|
||||
"header file to produce kotlin bindings for").multiple().delimiter(",")
|
||||
val compilerOpts by argParser.option(ArgType.String,
|
||||
description = "additional compiler options (allows to add several options separated by spaces)")
|
||||
.multiple().delimiter(" ")
|
||||
val compilerOptions by argParser.option(ArgType.String, "compiler-options",
|
||||
description = "additional compiler options (allows to add several options separated by spaces)")
|
||||
.multiple().delimiter(" ")
|
||||
val linkerOpts = argParser.option(ArgType.String, "linkerOpts",
|
||||
description = "additional linker options (allows to add several options separated by spaces)")
|
||||
.multiple().delimiter(" ")
|
||||
val linkerOptions = argParser.option(ArgType.String, "linker-options",
|
||||
description = "additional linker options (allows to add several options separated by spaces)")
|
||||
.multiple().delimiter(" ")
|
||||
val compilerOption by argParser.option(ArgType.String, "compiler-option",
|
||||
description = "additional compiler option").multiple()
|
||||
val linkerOption = argParser.option(ArgType.String, "linker-option",
|
||||
description = "additional linker option").multiple()
|
||||
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) {
|
||||
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) {
|
||||
|
||||
+9
-11
@@ -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.indexer.*
|
||||
import org.jetbrains.kotlin.native.interop.tool.*
|
||||
import org.jetbrains.kliopt.ArgParser
|
||||
import kotlinx.cli.ArgParser
|
||||
import java.io.File
|
||||
import java.lang.IllegalArgumentException
|
||||
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 def = DefFile(defFile, tool.substitutions)
|
||||
val isLinkerOptsSetByUser = (cinteropArguments.argParser.getOrigin("linkerOpts") == ArgParser.ValueOrigin.SET_BY_USER) ||
|
||||
(cinteropArguments.argParser.getOrigin("linker-option") == ArgParser.ValueOrigin.SET_BY_USER) ||
|
||||
(cinteropArguments.argParser.getOrigin("linker-options") == ArgParser.ValueOrigin.SET_BY_USER) ||
|
||||
(cinteropArguments.argParser.getOrigin("lopt") == ArgParser.ValueOrigin.SET_BY_USER)
|
||||
val isLinkerOptsSetByUser = (cinteropArguments.linkerOpts.valueOrigin == ArgParser.ValueOrigin.SET_BY_USER) ||
|
||||
(cinteropArguments.linkerOptions.valueOrigin == ArgParser.ValueOrigin.SET_BY_USER) ||
|
||||
(cinteropArguments.linkerOption.valueOrigin == ArgParser.ValueOrigin.SET_BY_USER)
|
||||
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() +
|
||||
cinteropArguments.linkerOptions.toTypedArray() + cinteropArguments.lopt.toTypedArray()
|
||||
val additionalLinkerOpts = cinteropArguments.linkerOpts.value.toTypedArray() + cinteropArguments.linkerOption.value.toTypedArray() +
|
||||
cinteropArguments.linkerOptions.value.toTypedArray()
|
||||
val verbose = cinteropArguments.verbose
|
||||
|
||||
val language = selectNativeLanguage(def.config)
|
||||
@@ -305,10 +304,9 @@ internal fun buildNativeLibrary(
|
||||
arguments: CInteropArguments,
|
||||
imports: ImportsImpl
|
||||
): NativeLibrary {
|
||||
val additionalHeaders = (arguments.header + arguments.shortHeaderForm).toTypedArray()
|
||||
val additionalHeaders = (arguments.header).toTypedArray()
|
||||
val additionalCompilerOpts = (arguments.compilerOpts +
|
||||
arguments.compilerOptions + arguments.compilerOption +
|
||||
arguments.copt).toTypedArray()
|
||||
arguments.compilerOptions + arguments.compilerOption).toTypedArray()
|
||||
|
||||
val headerFiles = def.config.headers + additionalHeaders
|
||||
val language = selectNativeLanguage(def.config)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package org.jetbrains.kotlin.native.interop.gen.wasm
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.native.interop.gen.argsToCompiler
|
||||
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
|
||||
|
||||
fun kotlinHeader(packageName: String): String {
|
||||
|
||||
@@ -191,7 +191,7 @@ targetList.each { target ->
|
||||
"-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'
|
||||
def konanArgs = [*defaultArgs,
|
||||
'-target', target,
|
||||
|
||||
@@ -111,6 +111,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
|
||||
with(configuration) {
|
||||
|
||||
put(NODEFAULTLIBS, arguments.nodefaultlibs)
|
||||
put(NOENDORSEDLIBS, arguments.noendorsedlibs)
|
||||
put(NOSTDLIB, arguments.nostdlib)
|
||||
put(NOPACK, arguments.nopack)
|
||||
put(NOMAIN, arguments.nomain)
|
||||
|
||||
@@ -57,9 +57,12 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
|
||||
valueDescription = "<path>", description = "Include the native bitcode library", delimiter = "")
|
||||
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
|
||||
|
||||
@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")
|
||||
var nomain: Boolean = false
|
||||
|
||||
|
||||
+2
-1
@@ -128,7 +128,8 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
||||
resolver.resolveWithDependencies(
|
||||
unresolvedLibraries + includedLibraryFiles.map { UnresolvedLibrary(it.absolutePath, null) },
|
||||
noStdLib = configuration.getBoolean(KonanConfigKeys.NOSTDLIB),
|
||||
noDefaultLibs = configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS)
|
||||
noDefaultLibs = configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS),
|
||||
noEndorsedLibs = configuration.getBoolean(KonanConfigKeys.NOENDORSEDLIBS)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -66,6 +66,8 @@ class KonanConfigKeys {
|
||||
= CompilerConfigurationKey.create("native library file paths")
|
||||
val NODEFAULTLIBS: CompilerConfigurationKey<Boolean>
|
||||
= 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>
|
||||
= CompilerConfigurationKey.create("assume 'main' entry point to be provided by external libraries")
|
||||
val NOSTDLIB: CompilerConfigurationKey<Boolean>
|
||||
|
||||
+1
-1
@@ -826,7 +826,7 @@ internal val ModuleDescriptor.namePrefix: String get() {
|
||||
internal fun abbreviate(name: String): String {
|
||||
val normalizedName = name
|
||||
.capitalize()
|
||||
.replace('-', '_')
|
||||
.replace("-|\\.".toRegex(), "_")
|
||||
|
||||
val uppers = normalizedName.filterIndexed { index, character -> index == 0 || character.isUpperCase() }
|
||||
if (uppers.length >= 3) return uppers
|
||||
|
||||
@@ -3225,6 +3225,7 @@ void createInterop(String name, Closure conf) {
|
||||
interop(name, targets: [target.name]) {
|
||||
conf(it)
|
||||
noDefaultLibs(true)
|
||||
noEndorsedLibs(true)
|
||||
baseDir "$testOutputLocal/$name"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ class NativeInteropPlugin implements Plugin<Project> {
|
||||
|
||||
prj.dependencies {
|
||||
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
|
||||
|
||||
@@ -137,7 +137,7 @@ open class BenchmarkingPlugin: Plugin<Project> {
|
||||
|
||||
project.configurations.getByName(nativeMain.implementationConfigurationName).apply {
|
||||
// Exclude dependencies already included into K/N distribution (aka endorsed libraries).
|
||||
exclude(mapOf("module" to "kliopt"))
|
||||
exclude(mapOf("module" to "kotlinx.cli"))
|
||||
}
|
||||
|
||||
repositories.maven {
|
||||
|
||||
+1
-1
@@ -286,7 +286,7 @@ task distCompiler(type: Copy) {
|
||||
into('konan/lib')
|
||||
}
|
||||
|
||||
from(project(':endorsedLibraries:kliopt').file('build/libs')) {
|
||||
from(project(':endorsedLibraries:kotlinx.cli').file('build/libs')) {
|
||||
into('konan/lib')
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -89,13 +89,13 @@ SHARED_JAR="${KONAN_HOME}/konan/lib/shared.jar"
|
||||
EXTRACTED_METADATA_JAR="${KONAN_HOME}/konan/lib/konan.metadata.jar"
|
||||
EXTRACTED_SERIALIZER_JAR="${KONAN_HOME}/konan/lib/konan.serializer.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"
|
||||
NATIVE_UTILS_JAR="${KONAN_HOME}/konan/lib/kotlin-native-utils.jar"
|
||||
UTIL_IO_JAR="${KONAN_HOME}/konan/lib/kotlin-util-io.jar"
|
||||
UTIL_KLIB_JAR="${KONAN_HOME}/konan/lib/kotlin-util-klib.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
|
||||
|
||||
LIBCLANG_DISABLE_CRASH_RECOVERY=1 \
|
||||
|
||||
+2
-2
@@ -55,7 +55,7 @@ set "NATIVE_LIB=%_KONAN_HOME%\konan\nativelib"
|
||||
set "KONAN_LIB=%_KONAN_HOME%\konan\lib"
|
||||
|
||||
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_SERIALIZER_JAR=%KONAN_LIB%\konan.serializer.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 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 ^
|
||||
-Xmx3G ^
|
||||
|
||||
@@ -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 {
|
||||
doLast {
|
||||
@@ -16,11 +22,11 @@ task jvmJar {
|
||||
targetList.each { target ->
|
||||
task("${target}EndorsedLibraries", type: Copy) {
|
||||
endorsedLibrariesList.each { library ->
|
||||
dependsOn "$library:${target}${library.capitalize()}"
|
||||
dependsOn "$library:${target}${ toTaskName(library) }"
|
||||
}
|
||||
destinationDir project.buildDir
|
||||
endorsedLibrariesList.each { library ->
|
||||
from(project("$library").file("build/${target}${library.capitalize()}")) {
|
||||
from(project("$library").file("build/${target}${ toTaskName(library) }")) {
|
||||
include('**')
|
||||
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 {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = ["-Xuse-experimental=org.jetbrains.kliopt.ExperimentalCli"]
|
||||
freeCompilerArgs = ["-Xuse-experimental=kotlinx.cli.ExperimentalCli"]
|
||||
suppressWarnings = true
|
||||
}
|
||||
}
|
||||
@@ -76,23 +76,23 @@ targetList.each { target ->
|
||||
"-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'
|
||||
def konanArgs = [*defaultArgs,
|
||||
'-target', target,
|
||||
"-Xruntime=${project(':runtime').file('build/' + target + '/runtime.bc')}",
|
||||
*project.globalBuildArgs]
|
||||
|
||||
task("${target}Kliopt", type: JavaExec) {
|
||||
task("${target}KotlinxCli", type: JavaExec) {
|
||||
dependsOn ":${target}CrossDistRuntime"
|
||||
|
||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
||||
classpath = project(":backend.native").configurations.cli_bc
|
||||
jvmArgs = konanJvmArgs
|
||||
args = [*konanArgs,
|
||||
'-output', project.file("build/${target}Kliopt"),
|
||||
'-produce', 'library', '-module-name', 'kliopt', '-XXLanguage:+AllowContractsForCustomFunctions',
|
||||
'-Xmulti-platform', '-Xuse-experimental=org.jetbrains.kliopt.ExperimentalCli',
|
||||
'-output', project.file("build/${target}KotlinxCli"),
|
||||
'-produce', 'library', '-module-name', 'kotlinx-cli', '-XXLanguage:+AllowContractsForCustomFunctions',
|
||||
'-Xmulti-platform', '-Xuse-experimental=kotlinx.cli.ExperimentalCli',
|
||||
'-Xuse-experimental=kotlin.ExperimentalMultiplatform',
|
||||
'-Xallow-result-return-type',
|
||||
commonSrc.absolutePath,
|
||||
@@ -100,6 +100,6 @@ targetList.each { target ->
|
||||
nativeSrc]
|
||||
inputs.dir(nativeSrc)
|
||||
inputs.dir(commonSrc)
|
||||
outputs.dir(project.file("build/${target}Kliopt"))
|
||||
outputs.dir(project.file("build/${target}KotlinxCli"))
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kliopt
|
||||
package kotlinx.cli
|
||||
|
||||
internal actual fun exitProcess(status: Int): Nothing {
|
||||
error("Not implemented for JS!")
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kliopt
|
||||
package kotlinx.cli
|
||||
|
||||
internal actual fun exitProcess(status: Int): Nothing {
|
||||
kotlin.system.exitProcess(0)
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kliopt
|
||||
package kotlinx.cli
|
||||
|
||||
internal actual fun exitProcess(status: Int): Nothing {
|
||||
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")
|
||||
}
|
||||
+18
-18
@@ -3,7 +3,7 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kliopt
|
||||
package kotlinx.cli
|
||||
|
||||
/**
|
||||
* Possible types of arguments.
|
||||
@@ -23,7 +23,7 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
|
||||
*
|
||||
* @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.
|
||||
@@ -32,8 +32,8 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
|
||||
override val description: kotlin.String
|
||||
get() = ""
|
||||
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String, _: kotlin.String) -> kotlin.Boolean
|
||||
get() = { value, _ , _ -> if (value == "false") false else true }
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.Boolean
|
||||
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
|
||||
get() = "{ String }"
|
||||
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String, _: kotlin.String) -> kotlin.String
|
||||
get() = { value, _, _ -> value }
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.String
|
||||
get() = { value, _ -> value }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,9 +54,9 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
|
||||
override val description: kotlin.String
|
||||
get() = "{ Int }"
|
||||
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String, helpMessage: kotlin.String) -> kotlin.Int
|
||||
get() = { value, name, helpMessage -> value.toIntOrNull()
|
||||
?: error("Option $name is expected to be integer number. $value is provided.\n$helpMessage") }
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.Int
|
||||
get() = { value, name -> value.toIntOrNull()
|
||||
?: 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
|
||||
get() = "{ Double }"
|
||||
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String,
|
||||
helpMessage: kotlin.String) -> kotlin.Double
|
||||
get() = { value, name, helpMessage -> value.toDoubleOrNull()
|
||||
?: error("Option $name is expected to be double number. $value is provided.\n$helpMessage") }
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.Double
|
||||
get() = { value, name -> value.toDoubleOrNull()
|
||||
?: throw ParsingException("Option $name is expected to be double number. $value is provided.") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,9 +78,10 @@ abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
|
||||
override val description: kotlin.String
|
||||
get() = "{ Value should be one of $values }"
|
||||
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String,
|
||||
helpMessage: kotlin.String) -> kotlin.String
|
||||
get() = { value, name, helpMessage -> if (value in values) value
|
||||
else error("Option $name is expected to be one of $values. $value is provided.\n$helpMessage") }
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String) -> kotlin.String
|
||||
get() = { value, name -> if (value in values) value
|
||||
else throw ParsingException("Option $name is expected to be one of $values. $value is provided.") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kliopt
|
||||
package kotlinx.cli
|
||||
|
||||
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
|
||||
* 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)
|
||||
@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
|
||||
}
|
||||
+6
-4
@@ -3,8 +3,10 @@
|
||||
* 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.*
|
||||
|
||||
class ArgumentsTests {
|
||||
@@ -24,7 +26,7 @@ class ArgumentsTests {
|
||||
fun testArgumetsWithAnyNumberOfValues() {
|
||||
val argParser = ArgParser("testParser")
|
||||
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",
|
||||
"input4.txt"))
|
||||
assertEquals("out.txt", output)
|
||||
@@ -34,7 +36,7 @@ class ArgumentsTests {
|
||||
@Test
|
||||
fun testArgumetsWithSeveralValues() {
|
||||
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 debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
|
||||
argParser.parse(arrayOf("2", "-d", "3", "out.txt"))
|
||||
@@ -48,7 +50,7 @@ class ArgumentsTests {
|
||||
@Test
|
||||
fun testSkippingExtraArguments() {
|
||||
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 debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
|
||||
argParser.parse(arrayOf("2", "-d", "3", "out.txt", "something", "else", "in", "string"))
|
||||
+8
-7
@@ -3,20 +3,22 @@
|
||||
* 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.*
|
||||
|
||||
class ErrorTests {
|
||||
@Test
|
||||
fun testExtraArguments() {
|
||||
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 debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
|
||||
val exception = assertFailsWith<IllegalStateException> { argParser.parse(
|
||||
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
|
||||
@@ -43,10 +45,9 @@ class ErrorTests {
|
||||
@Test
|
||||
fun testWrongChoice() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report",
|
||||
defaultValue = false)
|
||||
val renders by argParser.options(ArgType.Choice(listOf("text", "html")),
|
||||
"renders", "r", "Renders for showing information", listOf("text"), multiple = true)
|
||||
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report").default(false)
|
||||
val renders by argParser.option(ArgType.Choice(listOf("text", "html")),
|
||||
"renders", "r", "Renders for showing information").multiple().default(listOf("text"))
|
||||
val exception = assertFailsWith<IllegalStateException> {
|
||||
argParser.parse(arrayOf("-r", "xml"))
|
||||
}
|
||||
+36
-31
@@ -3,8 +3,12 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
@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.test.*
|
||||
|
||||
@@ -13,19 +17,19 @@ class HelpTests {
|
||||
fun testHelpMessage() {
|
||||
val argParser = ArgParser("test")
|
||||
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 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",
|
||||
"Show short version of report", defaultValue = false)
|
||||
val renders by argParser.options(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")),
|
||||
shortName = "r", description = "Renders for showing information", defaultValue = listOf("text"), multiple = true)
|
||||
"Show short version of report").default(false)
|
||||
val renders by argParser.option(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")),
|
||||
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")
|
||||
argParser.parse(arrayOf("-h"))
|
||||
argParser.parse(arrayOf("main.txt"))
|
||||
val helpOutput = argParser.makeUsage().trimIndent()
|
||||
val expectedOutput = """
|
||||
Usage: test options_list
|
||||
Usage: test options_list
|
||||
Arguments:
|
||||
mainReport -> Main report for analysis { String }
|
||||
compareToReport -> Report to compare to (optional) { String }
|
||||
@@ -37,31 +41,28 @@ Options:
|
||||
--user, -u -> User access information for authorization { String }
|
||||
--help, -h -> Usage info
|
||||
""".trimIndent()
|
||||
assertEquals(helpOutput, expectedOutput)
|
||||
assertEquals(expectedOutput, helpOutput)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHelpForSubcommands() {
|
||||
class Summary: Subcommand("summary") {
|
||||
val exec by option(ArgType.Choice(listOf("samples", "geomean")),
|
||||
description = "Execution time way of calculation", defaultValue = "geomean")
|
||||
val execSamples by options(ArgType.String, "exec-samples",
|
||||
description = "Samples used for execution time metric (value 'all' allows use all samples)",
|
||||
delimiter = ",")
|
||||
description = "Execution time way of calculation").default("geomean")
|
||||
val execSamples by option(ArgType.String, "exec-samples",
|
||||
description = "Samples used for execution time metric (value 'all' allows use all samples)").delimiter(",")
|
||||
val execNormalize by option(ArgType.String, "exec-normalize",
|
||||
description = "File with golden results which should be used for normalization")
|
||||
val compile by option(ArgType.Choice(listOf("samples", "geomean")),
|
||||
description = "Compile time way of calculation", defaultValue = "geomean")
|
||||
val compileSamples by options(ArgType.String, "compile-samples",
|
||||
description = "Samples used for compile time metric (value 'all' allows use all samples)",
|
||||
delimiter = ",")
|
||||
description = "Compile time way of calculation").default("geomean")
|
||||
val compileSamples by option(ArgType.String, "compile-samples",
|
||||
description = "Samples used for compile time metric (value 'all' allows use all samples)").delimiter(",")
|
||||
val compileNormalize by option(ArgType.String, "compile-normalize",
|
||||
description = "File with golden results which should be used for normalization")
|
||||
val codesize by option(ArgType.Choice(listOf("samples", "geomean")),
|
||||
description = "Code size way of calculation", defaultValue = "geomean")
|
||||
val codesizeSamples by options(ArgType.String, "codesize-samples",
|
||||
description = "Samples used for code size metric (value 'all' allows use all samples)",
|
||||
delimiter = ",")
|
||||
description = "Code size way of calculation").default("geomean")
|
||||
val codesizeSamples by option(ArgType.String, "codesize-samples",
|
||||
description = "Samples used for code size metric (value 'all' allows use all samples)").delimiter(",")
|
||||
val codesizeNormalize by option(ArgType.String, "codesize-normalize",
|
||||
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")
|
||||
@@ -75,21 +76,25 @@ Options:
|
||||
// Parse args.
|
||||
val argParser = ArgParser("test")
|
||||
argParser.subcommands(action)
|
||||
argParser.parse(arrayOf("summary", "-h"))
|
||||
val helpOutput = argParser.makeUsage().trimIndent()
|
||||
argParser.parse(arrayOf("summary", "out.txt"))
|
||||
val helpOutput = action.makeUsage().trimIndent()
|
||||
val expectedOutput = """
|
||||
Usage: test summary options_list
|
||||
Usage: test summary options_list
|
||||
Arguments:
|
||||
mainReport -> Main report for analysis { String }
|
||||
compareToReport -> Report to compare to (optional) { String }
|
||||
Options:
|
||||
--output, -o -> Output file { String }
|
||||
--eps, -e [1.0] -> Meaningful performance changes { Double }
|
||||
--short, -s [false] -> Show short version of report
|
||||
--renders, -r [text] -> Renders for showing information { Value should be one of [text, html, teamcity, statistics, metrics] }
|
||||
--exec [geomean] -> Execution time way of calculation { Value should be one of [samples, geomean] }
|
||||
--exec-samples -> Samples used for execution time metric (value 'all' allows use all samples) { String }
|
||||
--exec-normalize -> File with golden results which should be used for normalization { String }
|
||||
--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 }
|
||||
--help, -h -> Usage info
|
||||
""".trimIndent()
|
||||
assertEquals(helpOutput, expectedOutput)
|
||||
""".trimIndent()
|
||||
assertEquals(expectedOutput, helpOutput)
|
||||
}
|
||||
}
|
||||
+31
-7
@@ -3,8 +3,10 @@
|
||||
* 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.*
|
||||
|
||||
class OptionsTests {
|
||||
@@ -41,9 +43,9 @@ class OptionsTests {
|
||||
@Test
|
||||
fun testMultipleOptions() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report", defaultValue = false)
|
||||
val renders by argParser.options(ArgType.Choice(listOf("text", "html", "xml", "json")),
|
||||
"renders", "r", "Renders for showing information", listOf("text"), multiple = true)
|
||||
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report").default(false)
|
||||
val renders by argParser.option(ArgType.Choice(listOf("text", "html", "xml", "json")),
|
||||
"renders", "r", "Renders for showing information").multiple().default(listOf("text"))
|
||||
argParser.parse(arrayOf("-s", "-r", "text", "-r", "json"))
|
||||
assertEquals(true, useShortForm)
|
||||
assertEquals(2, renders.size)
|
||||
@@ -55,12 +57,34 @@ class OptionsTests {
|
||||
@Test
|
||||
fun testDefaultOptions() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report", defaultValue = false)
|
||||
val renders by argParser.options(ArgType.Choice(listOf("text", "html", "xml", "json")),
|
||||
"renders", "r", "Renders for showing information", listOf("text"), multiple = true)
|
||||
val useShortForm by argParser.option(ArgType.Boolean, "short", "s", "Show short version of report").default(false)
|
||||
val renders by argParser.option(ArgType.Choice(listOf("text", "html", "xml", "json")),
|
||||
"renders", "r", "Renders for showing information").multiple().default(listOf("text"))
|
||||
val output by argParser.option(ArgType.String, "output", "o", "Output file")
|
||||
argParser.parse(arrayOf("-o", "out.txt"))
|
||||
assertEquals(false, useShortForm)
|
||||
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)
|
||||
}
|
||||
}
|
||||
+8
-4
@@ -3,8 +3,12 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
@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.*
|
||||
|
||||
class SubcommandsTests {
|
||||
@@ -14,7 +18,7 @@ class SubcommandsTests {
|
||||
val output by argParser.option(ArgType.String, "output", "o", "Output file")
|
||||
class Summary: Subcommand("summary") {
|
||||
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
|
||||
|
||||
override fun execute() {
|
||||
@@ -32,7 +36,7 @@ class SubcommandsTests {
|
||||
@Test
|
||||
fun testCommonOptions() {
|
||||
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") {
|
||||
val invert by option(ArgType.Boolean, "invert", "i", "Invert results")
|
||||
@@ -70,7 +74,7 @@ class SubcommandsTests {
|
||||
val argParser = ArgParser("testParser")
|
||||
|
||||
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
|
||||
|
||||
override fun execute() {
|
||||
+2
-1
@@ -15,7 +15,8 @@ interface KonanLibraryResolver {
|
||||
fun resolveWithDependencies(
|
||||
unresolvedLibraries: List<UnresolvedLibrary>,
|
||||
noStdLib: Boolean = false,
|
||||
noDefaultLibs: Boolean = false
|
||||
noDefaultLibs: Boolean = false,
|
||||
noEndorsedLibs: Boolean = false
|
||||
): KonanLibraryResolveResult
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -30,13 +30,14 @@ internal class KonanLibraryResolverImpl(
|
||||
override fun resolveWithDependencies(
|
||||
unresolvedLibraries: List<UnresolvedLibrary>,
|
||||
noStdLib: Boolean,
|
||||
noDefaultLibs: Boolean
|
||||
) = findLibraries(unresolvedLibraries, noStdLib, noDefaultLibs)
|
||||
noDefaultLibs: Boolean,
|
||||
noEndorsedLibs: Boolean
|
||||
) = findLibraries(unresolvedLibraries, noStdLib, noDefaultLibs, noEndorsedLibs)
|
||||
.leaveDistinct()
|
||||
.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
|
||||
* from the original library set (root set).
|
||||
@@ -44,14 +45,15 @@ internal class KonanLibraryResolverImpl(
|
||||
private fun findLibraries(
|
||||
unresolvedLibraries: List<UnresolvedLibrary>,
|
||||
noStdLib: Boolean,
|
||||
noDefaultLibs: Boolean
|
||||
noDefaultLibs: Boolean,
|
||||
noEndorsedLibs: Boolean
|
||||
): List<KonanLibrary> {
|
||||
|
||||
val userProvidedLibraries = unresolvedLibraries.asSequence()
|
||||
.map { searchPathResolver.resolve(it) }
|
||||
.toList()
|
||||
|
||||
val defaultLibraries = searchPathResolver.defaultLinks(noStdLib, noDefaultLibs)
|
||||
val defaultLibraries = searchPathResolver.defaultLinks(noStdLib, noDefaultLibs, noEndorsedLibs)
|
||||
|
||||
// Make sure the user provided ones appear first, so that
|
||||
// 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,
|
||||
skipCurrentDir = true,
|
||||
logger = KlibToolLogger)
|
||||
resolver.defaultLinks(false, true)
|
||||
resolver.defaultLinks(false, true, true)
|
||||
.mapTo(defaultModules) {
|
||||
DefaultDeserializedDescriptorFactory.createDescriptor(
|
||||
it, versionSpec, storageManager, module.builtIns)
|
||||
|
||||
@@ -17,7 +17,7 @@ benchmark {
|
||||
mingwSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/mingw")
|
||||
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
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.jetbrains.structsProducedByMacrosBenchmarks.*
|
||||
import org.jetbrains.benchmarksLauncher.*
|
||||
import org.jetbrains.structsBenchmarks.*
|
||||
import org.jetbrains.typesBenchmarks.*
|
||||
import org.jetbrains.kliopt.*
|
||||
import kotlinx.cli.*
|
||||
|
||||
class CinteropLauncher : Launcher() {
|
||||
override val benchmarks = BenchmarksCollection(
|
||||
|
||||
@@ -37,6 +37,8 @@ kotlin {
|
||||
}
|
||||
kotlin.srcDir "$toolsPath/benchmarks/shared/src"
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ benchmark {
|
||||
posixSrcDirs = listOf("../shared/src/main/kotlin-native/posix")
|
||||
linkerOpts = listOf("$buildDir/pi.o")
|
||||
|
||||
dependencies.common(project(":endorsedLibraries:kliopt"))
|
||||
dependencies.common(project(":endorsedLibraries:kotlinx.cli"))
|
||||
}
|
||||
|
||||
val compileLibary by tasks.creating {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import org.jetbrains.benchmarksLauncher.*
|
||||
import org.jetbrains.kliopt.*
|
||||
import kotlinx.cli.*
|
||||
|
||||
expect class NumericalLauncher() : Launcher {
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ benchmark {
|
||||
posixSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/posix")
|
||||
linkerOpts = listOf("-L$buildDir", "-lcomplexnumbers")
|
||||
|
||||
dependencies.common(project(":endorsedLibraries:kliopt"))
|
||||
dependencies.common(project(":endorsedLibraries:kotlinx.cli"))
|
||||
}
|
||||
|
||||
val compileLibary by tasks.creating {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import org.jetbrains.benchmarksLauncher.*
|
||||
import org.jetbrains.complexNumbers.*
|
||||
import org.jetbrains.kliopt.*
|
||||
import kotlinx.cli.*
|
||||
|
||||
class ObjCInteropLauncher: Launcher() {
|
||||
override val benchmarks = BenchmarksCollection(
|
||||
|
||||
@@ -15,5 +15,5 @@ benchmark {
|
||||
mingwSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/mingw")
|
||||
posixSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native/posix")
|
||||
|
||||
dependencies.common(project(":endorsedLibraries:kliopt"))
|
||||
dependencies.common(project(":endorsedLibraries:kotlinx.cli"))
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import org.jetbrains.ring.*
|
||||
import octoTest
|
||||
import org.jetbrains.benchmarksLauncher.*
|
||||
import org.jetbrains.kliopt.*
|
||||
import kotlinx.cli.*
|
||||
|
||||
class RingLauncher : Launcher() {
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.jetbrains.benchmarksLauncher
|
||||
|
||||
import org.jetbrains.report.BenchmarkResult
|
||||
import org.jetbrains.kliopt.*
|
||||
import kotlinx.cli.*
|
||||
|
||||
|
||||
abstract class Launcher {
|
||||
@@ -118,17 +118,18 @@ abstract class Launcher {
|
||||
abstract class BenchmarkArguments(argParser: ArgParser)
|
||||
|
||||
class BaseBenchmarkArguments(argParser: ArgParser): BenchmarkArguments(argParser) {
|
||||
val warmup by argParser.option(ArgType.Int, shortName = "w", description = "Number of warm up iterations",
|
||||
defaultValue = 20)
|
||||
val repeat by argParser.option(ArgType.Int, shortName = "r", description = "Number of each benchmark run",
|
||||
defaultValue = 60)
|
||||
val prefix by argParser.option(ArgType.String, shortName = "p", description = "Prefix added to benchmark name",
|
||||
defaultValue = "")
|
||||
val warmup by argParser.option(ArgType.Int, shortName = "w", description = "Number of warm up iterations")
|
||||
.default(20)
|
||||
val repeat by argParser.option(ArgType.Int, shortName = "r", description = "Number of each benchmark run").
|
||||
default(60)
|
||||
val prefix by argParser.option(ArgType.String, shortName = "p", description = "Prefix added to benchmark name")
|
||||
.default("")
|
||||
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 filterRegex by argParser.options(ArgType.String, shortName = "fr",
|
||||
description = "Benchmark to run, described by a regular expression", multiple = true)
|
||||
val verbose by argParser.option(ArgType.Boolean, shortName = "v", description = "Verbose mode of running", defaultValue = false)
|
||||
val filter by argParser.option(ArgType.String, shortName = "f", description = "Benchmark to run").multiple()
|
||||
val filterRegex by argParser.option(ArgType.String, shortName = "fr",
|
||||
description = "Benchmark to run, described by a regular expression").multiple()
|
||||
val verbose by argParser.option(ArgType.Boolean, shortName = "v", description = "Verbose mode of running")
|
||||
.default(false)
|
||||
}
|
||||
|
||||
object BenchmarksRunner {
|
||||
|
||||
@@ -36,7 +36,7 @@ kotlin {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion"
|
||||
implementation project(":endorsedLibraries:kliopt")
|
||||
implementation project(":endorsedLibraries:kotlinx.cli")
|
||||
}
|
||||
kotlin.srcDir "src"
|
||||
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/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) {
|
||||
exclude module: "kliopt"
|
||||
exclude module: "kotlinx.cli"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import org.jetbrains.benchmarksLauncher.*
|
||||
import org.jetbrains.kliopt.*
|
||||
import kotlinx.cli.*
|
||||
|
||||
class SwiftLauncher: Launcher() {
|
||||
override val benchmarks = BenchmarksCollection(
|
||||
|
||||
@@ -67,6 +67,7 @@ project.rootProject.ext.platformManager.enabled.each { target ->
|
||||
defFile df.file
|
||||
artifactName df.name
|
||||
noDefaultLibs true
|
||||
noEndorsedLibs true
|
||||
libraries {
|
||||
klibs df.config.depends
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ if (System.getProperty("os.name") == "Mac OS X") {
|
||||
|
||||
include ':platformLibs'
|
||||
include ':endorsedLibraries'
|
||||
include ':endorsedLibraries:kliopt'
|
||||
include ':endorsedLibraries:kotlinx.cli'
|
||||
|
||||
if (hasProperty("kotlinProjectPath")) {
|
||||
include ':runtime:generator'
|
||||
|
||||
@@ -22,7 +22,7 @@ interface SearchPathResolver : WithLogger {
|
||||
fun resolutionSequence(givenPath: String): Sequence<File>
|
||||
fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean = false): 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!
|
||||
@@ -157,21 +157,34 @@ internal open class KonanLibrarySearchPathResolver(
|
||||
val defaultRoots: List<File>
|
||||
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>()
|
||||
|
||||
if (!noStdLib) {
|
||||
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) {
|
||||
val defaultLibs = defaultRoots.flatMap { it.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) }
|
||||
result.addAll(defaultLibs)
|
||||
distPlatformHead?.let {
|
||||
result.addAll(getDefaultLibrariesFromDir(it))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -48,7 +48,7 @@ kotlin {
|
||||
}
|
||||
kotlin.srcDir '../benchmarks/shared/src'
|
||||
kotlin.srcDir 'src/main/kotlin'
|
||||
kotlin.srcDir '../../endorsedLibraries/kliopt/src/main/kotlin'
|
||||
kotlin.srcDir '../../endorsedLibraries/kotlinx.cli/src/main/kotlin'
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
@@ -71,21 +71,21 @@ kotlin {
|
||||
nativeMain {
|
||||
dependsOn commonMain
|
||||
kotlin.srcDir 'src/main/kotlin-native'
|
||||
kotlin.srcDir '../../endorsedLibraries/kliopt/src/main/kotlin-native'
|
||||
kotlin.srcDir '../../endorsedLibraries/kotlinx.cli/src/main/kotlin-native'
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
||||
}
|
||||
kotlin.srcDir 'src/main/kotlin-jvm'
|
||||
kotlin.srcDir '../../endorsedLibraries/kliopt/src/main/kotlin-jvm'
|
||||
kotlin.srcDir '../../endorsedLibraries/kotlinx.cli/src/main/kotlin-jvm'
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlinVersion"
|
||||
}
|
||||
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 }
|
||||
windowsMain { dependsOn nativeMain }
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import org.jetbrains.analyzer.sendGetRequest
|
||||
import org.jetbrains.analyzer.readFile
|
||||
import org.jetbrains.analyzer.SummaryBenchmarksReport
|
||||
import org.jetbrains.kliopt.*
|
||||
import kotlinx.cli.*
|
||||
import org.jetbrains.renders.*
|
||||
import org.jetbrains.report.BenchmarksReport
|
||||
import org.jetbrains.report.BenchmarkResult
|
||||
@@ -106,31 +106,30 @@ fun parseNormalizeResults(results: String): Map<String, Map<String, Double>> {
|
||||
fun main(args: Array<String>) {
|
||||
class Summary: Subcommand("summary") {
|
||||
val exec by option(ArgType.Choice(listOf("samples", "geomean")),
|
||||
description = "Execution time way of calculation", defaultValue = "geomean")
|
||||
val execSamples by options(ArgType.String, "exec-samples",
|
||||
description = "Samples used for execution time metric (value 'all' allows use all samples)",
|
||||
delimiter = ",")
|
||||
description = "Execution time way of calculation").default("geomean")
|
||||
val execSamples by option(ArgType.String, "exec-samples",
|
||||
description = "Samples used for execution time metric (value 'all' allows use all samples)")
|
||||
.delimiter(",")
|
||||
val execNormalize by option(ArgType.String, "exec-normalize",
|
||||
description = "File with golden results which should be used for normalization")
|
||||
val compile by option(ArgType.Choice(listOf("samples", "geomean")),
|
||||
description = "Compile time way of calculation", defaultValue = "geomean")
|
||||
val compileSamples by options(ArgType.String, "compile-samples",
|
||||
description = "Samples used for compile time metric (value 'all' allows use all samples)",
|
||||
delimiter = ",")
|
||||
description = "Compile time way of calculation").default("geomean")
|
||||
val compileSamples by option(ArgType.String, "compile-samples",
|
||||
description = "Samples used for compile time metric (value 'all' allows use all samples)")
|
||||
.delimiter(",")
|
||||
val compileNormalize by option(ArgType.String, "compile-normalize",
|
||||
description = "File with golden results which should be used for normalization")
|
||||
val codesize by option(ArgType.Choice(listOf("samples", "geomean")),
|
||||
description = "Code size way of calculation", defaultValue = "geomean")
|
||||
val codesizeSamples by options(ArgType.String, "codesize-samples",
|
||||
description = "Samples used for code size metric (value 'all' allows use all samples)",
|
||||
delimiter = ",")
|
||||
description = "Code size way of calculation").default("geomean")
|
||||
val codesizeSamples by option(ArgType.String, "codesize-samples",
|
||||
description = "Samples used for code size metric (value 'all' allows use all samples)").delimiter(",")
|
||||
val codesizeNormalize by option(ArgType.String, "codesize-normalize",
|
||||
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 mainReport by argument(ArgType.String, description = "Main report for analysis")
|
||||
|
||||
override fun execute() {
|
||||
val benchsReport = SummaryBenchmarksReport(getBenchmarkReport(mainReport!!, user))
|
||||
val benchsReport = SummaryBenchmarksReport(getBenchmarkReport(mainReport, user))
|
||||
val results = mutableListOf<String>()
|
||||
val executionNormalize = execNormalize?.let {
|
||||
parseNormalizeResults(getFileContent(it))
|
||||
@@ -171,19 +170,20 @@ fun main(args: Array<String>) {
|
||||
val argParser = ArgParser("benchmarksAnalyzer")
|
||||
argParser.subcommands(action)
|
||||
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 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",
|
||||
"Show short version of report", defaultValue = false)
|
||||
val renders by argParser.options(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")),
|
||||
shortName = "r", description = "Renders for showing information", defaultValue = listOf("text"), multiple = true)
|
||||
"Show short version of report").default(false)
|
||||
val renders by argParser.option(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")),
|
||||
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")
|
||||
|
||||
if (argParser.parse(args).commandName == "benchmarksAnalyzer") {
|
||||
// Read contents of file.
|
||||
val mainBenchsReport = getBenchmarkReport(mainReport!!, user)
|
||||
val mainBenchsReport = getBenchmarkReport(mainReport, user)
|
||||
var compareToBenchsReport = compareToReport?.let {
|
||||
getBenchmarkReport(it, user)
|
||||
}
|
||||
@@ -194,7 +194,7 @@ fun main(args: Array<String>) {
|
||||
epsValue)
|
||||
|
||||
var outputFile = output
|
||||
renders?.forEach {
|
||||
renders.forEach {
|
||||
Render.getRenderByName(it).print(summaryReport, useShortForm, outputFile)
|
||||
outputFile = null
|
||||
}
|
||||
|
||||
+1
@@ -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 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) }
|
||||
|
||||
|
||||
+1
@@ -229,6 +229,7 @@ internal fun dumpProperties(task: Task) {
|
||||
println("enableOptimization : $enableOptimizations")
|
||||
println("enableAssertions : $enableAssertions")
|
||||
println("noDefaultLibs : $noDefaultLibs")
|
||||
println("noEndorsedLibs : $noEndorsedLibs")
|
||||
println("target : $target")
|
||||
println("languageVersion : $languageVersion")
|
||||
println("apiVersion : $apiVersion")
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ interface KonanArtifactWithLibrariesSpec: KonanArtifactSpec {
|
||||
fun libraries(configure: KonanLibrariesSpec.() -> Unit)
|
||||
|
||||
fun noDefaultLibs(flag: Boolean)
|
||||
fun noEndorsedLibs(flag: Boolean)
|
||||
fun dependencies(closure: Closure<Unit>)
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -167,6 +167,9 @@ abstract class KonanArtifactWithLibrariesTask: KonanArtifactTask(), KonanArtifac
|
||||
@Input
|
||||
var noDefaultLibs = false
|
||||
|
||||
@Input
|
||||
var noEndorsedLibs = false
|
||||
|
||||
// DSL
|
||||
|
||||
override fun libraries(closure: Closure<Unit>) = libraries(ConfigureUtil.configureUsing(closure))
|
||||
@@ -176,4 +179,8 @@ abstract class KonanArtifactWithLibrariesTask: KonanArtifactTask(), KonanArtifac
|
||||
override fun noDefaultLibs(flag: Boolean) {
|
||||
noDefaultLibs = flag
|
||||
}
|
||||
|
||||
override fun noEndorsedLibs(flag: Boolean) {
|
||||
noEndorsedLibs = flag
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -213,7 +213,8 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
|
||||
addKey("-ea", enableAssertions)
|
||||
addKey("-Xtime", measureTime)
|
||||
addKey("-Xprofile-phases", measureTime)
|
||||
addKey("-nodefaultlibs", noDefaultLibs)
|
||||
addKey("-no-default-libs", noDefaultLibs)
|
||||
addKey("-no-endorsed-libs", noEndorsedLibs)
|
||||
addKey("-Xmulti-platform", enableMultiplatform)
|
||||
|
||||
if (libraries.friends.isNotEmpty())
|
||||
|
||||
+2
-1
@@ -105,7 +105,8 @@ open class KonanInteropTask @Inject constructor(val workerExecutor: WorkerExecut
|
||||
addArgs("-library", libraries.namedKlibs)
|
||||
addArgs("-library", libraries.artifacts.map { it.artifact.canonicalPath })
|
||||
|
||||
addKey("-nodefaultlibs", noDefaultLibs)
|
||||
addKey("-no-default-libs", noDefaultLibs)
|
||||
addKey("-no-endorsed-libs", noEndorsedLibs)
|
||||
|
||||
addAll(extraOpts)
|
||||
}
|
||||
|
||||
+2
@@ -110,6 +110,7 @@ class IncrementalSpecification extends BaseKonanSpecification {
|
||||
"artifactName" | "'foo'"
|
||||
"extraOpts" | "'-Xtime'"
|
||||
"noDefaultLibs" | "true"
|
||||
"noEndorsedLibs" | "true"
|
||||
}
|
||||
|
||||
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'"
|
||||
"extraOpts" | "'-verbose'"
|
||||
"noDefaultLibs" | "true"
|
||||
"noEndorsedLibs" | "true"
|
||||
}
|
||||
|
||||
def 'includeDirs.headerFilterOnly change should cause recompilation and interop reprocessing'() {
|
||||
|
||||
+27
@@ -57,11 +57,13 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
|}
|
||||
""".stripMargin(), ArtifactType.LIBRARY)
|
||||
project.addSetting(name, "noDefaultLibs", "true")
|
||||
project.addSetting(name, "noEndorsedLibs", "true")
|
||||
}
|
||||
|
||||
void createInteropLibrary(KonanProject project, String name) {
|
||||
project.addCompilerArtifact(name, "headers = math.h", ArtifactType.INTEROP)
|
||||
project.addSetting(name, "noDefaultLibs", "true")
|
||||
project.addSetting(name, "noEndorsedLibs", "true")
|
||||
}
|
||||
|
||||
KonanProject createProjectWithLibraries(Closure createLibraryFunction, Closure callBuilder) {
|
||||
@@ -79,6 +81,7 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
|
||||
result.addCompilerArtifact("main", createMainWithCalls(libraryNames, callBuilder))
|
||||
result.addSetting("main", "noDefaultLibs", "true")
|
||||
result.addSetting("main", "noEndorsedLibs", "true")
|
||||
|
||||
for (int i = 0; i < libraries.size(); i++) {
|
||||
def foo = libraryNames[i].first
|
||||
@@ -130,12 +133,15 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
|
||||
it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
|
||||
it.addSetting("foo", "noDefaultLibs", "true")
|
||||
it.addSetting("foo", "noEndorsedLibs", "true")
|
||||
|
||||
it.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
|
||||
it.addSetting("bar", "noDefaultLibs", "true")
|
||||
it.addSetting("bar", "noEndorsedLibs", "true")
|
||||
|
||||
it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
|
||||
it.addSetting("main", "noDefaultLibs", "true" )
|
||||
it.addSetting("main", "noEndorsedLibs", "true" )
|
||||
it.addLibraryToArtifactCustom("main", "allLibrariesFrom project")
|
||||
}
|
||||
project.createRunner()
|
||||
@@ -153,15 +159,19 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
|
||||
project.addCompilerArtifact("wrongFoo","fun foo() { println(24) }", ArtifactType.LIBRARY)
|
||||
project.addSetting("wrongFoo", "noDefaultLibs", "true")
|
||||
project.addSetting("wrongFoo", "noEndorsedLibs", "true")
|
||||
|
||||
subproject.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
|
||||
subproject.addSetting("foo", "noDefaultLibs", "true")
|
||||
subproject.addSetting("foo", "noEndorsedLibs", "true")
|
||||
|
||||
subproject.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
|
||||
subproject.addSetting("bar", "noDefaultLibs", "true")
|
||||
subproject.addSetting("bar", "noEndorsedLibs", "true")
|
||||
|
||||
project.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
|
||||
project.addSetting("main", "noDefaultLibs", "true" )
|
||||
project.addSetting("main", "noEndorsedLibs", "true" )
|
||||
project.addLibraryToArtifactCustom("main", "allLibrariesFrom project('subproject')")
|
||||
|
||||
project.createRunner()
|
||||
@@ -180,15 +190,19 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
|
||||
project.addCompilerArtifact("wrongFoo1", "fun foo() { println(42) }", ArtifactType.LIBRARY)
|
||||
project.addSetting("wrongFoo1", "noDefaultLibs", "true")
|
||||
project.addSetting("wrongFoo1", "noEndorsedLibs", "true")
|
||||
|
||||
subproject.addCompilerArtifact("wrongFoo2", "fun foo() { println(42) }", ArtifactType.LIBRARY)
|
||||
subproject.addSetting("wrongFoo2", "noDefaultLibs", "true")
|
||||
subproject.addSetting("wrongFoo2", "noEndorsedLibs", "true")
|
||||
|
||||
subproject.addCompilerArtifact("math1", "headers = math.h", ArtifactType.INTEROP)
|
||||
subproject.addSetting("math1", "noDefaultLibs", "true")
|
||||
subproject.addSetting("math1", "noEndorsedLibs", "true")
|
||||
|
||||
subproject.addCompilerArtifact("math2", "headers = math.h", ArtifactType.INTEROP)
|
||||
subproject.addSetting("math2", "noDefaultLibs", "true")
|
||||
subproject.addSetting("math2", "noEndorsedLibs", "true")
|
||||
|
||||
project.addCompilerArtifact("main" ,"""
|
||||
|fun foo() {}
|
||||
@@ -196,6 +210,7 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
|fun main(args: Array<String>) { foo(); math1.cos(0.0); math2.cos(0.0) }
|
||||
""".stripMargin())
|
||||
project.addSetting("main", "noDefaultLibs", "true" )
|
||||
project.addSetting("main", "noEndorsedLibs", "true" )
|
||||
project.addLibraryToArtifactCustom("main", "allInteropLibrariesFrom project('subproject')")
|
||||
|
||||
|
||||
@@ -209,12 +224,15 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
|
||||
it.addCompilerArtifact("wrongFoo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
|
||||
it.addSetting("wrongFoo", "noDefaultLibs", "true")
|
||||
it.addSetting("wrongFoo", "noEndorsedLibs", "true")
|
||||
|
||||
it.addCompilerArtifact("math1", "headers = math.h", ArtifactType.INTEROP)
|
||||
it.addSetting("math1", "noDefaultLibs", "true")
|
||||
it.addSetting("math1", "noEndorsedLibs", "true")
|
||||
|
||||
it.addCompilerArtifact("math2", "headers = math.h", ArtifactType.INTEROP)
|
||||
it.addSetting("math2", "noDefaultLibs", "true")
|
||||
it.addSetting("math2", "noEndorsedLibs", "true")
|
||||
|
||||
it.addCompilerArtifact("main" ,"""
|
||||
|fun foo() {}
|
||||
@@ -222,6 +240,7 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
|fun main(args: Array<String>) { foo(); math1.cos(0.0); math2.cos(0.0) }
|
||||
""".stripMargin())
|
||||
it.addSetting("main", "noDefaultLibs", "true" )
|
||||
it.addSetting("main", "noEndorsedLibs", "true" )
|
||||
it.addLibraryToArtifactCustom("main", "allInteropLibrariesFrom project")
|
||||
}
|
||||
project.createRunner()
|
||||
@@ -234,11 +253,13 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
|
||||
it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
|
||||
it.addSetting("foo", "noDefaultLibs", "true")
|
||||
it.addSetting("foo", "noEndorsedLibs", "true")
|
||||
it.addSetting("foo", "baseDir", "file('out')")
|
||||
|
||||
|
||||
it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo() }")
|
||||
it.addSetting("main", "noDefaultLibs", "true" )
|
||||
it.addSetting("main", "noEndorsedLibs", "true" )
|
||||
it.addSetting("main", "dependsOn", "konanArtifacts.foo.$KonanProject.HOST")
|
||||
it.addLibraryToArtifactCustom("main", "klib 'foo'")
|
||||
it.addLibraryToArtifactCustom("main", "useRepo 'out/$KonanProject.HOST'")
|
||||
@@ -253,13 +274,16 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
|
||||
it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
|
||||
it.addSetting("foo", "noDefaultLibs", "true")
|
||||
it.addSetting("foo", "noEndorsedLibs", "true")
|
||||
|
||||
it.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
|
||||
it.addSetting("bar", "noDefaultLibs", "true")
|
||||
it.addSetting("bar", "noEndorsedLibs", "true")
|
||||
it.addLibraryToArtifact("bar", "foo")
|
||||
|
||||
it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
|
||||
it.addSetting("main", "noDefaultLibs", "true" )
|
||||
it.addSetting("main", "noEndorsedLibs", "true" )
|
||||
it.addLibraryToArtifact("main", "bar")
|
||||
}
|
||||
|
||||
@@ -281,15 +305,18 @@ class LibrarySpecification extends BaseKonanSpecification {
|
||||
|
||||
subproject1.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
|
||||
subproject1.addSetting("foo", "noDefaultLibs", "true")
|
||||
subproject1.addSetting("foo", "noEndorsedLibs", "true")
|
||||
|
||||
subproject2.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
|
||||
subproject2.addSetting("bar", "noDefaultLibs", "true")
|
||||
subproject2.addSetting("bar", "noEndorsedLibs", "true")
|
||||
subproject2.addLibraryToArtifactCustom(
|
||||
"bar", "artifact rootProject.project('subproject1'), 'foo'"
|
||||
)
|
||||
|
||||
project.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
|
||||
project.addSetting("main", "noDefaultLibs", "true" )
|
||||
project.addSetting("main", "noEndorsedLibs", "true" )
|
||||
project.addLibraryToArtifactCustom(
|
||||
"main", "artifact project('subproject2'), 'bar'"
|
||||
)
|
||||
|
||||
@@ -629,7 +629,8 @@ class ExperimentalPluginTests {
|
||||
dependencies {
|
||||
implementation project(':libBar')
|
||||
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()
|
||||
arguments.argParser.parse(args)
|
||||
val outputFileName = arguments.output
|
||||
val noDefaultLibs = arguments.nodefaultlibs
|
||||
val noDefaultLibs = arguments.nodefaultlibs || arguments.nodefaultlibsDeprecated
|
||||
val noEndorsedLibs = arguments.noendorsedlibs
|
||||
val purgeUserLibs = arguments.purgeUserLibs
|
||||
val temporaryFilesDir = arguments.tempDir
|
||||
|
||||
@@ -49,7 +50,8 @@ fun invokeInterop(flavor: String, args: Array<String>): Array<String> {
|
||||
Distribution()
|
||||
).libraryResolver()
|
||||
val allLibraries = resolver.resolveWithDependencies(
|
||||
libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs
|
||||
libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs,
|
||||
noEndorsedLibs = noEndorsedLibs
|
||||
).getFullList()
|
||||
|
||||
val imports = allLibraries.map { library ->
|
||||
@@ -82,6 +84,7 @@ fun invokeInterop(flavor: String, args: Array<String>): Array<String> {
|
||||
libraries.flatMap { listOf("-library", it) } +
|
||||
repos.flatMap { listOf("-repo", it) } +
|
||||
(if (noDefaultLibs) arrayOf("-$NODEFAULTLIBS") else emptyArray()) +
|
||||
(if (noEndorsedLibs) arrayOf("-$NOENDORSEDLIBS") else emptyArray()) +
|
||||
(if (purgeUserLibs) arrayOf("-$PURGE_USER_LIBS") else emptyArray())
|
||||
|
||||
return konancArgs
|
||||
|
||||
Reference in New Issue
Block a user