Rewrite interop tools command line options to have help messages without crashes (#2672)
This commit is contained in:
@@ -38,6 +38,9 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
|
sourceSets {
|
||||||
|
main.kotlin.srcDirs += "$rootDir/tools/kliopt"
|
||||||
|
}
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
freeCompilerArgs = ['-Xuse-experimental=kotlin.ExperimentalUnsignedTypes']
|
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.gen.jvm.prepareTool
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.NativeLibraryHeaders
|
import org.jetbrains.kotlin.native.interop.indexer.NativeLibraryHeaders
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.getHeaderPaths
|
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
|
import java.io.File
|
||||||
|
|
||||||
fun defFileDependencies(args: Array<String>) {
|
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 {
|
private fun makeDependencyAssignerForTarget(target: String, defFiles: List<File>): SingleTargetDependencyAssigner {
|
||||||
val tool = prepareTool(target, KotlinPlatform.NATIVE)
|
val tool = prepareTool(target, KotlinPlatform.NATIVE)
|
||||||
|
val argParser = ArgParser(getCInteropArguments(), useDefaultHelpShortName = false)
|
||||||
|
argParser.parse(arrayOf<String>())
|
||||||
val libraries = defFiles.associateWith {
|
val libraries = defFiles.associateWith {
|
||||||
buildNativeLibrary(
|
buildNativeLibrary(
|
||||||
tool,
|
tool,
|
||||||
DefFile(it, tool.substitutions),
|
DefFile(it, tool.substitutions),
|
||||||
CInteropArguments(),
|
argParser,
|
||||||
ImportsImpl(emptyMap())
|
ImportsImpl(emptyMap())
|
||||||
).getHeaderPaths()
|
).getHeaderPaths()
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-84
@@ -16,97 +16,61 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.interop.tool
|
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.
|
// TODO: unify camel and snake cases.
|
||||||
// Possible solution is to accept both cases
|
// Possible solution is to accept both cases
|
||||||
open class CommonInteropArguments : CommonToolArguments() {
|
fun getCommonInteropArguments() = listOf(
|
||||||
@Argument(value = "-flavor", valueDescription = "<flavor>", description = "One of: jvm, native or wasm")
|
OptionDescriptor(ArgType.Boolean(), "verbose", description = "Enable verbose logging output", defaultValue = "false"),
|
||||||
var flavor: String? = null
|
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")
|
fun getCInteropArguments(): List<OptionDescriptor> {
|
||||||
var pkg: String? = null
|
val options = listOf(
|
||||||
|
OptionDescriptor(ArgType.String(), "target", description = "native target to compile to", defaultValue = "host"),
|
||||||
@Argument(value = "-generated", valueDescription = "<dir>", description = "place generated bindings to the directory")
|
OptionDescriptor(ArgType.String(), "def", description = "the library definition file"),
|
||||||
var generated: String? = null
|
OptionDescriptor(ArgType.String(), "header", "h", "header file to produce kotlin bindings for",
|
||||||
|
isMultiple = true, delimiter = ",", deprecatedWarning = "Short form -h of option -header is deprecated"),
|
||||||
@Argument(value = "-libraryPath", valueDescription = "<dir>", description = "add a library search path")
|
OptionDescriptor(ArgType.String(), HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, "hfasp",
|
||||||
var libraryPath: Array<String> = arrayOf()
|
"header file to produce kotlin bindings for", isMultiple = true, delimiter = ","),
|
||||||
|
OptionDescriptor(ArgType.String(), "compilerOpts", "copt",
|
||||||
@Argument(value = "-manifest", valueDescription = "<file>", description = "library manifest addend")
|
"additional compiler options", isMultiple = true, delimiter = " "),
|
||||||
var manifest: String? = null
|
OptionDescriptor(ArgType.String(), "linkerOpts", "lopt",
|
||||||
|
"additional linker options", isMultiple = true, delimiter = " "),
|
||||||
@Argument(value = "-natives", valueDescription = "<directory>", description = "where to put the built native files")
|
OptionDescriptor(ArgType.Boolean(), "shims", description = "wrap bindings by a tracing layer", defaultValue = "false"),
|
||||||
var natives: String? = null
|
OptionDescriptor(ArgType.String(), "linker", description = "use specified linker")
|
||||||
|
)
|
||||||
@Argument(value = "-staticLibrary", valueDescription = "<file>", description = "embed static library to the result")
|
return (options + getCommonInteropArguments())
|
||||||
var staticLibrary: Array<String> = arrayOf()
|
|
||||||
|
|
||||||
@Argument(value = "-temporaryFilesDir", valueDescription = "<dir>", description = "Save temporary files to the given directory")
|
|
||||||
var temporaryFilesDir: String? = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
internal fun warn(msg: String) {
|
||||||
println("warning: $msg")
|
println("warning: $msg")
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a copy of CLITool.kt's function adapted to work without a collector.
|
fun ArgParser.getValuesAsArray(propertyName: String) =
|
||||||
private fun reportArgumentParseProblems(errors: ArgumentParseErrors) {
|
(getAll<String>(propertyName) ?: listOf<String>()).toTypedArray()
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+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.gen.wasm.processIdlLib
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||||
import org.jetbrains.kotlin.native.interop.tool.*
|
import org.jetbrains.kotlin.native.interop.tool.*
|
||||||
|
import org.jetbrains.kliopt.ArgParser
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
import java.nio.file.*
|
import java.nio.file.*
|
||||||
@@ -32,11 +33,12 @@ fun main(args: Array<String>) {
|
|||||||
processCLib(args)
|
processCLib(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun interop(flavor: String, args: Array<String>) = when(flavor) {
|
fun interop(flavor: String, args: Array<String>, additionalArgs: Map<String, Any> = mapOf()) =
|
||||||
"jvm", "native" -> processCLib(args)
|
when(flavor) {
|
||||||
"wasm" -> processIdlLib(args)
|
"jvm", "native" -> processCLib(args, additionalArgs)
|
||||||
else -> error("Unexpected flavor")
|
"wasm" -> processIdlLib(args, additionalArgs)
|
||||||
}
|
else -> error("Unexpected flavor")
|
||||||
|
}
|
||||||
|
|
||||||
// Options, whose values are space-separated and can be escaped.
|
// Options, whose values are space-separated and can be escaped.
|
||||||
val escapedOptions = setOf("-compilerOpts", "-linkerOpts")
|
val escapedOptions = setOf("-compilerOpts", "-linkerOpts")
|
||||||
@@ -80,23 +82,6 @@ private fun Properties.putAndRunOnReplace(key: Any, newValue: Any, beforeReplace
|
|||||||
this[key] = newValue
|
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 {
|
private fun selectNativeLanguage(config: DefFile.DefFileConfig): Language {
|
||||||
val languages = mapOf(
|
val languages = mapOf(
|
||||||
"C" to Language.C,
|
"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>? {
|
private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> = mapOf()): Array<String>? {
|
||||||
|
val argParser = ArgParser(getCInteropArguments(), useDefaultHelpShortName = false)
|
||||||
val arguments = parseCommandLine(args, CInteropArguments())
|
if (!argParser.parse(args))
|
||||||
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()
|
|
||||||
return null
|
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 def = DefFile(defFile, tool.substitutions)
|
||||||
|
|
||||||
val additionalLinkerOpts = arguments.linkerOpts
|
val additionalLinkerOpts = argParser.getValuesAsArray("linkerOpts")
|
||||||
val generateShims = arguments.shims
|
val generateShims = argParser.get<Boolean>("shims")!!
|
||||||
val verbose = arguments.verbose
|
val verbose = argParser.get<Boolean>("verbose")!!
|
||||||
|
|
||||||
val language = selectNativeLanguage(def.config)
|
val language = selectNativeLanguage(def.config)
|
||||||
|
|
||||||
@@ -204,14 +188,14 @@ private fun processCLib(args: Array<String>): Array<String>? {
|
|||||||
def.config.linkerOpts.toTypedArray() +
|
def.config.linkerOpts.toTypedArray() +
|
||||||
tool.defaultCompilerOpts +
|
tool.defaultCompilerOpts +
|
||||||
additionalLinkerOpts
|
additionalLinkerOpts
|
||||||
val linkerName = arguments.linker ?: def.config.linker
|
val linkerName = argParser.get<String>("linker") ?: def.config.linker
|
||||||
val linker = "${tool.llvmHome}/bin/$linkerName"
|
val linker = "${tool.llvmHome}/bin/$linkerName"
|
||||||
val compiler = "${tool.llvmHome}/bin/clang"
|
val compiler = "${tool.llvmHome}/bin/clang"
|
||||||
val excludedFunctions = def.config.excludedFunctions.toSet()
|
val excludedFunctions = def.config.excludedFunctions.toSet()
|
||||||
val excludedMacros = def.config.excludedMacros.toSet()
|
val excludedMacros = def.config.excludedMacros.toSet()
|
||||||
val staticLibraries = def.config.staticLibraries + arguments.staticLibrary
|
val staticLibraries = def.config.staticLibraries + argParser.getValuesAsArray("staticLibrary")
|
||||||
val libraryPaths = def.config.libraryPaths + arguments.libraryPath
|
val libraryPaths = def.config.libraryPaths + argParser.getValuesAsArray("libraryPath")
|
||||||
val fqParts = (arguments.pkg ?: def.config.packageName)?.let {
|
val fqParts = (argParser.get<String>("pkg") ?: def.config.packageName)?.let {
|
||||||
it.split('.')
|
it.split('.')
|
||||||
} ?: defFile!!.name.split('.').reversed().drop(1)
|
} ?: defFile!!.name.split('.').reversed().drop(1)
|
||||||
|
|
||||||
@@ -221,13 +205,13 @@ private fun processCLib(args: Array<String>): Array<String>? {
|
|||||||
val outKtFileRelative = (fqParts + outKtFileName).joinToString("/")
|
val outKtFileRelative = (fqParts + outKtFileName).joinToString("/")
|
||||||
val outKtFile = File(ktGenRoot, outKtFileRelative)
|
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(
|
val configuration = InteropConfiguration(
|
||||||
library = library,
|
library = library,
|
||||||
@@ -311,11 +295,11 @@ internal fun prepareTool(target: String?, flavor: KotlinPlatform): ToolConfig {
|
|||||||
internal fun buildNativeLibrary(
|
internal fun buildNativeLibrary(
|
||||||
tool: ToolConfig,
|
tool: ToolConfig,
|
||||||
def: DefFile,
|
def: DefFile,
|
||||||
arguments: CInteropArguments,
|
arguments: ArgParser,
|
||||||
imports: ImportsImpl
|
imports: ImportsImpl
|
||||||
): NativeLibrary {
|
): NativeLibrary {
|
||||||
val additionalHeaders = arguments.header
|
val additionalHeaders = arguments.getValuesAsArray("header")
|
||||||
val additionalCompilerOpts = arguments.compilerOpts
|
val additionalCompilerOpts = arguments.getValuesAsArray("compilerOpts")
|
||||||
|
|
||||||
val headerFiles = def.config.headers + additionalHeaders
|
val headerFiles = def.config.headers + additionalHeaders
|
||||||
val language = selectNativeLanguage(def.config)
|
val language = selectNativeLanguage(def.config)
|
||||||
@@ -323,7 +307,7 @@ internal fun buildNativeLibrary(
|
|||||||
addAll(def.config.compilerOpts)
|
addAll(def.config.compilerOpts)
|
||||||
addAll(tool.defaultCompilerOpts)
|
addAll(tool.defaultCompilerOpts)
|
||||||
addAll(additionalCompilerOpts)
|
addAll(additionalCompilerOpts)
|
||||||
addAll(getCompilerFlagsForVfsOverlay(arguments.headerFilterPrefix, def))
|
addAll(getCompilerFlagsForVfsOverlay(arguments.getValuesAsArray("headerFilterPrefix"), def))
|
||||||
addAll(when (language) {
|
addAll(when (language) {
|
||||||
Language.C -> emptyList()
|
Language.C -> emptyList()
|
||||||
Language.OBJECTIVE_C -> {
|
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.konan.file.File
|
||||||
import org.jetbrains.kotlin.native.interop.gen.argsToCompiler
|
import org.jetbrains.kotlin.native.interop.gen.argsToCompiler
|
||||||
import org.jetbrains.kotlin.native.interop.gen.wasm.idl.*
|
import org.jetbrains.kotlin.native.interop.gen.wasm.idl.*
|
||||||
import org.jetbrains.kotlin.native.interop.tool.CommonInteropArguments
|
import org.jetbrains.kliopt.ArgParser
|
||||||
import org.jetbrains.kotlin.native.interop.tool.parseCommandLine
|
import org.jetbrains.kotlin.native.interop.tool.getCommonInteropArguments
|
||||||
|
import org.jetbrains.kotlin.native.interop.tool.getValuesAsArray
|
||||||
|
|
||||||
fun kotlinHeader(packageName: String): String {
|
fun kotlinHeader(packageName: String): String {
|
||||||
return "package $packageName\n" +
|
return "package $packageName\n" +
|
||||||
@@ -391,21 +392,22 @@ fun generateJs(interfaces: List<Interface>): String =
|
|||||||
const val idlMathPackage = "kotlinx.interop.wasm.math"
|
const val idlMathPackage = "kotlinx.interop.wasm.math"
|
||||||
const val idlDomPackage = "kotlinx.interop.wasm.dom"
|
const val idlDomPackage = "kotlinx.interop.wasm.dom"
|
||||||
|
|
||||||
fun processIdlLib(args: Array<String>): Array<String> {
|
fun processIdlLib(args: Array<String>, additionalArgs: Map<String, Any> = mapOf()): Array<String>? {
|
||||||
val arguments = parseCommandLine(args, CommonInteropArguments())
|
val argParser = ArgParser(getCommonInteropArguments(), useDefaultHelpShortName = false)
|
||||||
|
if (!argParser.parse(args))
|
||||||
|
return null
|
||||||
// TODO: Refactor me.
|
// TODO: Refactor me.
|
||||||
val userDir = System.getProperty("user.dir")
|
val ktGenRoot = File(argParser.get<String>("generated")!!).mkdirs()
|
||||||
val ktGenRoot = File(arguments.generated ?: userDir).mkdirs()
|
val nativeLibsDir = File(argParser.get<String>("natives")!!).mkdirs()
|
||||||
val nativeLibsDir = File(arguments.natives ?: userDir).mkdirs()
|
|
||||||
|
|
||||||
val idl = when (arguments.pkg) {
|
val idl = when (argParser.get<String>("pkg")) {
|
||||||
idlMathPackage-> idlMath
|
idlMathPackage-> idlMath
|
||||||
idlDomPackage -> idlDom
|
idlDomPackage -> idlDom
|
||||||
else -> throw IllegalArgumentException("Please choose either $idlMathPackage or $idlDomPackage for -pkg argument")
|
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(nativeLibsDir, "js_stubs.js").writeText(generateJs(idl))
|
||||||
File(arguments.manifest!!).writeText("") // The manifest is currently unused for wasm.
|
File((additionalArgs["manifest"] as? String)!!).writeText("") // The manifest is currently unused for wasm.
|
||||||
return argsToCompiler(arguments.staticLibrary, arguments.libraryPath)
|
return argsToCompiler(argParser.getValuesAsArray("staticLibrary"), argParser.getValuesAsArray("libraryPath"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ class NamedNativeInteropConfig implements Named {
|
|||||||
|
|
||||||
args '-generated', generatedSrcDir
|
args '-generated', generatedSrcDir
|
||||||
args '-natives', nativeLibsDir
|
args '-natives', nativeLibsDir
|
||||||
args '-temporaryFilesDir', temporaryFilesDir
|
args '-Xtemporary-files-dir', temporaryFilesDir
|
||||||
args '-flavor', this.flavor
|
args '-flavor', this.flavor
|
||||||
// Uncomment to debug.
|
// Uncomment to debug.
|
||||||
// args '-verbose', 'true'
|
// args '-verbose', 'true'
|
||||||
@@ -251,7 +251,7 @@ class NamedNativeInteropConfig implements Named {
|
|||||||
args linkerOpts.collectMany { ['-lopt', it] }
|
args linkerOpts.collectMany { ['-lopt', it] }
|
||||||
|
|
||||||
headers.each {
|
headers.each {
|
||||||
args '-h', it
|
args '-header', it
|
||||||
}
|
}
|
||||||
|
|
||||||
if (project.hasProperty('shims')) {
|
if (project.hasProperty('shims')) {
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ abstract class Descriptor(val type: ArgType,
|
|||||||
val longName: String,
|
val longName: String,
|
||||||
val description: String? = null,
|
val description: String? = null,
|
||||||
val defaultValue: String? = null,
|
val defaultValue: String? = null,
|
||||||
val isRequired: Boolean = false) {
|
val isRequired: Boolean = false,
|
||||||
|
val deprecatedWarning: String? = null) {
|
||||||
abstract val textDescription: String
|
abstract val textDescription: String
|
||||||
abstract val helpMessage: String
|
abstract val helpMessage: String
|
||||||
}
|
}
|
||||||
@@ -72,7 +73,9 @@ class OptionDescriptor(
|
|||||||
description: String? = null,
|
description: String? = null,
|
||||||
defaultValue: String? = null,
|
defaultValue: String? = null,
|
||||||
isRequired: Boolean = false,
|
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
|
override val textDescription: String
|
||||||
get() = "option -$longName"
|
get() = "option -$longName"
|
||||||
|
|
||||||
@@ -83,8 +86,9 @@ class OptionDescriptor(
|
|||||||
shortName?.let { result.append(", -$it") }
|
shortName?.let { result.append(", -$it") }
|
||||||
defaultValue?.let { result.append(" [$it]") }
|
defaultValue?.let { result.append(" [$it]") }
|
||||||
description?.let {result.append(" -> ${it}")}
|
description?.let {result.append(" -> ${it}")}
|
||||||
if (!isRequired) result.append(" (optional)")
|
if (isRequired) result.append(" (always required)")
|
||||||
result.append(" ${type.description}")
|
result.append(" ${type.description}")
|
||||||
|
deprecatedWarning?.let { result.append(" Warning: $it") }
|
||||||
result.append("\n")
|
result.append("\n")
|
||||||
return result.toString()
|
return result.toString()
|
||||||
}
|
}
|
||||||
@@ -95,7 +99,8 @@ class ArgDescriptor(
|
|||||||
longName: String,
|
longName: String,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
defaultValue: 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
|
override val textDescription: String
|
||||||
get() = "argument $longName"
|
get() = "argument $longName"
|
||||||
|
|
||||||
@@ -107,16 +112,20 @@ class ArgDescriptor(
|
|||||||
description?.let {result.append(" -> ${it}")}
|
description?.let {result.append(" -> ${it}")}
|
||||||
if (!isRequired) result.append(" (optional)")
|
if (!isRequired) result.append(" (optional)")
|
||||||
result.append(" ${type.description}")
|
result.append(" ${type.description}")
|
||||||
|
deprecatedWarning?.let { result.append(" Warning: $it") }
|
||||||
result.append("\n")
|
result.append("\n")
|
||||||
return result.toString()
|
return result.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arguments parser.
|
// Arguments parser.
|
||||||
class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescriptor> = listOf<ArgDescriptor>()) {
|
class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescriptor> = listOf<ArgDescriptor>(),
|
||||||
private val options = optionsList.union(listOf(OptionDescriptor(ArgType.Boolean(), "help",
|
useDefaultHelpShortName: Boolean = true) {
|
||||||
"h", "Usage info")))
|
private val options = optionsList.union(if (useDefaultHelpShortName)
|
||||||
.toList()
|
listOf(OptionDescriptor(ArgType.Boolean(), "help", "h", "Usage info"))
|
||||||
|
else
|
||||||
|
listOf(OptionDescriptor(ArgType.Boolean(), "help", description = "Usage info"))
|
||||||
|
).toList()
|
||||||
private val arguments = argsList
|
private val arguments = argsList
|
||||||
private lateinit var parsedValues: MutableMap<String, ParsedArg?>
|
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.
|
// 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()}")
|
error("$message\n${makeUsage()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +172,7 @@ class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescripto
|
|||||||
val name = nullArgs.firstOrNull()
|
val name = nullArgs.firstOrNull()
|
||||||
name?. let {
|
name?. let {
|
||||||
argDescriptors.getValue(name).type.check(arg, name)
|
argDescriptors.getValue(name).type.check(arg, name)
|
||||||
|
argDescriptors.getValue(name).deprecatedWarning?.let { println ("Warning: $it") }
|
||||||
processedValues.getValue(name).add(arg)
|
processedValues.getValue(name).add(arg)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -173,8 +183,14 @@ class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescripto
|
|||||||
if (!descriptor.isMultiple && !processedValues.getValue(descriptor.longName).isEmpty()) {
|
if (!descriptor.isMultiple && !processedValues.getValue(descriptor.longName).isEmpty()) {
|
||||||
printError("Option ${descriptor.longName} is used more than one time!")
|
printError("Option ${descriptor.longName} is used more than one time!")
|
||||||
}
|
}
|
||||||
descriptor.type.check(value, descriptor.longName)
|
descriptor.deprecatedWarning?.let { if (processedValues.getValue(descriptor.longName).isEmpty()) println ("Warning: $it") }
|
||||||
processedValues.getValue(descriptor.longName).add(value)
|
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.
|
// Parse arguments.
|
||||||
@@ -218,7 +234,7 @@ class ArgParser(optionsList: List<OptionDescriptor>, argsList: List<ArgDescripto
|
|||||||
} else {
|
} else {
|
||||||
// Argument is found.
|
// Argument is found.
|
||||||
if (!saveAsArg(argDescriptors, arg, processedValues)) {
|
if (!saveAsArg(argDescriptors, arg, processedValues)) {
|
||||||
printError("Too many arguments!")
|
printError("Too many arguments! Couldn't proccess argument $arg!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index++
|
index++
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ open class CInteropTask @Inject constructor(val settings: CInteropSettingsImpl):
|
|||||||
addArgIfNotNull("-def", defFile.canonicalPath)
|
addArgIfNotNull("-def", defFile.canonicalPath)
|
||||||
addArgIfNotNull("-pkg", packageName)
|
addArgIfNotNull("-pkg", packageName)
|
||||||
|
|
||||||
addFileArgs("-h", headers)
|
addFileArgs("-header", headers)
|
||||||
|
|
||||||
compilerOpts.forEach {
|
compilerOpts.forEach {
|
||||||
addArg("-copt", it)
|
addArg("-copt", it)
|
||||||
|
|||||||
+1
-1
@@ -80,7 +80,7 @@ open class KonanInteropTask @Inject constructor(val workerExecutor: WorkerExecut
|
|||||||
addArgIfNotNull("-def", defFile.canonicalPath)
|
addArgIfNotNull("-def", defFile.canonicalPath)
|
||||||
addArgIfNotNull("-pkg", packageName)
|
addArgIfNotNull("-pkg", packageName)
|
||||||
|
|
||||||
addFileArgs("-h", headers)
|
addFileArgs("-header", headers)
|
||||||
|
|
||||||
compilerOpts.forEach {
|
compilerOpts.forEach {
|
||||||
addArg("-copt", it)
|
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.file.File
|
||||||
import org.jetbrains.kotlin.konan.target.PlatformManager
|
import org.jetbrains.kotlin.konan.target.PlatformManager
|
||||||
import org.jetbrains.kotlin.konan.KonanAbiVersion
|
|
||||||
import org.jetbrains.kotlin.konan.library.*
|
import org.jetbrains.kotlin.konan.library.*
|
||||||
import org.jetbrains.kotlin.konan.target.Distribution
|
import org.jetbrains.kotlin.konan.target.Distribution
|
||||||
import org.jetbrains.kotlin.native.interop.gen.jvm.interop
|
import org.jetbrains.kotlin.native.interop.gen.jvm.interop
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
import org.jetbrains.kliopt.ArgParser
|
||||||
private const val NODEFAULTLIBS = "-nodefaultlibs"
|
import org.jetbrains.kotlin.native.interop.tool.*
|
||||||
private const val PURGE_USER_LIBS = "-Xpurge-user-libs"
|
|
||||||
|
|
||||||
// TODO: this function should eventually be eliminated from 'utilities'.
|
// 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 compler should be streamlined.
|
||||||
|
|
||||||
fun invokeInterop(flavor: String, args: Array<String>): Array<String> {
|
fun invokeInterop(flavor: String, args: Array<String>): Array<String>? {
|
||||||
val cinteropArgFilter = listOf(NODEFAULTLIBS, PURGE_USER_LIBS)
|
val argParser = ArgParser(if (flavor == "native") getCInteropArguments() else getCommonInteropArguments(),
|
||||||
|
useDefaultHelpShortName = false)
|
||||||
var outputFileName = "nativelib"
|
if (!argParser.parse(args))
|
||||||
var targetRequest = "host"
|
return null
|
||||||
val libraries = mutableListOf<String>()
|
val outputFileName = argParser.get<String>("output")!!
|
||||||
val repos = mutableListOf<String>()
|
val noDefaultLibs = argParser.get<Boolean>(NODEFAULTLIBS)!!
|
||||||
var noDefaultLibs = false
|
val purgeUserLibs = argParser.get<Boolean>(PURGE_USER_LIBS)!!
|
||||||
var purgeUserLibs = false
|
val temporaryFilesDir = argParser.get<String>(TEMP_DIR)
|
||||||
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 ?: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val buildDir = File("$outputFileName-build")
|
val buildDir = File("$outputFileName-build")
|
||||||
val generatedDir = File(buildDir, "kotlin")
|
val generatedDir = File(buildDir, "kotlin")
|
||||||
val nativesDir = File(buildDir, "natives")
|
val nativesDir = File(buildDir, "natives")
|
||||||
val cstubsName ="cstubs"
|
|
||||||
val manifest = File(buildDir, "manifest.properties")
|
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
|
if (flavor == "native") {
|
||||||
val resolver = defaultResolver(
|
val targetRequest = argParser.get<String>("target")!!
|
||||||
repos,
|
val target = PlatformManager().targetManager(targetRequest).target
|
||||||
libraries.filter { it.contains(File.separator) },
|
targetName = target.visibleName
|
||||||
target,
|
val resolver = defaultResolver(
|
||||||
Distribution()
|
repos,
|
||||||
).libraryResolver()
|
libraries.filter { it.contains(File.separator) },
|
||||||
val allLibraries = resolver.resolveWithDependencies(
|
target,
|
||||||
libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs
|
Distribution()
|
||||||
).getFullList()
|
).libraryResolver()
|
||||||
|
val allLibraries = resolver.resolveWithDependencies(
|
||||||
|
libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs
|
||||||
|
).getFullList()
|
||||||
|
|
||||||
val importArgs = allLibraries.flatMap { library ->
|
val imports = allLibraries.map { library ->
|
||||||
// TODO: handle missing properties?
|
// TODO: handle missing properties?
|
||||||
library.packageFqName?.let { packageFqName ->
|
library.packageFqName?.let { packageFqName ->
|
||||||
val headerIds = library.includedHeaders
|
val headerIds = library.includedHeaders
|
||||||
val arg = "$packageFqName:${headerIds.joinToString(";")}"
|
"$packageFqName:${headerIds.joinToString(";")}"
|
||||||
listOf("-import", arg)
|
}
|
||||||
} ?: emptyList()
|
}.filterNotNull()
|
||||||
|
additionalProperties.putAll(mapOf("cstubsname" to cstubsName, "import" to imports))
|
||||||
}
|
}
|
||||||
|
|
||||||
val additionalArgs = listOf(
|
val cinteropArgsToCompiler = interop(flavor, args + additionalArgs, additionalProperties) ?: return null
|
||||||
"-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 nativeStubs =
|
val nativeStubs =
|
||||||
if (flavor == "wasm")
|
if (flavor == "wasm")
|
||||||
arrayOf("-include-binary", File(nativesDir, "js_stubs.js").path)
|
arrayOf("-include-binary", File(nativesDir, "js_stubs.js").path)
|
||||||
else
|
else
|
||||||
arrayOf("-native-library",File(nativesDir, "$cstubsName.bc").path)
|
arrayOf("-native-library", File(nativesDir, "$cstubsName.bc").path)
|
||||||
|
|
||||||
val konancArgs = arrayOf(
|
val konancArgs = arrayOf(
|
||||||
generatedDir.path,
|
generatedDir.path,
|
||||||
"-produce", "library",
|
"-produce", "library",
|
||||||
"-o", outputFileName,
|
"-o", outputFileName,
|
||||||
"-target", target.visibleName,
|
"-target", targetName,
|
||||||
"-manifest", manifest.path,
|
"-manifest", manifest.path,
|
||||||
"-Xtemporary-files-dir=$temporaryFilesDir") +
|
"-Xtemporary-files-dir=$temporaryFilesDir") +
|
||||||
nativeStubs +
|
nativeStubs +
|
||||||
cinteropArgsToCompiler +
|
cinteropArgsToCompiler +
|
||||||
libraries.flatMap { listOf("-library", it) } +
|
libraries.flatMap { listOf("-library", it) } +
|
||||||
repos.flatMap { listOf("-repo", it) } +
|
repos.flatMap { listOf("-repo", it) } +
|
||||||
(if (noDefaultLibs) arrayOf(NODEFAULTLIBS) else emptyArray()) +
|
(if (noDefaultLibs) arrayOf("-$NODEFAULTLIBS") else emptyArray()) +
|
||||||
(if (purgeUserLibs) arrayOf(PURGE_USER_LIBS) else emptyArray())
|
(if (purgeUserLibs) arrayOf("-$PURGE_USER_LIBS") else emptyArray())
|
||||||
|
|
||||||
return konancArgs
|
return konancArgs
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ fun main(args: Array<String>) {
|
|||||||
konancMain(utilityArgs)
|
konancMain(utilityArgs)
|
||||||
"cinterop" -> {
|
"cinterop" -> {
|
||||||
val konancArgs = invokeInterop("native", utilityArgs)
|
val konancArgs = invokeInterop("native", utilityArgs)
|
||||||
konancMain(konancArgs)
|
konancArgs?.let { konancMain(it) }
|
||||||
}
|
}
|
||||||
"jsinterop" -> {
|
"jsinterop" -> {
|
||||||
val konancArgs = invokeInterop("wasm", utilityArgs)
|
val konancArgs = invokeInterop("wasm", utilityArgs)
|
||||||
konancMain(konancArgs)
|
konancArgs?.let { konancMain(it) }
|
||||||
}
|
}
|
||||||
"klib" ->
|
"klib" ->
|
||||||
klibMain(utilityArgs)
|
klibMain(utilityArgs)
|
||||||
|
|||||||
Reference in New Issue
Block a user