59
Making friends with robots 1

Андрей Володин — Как подружиться с роботом

Embed Size (px)

Citation preview

Page 1: Андрей Володин — Как подружиться с роботом

Making friends with robots

1

Page 2: Андрей Володин — Как подружиться с роботом

Agenda

• Building hello-world for Android

• Building Fiber2D project for Android

• Swift + Java = <3

• Overview

2

Page 3: Андрей Володин — Как подружиться с роботом

Hello world

• Linux environment

• Ubuntu 15.10 is the only officially supported

• 16.04 is used in Fiber2D

• NDK (r13 and later)

• Android API Version 21+

• Android device (meh…) with remote debugging

Prerequisites

3

Page 4: Андрей Володин — Как подружиться с роботом

Main header

• Installing curl, autoconf, automake, libtool, git + CMake (!)

• Getting Android NDK r13b (and later)

• Getting SwiftAndroid/libiconv-libicu-android

Building dependencies

4

Page 5: Андрей Володин — Как подружиться с роботом

Hello world

Building Swift compiler

$ utils/build-script -R \ --android \ --android-ndk $ANDROID_NDK_HOME \ --android-api-level 21 \ --android-icu-uc $ANDROID_LIBICONV/armeabi-v7a \ --android-icu-uc-include $ANDROID_LIBICONV/armeabi-v7a/icu/source/common \ --android-icu-i18n $ANDROID_LIBICONV/armeabi-v7a \ --android-icu-i18n-include $ANDROID_LIBICONV/armeabi-v7a/icu/source/i18n/

Export $ANDROID_NDK_HOME and $ANDROID_LIBICONV

5

Page 6: Андрей Володин — Как подружиться с роботом

Hello world

Before building the executable

sudo ln -s \ $ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ld.gold \ /usr/bin/armv7-none-linux-androideabi-ld.gold

Let Swift compiler find the correct linker (gold)

From docs:

Also needed:sudo ln -s \ $ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ld.gold \ /usr/bin/armv7-none-linux-android-ld.gold

6

Page 7: Андрей Володин — Как подружиться с роботом

Hello world

Notes: compiler flags

7

• -l[foo] : Link to libfoo[.so][.dylib]XCode: Other Link Flags

• -L[/path/foo] : Search path for linked libraries XCode: Library Search Paths

• -I[/path/foo] : Search path for headers (C/C++) or .swiftmodule (Swift) XCode: Header Search Paths/Import Paths

