diff --git a/INTEROP.md b/INTEROP.md index d9e40ed90e0..a9bdd4d7e5d 100644 --- a/INTEROP.md +++ b/INTEROP.md @@ -1,4 +1,23 @@ -# An example of interop tool use # +# Kotlin N interoperability # + +## Introduction ## + + _Kotlin N_ follows general tradition of Kotlin to provide excellent +existing platform software interoperability. In case of native platform +most important interoperability target is a C library. Thus _Kotlin N_ +comes with an `interop` tool, which could be used to quickly generate +everything needed to interact with an external library. + + Following workflow is expected when interacting with the native library. + * create `.def` file describing what to include into bindings + * use `interop` tool to produce `stubs.bc` and Kotlin bindings + * run _Kotlin N_ compiler on an application to produce the final executable + + Interoperability tool analyses C headers and produces "natural" mapping of +types, function and constants into the Kotlin world. Generated stubs can be +imported into an IDE for purposes of code completion and navigation. + +## Simple example ## Build the dependencies and the compiler (see README.md). @@ -8,7 +27,7 @@ Prepare stubs for the system sockets library: Compile the echo server: - ./dist/bin/konanc backend.native/tests/interop/basics/echo_server.kt \ + ./dist/bin/kotlinc backend.native/tests/interop/basics/echo_server.kt \ sockets -nativelibrary socketsstubs.bc Run the server: @@ -23,3 +42,38 @@ Write something to console and watch server echoing it back. ~~Quit telnet by pressing ctrl+] ctrl+D~~ + +## Creating bindings for a new library ## + + To create bindings for a new library, start by creating `.def` file. +Structurally it's a simple property file, looking like this: + + + header = zlib.h + compilerOpts = -std=c99 + linkerOpts = -lz + +Then run interop tool with something like (note that for host libraries not included +in sysroot search paths for headers may be needed): + + ./dist/bin/interop -def:zlib.def -copt:-I/opt/local/include + +This command will produce directory named `zlib` containing file `zlib.kt` +and file `zlibstubs.bc` containing implementation specific glue bitcode. + +If behavior for certain platform shall be modified, one may use format like +`compilerOpts.osx` or `compilerOpts.linux` to provide platform-specific values +to options. + +Note, that generated bindings are generally platform-specific, so if developing for +multiple targets, bindings need to be regenerated. + +After generation of bindings they could be used by IDE as proxy view of the +native library. + +For typical Unix library with config script `compilerOpts` will likely contain +output of config script with `--cflags` flag (maybe without exact paths) and +`linkerOpts` - output of config script with `--libs`. + +Also all those values could be passed directly as values for `-copt` and +`linkedArgs` respectively. 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 fe7913b9a22..7bbb8890238 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 @@ -21,7 +21,7 @@ fun main(args: Array) { } private fun detectHost():String { - val os =System.getProperty("os.name") + val os = System.getProperty("os.name") when (os) { "Linux" -> return "linux" "Windows" -> return "win" @@ -53,19 +53,19 @@ private fun substitute(properties: Properties, substitutions: Map): Pro return result } +private fun usage() { + println(""" +Run interop tool with -def:.def +Following flags are supported: + -def:.def specifies library definition file + -copt: specifies flags passed to clang + -lopt: specifies flags passed to linker + -verbose increases verbosity + -shims adds generation of shims tracing native library calls + -pkg: + -h:.h header files to parse +""") +} + private fun processLib(konanHome: String, substitutions: Map, commandArgs: List) { - val args = commandArgs.groupBy ({ getArgPrefix(it)!! }, { dropPrefix(it)!! }) // TODO + val args = commandArgs.groupBy ({ getArgPrefix(it) }, { dropPrefix(it) }) // TODO val userDir = System.getProperty("user.dir") val ktGenRoot = args["-generated"]?.single() ?: userDir @@ -162,6 +176,12 @@ private fun processLib(konanHome: String, val platform = KotlinPlatform.values().single { it.name.equals(platformName, ignoreCase = true) } val defFile = args["-def"]?.single()?.let { File(it) } + + if (defFile == null && args["-pkg"] == null) { + usage() + return + } + val config = loadProperties(defFile, substitutions) val konanFileName = args["-properties"]?.single() ?: diff --git a/README.md b/README.md index 85a9665fd66..eea062e4976 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ Then build the compiler: After that you should be able to compile your programs like that: - ./dist/bin/konanc hello.kt -o hello + ./dist/bin/kotlinc hello.kt -o hello For an optimized compilation use -opt: - ./dist/bin/konanc hello.kt -o hello -opt + ./dist/bin/kotlinc hello.kt -o hello -opt For some tests, use: diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 340dd95be7b..6e15ddf219c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -44,9 +44,9 @@ between threads are allowed. Download Kotlin N redistributable and unpack it. You can run command line compiler with - ./dist/bin/kotlinnc .kt -o .kexe + ./dist/bin/kotlinc .kt -o .kexe - One may use `'-h'` flag to `kotlinnc` to see available flags. + One may use `'-h'` flag to `kotlinc` to see available flags. For documentation on C interoperability stubs see INTEROP.md. diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.java b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.java index b97375db487..3235edf2871 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.java +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.java @@ -28,7 +28,7 @@ public class K2NativeCompilerArguments extends CommonCompilerArguments { @Argument(value = "nolink", description = "Don't link, just produce a bitcode file") public boolean nolink; - @Argument(value = "linkerArg", description = "Add argument to linker") + @Argument(value = "linkerArgs", description = "Pass arguments to linker", delimiter = " ") @ValueDescription("") public String[] linkerArguments; diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index 0e444b41e81..395e8a0c6a5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -175,11 +175,26 @@ internal class LinkStage(val context: Context) { return objectFile } + fun asLinkerArgs(args: List): List { + val result = mutableListOf() + for (arg in args) { + // If user passes compiler arguments to us - transform them to linker ones. + if (arg.startsWith("-Wl,")) { + result.addAll(arg.substring(4).split(',')) + } else { + result.add(arg) + } + } + return result + } + fun link(objectFiles: List): ExecutableFile { val executable = config.get(KonanConfigKeys.EXECUTABLE_FILE)!! val linkCommand = platform.linkCommand(objectFiles, executable, optimize) + - config.getNotNull(KonanConfigKeys.LINKER_ARGS) + asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) + + println(linkCommand.joinToString("|||")) runTool(*linkCommand.toTypedArray()) diff --git a/cmd/konanc b/cmd/konanc index ecc23b021be..4ac2d939e4c 100755 --- a/cmd/konanc +++ b/cmd/konanc @@ -31,13 +31,12 @@ while [ $# -gt 0 ]; do shift ;; *) - konan_args=("${konan_args[@]}" "$1") + konan_args[${#konan_args[@]}]=$1 shift ;; esac done -# Based on findScalaHome() from scalac script findHome() { local source="${BASH_SOURCE[0]}" while [ -h "$source" ] ; do @@ -64,5 +63,5 @@ JAVA_OPTS=-ea java_args=("${java_args[@]} -noverify -Dkonan.home=${KONAN_HOME} -Djava.library.path=${NATIVE_LIB}") -$JAVACMD $JAVA_OPTS ${java_args[@]} -cp $KONAN_CLASSPATH $KONAN_COMPILER ${konan_args[@]} +$JAVACMD $JAVA_OPTS ${java_args[@]} -cp $KONAN_CLASSPATH $KONAN_COMPILER "${konan_args[@]}"