From 75805b3528435738d93f75feeef70ce39d64e0e2 Mon Sep 17 00:00:00 2001 From: LepilkinaElena Date: Wed, 20 Feb 2019 09:14:28 +0300 Subject: [PATCH] Rewrite interop tools command line options to have help messages without crashes (#2672) --- Interop/StubGenerator/build.gradle | 3 + .../native/interop/gen/defFileDependencies.kt | 8 +- .../native/interop/gen/jvm/CommandLine.kt | 132 +++++++----------- .../kotlin/native/interop/gen/jvm/main.kt | 86 +++++------- .../native/interop/gen/wasm/StubGenerator.kt | 24 ++-- .../kotlin/NativeInteropPlugin.groovy | 4 +- tools/kliopt/org/jetbrains/kliopt/KliOpt.kt | 40 ++++-- .../plugin/experimental/tasks/CInteropTask.kt | 2 +- .../plugin/konan/tasks/KonanInteropTask.kt | 2 +- .../kotlin/cli/utilities/InteropCompiler.kt | 117 +++++++--------- .../jetbrains/kotlin/cli/utilities/main.kt | 4 +- 11 files changed, 187 insertions(+), 235 deletions(-) diff --git a/Interop/StubGenerator/build.gradle b/Interop/StubGenerator/build.gradle index b73417847a8..0ed363c5d95 100644 --- a/Interop/StubGenerator/build.gradle +++ b/Interop/StubGenerator/build.gradle @@ -38,6 +38,9 @@ dependencies { } compileKotlin { + sourceSets { + main.kotlin.srcDirs += "$rootDir/tools/kliopt" + } kotlinOptions { freeCompilerArgs = ['-Xuse-experimental=kotlin.ExperimentalUnsignedTypes'] } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/defFileDependencies.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/defFileDependencies.kt index 4c8b1f8b166..45bd6a47a29 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/defFileDependencies.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/defFileDependencies.kt @@ -6,7 +6,8 @@ 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.CInteropArguments +import org.jetbrains.kotlin.native.interop.tool.getCInteropArguments +import org.jetbrains.kliopt.ArgParser import java.io.File fun defFileDependencies(args: Array) { @@ -38,12 +39,13 @@ private fun makeDependencyAssigner(targets: List, defFiles: List) private fun makeDependencyAssignerForTarget(target: String, defFiles: List): SingleTargetDependencyAssigner { val tool = prepareTool(target, KotlinPlatform.NATIVE) - + val argParser = ArgParser(getCInteropArguments(), useDefaultHelpShortName = false) + argParser.parse(arrayOf()) val libraries = defFiles.associateWith { buildNativeLibrary( tool, DefFile(it, tool.substitutions), - CInteropArguments(), + argParser, ImportsImpl(emptyMap()) ).getHeaderPaths() } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt index c8790853235..1bec642f53e 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt @@ -16,97 +16,61 @@ package org.jetbrains.kotlin.native.interop.tool -import org.jetbrains.kotlin.cli.common.arguments.* +import org.jetbrains.kliopt.* + +const val HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX = "headerFilterAdditionalSearchPrefix" +const val NODEFAULTLIBS = "nodefaultlibs" +const val PURGE_USER_LIBS = "Xpurge-user-libs" +const val TEMP_DIR = "Xtemporary-files-dir" // TODO: unify camel and snake cases. // Possible solution is to accept both cases -open class CommonInteropArguments : CommonToolArguments() { - @Argument(value = "-flavor", valueDescription = "", description = "One of: jvm, native or wasm") - var flavor: String? = null +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", + description = "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") + ) - @Argument(value = "-pkg", valueDescription = "", description = "place generated bindings to the package") - var pkg: String? = null - - @Argument(value = "-generated", valueDescription = "", description = "place generated bindings to the directory") - var generated: String? = null - - @Argument(value = "-libraryPath", valueDescription = "", description = "add a library search path") - var libraryPath: Array = arrayOf() - - @Argument(value = "-manifest", valueDescription = "", description = "library manifest addend") - var manifest: String? = null - - @Argument(value = "-natives", valueDescription = "", description = "where to put the built native files") - var natives: String? = null - - @Argument(value = "-staticLibrary", valueDescription = "", description = "embed static library to the result") - var staticLibrary: Array = arrayOf() - - @Argument(value = "-temporaryFilesDir", valueDescription = "", description = "Save temporary files to the given directory") - var temporaryFilesDir: String? = null +fun getCInteropArguments(): List { + 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", "h", "header file to produce kotlin bindings for", + isMultiple = true, delimiter = ",", deprecatedWarning = "Short form -h of option -header is deprecated"), + OptionDescriptor(ArgType.String(), HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, "hfasp", + "header file to produce kotlin bindings for", isMultiple = true, delimiter = ","), + OptionDescriptor(ArgType.String(), "compilerOpts", "copt", + "additional compiler options", isMultiple = true, delimiter = " "), + OptionDescriptor(ArgType.String(), "linkerOpts", "lopt", + "additional linker options", isMultiple = true, delimiter = " "), + OptionDescriptor(ArgType.Boolean(), "shims", description = "wrap bindings by a tracing layer", defaultValue = "false"), + OptionDescriptor(ArgType.String(), "linker", description = "use specified linker") + ) + return (options + getCommonInteropArguments()) } -class CInteropArguments : CommonInteropArguments() { - @Argument(value = "-import", valueDescription = "", description = "a semicolon separated list of headers, prepended with the package name") - var import: Array = arrayOf() - - @Argument(value = "-target", valueDescription = "", description = "native target to compile to") - var target: String? = null - - @Argument(value = "-def", valueDescription = "", description = "the library definition file") - var def: String? = null - - // TODO: the short -h for -header conflicts with -h for -help. - // The -header currently wins, but need to make it a little more sound. - @Argument(value = "-header", shortName = "-h", valueDescription = "", description = "header file to produce kotlin bindings for") - var header: Array = arrayOf() - - @Argument(value = HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, shortName = "-hfasp", valueDescription = "", description = "header file to produce kotlin bindings for") - var headerFilterPrefix: Array = arrayOf() - - @Argument(value = "-compilerOpts", shortName = "-copt", valueDescription = "", description = "additional compiler options", delimiter = " ") - var compilerOpts: Array = arrayOf() - - @Argument(value = "-linkerOpts", shortName = "-lopt", valueDescription = "", description = "additional linker options", delimiter = " ") - var linkerOpts: Array = arrayOf() - - @Argument(value = "-shims", description = "wrap bindings by a tracing layer") - var shims: Boolean = false - - @Argument(value = "-linker", valueDescription = "", description = "use specified linker") - - var linker: String? = null - @Argument(value = "-cstubsname", valueDescription = "", description = "provide a name for the generated c stubs file") - var cstubsname: String? = null -} - -const val HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX = "-headerFilterAdditionalSearchPrefix" - -fun parseCommandLine(args: Array, arguments: T): T { - parseCommandLineArguments(args.asList(), arguments) - arguments.errors?.let { reportArgumentParseProblems(it) } - return arguments -} - -// Integrate with CLITool from the big Kotlin and get rid of the mess below. - internal fun warn(msg: String) { println("warning: $msg") } -// This is a copy of CLITool.kt's function adapted to work without a collector. -private fun reportArgumentParseProblems(errors: ArgumentParseErrors) { - for (flag in errors.unknownExtraFlags) { - warn("Flag is not supported by this version of the compiler: $flag") - } - for (argument in errors.extraArgumentsPassedInObsoleteForm) { - warn("Advanced option value is passed in an obsolete form. Please use the '=' character " + - "to specify the value: $argument=...") - } - for ((key, value) in errors.duplicateArguments) { - warn("Argument $key is passed multiple times. Only the last value will be used: $value") - } - for ((deprecatedName, newName) in errors.deprecatedArguments) { - warn("Argument $deprecatedName is deprecated. Please use $newName instead") - } -} +fun ArgParser.getValuesAsArray(propertyName: String) = + (getAll(propertyName) ?: listOf()).toTypedArray() diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt index 42e9466cb59..a3480429318 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.native.interop.gen.* import org.jetbrains.kotlin.native.interop.gen.wasm.processIdlLib import org.jetbrains.kotlin.native.interop.indexer.* import org.jetbrains.kotlin.native.interop.tool.* +import org.jetbrains.kliopt.ArgParser import java.io.File import java.lang.IllegalArgumentException import java.nio.file.* @@ -32,11 +33,12 @@ fun main(args: Array) { processCLib(args) } -fun interop(flavor: String, args: Array) = when(flavor) { - "jvm", "native" -> processCLib(args) - "wasm" -> processIdlLib(args) - else -> error("Unexpected flavor") - } +fun interop(flavor: String, args: Array, additionalArgs: Map = mapOf()) = + when(flavor) { + "jvm", "native" -> processCLib(args, additionalArgs) + "wasm" -> processIdlLib(args, additionalArgs) + else -> error("Unexpected flavor") + } // Options, whose values are space-separated and can be escaped. val escapedOptions = setOf("-compilerOpts", "-linkerOpts") @@ -80,23 +82,6 @@ private fun Properties.putAndRunOnReplace(key: Any, newValue: Any, beforeReplace this[key] = newValue } -// TODO: Utilize Usage from the big Kotlin. -// That requires to extend the CLITool class. -private fun usage() { - println(""" -Run interop tool with -def .def -Following flags are supported: - -def .def specifies library definition file - -compilerOpts specifies flags passed to clang - -linkerOpts specifies flags passed to linker - -verbose increases verbosity - -shims adds generation of shims tracing native library calls - -pkg place the resulting definitions into the package - -h .h header files to parse - -o .klib specifies the resulting library file -""") -} - private fun selectNativeLanguage(config: DefFile.DefFileConfig): Language { val languages = mapOf( "C" to Language.C, @@ -173,29 +158,28 @@ private fun findFilesByGlobs(roots: List, globs: List): Map): Array? { - - val arguments = parseCommandLine(args, CInteropArguments()) - val userDir = System.getProperty("user.dir") - val ktGenRoot = arguments.generated ?: userDir - val nativeLibsDir = arguments.natives ?: userDir - val flavorName = arguments.flavor ?: "jvm" - val flavor = KotlinPlatform.values().single { it.name.equals(flavorName, ignoreCase = true) } - val defFile = arguments.def?.let { File(it) } - val manifestAddend = arguments.manifest?.let { File(it) } - - if (defFile == null && arguments.pkg == null) { - usage() +private fun processCLib(args: Array, additionalArgs: Map = mapOf()): Array? { + val argParser = ArgParser(getCInteropArguments(), useDefaultHelpShortName = false) + if (!argParser.parse(args)) return null + val ktGenRoot = argParser.get("generated") + val nativeLibsDir = argParser.get("natives") + val flavorName = argParser.get("flavor") + val flavor = KotlinPlatform.values().single { it.name.equals(flavorName, ignoreCase = true) } + val defFile = argParser.get("def")?.let { File(it) } + val manifestAddend = (additionalArgs["manifest"] as? String)?.let { File(it) } + + if (defFile == null && argParser.get("pkg") == null) { + argParser.printError("-def or -pkg should provided!") } - val tool = prepareTool(arguments.target, flavor) + val tool = prepareTool(argParser.get("target"), flavor) val def = DefFile(defFile, tool.substitutions) - val additionalLinkerOpts = arguments.linkerOpts - val generateShims = arguments.shims - val verbose = arguments.verbose + val additionalLinkerOpts = argParser.getValuesAsArray("linkerOpts") + val generateShims = argParser.get("shims")!! + val verbose = argParser.get("verbose")!! val language = selectNativeLanguage(def.config) @@ -204,14 +188,14 @@ private fun processCLib(args: Array): Array? { def.config.linkerOpts.toTypedArray() + tool.defaultCompilerOpts + additionalLinkerOpts - val linkerName = arguments.linker ?: def.config.linker + val linkerName = argParser.get("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 + arguments.staticLibrary - val libraryPaths = def.config.libraryPaths + arguments.libraryPath - val fqParts = (arguments.pkg ?: def.config.packageName)?.let { + val staticLibraries = def.config.staticLibraries + argParser.getValuesAsArray("staticLibrary") + val libraryPaths = def.config.libraryPaths + argParser.getValuesAsArray("libraryPath") + val fqParts = (argParser.get("pkg") ?: def.config.packageName)?.let { it.split('.') } ?: defFile!!.name.split('.').reversed().drop(1) @@ -221,13 +205,13 @@ private fun processCLib(args: Array): Array? { val outKtFileRelative = (fqParts + outKtFileName).joinToString("/") val outKtFile = File(ktGenRoot, outKtFileRelative) - val libName = arguments.cstubsname ?: fqParts.joinToString("") + "stubs" + val libName = (additionalArgs["cstubsname"] as? String)?: fqParts.joinToString("") + "stubs" - val tempFiles = TempFiles(libName, arguments.temporaryFilesDir) + val tempFiles = TempFiles(libName, argParser.get("Xtemporary-files-dir")) - val imports = parseImports(arguments.import) + val imports = parseImports((additionalArgs["import"] as? List)?.toTypedArray() ?: arrayOf()) - val library = buildNativeLibrary(tool, def, arguments, imports) + val library = buildNativeLibrary(tool, def, argParser, imports) val configuration = InteropConfiguration( library = library, @@ -311,11 +295,11 @@ internal fun prepareTool(target: String?, flavor: KotlinPlatform): ToolConfig { internal fun buildNativeLibrary( tool: ToolConfig, def: DefFile, - arguments: CInteropArguments, + arguments: ArgParser, imports: ImportsImpl ): NativeLibrary { - val additionalHeaders = arguments.header - val additionalCompilerOpts = arguments.compilerOpts + val additionalHeaders = arguments.getValuesAsArray("header") + val additionalCompilerOpts = arguments.getValuesAsArray("compilerOpts") val headerFiles = def.config.headers + additionalHeaders val language = selectNativeLanguage(def.config) @@ -323,7 +307,7 @@ internal fun buildNativeLibrary( addAll(def.config.compilerOpts) addAll(tool.defaultCompilerOpts) addAll(additionalCompilerOpts) - addAll(getCompilerFlagsForVfsOverlay(arguments.headerFilterPrefix, def)) + addAll(getCompilerFlagsForVfsOverlay(arguments.getValuesAsArray("headerFilterPrefix"), def)) addAll(when (language) { Language.C -> emptyList() Language.OBJECTIVE_C -> { diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/wasm/StubGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/wasm/StubGenerator.kt index 68b9870f209..b7daa0ee7bf 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/wasm/StubGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/wasm/StubGenerator.kt @@ -3,8 +3,9 @@ package org.jetbrains.kotlin.native.interop.gen.wasm import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.native.interop.gen.argsToCompiler import org.jetbrains.kotlin.native.interop.gen.wasm.idl.* -import org.jetbrains.kotlin.native.interop.tool.CommonInteropArguments -import org.jetbrains.kotlin.native.interop.tool.parseCommandLine +import org.jetbrains.kliopt.ArgParser +import org.jetbrains.kotlin.native.interop.tool.getCommonInteropArguments +import org.jetbrains.kotlin.native.interop.tool.getValuesAsArray fun kotlinHeader(packageName: String): String { return "package $packageName\n" + @@ -391,21 +392,22 @@ fun generateJs(interfaces: List): String = const val idlMathPackage = "kotlinx.interop.wasm.math" const val idlDomPackage = "kotlinx.interop.wasm.dom" -fun processIdlLib(args: Array): Array { - val arguments = parseCommandLine(args, CommonInteropArguments()) +fun processIdlLib(args: Array, additionalArgs: Map = mapOf()): Array? { + val argParser = ArgParser(getCommonInteropArguments(), useDefaultHelpShortName = false) + if (!argParser.parse(args)) + return null // TODO: Refactor me. - val userDir = System.getProperty("user.dir") - val ktGenRoot = File(arguments.generated ?: userDir).mkdirs() - val nativeLibsDir = File(arguments.natives ?: userDir).mkdirs() + val ktGenRoot = File(argParser.get("generated")!!).mkdirs() + val nativeLibsDir = File(argParser.get("natives")!!).mkdirs() - val idl = when (arguments.pkg) { + val idl = when (argParser.get("pkg")) { idlMathPackage-> idlMath idlDomPackage -> idlDom else -> throw IllegalArgumentException("Please choose either $idlMathPackage or $idlDomPackage for -pkg argument") } - File(ktGenRoot, "kotlin_stubs.kt").writeText(generateKotlin(arguments.pkg!!, idl)) + File(ktGenRoot, "kotlin_stubs.kt").writeText(generateKotlin(argParser.get("pkg")!!, idl)) File(nativeLibsDir, "js_stubs.js").writeText(generateJs(idl)) - File(arguments.manifest!!).writeText("") // The manifest is currently unused for wasm. - return argsToCompiler(arguments.staticLibrary, arguments.libraryPath) + File((additionalArgs["manifest"] as? String)!!).writeText("") // The manifest is currently unused for wasm. + return argsToCompiler(argParser.getValuesAsArray("staticLibrary"), argParser.getValuesAsArray("libraryPath")) } diff --git a/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy b/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy index 354e21a3c77..13b287b3748 100644 --- a/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy +++ b/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy @@ -222,7 +222,7 @@ class NamedNativeInteropConfig implements Named { args '-generated', generatedSrcDir args '-natives', nativeLibsDir - args '-temporaryFilesDir', temporaryFilesDir + args '-Xtemporary-files-dir', temporaryFilesDir args '-flavor', this.flavor // Uncomment to debug. // args '-verbose', 'true' @@ -251,7 +251,7 @@ class NamedNativeInteropConfig implements Named { args linkerOpts.collectMany { ['-lopt', it] } headers.each { - args '-h', it + args '-header', it } if (project.hasProperty('shims')) { diff --git a/tools/kliopt/org/jetbrains/kliopt/KliOpt.kt b/tools/kliopt/org/jetbrains/kliopt/KliOpt.kt index a4eb381d997..8ef0e2c1039 100644 --- a/tools/kliopt/org/jetbrains/kliopt/KliOpt.kt +++ b/tools/kliopt/org/jetbrains/kliopt/KliOpt.kt @@ -60,7 +60,8 @@ abstract class Descriptor(val type: ArgType, val longName: String, val description: String? = null, val defaultValue: String? = null, - val isRequired: Boolean = false) { + val isRequired: Boolean = false, + val deprecatedWarning: String? = null) { abstract val textDescription: String abstract val helpMessage: String } @@ -72,7 +73,9 @@ class OptionDescriptor( description: String? = null, defaultValue: String? = null, isRequired: Boolean = false, - val isMultiple: Boolean = false) : Descriptor (type, longName, description, defaultValue, isRequired) { + 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" @@ -83,8 +86,9 @@ class OptionDescriptor( shortName?.let { result.append(", -$it") } defaultValue?.let { result.append(" [$it]") } description?.let {result.append(" -> ${it}")} - if (!isRequired) result.append(" (optional)") + if (isRequired) result.append(" (always required)") result.append(" ${type.description}") + deprecatedWarning?.let { result.append(" Warning: $it") } result.append("\n") return result.toString() } @@ -95,7 +99,8 @@ class ArgDescriptor( longName: String, description: String? = null, defaultValue: String? = null, - isRequired: Boolean = true) : Descriptor (type, longName, description, defaultValue, isRequired) { + isRequired: Boolean = true, + deprecatedWarning: String? = null) : Descriptor (type, longName, description, defaultValue, isRequired, deprecatedWarning) { override val textDescription: String get() = "argument $longName" @@ -107,16 +112,20 @@ class ArgDescriptor( 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, argsList: List = listOf()) { - private val options = optionsList.union(listOf(OptionDescriptor(ArgType.Boolean(), "help", - "h", "Usage info"))) - .toList() +class ArgParser(optionsList: List, argsList: List = listOf(), + 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 @@ -153,7 +162,7 @@ class ArgParser(optionsList: List, argsList: List, argsList: List, argsList: List, argsList: List): Array { - val cinteropArgFilter = listOf(NODEFAULTLIBS, PURGE_USER_LIBS) - - var outputFileName = "nativelib" - var targetRequest = "host" - val libraries = mutableListOf() - val repos = mutableListOf() - var noDefaultLibs = false - var purgeUserLibs = false - var temporaryFilesDir = "" - for (i in args.indices) { - val arg = args[i] - val nextArg = args.getOrNull(i + 1) - if (arg.startsWith("-o")) - outputFileName = nextArg ?: outputFileName - if (arg == "-target") - targetRequest = nextArg ?: targetRequest - if (arg == "-library") - libraries.addIfNotNull(nextArg) - if (arg == "-r" || arg == "-repo") - repos.addIfNotNull(nextArg) - if (arg == NODEFAULTLIBS) - noDefaultLibs = true - if (arg == PURGE_USER_LIBS) - purgeUserLibs = true - if (arg == "-Xtemporary-files-dir") - temporaryFilesDir = nextArg ?: "" - } +// The interaction of interop and the compler should be streamlined. +fun invokeInterop(flavor: String, args: Array): Array? { + val argParser = ArgParser(if (flavor == "native") getCInteropArguments() else getCommonInteropArguments(), + useDefaultHelpShortName = false) + if (!argParser.parse(args)) + return null + val outputFileName = argParser.get("output")!! + val noDefaultLibs = argParser.get(NODEFAULTLIBS)!! + val purgeUserLibs = argParser.get(PURGE_USER_LIBS)!! + val temporaryFilesDir = argParser.get(TEMP_DIR) val buildDir = File("$outputFileName-build") val generatedDir = File(buildDir, "kotlin") val nativesDir = File(buildDir, "natives") - val cstubsName ="cstubs" val manifest = File(buildDir, "manifest.properties") + val additionalArgs = listOf( + "-generated", generatedDir.path, + "-natives", nativesDir.path, + "-flavor", flavor + ) + val additionalProperties = mutableMapOf( + "manifest" to manifest.path) + val cstubsName ="cstubs" + val libraries = argParser.getAll("library") ?: listOf() + val repos = argParser.getAll("repo") ?: listOf() + var targetName = "wasm32" - val target = PlatformManager().targetManager(targetRequest).target - val resolver = defaultResolver( - repos, - libraries.filter { it.contains(File.separator) }, - target, - Distribution() - ).libraryResolver() - val allLibraries = resolver.resolveWithDependencies( - libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs - ).getFullList() + if (flavor == "native") { + val targetRequest = argParser.get("target")!! + val target = PlatformManager().targetManager(targetRequest).target + targetName = target.visibleName + val resolver = defaultResolver( + repos, + libraries.filter { it.contains(File.separator) }, + target, + Distribution() + ).libraryResolver() + val allLibraries = resolver.resolveWithDependencies( + libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs + ).getFullList() - val importArgs = allLibraries.flatMap { library -> - // TODO: handle missing properties? - library.packageFqName?.let { packageFqName -> - val headerIds = library.includedHeaders - val arg = "$packageFqName:${headerIds.joinToString(";")}" - listOf("-import", arg) - } ?: emptyList() + val imports = allLibraries.map { library -> + // TODO: handle missing properties? + library.packageFqName?.let { packageFqName -> + val headerIds = library.includedHeaders + "$packageFqName:${headerIds.joinToString(";")}" + } + }.filterNotNull() + additionalProperties.putAll(mapOf("cstubsname" to cstubsName, "import" to imports)) } - val additionalArgs = listOf( - "-generated", generatedDir.path, - "-natives", nativesDir.path, - "-cstubsname", cstubsName, - "-manifest", manifest.path, - "-flavor", flavor, - "-temporaryFilesDir", temporaryFilesDir - ) + importArgs - - val cinteropArgs = (additionalArgs + args.filter { it !in cinteropArgFilter }).toTypedArray() - - val cinteropArgsToCompiler = interop(flavor, cinteropArgs) ?: emptyArray() + val cinteropArgsToCompiler = interop(flavor, args + additionalArgs, additionalProperties) ?: return null val nativeStubs = if (flavor == "wasm") arrayOf("-include-binary", File(nativesDir, "js_stubs.js").path) else - arrayOf("-native-library",File(nativesDir, "$cstubsName.bc").path) + arrayOf("-native-library", File(nativesDir, "$cstubsName.bc").path) val konancArgs = arrayOf( generatedDir.path, "-produce", "library", "-o", outputFileName, - "-target", target.visibleName, + "-target", targetName, "-manifest", manifest.path, "-Xtemporary-files-dir=$temporaryFilesDir") + nativeStubs + cinteropArgsToCompiler + libraries.flatMap { listOf("-library", it) } + repos.flatMap { listOf("-repo", it) } + - (if (noDefaultLibs) arrayOf(NODEFAULTLIBS) else emptyArray()) + - (if (purgeUserLibs) arrayOf(PURGE_USER_LIBS) else emptyArray()) + (if (noDefaultLibs) arrayOf("-$NODEFAULTLIBS") else emptyArray()) + + (if (purgeUserLibs) arrayOf("-$PURGE_USER_LIBS") else emptyArray()) return konancArgs } diff --git a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt index aa6a4cdd3ef..0e27cc33ba7 100644 --- a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt +++ b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt @@ -16,11 +16,11 @@ fun main(args: Array) { konancMain(utilityArgs) "cinterop" -> { val konancArgs = invokeInterop("native", utilityArgs) - konancMain(konancArgs) + konancArgs?.let { konancMain(it) } } "jsinterop" -> { val konancArgs = invokeInterop("wasm", utilityArgs) - konancMain(konancArgs) + konancArgs?.let { konancMain(it) } } "klib" -> klibMain(utilityArgs)