From 83a56f193177df36427d35d542b6345c82f73edf Mon Sep 17 00:00:00 2001 From: Alexander Gorshenev Date: Wed, 20 Dec 2017 16:26:57 +0300 Subject: [PATCH] Move cinterop tool to @Argument based command line description. --- Interop/StubGenerator/build.gradle | 1 + .../native/interop/gen/jvm/CommandLine.kt | 113 ++++++++++++++++++ .../kotlin/native/interop/gen/jvm/main.kt | 84 +++++-------- .../jetbrains/kotlin/cli/utilities/main.kt | 2 +- 4 files changed, 147 insertions(+), 53 deletions(-) create mode 100644 Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt diff --git a/Interop/StubGenerator/build.gradle b/Interop/StubGenerator/build.gradle index 9bb10af6bc4..61be955a818 100644 --- a/Interop/StubGenerator/build.gradle +++ b/Interop/StubGenerator/build.gradle @@ -25,6 +25,7 @@ mainClassName = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt" dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" + compile "org.jetbrains.kotlin:kotlin-compiler:$buildKotlinVersion" compile project(':Interop:Indexer') compile project(':shared') } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt new file mode 100644 index 00000000000..bedab280452 --- /dev/null +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/CommandLine.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.native.interop.tool + +import org.jetbrains.kotlin.cli.common.arguments.* + +open class CommonInteropArguments : CommonToolArguments() { + @Argument(value = "-flavor", valueDescription = "", description = "One of: jvm, native") + var flavor: String? = null + + @Argument(value = "-pkg", valueDescription = "", description = "place generated bindings to the package") + var pkg: String? = null + + @Argument(value = "-generated", valueDescription = "", description = "place generated bindings to the directory") + var generated: String? = null +} + +class CInteropArguments : CommonInteropArguments() { + @Argument(value = "-import", valueDescription = "", description = "a semicolon separated list of headers, prepended with the package name") + var import: Array = arrayOf() + + @Argument(value = "-target", valueDescription = "", description = "native target to compile to") + var target: String? = null + + @Argument(value = "-natives", valueDescription = "", description = "where to put the built native files") + var natives: String? = null + + @Argument(value = "-def", valueDescription = "", description = "the library definition file") + var def: String? = null + + @Argument(value = "-manifest", valueDescription = "", description = "library manifest addend") + var manifest: String? = null + + @Argument(value = "-properties", valueDescription = "", description = "an alternative location of konan.properties file") + var properties: String? = null + + // TODO: the short -h for -header conflicts with -h for -help. + // The -header currently wins, but need to make it a little more sound. + @Argument(value = "-header", shortName = "-h", valueDescription = "", description = "header file to produce kotlin bindings for") + var header: Array = arrayOf() + + @Argument(value = HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX, shortName = "-hfasp", valueDescription = "", description = "header file to produce kotlin bindings for") + var headerFilterPrefix: Array = arrayOf() + + @Argument(value = "-compilerOpts", shortName = "-copt", valueDescription = "", description = "additional compiler options") + var compilerOpts: Array = arrayOf() + + @Argument(value = "-linkerOpts", shortName = "-lopt", valueDescription = "", description = "additional linker options") + var linkerOpts: Array = arrayOf() + + @Argument(value = "-shims", description = "wrap bindings by a tracing layer") + var shims: Boolean = false + + @Argument(value = "-linker", valueDescription = "", description = "use specified linker") + var linker: String? = null + + @Argument(value = "-staticLibrary", valueDescription = "", description = "embed static library to the result") + var staticLibrary: Array = arrayOf() + + @Argument(value = "-libraryPath", valueDescription = "", description = "add a library search path") + var libraryPath: Array = arrayOf() + + @Argument(value = "-cstubsname", valueDescription = "", description = "provide a name for the generated c stubs file") + var cstubsname: String? = null + + @Argument(value = "-keepcstubs", description = "preserve the generated c stubs for inspection") + var keepcstubs: Boolean = false +} + +const val HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX = "-headerFilterAdditionalSearchPrefix" + +fun parseCommandLine(args: Array, arguments: T): T { + parseCommandLineArguments(args.asList(), arguments) + reportArgumentParseProblems(arguments.errors) + 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") + } +} diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt index 3e57ad4612c..377fa0d5e62 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt @@ -29,8 +29,8 @@ import java.util.* fun main(args: Array) = interop(args, null) fun interop(args: Array, argsToCompiler: MutableList? = null) { - - processLib(parseArgs(args), argsToCompiler) + val arguments = parseCommandLine(args, CInteropArguments()) + processLib(arguments, argsToCompiler) } // Options, whose values are space-separated and can be escaped. @@ -42,22 +42,6 @@ private fun String.asArgList(key: String) = else listOf(this) -private fun parseArgs(args: Array): Map> { - val commandLine = mutableMapOf>() - for (index in 0..args.size - 1 step 2) { - val key = args[index] - if (key[0] != '-') { - throw IllegalArgumentException("Expected a flag with initial dash: $key") - } - if (index + 1 == args.size) { - throw IllegalArgumentException("Expected an value after $key") - } - val value = args[index + 1].asArgList(key) - commandLine[key]?.addAll(value) ?: commandLine.put(key, value.toMutableList()) - } - return commandLine -} - // Performs substitution similar to: // foo = ${foo} ${foo.${arch}} ${foo.${os}} private fun substitute(properties: Properties, substitutions: Map) { @@ -150,10 +134,8 @@ private fun Properties.putAndRunOnReplace(key: Any, newValue: Any, beforeReplace this[key] = newValue } -private fun warn(msg: String) { - println("warning: $msg") -} - +// TODO: Utilize Usage from the big Kotlin. +// That requires to extend the CLITool class. private fun usage() { println(""" Run interop tool with -def .def @@ -201,23 +183,21 @@ private fun argsToCompiler(staticLibraries: List, libraryPaths: List listOf("-includeBinary", it) } .flatten() } -private fun parseImports(args: Map>): ImportsImpl { - val headerIdToPackage = (args["-import"] ?: emptyList()).map { arg -> +private fun parseImports(imports: Array): ImportsImpl { + val headerIdToPackage = imports.map { arg -> val (pkg, joinedIds) = arg.split(':') - val ids = joinedIds.split(',') + val ids = joinedIds.split(';') ids.map { HeaderId(it) to pkg } }.reversed().flatten().toMap() return ImportsImpl(headerIdToPackage) } -fun getCompilerFlagsForVfsOverlay(args: Map>, def: DefFile): List { +fun getCompilerFlagsForVfsOverlay(headerFilterPrefix: Array, def: DefFile): List { val relativeToRoot = mutableMapOf() // TODO: handle clashes - val HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX = "-headerFilterAdditionalSearchPrefix" - - val filteredIncludeDirs = args[HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX]?.map { Paths.get(it) } - if (filteredIncludeDirs != null) { + val filteredIncludeDirs = headerFilterPrefix .map { Paths.get(it) } + if (filteredIncludeDirs.isNotEmpty()) { val headerFilterGlobs = def.config.headerFilter if (headerFilterGlobs.isEmpty()) { error("'$HEADER_FILTER_ADDITIONAL_SEARCH_PREFIX' option requires " + @@ -267,36 +247,36 @@ private fun findFilesByGlobs(roots: List, globs: List): Map>, +private fun processLib(arguments: CInteropArguments, argsToCompiler: MutableList?) { val userDir = System.getProperty("user.dir") - val ktGenRoot = args["-generated"]?.single() ?: userDir - val nativeLibsDir = args["-natives"]?.single() ?: userDir - val flavorName = args["-flavor"]?.single() ?: "jvm" + 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 = args["-def"]?.single()?.let { File(it) } - val manifestAddend = args["-manifest"]?.single()?.let { File(it) } + val defFile = arguments.def?.let { File(it) } + val manifestAddend = arguments.manifest?.let { File(it) } - if (defFile == null && args["-pkg"] == null) { + if (defFile == null && arguments.pkg == null) { usage() return } val tool = ToolConfig( - args["-target"]?.single(), - args["-properties"]?.single(), + arguments.target, + arguments.properties, System.getProperty("konan.home") ) tool.downloadDependencies() val def = DefFile(defFile, tool.substitutions) - val additionalHeaders = args["-h"].orEmpty() - val additionalCompilerOpts = args["-copt"].orEmpty() + args["-compilerOpts"].orEmpty() - val additionalLinkerOpts = args["-lopt"].orEmpty() + args["-linkerOpts"].orEmpty() - val generateShims = args["-shims"].isTrue() - val verbose = args["-verbose"].isTrue() + val additionalHeaders = arguments.header + val additionalCompilerOpts = arguments.compilerOpts + val additionalLinkerOpts = arguments.linkerOpts + val generateShims = arguments.shims + val verbose = arguments.verbose System.load(tool.libclang) @@ -306,7 +286,7 @@ private fun processLib(args: Map>, addAll(def.config.compilerOpts) addAll(tool.defaultCompilerOpts) addAll(additionalCompilerOpts) - addAll(getCompilerFlagsForVfsOverlay(args, def)) + addAll(getCompilerFlagsForVfsOverlay(arguments.headerFilterPrefix, def)) addAll(when (language) { Language.C -> emptyList() Language.OBJECTIVE_C -> { @@ -328,15 +308,15 @@ private fun processLib(args: Map>, def.config.linkerOpts.toTypedArray() + tool.defaultCompilerOpts + additionalLinkerOpts - val linkerName = args["-linker"]?.atMostOne() ?: def.config.linker + val linkerName = arguments.linker ?: def.config.linker val linker = "${tool.llvmHome}/bin/$linkerName" val compiler = "${tool.llvmHome}/bin/clang" val excludedFunctions = def.config.excludedFunctions.toSet() - val staticLibraries = def.config.staticLibraries + args["-staticLibrary"].orEmpty() - val libraryPaths = def.config.libraryPaths + args["-libraryPath"].orEmpty() + val staticLibraries = def.config.staticLibraries + arguments.staticLibrary + val libraryPaths = def.config.libraryPaths + arguments.libraryPath argsToCompiler ?. let { it.addAll(argsToCompiler(staticLibraries, libraryPaths)) } - val fqParts = (args["-pkg"]?.atMostOne() ?: def.config.packageName)?.let { + val fqParts = (arguments.pkg ?: def.config.packageName)?.let { it.split('.') } ?: defFile!!.name.split('.').reversed().drop(1) @@ -346,10 +326,10 @@ private fun processLib(args: Map>, val outKtFileRelative = (fqParts + outKtFileName).joinToString("/") val outKtFile = File(ktGenRoot, outKtFileRelative) - val libName = args["-cstubsname"]?.atMostOne() ?: fqParts.joinToString("") + "stubs" + val libName = arguments.cstubsname ?: fqParts.joinToString("") + "stubs" val headerFilterGlobs = def.config.headerFilter - val imports = parseImports(args) + val imports = parseImports(arguments.import) val headerInclusionPolicy = HeadersInclusionPolicyImpl(headerFilterGlobs, imports) val library = NativeLibrary( @@ -431,7 +411,7 @@ private fun processLib(args: Map>, runCmd(compilerCmd, workDir, verbose) } - if (!args["-keepcstubs"].isTrue()) { + if (!arguments.keepcstubs) { outCFile.delete() } } diff --git a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt index ad3ebacf0b9..e5eb8a07f55 100644 --- a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt +++ b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt @@ -76,7 +76,7 @@ fun invokeCinterop(args: Array) { manifestProperties["package"]?.let { val pkg = it as String val headerIds = (manifestProperties["includedHeaders"] as String).split(' ') - val arg = "$pkg:${headerIds.joinToString(",")}" + val arg = "$pkg:${headerIds.joinToString(";")}" listOf("-import", arg) } ?: emptyList() }