通过 LLVM 在 Android 上运行 Swift 代码

简介:

Swift 已经发布一年多了,苹果承诺将在 2015 年底开源 Swift。这是非常棒的一件事情,但是我们现在可以在 Android 设备上运行 Swift 吗?

Swift 编译器

这都是由 Chris Lattner 设计的,很容易就可以发现 Swift 的编译器是基于 LLVM 构建的。LLVM 是个编译器基础设施,利用了了一个可重定向编译器的有趣概念。

image

也就是说,不是生成特定架构的机器代码,LLVM 为一个虚拟机生成汇编代码,然后转换成中间代码,适配架构需要的实际代码。

模块化的设计非常的好,因为允许高度代码复用(前端和后端的共享优化)。更多关于 LLVM 的资料请看这里。

适配不同的机器

在这一点上,你可能会想:

如果 LLVM 已经够模块化,那么我们是否可以使用一个不同的后端,生成二进制代码,适配 OS X,iOS 或者是 Android?

假设是可以的,我们来看看如何实现。

手动构建 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++。事实上,print 函数并没有成为 print symbol ,但是成为了更复杂的 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!

是的,这个方法是可行的。

适配 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 个步骤:

  1. 让 Swift 编译器生成一些 LLVM-IR
  2. 使用 LLVM 从中间表示的代码生成 ARM ELF
  3. 使用 Android NDK 来生成一个二进制代码,连接到已生成的对象文件

1. 让 Swift 编译器生成一些 LLVM-IR

在之前的步骤中,当运行 swiftc hello.swift,Swift 编译器实际在干两件事情:

  1. 从 Swift 代码中生成 LLVM 中间表示代码
  2. 转换 IR 为一些 x86_64 机器代码,打包为一个 Mach-O 文件

这个实际上是非常常用的事例,所以编译器可以一次性做完这些。但是我们想要生成一些 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);}

最后,我们需要打包所有,变成一个共享库:

$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 应用中分享对象文件,然后运行:

image

总结

这非常有趣,但是并没有什么用:

一般来讲,NDK 只是对一小部分的应用有意义,所以情况的 Google 反对使用 NDK 编写整个 Android 应用。

而且,因为我们缺失 SwiftCore 库,所以有了一定的限制,只适用于一小部分的 Swift 子集。

最后,很重要的一点,这个示例已经放到了 GitHub。

文章转载自 开源中国社区[https://www.oschina.net]

相关文章
|
2月前
|
算法 数据处理 Android开发
掌握安卓性能优化的秘诀:电池寿命与运行效率的提升
【10月更文挑战第6天】 本文深入探讨了安卓应用开发中的性能优化技巧,重点分析了影响电池寿命和运行效率的关键因素,并提供了针对性的优化策略。通过代码优化、资源管理、后台任务处理等方法,开发者可以显著提升应用的续航能力和流畅度。同时,结合具体案例,展示了如何在实际开发中应用这些技巧,确保应用在各种场景下都能保持高效运行。本文旨在为安卓开发者提供实用的性能优化指导,助力其打造更优质的应用体验。
73 2
|
2月前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
Android远程连接和登录FTPS服务代码(commons.net库)
35 1
|
2月前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异:从代码到用户体验
【10月更文挑战第5天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。它们在技术架构、开发环境及用户体验上有着根本的不同。本文通过比较这两种平台的开发过程,揭示背后的设计理念和技术选择如何影响最终产品。我们将深入探讨各自平台的代码示例,理解开发者面临的挑战,以及这些差异如何塑造用户的日常体验。
|
2月前
|
Java Unix Linux
Android Studio中Terminal运行./gradlew clean build提示错误信息
遇到 `./gradlew clean build`命令执行出错时,首先应检查错误信息的具体内容,这通常会指向问题的根源。从权限、环境配置、依赖下载、版本兼容性到项目配置本身,逐一排查并应用相应的解决措施。记住,保持耐心,逐步解决问题,往往复杂问题都是由简单原因引起的。
376 2
|
3月前
|
存储 Java Android开发
🔥Android开发大神揭秘:从菜鸟到高手,你的代码为何总是慢人一步?💻
在Android开发中,每位开发者都渴望应用响应迅速、体验流畅。然而,代码执行缓慢却是常见问题。本文将跟随一位大神的脚步,剖析三大典型案例:主线程阻塞导致卡顿、内存泄漏引发性能下降及不合理布局引起的渲染问题,并提供优化方案。通过学习这些技巧,你将能够显著提升应用性能,从新手蜕变为高手。
38 2
|
4月前
|
开发工具 Android开发
解决Android运行出现NDK at /Library/Android/sdk/ndk-bundle did not have a source.properties file
解决Android运行出现NDK at /Library/Android/sdk/ndk-bundle did not have a source.properties file
195 4
解决Android运行出现NDK at /Library/Android/sdk/ndk-bundle did not have a source.properties file
|
4月前
|
JSON JavaScript 前端开发
Android调用Vue中的JavaScript代码
Android调用Vue中的JavaScript代码
58 3
|
4月前
|
Android开发 iOS开发
[ionic]解决运行Android、IOS出现Could not find the web assets directory
[ionic]解决运行Android、IOS出现Could not find the web assets directory
44 1
|
4月前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
很多文章都介绍了FTPClient如何连接ftp服务器,但却很少有人说如何连接一台开了SSL认证的ftp服务器,现在代码来了。
119 2
|
5月前
|
存储 Java Android开发
🔥Android开发大神揭秘:从菜鸟到高手,你的代码为何总是慢人一步?💻
【7月更文挑战第28天】在Android开发中,每位开发者都追求极致的用户体验。然而,“代码执行慢”的问题时常困扰着开发者。通过案例分析,我们可探索从新手到高手的成长路径。
45 3