diff --git a/Interop/StubGenerator/build.gradle b/Interop/StubGenerator/build.gradle index cfb1158033a..4d3f2915ad4 100644 --- a/Interop/StubGenerator/build.gradle +++ b/Interop/StubGenerator/build.gradle @@ -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 { diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/defFileDependencies.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/defFileDependencies.kt index ba520573cd5..841cf845fa4 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/defFileDependencies.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/defFileDependencies.kt @@ -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) { diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt index 6274dc71e65..81bfcd58afa 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt @@ -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) { diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt index 4a2b181a071..37e38d60695 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt @@ -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, additionalArgs: Map = 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) diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/wasm/StubGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/wasm/StubGenerator.kt index f2569a234a1..138dd8652f3 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/wasm/StubGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/wasm/StubGenerator.kt @@ -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 { diff --git a/backend.native/build.gradle b/backend.native/build.gradle index 705944ffc08..fcd2db0dd78 100644 --- a/backend.native/build.gradle +++ b/backend.native/build.gradle @@ -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, diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 3b003411987..dc3df5e66fa 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -111,6 +111,7 @@ class K2Native : CLICompiler() { with(configuration) { put(NODEFAULTLIBS, arguments.nodefaultlibs) + put(NOENDORSEDLIBS, arguments.noendorsedlibs) put(NOSTDLIB, arguments.nostdlib) put(NOPACK, arguments.nopack) put(NOMAIN, arguments.nomain) diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt index be4bf9332f1..bc607c91bb9 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt @@ -57,9 +57,12 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { valueDescription = "", description = "Include the native bitcode library", delimiter = "") var nativeLibraries: Array? = 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 diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index a6d820d03bd..0367cd2dd37 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -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) ) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index 901607c4a51..e07d243d236 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -66,6 +66,8 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("native library file paths") val NODEFAULTLIBS: CompilerConfigurationKey = CompilerConfigurationKey.create("don't link with the default libraries") + val NOENDORSEDLIBS: CompilerConfigurationKey + = CompilerConfigurationKey.create("don't link with the endorsed libraries") val NOMAIN: CompilerConfigurationKey = CompilerConfigurationKey.create("assume 'main' entry point to be provided by external libraries") val NOSTDLIB: CompilerConfigurationKey diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt index 84b64a19214..d24fff2c4f1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt @@ -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 diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 5889cbee5d5..f8859963266 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -3225,6 +3225,7 @@ void createInterop(String name, Closure conf) { interop(name, targets: [target.name]) { conf(it) noDefaultLibs(true) + noEndorsedLibs(true) baseDir "$testOutputLocal/$name" } } diff --git a/build-tools/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy b/build-tools/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy index 701ddc9cf97..35913945e8e 100644 --- a/build-tools/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy +++ b/build-tools/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy @@ -301,7 +301,7 @@ class NativeInteropPlugin implements Plugin { 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 diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/BenchmarkingPlugin.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/BenchmarkingPlugin.kt index 149bbaca447..2a18d59d75a 100644 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/BenchmarkingPlugin.kt +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/BenchmarkingPlugin.kt @@ -137,7 +137,7 @@ open class BenchmarkingPlugin: Plugin { 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 { diff --git a/build.gradle b/build.gradle index 47f9e1c3ae4..f645a36a2e8 100644 --- a/build.gradle +++ b/build.gradle @@ -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') } diff --git a/cmd/run_konan b/cmd/run_konan index 99db30dc20a..d9d563c6c90 100755 --- a/cmd/run_konan +++ b/cmd/run_konan @@ -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 \ diff --git a/cmd/run_konan.bat b/cmd/run_konan.bat index 3bc65beb458..7e4af6392a2 100644 --- a/cmd/run_konan.bat +++ b/cmd/run_konan.bat @@ -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 ^ diff --git a/endorsedLibraries/build.gradle b/endorsedLibraries/build.gradle index fc928fa4973..8d2da95ce14 100644 --- a/endorsedLibraries/build.gradle +++ b/endorsedLibraries/build.gradle @@ -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") } diff --git a/endorsedLibraries/kliopt/src/main/kotlin/org/jetbrains/kliopt/KliOpt.kt b/endorsedLibraries/kliopt/src/main/kotlin/org/jetbrains/kliopt/KliOpt.kt deleted file mode 100644 index 0fa7c1408fd..00000000000 --- a/endorsedLibraries/kliopt/src/main/kotlin/org/jetbrains/kliopt/KliOpt.kt +++ /dev/null @@ -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>) { - /** - * 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(val type: ArgType, - val fullName: String, - val description: String? = null, - val defaultValue: List = 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>() - /** - * Map of arguments: key - fullname of argument, value - pair of descriptor and parsed values. - */ - protected val arguments = mutableMapOf>() - /** - * Map of subcommands. - */ - @UseExperimental(ExperimentalCli::class) - protected val subcommands = mutableMapOf() - - /** - * Mapping for short options names for quick search. - */ - private lateinit var shortNames: Map> - - /** - * 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(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( - type: ArgType, - fullName: String, - val shortName: String ? = null, - description: String? = null, - defaultValue: List = emptyList(), - required: Boolean = false, - val multiple: Boolean = false, - val delimiter: String? = null, - deprecatedWarning: String? = null) : Descriptor (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( - type: ArgType, - fullName: String, - val number: Int? = null, - description: String? = null, - defaultValue: List = emptyList(), - required: Boolean = true, - deprecatedWarning: String? = null) : Descriptor (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(val type: ArgType, - 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 { - 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(val type: ArgType, - 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 { - 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(val type: ArgType, - val fullName: String? = null, - val shortName: String ? = null, - val description: String? = null, - val defaultValue: List = 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> { - 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 option(type: ArgType, - 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 option(type: ArgType, - 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 options(type: ArgType, - fullName: String? = null, - shortName: String ? = null, - description: String? = null, - defaultValue: List = 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(val type: ArgType, - val fullName: String? = null, - val description: String? = null, - val required: Boolean = true, - val deprecatedWarning: String? = null) { - operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface { - 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(val type: ArgType, - 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 { - 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(val type: ArgType, - val fullName: String? = null, - val number: Int? = null, - val description: String? = null, - val defaultValue: List = emptyList(), - val required: Boolean = true, - val deprecatedWarning: String? = null) { - operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface> { - 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 argument(type: ArgType, - 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 argument(type: ArgType, - 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 arguments(type: ArgType, - fullName: String? = null, - number: Int? = null, - description: String? = null, - defaultValue: List = 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 arguments(type: ArgType, - description: String? = null, - defaultValue: List = emptyList(), - required: Boolean = true, - deprecatedWarning: String? = null): ArgumentValueInterface> { - 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(val descriptor: Descriptor, val argumentValue: ArgumentValue) { - - /** - * Add parsed value from command line. - */ - fun addValue(stringValue: String, - setValue: ArgumentValue.(String, String) -> Unit = ArgumentValue::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::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 { - operator fun getValue(thisRef: Any?, property: KProperty<*>): T - } - - /** - * Argument/option value. - */ - abstract class ArgumentValue(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(conversion: (value: String, name: String, helpMessage: String)->T): - ArgumentValue(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(conversion: (value: String, name: String, helpMessage: String)->T): - ArgumentSingleValue(conversion), ArgumentValueInterface { - 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(conversion: (value: String, name: String, helpMessage: String)->T): - ArgumentSingleValue(conversion), ArgumentValueInterface { - 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(conversion: (value: String, name: String, helpMessage: String)->T): - ArgumentValue> ( - { value, name, _ -> mutableListOf(conversion(value, name, makeUsage())) } - ), ArgumentValueInterface> { - - init { - values = mutableListOf() - } - - override operator fun getValue(thisRef: Any?, property: KProperty<*>): MutableList = 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 saveAsOption(parsingValue: ParsingValue, 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): 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() - } -} \ No newline at end of file diff --git a/endorsedLibraries/kliopt/build.gradle b/endorsedLibraries/kotlinx.cli/build.gradle similarity index 83% rename from endorsedLibraries/kliopt/build.gradle rename to endorsedLibraries/kotlinx.cli/build.gradle index 93698b2d0bf..65d8c4cf074 100644 --- a/endorsedLibraries/kliopt/build.gradle +++ b/endorsedLibraries/kotlinx.cli/build.gradle @@ -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")) } } \ No newline at end of file diff --git a/endorsedLibraries/kliopt/gradle.properties b/endorsedLibraries/kotlinx.cli/gradle.properties similarity index 100% rename from endorsedLibraries/kliopt/gradle.properties rename to endorsedLibraries/kotlinx.cli/gradle.properties diff --git a/endorsedLibraries/kliopt/src/main/kotlin-js/org/jetbrains/kliopt/KliOpt.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin-js/kotlinx.cli/Utils.kt similarity index 89% rename from endorsedLibraries/kliopt/src/main/kotlin-js/org/jetbrains/kliopt/KliOpt.kt rename to endorsedLibraries/kotlinx.cli/src/main/kotlin-js/kotlinx.cli/Utils.kt index 97d4403f396..6b02f1a2f4f 100644 --- a/endorsedLibraries/kliopt/src/main/kotlin-js/org/jetbrains/kliopt/KliOpt.kt +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin-js/kotlinx.cli/Utils.kt @@ -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!") diff --git a/endorsedLibraries/kliopt/src/main/kotlin-jvm/org/jetbrains/kliopt/KliOpt.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin-jvm/kotlinx/cli/Utils.kt similarity index 89% rename from endorsedLibraries/kliopt/src/main/kotlin-jvm/org/jetbrains/kliopt/KliOpt.kt rename to endorsedLibraries/kotlinx.cli/src/main/kotlin-jvm/kotlinx/cli/Utils.kt index b09955400bf..bcc87bd5d9f 100644 --- a/endorsedLibraries/kliopt/src/main/kotlin-jvm/org/jetbrains/kliopt/KliOpt.kt +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin-jvm/kotlinx/cli/Utils.kt @@ -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) diff --git a/endorsedLibraries/kliopt/src/main/kotlin-native/org/jetbrains/kliopt/KliOpt.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin-native/kotlinx/cli/Utils.kt similarity index 89% rename from endorsedLibraries/kliopt/src/main/kotlin-native/org/jetbrains/kliopt/KliOpt.kt rename to endorsedLibraries/kotlinx.cli/src/main/kotlin-native/kotlinx/cli/Utils.kt index 29643db35c4..52d171a0ff8 100644 --- a/endorsedLibraries/kliopt/src/main/kotlin-native/org/jetbrains/kliopt/KliOpt.kt +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin-native/kotlinx/cli/Utils.kt @@ -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) diff --git a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgParser.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgParser.kt new file mode 100644 index 00000000000..69101439071 --- /dev/null +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgParser.kt @@ -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>) { + /** + * 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 { + 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>() + /** + * Map of arguments: key - full name of argument, value - pair of descriptor and parsed values. + */ + private val arguments = mutableMapOf>() + + /** + * Map with declared options. + */ + private val declaredOptions = mutableListOf() + + /** + * Map with declared arguments. + */ + private val declaredArguments = mutableListOf() + + /** + * Map of subcommands. + */ + @UseExperimental(ExperimentalCli::class) + protected val subcommands = mutableMapOf() + + /** + * Mapping for short options names for quick search. + */ + private val shortNames = mutableMapOf>() + + /** + * 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 option(type: ArgType, + fullName: String? = null, + shortName: String ? = null, + description: String? = null, + deprecatedWarning: String? = null): SingleNullableOption { + 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 argument(type: ArgType, + fullName: String? = null, + description: String? = null, + deprecatedWarning: String? = null) : SingleArgument { + 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 saveAsOption(parsingValue: ParsingValue, 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) = parse(args.asList()) + + protected fun parse(args: List): ArgParserResult { + // Add help option. + val helpDescriptor = if (useDefaultHelpShortName) OptionDescriptor(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") +} \ No newline at end of file diff --git a/endorsedLibraries/kliopt/src/main/kotlin/org/jetbrains/kliopt/ArgType.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgType.kt similarity index 66% rename from endorsedLibraries/kliopt/src/main/kotlin/org/jetbrains/kliopt/ArgType.kt rename to endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgType.kt index 895d35ec32a..e9b55eec955 100644 --- a/endorsedLibraries/kliopt/src/main/kotlin/org/jetbrains/kliopt/ArgType.kt +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgType.kt @@ -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(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(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(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(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(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(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.") } } -} \ No newline at end of file +} + +internal class ParsingException(message: String) : Exception(message) \ No newline at end of file diff --git a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgumentValues.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgumentValues.kt new file mode 100644 index 00000000000..a8d27c4e17e --- /dev/null +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ArgumentValues.kt @@ -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(val descriptor: Descriptor) { + /** + * 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(descriptor: Descriptor): + ParsingValue(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(descriptor: Descriptor): AbstractArgumentSingleValue(descriptor), + ArgumentValueDelegate { + override var value: T + get() = parsedValue + set(value) = setDelegatedValue(value) +} + +/** + * Single nullable argument value. + * + * @property descriptor descriptor of option/argument. + */ +internal class ArgumentSingleNullableValue(descriptor: Descriptor): + AbstractArgumentSingleValue(descriptor), ArgumentValueDelegate { + 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(descriptor: Descriptor>): + ParsingValue>(descriptor), ArgumentValueDelegate> { + + private val addedValue = mutableListOf() + init { + parsedValue = addedValue + } + + override var value: List + 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() +} \ No newline at end of file diff --git a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Arguments.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Arguments.kt new file mode 100644 index 00000000000..45049cc6df9 --- /dev/null +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Arguments.kt @@ -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 internal constructor(internal val owner: CLIEntityWrapper) { + /** + * Wrapper for element - read only property. + * Needed to close set of variable [cliElement]. + */ + lateinit var delegate: ArgumentValueDelegate + 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 { + (delegate as ParsingValue<*, *>).provideName(prop.name) + return delegate + } +} + +/** + * Argument instance. + */ +abstract class Argument internal constructor(owner: CLIEntityWrapper): CLIEntity(owner) + +/** + * Common single argument instance. + */ +abstract class AbstractSingleArgument internal constructor(owner: CLIEntityWrapper): Argument(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 internal constructor(descriptor: ArgDescriptor, owner: CLIEntityWrapper): + AbstractSingleArgument(owner) { + init { + checkDescriptor(descriptor) + delegate = ArgumentSingleValue(descriptor) + } +} + +/** + * Argument with single nullable value. + */ +class SingleNullableArgument internal constructor(descriptor: ArgDescriptor, owner: CLIEntityWrapper): + AbstractSingleArgument(owner){ + init { + checkDescriptor(descriptor) + delegate = ArgumentSingleNullableValue(descriptor) + } +} + +/** + * Argument with multiple values. + */ +class MultipleArgument internal constructor(descriptor: ArgDescriptor>, owner: CLIEntityWrapper): + Argument>(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 AbstractSingleArgument.number(value: Int? = null): MultipleArgument { + 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).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 AbstractSingleArgument.default(value: T): SingleArgument { + val newArgument = with((delegate as ParsingValue).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 MultipleArgument.default(value: Collection): MultipleArgument { + val newArgument = with((delegate as ParsingValue>).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 AbstractSingleArgument.optional(): SingleNullableArgument { + val newArgument = with((delegate as ParsingValue).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 MultipleArgument.optional(): MultipleArgument { + val newArgument = with((delegate as ParsingValue>).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) \ No newline at end of file diff --git a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Descriptors.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Descriptors.kt new file mode 100644 index 00000000000..05965d287d1 --- /dev/null +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Descriptors.kt @@ -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(val type: ArgType, + 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( + val optionFullFormPrefix: String, + val optionShortFromPrefix: String, + type: ArgType, + 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(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( + type: ArgType, + fullName: String?, + val number: Int? = null, + description: String? = null, + defaultValue: TResult? = null, + required: Boolean = true, + deprecatedWarning: String? = null) : Descriptor(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() + } +} \ No newline at end of file diff --git a/endorsedLibraries/kliopt/src/main/kotlin/org/jetbrains/kliopt/ExperimentalCli.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ExperimentalCli.kt similarity index 89% rename from endorsedLibraries/kliopt/src/main/kotlin/org/jetbrains/kliopt/ExperimentalCli.kt rename to endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ExperimentalCli.kt index 60a39f026f7..77c979a3333 100644 --- a/endorsedLibraries/kliopt/src/main/kotlin/org/jetbrains/kliopt/ExperimentalCli.kt +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/ExperimentalCli.kt @@ -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) diff --git a/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Options.kt b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Options.kt new file mode 100644 index 00000000000..d9d598a8554 --- /dev/null +++ b/endorsedLibraries/kotlinx.cli/src/main/kotlin/kotlinx/cli/Options.kt @@ -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 internal constructor(owner: CLIEntityWrapper): CLIEntity(owner) + +/** + * Common single option instance. + */ +abstract class AbstractSingleOption internal constructor(owner: CLIEntityWrapper): Option(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 internal constructor(descriptor: OptionDescriptor, owner: CLIEntityWrapper): + AbstractSingleOption(owner) { + init { + checkDescriptor(descriptor) + delegate = ArgumentSingleValue(descriptor) + } +} + +/** + * Option with single nullable value. + */ +class SingleNullableOption internal constructor(descriptor: OptionDescriptor, owner: CLIEntityWrapper): + AbstractSingleOption(owner) { + init { + checkDescriptor(descriptor) + delegate = ArgumentSingleNullableValue(descriptor) + } +} + +/** + * Option with multiple values. + */ +class MultipleOption internal constructor(descriptor: OptionDescriptor>, owner: CLIEntityWrapper): + Option>(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 AbstractSingleOption.multiple(): MultipleOption { + val newOption = with((delegate as ParsingValue).descriptor as OptionDescriptor) { + MultipleOption(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 MultipleOption.multiple(): MultipleOption { + val newOption = with((delegate as ParsingValue>).descriptor as OptionDescriptor) { + if (multiple) { + error("Try to use modifier multiple() twice on option ${fullName ?: ""}") + } + MultipleOption(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 AbstractSingleOption.default(value: T): SingleOption { + val newOption = with((delegate as ParsingValue).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 + MultipleOption.default(value: Collection): MultipleOption { + val newOption = with((delegate as ParsingValue>).descriptor as OptionDescriptor) { + if (required) { + printWarning("required() is unneeded, because option with default value is defined.") + } + MultipleOption(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 AbstractSingleOption.required(): SingleOption { + val newOption = with((delegate as ParsingValue).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 + MultipleOption.required(): MultipleOption { + val newOption = with((delegate as ParsingValue>).descriptor as OptionDescriptor) { + if (required) { + printWarning("required() is unneeded, because option with default value is defined.") + } + MultipleOption(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 AbstractSingleOption.delimiter(delimiterValue: String): MultipleOption { + val newOption = with((delegate as ParsingValue).descriptor as OptionDescriptor) { + MultipleOption(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 MultipleOption.delimiter(delimiterValue: String): MultipleOption { + val newOption = with((delegate as ParsingValue>).descriptor as OptionDescriptor) { + MultipleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, + description, defaultValue?.toList() ?: listOf(), + required, multiple, delimiterValue, deprecatedWarning), owner) + } + owner.entity = newOption + return newOption +} \ No newline at end of file diff --git a/endorsedLibraries/kliopt/src/tests/ArgumentsTests.kt b/endorsedLibraries/kotlinx.cli/src/tests/ArgumentsTests.kt similarity index 84% rename from endorsedLibraries/kliopt/src/tests/ArgumentsTests.kt rename to endorsedLibraries/kotlinx.cli/src/tests/ArgumentsTests.kt index eb0d5a18d92..32069536507 100644 --- a/endorsedLibraries/kliopt/src/tests/ArgumentsTests.kt +++ b/endorsedLibraries/kotlinx.cli/src/tests/ArgumentsTests.kt @@ -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")) diff --git a/endorsedLibraries/kliopt/src/tests/ErrorTests.kt b/endorsedLibraries/kotlinx.cli/src/tests/ErrorTests.kt similarity index 81% rename from endorsedLibraries/kliopt/src/tests/ErrorTests.kt rename to endorsedLibraries/kotlinx.cli/src/tests/ErrorTests.kt index 600895455e6..8e53ee35718 100644 --- a/endorsedLibraries/kliopt/src/tests/ErrorTests.kt +++ b/endorsedLibraries/kotlinx.cli/src/tests/ErrorTests.kt @@ -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 { 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 { argParser.parse(arrayOf("-r", "xml")) } diff --git a/endorsedLibraries/kliopt/src/tests/HelpTests.kt b/endorsedLibraries/kotlinx.cli/src/tests/HelpTests.kt similarity index 61% rename from endorsedLibraries/kliopt/src/tests/HelpTests.kt rename to endorsedLibraries/kotlinx.cli/src/tests/HelpTests.kt index 75542574f0e..57a634b7f52 100644 --- a/endorsedLibraries/kliopt/src/tests/HelpTests.kt +++ b/endorsedLibraries/kotlinx.cli/src/tests/HelpTests.kt @@ -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) } } diff --git a/endorsedLibraries/kliopt/src/tests/OptionsTests.kt b/endorsedLibraries/kotlinx.cli/src/tests/OptionsTests.kt similarity index 60% rename from endorsedLibraries/kliopt/src/tests/OptionsTests.kt rename to endorsedLibraries/kotlinx.cli/src/tests/OptionsTests.kt index ee7e4c3dffe..94814546ce8 100644 --- a/endorsedLibraries/kliopt/src/tests/OptionsTests.kt +++ b/endorsedLibraries/kotlinx.cli/src/tests/OptionsTests.kt @@ -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) + } } diff --git a/endorsedLibraries/kliopt/src/tests/SubcommandsTests.kt b/endorsedLibraries/kotlinx.cli/src/tests/SubcommandsTests.kt similarity index 87% rename from endorsedLibraries/kliopt/src/tests/SubcommandsTests.kt rename to endorsedLibraries/kotlinx.cli/src/tests/SubcommandsTests.kt index 844e4db7654..1a8d0e5f89f 100644 --- a/endorsedLibraries/kliopt/src/tests/SubcommandsTests.kt +++ b/endorsedLibraries/kotlinx.cli/src/tests/SubcommandsTests.kt @@ -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() { diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt index 3b34081cc43..ec4d0842ad3 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt @@ -15,7 +15,8 @@ interface KonanLibraryResolver { fun resolveWithDependencies( unresolvedLibraries: List, noStdLib: Boolean = false, - noDefaultLibs: Boolean = false + noDefaultLibs: Boolean = false, + noEndorsedLibs: Boolean = false ): KonanLibraryResolveResult } diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt index cfead652f9c..a569aa6fdbf 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt @@ -30,13 +30,14 @@ internal class KonanLibraryResolverImpl( override fun resolveWithDependencies( unresolvedLibraries: List, 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, noStdLib: Boolean, - noDefaultLibs: Boolean + noDefaultLibs: Boolean, + noEndorsedLibs: Boolean ): List { 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. diff --git a/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/main.kt b/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/main.kt index d8df64ee3c6..82e1193c1e2 100644 --- a/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/main.kt +++ b/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/main.kt @@ -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) diff --git a/performance/cinterop/build.gradle.kts b/performance/cinterop/build.gradle.kts index d9cdb7fccc7..031ba1f2725 100644 --- a/performance/cinterop/build.gradle.kts +++ b/performance/cinterop/build.gradle.kts @@ -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 diff --git a/performance/cinterop/src/main/kotlin/main.kt b/performance/cinterop/src/main/kotlin/main.kt index 45969a7b4f1..e1b55452128 100644 --- a/performance/cinterop/src/main/kotlin/main.kt +++ b/performance/cinterop/src/main/kotlin/main.kt @@ -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( diff --git a/performance/framework/build.gradle b/performance/framework/build.gradle index 54480218d6d..5783a56f418 100644 --- a/performance/framework/build.gradle +++ b/performance/framework/build.gradle @@ -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" } } diff --git a/performance/numerical/build.gradle.kts b/performance/numerical/build.gradle.kts index 00a7aa3604c..781eb33dd16 100644 --- a/performance/numerical/build.gradle.kts +++ b/performance/numerical/build.gradle.kts @@ -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 { diff --git a/performance/numerical/src/main/kotlin/main.kt b/performance/numerical/src/main/kotlin/main.kt index 6d68e22d419..395d532fb16 100644 --- a/performance/numerical/src/main/kotlin/main.kt +++ b/performance/numerical/src/main/kotlin/main.kt @@ -4,7 +4,7 @@ */ import org.jetbrains.benchmarksLauncher.* -import org.jetbrains.kliopt.* +import kotlinx.cli.* expect class NumericalLauncher() : Launcher { } diff --git a/performance/objcinterop/build.gradle.kts b/performance/objcinterop/build.gradle.kts index 3fadb4b7071..f01dc398c84 100644 --- a/performance/objcinterop/build.gradle.kts +++ b/performance/objcinterop/build.gradle.kts @@ -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 { diff --git a/performance/objcinterop/src/main/kotlin/main.kt b/performance/objcinterop/src/main/kotlin/main.kt index d14709486cd..b37ea14bbe2 100644 --- a/performance/objcinterop/src/main/kotlin/main.kt +++ b/performance/objcinterop/src/main/kotlin/main.kt @@ -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( diff --git a/performance/ring/build.gradle.kts b/performance/ring/build.gradle.kts index b6b00d05594..f866db108f2 100644 --- a/performance/ring/build.gradle.kts +++ b/performance/ring/build.gradle.kts @@ -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")) } diff --git a/performance/ring/src/main/kotlin/main.kt b/performance/ring/src/main/kotlin/main.kt index 22cdd5a1c96..0ba8a0448f1 100644 --- a/performance/ring/src/main/kotlin/main.kt +++ b/performance/ring/src/main/kotlin/main.kt @@ -18,7 +18,7 @@ import org.jetbrains.ring.* import octoTest import org.jetbrains.benchmarksLauncher.* -import org.jetbrains.kliopt.* +import kotlinx.cli.* class RingLauncher : Launcher() { diff --git a/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt b/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt index e1c4d74d34f..54a78a37a7d 100644 --- a/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt +++ b/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt @@ -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 { diff --git a/performance/swiftinterop/build.gradle b/performance/swiftinterop/build.gradle index 435f87a3ca4..73f4d575955 100644 --- a/performance/swiftinterop/build.gradle +++ b/performance/swiftinterop/build.gradle @@ -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" } } } diff --git a/performance/swiftinterop/src/main/kotlin/org/jetbrains/launcher/SwiftLauncher.kt b/performance/swiftinterop/src/main/kotlin/org/jetbrains/launcher/SwiftLauncher.kt index 608fce75e3f..15bd7099b54 100644 --- a/performance/swiftinterop/src/main/kotlin/org/jetbrains/launcher/SwiftLauncher.kt +++ b/performance/swiftinterop/src/main/kotlin/org/jetbrains/launcher/SwiftLauncher.kt @@ -4,7 +4,7 @@ */ import org.jetbrains.benchmarksLauncher.* -import org.jetbrains.kliopt.* +import kotlinx.cli.* class SwiftLauncher: Launcher() { override val benchmarks = BenchmarksCollection( diff --git a/platformLibs/build.gradle b/platformLibs/build.gradle index c977135110a..74e8a6ef285 100644 --- a/platformLibs/build.gradle +++ b/platformLibs/build.gradle @@ -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 } diff --git a/settings.gradle b/settings.gradle index 124654f0d1c..4b99c8f77fe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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' diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt index 7a000d50b85..5075a31099e 100644 --- a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt @@ -22,7 +22,7 @@ interface SearchPathResolver : WithLogger { fun resolutionSequence(givenPath: String): Sequence fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean = false): KonanLibraryImpl fun resolve(givenPath: String): KonanLibraryImpl - fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List + fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean, noEndorsedLibs: Boolean): List } // FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface! @@ -157,21 +157,34 @@ internal open class KonanLibrarySearchPathResolver( val defaultRoots: List get() = listOfNotNull(distHead, distPlatformHead).filter { it.exists } - override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List { + 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 { val result = mutableListOf() 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 diff --git a/tools/benchmarksAnalyzer/build.gradle b/tools/benchmarksAnalyzer/build.gradle index 82f60503b51..1e46e464358 100644 --- a/tools/benchmarksAnalyzer/build.gradle +++ b/tools/benchmarksAnalyzer/build.gradle @@ -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 } diff --git a/tools/benchmarksAnalyzer/src/main/kotlin/main.kt b/tools/benchmarksAnalyzer/src/main/kotlin/main.kt index b5fa956b8db..8ead8141b2e 100644 --- a/tools/benchmarksAnalyzer/src/main/kotlin/main.kt +++ b/tools/benchmarksAnalyzer/src/main/kotlin/main.kt @@ -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> { fun main(args: Array) { 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() val executionNormalize = execNormalize?.let { parseNormalizeResults(getFileContent(it)) @@ -171,19 +170,20 @@ fun main(args: Array) { 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) { epsValue) var outputFile = output - renders?.forEach { + renders.forEach { Render.getRenderByName(it).print(summaryReport, useShortForm, outputFile) outputFile = null } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanBuildingConfig.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanBuildingConfig.kt index 4eb2a300bcb..a015407eceb 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanBuildingConfig.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanBuildingConfig.kt @@ -160,6 +160,7 @@ abstract class KonanBuildingConfig(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) } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanPlugin.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanPlugin.kt index c5cb2e66e39..65d1c8e75d3 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanPlugin.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanPlugin.kt @@ -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") diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanSpecs.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanSpecs.kt index 8c5b2b696f4..1a40635bedb 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanSpecs.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanSpecs.kt @@ -30,6 +30,7 @@ interface KonanArtifactWithLibrariesSpec: KonanArtifactSpec { fun libraries(configure: KonanLibrariesSpec.() -> Unit) fun noDefaultLibs(flag: Boolean) + fun noEndorsedLibs(flag: Boolean) fun dependencies(closure: Closure) } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanBaseTasks.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanBaseTasks.kt index 11db47e0eeb..7ebb04746b8 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanBaseTasks.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanBaseTasks.kt @@ -167,6 +167,9 @@ abstract class KonanArtifactWithLibrariesTask: KonanArtifactTask(), KonanArtifac @Input var noDefaultLibs = false + @Input + var noEndorsedLibs = false + // DSL override fun libraries(closure: Closure) = 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 + } } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanCompileTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanCompileTask.kt index 836b6e5e4cb..663bafe8b13 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanCompileTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanCompileTask.kt @@ -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()) diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanInteropTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanInteropTask.kt index 18f5608c464..d38f2b14ddc 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanInteropTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanInteropTask.kt @@ -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) } diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy index f6c61ea7ac3..514f46f65d9 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/IncrementalSpecification.groovy @@ -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'() { diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/LibrarySpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/LibrarySpecification.groovy index ae15ed08432..aabf8e1d2db 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/LibrarySpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/LibrarySpecification.groovy @@ -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) { 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) { 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) { 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) { 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) { 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) { 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) { foo(); bar() }") project.addSetting("main", "noDefaultLibs", "true" ) + project.addSetting("main", "noEndorsedLibs", "true" ) project.addLibraryToArtifactCustom( "main", "artifact project('subproject2'), 'bar'" ) diff --git a/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt index e476b31baa1..ed6a6fb0539 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt +++ b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt @@ -629,7 +629,8 @@ class ExperimentalPluginTests { dependencies { implementation project(':libBar') cinterop('mystdio') { - extraOpts '-nodefaultlibs' + extraOpts '-no-default-libs' + extraOpts '-no-endorsed-libs' } } } diff --git a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt index cf38cdedad2..6bc4e6c043c 100644 --- a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt +++ b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt @@ -19,7 +19,8 @@ fun invokeInterop(flavor: String, args: Array): Array { 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): Array { 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): Array { 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