Rewrite interop tools command line options to have help messages without crashes (#2672)
This commit is contained in:
@@ -38,6 +38,9 @@ dependencies {
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
sourceSets {
|
||||
main.kotlin.srcDirs += "$rootDir/tools/kliopt"
|
||||
}
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = ['-Xuse-experimental=kotlin.ExperimentalUnsignedTypes']
|
||||
}
|
||||
|
||||
+5
-3
@@ -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<String>) {
|
||||
@@ -38,12 +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 libraries = defFiles.associateWith {
|
||||
buildNativeLibrary(
|
||||
tool,
|
||||
DefFile(it, tool.substitutions),
|
||||
CInteropArguments(),
|
||||
argParser,
|
||||
ImportsImpl(emptyMap())
|
||||
).getHeaderPaths()
|
||||
}
|
||||
|
||||
+48
-84
@@ -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 = "<flavor>", 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 = "<fully qualified name>", description = "place generated bindings to the package")
|
||||
var pkg: String? = null
|
||||
|
||||
@Argument(value = "-generated", valueDescription = "<dir>", description = "place generated bindings to the directory")
|
||||
var generated: String? = null
|
||||
|
||||
@Argument(value = "-libraryPath", valueDescription = "<dir>", description = "add a library search path")
|
||||
var libraryPath: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = "-manifest", valueDescription = "<file>", description = "library manifest addend")
|
||||
var manifest: String? = null
|
||||
|
||||
@Argument(value = "-natives", valueDescription = "<directory>", description = "where to put the built native files")
|
||||
var natives: String? = null
|
||||
|
||||
@Argument(value = "-staticLibrary", valueDescription = "<file>", description = "embed static library to the result")
|
||||
var staticLibrary: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = "-temporaryFilesDir", valueDescription = "<dir>", description = "Save temporary files to the given directory")
|
||||
var temporaryFilesDir: String? = null
|
||||
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", "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 = "<imports>", description = "a semicolon separated list of headers, prepended with the package name")
|
||||
var import: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = "-target", valueDescription = "<target>", description = "native target to compile to")
|
||||
var target: String? = null
|
||||
|
||||
@Argument(value = "-def", valueDescription = "<file>", 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 = "<file>", description = "header file to produce kotlin bindings for")
|
||||
var header: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, shortName = "-hfasp", valueDescription = "<file>", description = "header file to produce kotlin bindings for")
|
||||
var headerFilterPrefix: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = "-compilerOpts", shortName = "-copt", valueDescription = "<arg>", description = "additional compiler options", delimiter = " ")
|
||||
var compilerOpts: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = "-linkerOpts", shortName = "-lopt", valueDescription = "<arg>", description = "additional linker options", delimiter = " ")
|
||||
var linkerOpts: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = "-shims", description = "wrap bindings by a tracing layer")
|
||||
var shims: Boolean = false
|
||||
|
||||
@Argument(value = "-linker", valueDescription = "<file>", description = "use specified linker")
|
||||
|
||||
var linker: String? = null
|
||||
@Argument(value = "-cstubsname", valueDescription = "<name>", description = "provide a name for the generated c stubs file")
|
||||
var cstubsname: String? = null
|
||||
}
|
||||
|
||||
const val HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX = "-headerFilterAdditionalSearchPrefix"
|
||||
|
||||
fun <T: CommonToolArguments> parseCommandLine(args: Array<String>, 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<String>(propertyName) ?: listOf<String>()).toTypedArray()
|
||||
|
||||
+35
-51
@@ -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<String>) {
|
||||
processCLib(args)
|
||||
}
|
||||
|
||||
fun interop(flavor: String, args: Array<String>) = when(flavor) {
|
||||
"jvm", "native" -> processCLib(args)
|
||||
"wasm" -> processIdlLib(args)
|
||||
else -> error("Unexpected flavor")
|
||||
}
|
||||
fun interop(flavor: String, args: Array<String>, additionalArgs: Map<String, Any> = 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_file_for_lib>.def
|
||||
Following flags are supported:
|
||||
-def <file>.def specifies library definition file
|
||||
-compilerOpts <c compiler flags> specifies flags passed to clang
|
||||
-linkerOpts <linker flags> specifies flags passed to linker
|
||||
-verbose <boolean> increases verbosity
|
||||
-shims <boolean> adds generation of shims tracing native library calls
|
||||
-pkg <fully qualified package name> place the resulting definitions into the package
|
||||
-h <file>.h header files to parse
|
||||
-o <file>.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<Path>, globs: List<String>): Map<Path,
|
||||
}
|
||||
|
||||
|
||||
private fun processCLib(args: Array<String>): Array<String>? {
|
||||
|
||||
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<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")
|
||||
val flavor = KotlinPlatform.values().single { it.name.equals(flavorName, ignoreCase = true) }
|
||||
val defFile = argParser.get<String>("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!")
|
||||
}
|
||||
|
||||
val tool = prepareTool(arguments.target, flavor)
|
||||
val tool = prepareTool(argParser.get<String>("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<Boolean>("shims")!!
|
||||
val verbose = argParser.get<Boolean>("verbose")!!
|
||||
|
||||
val language = selectNativeLanguage(def.config)
|
||||
|
||||
@@ -204,14 +188,14 @@ private fun processCLib(args: Array<String>): Array<String>? {
|
||||
def.config.linkerOpts.toTypedArray() +
|
||||
tool.defaultCompilerOpts +
|
||||
additionalLinkerOpts
|
||||
val linkerName = arguments.linker ?: def.config.linker
|
||||
val linkerName = argParser.get<String>("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<String>("pkg") ?: def.config.packageName)?.let {
|
||||
it.split('.')
|
||||
} ?: defFile!!.name.split('.').reversed().drop(1)
|
||||
|
||||
@@ -221,13 +205,13 @@ private fun processCLib(args: Array<String>): Array<String>? {
|
||||
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<String>("Xtemporary-files-dir"))
|
||||
|
||||
val imports = parseImports(arguments.import)
|
||||
val imports = parseImports((additionalArgs["import"] as? List<String>)?.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 -> {
|
||||
|
||||
+13
-11
@@ -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<Interface>): String =
|
||||
const val idlMathPackage = "kotlinx.interop.wasm.math"
|
||||
const val idlDomPackage = "kotlinx.interop.wasm.dom"
|
||||
|
||||
fun processIdlLib(args: Array<String>): Array<String> {
|
||||
val arguments = parseCommandLine(args, CommonInteropArguments())
|
||||
fun processIdlLib(args: Array<String>, additionalArgs: Map<String, Any> = mapOf()): Array<String>? {
|
||||
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<String>("generated")!!).mkdirs()
|
||||
val nativeLibsDir = File(argParser.get<String>("natives")!!).mkdirs()
|
||||
|
||||
val idl = when (arguments.pkg) {
|
||||
val idl = when (argParser.get<String>("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<String>("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"))
|
||||
}
|
||||
|
||||
@@ -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')) {
|
||||
|
||||
@@ -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<OptionDescriptor>, argsList: List<ArgDescriptor> = listOf<ArgDescriptor>()) {
|
||||
private val options = optionsList.union(listOf(OptionDescriptor(ArgType.Boolean(), "help",
|
||||
"h", "Usage info")))
|
||||
.toList()
|
||||
class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescriptor> = listOf<ArgDescriptor>(),
|
||||
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?>
|
||||
|
||||
@@ -153,7 +162,7 @@ class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescripto
|
||||
}
|
||||
|
||||
// Output error. Also adds help usage information for easy understanding of problem.
|
||||
private fun printError(message: String): Nothing {
|
||||
fun printError(message: String): Nothing {
|
||||
error("$message\n${makeUsage()}")
|
||||
}
|
||||
|
||||
@@ -163,6 +172,7 @@ class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescripto
|
||||
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
|
||||
}
|
||||
@@ -173,8 +183,14 @@ class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescripto
|
||||
if (!descriptor.isMultiple && !processedValues.getValue(descriptor.longName).isEmpty()) {
|
||||
printError("Option ${descriptor.longName} is used more than one time!")
|
||||
}
|
||||
descriptor.type.check(value, descriptor.longName)
|
||||
processedValues.getValue(descriptor.longName).add(value)
|
||||
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.
|
||||
@@ -218,7 +234,7 @@ class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescripto
|
||||
} else {
|
||||
// Argument is found.
|
||||
if (!saveAsArg(argDescriptors, arg, processedValues)) {
|
||||
printError("Too many arguments!")
|
||||
printError("Too many arguments! Couldn't proccess argument $arg!")
|
||||
}
|
||||
}
|
||||
index++
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ open class CInteropTask @Inject constructor(val settings: CInteropSettingsImpl):
|
||||
addArgIfNotNull("-def", defFile.canonicalPath)
|
||||
addArgIfNotNull("-pkg", packageName)
|
||||
|
||||
addFileArgs("-h", headers)
|
||||
addFileArgs("-header", headers)
|
||||
|
||||
compilerOpts.forEach {
|
||||
addArg("-copt", it)
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ open class KonanInteropTask @Inject constructor(val workerExecutor: WorkerExecut
|
||||
addArgIfNotNull("-def", defFile.canonicalPath)
|
||||
addArgIfNotNull("-pkg", packageName)
|
||||
|
||||
addFileArgs("-h", headers)
|
||||
addFileArgs("-header", headers)
|
||||
|
||||
compilerOpts.forEach {
|
||||
addArg("-copt", it)
|
||||
|
||||
@@ -6,106 +6,87 @@ package org.jetbrains.kotlin.cli.utilities
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.target.PlatformManager
|
||||
import org.jetbrains.kotlin.konan.KonanAbiVersion
|
||||
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.kotlin.utils.addIfNotNull
|
||||
|
||||
private const val NODEFAULTLIBS = "-nodefaultlibs"
|
||||
private const val PURGE_USER_LIBS = "-Xpurge-user-libs"
|
||||
import org.jetbrains.kliopt.ArgParser
|
||||
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.
|
||||
|
||||
fun invokeInterop(flavor: String, args: Array<String>): Array<String> {
|
||||
val cinteropArgFilter = listOf(NODEFAULTLIBS, PURGE_USER_LIBS)
|
||||
|
||||
var outputFileName = "nativelib"
|
||||
var targetRequest = "host"
|
||||
val libraries = mutableListOf<String>()
|
||||
val repos = mutableListOf<String>()
|
||||
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<String>): Array<String>? {
|
||||
val argParser = ArgParser(if (flavor == "native") getCInteropArguments() else getCommonInteropArguments(),
|
||||
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)
|
||||
|
||||
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<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>()
|
||||
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<String>("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
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ fun main(args: Array<String>) {
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user