New CLI tool (#3085)
This commit is contained in:
+3
-3
@@ -135,9 +135,9 @@ To update the blackbox compiler tests set TeamCity build number in `gradle.prope
|
||||
cd tools/benchmarksAnalyzer/build/bin/<target>/benchmarksAnalyzerReleaseExecutable/
|
||||
./benchmarksAnalyzer.kexe <file1> <file2>
|
||||
|
||||
Tool has several renders which allow produce output report in different forms (text, html, etc.). To set up render use flag `-render/-r`.
|
||||
Output can be redirected to file with flag `-output/-o`.
|
||||
To get detailed information about supported options, please use `-help/-h`.
|
||||
Tool has several renders which allow produce output report in different forms (text, html, etc.). To set up render use flag `--render/-r`.
|
||||
Output can be redirected to file with flag `--output/-o`.
|
||||
To get detailed information about supported options, please use `--help/-h`.
|
||||
|
||||
Analyzer tool can compare both local files and files placed on Bintray/TeamCity.
|
||||
|
||||
|
||||
@@ -29,18 +29,15 @@ repositories {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion"
|
||||
compile "org.jetbrains.kotlin:kotlin-compiler:$buildKotlinVersion"
|
||||
compile project(':Interop:Indexer')
|
||||
compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion"
|
||||
compile project(':endorsedLibraries:kliopt')
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
sourceSets {
|
||||
main.kotlin.srcDirs += "$rootDir/tools/kliopt"
|
||||
}
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = ['-Xuse-experimental=kotlin.ExperimentalUnsignedTypes']
|
||||
}
|
||||
|
||||
+4
-4
@@ -6,7 +6,7 @@ import org.jetbrains.kotlin.native.interop.gen.jvm.buildNativeLibrary
|
||||
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.getCInteropArguments
|
||||
import org.jetbrains.kotlin.native.interop.tool.CInteropArguments
|
||||
import org.jetbrains.kliopt.ArgParser
|
||||
import java.io.File
|
||||
|
||||
@@ -39,13 +39,13 @@ private fun makeDependencyAssigner(targets: List<String>, defFiles: List<File>)
|
||||
|
||||
private fun makeDependencyAssignerForTarget(target: String, defFiles: List<File>): SingleTargetDependencyAssigner {
|
||||
val tool = prepareTool(target, KotlinPlatform.NATIVE)
|
||||
val argParser = ArgParser(getCInteropArguments(), useDefaultHelpShortName = false)
|
||||
argParser.parse(arrayOf<String>())
|
||||
val cinteropArguments = CInteropArguments()
|
||||
cinteropArguments.argParser.parse(arrayOf<String>())
|
||||
val libraries = defFiles.associateWith {
|
||||
buildNativeLibrary(
|
||||
tool,
|
||||
DefFile(it, tool.substitutions),
|
||||
argParser,
|
||||
cinteropArguments,
|
||||
ImportsImpl(emptyMap())
|
||||
).getHeaderPaths()
|
||||
}
|
||||
|
||||
+68
-65
@@ -25,75 +25,78 @@ const val TEMP_DIR = "Xtemporary-files-dir"
|
||||
|
||||
// TODO: unify camel and snake cases.
|
||||
// Possible solution is to accept both cases
|
||||
fun getCommonInteropArguments() = listOf(
|
||||
OptionDescriptor(ArgType.Boolean(), "verbose", description = "Enable verbose logging output", defaultValue = "false"),
|
||||
OptionDescriptor(ArgType.Choice(listOf("jvm", "native", "wasm")),
|
||||
"flavor", description = "Interop target", defaultValue = "jvm"),
|
||||
OptionDescriptor(ArgType.String(), "pkg", description = "place generated bindings to the package"),
|
||||
OptionDescriptor(ArgType.String(), "output", "o", "specifies the resulting library file", defaultValue = "nativelib"),
|
||||
OptionDescriptor(ArgType.String(), "libraryPath", description = "add a library search path",
|
||||
isMultiple = true, delimiter = ","),
|
||||
OptionDescriptor(ArgType.String(), "staticLibrary", description = "embed static library to the result",
|
||||
isMultiple = true, delimiter = ","),
|
||||
OptionDescriptor(ArgType.String(), "generated", description = "place generated bindings to the directory",
|
||||
defaultValue = System.getProperty("user.dir")),
|
||||
OptionDescriptor(ArgType.String(), "natives", description = "where to put the built native files",
|
||||
defaultValue = System.getProperty("user.dir")),
|
||||
OptionDescriptor(ArgType.String(), "library", "l", "library to use for building", isMultiple = true),
|
||||
OptionDescriptor(ArgType.String(), "repo", "r",
|
||||
"repository to resolve dependencies", isMultiple = true),
|
||||
OptionDescriptor(ArgType.Boolean(), NODEFAULTLIBS, description = "don't link the libraries from dist/klib automatically",
|
||||
defaultValue = "false"),
|
||||
OptionDescriptor(ArgType.Boolean(), PURGE_USER_LIBS, description = "don't link unused libraries even explicitly specified",
|
||||
defaultValue = "false"),
|
||||
OptionDescriptor(ArgType.String(), TEMP_DIR, description = "save temporary files to the given directory")
|
||||
)
|
||||
|
||||
fun getCInteropArguments(): List<OptionDescriptor> {
|
||||
val options = listOf(
|
||||
OptionDescriptor(ArgType.String(), "target", description = "native target to compile to", defaultValue = "host"),
|
||||
OptionDescriptor(ArgType.String(), "def", description = "the library definition file"),
|
||||
OptionDescriptor(ArgType.String(), "header", description = "header file to produce kotlin bindings for",
|
||||
isMultiple = true, delimiter = ","),
|
||||
OptionDescriptor(ArgType.String(), "h", description = "header file to produce kotlin bindings for",
|
||||
isMultiple = true, delimiter = ",", deprecatedWarning = "Option -h is deprecated. Please use -header."),
|
||||
OptionDescriptor(ArgType.String(), HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, "hfasp",
|
||||
"header file to produce kotlin bindings for", isMultiple = true, delimiter = ","),
|
||||
OptionDescriptor(ArgType.String(), "compilerOpts",
|
||||
description = "additional compiler options (allows to add several options separated by spaces)",
|
||||
isMultiple = true, delimiter = " "),
|
||||
OptionDescriptor(ArgType.String(), "compiler-options",
|
||||
description = "additional compiler options (allows to add several options separated by spaces)",
|
||||
isMultiple = true, delimiter = " "),
|
||||
OptionDescriptor(ArgType.String(), "linkerOpts",
|
||||
description = "additional linker options (allows to add several options separated by spaces)",
|
||||
isMultiple = true, delimiter = " "),
|
||||
OptionDescriptor(ArgType.String(), "linker-options",
|
||||
description = "additional linker options (allows to add several options separated by spaces)",
|
||||
isMultiple = true, delimiter = " "),
|
||||
OptionDescriptor(ArgType.String(), "compiler-option",
|
||||
description = "additional compiler option", isMultiple = true),
|
||||
OptionDescriptor(ArgType.String(), "linker-option",
|
||||
description = "additional linker option", isMultiple = true),
|
||||
OptionDescriptor(ArgType.String(), "copt", description = "additional compiler options (allows to add several options separated by spaces)",
|
||||
isMultiple = true, delimiter = " ", deprecatedWarning = "Option -copt is deprecated. Please use -compiler-options."),
|
||||
OptionDescriptor(ArgType.String(), "lopt", description = "additional linker options (allows to add several options separated by spaces)",
|
||||
isMultiple = true, delimiter = " ", deprecatedWarning = "Option -lopt is deprecated. Please use -linker-options."),
|
||||
OptionDescriptor(ArgType.String(), "linker", description = "use specified linker")
|
||||
)
|
||||
return (options + getCommonInteropArguments())
|
||||
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 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 nodefaultlibs by argParser.option(ArgType.Boolean, NODEFAULTLIBS,
|
||||
description = "don't link the libraries from dist/klib automatically",
|
||||
defaultValue = false)
|
||||
val purgeUserLibs by argParser.option(ArgType.Boolean, PURGE_USER_LIBS,
|
||||
description = "don't link unused libraries even explicitly specified", defaultValue = false)
|
||||
val tempDir by argParser.option(ArgType.String, TEMP_DIR,
|
||||
description = "save temporary files to the given directory")
|
||||
}
|
||||
|
||||
fun getJSInteropArguments(): List<OptionDescriptor> {
|
||||
val options = listOf(
|
||||
OptionDescriptor(ArgType.Choice(listOf("wasm32")), "target", description = "wasm target to compile to", defaultValue = "wasm32")
|
||||
)
|
||||
return (options + getCommonInteropArguments())
|
||||
class CInteropArguments(argParser: ArgParser =
|
||||
ArgParser("cinterop", useDefaultHelpShortName = false,
|
||||
prefixStyle = ArgParser.OPTION_PREFIX_STYLE.JVM)): CommonInteropArguments(argParser) {
|
||||
val target by argParser.option(ArgType.String, description = "native target to compile to", defaultValue = "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 linker by argParser.option(ArgType.String, description = "use specified linker")
|
||||
}
|
||||
|
||||
class JSInteropArguments(argParser: ArgParser = ArgParser("jsinterop", useDefaultHelpShortName = false,
|
||||
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")
|
||||
}
|
||||
|
||||
internal fun warn(msg: String) {
|
||||
println("warning: $msg")
|
||||
}
|
||||
|
||||
fun ArgParser.getValuesAsArray(propertyName: String) =
|
||||
(getAll<String>(propertyName) ?: listOf<String>()).toTypedArray()
|
||||
|
||||
+29
-30
@@ -158,35 +158,34 @@ private fun findFilesByGlobs(roots: List<Path>, globs: List<String>): Map<Path,
|
||||
}
|
||||
|
||||
|
||||
private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> = mapOf()): Array<String>? {
|
||||
val argParser = ArgParser(getCInteropArguments(), useDefaultHelpShortName = false)
|
||||
if (!argParser.parse(args))
|
||||
return null
|
||||
val ktGenRoot = argParser.get<String>("generated")
|
||||
val nativeLibsDir = argParser.get<String>("natives")
|
||||
val flavorName = argParser.get<String>("flavor")
|
||||
private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> = mapOf()): Array<String> {
|
||||
val cinteropArguments = CInteropArguments()
|
||||
cinteropArguments.argParser.parse(args)
|
||||
val ktGenRoot = cinteropArguments.generated
|
||||
val nativeLibsDir = cinteropArguments.natives
|
||||
val flavorName = cinteropArguments.flavor
|
||||
val flavor = KotlinPlatform.values().single { it.name.equals(flavorName, ignoreCase = true) }
|
||||
val defFile = argParser.get<String>("def")?.let { File(it) }
|
||||
val defFile = cinteropArguments.def?.let { File(it) }
|
||||
val manifestAddend = (additionalArgs["manifest"] as? String)?.let { File(it) }
|
||||
|
||||
if (defFile == null && argParser.get<String>("pkg") == null) {
|
||||
argParser.printError("-def or -pkg should provided!")
|
||||
if (defFile == null && cinteropArguments.pkg == null) {
|
||||
cinteropArguments.argParser.printError("-def or -pkg should provided!")
|
||||
}
|
||||
|
||||
val tool = prepareTool(argParser.get<String>("target"), flavor)
|
||||
val tool = prepareTool(cinteropArguments.target, flavor)
|
||||
|
||||
val def = DefFile(defFile, tool.substitutions)
|
||||
val isLinkerOptsSetByUser = (argParser.getOrigin("linkerOpts") == ArgParser.ValueOrigin.SET_BY_USER) ||
|
||||
(argParser.getOrigin("linker-option") == ArgParser.ValueOrigin.SET_BY_USER) ||
|
||||
(argParser.getOrigin("linker-options") == ArgParser.ValueOrigin.SET_BY_USER) ||
|
||||
(argParser.getOrigin("lopt") == ArgParser.ValueOrigin.SET_BY_USER)
|
||||
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)
|
||||
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.")
|
||||
}
|
||||
|
||||
val additionalLinkerOpts = argParser.getValuesAsArray("linkerOpts") + argParser.getValuesAsArray("linker-option") +
|
||||
argParser.getValuesAsArray("linker-options") + argParser.getValuesAsArray("lopt")
|
||||
val verbose = argParser.get<Boolean>("verbose")!!
|
||||
val additionalLinkerOpts = cinteropArguments.linkerOpts.toTypedArray() + cinteropArguments.linkerOption.toTypedArray() +
|
||||
cinteropArguments.linkerOptions.toTypedArray() + cinteropArguments.lopt.toTypedArray()
|
||||
val verbose = cinteropArguments.verbose
|
||||
|
||||
val language = selectNativeLanguage(def.config)
|
||||
|
||||
@@ -195,14 +194,14 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
|
||||
def.config.linkerOpts.toTypedArray() +
|
||||
tool.defaultCompilerOpts +
|
||||
additionalLinkerOpts
|
||||
val linkerName = argParser.get<String>("linker") ?: def.config.linker
|
||||
val linkerName = cinteropArguments.linker ?: def.config.linker
|
||||
val linker = "${tool.llvmHome}/bin/$linkerName"
|
||||
val compiler = "${tool.llvmHome}/bin/clang"
|
||||
val excludedFunctions = def.config.excludedFunctions.toSet()
|
||||
val excludedMacros = def.config.excludedMacros.toSet()
|
||||
val staticLibraries = def.config.staticLibraries + argParser.getValuesAsArray("staticLibrary")
|
||||
val libraryPaths = def.config.libraryPaths + argParser.getValuesAsArray("libraryPath")
|
||||
val fqParts = (argParser.get<String>("pkg") ?: def.config.packageName)?.split('.')
|
||||
val staticLibraries = def.config.staticLibraries + cinteropArguments.staticLibrary.toTypedArray()
|
||||
val libraryPaths = def.config.libraryPaths + cinteropArguments.libraryPath.toTypedArray()
|
||||
val fqParts = (cinteropArguments.pkg ?: def.config.packageName)?.split('.')
|
||||
?: defFile!!.name.split('.').reversed().drop(1)
|
||||
|
||||
val outKtFileName = fqParts.last() + ".kt"
|
||||
@@ -213,11 +212,11 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
|
||||
|
||||
val libName = (additionalArgs["cstubsname"] as? String)?: fqParts.joinToString("") + "stubs"
|
||||
|
||||
val tempFiles = TempFiles(libName, argParser.get<String>("Xtemporary-files-dir"))
|
||||
val tempFiles = TempFiles(libName, cinteropArguments.tempDir)
|
||||
|
||||
val imports = parseImports((additionalArgs["import"] as? List<String>)?.toTypedArray() ?: arrayOf())
|
||||
|
||||
val library = buildNativeLibrary(tool, def, argParser, imports)
|
||||
val library = buildNativeLibrary(tool, def, cinteropArguments, imports)
|
||||
|
||||
val (nativeIndex, compilation) = buildNativeIndex(library, verbose)
|
||||
|
||||
@@ -303,13 +302,13 @@ internal fun prepareTool(target: String?, flavor: KotlinPlatform): ToolConfig {
|
||||
internal fun buildNativeLibrary(
|
||||
tool: ToolConfig,
|
||||
def: DefFile,
|
||||
arguments: ArgParser,
|
||||
arguments: CInteropArguments,
|
||||
imports: ImportsImpl
|
||||
): NativeLibrary {
|
||||
val additionalHeaders = arguments.getValuesAsArray("header") + arguments.getValuesAsArray("h")
|
||||
val additionalCompilerOpts = arguments.getValuesAsArray("compilerOpts") +
|
||||
arguments.getValuesAsArray("compiler-options") + arguments.getValuesAsArray("compiler-option") +
|
||||
arguments.getValuesAsArray("copt")
|
||||
val additionalHeaders = (arguments.header + arguments.shortHeaderForm).toTypedArray()
|
||||
val additionalCompilerOpts = (arguments.compilerOpts +
|
||||
arguments.compilerOptions + arguments.compilerOption +
|
||||
arguments.copt).toTypedArray()
|
||||
|
||||
val headerFiles = def.config.headers + additionalHeaders
|
||||
val language = selectNativeLanguage(def.config)
|
||||
@@ -317,7 +316,7 @@ internal fun buildNativeLibrary(
|
||||
addAll(def.config.compilerOpts)
|
||||
addAll(tool.defaultCompilerOpts)
|
||||
addAll(additionalCompilerOpts)
|
||||
addAll(getCompilerFlagsForVfsOverlay(arguments.getValuesAsArray("headerFilterAdditionalSearchPrefix"), def))
|
||||
addAll(getCompilerFlagsForVfsOverlay(arguments.headerFilterPrefix.toTypedArray(), def))
|
||||
addAll(when (language) {
|
||||
Language.C -> emptyList()
|
||||
Language.OBJECTIVE_C -> {
|
||||
|
||||
+10
-13
@@ -4,8 +4,7 @@ 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 org.jetbrains.kotlin.native.interop.tool.getJSInteropArguments
|
||||
import org.jetbrains.kotlin.native.interop.tool.getValuesAsArray
|
||||
import org.jetbrains.kotlin.native.interop.tool.JSInteropArguments
|
||||
|
||||
fun kotlinHeader(packageName: String): String {
|
||||
return "package $packageName\n" +
|
||||
@@ -392,22 +391,20 @@ fun generateJs(interfaces: List<Interface>): String =
|
||||
const val idlMathPackage = "kotlinx.interop.wasm.math"
|
||||
const val idlDomPackage = "kotlinx.interop.wasm.dom"
|
||||
|
||||
fun processIdlLib(args: Array<String>, additionalArgs: Map<String, Any> = mapOf()): Array<String>? {
|
||||
val argParser = ArgParser(getJSInteropArguments(), useDefaultHelpShortName = false)
|
||||
if (!argParser.parse(args))
|
||||
return null
|
||||
fun processIdlLib(args: Array<String>, additionalArgs: Map<String, Any> = mapOf()): Array<String> {
|
||||
val jsInteropArguments = JSInteropArguments()
|
||||
jsInteropArguments.argParser.parse(args)
|
||||
// TODO: Refactor me.
|
||||
val ktGenRoot = File(argParser.get<String>("generated")!!).mkdirs()
|
||||
val nativeLibsDir = File(argParser.get<String>("natives")!!).mkdirs()
|
||||
val ktGenRoot = File(jsInteropArguments.generated).mkdirs()
|
||||
val nativeLibsDir = File(jsInteropArguments.natives).mkdirs()
|
||||
|
||||
val idl = when (argParser.get<String>("pkg")) {
|
||||
idlMathPackage-> idlMath
|
||||
val idl = when (jsInteropArguments.pkg) {
|
||||
idlMathPackage -> idlMath
|
||||
idlDomPackage -> idlDom
|
||||
else -> throw IllegalArgumentException("Please choose either $idlMathPackage or $idlDomPackage for -pkg argument")
|
||||
}
|
||||
|
||||
File(ktGenRoot, "kotlin_stubs.kt").writeText(generateKotlin(argParser.get<String>("pkg")!!, idl))
|
||||
File(ktGenRoot, "kotlin_stubs.kt").writeText(generateKotlin(jsInteropArguments.pkg!!, idl))
|
||||
File(nativeLibsDir, "js_stubs.js").writeText(generateJs(idl))
|
||||
File((additionalArgs["manifest"] as? String)!!).writeText("") // The manifest is currently unused for wasm.
|
||||
return argsToCompiler(argParser.getValuesAsArray("staticLibrary"), argParser.getValuesAsArray("libraryPath"))
|
||||
return argsToCompiler(jsInteropArguments.staticLibrary.toTypedArray(), jsInteropArguments.libraryPath.toTypedArray())
|
||||
}
|
||||
|
||||
@@ -301,6 +301,7 @@ class NativeInteropPlugin implements Plugin<Project> {
|
||||
|
||||
prj.dependencies {
|
||||
interopStubGenerator project(path: ":Interop:StubGenerator")
|
||||
interopStubGenerator project(path: ":endorsedLibraries:kliopt", configuration: "jvmRuntimeElements")
|
||||
}
|
||||
|
||||
// FIXME: choose tasks more wisely
|
||||
|
||||
@@ -102,10 +102,10 @@ open class BuildRegister : DefaultTask() {
|
||||
val target = System.getProperty("os.name").replace("\\s".toRegex(), "")
|
||||
|
||||
// Get summary information.
|
||||
val output = arrayOf("$analyzer", "summary", "-exec-samples", "all", "-compile", "samples",
|
||||
"-compile-samples", "HelloWorld,Videoplayer", "-codesize-samples", "all",
|
||||
"-exec-normalize", "bintray:goldenResults.csv",
|
||||
"-codesize-normalize", "bintray:goldenResults.csv", "$currentBenchmarksReportFile")
|
||||
val output = arrayOf("$analyzer", "summary", "--exec-samples", "all", "--compile", "samples",
|
||||
"--compile-samples", "HelloWorld,Videoplayer", "--codesize-samples", "all",
|
||||
"--exec-normalize", "bintray:goldenResults.csv",
|
||||
"--codesize-normalize", "bintray:goldenResults.csv", "$currentBenchmarksReportFile")
|
||||
.runCommand()
|
||||
|
||||
// Postprocess information.
|
||||
@@ -129,9 +129,9 @@ open class BuildRegister : DefaultTask() {
|
||||
// Collect framework run details.
|
||||
if (target == "MacOSX") {
|
||||
|
||||
val frameworkOutput = arrayOf("$analyzer", "summary", "-compile", "samples",
|
||||
"-compile-samples", "FrameworkBenchmarksAnalyzer", "-codesize-samples", "FrameworkBenchmarksAnalyzer",
|
||||
"-codesize-normalize", "bintray:goldenResults.csv", "$currentBenchmarksReportFile")
|
||||
val frameworkOutput = arrayOf("$analyzer", "summary", "--compile", "samples",
|
||||
"--compile-samples", "FrameworkBenchmarksAnalyzer", "--codesize-samples", "FrameworkBenchmarksAnalyzer",
|
||||
"--codesize-normalize", "bintray:goldenResults.csv", "$currentBenchmarksReportFile")
|
||||
.runCommand()
|
||||
|
||||
val buildInfoPartsFramework = frameworkOutput.split(',')
|
||||
|
||||
+28
-3
@@ -226,6 +226,7 @@ task distCompiler(type: Copy) {
|
||||
dependsOn ':utilities:jar'
|
||||
dependsOn ':klib:jar'
|
||||
dependsOn ':sharedJar'
|
||||
dependsOn ':endorsedLibraries:jvmJar'
|
||||
|
||||
destinationDir distDir
|
||||
|
||||
@@ -285,6 +286,10 @@ task distCompiler(type: Copy) {
|
||||
into('konan/lib')
|
||||
}
|
||||
|
||||
from(project(':endorsedLibraries:kliopt').file('build/libs')) {
|
||||
into('konan/lib')
|
||||
}
|
||||
|
||||
from(file("${gradle.includedBuild('kotlin-native-shared').projectDir}/build/libs")) {
|
||||
into('konan/lib')
|
||||
}
|
||||
@@ -323,7 +328,13 @@ task distRuntime(type: Copy) {
|
||||
dependsOn('commonDistRuntime')
|
||||
}
|
||||
|
||||
task distEndorsedLibraries {
|
||||
dependsOn "${hostName}CrossDistEndorsedLibraries"
|
||||
}
|
||||
|
||||
def stdlib = 'klib/common/stdlib'
|
||||
def endorsedLibs = 'klib/common/endorsedLibraries'
|
||||
def endorsedLibsBase = 'klib/common'
|
||||
|
||||
task commonDistRuntime(type: Copy) {
|
||||
destinationDir distDir
|
||||
@@ -340,6 +351,10 @@ task crossDistRuntime(type: Copy) {
|
||||
dependsOn('commonDistRuntime')
|
||||
}
|
||||
|
||||
task crossDistEndorsedLibraries(type: Copy) {
|
||||
dependsOn.addAll(targetList.collect { "${it}CrossDistEndorsedLibraries" })
|
||||
}
|
||||
|
||||
task crossDistPlatformLibs {
|
||||
dependsOn.addAll(targetList.collect { "${it}PlatformLibs" })
|
||||
}
|
||||
@@ -378,7 +393,17 @@ targetList.each { target ->
|
||||
}
|
||||
|
||||
task("${target}CrossDist") {
|
||||
dependsOn "${target}CrossDistRuntime", 'distCompiler', 'commonDistRuntime'
|
||||
dependsOn "${target}CrossDistRuntime", 'distCompiler', 'commonDistRuntime', "${target}CrossDistEndorsedLibraries"
|
||||
}
|
||||
|
||||
task("${target}CrossDistEndorsedLibraries", type: Copy) {
|
||||
dependsOn ":endorsedLibraries:${target}EndorsedLibraries"
|
||||
|
||||
destinationDir distDir
|
||||
from(project(':endorsedLibraries').file("build")) {
|
||||
include('**')
|
||||
into("$endorsedLibsBase")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,11 +412,11 @@ task distPlatformLibs {
|
||||
}
|
||||
|
||||
task dist {
|
||||
dependsOn 'distCompiler', 'distRuntime'
|
||||
dependsOn 'distCompiler', 'distRuntime', 'distEndorsedLibraries'
|
||||
}
|
||||
|
||||
task crossDist {
|
||||
dependsOn 'crossDistRuntime', 'distCompiler'
|
||||
dependsOn 'crossDistRuntime', 'distCompiler', 'crossDistEndorsedLibraries'
|
||||
}
|
||||
|
||||
task bundle(type: (isWindows()) ? Zip : Tar) {
|
||||
|
||||
+2
-1
@@ -89,12 +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"
|
||||
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"
|
||||
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"
|
||||
TOOL_CLASS=org.jetbrains.kotlin.cli.utilities.MainKt
|
||||
|
||||
LIBCLANG_DISABLE_CRASH_RECOVERY=1 \
|
||||
|
||||
+2
-1
@@ -55,6 +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 "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"
|
||||
@@ -72,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%"
|
||||
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 JAVA_OPTS=-ea ^
|
||||
-Xmx3G ^
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
def endorsedLibrariesList = ['kliopt']
|
||||
|
||||
task clean {
|
||||
doLast {
|
||||
delete buildDir
|
||||
}
|
||||
}
|
||||
|
||||
task jvmJar {
|
||||
endorsedLibrariesList.each { library ->
|
||||
dependsOn "$library:jvmJar"
|
||||
}
|
||||
}
|
||||
|
||||
// Build all default libraries.
|
||||
targetList.each { target ->
|
||||
task("${target}EndorsedLibraries", type: Copy) {
|
||||
endorsedLibrariesList.each { library ->
|
||||
dependsOn "$library:${target}${library.capitalize()}"
|
||||
}
|
||||
destinationDir project.buildDir
|
||||
endorsedLibrariesList.each { library ->
|
||||
from(project("$library").file("build/${target}${library.capitalize()}")) {
|
||||
include('**')
|
||||
into("$library")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://cache-redirector.jetbrains.com/jcenter'
|
||||
}
|
||||
maven {
|
||||
url kotlinCompilerRepo
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin-multiplatform'
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://cache-redirector.jetbrains.com/jcenter'
|
||||
}
|
||||
maven {
|
||||
url kotlinCompilerRepo
|
||||
}
|
||||
maven {
|
||||
url buildKotlinCompilerRepo
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion"
|
||||
}
|
||||
kotlin.srcDir 'src/main/kotlin'
|
||||
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-test-common:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-test-annotations-common:$kotlinVersion"
|
||||
}
|
||||
kotlin.srcDir 'src/tests'
|
||||
}
|
||||
jvm().compilations.main.defaultSourceSet {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-jdk8')
|
||||
}
|
||||
kotlin.srcDir 'src/main/kotlin-jvm'
|
||||
}
|
||||
// JVM-specific tests and their dependencies:
|
||||
jvm().compilations.test.defaultSourceSet {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
|
||||
jvm().compilations.all {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = ["-Xuse-experimental=org.jetbrains.kliopt.ExperimentalCli"]
|
||||
suppressWarnings = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def commonSrc = new File("$projectDir/src/main/kotlin")
|
||||
def nativeSrc = new File("$projectDir/src/main/kotlin-native")
|
||||
|
||||
targetList.each { target ->
|
||||
def konanJvmArgs = [*HostManager.regularJvmArgs,
|
||||
"-Dkonan.home=${rootProject.projectDir}/dist",
|
||||
"-Djava.library.path=${project.buildDir}/nativelibs/$hostName",
|
||||
]
|
||||
|
||||
def defaultArgs = ['-nopack', '-nodefaultlibs']
|
||||
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) {
|
||||
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',
|
||||
'-Xuse-experimental=kotlin.ExperimentalMultiplatform',
|
||||
'-Xallow-result-return-type',
|
||||
commonSrc.absolutePath,
|
||||
"-Xcommon-sources=${commonSrc.absolutePath}",
|
||||
nativeSrc]
|
||||
inputs.dir(nativeSrc)
|
||||
inputs.dir(commonSrc)
|
||||
outputs.dir(project.file("build/${target}Kliopt"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.jetbrains.kotlin.native.home=../../dist
|
||||
org.jetbrains.kotlin.native.jvmArgs=-Xmx6G
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
internal actual fun exitProcess(status: Int): Nothing {
|
||||
error("Not implemented for JS!")
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
internal actual fun exitProcess(status: Int): Nothing {
|
||||
kotlin.system.exitProcess(0)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
internal actual fun exitProcess(status: Int): Nothing {
|
||||
kotlin.system.exitProcess(0)
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Possible types of arguments.
|
||||
*
|
||||
* Inheritors describe type of argument value. New types can be added by user.
|
||||
* In case of options type can have parameter or not.
|
||||
*/
|
||||
abstract class ArgType<T : Any>(val hasParameter: kotlin.Boolean) {
|
||||
/**
|
||||
* Text description of type for helpMessage.
|
||||
*/
|
||||
abstract val description: kotlin.String
|
||||
|
||||
/**
|
||||
* Function to convert string argument value to its type.
|
||||
* In case of error during conversion also provides help message.
|
||||
*
|
||||
* @param value value
|
||||
*/
|
||||
abstract val conversion: (value: kotlin.String, name: kotlin.String, helpMessage: kotlin.String)->T
|
||||
|
||||
/**
|
||||
* Argument type for flags that can be only set/unset.
|
||||
*/
|
||||
object Boolean : ArgType<kotlin.Boolean>(false) {
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument type for string values.
|
||||
*/
|
||||
object String : ArgType<kotlin.String>(true) {
|
||||
override val description: kotlin.String
|
||||
get() = "{ String }"
|
||||
|
||||
override val conversion: (value: kotlin.String, name: kotlin.String, _: kotlin.String) -> kotlin.String
|
||||
get() = { value, _, _ -> value }
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument type for integer values.
|
||||
*/
|
||||
object Int : ArgType<kotlin.Int>(true) {
|
||||
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") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument type for double values.
|
||||
*/
|
||||
object Double : ArgType<kotlin.Double>(true) {
|
||||
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") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Type for arguments that have limited set of possible values.
|
||||
*/
|
||||
class Choice(val values: List<kotlin.String>) : ArgType<kotlin.String>(true) {
|
||||
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") }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.annotation.AnnotationTarget.*
|
||||
|
||||
/**
|
||||
* This annotation marks the experimental API for working with command line arguments.
|
||||
*
|
||||
* > Beware using the annotated API especially if you're developing a library, since your library might become binary incompatible
|
||||
* with the future versions of the CLI library.
|
||||
*
|
||||
* 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`.
|
||||
*/
|
||||
@Experimental(level = Experimental.Level.WARNING)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(
|
||||
CLASS,
|
||||
ANNOTATION_CLASS,
|
||||
PROPERTY,
|
||||
FIELD,
|
||||
LOCAL_VARIABLE,
|
||||
VALUE_PARAMETER,
|
||||
CONSTRUCTOR,
|
||||
FUNCTION,
|
||||
PROPERTY_GETTER,
|
||||
PROPERTY_SETTER,
|
||||
TYPEALIAS
|
||||
)
|
||||
@SinceKotlin("1.3")
|
||||
public annotation class ExperimentalCli
|
||||
@@ -0,0 +1,863 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kliopt
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
internal expect fun exitProcess(status: Int): Nothing
|
||||
|
||||
/**
|
||||
* Queue of arguments descriptors.
|
||||
* Arguments can have several values, so one descriptor can be returned several times.
|
||||
*/
|
||||
internal class ArgumentsQueue(argumentsDescriptors: List<ArgParser.ArgDescriptor<*>>) {
|
||||
/**
|
||||
* Map of arguments descriptors and their current usage number.
|
||||
*/
|
||||
private val argumentsUsageNumber = linkedMapOf(*argumentsDescriptors.map { it to 0 }.toTypedArray())
|
||||
|
||||
/**
|
||||
* Get next descriptor from queue.
|
||||
*/
|
||||
fun pop(): String? {
|
||||
if (argumentsUsageNumber.isEmpty())
|
||||
return null
|
||||
|
||||
val (currentDescriptor, usageNumber) = argumentsUsageNumber.iterator().next()
|
||||
currentDescriptor.number?.let {
|
||||
// Parse all arguments for current argument description.
|
||||
if (usageNumber + 1 >= currentDescriptor.number) {
|
||||
// All needed arguments were provided.
|
||||
argumentsUsageNumber.remove(currentDescriptor)
|
||||
} else {
|
||||
argumentsUsageNumber[currentDescriptor] = usageNumber + 1
|
||||
}
|
||||
}
|
||||
return currentDescriptor.fullName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract base class for subcommands.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalCli
|
||||
abstract class Subcommand(val name: String): ArgParser(name) {
|
||||
/**
|
||||
* Execute action if subcommand was provided.
|
||||
*/
|
||||
abstract fun execute()
|
||||
}
|
||||
|
||||
/**
|
||||
* Common descriptor both for options and positional arguments.
|
||||
*
|
||||
* @property type option/argument type, one of [ArgType].
|
||||
* @property fullName option/argument full name.
|
||||
* @property description text descrition of option/argument.
|
||||
* @property defaultValue default value for option/argument.
|
||||
* @property required if option/argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @property deprecatedWarning text message with information in case if option is deprecated.
|
||||
*/
|
||||
abstract class Descriptor<T : Any>(val type: ArgType<T>,
|
||||
val fullName: String,
|
||||
val description: String? = null,
|
||||
val defaultValue: List<T> = emptyList(),
|
||||
val required: Boolean = false,
|
||||
val deprecatedWarning: String? = null) {
|
||||
/**
|
||||
* Text description for help message.
|
||||
*/
|
||||
abstract val textDescription: String
|
||||
/**
|
||||
* Help message for descriptor.
|
||||
*/
|
||||
abstract val helpMessage: String
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument parsing result.
|
||||
* Contains name of subcommand which was called.
|
||||
*
|
||||
* @property commandName name of command which was called.
|
||||
*/
|
||||
class ArgParserResult(val commandName: String)
|
||||
|
||||
/**
|
||||
* Arguments parser.
|
||||
*
|
||||
* @property programName name of current program.
|
||||
* @property useDefaultHelpShortName add or not -h flag for hrlp message.
|
||||
* @property prefixStyle style of expected options prefix.
|
||||
* @property skipExtraArguments just skip extra arhuments in command line string without producing error message.
|
||||
*/
|
||||
open class ArgParser(val programName: String, var useDefaultHelpShortName: Boolean = true,
|
||||
var prefixStyle: OPTION_PREFIX_STYLE = OPTION_PREFIX_STYLE.LINUX,
|
||||
var skipExtraArguments: Boolean = false) {
|
||||
|
||||
/**
|
||||
* Map of options: key - fullname of option, value - pair of descriptor and parsed values.
|
||||
*/
|
||||
protected val options = mutableMapOf<String, ParsingValue<*, *>>()
|
||||
/**
|
||||
* Map of arguments: key - fullname of argument, value - pair of descriptor and parsed values.
|
||||
*/
|
||||
protected val arguments = mutableMapOf<String, ParsingValue<*, *>>()
|
||||
/**
|
||||
* Map of subcommands.
|
||||
*/
|
||||
@UseExperimental(ExperimentalCli::class)
|
||||
protected val subcommands = mutableMapOf<String, Subcommand>()
|
||||
|
||||
/**
|
||||
* Mapping for short options names for quick search.
|
||||
*/
|
||||
private lateinit var shortNames: Map<String, ParsingValue<*, *>>
|
||||
|
||||
/**
|
||||
* Used prefix form for full option form.
|
||||
*/
|
||||
protected val optionFullFormPrefix = if (prefixStyle == OPTION_PREFIX_STYLE.LINUX) "--" else "-"
|
||||
|
||||
/**
|
||||
* Used prefix form for short option form.
|
||||
*/
|
||||
protected val optionShortFromPrefix = "-"
|
||||
|
||||
/**
|
||||
* Name with all commands that should be executed.
|
||||
*/
|
||||
protected val fullCommandName = mutableListOf<String>(programName)
|
||||
|
||||
/**
|
||||
* Origin of option/argument value.
|
||||
*
|
||||
* Possible values:
|
||||
* SET_BY_USER - value of option was provided in command line string;
|
||||
* SET_DEFAULT_VALUE - value of option wasn't provided in command line, but set using default value;
|
||||
* UNSET - value of option is unset
|
||||
* REDEFINED - value of option was redefined in source code after parsing.
|
||||
*/
|
||||
enum class ValueOrigin { SET_BY_USER, SET_DEFAULT_VALUE, UNSET, REDEFINED }
|
||||
/**
|
||||
* Options prefix style.
|
||||
*
|
||||
* Possible values:
|
||||
* LINUX - Linux style, for full forms of options "--", for short form - "-"
|
||||
* JVM - JVM style, both for full and short forms of options "-"
|
||||
*/
|
||||
enum class OPTION_PREFIX_STYLE { LINUX, JVM }
|
||||
|
||||
/**
|
||||
* Option descriptor.
|
||||
*
|
||||
* Command line entity started with some prefix (-/—) and can have value as next entity in command line string.
|
||||
*
|
||||
* @property type option type, one of [ArgType].
|
||||
* @property fullName option full name.
|
||||
* @property shortName option short name.
|
||||
* @property description text descrition of option.
|
||||
* @property defaultValue default value for option.
|
||||
* @property required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @property multiple if option can be repeated several times in command line with different values. All values are stored.
|
||||
* @property delimiter delimiter that separate option provided as one string to several values.
|
||||
* @property deprecatedWarning text message with information in case if option is deprecated.
|
||||
*/
|
||||
inner class OptionDescriptor<T : Any>(
|
||||
type: ArgType<T>,
|
||||
fullName: String,
|
||||
val shortName: String ? = null,
|
||||
description: String? = null,
|
||||
defaultValue: List<T> = emptyList(),
|
||||
required: Boolean = false,
|
||||
val multiple: Boolean = false,
|
||||
val delimiter: String? = null,
|
||||
deprecatedWarning: String? = null) : Descriptor<T> (type, fullName, description, defaultValue,
|
||||
required, deprecatedWarning) {
|
||||
|
||||
override val textDescription: String
|
||||
get() = "option $optionFullFormPrefix$fullName"
|
||||
|
||||
override val helpMessage: String
|
||||
get() {
|
||||
val result = StringBuilder()
|
||||
result.append(" $optionFullFormPrefix$fullName")
|
||||
shortName?.let { result.append(", $optionShortFromPrefix$it") }
|
||||
(defaultValue.joinToString(",") { it.toString() }).also { if (!it.isEmpty()) result.append(" [$it]") }
|
||||
description?.let {result.append(" -> ${it}")}
|
||||
if (required) result.append(" (always required)")
|
||||
result.append(" ${type.description}")
|
||||
deprecatedWarning?.let { result.append(" Warning: $it") }
|
||||
result.append("\n")
|
||||
return result.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument descriptor.
|
||||
*
|
||||
* Command line entity which role is connected only with its position.
|
||||
*
|
||||
* @property type argument type, one of [ArgType].
|
||||
* @property fullName argument full name.
|
||||
* @property number expected number of values. Null means any possible number of values.
|
||||
* @property description text descrition of argument.
|
||||
* @property defaultValue default value for argument.
|
||||
* @property required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @property deprecatedWarning text message with information in case if argument is deprecated.
|
||||
*/
|
||||
inner class ArgDescriptor<T : Any>(
|
||||
type: ArgType<T>,
|
||||
fullName: String,
|
||||
val number: Int? = null,
|
||||
description: String? = null,
|
||||
defaultValue: List<T> = emptyList(),
|
||||
required: Boolean = true,
|
||||
deprecatedWarning: String? = null) : Descriptor<T> (type, fullName, description, defaultValue,
|
||||
required, deprecatedWarning) {
|
||||
|
||||
init {
|
||||
// Check arguments number correctness.
|
||||
number?.let {
|
||||
if (it < 0)
|
||||
printError("Number of arguments for argument description $fullName should be greater than zero.")
|
||||
}
|
||||
}
|
||||
|
||||
override val textDescription: String
|
||||
get() = "argument $fullName"
|
||||
|
||||
override val helpMessage: String
|
||||
get() {
|
||||
val result = StringBuilder()
|
||||
result.append(" ${fullName}")
|
||||
(defaultValue.joinToString(",") { it.toString() }).also { if (!it.isEmpty()) result.append(" [$it]") }
|
||||
description?.let { result.append(" -> ${it}") }
|
||||
if (!required) result.append(" (optional)")
|
||||
result.append(" ${type.description}")
|
||||
deprecatedWarning?.let { result.append(" Warning: $it") }
|
||||
result.append("\n")
|
||||
return result.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader for option with single possible value which is nullable.
|
||||
*/
|
||||
inner class SingleNullableOptionLoader<T : Any>(val type: ArgType<T>,
|
||||
val fullName: String? = null,
|
||||
val shortName: String ? = null,
|
||||
val description: String? = null,
|
||||
val required: Boolean = false,
|
||||
val deprecatedWarning: String? = null) {
|
||||
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<T?> {
|
||||
val name = fullName ?: prop.name
|
||||
val descriptor = OptionDescriptor(type, name, shortName, description, emptyList(),
|
||||
required, deprecatedWarning = deprecatedWarning)
|
||||
val cliElement = ArgumentSingleNullableValue(type.conversion)
|
||||
options[name] = ParsingValue(descriptor, cliElement)
|
||||
return cliElement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader for option with single possible value which has default value.
|
||||
*/
|
||||
inner class SingleOptionWithDefaultLoader<T : Any>(val type: ArgType<T>,
|
||||
val fullName: String? = null,
|
||||
val shortName: String ? = null,
|
||||
val description: String? = null,
|
||||
val defaultValue: T,
|
||||
val required: Boolean = false,
|
||||
val deprecatedWarning: String? = null) {
|
||||
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<T> {
|
||||
val name = fullName ?: prop.name
|
||||
val descriptor = OptionDescriptor(type, name, shortName, description, listOf(defaultValue),
|
||||
required, deprecatedWarning = deprecatedWarning)
|
||||
val cliElement = ArgumentSingleValueWithDefault(type.conversion)
|
||||
options[name] = ParsingValue(descriptor, cliElement)
|
||||
return cliElement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader for option with multiple possible values.
|
||||
*/
|
||||
inner class MultipleOptionsLoader<T : Any>(val type: ArgType<T>,
|
||||
val fullName: String? = null,
|
||||
val shortName: String ? = null,
|
||||
val description: String? = null,
|
||||
val defaultValue: List<T> = emptyList(),
|
||||
val required: Boolean = false,
|
||||
val multiple: Boolean = false,
|
||||
val delimiter: String? = null,
|
||||
val deprecatedWarning: String? = null) {
|
||||
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<MutableList<T>> {
|
||||
val name = fullName ?: prop.name
|
||||
val descriptor = OptionDescriptor(type, name, shortName, description, defaultValue,
|
||||
required, multiple, delimiter, deprecatedWarning)
|
||||
if (!multiple && delimiter == null)
|
||||
printError("Several values are expected for option $name. " +
|
||||
"Option must be used multiple times or split with delimiter.")
|
||||
val cliElement = ArgumentMultipleValues(type.conversion)
|
||||
options[name] = ParsingValue(descriptor, cliElement)
|
||||
return cliElement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add option with single possible value and get delegator to its value.
|
||||
*
|
||||
* @param type argument type, one of [ArgType].
|
||||
* @param fullName argument full name.
|
||||
* @param shortName option short name.
|
||||
* @param description text descrition of option.
|
||||
* @param required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @param deprecatedWarning text message with information in case if option is deprecated.
|
||||
*/
|
||||
fun <T : Any>option(type: ArgType<T>,
|
||||
fullName: String? = null,
|
||||
shortName: String ? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
deprecatedWarning: String? = null) = SingleNullableOptionLoader(type, fullName, shortName,
|
||||
description, required, deprecatedWarning)
|
||||
|
||||
/**
|
||||
* Add option with single possible value with default and get delegator to its value.
|
||||
*
|
||||
* @param type option type, one of [ArgType].
|
||||
* @param fullName option full name.
|
||||
* @param shortName option short name.
|
||||
* @param description text descrition of option.
|
||||
* @param defaultValue default value for option.
|
||||
* @param required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @param deprecatedWarning text message with information in case if option is deprecated.
|
||||
*/
|
||||
fun <T : Any>option(type: ArgType<T>,
|
||||
fullName: String? = null,
|
||||
shortName: String ? = null,
|
||||
description: String? = null,
|
||||
defaultValue: T,
|
||||
required: Boolean = false,
|
||||
deprecatedWarning: String? = null) = SingleOptionWithDefaultLoader(type, fullName, shortName,
|
||||
description, defaultValue, required, deprecatedWarning)
|
||||
|
||||
/**
|
||||
* Add option with multiple possible values and get delegator to its values.
|
||||
*
|
||||
* @param type option type, one of [ArgType].
|
||||
* @param fullName option full name.
|
||||
* @param shortName option short name.
|
||||
* @param description text descrition of option.
|
||||
* @param defaultValue default value for option.
|
||||
* @param required if option is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @param multiple if option can be repeated several times in command line with different values. All values are stored.
|
||||
* @param delimiter delimiter that separate option provided as one string to several values.
|
||||
* @param deprecatedWarning text message with information in case if option is deprecated.
|
||||
*/
|
||||
fun <T : Any>options(type: ArgType<T>,
|
||||
fullName: String? = null,
|
||||
shortName: String ? = null,
|
||||
description: String? = null,
|
||||
defaultValue: List<T> = emptyList(),
|
||||
required: Boolean = false,
|
||||
multiple: Boolean = false,
|
||||
delimiter: String? = null,
|
||||
deprecatedWarning: String? = null) = MultipleOptionsLoader(type, fullName, shortName,
|
||||
description, defaultValue, required, multiple, delimiter, deprecatedWarning)
|
||||
|
||||
/**
|
||||
* Loader for argument with single possible value which is nullable.
|
||||
*/
|
||||
inner class SingleNullableArgumentLoader<T : Any>(val type: ArgType<T>,
|
||||
val fullName: String? = null,
|
||||
val description: String? = null,
|
||||
val required: Boolean = true,
|
||||
val deprecatedWarning: String? = null) {
|
||||
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<T?> {
|
||||
val name = fullName ?: prop.name
|
||||
val descriptor = ArgDescriptor(type, name, 1, description,
|
||||
emptyList(), required, deprecatedWarning)
|
||||
val cliElement = ArgumentSingleNullableValue(type.conversion)
|
||||
arguments[name] = ParsingValue(descriptor, cliElement)
|
||||
return cliElement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader for argument with single possible value which has default one.
|
||||
*/
|
||||
inner class SingleArgumentWithDefaultLoader<T : Any>(val type: ArgType<T>,
|
||||
val fullName: String? = null,
|
||||
val description: String? = null,
|
||||
val defaultValue: T,
|
||||
val required: Boolean = true,
|
||||
val deprecatedWarning: String? = null) {
|
||||
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<T> {
|
||||
val name = fullName ?: prop.name
|
||||
val descriptor = ArgDescriptor(type, name, 1, description,
|
||||
listOf(defaultValue), required, deprecatedWarning)
|
||||
val cliElement = ArgumentSingleValueWithDefault(type.conversion)
|
||||
arguments[name] = ParsingValue(descriptor, cliElement)
|
||||
return cliElement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader for option with multiple possible values.
|
||||
*/
|
||||
inner class MultipleArgumentsLoader<T : Any>(val type: ArgType<T>,
|
||||
val fullName: String? = null,
|
||||
val number: Int? = null,
|
||||
val description: String? = null,
|
||||
val defaultValue: List<T> = emptyList(),
|
||||
val required: Boolean = true,
|
||||
val deprecatedWarning: String? = null) {
|
||||
operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ArgumentValueInterface<MutableList<T>> {
|
||||
val name = fullName ?: prop.name
|
||||
val descriptor = ArgDescriptor(type, name, number, description,
|
||||
defaultValue, required, deprecatedWarning)
|
||||
val cliElement = ArgumentMultipleValues(type.conversion)
|
||||
arguments[name] = ParsingValue(descriptor, cliElement)
|
||||
return cliElement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add argument with single nullable value and get delegator to its value.
|
||||
*
|
||||
* @param type argument type, one of [ArgType].
|
||||
* @param fullName argument full name.
|
||||
* @param description text descrition of argument.
|
||||
* @param required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @param deprecatedWarning text message with information in case if argument is deprecated.
|
||||
*/
|
||||
fun <T : Any>argument(type: ArgType<T>,
|
||||
fullName: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = true,
|
||||
deprecatedWarning: String? = null) = SingleNullableArgumentLoader(type, fullName, description,
|
||||
required, deprecatedWarning)
|
||||
|
||||
/**
|
||||
* Add argument with single value with default and get delegator to its value.
|
||||
*
|
||||
* @param type argument type, one of [ArgType].
|
||||
* @param fullName argument full name.
|
||||
* @param description text descrition of argument.
|
||||
* @param defaultValue default value for argument.
|
||||
* @param required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @param deprecatedWarning text message with information in case if argument is deprecated.
|
||||
*/
|
||||
fun <T : Any>argument(type: ArgType<T>,
|
||||
fullName: String? = null,
|
||||
description: String? = null,
|
||||
defaultValue: T,
|
||||
required: Boolean = true,
|
||||
deprecatedWarning: String? = null) = SingleArgumentWithDefaultLoader(type, fullName,
|
||||
description, defaultValue, required, deprecatedWarning )
|
||||
|
||||
/**
|
||||
* Add argument with [number] possible values and get delegator to its value.
|
||||
*
|
||||
* @param type argument type, one of [ArgType].
|
||||
* @param fullName argument full name.
|
||||
* @param number expected number of values. Null means any possible number of values.
|
||||
* @param description text descrition of argument.
|
||||
* @param defaultValue default value for argument.
|
||||
* @param required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @param deprecatedWarning text message with information in case if argument is deprecated.
|
||||
*/
|
||||
fun <T : Any>arguments(type: ArgType<T>,
|
||||
fullName: String? = null,
|
||||
number: Int? = null,
|
||||
description: String? = null,
|
||||
defaultValue: List<T> = emptyList(),
|
||||
required: Boolean = true,
|
||||
deprecatedWarning: String? = null) = MultipleArgumentsLoader(type, fullName, number,
|
||||
description, defaultValue, required, deprecatedWarning)
|
||||
|
||||
/**
|
||||
* Add subcommands.
|
||||
*
|
||||
* @param subcommandsList subcommands to add.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalCli
|
||||
fun subcommands(vararg subcommandsList: Subcommand) {
|
||||
subcommandsList.forEach {
|
||||
if (it.name in subcommands) {
|
||||
printError("Subcommand with name ${it.name} was already defined.")
|
||||
}
|
||||
|
||||
// Set same settings as main parser.
|
||||
it.prefixStyle = prefixStyle
|
||||
it.useDefaultHelpShortName = useDefaultHelpShortName
|
||||
fullCommandName.forEachIndexed { index, namePart ->
|
||||
it.fullCommandName.add(index, namePart)
|
||||
}
|
||||
subcommands[it.name] = it
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all free arguments as unnamed list.
|
||||
*
|
||||
* @param type argument type, one of [ArgType].
|
||||
* @param description text descrition of argument.
|
||||
* @param defaultValue default value for argument.
|
||||
* @param required if argument is required or not. If it's required and not provided in command line and have no default value, error will be generated.
|
||||
* @param deprecatedWarning text message with information in case if argument is deprecated.
|
||||
*/
|
||||
fun <T : Any>arguments(type: ArgType<T>,
|
||||
description: String? = null,
|
||||
defaultValue: List<T> = emptyList(),
|
||||
required: Boolean = true,
|
||||
deprecatedWarning: String? = null): ArgumentValueInterface<MutableList<T>> {
|
||||
val descriptor = ArgDescriptor(type, "", null, description,
|
||||
defaultValue, required, deprecatedWarning)
|
||||
val cliElement = ArgumentMultipleValues(type.conversion)
|
||||
if ("" in arguments) {
|
||||
printError("You can have only one unnamed list with positional arguments.")
|
||||
}
|
||||
arguments[""] = ParsingValue(descriptor, cliElement)
|
||||
return cliElement
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsing value of option/argument.
|
||||
*/
|
||||
protected inner class ParsingValue<T: Any, U: Any>(val descriptor: Descriptor<T>, val argumentValue: ArgumentValue<U>) {
|
||||
|
||||
/**
|
||||
* Add parsed value from command line.
|
||||
*/
|
||||
fun addValue(stringValue: String,
|
||||
setValue: ArgumentValue<U>.(String, String) -> Unit = ArgumentValue<U>::addValue) {
|
||||
// Check of possibility to set several values to one option/argument.
|
||||
if (descriptor is OptionDescriptor<*> && !descriptor.multiple &&
|
||||
!argumentValue.isEmpty() && descriptor.delimiter == null) {
|
||||
printError("Try to provide more than one value for ${descriptor.fullName}.")
|
||||
}
|
||||
// Show deprecated warning only first time of using option/argument.
|
||||
descriptor.deprecatedWarning?.let {
|
||||
if (argumentValue.isEmpty())
|
||||
println ("Warning: $it")
|
||||
}
|
||||
// Split value if needed.
|
||||
if (descriptor is OptionDescriptor<*> && descriptor.delimiter != null) {
|
||||
stringValue.split(descriptor.delimiter).forEach {
|
||||
argumentValue.setValue(it, descriptor.fullName)
|
||||
}
|
||||
} else {
|
||||
argumentValue.setValue(stringValue, descriptor.fullName)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default value to option.
|
||||
*/
|
||||
fun addDefaultValue() {
|
||||
descriptor.defaultValue.forEach {
|
||||
addValue(it.toString(), ArgumentValue<U>::addDefaultValue)
|
||||
}
|
||||
|
||||
if (descriptor.defaultValue.isEmpty() && descriptor.required) {
|
||||
printError("Please, provide value for ${descriptor.textDescription}. It should be always set.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface of argument value.
|
||||
*/
|
||||
interface ArgumentValueInterface<T> {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): T
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument/option value.
|
||||
*/
|
||||
abstract class ArgumentValue<T : Any>(val conversion: (value: String, name: String, helpMessage: String)->T) {
|
||||
/**
|
||||
* Values of arguments.
|
||||
*/
|
||||
protected lateinit var values: T
|
||||
/**
|
||||
* Value origin.
|
||||
*/
|
||||
var valueOrigin = ValueOrigin.UNSET
|
||||
protected set
|
||||
|
||||
/**
|
||||
* Add value from command line.
|
||||
*
|
||||
* @param stringValue value from command line.
|
||||
* @param argumentName name of argument value is added for.
|
||||
*/
|
||||
abstract fun addValue(stringValue: String, argumentName: String)
|
||||
|
||||
/**
|
||||
* Add default value.
|
||||
*
|
||||
* @param stringValue value from command line.
|
||||
* @param argumentName name of argument value is added for.
|
||||
*/
|
||||
fun addDefaultValue(stringValue: String, argumentName: String) {
|
||||
addValue(stringValue, argumentName)
|
||||
valueOrigin = ValueOrigin.SET_DEFAULT_VALUE
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if values of argument are empty.
|
||||
*/
|
||||
abstract fun isEmpty(): Boolean
|
||||
|
||||
/**
|
||||
* Check if value of argument was initialized.
|
||||
*/
|
||||
protected fun valuesAreInitialized() = ::values.isInitialized
|
||||
|
||||
/**
|
||||
* Set value from delegated property.
|
||||
*/
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
values = value
|
||||
valueOrigin = ValueOrigin.REDEFINED
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Single argument value.
|
||||
*
|
||||
* @property conversion conversion function from string value from command line to expected type.
|
||||
*/
|
||||
inner abstract class ArgumentSingleValue<T : Any>(conversion: (value: String, name: String, helpMessage: String)->T):
|
||||
ArgumentValue<T>(conversion) {
|
||||
|
||||
override fun addValue(stringValue: String, argumentName: String) {
|
||||
if (!valuesAreInitialized()) {
|
||||
values = conversion(stringValue, argumentName, makeUsage())
|
||||
valueOrigin = ValueOrigin.SET_BY_USER
|
||||
} else {
|
||||
printError("Try to provide more than one value $values and $stringValue for $argumentName.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean = !valuesAreInitialized()
|
||||
}
|
||||
|
||||
/**
|
||||
* Single nullable argument value.
|
||||
*
|
||||
* @property conversion conversion function from string value from command line to expected type.
|
||||
*/
|
||||
inner class ArgumentSingleNullableValue<T : Any>(conversion: (value: String, name: String, helpMessage: String)->T):
|
||||
ArgumentSingleValue<T>(conversion), ArgumentValueInterface<T?> {
|
||||
override operator fun getValue(thisRef: Any?, property: KProperty<*>): T? = if (!isEmpty()) values else null
|
||||
}
|
||||
|
||||
/**
|
||||
* Single argument value with default.
|
||||
*
|
||||
* @property conversion conversion function from string value from command line to expected type.
|
||||
*/
|
||||
inner class ArgumentSingleValueWithDefault<T : Any>(conversion: (value: String, name: String, helpMessage: String)->T):
|
||||
ArgumentSingleValue<T>(conversion), ArgumentValueInterface<T> {
|
||||
override operator fun getValue(thisRef: Any?, property: KProperty<*>): T = values
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple argument values.
|
||||
*
|
||||
* @property conversion conversion function from string value from command line to expected type.
|
||||
*/
|
||||
inner class ArgumentMultipleValues<T : Any>(conversion: (value: String, name: String, helpMessage: String)->T):
|
||||
ArgumentValue<MutableList<T>> (
|
||||
{ value, name, _ -> mutableListOf(conversion(value, name, makeUsage())) }
|
||||
), ArgumentValueInterface<MutableList<T>> {
|
||||
|
||||
init {
|
||||
values = mutableListOf()
|
||||
}
|
||||
|
||||
override operator fun getValue(thisRef: Any?, property: KProperty<*>): MutableList<T> = values
|
||||
|
||||
override fun addValue(stringValue: String, argumentName: String) {
|
||||
values.addAll(conversion(stringValue, argumentName, makeUsage()))
|
||||
valueOrigin = ValueOrigin.SET_BY_USER
|
||||
}
|
||||
|
||||
override fun isEmpty() = values.isEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* Output error. Also adds help usage information for easy understanding of problem.
|
||||
*
|
||||
* @param message error message.
|
||||
*/
|
||||
fun printError(message: String): Nothing {
|
||||
error("$message\n${makeUsage()}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get origin of option value.
|
||||
*
|
||||
* @param name name of argument/option.
|
||||
*/
|
||||
fun getOrigin(name: String) = options[name]?.argumentValue?.valueOrigin ?:
|
||||
arguments[name]?.argumentValue?.valueOrigin ?: printError("No option/argument $name in list of avaliable options")
|
||||
|
||||
/**
|
||||
* Save value as argument value.
|
||||
*
|
||||
* @param arg string with argument value.
|
||||
* @param argumentsQueue queue with active argument descriptors.
|
||||
*/
|
||||
private fun saveAsArg(arg: String, argumentsQueue: ArgumentsQueue): Boolean {
|
||||
// Find next uninitialized arguments.
|
||||
val name = argumentsQueue.pop()
|
||||
name?.let {
|
||||
val argumentValue = arguments[name]!!
|
||||
argumentValue.descriptor.deprecatedWarning?.let { println ("Warning: $it") }
|
||||
argumentValue.addValue(arg)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Save value as option value.
|
||||
*/
|
||||
private fun <T : Any, U: Any> saveAsOption(parsingValue: ParsingValue<T, U>, value: String) {
|
||||
parsingValue.addValue(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to recognize command line element as full form of option.
|
||||
*
|
||||
* @param candidate string with candidate in options.
|
||||
*/
|
||||
protected fun recognizeOptionFullForm(candidate: String) =
|
||||
if (candidate.startsWith(optionFullFormPrefix))
|
||||
options[candidate.substring(optionFullFormPrefix.length)]
|
||||
else null
|
||||
|
||||
/**
|
||||
* Try to recognize command line element as short form of option.
|
||||
*
|
||||
* @param candidate string with candidate in options.
|
||||
*/
|
||||
protected fun recognizeOptionShortForm(candidate: String) =
|
||||
if (candidate.startsWith(optionShortFromPrefix))
|
||||
shortNames[candidate.substring(optionShortFromPrefix.length)]
|
||||
else null
|
||||
|
||||
/**
|
||||
* Parse arguments.
|
||||
*
|
||||
* @param args array with command line arguments.
|
||||
*
|
||||
* @return true if all arguments were parsed successfully, otherwise return false and print help message.
|
||||
*/
|
||||
fun parse(args: Array<String>): ArgParserResult {
|
||||
// Add help option.
|
||||
val helpDescriptor = if (useDefaultHelpShortName) OptionDescriptor(ArgType.Boolean,
|
||||
"help", "h", "Usage info")
|
||||
else OptionDescriptor(ArgType.Boolean, "help", description = "Usage info")
|
||||
options["help"] = ParsingValue(helpDescriptor, ArgumentSingleNullableValue(helpDescriptor.type.conversion))
|
||||
|
||||
// Add default list with arguments if there can be extra free arguments.
|
||||
if (skipExtraArguments) {
|
||||
arguments(ArgType.String)
|
||||
}
|
||||
val argumentsQueue = ArgumentsQueue(arguments.map { it.value.descriptor as ArgDescriptor<*> })
|
||||
|
||||
// Fill map with short names of options.
|
||||
shortNames = options.filter { (it.value.descriptor as? OptionDescriptor<*>)?.shortName != null }.
|
||||
map { (it.value.descriptor as OptionDescriptor<*>).shortName!! to it.value }.toMap()
|
||||
|
||||
var index = 0
|
||||
while (index < args.size) {
|
||||
val arg = args[index]
|
||||
// Check for subcommands.
|
||||
@UseExperimental(ExperimentalCli::class)
|
||||
subcommands.forEach { (name, subcommand) ->
|
||||
if (arg == name) {
|
||||
// Use parser for this subcommand.
|
||||
subcommand.parse(args.slice(index + 1..args.size - 1).toTypedArray())
|
||||
subcommand.execute()
|
||||
|
||||
return ArgParserResult(name)
|
||||
}
|
||||
}
|
||||
// Parse argumnets from command line.
|
||||
if (arg.startsWith('-')) {
|
||||
// Candidate in being option.
|
||||
// Option is found.
|
||||
val argValue = recognizeOptionShortForm(arg) ?: recognizeOptionFullForm(arg)
|
||||
argValue?.descriptor?.let {
|
||||
if (argValue.descriptor.type.hasParameter) {
|
||||
if (index < args.size - 1) {
|
||||
saveAsOption(argValue, args[index + 1])
|
||||
index++
|
||||
} else {
|
||||
// An error, option with value without value.
|
||||
printError("No value for ${argValue.descriptor.textDescription}")
|
||||
}
|
||||
} else {
|
||||
// Boolean flags.
|
||||
if (argValue.descriptor.fullName == "help") {
|
||||
println(makeUsage())
|
||||
exitProcess(0)
|
||||
}
|
||||
saveAsOption(argValue, "true")
|
||||
}
|
||||
} ?: run {
|
||||
// Try save as argument.
|
||||
if (!saveAsArg(arg, argumentsQueue)) {
|
||||
printError("Unknown option $arg")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Argument is found.
|
||||
if (!saveAsArg(arg, argumentsQueue)) {
|
||||
printError("Too many arguments! Couldn't proccess argument $arg!")
|
||||
}
|
||||
}
|
||||
index++
|
||||
}
|
||||
|
||||
// Postprocess results of parsing.
|
||||
options.values.union(arguments.values).forEach { value ->
|
||||
// Not inited, append default value if needed.
|
||||
if (value.argumentValue.isEmpty()) {
|
||||
value.addDefaultValue()
|
||||
}
|
||||
}
|
||||
return ArgParserResult(programName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create message with usage description.
|
||||
*/
|
||||
internal fun makeUsage(): String {
|
||||
val result = StringBuilder()
|
||||
result.append("Usage: ${fullCommandName.joinToString(" ")} options_list\n")
|
||||
if (!arguments.isEmpty()) {
|
||||
result.append("Arguments: \n")
|
||||
arguments.forEach {
|
||||
result.append(it.value.descriptor.helpMessage)
|
||||
}
|
||||
}
|
||||
result.append("Options: \n")
|
||||
options.forEach {
|
||||
result.append(it.value.descriptor.helpMessage)
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.test.*
|
||||
|
||||
class ArgumentsTests {
|
||||
@Test
|
||||
fun testPositionalArguments() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
|
||||
val input by argParser.argument(ArgType.String, "input", "Input file")
|
||||
val output by argParser.argument(ArgType.String, "output", "Output file")
|
||||
argParser.parse(arrayOf("-d", "input.txt", "out.txt"))
|
||||
assertEquals(true, debugMode)
|
||||
assertEquals("out.txt", output)
|
||||
assertEquals("input.txt", input)
|
||||
}
|
||||
|
||||
@Test
|
||||
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")
|
||||
argParser.parse(arrayOf("out.txt", "input1.txt", "input2.txt", "input3.txt",
|
||||
"input4.txt"))
|
||||
assertEquals("out.txt", output)
|
||||
assertEquals(4, inputs.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testArgumetsWithSeveralValues() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val addendums by argParser.arguments(ArgType.Int, "addendums", 2, description = "Addendums")
|
||||
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"))
|
||||
assertEquals("out.txt", output)
|
||||
val (first, second) = addendums
|
||||
assertEquals(2, addendums.size)
|
||||
assertEquals(2, first)
|
||||
assertEquals(3, second)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSkippingExtraArguments() {
|
||||
val argParser = ArgParser("testParser", skipExtraArguments = true)
|
||||
val addendums by argParser.arguments(ArgType.Int, "addendums", 2, description = "Addendums")
|
||||
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"))
|
||||
assertEquals("out.txt", output)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.test.*
|
||||
|
||||
class ErrorTests {
|
||||
@Test
|
||||
fun testExtraArguments() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val addendums by argParser.arguments(ArgType.Int, "addendums", 2, description = "Addendums")
|
||||
val output by argParser.argument(ArgType.String, "output", "Output file")
|
||||
val debugMode by argParser.option(ArgType.Boolean, "debug", "d", "Debug mode")
|
||||
val exception = assertFailsWith<IllegalStateException> { argParser.parse(
|
||||
arrayOf("2", "-d", "3", "out.txt", "something", "else", "in", "string")) }
|
||||
assertTrue("Too many arguments! Couldn't proccess argument something" in exception.message!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnknownOption() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val output by argParser.option(ArgType.String, "output", "o", "Output file")
|
||||
val input by argParser.option(ArgType.String, "input", "i", "Input file")
|
||||
val exception = assertFailsWith<IllegalStateException> {
|
||||
argParser.parse(arrayOf("-o", "out.txt", "-d", "-i", "input.txt"))
|
||||
}
|
||||
assertTrue("Unknown option -d" in exception.message!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWrongFormat() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val number by argParser.option(ArgType.Int, "number", description = "Integer number")
|
||||
val exception = assertFailsWith<IllegalStateException> {
|
||||
argParser.parse(arrayOf("--number", "out.txt"))
|
||||
}
|
||||
assertTrue("Option number is expected to be integer number. out.txt is provided." in exception.message!!)
|
||||
}
|
||||
|
||||
@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 exception = assertFailsWith<IllegalStateException> {
|
||||
argParser.parse(arrayOf("-r", "xml"))
|
||||
}
|
||||
assertTrue("Option renders is expected to be one of [text, html]. xml is provided." in exception.message!!)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
@file:UseExperimental(ExperimentalCli::class)
|
||||
package org.jetbrains.kliopt
|
||||
|
||||
import kotlin.math.exp
|
||||
import kotlin.test.*
|
||||
|
||||
class HelpTests {
|
||||
@Test
|
||||
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 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 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)
|
||||
val user by argParser.option(ArgType.String, shortName = "u", description = "User access information for authorization")
|
||||
argParser.parse(arrayOf("-h"))
|
||||
val helpOutput = argParser.makeUsage().trimIndent()
|
||||
val expectedOutput = """
|
||||
Usage: test 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] }
|
||||
--user, -u -> User access information for authorization { String }
|
||||
--help, -h -> Usage info
|
||||
""".trimIndent()
|
||||
assertEquals(helpOutput, expectedOutput)
|
||||
}
|
||||
|
||||
@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 = ",")
|
||||
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 = ",")
|
||||
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 = ",")
|
||||
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() {
|
||||
println("Do some important things!")
|
||||
}
|
||||
}
|
||||
val action = Summary()
|
||||
// Parse args.
|
||||
val argParser = ArgParser("test")
|
||||
argParser.subcommands(action)
|
||||
argParser.parse(arrayOf("summary", "-h"))
|
||||
val helpOutput = argParser.makeUsage().trimIndent()
|
||||
val expectedOutput = """
|
||||
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] }
|
||||
--user, -u -> User access information for authorization { String }
|
||||
--help, -h -> Usage info
|
||||
""".trimIndent()
|
||||
assertEquals(helpOutput, expectedOutput)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.test.*
|
||||
|
||||
class OptionsTests {
|
||||
@Test
|
||||
fun testShortForm() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val output by argParser.option(ArgType.String, "output", "o", "Output file")
|
||||
val input by argParser.option(ArgType.String, "input", "i", "Input file")
|
||||
argParser.parse(arrayOf("-o", "out.txt", "-i", "input.txt"))
|
||||
assertEquals("out.txt", output)
|
||||
assertEquals("input.txt", input)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFullForm() {
|
||||
val argParser = ArgParser("testParser")
|
||||
val output by argParser.option(ArgType.String, shortName = "o", description = "Output file")
|
||||
val input by argParser.option(ArgType.String, shortName = "i", description = "Input file")
|
||||
argParser.parse(arrayOf("--output", "out.txt", "--input", "input.txt"))
|
||||
assertEquals("out.txt", output)
|
||||
assertEquals("input.txt", input)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJavaPrefix() {
|
||||
val argParser = ArgParser("testParser", prefixStyle = ArgParser.OPTION_PREFIX_STYLE.JVM)
|
||||
val output by argParser.option(ArgType.String, "output", "o", "Output file")
|
||||
val input by argParser.option(ArgType.String, "input", "i", "Input file")
|
||||
argParser.parse(arrayOf("-output", "out.txt", "-i", "input.txt"))
|
||||
assertEquals("out.txt", output)
|
||||
assertEquals("input.txt", input)
|
||||
}
|
||||
|
||||
@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)
|
||||
argParser.parse(arrayOf("-s", "-r", "text", "-r", "json"))
|
||||
assertEquals(true, useShortForm)
|
||||
assertEquals(2, renders.size)
|
||||
val (firstRender, secondRender) = renders
|
||||
assertEquals("text", firstRender)
|
||||
assertEquals("json", secondRender)
|
||||
}
|
||||
|
||||
@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 output by argParser.option(ArgType.String, "output", "o", "Output file")
|
||||
argParser.parse(arrayOf("-o", "out.txt"))
|
||||
assertEquals(false, useShortForm)
|
||||
assertEquals("text", renders[0])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
@file:UseExperimental(ExperimentalCli::class)
|
||||
package org.jetbrains.kliopt
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
class SubcommandsTests {
|
||||
@Test
|
||||
fun testSubcommand() {
|
||||
val argParser = ArgParser("testParser")
|
||||
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")
|
||||
var result: Int = 0
|
||||
|
||||
override fun execute() {
|
||||
result = addendums.sum()
|
||||
result = if (invert!!) -1 * result else result
|
||||
}
|
||||
}
|
||||
val action = Summary()
|
||||
argParser.subcommands(action)
|
||||
argParser.parse(arrayOf("-o", "out.txt", "summary", "-i", "2", "3", "5"))
|
||||
assertEquals("out.txt", output)
|
||||
assertEquals(-10, action.result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCommonOptions() {
|
||||
abstract class CommonOptions(name: String): Subcommand(name) {
|
||||
val numbers by arguments(ArgType.Int, "numbers", description = "Numbers")
|
||||
}
|
||||
class Summary: CommonOptions("summary") {
|
||||
val invert by option(ArgType.Boolean, "invert", "i", "Invert results")
|
||||
var result: Int = 0
|
||||
|
||||
override fun execute() {
|
||||
result = numbers.sum()
|
||||
result = invert?.let { -1 * result } ?: result
|
||||
}
|
||||
}
|
||||
|
||||
class Subtraction : CommonOptions("sub") {
|
||||
var result: Int = 0
|
||||
|
||||
override fun execute() {
|
||||
result = numbers.map { -it }.sum()
|
||||
}
|
||||
}
|
||||
|
||||
val summaryAction = Summary()
|
||||
val subtractionAction = Subtraction()
|
||||
val argParser = ArgParser("testParser")
|
||||
argParser.subcommands(summaryAction, subtractionAction)
|
||||
argParser.parse(arrayOf("summary", "2", "3", "5"))
|
||||
assertEquals(10, summaryAction.result)
|
||||
|
||||
val argParserSubtraction = ArgParser("testParser")
|
||||
argParserSubtraction.subcommands(summaryAction, subtractionAction)
|
||||
argParserSubtraction.parse(arrayOf("sub", "8", "-2", "3"))
|
||||
assertEquals(-9, subtractionAction.result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRecursiveSubcommands() {
|
||||
val argParser = ArgParser("testParser")
|
||||
|
||||
class Summary: Subcommand("summary") {
|
||||
val addendums by arguments(ArgType.Int, "addendums", description = "Addendums")
|
||||
var result: Int = 0
|
||||
|
||||
override fun execute() {
|
||||
result = addendums.sum()
|
||||
}
|
||||
}
|
||||
|
||||
class Calculation: Subcommand("calc") {
|
||||
init {
|
||||
subcommands(Summary())
|
||||
}
|
||||
val invert by option(ArgType.Boolean, "invert", "i", "Invert results")
|
||||
var result: Int = 0
|
||||
|
||||
override fun execute() {
|
||||
result = (subcommands["summary"] as Summary).result
|
||||
result = if (invert!!) -1 * result else result
|
||||
}
|
||||
}
|
||||
|
||||
val action = Calculation()
|
||||
argParser.subcommands(action)
|
||||
argParser.parse(arrayOf("calc", "-i", "summary", "2", "3", "5"))
|
||||
assertEquals(-10, action.result)
|
||||
}
|
||||
}
|
||||
@@ -170,5 +170,5 @@ task swiftinterop {
|
||||
|
||||
task videoplayer {
|
||||
dependsOn 'clean'
|
||||
dependsOn 'swiftinterop:konanRun'
|
||||
dependsOn 'videoplayer:konanRun'
|
||||
}
|
||||
@@ -11,7 +11,7 @@ plugins {
|
||||
|
||||
benchmark {
|
||||
applicationName = "Cinterop"
|
||||
commonSrcDirs = listOf("../../tools/benchmarks/shared/src", "src/main/kotlin", "../shared/src/main/kotlin", "../../tools/kliopt")
|
||||
commonSrcDirs = listOf("../../tools/benchmarks/shared/src", "src/main/kotlin", "../shared/src/main/kotlin")
|
||||
jvmSrcDirs = listOf("src/main/kotlin-jvm", "../shared/src/main/kotlin-jvm")
|
||||
nativeSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native")
|
||||
}
|
||||
|
||||
@@ -39,8 +39,10 @@ class CinteropLauncher : Launcher() {
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val launcher = CinteropLauncher()
|
||||
BenchmarksRunner.runBenchmarks(args, { parser: ArgParser ->
|
||||
launcher.launch(parser.get<Int>("warmup")!!, parser.get<Int>("repeat")!!, parser.get<String>("prefix")!!,
|
||||
parser.getAll<String>("filter"), parser.getAll<String>("filterRegex"))
|
||||
BenchmarksRunner.runBenchmarks(args, { arguments: BenchmarkArguments ->
|
||||
if (arguments is BaseBenchmarkArguments) {
|
||||
launcher.launch(arguments.warmup, arguments.repeat, arguments.prefix,
|
||||
arguments.filter, arguments.filterRegex)
|
||||
} else emptyList()
|
||||
}, benchmarksListAction = launcher::benchmarksListAction)
|
||||
}
|
||||
@@ -37,7 +37,8 @@ kotlin {
|
||||
}
|
||||
kotlin.srcDir "$toolsPath/benchmarks/shared/src"
|
||||
kotlin.srcDir "$toolsPath/benchmarksAnalyzer/src/main/kotlin"
|
||||
kotlin.srcDir "$toolsPath/kliopt"
|
||||
kotlin.srcDir "$rootProject.projectDir/endorsedLibraries/kliopt/src/main/kotlin"
|
||||
kotlin.srcDir "$rootProject.projectDir/endorsedLibraries/kliopt/src/main/kotlin-native"
|
||||
kotlin.srcDir "$toolsPath/benchmarksAnalyzer/src/main/kotlin-native"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ plugins {
|
||||
|
||||
benchmark {
|
||||
applicationName = "ObjCInterop"
|
||||
commonSrcDirs = listOf("../../tools/benchmarks/shared/src", "src/main/kotlin", "../shared/src/main/kotlin", "../../tools/kliopt")
|
||||
commonSrcDirs = listOf("../../tools/benchmarks/shared/src", "src/main/kotlin", "../shared/src/main/kotlin")
|
||||
jvmSrcDirs = listOf("src/main/kotlin-jvm", "../shared/src/main/kotlin-jvm")
|
||||
nativeSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native")
|
||||
linkerOpts = listOf("-L$buildDir", "-lcomplexnumbers")
|
||||
|
||||
@@ -26,8 +26,10 @@ class ObjCInteropLauncher: Launcher() {
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val launcher = ObjCInteropLauncher()
|
||||
BenchmarksRunner.runBenchmarks(args, { parser: ArgParser ->
|
||||
launcher.launch(parser.get<Int>("warmup")!!, parser.get<Int>("repeat")!!, parser.get<String>("prefix")!!,
|
||||
parser.getAll<String>("filter"), parser.getAll<String>("filterRegex"))
|
||||
BenchmarksRunner.runBenchmarks(args, { arguments: BenchmarkArguments ->
|
||||
if (arguments is BaseBenchmarkArguments) {
|
||||
launcher.launch(arguments.warmup, arguments.repeat, arguments.prefix,
|
||||
arguments.filter, arguments.filterRegex)
|
||||
} else emptyList()
|
||||
}, benchmarksListAction = launcher::benchmarksListAction)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ plugins {
|
||||
|
||||
benchmark {
|
||||
applicationName = "Ring"
|
||||
commonSrcDirs = listOf("../../tools/benchmarks/shared/src", "src/main/kotlin", "../shared/src/main/kotlin", "../../tools/kliopt")
|
||||
jvmSrcDirs = listOf("src/main/kotlin-jvm", "../shared/src/main/kotlin-jvm")
|
||||
nativeSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native")
|
||||
commonSrcDirs = listOf("../../tools/benchmarks/shared/src", "src/main/kotlin", "../shared/src/main/kotlin", "../../endorsedLibraries/kliopt/src/main/kotlin")
|
||||
jvmSrcDirs = listOf("src/main/kotlin-jvm", "../shared/src/main/kotlin-jvm", "../../endorsedLibraries/kliopt/src/main/kotlin-jvm")
|
||||
nativeSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native", "../../endorsedLibraries/kliopt/src/main/kotlin-native")
|
||||
}
|
||||
|
||||
@@ -209,8 +209,10 @@ class RingLauncher : Launcher() {
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val launcher = RingLauncher()
|
||||
BenchmarksRunner.runBenchmarks(args, { parser: ArgParser ->
|
||||
launcher.launch(parser.get<Int>("warmup")!!, parser.get<Int>("repeat")!!, parser.get<String>("prefix")!!,
|
||||
parser.getAll<String>("filter"), parser.getAll<String>("filterRegex"))
|
||||
BenchmarksRunner.runBenchmarks(args, { arguments: BenchmarkArguments ->
|
||||
if (arguments is BaseBenchmarkArguments) {
|
||||
launcher.launch(arguments.warmup, arguments.repeat, arguments.prefix,
|
||||
arguments.filter, arguments.filterRegex)
|
||||
} else emptyList()
|
||||
}, benchmarksListAction = launcher::benchmarksListAction)
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:UseExperimental(ExperimentalCli::class)
|
||||
package org.jetbrains.benchmarksLauncher
|
||||
|
||||
import org.jetbrains.report.BenchmarkResult
|
||||
@@ -87,46 +87,58 @@ abstract class Launcher {
|
||||
return benchmarkResults
|
||||
}
|
||||
|
||||
fun benchmarksListAction(argParser: ArgParser) {
|
||||
fun benchmarksListAction() {
|
||||
benchmarks.keys.forEach {
|
||||
println(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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)
|
||||
}
|
||||
|
||||
object BenchmarksRunner {
|
||||
fun parse(args: Array<String>, benchmarksListAction: (ArgParser)->Unit): ArgParser? {
|
||||
val actions = mapOf("list" to Action(
|
||||
benchmarksListAction,
|
||||
ArgParser(listOf<OptionDescriptor>()))
|
||||
)
|
||||
val options = listOf(
|
||||
OptionDescriptor(ArgType.Int(), "warmup", "w", "Number of warm up iterations", "20"),
|
||||
OptionDescriptor(ArgType.Int(), "repeat", "r", "Number of each benchmark run", "60"),
|
||||
OptionDescriptor(ArgType.String(), "prefix", "p", "Prefix added to benchmark name", ""),
|
||||
OptionDescriptor(ArgType.String(), "output", "o", "Output file"),
|
||||
OptionDescriptor(ArgType.String(), "filter", "f", "Benchmark to run", isMultiple = true),
|
||||
OptionDescriptor(ArgType.String(), "filterRegex", "fr", "Benchmark to run, described by a regular expression", isMultiple = true)
|
||||
)
|
||||
fun parse(args: Array<String>, benchmarksListAction: ()->Unit): BenchmarkArguments? {
|
||||
class List: Subcommand("list") {
|
||||
override fun execute() {
|
||||
benchmarksListAction()
|
||||
}
|
||||
}
|
||||
|
||||
// Parse args.
|
||||
val argParser = ArgParser(options, actions = actions)
|
||||
return if (argParser.parse(args)) argParser else null
|
||||
val argParser = ArgParser("benchmark")
|
||||
argParser.subcommands(List())
|
||||
val argumentsValues = BaseBenchmarkArguments(argParser)
|
||||
return if (argParser.parse(args).commandName == "benchmark") argumentsValues else null
|
||||
}
|
||||
|
||||
fun collect(results: List<BenchmarkResult>, parser: ArgParser) {
|
||||
JsonReportCreator(results).printJsonReport(parser.get<String>("output"))
|
||||
fun collect(results: List<BenchmarkResult>, arguments: BenchmarkArguments) {
|
||||
if (arguments is BaseBenchmarkArguments) {
|
||||
JsonReportCreator(results).printJsonReport(arguments.output)
|
||||
}
|
||||
}
|
||||
|
||||
fun runBenchmarks(args: Array<String>,
|
||||
run: (parser: ArgParser) -> List<BenchmarkResult>,
|
||||
parseArgs: (args: Array<String>, benchmarksListAction: (ArgParser)->Unit) -> ArgParser? = this::parse,
|
||||
collect: (results: List<BenchmarkResult>, parser: ArgParser) -> Unit = this::collect,
|
||||
benchmarksListAction: (ArgParser)->Unit) {
|
||||
val parser = parseArgs(args, benchmarksListAction)
|
||||
parser?.let {
|
||||
val results = run(parser)
|
||||
collect(results, parser)
|
||||
run: (parser: BenchmarkArguments) -> List<BenchmarkResult>,
|
||||
parseArgs: (args: Array<String>, benchmarksListAction: ()->Unit) -> BenchmarkArguments? = this::parse,
|
||||
collect: (results: List<BenchmarkResult>, arguments: BenchmarkArguments) -> Unit = this::collect,
|
||||
benchmarksListAction: ()->Unit) {
|
||||
val arguments = parseArgs(args, benchmarksListAction)
|
||||
arguments?.let {
|
||||
val results = run(arguments)
|
||||
collect(results, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +39,13 @@ kotlin {
|
||||
}
|
||||
kotlin.srcDir "src"
|
||||
kotlin.srcDir "../shared/src/main/kotlin"
|
||||
kotlin.srcDir "$toolsPath/kliopt"
|
||||
kotlin.srcDir "$toolsPath/benchmarks/shared/src"
|
||||
kotlin.srcDir "$rootProject.projectDir/endorsedLibraries/kliopt/src/main/kotlin"
|
||||
kotlin.srcDir "$toolsPath/benchmarks/shared/src/main/kotlin"
|
||||
}
|
||||
macosMain {
|
||||
dependsOn commonMain
|
||||
kotlin.srcDir "../shared/src/main/kotlin-native"
|
||||
kotlin.srcDir "$rootProject.projectDir/endorsedLibraries/kliopt/src/main/kotlin-native"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ let companion = BenchmarkEntryWithInit.Companion()
|
||||
|
||||
var swiftLauncher = SwiftLauncher()
|
||||
|
||||
runner.runBenchmarks(args: args, run: { (parser: ArgParser) -> [BenchmarkResult] in
|
||||
runner.runBenchmarks(args: args, run: { (arguments: BenchmarkArguments) -> [BenchmarkResult] in
|
||||
swiftLauncher.add(name: "createMultigraphOfInt", benchmark: companion.create(ctor: { return SwiftInteropBenchmarks() },
|
||||
lambda: { ($0 as! SwiftInteropBenchmarks).createMultigraphOfInt() }))
|
||||
swiftLauncher.add(name: "fillCityMap", benchmark: companion.create(ctor: { return SwiftInteropBenchmarks() },
|
||||
@@ -38,12 +38,16 @@ runner.runBenchmarks(args: args, run: { (parser: ArgParser) -> [BenchmarkResult]
|
||||
lambda: { ($0 as! SwiftInteropBenchmarks).stringInterop() }))
|
||||
swiftLauncher.add(name: "simpleFunction", benchmark: companion.create(ctor: { return SwiftInteropBenchmarks() },
|
||||
lambda: { ($0 as! SwiftInteropBenchmarks).simpleFunction() }))
|
||||
return swiftLauncher.launch(numWarmIterations: parser.get(name: "warmup") as! Int32,
|
||||
numberOfAttempts: parser.get(name: "repeat") as! Int32,
|
||||
prefix: parser.get(name: "prefix") as! String, filters: parser.getAll(name: "filter"),
|
||||
filterRegexes: parser.getAll(name: "filterRegex"))
|
||||
}, parseArgs: { (args: KotlinArray, benchmarksListAction: ((ArgParser) -> KotlinUnit)) -> ArgParser? in
|
||||
if arguments is BaseBenchmarkArguments {
|
||||
let argumentsList: BaseBenchmarkArguments = arguments as! BaseBenchmarkArguments
|
||||
return swiftLauncher.launch(numWarmIterations: argumentsList.warmup,
|
||||
numberOfAttempts: argumentsList.repeat,
|
||||
prefix: argumentsList.prefix, filters: argumentsList.filter,
|
||||
filterRegexes: argumentsList.filterRegex)
|
||||
}
|
||||
return [BenchmarkResult]()
|
||||
}, parseArgs: { (args: KotlinArray, benchmarksListAction: (() -> KotlinUnit)) -> BenchmarkArguments? in
|
||||
return runner.parse(args: args, benchmarksListAction: swiftLauncher.benchmarksListAction) },
|
||||
collect: { (benchmarks: [BenchmarkResult], parser: ArgParser) -> Void in
|
||||
runner.collect(results: benchmarks, parser: parser)
|
||||
collect: { (benchmarks: [BenchmarkResult], arguments: BenchmarkArguments) -> Void in
|
||||
runner.collect(results: benchmarks, arguments: arguments)
|
||||
}, benchmarksListAction: swiftLauncher.benchmarksListAction)
|
||||
@@ -43,6 +43,8 @@ if (System.getProperty("os.name") == "Mac OS X") {
|
||||
}
|
||||
|
||||
include ':platformLibs'
|
||||
include ':endorsedLibraries'
|
||||
include ':endorsedLibraries:kliopt'
|
||||
|
||||
if (hasProperty("kotlinProjectPath")) {
|
||||
include ':runtime:generator'
|
||||
|
||||
@@ -164,7 +164,6 @@ internal open class KonanLibrarySearchPathResolver(
|
||||
if (!noStdLib) {
|
||||
result.add(resolve(UnresolvedLibrary(KONAN_STDLIB_NAME, null), true))
|
||||
}
|
||||
|
||||
if (!noDefaultLibs) {
|
||||
val defaultLibs = defaultRoots.flatMap { it.listFiles }
|
||||
.asSequence()
|
||||
|
||||
@@ -48,8 +48,7 @@ kotlin {
|
||||
}
|
||||
kotlin.srcDir '../benchmarks/shared/src'
|
||||
kotlin.srcDir 'src/main/kotlin'
|
||||
kotlin.srcDir '../kliopt'
|
||||
|
||||
kotlin.srcDir '../../endorsedLibraries/kliopt/src/main/kotlin'
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
@@ -72,18 +71,21 @@ kotlin {
|
||||
nativeMain {
|
||||
dependsOn commonMain
|
||||
kotlin.srcDir 'src/main/kotlin-native'
|
||||
kotlin.srcDir '../../endorsedLibraries/kliopt/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'
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlinVersion"
|
||||
}
|
||||
kotlin.srcDir 'src/main/kotlin-js'
|
||||
kotlin.srcDir '../../endorsedLibraries/kliopt/src/main/kotlin-js'
|
||||
}
|
||||
linuxMain { dependsOn nativeMain }
|
||||
windowsMain { dependsOn nativeMain }
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:UseExperimental(ExperimentalCli::class)
|
||||
import org.jetbrains.analyzer.sendGetRequest
|
||||
import org.jetbrains.analyzer.readFile
|
||||
import org.jetbrains.analyzer.SummaryBenchmarksReport
|
||||
@@ -103,108 +103,94 @@ fun parseNormalizeResults(results: String): Map<String, Map<String, Double>> {
|
||||
return parsedNormalizeResults
|
||||
}
|
||||
|
||||
// Prints text summary by users request.
|
||||
fun summaryAction(argParser: ArgParser) {
|
||||
val benchsReport = SummaryBenchmarksReport(getBenchmarkReport(argParser.get<String>("mainReport")!!, argParser.get<String>("user")))
|
||||
val results = mutableListOf<String>()
|
||||
val executionNormalize = argParser.get<String>("exec-normalize")?.let {
|
||||
parseNormalizeResults(getFileContent(it))
|
||||
}
|
||||
val compileNormalize = argParser.get<String>("compile-normalize")?.let {
|
||||
parseNormalizeResults(getFileContent(it))
|
||||
}
|
||||
val codesizeNormalize = argParser.get<String>("codesize-normalize")?.let {
|
||||
parseNormalizeResults(getFileContent(it))
|
||||
}
|
||||
results.apply {
|
||||
add(benchsReport.failedBenchmarks.size.toString())
|
||||
argParser.getAll<String>("exec-samples")?. let {
|
||||
val filter = if (it.first() == "all") null else it
|
||||
add(benchsReport.getResultsByMetric(BenchmarkResult.Metric.EXECUTION_TIME,
|
||||
argParser.get<String>("exec")!! == "geomean", filter, executionNormalize).joinToString(";"))
|
||||
}
|
||||
argParser.getAll<String>("compile-samples")?. let {
|
||||
val filter = if (it.first() == "all") null else it
|
||||
add(benchsReport.getResultsByMetric(BenchmarkResult.Metric.COMPILE_TIME,
|
||||
argParser.get<String>("compile")!! == "geomean", filter, compileNormalize).joinToString(";"))
|
||||
}
|
||||
argParser.getAll<String>("codesize-samples")?. let {
|
||||
val filter = if (it.first() == "all") null else it
|
||||
add(benchsReport.getResultsByMetric(BenchmarkResult.Metric.CODE_SIZE,
|
||||
argParser.get<String>("codesize")!! == "geomean", filter, codesizeNormalize).joinToString(";"))
|
||||
}
|
||||
}
|
||||
println(results.joinToString())
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
class Summary: Subcommand("summary") {
|
||||
val exec by option(ArgType.Choice(listOf("samples", "geomean")),
|
||||
description = "Execution time way of calculation", defaultValue = "geomean")
|
||||
val execSamples by options(ArgType.String, "exec-samples",
|
||||
description = "Samples used for execution time metric (value 'all' allows use all samples)",
|
||||
delimiter = ",")
|
||||
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 = ",")
|
||||
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 = ",")
|
||||
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")
|
||||
|
||||
val actions = mapOf( "summary" to Action(
|
||||
::summaryAction,
|
||||
ArgParser(
|
||||
listOf(
|
||||
OptionDescriptor(ArgType.Choice(listOf("samples", "geomean")), "exec",
|
||||
description = "Execution time way of calculation", defaultValue = "geomean"),
|
||||
OptionDescriptor(ArgType.String(), "exec-samples",
|
||||
description = "Samples used for execution time metric (value 'all' allows use all samples)",
|
||||
delimiter = ","),
|
||||
OptionDescriptor(ArgType.String(), "exec-normalize",
|
||||
description = "File with golden results which should be used for normalization"),
|
||||
OptionDescriptor(ArgType.Choice(listOf("samples", "geomean")), "compile",
|
||||
description = "Compile time way of calculation", defaultValue = "geomean"),
|
||||
OptionDescriptor(ArgType.String(), "compile-samples",
|
||||
description = "Samples used for compile time metric (value 'all' allows use all samples)",
|
||||
delimiter = ","),
|
||||
OptionDescriptor(ArgType.String(), "compile-normalize",
|
||||
description = "File with golden results which should be used for normalization"),
|
||||
OptionDescriptor(ArgType.Choice(listOf("samples", "geomean")), "codesize",
|
||||
description = "Code size way of calculation", defaultValue = "geomean"),
|
||||
OptionDescriptor(ArgType.String(), "codesize-samples",
|
||||
description = "Samples used for code size metric (value 'all' allows use all samples)",
|
||||
delimiter = ","),
|
||||
OptionDescriptor(ArgType.String(), "codesize-normalize",
|
||||
description = "File with golden results which should be used for normalization"),
|
||||
OptionDescriptor(ArgType.String(), "user", "u", "User access information for authorization")
|
||||
), listOf(ArgDescriptor(ArgType.String(), "mainReport", "Main report for analysis"))
|
||||
)
|
||||
)
|
||||
)
|
||||
override fun execute() {
|
||||
val benchsReport = SummaryBenchmarksReport(getBenchmarkReport(mainReport!!, user))
|
||||
val results = mutableListOf<String>()
|
||||
val executionNormalize = execNormalize?.let {
|
||||
parseNormalizeResults(getFileContent(it))
|
||||
}
|
||||
val compileNormalize = compileNormalize?.let {
|
||||
parseNormalizeResults(getFileContent(it))
|
||||
}
|
||||
val codesizeNormalize = codesizeNormalize?.let {
|
||||
parseNormalizeResults(getFileContent(it))
|
||||
}
|
||||
results.apply {
|
||||
add(benchsReport.failedBenchmarks.size.toString())
|
||||
|
||||
val options = listOf(
|
||||
OptionDescriptor(ArgType.String(), "output", "o", "Output file"),
|
||||
OptionDescriptor(ArgType.Double(), "eps", "e", "Meaningful performance changes", "1.0"),
|
||||
OptionDescriptor(ArgType.Boolean(), "short", "s", "Show short version of report", "false"),
|
||||
OptionDescriptor(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")),
|
||||
"renders", "r", "Renders for showing information", "text", isMultiple = true),
|
||||
OptionDescriptor(ArgType.String(), "user", "u", "User access information for authorization")
|
||||
)
|
||||
val filterExec = if (execSamples.first() == "all") null else execSamples
|
||||
add(benchsReport.getResultsByMetric(BenchmarkResult.Metric.EXECUTION_TIME,
|
||||
exec == "geomean", filterExec, executionNormalize).joinToString(";"))
|
||||
|
||||
val arguments = listOf(
|
||||
ArgDescriptor(ArgType.String(), "mainReport", "Main report for analysis"),
|
||||
ArgDescriptor(ArgType.String(), "compareToReport", "Report to compare to", isRequired = false)
|
||||
)
|
||||
val filterCompile = if (compileSamples.first() == "all") null else compileSamples
|
||||
add(benchsReport.getResultsByMetric(BenchmarkResult.Metric.COMPILE_TIME,
|
||||
compile == "geomean", filterCompile, compileNormalize).joinToString(";"))
|
||||
|
||||
// Parse args.
|
||||
val argParser = ArgParser(options, arguments, actions)
|
||||
if (argParser.parse(args)) {
|
||||
// Read contents of file.
|
||||
val mainBenchsReport = getBenchmarkReport(argParser.get<String>("mainReport")!!, argParser.get<String>("user"))
|
||||
var compareToBenchsReport = argParser.get<String>("compareToReport")?.let {
|
||||
getBenchmarkReport(it, argParser.get<String>("user"))
|
||||
val filterCodesize = if (codesizeSamples.first() == "all") null else codesizeSamples
|
||||
add(benchsReport.getResultsByMetric(BenchmarkResult.Metric.CODE_SIZE,
|
||||
codesize == "geomean", filterCodesize, codesizeNormalize).joinToString(";"))
|
||||
|
||||
}
|
||||
println(results.joinToString())
|
||||
}
|
||||
}
|
||||
val action = Summary()
|
||||
// Parse args.
|
||||
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 renders = argParser.getAll<String>("renders")
|
||||
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 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)
|
||||
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)
|
||||
var compareToBenchsReport = compareToReport?.let {
|
||||
getBenchmarkReport(it, user)
|
||||
}
|
||||
|
||||
// Generate comparasion report.
|
||||
val summaryReport = SummaryBenchmarksReport(mainBenchsReport,
|
||||
compareToBenchsReport,
|
||||
argParser.get<Double>("eps")!!)
|
||||
|
||||
var output = argParser.get<String>("output")
|
||||
epsValue)
|
||||
|
||||
var outputFile = output
|
||||
renders?.forEach {
|
||||
Render.getRenderByName(it).print(summaryReport, argParser.get<Boolean>("short")!!, output)
|
||||
output = null
|
||||
Render.getRenderByName(it).print(summaryReport, useShortForm, outputFile)
|
||||
outputFile = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.jetbrains.kliopt
|
||||
|
||||
// Possible types of arguments.
|
||||
sealed class ArgType(val hasParameter: kotlin.Boolean) {
|
||||
abstract val description: kotlin.String
|
||||
open fun check(value: kotlin.String, name: kotlin.String) {}
|
||||
class Boolean : ArgType(false) {
|
||||
override val description: kotlin.String
|
||||
get() = ""
|
||||
}
|
||||
class String : ArgType(true) {
|
||||
override val description: kotlin.String
|
||||
get() = "{ String }"
|
||||
}
|
||||
class Int : ArgType(true) {
|
||||
override val description: kotlin.String
|
||||
get() = "{ Int }"
|
||||
|
||||
override fun check(value: kotlin.String, name: kotlin.String) {
|
||||
value.toIntOrNull() ?: error("Option $name is expected to be integer number. $value is provided.")
|
||||
}
|
||||
}
|
||||
class Double : ArgType(true) {
|
||||
override val description: kotlin.String
|
||||
get() = "{ Double }"
|
||||
|
||||
override fun check(value: kotlin.String, name: kotlin.String) {
|
||||
value.toDoubleOrNull() ?: error("Option $name is expected to be double number. $value is provided.")
|
||||
}
|
||||
}
|
||||
class Choice(val values: List<kotlin.String>) : ArgType(true) {
|
||||
override val description: kotlin.String
|
||||
get() = "{ Value should be one of $values }"
|
||||
|
||||
override fun check(value: kotlin.String, name: kotlin.String) {
|
||||
if (value !in values) error("Option $name is expected to be one of $values. $value is provided.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Action(val callback: (parser: ArgParser) -> Unit, val parser: ArgParser)
|
||||
|
||||
// Common descriptor both for options and positional arguments.
|
||||
abstract class Descriptor(val type: ArgType,
|
||||
val longName: String,
|
||||
val description: String? = null,
|
||||
val defaultValue: String? = null,
|
||||
val isRequired: Boolean = false,
|
||||
val deprecatedWarning: String? = null) {
|
||||
abstract val textDescription: String
|
||||
abstract val helpMessage: String
|
||||
}
|
||||
|
||||
class OptionDescriptor(
|
||||
type: ArgType,
|
||||
longName: String,
|
||||
val shortName: String ? = null,
|
||||
description: String? = null,
|
||||
defaultValue: String? = null,
|
||||
isRequired: Boolean = false,
|
||||
val isMultiple: Boolean = false,
|
||||
val delimiter: String? = null,
|
||||
deprecatedWarning: String? = null) : Descriptor (type, longName, description, defaultValue, isRequired, deprecatedWarning) {
|
||||
override val textDescription: String
|
||||
get() = "option -$longName"
|
||||
|
||||
override val helpMessage: String
|
||||
get() {
|
||||
val result = StringBuilder()
|
||||
result.append(" -${longName}")
|
||||
shortName?.let { result.append(", -$it") }
|
||||
defaultValue?.let { result.append(" [$it]") }
|
||||
description?.let {result.append(" -> ${it}")}
|
||||
if (isRequired) result.append(" (always required)")
|
||||
result.append(" ${type.description}")
|
||||
deprecatedWarning?.let { result.append(" Warning: $it") }
|
||||
result.append("\n")
|
||||
return result.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class ArgDescriptor(
|
||||
type: ArgType,
|
||||
longName: String,
|
||||
description: String? = null,
|
||||
defaultValue: String? = null,
|
||||
isRequired: Boolean = true,
|
||||
deprecatedWarning: String? = null) : Descriptor (type, longName, description, defaultValue, isRequired, deprecatedWarning) {
|
||||
override val textDescription: String
|
||||
get() = "argument $longName"
|
||||
|
||||
override val helpMessage: String
|
||||
get() {
|
||||
val result = StringBuilder()
|
||||
result.append(" ${longName}")
|
||||
defaultValue?.let { result.append(" [$it]") }
|
||||
description?.let {result.append(" -> ${it}")}
|
||||
if (!isRequired) result.append(" (optional)")
|
||||
result.append(" ${type.description}")
|
||||
deprecatedWarning?.let { result.append(" Warning: $it") }
|
||||
result.append("\n")
|
||||
return result.toString()
|
||||
}
|
||||
}
|
||||
|
||||
// Arguments parser.
|
||||
class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescriptor> = listOf<ArgDescriptor>(),
|
||||
val actions: Map<String, Action> = emptyMap(), useDefaultHelpShortName: Boolean = true) {
|
||||
private val options = optionsList.union(if (useDefaultHelpShortName)
|
||||
listOf(OptionDescriptor(ArgType.Boolean(), "help", "h", "Usage info"))
|
||||
else
|
||||
listOf(OptionDescriptor(ArgType.Boolean(), "help", description = "Usage info"))
|
||||
).toList()
|
||||
private val arguments = argsList
|
||||
private lateinit var parsedValues: MutableMap<String, ParsedArg?>
|
||||
private lateinit var valuesOrigin: MutableMap<String, ValueOrigin?>
|
||||
|
||||
enum class ValueOrigin { SET_BY_USER, SET_DEFAULT_VALUE, UNSET }
|
||||
|
||||
inner class ParsedArg(val descriptor: Descriptor, val values: List<String>) {
|
||||
init {
|
||||
// Check correctness of initialization data.
|
||||
if (values.isEmpty()) {
|
||||
printError("Parsed value should be provided!")
|
||||
}
|
||||
}
|
||||
|
||||
// Get value of argument converted to expected type.
|
||||
private fun <T : Any> getTyped(value: String): T {
|
||||
val typedValue = when (descriptor.type) {
|
||||
is ArgType.Int -> value.toInt()
|
||||
is ArgType.Double -> value.toDouble()
|
||||
is ArgType.Boolean -> value == "true"
|
||||
else -> value
|
||||
} as? T
|
||||
typedValue ?: printError("Argument ${descriptor.longName} has type ${descriptor.type} which differs from expected!")
|
||||
return typedValue
|
||||
}
|
||||
|
||||
// Get value of argument.
|
||||
fun <T : Any> get(): T {
|
||||
return getTyped<T>(values[0])
|
||||
}
|
||||
|
||||
// Get all values of argument.
|
||||
// For options that can be set multiple types.
|
||||
fun <T : Any> getAll(): List<T> {
|
||||
return values.map { getTyped<T>(it) }
|
||||
}
|
||||
}
|
||||
|
||||
// Output error. Also adds help usage information for easy understanding of problem.
|
||||
fun printError(message: String): Nothing {
|
||||
error("$message\n${makeUsage()}")
|
||||
}
|
||||
|
||||
// Get origin of option value.
|
||||
fun getOrigin(name: String) = valuesOrigin[name] ?: printError("No option/argument $name in list of avaliable options")
|
||||
|
||||
private fun saveAsArg(argDescriptors: Map<String, ArgDescriptor>, arg: String, processedValues: Map<String, MutableList<String>>): Boolean {
|
||||
// Find uninitialized arguments.
|
||||
val nullArgs = argDescriptors.keys.filter { processedValues.getValue(it).isEmpty() }
|
||||
val name = nullArgs.firstOrNull()
|
||||
name?. let {
|
||||
argDescriptors.getValue(name).type.check(arg, name)
|
||||
argDescriptors.getValue(name).deprecatedWarning?.let { println ("Warning: $it") }
|
||||
processedValues.getValue(name).add(arg)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun saveAsOption(descriptor: OptionDescriptor, value: String, processedValues: Map<String, MutableList<String>>) {
|
||||
if (!descriptor.isMultiple && !processedValues.getValue(descriptor.longName).isEmpty()) {
|
||||
printError("Option ${descriptor.longName} is used more than one time!")
|
||||
}
|
||||
descriptor.deprecatedWarning?.let { if (processedValues.getValue(descriptor.longName).isEmpty()) println ("Warning: $it") }
|
||||
val savedValues = descriptor.delimiter?.let { value.split(it) } ?: listOf(value)
|
||||
|
||||
savedValues.forEach {
|
||||
descriptor.type.check(it, descriptor.longName)
|
||||
processedValues.getValue(descriptor.longName).add(it)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Parse arguments.
|
||||
// Returns true if all arguments were parsed, otherwise return false and print help message.
|
||||
fun parse(args: Array<String>): Boolean {
|
||||
var index = 0
|
||||
val optDescriptors = options.map { it.longName to it }.toMap()
|
||||
val shortNames = options.filter { it.shortName != null }.map { it.shortName!! to it.longName }.toMap()
|
||||
val argDescriptors = arguments.map { it.longName to it }.toMap()
|
||||
val descriptorsKeys = optDescriptors.keys.union(argDescriptors.keys).toList()
|
||||
val processedValues = descriptorsKeys.map { it to mutableListOf<String>() }.toMap().toMutableMap()
|
||||
parsedValues = descriptorsKeys.map { it to null }.toMap().toMutableMap()
|
||||
valuesOrigin = descriptorsKeys.map { it to ValueOrigin.UNSET }.toMap().toMutableMap()
|
||||
while (index < args.size) {
|
||||
val arg = args[index]
|
||||
// Check for actions.
|
||||
actions.forEach { (name, action) ->
|
||||
if (arg == name) {
|
||||
// Use parser for this action.
|
||||
val parseResult = action.parser.parse(args.slice(index + 1..args.size - 1).toTypedArray())
|
||||
if (parseResult)
|
||||
action.callback(action.parser)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (arg.startsWith('-')) {
|
||||
// Option is found.
|
||||
val name = shortNames[arg.substring(1)] ?: arg.substring(1)
|
||||
val descriptor = optDescriptors[name]
|
||||
descriptor?. let {
|
||||
if (descriptor.type.hasParameter) {
|
||||
if (index < args.size - 1) {
|
||||
saveAsOption(descriptor, args[index + 1], processedValues)
|
||||
index++
|
||||
} else {
|
||||
// An error, option with value without value.
|
||||
printError("No value for ${descriptor.textDescription}")
|
||||
}
|
||||
} else {
|
||||
if (name == "help") {
|
||||
println(makeUsage())
|
||||
return false
|
||||
}
|
||||
saveAsOption (descriptor, "true", processedValues)
|
||||
}
|
||||
} ?: run {
|
||||
// Try save as argument.
|
||||
if (!saveAsArg(argDescriptors, arg, processedValues)) {
|
||||
printError("Unknown option $arg")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Argument is found.
|
||||
if (!saveAsArg(argDescriptors, arg, processedValues)) {
|
||||
printError("Too many arguments! Couldn't proccess argument $arg!")
|
||||
}
|
||||
}
|
||||
index++
|
||||
}
|
||||
|
||||
processedValues.forEach { (key, value) ->
|
||||
val descriptor = optDescriptors[key] ?: argDescriptors.getValue(key)
|
||||
// Not inited, append default value if needed.
|
||||
if (value.isEmpty()) {
|
||||
descriptor.defaultValue?. let {
|
||||
parsedValues[key] = ParsedArg(descriptor, listOf(descriptor.defaultValue))
|
||||
valuesOrigin[key] = ValueOrigin.SET_DEFAULT_VALUE
|
||||
} ?: run {
|
||||
if (descriptor.isRequired) {
|
||||
printError("Please, provide value for ${descriptor.textDescription}. It should be always set")
|
||||
} else {
|
||||
parsedValues[key] = null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parsedValues[key] = ParsedArg(descriptor, value)
|
||||
valuesOrigin[key] = ValueOrigin.SET_BY_USER
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Get value of argument.
|
||||
fun <T : Any> get(name: String): T? {
|
||||
if (::parsedValues.isInitialized) {
|
||||
val arg = parsedValues[name]
|
||||
return arg?.get()
|
||||
} else {
|
||||
printError("Method parse() of ArgParser class should be called before getting arguments and options.")
|
||||
}
|
||||
}
|
||||
|
||||
// Get all values of argument.
|
||||
// For options that can be set multiple types.
|
||||
fun <T : Any> getAll(name: String): List<T>? {
|
||||
if (::parsedValues.isInitialized) {
|
||||
val arg = parsedValues[name]
|
||||
return arg?.getAll()
|
||||
} else {
|
||||
printError("Method parse() of ArgParser class should be called before getting arguments and options.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeUsage(): String {
|
||||
val result = StringBuilder()
|
||||
result.append("Usage: \n")
|
||||
if (!arguments.isEmpty()) {
|
||||
result.append("Arguments: \n")
|
||||
arguments.forEach {
|
||||
result.append(it.helpMessage)
|
||||
}
|
||||
}
|
||||
result.append("Options: \n")
|
||||
options.forEach {
|
||||
result.append(it.helpMessage)
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
}
|
||||
@@ -9,24 +9,19 @@ import org.jetbrains.kotlin.konan.target.PlatformManager
|
||||
import org.jetbrains.kotlin.konan.library.*
|
||||
import org.jetbrains.kotlin.konan.target.Distribution
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.interop
|
||||
import org.jetbrains.kliopt.ArgParser
|
||||
import org.jetbrains.kotlin.konan.CURRENT
|
||||
import org.jetbrains.kotlin.konan.KonanVersion
|
||||
import org.jetbrains.kotlin.library.toUnresolvedLibraries
|
||||
import org.jetbrains.kotlin.native.interop.tool.*
|
||||
|
||||
// TODO: this function should eventually be eliminated from 'utilities'.
|
||||
// The interaction of interop and the compler should be streamlined.
|
||||
// The interaction of interop and the compiler should be streamlined.
|
||||
|
||||
fun invokeInterop(flavor: String, args: Array<String>): Array<String>? {
|
||||
val argParser = ArgParser(if (flavor == "native") getCInteropArguments() else getJSInteropArguments(),
|
||||
useDefaultHelpShortName = false)
|
||||
if (!argParser.parse(args))
|
||||
return null
|
||||
val outputFileName = argParser.get<String>("output")!!
|
||||
val noDefaultLibs = argParser.get<Boolean>(NODEFAULTLIBS)!!
|
||||
val purgeUserLibs = argParser.get<Boolean>(PURGE_USER_LIBS)!!
|
||||
val temporaryFilesDir = argParser.get<String>(TEMP_DIR)
|
||||
fun invokeInterop(flavor: String, args: Array<String>): Array<String> {
|
||||
val arguments = if (flavor == "native") CInteropArguments() else JSInteropArguments()
|
||||
arguments.argParser.parse(args)
|
||||
val outputFileName = arguments.output
|
||||
val noDefaultLibs = arguments.nodefaultlibs
|
||||
val purgeUserLibs = arguments.purgeUserLibs
|
||||
val temporaryFilesDir = arguments.tempDir
|
||||
|
||||
val buildDir = File("$outputFileName-build")
|
||||
val generatedDir = File(buildDir, "kotlin")
|
||||
@@ -40,9 +35,10 @@ fun invokeInterop(flavor: String, args: Array<String>): Array<String>? {
|
||||
val additionalProperties = mutableMapOf<String, Any>(
|
||||
"manifest" to manifest.path)
|
||||
val cstubsName ="cstubs"
|
||||
val libraries = argParser.getAll<String>("library") ?: listOf<String>()
|
||||
val repos = argParser.getAll<String>("repo") ?: listOf<String>()
|
||||
val targetRequest = argParser.get<String>("target")!!
|
||||
val libraries = arguments.library
|
||||
val repos = arguments.repo
|
||||
val targetRequest = if (arguments is CInteropArguments) arguments.target
|
||||
else (arguments as JSInteropArguments).target
|
||||
val target = PlatformManager().targetManager(targetRequest).target
|
||||
|
||||
if (flavor == "native") {
|
||||
@@ -66,7 +62,7 @@ fun invokeInterop(flavor: String, args: Array<String>): Array<String>? {
|
||||
additionalProperties.putAll(mapOf("cstubsname" to cstubsName, "import" to imports))
|
||||
}
|
||||
|
||||
val cinteropArgsToCompiler = interop(flavor, args + additionalArgs, additionalProperties) ?: return null
|
||||
val cinteropArgsToCompiler = interop(flavor, args + additionalArgs, additionalProperties)
|
||||
|
||||
val nativeStubs =
|
||||
if (flavor == "wasm")
|
||||
|
||||
Reference in New Issue
Block a user