Move everything under kotlin-native folder
I was forced to manually do update the following files, because otherwise they would be ignored according .gitignore settings. Probably they should be deleted from repo. Interop/.idea/compiler.xml Interop/.idea/gradle.xml Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_3.xml Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_3.xml Interop/.idea/modules.xml Interop/.idea/modules/Indexer/Indexer.iml Interop/.idea/modules/Runtime/Runtime.iml Interop/.idea/modules/StubGenerator/StubGenerator.iml backend.native/backend.native.iml backend.native/bc.frontend/bc.frontend.iml backend.native/cli.bc/cli.bc.iml backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt backend.native/tests/link/lib/foo.kt backend.native/tests/link/lib/foo2.kt backend.native/tests/teamcity-test.property
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
# C to Kotlin/Native interoperability example
|
||||
|
||||
This example shows how to use Kotlin/Native programs from other execution environments, such as Python.
|
||||
Python has C native interface, which could be used to organize a bridge with the
|
||||
Kotlin/Native application. File `kotlin_bridge.c` contains translation code between Kotlin and Python
|
||||
lands. This demo works on Linux, Windows (64-bit) or macOS hosts.
|
||||
|
||||
To build and run the sample do the following:
|
||||
|
||||
* Install Python with development headers, i.e. on Linux
|
||||
```
|
||||
sudo apt install python-dev
|
||||
```
|
||||
|
||||
* Run `build.sh`, it will ask for superuser password to install Python extension.
|
||||
Use build.bat on Windows.
|
||||
* On macOS copy Kotlin binary to extension's directory and change install name with
|
||||
`install_name_tool` tool, i.e.
|
||||
```
|
||||
sudo cp ./build/libserver.dylib /Library/Python/2.7/site-packages/
|
||||
sudo install_name_tool /Library/Python/2.7/site-packages/kotlin_bridge.so \
|
||||
-change libserver.dylib @loader_path/libserver.dylib
|
||||
```
|
||||
* On Linux copy Kotlin binary in some place where libraries could be loaded from, i.e.
|
||||
```
|
||||
cp ./build/libserver.so /usr/local/lib/
|
||||
ldconfig
|
||||
```
|
||||
or modify dynamic loader search path with
|
||||
```
|
||||
export LD_LIBRARY_PATH=`pwd`
|
||||
```
|
||||
|
||||
* run Python code using Kotlin functionality with
|
||||
```
|
||||
python src/main/python/main.py
|
||||
```
|
||||
* it will show you result of using several Kotlin/Native APIs, accepting and returning both objects and
|
||||
primitive types
|
||||
|
||||
The example works as following. Kotlin/Native API is implemented in `Server.kt`, and we run Kotlin/Native compiler
|
||||
with `-produce dynamic` option. Compiler produces two artifacts: `server_api.h` which is C language API
|
||||
to all public functions and classes available in the application. `libserver.dylib` or `libserver.so` or `server.dll`
|
||||
shared object contains C bridge to all above APIs.
|
||||
|
||||
This C bridge looks like a C struct, reflecting all scopes in program, with operations available. For example,
|
||||
for class Server
|
||||
```c_cpp
|
||||
class Server(val prefix: String) {
|
||||
fun greet(session: Session) = "$prefix: Hello from Kotlin/Native in ${session}"
|
||||
fun concat(session: Session, a: String, b: String) = "$prefix: $a $b in ${session}"
|
||||
fun add(session: Session, a: Int, b: Int) = a + b + session.number
|
||||
}
|
||||
```
|
||||
following C API is produced
|
||||
```c_cpp
|
||||
typedef struct {
|
||||
server_KNativePtr pinned;
|
||||
} server_kref_demo_Session;
|
||||
typedef struct {
|
||||
server_KNativePtr pinned;
|
||||
} server_kref_demo_Server;
|
||||
|
||||
typedef struct {
|
||||
/* Service functions. */
|
||||
void (*DisposeStablePointer)(server_KNativePtr ptr);
|
||||
void (*DisposeString)(const char* string);
|
||||
server_KBoolean (*IsInstance)(server_KNativePtr ref, const server_KType* type);
|
||||
|
||||
/* User functions. */
|
||||
struct {
|
||||
struct {
|
||||
struct {
|
||||
server_KType* (*_type)(void);
|
||||
server_kref_demo_Session (*Session)(const char* name, server_KInt number);
|
||||
} Session;
|
||||
struct {
|
||||
server_KType* (*_type)(void);
|
||||
server_kref_demo_Server (*Server)(const char* prefix);
|
||||
const char* (*greet)(server_kref_demo_Server thiz, server_kref_demo_Session session);
|
||||
const char* (*concat)(server_kref_demo_Server thiz, server_kref_demo_Session session, const char* a, const char* b);
|
||||
server_KInt (*add)(server_kref_demo_Server thiz, server_kref_demo_Session session, server_KInt a, server_KInt b);
|
||||
} Server;
|
||||
} demo;
|
||||
} kotlin;
|
||||
} server_ExportedSymbols;
|
||||
extern server_ExportedSymbols* server_symbols(void);
|
||||
```
|
||||
|
||||
So every class instance is represented with a single element structure, encapsulating stable pointer to an instance.
|
||||
Once no longer needed, `DisposeStablePointer()` with that stable pointer shall be called, and if value is not stored
|
||||
somewhere else - it is disposed. For primitive types and `kotlin.String` smart bridges converting to C primitive types
|
||||
or to C strings (which has to be manually freed with `DisposeString()`) are implemented.
|
||||
|
||||
For example, running constructor of class Server taking a string will look like
|
||||
|
||||
server_kref_demo_Server server = server_symbols()->kotlin.demo.Server.Server("the server");
|
||||
|
||||
And disposing no longer needed instance will look like
|
||||
|
||||
server_symbols()->DisposeStablePointer(server.pinned);
|
||||
|
||||
To make code easier readable, macro definitions like
|
||||
|
||||
#define T_(name) server_kref_demo_ ## name
|
||||
#define __ server_symbols()->
|
||||
|
||||
will transform above, overly verbose lines to more readable
|
||||
|
||||
T_(Server) server = __ kotlin.demo.Server.Server("the server");
|
||||
|
||||
`_type()` function will return opaque type pointer, which could be checked with `IsInstance()` operation, like
|
||||
|
||||
__ IsInstance(ref.pinned, __ kotlin.demo.Server._type())
|
||||
@@ -0,0 +1,15 @@
|
||||
setlocal
|
||||
set DIR=.
|
||||
|
||||
if defined KONAN_HOME (
|
||||
set "PATH=%KONAN_HOME%\bin;%PATH%"
|
||||
) else (
|
||||
set "PATH=..\..\dist\bin;..\..\bin;%PATH%"
|
||||
)
|
||||
kotlinc-native -p dynamic src/main/kotlin/Server.kt -o server
|
||||
|
||||
rem Prepare MSVC build environment, and .lib file for linking with our .dll.
|
||||
SET VS90COMNTOOLS=%VS140COMNTOOLS%
|
||||
\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\lib.exe" /def:server.def /out:server.lib /machine:X64
|
||||
|
||||
python src/main/python/setup.py install
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
DIR=$(cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
if [ -z "$KONAN_HOME" ]; then
|
||||
PATH="$DIR/../../dist/bin:$DIR/../../bin:$PATH"
|
||||
else
|
||||
PATH="$KONAN_HOME/bin:$PATH"
|
||||
fi
|
||||
|
||||
KONAN_USER_DIR=${KONAN_DATA_DIR:-"$HOME/.konan"}
|
||||
KONAN_DEPS="$KONAN_USER_DIR/dependencies"
|
||||
|
||||
# python3 shall work as well.
|
||||
PYTHON=python
|
||||
|
||||
mkdir -p $DIR/build
|
||||
cd $DIR/build
|
||||
|
||||
kotlinc-native -p dynamic $DIR/src/main/kotlin/Server.kt -o server
|
||||
|
||||
cd $DIR
|
||||
sudo -S $PYTHON ${DIR}/src/main/python/setup.py install
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "server_api.h"
|
||||
|
||||
#define __ server_symbols()->
|
||||
#define T_(name) server_kref_demo_ ## name
|
||||
|
||||
// Note, that as we cache this in the global, and Kotlin/Native object references
|
||||
// are currently thread local, we make this global a TLS variable.
|
||||
#ifdef _MSC_VER
|
||||
#define TLSVAR __declspec(thread)
|
||||
#else
|
||||
#define TLSVAR __thread
|
||||
#endif
|
||||
|
||||
static TLSVAR server_kref_demo_Server server = { 0 };
|
||||
|
||||
static T_(Server) getServer(void) {
|
||||
if (!server.pinned) {
|
||||
server = __ kotlin.root.demo.Server.Server("the server");
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
static T_(Session) getSession(PyObject* args) {
|
||||
T_(Session) result = { 0 };
|
||||
long long pinned;
|
||||
if (PyArg_ParseTuple(args, "L", &pinned)) {
|
||||
result.pinned = (void*)(uintptr_t)pinned;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject* open_session(PyObject* self, PyObject* args) {
|
||||
PyObject *result = NULL;
|
||||
char* string_arg = NULL;
|
||||
int int_arg = 0;
|
||||
if (PyArg_ParseTuple(args, "is", &int_arg, &string_arg)) {
|
||||
T_(Session) session = __ kotlin.root.demo.Session.Session(string_arg, int_arg);
|
||||
result = Py_BuildValue("L", session.pinned);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject* close_session(PyObject* self, PyObject* args) {
|
||||
T_(Session) session = getSession(args);
|
||||
__ DisposeStablePointer(session.pinned);
|
||||
__ DisposeStablePointer(getServer().pinned);
|
||||
server.pinned = 0;
|
||||
return Py_BuildValue("L", 0);
|
||||
}
|
||||
|
||||
static PyObject* greet_server(PyObject* self, PyObject* args) {
|
||||
T_(Server) server = getServer();
|
||||
T_(Session) session = getSession(args);
|
||||
const char* string = __ kotlin.root.demo.Server.greet(server, session);
|
||||
PyObject* result = Py_BuildValue("s", string);
|
||||
__ DisposeString(string);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject* concat_server(PyObject* self, PyObject* args) {
|
||||
long long session_arg;
|
||||
char* string_arg1 = NULL;
|
||||
char* string_arg2 = NULL;
|
||||
PyObject* result = NULL;
|
||||
|
||||
if (PyArg_ParseTuple(args, "Lss", &session_arg, &string_arg1, &string_arg2)) {
|
||||
T_(Server) server = getServer();
|
||||
T_(Session) session = { (void*)(uintptr_t)session_arg };
|
||||
const char* string = __ kotlin.root.demo.Server.concat(server, session, string_arg1, string_arg2);
|
||||
result = Py_BuildValue("s", string);
|
||||
__ DisposeString(string);
|
||||
} else {
|
||||
result = Py_BuildValue("s", NULL);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject* add_server(PyObject* self, PyObject* args) {
|
||||
long long session_arg;
|
||||
int int_arg1 = 0;
|
||||
int int_arg2 = 0;
|
||||
PyObject* result = NULL;
|
||||
|
||||
if (PyArg_ParseTuple(args, "Lii", &session_arg, &int_arg1, &int_arg2)) {
|
||||
T_(Server) server = getServer();
|
||||
T_(Session) session = { (void*)(uintptr_t)session_arg };
|
||||
int sum = __ kotlin.root.demo.Server.add(server, session, int_arg1, int_arg2);
|
||||
result = Py_BuildValue("i", sum);
|
||||
} else {
|
||||
result = Py_BuildValue("i", 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMethodDef kotlin_bridge_funcs[] = {
|
||||
{ "open_session", (PyCFunction)open_session, METH_VARARGS, "Opens a session" },
|
||||
{ "close_session", (PyCFunction)close_session, METH_VARARGS, "Closes the session" },
|
||||
{ "greet_server", (PyCFunction)greet_server, METH_VARARGS, "Greeting service" },
|
||||
{ "concat_server", (PyCFunction)concat_server, METH_VARARGS, "Concatenation service" },
|
||||
{ "add_server", (PyCFunction)add_server, METH_VARARGS, "Addition service" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
|
||||
struct module_state {
|
||||
server_kref_demo_Server server;
|
||||
};
|
||||
|
||||
static int kotlin_bridge_traverse(PyObject *m, visitproc visit, void *arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kotlin_bridge_clear(PyObject *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"kotlin_bridge",
|
||||
NULL,
|
||||
sizeof(struct module_state),
|
||||
kotlin_bridge_funcs,
|
||||
NULL,
|
||||
kotlin_bridge_traverse,
|
||||
kotlin_bridge_clear,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC PyInit_kotlin_bridge(void) {
|
||||
PyObject *module = PyModule_Create(&moduledef);
|
||||
return module;
|
||||
}
|
||||
#else
|
||||
void initkotlin_bridge(void) {
|
||||
Py_InitModule3("kotlin_bridge", kotlin_bridge_funcs, "Kotlin/Native example module");
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package demo
|
||||
|
||||
class Session(val name: String, val number: Int)
|
||||
|
||||
class Server(val prefix: String) {
|
||||
fun greet(session: Session) = "$prefix: Hello from Kotlin/Native in ${session}"
|
||||
fun concat(session: Session, a: String, b: String) = "$prefix: $a $b in ${session}"
|
||||
fun add(session: Session, a: Int, b: Int) = a + b + session.number
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
# that can be found in the license/LICENSE.txt file.
|
||||
#
|
||||
|
||||
import kotlin_bridge
|
||||
|
||||
session = kotlin_bridge.open_session(239, 'konan')
|
||||
|
||||
message = kotlin_bridge.greet_server(session)
|
||||
print("Greet '{}'".format(message))
|
||||
|
||||
message = kotlin_bridge.concat_server(session, "Coding", "fun")
|
||||
print("Concat '{}'".format(message))
|
||||
|
||||
message = kotlin_bridge.add_server(session, 1, 60)
|
||||
print("Sum '{}'".format(message))
|
||||
|
||||
|
||||
kotlin_bridge.close_session(session)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
# that can be found in the license/LICENSE.txt file.
|
||||
#
|
||||
|
||||
from distutils.core import setup, Extension
|
||||
|
||||
setup(name='kotlin_bridge',
|
||||
version='1.0',
|
||||
maintainer = 'JetBrains',
|
||||
maintainer_email = 'info@jetbrains.com',
|
||||
author = 'JetBrains',
|
||||
author_email = 'info@jetbrains.com',
|
||||
description = 'Kotlin/Native Python bridge',
|
||||
long_description = 'Using Kotlin/Native from Python example',
|
||||
|
||||
# data_files=[("/Library/Python/2.7/site-packages/", ['libserver.dylib'])],
|
||||
|
||||
ext_modules=[
|
||||
Extension('kotlin_bridge',
|
||||
include_dirs = ['./build'],
|
||||
libraries = ['server'],
|
||||
library_dirs = ['./build'],
|
||||
depends = ['server_api.h'],
|
||||
sources = ['src/main/c/kotlin_bridge.c']
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
# On macOS, after install you may want to copy libserver.dylib to Python's extension directory,
|
||||
# and do something like
|
||||
# sudo install_name_tool /Library/Python/2.7/site-packages/kotlin_bridge.so -change libserver.dylib @loader_path/libserver.dylib
|
||||
# This way libserver.dylib could be loaded from extension's directory.
|
||||
Reference in New Issue
Block a user