通过 LLVM 在 Android 上运行 Swift 代码
Swift 已经发布一年多了,苹果承诺将在 2015 年底开源 Swift。这是非常棒的一件事情,但是我们现在可以在 Android 设备上运行 Swift 吗?
Swift 编译器
这都是由 Chris Lattner 设计的,很容易就可以发现 Swift 的编译器是基于 LLVM 构建的。LLVM 是个编译器基础设施,利用了了一个可重定向编译器的有趣概念。
也就是说,不是生成特定架构的机器代码,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 个步骤:
- 让 Swift 编译器生成一些 LLVM-IR
- 使用 LLVM 从中间表示的代码生成 ARM ELF
- 使用 Android NDK 来生成一个二进制代码,连接到已生成的对象文件
1. 让 Swift 编译器生成一些 LLVM-IR
在之前的步骤中,当运行 swiftc hello.swift,Swift 编译器实际在干两件事情:
- 从 Swift 代码中生成 LLVM 中间表示代码
- 转换 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 应用中分享对象文件,然后运行:
总结
这非常有趣,但是并没有什么用:
一般来讲,NDK 只是对一小部分的应用有意义,所以情况的 Google 反对使用 NDK 编写整个 Android 应用。 而且,因为我们缺失 SwiftCore 库,所以有了一定的限制,只适用于一小部分的 Swift 子集。
最后,很重要的一点,这个示例已经放到了 GitHub。
文章转载自 开源中国社区[https://www.oschina.net]

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
为什么说 Linux 桌面已经不重要了
Linux桌面系统是一个难用的集合。尽管已经有非常明显的证据显示,Linux桌面没有任何一丝机会让自己变成主流,那些为Linux辩护的人仍然扭着手说:“我们基本上已经成功了!……某种程度上…… ” 当然,确实可以说(而且我也已经这么说好些年了)Linux借助Android的身份已经稳坐了“桌面系统”冠军,但这恰恰证明了Linux在桌面操作系统领域中的彻底失败。 原因很简单:对于瓦莱丽来说,Linux系统从来没有足够简单和有用。 最适合瓦莱丽的操作系统 瓦莱丽是一位女士,25年来她每六个星期替我剪一次头发。她不是什么技术的早期使用者,而是当前主流大众的一个,而且如果瓦莱丽在用某项技术,那么基本上表明所有其他人都已经在用了。 瓦莱丽最近在试着从Windows电脑切换到MacBook。按照她的兴趣(主要是摄影),我曾经觉得一台Mac会是非常棒的选择。但是,她却在挣扎着想要搞明白怎么把她保存在希捷移动硬盘上的相片导入Mac好进行编辑,因为Mac不能正常识别她的移动硬盘(似乎是个普遍问题)。百思买客服的建议是,她需要格式化她的希捷硬盘来让Mac OS X兼容。如果她那么做的话,那她所有的数据也就...
- 下一篇
索尼部分 Xperia 设备开源,支持 Linux 内核
索尼正在试图说服Linux开发者Xperia设备在许多方面提供有趣的应用,他们正在主线Linux内核中添加了对Xperia设备的支持。索尼似乎是真正关心开源和硬件设备在开源当中作用的少数几家公司之一。但是,索尼没有兴趣在开源项目当中支持旧设备,他们的目标是让Linux内核支持较新的Xperia设备。 谷歌有一个名为Android Open Source Project(ASOP)的开源项目,这基本上是提供Android开源相关的所有东西,索尼目前正在和ASOP开源社区合作。并不是所有索尼设备都可 以开源,但已经有很多新的索尼设备开源,比如Xperia Z5, Xperia Z3+等等。索尼正在推动补丁到Linux内核,这大大增强了开发者对硬件的访问能力。 现在,开发者已经让APQ8064(Xperia Z)和MSM8974(Xperia Z1,Z2,Z3)基本上可以运行主流Linux内核,但是并非所有的功能都可用,开发者正在继续开发工作。 ====================================分割线================================文章转...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- CentOS关闭SELinux安全模块
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8