Rewrite interop tools command line options to have help messages without crashes (#2672)

This commit is contained in:
LepilkinaElena
2019-02-20 09:14:28 +03:00
committed by GitHub
parent 554348ace6
commit 75805b3528
11 changed files with 187 additions and 235 deletions
+3
View File
@@ -38,6 +38,9 @@ dependencies {
}
compileKotlin {
sourceSets {
main.kotlin.srcDirs += "$rootDir/tools/kliopt"
}
kotlinOptions {
freeCompilerArgs = ['-Xuse-experimental=kotlin.ExperimentalUnsignedTypes']
}
@@ -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()
}
@@ -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()
@@ -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 -> {
@@ -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')) {
+28 -12
View File
@@ -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++
@@ -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)
@@ -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)