适用于iOS的Haskell交叉编译器
到目前为止,我们已经为Raspberry Pi构建了Haskell Cross Compiler,并且为Android构建了Haskell Cross Compiler。 为了解决这个问题,我们还将为iOS构建一个交叉编译器。
WWDC发出信号通知32位设备的末尾,最后32位设备是iPad(第四代)和iPhone 5 / iPhone 5C,我们将只构建64位交叉编译器。 请注意,Apple 在其他地方 将 arm64
称为 aarch64
。 这是很不幸的。
SDK和LLVM
Apple随Xcode一起提供了iOS SDK。 因此,需要AppStore的Xcode的最新副本。 由于Apple不随Xcode一起交付opt
和llc
,而且GHC当前要求llvm4提供opt
和llc
,因此我们也需要从LLVM发布下载网站获取副本。
工具链包装
Apple为xcrun
实用程序提供了针对我们所需工具的自动设置工具链。 但是,我们仍然需要提供目标前缀别名,以实现更好的自动工具互操作。 最初的设置工作归功于ghc-ios-scripts,我们将使用经过稍微修改的版本:
#!/ bin / bash
名称= $ {0 ## * /}
cmd = $ {name ## *-}
target = $ {name%-*}
案例$ name
*-阴谋)
fcommon =“-builddir = dist / $ {target}”
fcompile =“ --with-ghc = $ {target} -ghc”
fcompile + =“ --with-ghc-pkg = $ {target} -ghc-pkg”
fcompile + =“ --with-gcc = $ {target} -clang”
fcompile + =“ --with-ld = $ {target} -ld”
fcompile + =“ --hsc2hs-options =-交叉编译”
fconfig =“-disable-shared --configure-option =-host = $ {target}”
案例$ 1
configure | install)flags =“ $ {fcommon} $ {fcompile} $ {fconfig}” ;;;
build)flags =“ $ {fcommon} $ {fcompile}” ;;
list | info | update)flags =“” ;;
“”)flags =“” ;;
*)标志= $ fcommon ;;
埃萨克
;;
aarch64-apple-ios-clang | aarch64-apple-ios-ld)
flags =“-sdk iphoneos $ {cmd} -arch arm64”
cmd =“ xcrun”
;;
aarch64-apple-ios- * | aarch64-apple-ios- *)
flags =“-sdk iphoneos $ {cmd}”
cmd =“ xcrun”
;;
x86_64-apple-ios-clang | x86_64-apple-ios-ld)
flags =“-sdk iphonesimulator $ {cmd} -arch x86_64”
cmd =“ xcrun”
;;
x86_64-apple-ios- *)
flags =“-sdk iphonesimulator $ {cmd}”
cmd =“ xcrun”
;;
#默认
* -nm | * -ar | * -ranlib);;
*)echo“未知命令:$ {0 ## * /}”>&2; 1号出口;;
埃萨克
exec $ cmd $ flags“ $ @”
通用wrapper
可以从zw3rk / toolchain-wrapper存储库中获得。 它不仅包含上面的iOS部分,还包含Raspberry Pi和Android的部分,因此可用于所有三个平台。
同样,我们需要创建目标前缀别名:
针对“ aarch64-apple-ios x86_64-apple-ios”中的目标; 做
用于“ clang ld ld.gold nm ar ranlib cabal”中的命令; 做
ln -s包装程序$ target- $ command
完成
完成
zw3rk / toolchain-wrapper存储库中的引导脚本将生成Android,iOS和Raspberry Pi的所有别名。
先决条件
使用PATH
的工具链包装器和别名,构建ghc
所需的全部就是ghc
和cabal
。 使用自制程序获取ghc
和llvm-3.7
的最新副本应该很容易
酿造安装ghc llvm@3.7
llvm@3.7
将安装opt-3.7
和llc-3.7
,这是引导编译器所需的。 但是,homebrew安装的ghc
不带llc
和opt
的版本后缀。 只需更换即可解决
(“ LLVM llc命令”,“ llc”),
(“ LLVM opt命令”,“ opt”)
与
(“ LLVM llc命令”,“ llc-3.7”),
(“ LLVM opt命令”,“ opt-3.7”)
在settings
文件中,如果是来自自制软件的ghc
,则位于
/usr/local/opt/ghc/lib/ghc-8.0.2/settings
我们还需要alex
和happy
,可以与cabal
一起安装
阴谋集团安装亚历克斯快乐
正如我们为Raspberry Pi和Android所做的那样,我们还需要针对iOS的libffi
git clone https://github.com/zw3rk/libffi.git
cd libffi
./autogen.sh
CC =“ aarch64-apple-ios-clang” \
CXX =“ aarch64-apple-ios-clang” \
。/配置 \
--prefix = /路径/到/ libffi / aarch64-apple-ios \
--host = aarch64-apple-ios \
--enable-static =是--enable-shared =是
进行&&进行安装
git clean -f -x -d
./autogen.sh
CC =“ x86_64-apple-ios-clang” \
CXX =“ x86_64-apple-ios-clang” \
。/配置 \
--prefix = /路径/到/ libffi / x86_64-apple-ios \
--host = x86_64-apple-ios \
--enable-static =是--enable-shared =是
进行&&进行安装
注意:我们需要使用 zw3rk / libffi 分支来提供 -ios
支持,直到将 libffi / libffi#307 拉取请求合并到官方libffi存储库中为止。 或者一个新的autoconf版本(最新的2.69版本于2012年发布)已被切断并广泛可用。
建筑GHC
使用llvm4, alex
opt
和llc
和PATH
happy
导出PATH = $ HOME / .cabal / bin:$ PATH
导出PATH = / path / to / llvm4 / bin:$ PATH
导出PATH = / path / to / wrapped-toolchain:$ PATH
从打补丁的my-ghc
分支构建GHC
git clone-递归git://git.haskell.org/ghc.git
光盘ghc
git remote add zw3rk https://github.com/zw3rk/ghc.git
git fetch zw3rk
git checkout zw3rk / my-ghc -b my-ghc
git子模块更新--init --recursive
应该只需要
#设置路径
导出PREFIX = / my / prefix
导出LIBFFI = /path/to/libffi
针对“ aarch64-apple-ios x86_64-apple-ios”中的目标; 做
#清理构建树
git clean -x -f -d
#启动构建系统
./启动
#配置以$ target为目标的GHC
./configure --target = $ target \
--prefix = $ PREFIX \
--disable-large-address-space \
--with-system-libffi \
--with-ffi-includes = $ LIBFFI / $ target/include \
/include \
--with-ffi-libraries = $ LIBFFI / $ target / lib
#创建一个mk / build.mk并将BuildFlavour设置为quick-cross
sed -E“ s / ^#(BuildFlavour [] + = quick-cross)$ / \ 1 /” \
mk / build.mk.sample> mk / build.mk
#编译并安装ghc
进行-j &&进行安装
完成
60-120分钟后,根据您的硬件,在$PREFIX/bin
应显示和闪亮的新aarch64-apple-ios-ghc
和x86_64-apple-ios-ghc
。
编译Hello World
与我们为Android构建Hello World库的方式相似,我们将构建相同的Hello World库并将其包装到iOS应用程序中。
具有以下代码的Lib.hs
库
模块库在哪里
导入Foreign.C(CString,newCString)
-| 将haskell函数@ chello @导出为@ hello @。
国外出口ccall“你好” chello :: IO CString
-| 小包装器返回CString
chello = newCString你好
-| 原始的haskell功能。
你好=“你好,来自Haskell”
导出chello
C函数,我们将在iOS应用程序中调用该函数以从Haskell获取C字符串。 没什么令人兴奋的,但是它将演示基本的互操作性。
使用Swift和Xcode创建一个简单的iOS 单视图应用程序应该为我们的Hello World应用程序提供必要的应用程序模板。 使用Objective-C将使调用 chello
更加容易。 但是,由于苹果现在已经推动swift一段时间了,我们将使用swift。
我们将需要一个桥接标头,以使C原型迅速发展。 最简单的方法是将一个新的目标c文件添加到项目中,例如tmp.m
,这反过来将导致Xcode询问是否应创建桥接标头。 是,然后删除tmp.h
将所需的原型添加到helloworld-Bridging-Header.h
extern void hs_init(int * argc,char ** argv []);
extern char * hello();
在AppDelegate.swift
,当应用程序完成启动时,我们将调用hs_init
:
func application(_ application:UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey:Any]?)->布尔{
//应用程序启动后进行自定义的替代点。
hs_init(nil,nil)
返回真
}
将Label
添加到Main.storyboard
,将IBOutlet
连接到ViewController.swift
,并将其text
属性设置为hello
的结果
类ViewController:UIViewController {
@IBOutlet弱var标签:UILabel!
覆盖func viewDidLoad(){
super.viewDidLoad()
label.text =字符串(cString:hello())
}
}
应该足够了。 在实际设备上构建和运行应用程序之前,我们需要构建Haskell库并告诉Xcode对其进行链接。
编译成用于aarch64
(设备)和x86_64
(模拟器)的静态库
aarch64-apple-ios-ghc -odir arm64 -hidir arm64 \
-lffi -L /路径/到/ libffi / aarch64-apple-ios / lib \
-staticlib -o hs-libs / arm64 / libhs.a hs / Lib.hs
x86_64-apple-ios-ghc -odr x86_64 -hidir x86_64 \
-lffi -L /路径/到/ libffi / x86_64-apple-ios / lib \
-staticlib -o hs-libs / x86_64 / libhs.a hs / Lib.hs
并将它们转变为结合了两种架构的通用库
lipo -create -output hs-libs / libhs.a \
hs-libs / arm64 / libhs.a hs-libs / x86_64 / libhs.a
应该在hs-libs
文件夹中提供libhs.a
静态库。 在Xcode中链接它需要将它与libiconv.tbd一起添加到Xcode的helloworld项目的Build Phases选项卡的Link Binary With Libraries部分中。 由于我们不能使用GHC仅构建位码库,因此我们还需要在“ 构建设置”选项卡中将“ 启用位码 ”设置为No。
最终在设备上运行该应用程序应向我们展示