Upload
cocoaheads
View
82
Download
6
Embed Size (px)
Citation preview
Making friends with robots
1
Agenda
• Building hello-world for Android
• Building Fiber2D project for Android
• Swift + Java = <3
• Overview
2
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
Main header
• Installing curl, autoconf, automake, libtool, git + CMake (!)
• Getting Android NDK r13b (and later)
• Getting SwiftAndroid/libiconv-libicu-android
Building dependencies
4
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
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
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
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
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
adb shell LD_LIBRARY_PATH=/data/local/tmp \/data/local/tmp/hello
Hello world
10
Compiling Fiber2D
11
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
Compiling Fiber2D
Project structure
Fiber2D
Cpng CChipmunk2DSwiftMath
SwiftBGFX
Cbgfx
bgfx
14
Compiling Fiber2D
Project structure
Fiber2D
Cpng CChipmunk2DSwiftMath
SwiftBGFX
Cbgfx
bgfx
Legend: • C++ • C • Swift
15
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
Compiling Fiber2D
Notes: module maps
module CChipmunk2D { header "chipmunk_private.h" header "chipmunk.h" header "cpHastySpace.h" link "CChipmunk2D" export * }
17
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
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
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
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
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
Compiling Fiber2D
Notes: Swift name mangling
23
class Shape { func numberOfSides() -> Int { return 5 } }
_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si
JFYI: You can use swift-demangle
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
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
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
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
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
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
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
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
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
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
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
Compiling Fiber2D
Notes: executable file formats
35
• PE (Windows) • Mach-O (Mac/iOS) • ELF (Linux/Android)
Contain executable format, architecture type and program header
Compiling Fiber2D
Notes: executable file formats
36
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
Compiling Fiber2D
Deploying the app
• ant debug install • Run the app from Android launcher • Use logcat to debug (NSLog() instead of print())
38
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
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
Compiling Fiber2D
libxml/libcurl hackreadelf -d libFoundation.so
41
Compiling Fiber2D
libxml/libcurl hackreadelf -d libFoundation.so
42
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
Compiling Fiber2D
Results
44
Compiling Fiber2D
Results
45
Swift + Java = <3
46
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
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
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
Swift + Java
Calling from Swift into Java
Hint: create dummy XCode project and add «jni.h» to Bridging Header
50
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
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
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
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
What’s good
• Code once, run everywhere (really)
• Speed
• Swift itself
55
Try the future today!Okay, maybe not that exciting…
56
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
That’s it!
58