通過 LLVM 在 Android 上運行 Swift 代碼
Swift 已經發布一年多了,蘋果承諾將在 2015 年底開源 Swift。這是非常棒的一件事情,但是我們現在可以在 Android 設備上運行 Swift 嗎?
Swift 編譯器
這都是由 Chris Lattner 設計的,很容易就可以發現 Swift 的編譯器是基于 LLVM 構建的。LLVM 是個編譯器基礎設施,利用了了一個可重定向編譯器的有趣概念。
也就是說,不是生成特定架構的機器代碼,LLVM 為一個虛擬機生成匯編代碼,然后轉換成中間代碼,適配架構需要的實際代碼。
模塊化的設計非常的好,因為允許高度代碼復用(前端和后端的共享優化)。更多關于 LLVM 的資料請看這里。
適配不同的機器
在這一點上,你可能會想:
如果 LLVM 已經夠模塊化,那么我們是否可以使用一個不同的后端,生成二進制代碼,適配 OS X,iOS 或者是 Android?
</blockquote>假設是可以的,我們來看看如何實現。
手動構建 Swift 代碼
如果使用 Xcode,系統會自動完成這些。我們現在需要手動編譯和連接一個簡單的 Swift "Hello world" :
// hello.swiftprint("Hello, world!");構建對象文件:
$ $SDK/usr/bin/swiftc -emit-object hello.swift
hello.o里面到底有什么:$ nm hello_swift.o U __TFSSCfMSSFT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS U __TFSs27_allocateUninitializedArrayurFBwTGSaq__Bp_ U __TFSs5printFTGSaP__9separatorSS10terminatorSS_T_ U __TIFSs5printFTGSaP__9separatorSS10terminatorSS_T_A0_ U __TIFSs5printFTGSaP__9separatorSS10terminatorSS_T_A1_ 0000000000000140 S __TMLP_ 0000000000000100 S __TMaP_ U __TMdSS U __TZvOSs7Process11_unsafeArgvGVSs20UnsafeMutablePointerGS0_VSs4Int8__ U __TZvOSs7Process5_argcVSs5Int32 U _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func6 U _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token6 0000000000000000 T _main U _swift_getExistentialTypeMetadata U _swift_once看吧,這非常有趣。Swift mangles symbols 看起來明顯有點像 C++。事實上,
_printsymbol ,但是成為了更復雜的 symbol 的__TFSs5printFTGSaP__9separatorSS10terminatorSS_T_列表。同時也要求其他 symbols,主要是為了處理字符串轉換和內存處理。
無論如何,所有這些 symbols 已經在
libswiftCore.dylib定義,也出現在$SDK。我們現在要把這些信息給 linker:$ ld -arch x86_64 -o hello hello.o -L$SDK/usr/lib/swift/macosx -lSystem -lswiftCore$ DYLD_LIBRARY_PATH=$SDK/usr/lib/swift/macosx ./hello Hello, world!</pre>
是的,這個方法是可行的。
適配 Android
現在最大的問題是 SwiftCore 庫缺失。現在蘋果已經為 iOS,OS X 和 Watch OS 都提供了一個。但是,很明顯,并沒有提供 Android 版本。
但是,不是所有 Swift 代碼都要求 SwiftCore 庫,跟不是所有 C++ 代碼都要求 STL 一樣。所以只要使用 Swift 的子集,不需要 SwiftCore 的那部分,這問題就算解決了。
為了演示,我們先來一個簡單的:
// add.swiftfunc addTwoNumbers(first: UInt8, second: UInt8) -> UInt8 { return first + second}所以這過程基本分為 3 個步驟:
讓 Swift 編譯器生成一些 LLVM-IR
</li>使用 LLVM 從中間表示的代碼生成 ARM ELF
</li>使用 Android NDK 來生成一個二進制代碼,連接到已生成的對象文件
</li> </ol>1. 讓 Swift 編譯器生成一些 LLVM-IR
在之前的步驟中,當運行
swiftc hello.swift,Swift 編譯器實際在干兩件事情:
從 Swift 代碼中生成 LLVM 中間表示代碼
</li>轉換 IR 為一些 x86_64 機器代碼,打包為一個 Mach-O 文件
</li> </ol>這個實際上是非常常用的事例,所以編譯器可以一次性做完這些。但是我們想要生成一些 ARM ELF 文件 (在 Android 上使用的二進制格式文件)。
$SDK/usr/bin/swiftc -parse-as-library # We don't need a "main" function -target armv7-apple-ios9.0 -emit-ir add.swift | grep -v "^!" # Filter-out iOS metadata > add.ll注意:我們需要添加 "grep" 過濾器來移除一些 iOS 特定的元數據(Swift 編譯器加進去的) 。
2. 從 LLVM-IR 中生成一個對象文件
在這點上,我們需要 Android NDK。非常幸運的是已經包括了一個 LLVM 工具鏈,我們可以利用
llc(LLVM static compiler) :$NDK/toolchains/llvm-3.5/prebuilt/darwin-x86_64/bin/llc -mtriple=armv7-none-linux-androideabi -filetype=obj add.ll非常棒,所以我們已經構建了一個 ARM ELF 對象文件!
3. 打包一個 Android 應用的對象文件
我們需要從 Java 中調用它,所以需要一個 JNI bridge。這使用 C 來編寫非常簡單:
// jni-bridge.c// Let's work around Swift symbol mangling#define SWIFT_ADD _TF3add13addTwoNumbersFTVSs5UInt86secondS0__S0_uint8_t SWIFT_ADD(uint8_t, uint8_t);jstring jni_bridge(JNIEnv * env, jobject thiz ) { uint8_t a = 123; uint8_t b = 45; uint8_t c = SWIFT_ADD(a,b);char result[255]; sprintf(result, "The result is %d", c);
return (*env)->NewStringUTF(env, result);}</pre>
最后,我們需要打包所有,變成一個共享庫:
$NDK_GCC/bin/arm-linux-androideabi-ld add.o jni_bridge.o -shared # Build a shared library -lc # We'll need the libc -L$NDK/platforms/android-13/arch-arm/usr/lib就是這樣!我們需要打包,在一個 Android 應用中分享對象文件,然后運行:
![]()
總結
這非常有趣,但是并沒有什么用:
一般來講,NDK 只是對一小部分的應用有意義,所以情況的 Google 反對使用 NDK 編寫整個 Android 應用。
</li>而且,因為我們缺失 SwiftCore 庫,所以有了一定的限制,只適用于一小部分的 Swift 子集。
</li> </ul>最后,很重要的一點,這個示例已經放到了 GitHub。
via romain.goyet.com
來自:http://www.oschina.net/news/67082/running-swift-code-on-android本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!