Slide 1

Slide 1 text

Making Noise with Kotlin/Native Josh Skeen | [email protected] | @mutexkid slides & code: https://speakerdeck.com/mutexkid/native

Slide 2

Slide 2 text

Who am I? Android Developer & Instructor at Big Nerd Ranch Josh Skeen | [email protected] | @mutexkid

Slide 3

Slide 3 text

Why Kotlin/Native?

Slide 4

Slide 4 text

• Create native binaries for platforms like macOS or embedded • Write high performance audio code on the c layer • Interface with native binaries Why Kotlin/Native?

Slide 5

Slide 5 text

• konanc - generates a native binary executable for a target platform (macOS, Linux variants, Windows, etc) Kotlin/Native Tools Overview

Slide 6

Slide 6 text

Kotlin/Native Tools Overview • cinterop - generates interoperability bindings between a c library or program, and Kotlin code. • konanc - generates a native binary executable for a target platform (macOS, Linux variants, Windows, etc)

Slide 7

Slide 7 text

A First Kotlin/Native Program #include int main() { printf("hello c"); return 0; }

Slide 8

Slide 8 text

Convert from C to Kotlin?

Slide 9

Slide 9 text

A First Kotlin/Native Program

Slide 10

Slide 10 text

A First Kotlin/Native Program

Slide 11

Slide 11 text

Firing up Hopper a.out Hopper disassembler: https://www.hopperapp.com/

Slide 12

Slide 12 text

konan runtime, kotlin/native stdlib, and more! Hopper disassembler: https://www.hopperapp.com/ program.kexe Firing up Hopper

Slide 13

Slide 13 text

konan runtime, kotlin/native stdlib, and more! Hopper disassembler: https://www.hopperapp.com/ program.kexe Firing up Hopper

Slide 14

Slide 14 text

konan runtime, kotlin/native stdlib, and more! Hopper disassembler: https://www.hopperapp.com/ program.kexe Firing up Hopper

Slide 15

Slide 15 text

Firing up Hopper Hopper disassembler: https://www.hopperapp.com/ program.kexe

Slide 16

Slide 16 text

Diving Deeper

Slide 17

Slide 17 text

Building KQuarium • Configure the CLion IDE to work with Kotlin/Native • Call posix standard c library functions • Compile and interface with a c library (ncurses) via cinterop POSIX = portable operating system interface

Slide 18

Slide 18 text

Kotlin Native Gradle Plugin plugins { id "org.jetbrains.kotlin.konan" version "0.9.2" } konanArtifacts { program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" } } plugins { id "org.jetbrains.kotlin.konan" version "0.9.2" } konanArtifacts { program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" } }

Slide 19

Slide 19 text

Kotlin Native Gradle Plugin plugins { id "org.jetbrains.kotlin.konan" version "0.9.2" } konanArtifacts { program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" } } plugins { id "org.jetbrains.kotlin.konan" version "0.9.2" } konanArtifacts { program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" } }

Slide 20

Slide 20 text

CLion: Gradle Plugin plugins { id "org.jetbrains.kotlin.konan" version "0.9.2" } konanArtifacts { program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" } } plugins { id "org.jetbrains.kotlin.konan" version "0.9.2" } konanArtifacts { program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" } }

Slide 21

Slide 21 text

Kotlin Native Gradle Plugin

Slide 22

Slide 22 text

CLion: Gradle Aware

Slide 23

Slide 23 text

CLion: Gradle Aware

Slide 24

Slide 24 text

CLion: Gradle Aware - External Libs

Slide 25

Slide 25 text

// IntelliJ API Decompiler stub source generated from a class file // Implementation of methods is not available package kotlin.collections private const val INT_MAX_POWER_OF_TWO: kotlin.Int /* compiled code */ public val kotlin.Array.indices: kotlin.ranges.IntRange /* compiled code */ public val kotlin.BooleanArray.indices: kotlin.ranges.IntRange /* compiled code */ public val kotlin.ByteArray.indices: kotlin.ranges.IntRange /* compiled code */ public val kotlin.CharArray.indices: kotlin.ranges.IntRange /* compiled code */ CLion: Gradle Aware - External Libs stdlib/linkdata/package_kotlin.collections.knm:

