在iOS上运行Racket CS

2021-01-21 10:19:57

几周前,我开始着手让Racket CS编译并在iOS上运行,在Matthew Flatt的大量指导下,我设法使其工作(有一些警告)。这些变更现已合并,因此我想在信息仍在脑海中的时候,我会写另一本指南。

您需要最新版本的Racket及其与ChezScheme关联的分支,该分支是为您的主机构建的,以便进行交叉编译。要构建它们,请克隆Racket存储库并按照构建说明进行操作。如果您使用默认的make目标,那么最终将在存储库中的racket /目录顶部放置一个主机Racket安装,并以Chez Schemeat的build / src / build / cs / c /构建。

接下来,您可以通过在racket / src /中创建一个新的build目录并通过指定自定义前缀(以便make install步骤不会覆盖主机的Racket安装)来配置交叉构建,从而对iOS的Racket CS进行交叉编译, iOS SDK的路径(或简称“ iPhoneOS”)以及主机Racket and Chez Scheme的路径。

mkdir球拍/ src / build-ios \&& cd球拍/ src / build-ios \&& ../configure \ --prefix =" $(pwd)/../../../racket-ios" \ --enable-macprefix \ --host = aarch64-apple-darwin \ --enable-ios = iPhoneOS \ --enable-racket =" $(pwd)/../../bin/racket" \ --enable-scheme =" $(pwd)/../build/cs/c" \&&使\&&进行安装

运行上述一系列命令后,您应该最终在源存储库内的racket-ios /上进行交叉编译的Racket安装。

我在“ Inside Racket”文档中添加了有关如何交叉编译Racketmodules的部分,因此请参考。简而言之,如果将以下模块保存在app.rkt下

/ path / to / racket / bin / racket \ --compile-any \ --compiled' compiled_host:tarm64osx' \ --cross \ --cross-compiler tarm64osx / path / to / racket / racket-ios / lib \ --config / path / to / racket / racket-ios / etc \ --collects / path / to / racket /球拍ios /收集\ -l- \ raco ctool --mods app.zo app.rkt

生成app.zo,这是一个二进制对象,其中包含该模块及其所有依赖项的交叉编译代码。

要在XCode项目中链接和使用Racket CS,请从racket-ios / include /复制racketcs.h,racketcsboot.h和chezscheme.h到项目的子目录中,然后将该子目录添加到“ Header Search”中项目的“构建设置”标签下的“路径”部分。

接下来,将来自racket-ios / lib的libracketcs.a,petite.boot,scheme.boot和racket.boot复制到项目的子目录vendor /中,并将vendor /目录拖放到XCode项目中。然后,指示XCode在“构建阶段”选项卡中将libracketcs.a和libiconv.tbd与您的代码链接。您必须使用“添加其他...”子菜单将libracketcs.a添加到您的项目中。

接下来,添加一个名为vendor.c的新C源文件,如果提示创建Swift的桥接头,则回答“是”。我倾向于将桥接头重命名为纯bridge.h,因为我不喜欢默认情况下XCode生成的名称。如果执行此操作,则必须在“构建设置”标签中更新“ Objective-C桥接标头”设置。在bridge.h中,将vendor.h包含在vendor.h中,然后为racket_init和echo添加定义

#ifndef vendor_h #define vendor_h #include< stdlib.h> int racket_init(const char *,const char *,const char *,const char *); void echo(const char *); #万一

#include< string.h> #include" chezscheme.h" #include" racketcs.h" #include" vendor.h" int racket_init(const char * petite_path,const char * scheme_path,const char * racket_path,const char * app_path){racket_boot_arguments_t ba; memset(& ba,0,sizeof(ba)); 。 boot1_path = petite_path; 。 boot2_path = scheme_path; 。 boot3_path =球拍路径; 。 exec_file =" example" ; racket_boot(& ba); racket_embedded_load_file(app_path,1); ptr mod = Scons(Sstring_to_symbol(" quote"),Scons(Sstring_to_symbol(" main"),Snil)); racket_dynamic_require(mod,Sfalse); Sdeactivate_thread();返回0; } void echo(const char * message){Sactivate_thread(); ptr mod = Scons(Sstring_to_symbol(" quote"),Scons(Sstring_to_symbol(" main"),Snil)); ptr echo_fn =疤痕(racket_dynamic_require(mod,Sstring_to_symbol(" echo"))); racket_apply(fn,Scons(Sstring(message),Snil)); Sdeactivate_thread(); }

请查看Inside Racket CS文档,以获取有关Racket CS嵌入界面的详细信息。 racket_init的要点在于,它将petite.boot,scheme.boot,racket.boot和app.zo的路径作为参数,以初始化Racket,然后加载app.zo模块,您可以在AppDelegate的应用程序中执行以下操作( _:didFinishLaunchingWithOptions :)方法:

让vendorPath = Bundle。 主要。 resourcePath!。 附加(" / vendor")让ok = racket_init(vendorPath。附加(" /petite.boot"),vendorPath。附加(" /scheme.boot") ,如果ok = 0 {打印("初始化失败),则将vendorPath。追加(" /racket.boot"),将vendorPath。追加(" /app.zo")) 球拍")出口(1)} 在设备上编译并运行项目,您应该看到“ Hello fromRacket!”。 在您的调试控制台中打印出来。 如果将vendor /复制到项目中而不是在拖放时创建“ folderreferences”,则代码签名可能会失败并出现一个模棱两可的错误。 避免对您的任何资源使用符号链接(例如,stuffin供应商/)。 这样做会使代码复制到设备失败,并出现“安全”错误,根本没有提到根本问题。