• -D[FLAG] : Preprocessor flag (#if FLAG) XCode: Preprocessor macros

Page 8: Андрей Володин — Как подружиться с роботом

Hello world

Building the executable

$ build/Ninja-ReleaseAssert/swift-linux-x86_64/bin/swiftc \ -target armv7-none-linux-androideabi \ -sdk $ANDROID_NDK_HOME/platforms/android-21/arch-arm \ -L $ANDROID_NDK_HOME/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a \ -L $ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9 \ hello.swift

8

Page 9: Андрей Володин — Как подружиться с роботом

Hello world

Deploying the executableStandalone Swift executable requires this to run:

• libswiftCore.so • libswiftGlibc.so • libswiftRemoteMirror.so • libswiftSwiftOnoneSupport.so

and this:• libc++_shared.so

finally, push the executable:

adb push hello /data/local/tmp

9

Page 10: Андрей Володин — Как подружиться с роботом

adb shell LD_LIBRARY_PATH=/data/local/tmp \/data/local/tmp/hello

Hello world

10

Page 11: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

11

Page 12: Андрей Володин — Как подружиться с роботом
Page 13: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

The goal

• Swift Package Manager at the heart of build process • Compile Fiber2D framework • Use it in the real world (.apk) Android application

13

Page 14: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Project structure

Fiber2D

Cpng CChipmunk2DSwiftMath

SwiftBGFX

Cbgfx

bgfx

14

Page 15: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Project structure

Fiber2D

Cpng CChipmunk2DSwiftMath

SwiftBGFX

Cbgfx

bgfx

Legend: • C++ • C • Swift

15

Page 16: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

SwiftPM limitations

• Does not allow mixing languages within one target • No Bridging-Headers • No custom source code layouts • No custom local config (-l/-L/-I/-D flags) • No custom scripts/makefiles

16

Page 17: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Notes: module maps

module CChipmunk2D { header "chipmunk_private.h" header "chipmunk.h" header "cpHastySpace.h" link "CChipmunk2D" export * }

17

Page 18: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

NDK• Android apps are Java-based • Android NDK lets you implement parts of your app using

native-code languages such as C and C++ • Java to C calls are handled by Java Native Interface (JNI)

public class MyActivity extends Activity {  /**  * Native method implemented in C/C++  */  public native void computeFoo();}

18

Page 19: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

NDK

• NDK project must contain jni folder at its root • ndk-build command recursively traverses it and

compiles every Android.mk it can find • Compiled binaries are stored in libs/<ARCH> folders

19

Page 20: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

SDL

• SDL - low-level abstraction layer for audio, keyboard, mouse, joystick, and graphics hardware

• Provides Android template project with JNI shim • Wants you to add your C/C++ files to its main lib’s

Android.mk:# Add your application source files here... LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c

20

Page 21: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

What SDL does not tell us?

#define main SDL_main // And then extern C_LINKAGE int SDL_main(int argc, char *argv[]);

• SDL secretly redefines main function with its own macro on certain platforms:

• We can’t use it in Swift

21

Page 22: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

What we want to do?

• Compile SDL and boilerplate code only once • Compile our project as dynamic library • Put it into Android project • Load it during the runtime • Make SDL call our SDL_main implementation

22

Page 23: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Notes: Swift name mangling

23

class Shape { func numberOfSides() -> Int { return 5 } }

_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si

JFYI: You can use swift-demangle

Page 24: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Implementing SDL_main

• Make sure you don’t have main.swift • Use @_silgen_name or @_cdecl attributes to overcome

Swift symbol mangling

@_cdecl("SDL_main")public func SDL_main(argc: Int32, argv: OpaquePointer) -> Int32 { …}

• Function must be top-level, public and can’t throw

24

Page 25: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Creating the build script

• To build SwiftPM package you do swift build • swift != swiftc • Use -Xlinker, -Xcc and -Xswiftc to forward flags • Declarations with spaces requires multiple -X* flags, i.e.: -Xlinker -framework -Xlinker AppKit

25

Page 26: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Creating the build script

First, add flags that we used to build hello-world:-Xswiftc -target -Xswiftc armv7-none-linux-androideabi \ -Xswiftc -sdk -Xswiftc $ANDROID_NDK_HOME/platforms/android-21/arch-arm \ -Xlinker -L$ANDROID_NDK_HOME/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a \ -Xlinker -L$ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/

26

Page 27: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Creating the build script

Add Fiber2D include paths:-Xswiftc -I../../../../.build/debug \-Xswiftc -I../../../../.build/checkouts/Cpng-8308068493617107876/Cpng/include \-Xswiftc -I../../../../.build/checkouts/CChipmunk2D--4671306517288557973/CChipmunk2D/include \-Xswiftc -I../../../../external/SwiftBGFX/.build/debug \-Xswiftc -I../android-project/jni/SDL2-2.0.5/include \

Android-specific ones:-Xswiftc -I$ANDROID_NDK_HOME/sources/android/native_app_glue/ \-Xcc -I$ANDROID_NDK_HOME/platforms/android-21/arch-arm/usr/include \

And some for Foundation:-Xswiftc -I$ANDROID_SWIFT_SOURCE/build/Ninja-ReleaseAssert/foundation-linux-x86_64/Foundation \-Xswiftc -I$ANDROID_SWIFT_SOURCE/swift-corelibs-foundation \-Xswiftc -I$ANDROID_SWIFT_SOURCE/swift-corelibs-foundation/closure \

27

Page 28: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Creating the build script

Link to libraries:-Xlinker -lgcc -Xlinker -lc++ -Xlinker -ldispatch \-Xlinker -lFoundation -Xlinker -latomic -Xlinker -lFiber2D \-Xlinker -licui18n -Xlinker -licuuc \

Add library search paths for libDispatch and libFoundation:-Xlinker -L/usr/local/lib/swift/android/ \-Xlinker -L$ANDROID_SWIFT_SOURCE/build/Ninja-ReleaseAssert/foundation-linux-x86_64/Foundation/ \

Add library search paths for Android stuff:-Xlinker -L$ANDROID_NDK_HOME/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a \-Xlinker -L$ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/ \-Xlinker -L$ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/lib/armv7-a/ \-Xlinker -L$ANDROID_LIBICONV/armeabi-v7a \

And for Fiber2D:-Xlinker -L../../../../.build/debug \-Xlinker -L../../../../external/SwiftBGFX/.build/debug \

28

Page 29: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Creating the build script

Compile libSwiftMath without SIMD:-Xswiftc -DNOSIMD \

Add cross-compilation flags:-Xcc -B -Xcc $ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ \-Xcc --sysroot=$ANDROID_NDK_HOME/platforms/android-21/arch-arm/ \-Xlinker --sysroot=$ANDROID_NDK_HOME/platforms/android-21/arch-arm/ # This does not work for now and requires a hack for clang

29

Page 30: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Build script:cd f2dc swift build \ -Xswiftc -I$ANDROID_NDK_HOME/sources/android/native_app_glue/ \ -Xcc -I$ANDROID_NDK_HOME/platforms/android-21/arch-arm/usr/include \ -Xswiftc -I../../../../.build/debug \ -Xswiftc -I../../../../.build/checkouts/Cpng-8308068493617107876/Cpng/include \ -Xswiftc -I../../../../.build/checkouts/CChipmunk2D--4671306517288557973/CChipmunk2D/include \ -Xswiftc -I../../../../external/SwiftBGFX/.build/debug \ -Xswiftc -I../android-project/jni/SDL2-2.0.5/include \ -Xswiftc -I$ANDROID_SWIFT_SOURCE/build/Ninja-ReleaseAssert/foundation-linux-x86_64/Foundation \ -Xswiftc -I$ANDROID_SWIFT_SOURCE/swift-corelibs-foundation \ -Xswiftc -I$ANDROID_SWIFT_SOURCE/swift-corelibs-foundation/closure \ -Xswiftc -target -Xswiftc armv7-none-linux-androideabi \ -Xswiftc -sdk -Xswiftc $ANDROID_NDK_HOME/platforms/android-21/arch-arm \ -Xswiftc -DNOSIMD \ -Xcc -target -Xcc armv7-none-linux-androideabi \ -Xcc -B -Xcc $ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ \ -Xcc --sysroot=$ANDROID_NDK_HOME/platforms/android-21/arch-arm/ \ -Xlinker -L/usr/local/lib/swift/android/ \ -Xlinker -L$ANDROID_SWIFT_SOURCE/build/Ninja-ReleaseAssert/foundation-linux-x86_64/Foundation/ \ -Xlinker -L$ANDROID_NDK_HOME/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a \ -Xlinker -L$ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/ \ -Xlinker -L$ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/lib/armv7-a/ \ -Xlinker -L$ANDROID_LIBICONV/armeabi-v7a \ -Xlinker -L../../../../.build/debug \ -Xlinker -L../../../../external/SwiftBGFX/.build/debug \ -Xlinker -L../android-project/libs/armeabi-v7a \ -Xlinker -lgcc -Xlinker -lc++ -Xlinker -ldispatch \ -Xlinker -lFoundation -Xlinker -latomic -Xlinker -lFiber2D \ -Xlinker -licui18n -Xlinker -licuuc \ -Xlinker --sysroot=$ANDROID_NDK_HOME/platforms/android-21/arch-arm/ # This does not work for now and requires a hack for clang

30

Page 31: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

clang hack

#!/bin/bash

# This is a proxy hack until it gets fixed in SPM # You have to add chmod +x to this file /usr/bin/@clang -target armv7-none-linux-androideabi \ -B $(ANDROID_NDK_HOME)/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ \ —sysroot=$(ANDROID_NDK_HOME)/platforms/android-21/arch-arm/ $*

• Linking C/C++ with Swift is done by clang • SwiftPM does not pass platform flags for cross-

compilation (-B/—sysroot/-target) to clang during linkage stage (even when passed via -Xlinker)

• So we had to do a proxy clang script • mv /usr/bin/clang /usr/bin/@clang • chmod +x /usr/bin/clang

31

Page 32: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Compiling libSDL2 and libmain

• Put freshly compiled libf2dc.so lib to jni/dependncies

• Add -lf2dc -Ljni/dependencies to main lib’s Android.mk

• Run ndk-build in the project root

32

Page 33: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

SDL hack + pure NDK

• As we wanted to use custom renderer we had to hack in SDL to prevent it from managing the window’s context

• We create CAndroidAppGlue package to wrap pure NDK API into Swift Module

• It is possible to create pure Swift + Java app

33

Page 34: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Putting Swift binaries into .apk

libCbgfx.so libCChipmunk2D.so

libCpng.so libcurlll.so

libdispatch.so libf2dc.so

libFiber2D.so libFoundation.so libscudata.so libscui18n.so libscuuc.so

libSwiftBGFX.so libswiftCore.so libswiftGlibc.so

libswiftRemoteMirror.so libswiftSwiftOnoneSupport.so

libxml222.so libc++_shared.so

Copy these libraries to libs/armeabi-v7a:

34

Page 35: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Notes: executable file formats

35

• PE (Windows) • Mach-O (Mac/iOS) • ELF (Linux/Android)

Contain executable format, architecture type and program header

Page 36: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Notes: executable file formats

36

Page 37: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Putting Swift binaries into .apk

Note that Android 5 manages sub-dependencies on its own, but on Android 4.4 you have to put all libs to

System.load section of SDLActivity.java

protected String[] getLibraries() { return new String[] { "SDL2", "main" }; }

// Load the .so public void loadLibraries() { for (String lib : getLibraries()) { System.loadLibrary(lib); } }

37

Page 38: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Deploying the app

• ant debug install • Run the app from Android launcher • Use logcat to debug (NSLog() instead of print())

38

Page 39: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

libicu hack

• Android linker ignores LD_LIBRARY_PATH when loading with System.loadLibrary()

• Android is shipped with its own outdated version of libicu

• We have to ship our own libraries • And hack into compiled binaries…

39

Page 40: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

libicu hack

• We have to fool Android linker to load our version of libicu

• Conventional hack: rename libi***.so to libs***.so

• Rename binary links as well: rpl -R -e libicu libscu lib*.so

• Note: keep the strings you change the same length

40

Page 41: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

libxml/libcurl hackreadelf -d libFoundation.so

41

Page 42: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

libxml/libcurl hackreadelf -d libFoundation.so

42

Page 43: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

libxml/libcurl hack• Android can’t load libraries via symlinks or custom

names • We have to ship our own versions of libxml and libcurl

• You can find initial binaries in $ANDROID_NDK_HOME/sysroot/src/libxml2/.libs

• Rename: libxml2.so.2 to libxml222.solibcurl.so.5 to libcurlll.so

43

Page 44: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Results

44

Page 45: Андрей Володин — Как подружиться с роботом

Compiling Fiber2D

Results

45

Page 46: Андрей Володин — Как подружиться с роботом

Swift + Java = <3

46

Page 47: Андрей Володин — Как подружиться с роботом

Swift + Java

Calling from Java into C

Compile your C code as dynamic library, load it at runtime

System.loadLibrary("fooLib");

Define method with native keyword

public native Int intFromJNI();

Java side:

47

Page 48: Андрей Володин — Как подружиться с роботом

Swift + Java

Calling from Java into C

C side:JNIEXPORT jint JNICALL Java_com_example_app_HelloJNI_intFromJNI( JNIEnv* env, jobject thiz )

• JNI naming rule:Java_[package_name]_[class_name]_[exported_function_name]

• JNIEnv* is the pointer to the VM, and jobject is a pointer to the implicit this object passed from the Java side.

48

Page 49: Андрей Володин — Как подружиться с роботом

Swift + Java

Calling from Java into Swift• Compile your Swift code as shared object library (.so),

load it at runtime • Use @_silgen_name or @_cdecl to create JNI

declaration • Call your top-level public Swift func whatever you want

internally

@_silgen_name(«Java_com_example_app__OurHello_intFromJNI") public func fooWhatever(env: UnsafeMutablePointer<JNIEnv>, jobj: jobject, x: jint, y: jint) -> jint { return x + y }

49

Page 50: Андрей Володин — Как подружиться с роботом

Swift + Java

Calling from Swift into Java

Hint: create dummy XCode project and add «jni.h» to Bridging Header

50

Page 51: Андрей Володин — Как подружиться с роботом

Swift + Java

Calling from Swift into Java

private func foo(env: UnsafeMutablePointer<JNIEnv>, jobj: jobject, value: jint) { let jni = env.memory.memory let methodName = "foo" let methodSignature = "(I)V" let javaClass = jni.GetObjectClass(env, jobj) let methodID = jni.GetMethodID(env, javaClass, methodName, methodSignature) let valueAsJValue = jvalue(i: value) var methodArgs: [jvalue] = [valueAsJValue] jni.CallVoidMethodA(env, jobj, methodID, &methodArgs) }

51

Page 52: Андрей Володин — Как подружиться с роботом

Swift + Java

Decoding calling from Swift into Java

… func foo(env: UnsafeMutablePointer<JNIEnv> …

env is passed by Java.

env JNIEnv JNINativeInterface

let jni = env.memory.memory

52

Page 53: Андрей Володин — Как подружиться с роботом

Swift + Java

Decoding calling from Swift into Java

let methodName = "foo" let methodSignature = "(I)V" let javaClass = jni.GetObjectClass(env, jobj) let methodID = jni.GetMethodID(env, javaClass, methodName, methodSignature)

Next we have to get an object and a method signature to be called according to Oracle JNI Docs

(I)V means that method receives one Int argument and returns Void

53

Page 54: Андрей Володин — Как подружиться с роботом

Swift + Java

Decoding calling from Swift into Java

• Actual calling should be done with Call(ReturnType)MethodA

• All args must be wrapped into jvalue struct

let valueAsJValue = jvalue(i: value) var methodArgs: [jvalue] = [valueAsJValue] jni.CallVoidMethodA(env, jobj, methodID, &methodArgs)

54

Page 55: Андрей Володин — Как подружиться с роботом

What’s good

• Code once, run everywhere (really)

• Speed

• Swift itself

55

Page 56: Андрей Володин — Как подружиться с роботом

Try the future today!Okay, maybe not that exciting…

56

Page 57: Андрей Володин — Как подружиться с роботом

What’s bad not ideal• App size overhead

• No macOS cross-compiling

• Compilation is not easy

• Native stuff (i.e. In-Apps) is not easy as well

• GPU is bad for dirty-rectangles type of apps

• MIPS, x86 and simulators are not supported yet

• No SIMD/NEON support

• No lldb (yet)

57

Page 58: Андрей Володин — Как подружиться с роботом

That’s it!

58