Slide 26

Slide 26 text

KQuarium: Reading Fish Data /\ _/./ ,-' `-:..-'/ : o ) _ ( "`-....,--; `-.\ `' ,--.. .''-.,' /@ `.-: > )< ,-.: `..-',` `-' _______ ,-~~~ ~~~-, ( ) \_-, , , , , ,-_/ / / | | \ \ | | | | | | | | | | | | / / / \ \ \ | | | | | | art/fishone.txt art/fishtwo.txt art/fishthree.txt

Slide 27

Slide 27 text

K/N Stdlib - Java.IO.File plz?

Slide 28

Slide 28 text

K/N Stdlib - Java.IO.File plz? • Much more minimal than what you’re used to w/ Java Stdlib • Collections, Ranges, Math, Text, System

Slide 29

Slide 29 text

Read Fish Data (hypothetical, c) #include char *readFile() { file = fopen(“fishone.txt”, "rb"); if (file) { while ((nread = fread(buf, 1, sizeof buf, file)) > 0) fwrite(buf, 1, nread, stdout); fclose(file); } } #include char *readFile() { file = fopen(“fishone.txt”, "rb"); if (file) { while ((nread = fread(buf, 1, sizeof buf, file)) > 0) fwrite(buf, 1, nread, stdout); fclose(file); } }

Slide 30

Slide 30 text

Read Fish Data (hypothetical, c) #include char *readFile() { file = fopen(“fishone.txt”, "rb"); if (file) { while ((nread = fread(buf, 1, sizeof buf, file)) > 0) fwrite(buf, 1, nread, stdout); fclose(file); } } #include char *readFile() { file = fopen(“fishone.txt”, "rb"); if (file) { while ((nread = fread(buf, 1, sizeof buf, file)) > 0) fwrite(buf, 1, nread, stdout); fclose(file); } } #include char *readFile() { file = fopen(“fishone.txt”, "rb"); if (file) { while ((nread = fread(buf, 1, sizeof buf, file)) > 0) fwrite(buf, 1, nread, stdout); fclose(file); } } #include char *readFile() { file = fopen(“fishone.txt”, "rb"); if (file) { while ((nread = fread(buf, 1, sizeof buf, file)) > 0) fwrite(buf, 1, nread, stdout); fclose(file); } } #include char *readFile() { file = fopen(“fishone.txt”, "rb"); if (file) { while ((nread = fread(buf, 1, sizeof buf, file)) > 0) fwrite(buf, 1, nread, stdout); fclose(file); } }

Slide 31

Slide 31 text

Kotlin/Native: Posix Library

Slide 32

Slide 32 text

Kotlin/Native: Posix Library public fun fgets(arg0: kotlinx.cinterop.CValuesRef */>?, arg1: kotlin.Int, arg2: kotlinx.cinterop.CValuesRef?): kotlinx.cinterop.CPointer */>? { /* compiled code */ }

Slide 33

Slide 33 text

Read Fish Data with Posix & Stdlib ReadFile.kt import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() } import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() }

Slide 34

Slide 34 text

Read Fish Data with Posix & Stdlib ReadFile.kt import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() } import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() }

Slide 35

Slide 35 text

Read Fish Data with Posix & Stdlib ReadFile.kt import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() } import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() } import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() } import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() }

Slide 36

Slide 36 text

Read Fish Data with Posix & Stdlib ReadFile.kt import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() } import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() } import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() } import kotlinx.cinterop.* import platform.posix.* fun readFileData(path: String): String { val pagesize = getpagesize() val filePointer = fopen(path, “r")!! val stringBuilder = StringBuilder() val charBuffer = nativeHeap.allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, this) != null) { stringBuilder.append(charArray.toKString()) } //free the allocated array (avoiding memory leak) free(charBuffer) return stringBuilder.toString() }

Slide 37

Slide 37 text

Allocating a Byte Array: Cinterop ReadFile.kt import kotlinx.cinterop.* import platform.posix.* val charArray: CArrayPointer = nativeHeap.allocArray(pagesize) while (filePointer.readLine(charArray, pagesize) != null) { stringBuilder.append(charArray.toKString()) } free(charArray) import kotlinx.cinterop.* import platform.posix.* val charArray: CArrayPointer = nativeHeap.allocArray(pagesize) while (filePointer.readLine(charArray, pagesize) != null) { stringBuilder.append(charArray.toKString()) } free(charArray) import kotlinx.cinterop.* import platform.posix.* val charArray: CArrayPointer = nativeHeap.allocArray(pagesize) while (filePointer.readLine(charArray, pagesize) != null) { stringBuilder.append(charArray.toKString()) } free(charArray) import kotlinx.cinterop.* import platform.posix.* val charArray: CArrayPointer = nativeHeap.allocArray(pagesize) while (filePointer.readLine(charArray, pagesize) != null) { stringBuilder.append(charArray.toKString()) } free(charArray)

Slide 38

Slide 38 text

Read Fish Data, Kotlin: memScoped ReadFile.kt fun readFileData(path: String) = memScoped { val charBuffer = allocArray(pagesize) //do stuff with the allocated byte array while(fgets(charBuffer, pagesize, filePointer) != null) { stringBuilder.append(charArray.toKString()) } stringBuilder.toString() //memory freed at close of scope }

Slide 39

Slide 39 text

Loading the Fish, Apply Kotlin Style fgets(filePointer, size, this) while (filePointer.readLine(charArray, pagesize, filePointer) != null) { sb.append(charArray.toKString()) } … fun CPointer.readLine(charArray: CValuesRef, size): = fgets(charArray, size, this) fun CPointer.closeFile() = fclose(this) ReadFile.kt

Slide 40

Slide 40 text

Compiling the Ncurses Library src/libs/ncurses-5.9 git:(master*) $ make install

Slide 41

Slide 41 text

Compiling the Ncurses Library /usr/local/lib libncursesw.dylib /usr/local/include/ncursesw libncursesw.a ncurses.h /usr/local/lib libncursesw.dylib /usr/local/include/ncursesw libncursesw.a ncurses.h

Slide 42

Slide 42 text

/usr/local/lib libncursesw.dylib /usr/local/include/ncursesw libncursesw.a ncurses.h Compiling the Ncurses Library /usr/local/lib libncursesw.dylib /usr/local/include/ncursesw libncursesw.a ncurses.h

Slide 43

Slide 43 text

Ncurses Interop Bindings package = ncurses headers = ncursesw/ncurses.h compilerOpts=-I/usr/local/include/ linkerOpts = -lncurses -L/usr/local/lib/ ncurses.def:

Slide 44

Slide 44 text

Interop Bindings: Gradle Config konanArtifacts { interop('ncurses', targets: ['macbook']) { defFile 'ncurses.def' } program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" libraries { artifact 'ncurses' } } } konanArtifacts { interop('ncurses', targets: ['macbook']) { defFile 'ncurses.def' } program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" libraries { artifact 'ncurses' } } } konanArtifacts { interop('ncurses', targets: ['macbook']) { defFile 'ncurses.def' } program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" libraries { artifact 'ncurses' } } }

Slide 45

Slide 45 text

Interop Bindings : Gradle Task

Slide 46

Slide 46 text

Anatomy of a klib

Slide 47

Slide 47 text

Anatomy of a klib: ncurses.kt … fun newwin(arg0: Int, arg1: Int, arg2: Int, arg3: Int): CPointer? { return interpretCPointer(kniBridge226(arg0, arg1, arg2, arg3)) } @SymbolName("ncurses_kniBridge226") private external fun kniBridge226(p0: Int, p1: Int, p2: Int, p3: Int): NativePtr …

Slide 48

Slide 48 text

A Closer Look (Hopper Disassembler) Linked “dynamically” ; newwin ; _newwin_ptr, _newwin_ptr,_newwin ; DATA XREF=imp___stubs__newwin ; in /usr/lib/libncurses.5.4.dylib _ncurses_kniBridge226: 0000000100057fb0 call imp___stubs__newwin imp___stubs__newwin: // newwin 000000010005a992 jmp qword [_newwin_ptr] _newwin_ptr: 00000001000711e8 extern _newwin _newwin: 00000001000e8290 extern function code @SymbolName("ncurses_kniBridge226") private external fun kniBridge226(p0: Int, p1: Int, p2: Int, p3: Int): NativePtr

Slide 49

Slide 49 text

package = ncurses headers = ncursesw/ncurses.h compilerOpts=-I/usr/local/include/ linkerOpts = -lncurses -L/usr/local/lib/ libraryPaths = /usr/local/lib staticLibraries = libncursesw.a ncurses.def Bundling a Static Library

Slide 50

Slide 50 text

ncurses.klib Bundling a Static Library

Slide 51

Slide 51 text

Using the Ncurses C Binding import ncurses.* fun main(args: Array) { Game.run() } object Game { private var mainWindow: CPointer = initscr()!! }

Slide 52

Slide 52 text

Game Loop: Window main.kt object Game { private var mainWindow: CPointer = initscr()!! val width: Int = mainWindow.pointed._maxx.toInt() val height: Int = mainWindow.pointed._maxy.toInt() object Game { private var mainWindow: CPointer = initscr()!! val width: Int = mainWindow.pointed._maxx.toInt() val height: Int = mainWindow.pointed._maxy.toInt() fun run() { curs_set(0) while (true) { mainWindow.clear() for (fish in sealife) { mainWindow.refresh() fish.update() } mainWindow.refresh()

Slide 53

Slide 53 text

Game Loop: Window main.kt object Game { private var mainWindow: CPointer = initscr()!! val width: Int = mainWindow.pointed._maxx.toInt() val height: Int = mainWindow.pointed._maxy.toInt() object Game { private var mainWindow: CPointer = initscr()!! val width: Int = mainWindow.pointed._maxx.toInt() val height: Int = mainWindow.pointed._maxy.toInt() fun run() { curs_set(0) while (true) { mainWindow.clear() for (fish in sealife) { mainWindow.refresh() fish.update() } mainWindow.refresh()

Slide 54

Slide 54 text

Anatomy of a klib: ncurses.kt typealias WINDOW = _win_st class _win_st(rawPtr: NativePtr) : CStructVar(rawPtr) { ... var _maxy: Short get() = memberAt(4).value set(value) { memberAt(4).value = value } var _maxx: Short get() = memberAt(6).value set(value) { memberAt(6).value = value } ... }

Slide 55

Slide 55 text

Game Loop: CPointer & Pointed main.kt private var mainWindow: CPointer = initscr()!! val width: Int = mainWindow.pointed._maxx.toInt() val height: Int = mainWindow.pointed._maxy.toInt() private var mainWindow: CPointer = initscr()!! val width: Int = mainWindow.pointed._maxx.toInt() val height: Int = mainWindow.pointed._maxy.toInt()

Slide 56

Slide 56 text

Game Loop: CPointer & Pointed main.kt private var mainWindow: CPointer = initscr()!! val width: Int = mainWindow.pointed._maxx.toInt() val height: Int = mainWindow.pointed._maxy.toInt() private var mainWindow: CPointer = initscr()!! val width: Int = mainWindow.pointed._maxx.toInt() val height: Int = mainWindow.pointed._maxy.toInt()

Slide 57

Slide 57 text

Tidying up Ncurses w/ Extensions NcursesExt.kt import kotlinx.cinterop.CPointer import ncurses.* fun CPointer.move(positionY: Int, positionX: Int) = mvwin(this, positionY, positionX) fun CPointer.clear() = wclear(this) fun CPointer.refresh() = wrefresh(this) fun CPointer.print(data: String) = wprintw(this, data)

Slide 58

Slide 58 text

Applying the Kotlin Style: Extensions NcursesExt.kt open class Fish(filePath: String, private val speed: Int = 3) { //fish-related behavior redacted for brevity fun update() { window.run { refresh() clear() move(posY, posX) print(frame) } posX += computeX posY += computeY } } open class Fish(filePath: String, private val speed: Int = 3) { //fish-related behavior redacted for brevity fun update() { window.run { refresh() clear() move(posY, posX) print(frame) } posX += computeX posY += computeY } }

Slide 59

Slide 59 text

NcursesExt.kt Generating Sound • Write sound library in c, call from Kotlin/Native • Write the sound library in Kotlin/Native

Slide 60

Slide 60 text

NcursesExt.kt Writing the CSynth lib • Write sound library in c, using c dependencies • Create a header • Write def file • Generate bindings • Call from kotlin/native

Slide 61

Slide 61 text

csynth.c Writing the CSynth lib #include #include #include #include #include typedef struct { int tableSize; int left_phase; int right_phase; float *sine; } synthData;

Slide 62

Slide 62 text

csynth.c Writing the CSynth lib PaError playWave(int tableSize, long millis) { ... data.sine = malloc(sizeof(float) * tableSize); for (i = 0; i < tableSize; i++) { float d = (float) sin(((double) i / (double) tableSize) * M_PI * 3.0); data.sine[i] = d; } ... }

Slide 63

Slide 63 text

csynth.c Writing the CSynth lib PaError playWave(int tableSize, long millis) { ... err = Pa_OpenStream( &stream, NULL, &outputParameters, SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff, audioCallback, &data); free(data.sine); ... }

Slide 64

Slide 64 text

csynth.c Writing the CSynth lib PaError playWave(int tableSize, long millis) { ... err = Pa_OpenStream( &stream, NULL, &outputParameters, SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff, audioCallback, &data); free(data.sine); ... } PaError playWave(int tableSize, long millis) { ... err = Pa_OpenStream( &stream, NULL, &outputParameters, SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff, audioCallback, &data); free(data.sine); ... }

Slide 65

Slide 65 text

csynth.c Writing the CSynth lib static int audioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { synthData *data = (synthData *) userData; float *out = (float *) outputBuffer; unsigned long i; (void) timeInfo; (void) statusFlags; (void) inputBuffer; for (i = 0; i < framesPerBuffer; i++) { *out++ = data->sine[data->left_phase]; *out++ = data->sine[data->right_phase]; data->left_phase += 1; if (data->left_phase >= data->size) data->left_phase -= data->size; data->right_phase += 3; if (data->right_phase >= data->size) data->right_phase -= data->size; } return paContinue; }

Slide 66

Slide 66 text

csynth.h CSynth lib Header #ifndef CSYNTH_H #define CSYNTH_H int playWave(int tableSize, int millis); #endif //CSYNTH_H

Slide 67

Slide 67 text

csynth.def Kotlin Side - CSynth def file package = libcsynth headers = csynth.h compilerOpts=-I./libs/csynth/ linkerOpts = -lportaudio libraryPaths = ./libs/csynth/ staticLibraries = libcsynth.a

Slide 68

Slide 68 text

csynth.def Kotlin Side - CSynth def file package = libcsynth headers = csynth.h compilerOpts=-I./libs/csynth/ linkerOpts = -lportaudio libraryPaths = ./libs/csynth/ staticLibraries = libcsynth.a

Slide 69

Slide 69 text

csynth.def Kotlin Side - CSynth def file interop('libcsynth', targets: ['macbook']) { defFile ‘libcsynth.def' } program('kquarium', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" libraries { artifact 'ncurses' artifact 'libcsynth' } }

Slide 70

Slide 70 text

Main.kt Kotlin Side - Calling CSynth sealife.forEach { fish -> sealife.forEach { if (it != fish && fish.hitTest(it)) { when (fish) { is Octopus -> libcsynth.play(it.posX * 15, 250) } } }

Slide 71

Slide 71 text

Targeting Embedded • Raspberry Pi build target • “Cross-Compile” libraries

Slide 72

Slide 72 text

Compiling for RPi: MacOS? ~/.konan/kotlin-native-macos-0.9.2/bin/konanc -list-targets macos_x64: (default) macbook, macos, imac ios_arm32: iphone32 ios_arm64: iphone, ipad, ios ios_x64: iphone_sim android_arm32: android_arm64: wasm32: zephyr_stm32f4_disco: ~/.konan/kotlin-native-macos-0.9.2/bin/konanc -list-targets macos_x64: (default) macbook, macos, imac ios_arm32: iphone32 ios_arm64: iphone, ipad, ios ios_x64: iphone_sim android_arm32: android_arm64: wasm32: zephyr_stm32f4_disco:

Slide 73

Slide 73 text

Compiling for RPi: MacOS? ~/.konan/kotlin-native-macos-0.9.2/bin/konanc -list-targets macos_x64: (default) macbook, macos, imac ios_arm32: iphone32 ios_arm64: iphone, ipad, ios ios_x64: iphone_sim android_arm32: android_arm64: wasm32: zephyr_stm32f4_disco: ~/.konan/kotlin-native-macos-0.9.2/bin/konanc -list-targets macos_x64: (default) macbook, macos, imac ios_arm32: iphone32 ios_arm64: iphone, ipad, ios ios_x64: iphone_sim android_arm32: android_arm64: wasm32: zephyr_stm32f4_disco:

Slide 74

Slide 74 text

Compiling for RPi: Docker https://github.com/JetBrains/kotlin-native/blob/master/tools/docker/Dockerfile docker run --rm -it -v $(pwd):/home/gradle -w /home/gradle joshskeen/kotlin-native root@9ed01dd64dcc:/home/gradle# ~/.konan/kotlin-native-linux-0.9.2/bin/konanc -list-targets linux_x64: (default) linux linux_arm32_hfp: raspberrypi linux_mips32: linux_mipsel32: android_arm32: android_arm64: wasm32: zephyr_stm32f4_disco: docker run --rm -it -v $(pwd):/home/gradle -w /home/gradle joshskeen/kotlin-native root@9ed01dd64dcc:/home/gradle# ~/.konan/kotlin-native-linux-0.9.2/bin/konanc -list-targets linux_x64: (default) linux linux_arm32_hfp: raspberrypi linux_mips32: linux_mipsel32: android_arm32: android_arm64: wasm32: zephyr_stm32f4_disco:

Slide 75

Slide 75 text

Compiling for RPi: Docker https://github.com/JetBrains/kotlin-native/blob/master/tools/docker/Dockerfile docker run --rm -it -v $(pwd):/home/gradle -w /home/gradle joshskeen/kotlin-native root@9ed01dd64dcc:/home/gradle# ~/.konan/kotlin-native-linux-0.9.2/bin/konanc -list-targets linux_x64: (default) linux linux_arm32_hfp: raspberrypi linux_mips32: linux_mipsel32: android_arm32: android_arm64: wasm32: zephyr_stm32f4_disco: docker run --rm -it -v $(pwd):/home/gradle -w /home/gradle joshskeen/kotlin-native root@9ed01dd64dcc:/home/gradle# ~/.konan/kotlin-native-linux-0.9.2/bin/konanc -list-targets linux_x64: (default) linux linux_arm32_hfp: raspberrypi linux_mips32: linux_mipsel32: android_arm32: android_arm64: wasm32: zephyr_stm32f4_disco:

Slide 76

Slide 76 text

NcursesExt.kt Make Noise

Slide 77

Slide 77 text

Why Kotlin/Native?

Slide 78

Slide 78 text

Next: Terminal Tedium https://github.com/mxmxmx/terminal_tedium/

Slide 79

Slide 79 text

Takeaways NcursesExt.kt • Kotlin Native opens the door to the C Layer with Interop • Minimal standard library • Supports multiple platforms, but C binary layer knows nothing about that!

Slide 80

Slide 80 text

K/N Wishlist • Better cross-compilation support (macOS) • Incremental compilation • Include docs in the API code view (like Kotlin on JVM) • Built-in disassembler & pseudocode view • “convert c to kotlin/native”

Slide 81

Slide 81 text

Resources • Hopper disassembler: https://www.hopperapp.com/ • Docker image: https://github.com/JetBrains/kotlin-native/blob/master/tools/ docker/Dockerfile • https://github.com/mutexkid/kquarium • https://github.com/mutexkid/kotlin-native-workshop • https://www.youtube.com/watch?v=443UNeGrFoM : ( How I program C, Eskil Steenberg )

Slide 82

Slide 82 text

Thanks! Josh Skeen | [email protected] | @mutexkid https://speakerdeck.com/mutexkid/native