在 iOS 和 Android 上运行 Go 代码

在本教程中 , 我们将构建一个简单的 Go 包 , 您可以从 iOS 应用程序(Swift)和 Android 应用程序(Kotlin)运行该软件包 。
本教程不会使用 go mobile [1] 框架 。 相反 , 它使用 Cgo 构建可导入到您的移动项目中的原始静态(iOS)和共享(android) C 库(Go Mobile 框架在后台进行此操作) 。
构建在本教程中 , 我们将创建具有以下结构的简单 monorepo:
.├── android/├── go/│├── cmd/││└── libfoo/││└── main.go│├── foo/││└── foo.go│├── go.mod│└── go.sum└── ios/$ mkdir -p android ios go/cmd/libfoo go/foo我们将从 Go 代码开始 , 稍后再返回创建 iOS 和 Android 项目 。
$ cd go$ go mod init rogchap.com/libfooFoo 包// go/foo/foo.gopackage foo// Reverse reverses the given string by each utf8 characterfunc Reverse(in string) string {n := 0rune := make([]rune, len(in))for _, r := range in {rune[n] = rn++}rune = rune[0:n]for i := 0; i < n/2; i++ {rune[i], rune[n-1-i] = rune[n-1-i], rune[i]}return string(rune)}我们的 foo 程序包有一个函数 Reverse, 该函数具有单个字符串参数 in 和单个字符串输出 。
导出为 C为了使我们的 C 库调用我们的 foo 包 , 我们需要导出所有要公开给 C 的函数 , 并带有特殊 export 注释 。 该包装器必须位于 main 包装中:
// go/cmd/libfoo/main.gopacakge mainimport "C"// other imports should be seperate from the special Cgo importimport ("rogchap.com/libfoo/foo")//export reversefunc reverse(in *C.char) *C.char {return C.CString(foo.Reverse(C.GoString(in)))}func main() {}我们正在使用特殊的 C.GoString() 和 C.CString() 函数在 Go 字符串和 C 字符串之间进行转换 。
*注意:*我们要导出的函数不必是导出的 Go 函数(即以大写字母开头) 。 还要注意是空 main 函数;这对于 Go 代码进行编译是必需的 , 否则会出现 function main is undeclared in the main package 错误 。
让我们通过使用 -buildmode 标志创建一个静态 C 库来测试我们的构建:
go build -buildmode=c-archive -o foo.a ./cmd/libfoo这应该已经输出了 C 库: foo.a 和头文件: foo.h。 您应该在头文件的底部看到导出的函数:
extern char* reverse(char* in);为 iOS 构建我们的目标是创建一个可以在 iOS 设备和 iOS 模拟器上使用的 Fat 二进制文件 [2]。
Go 标准库包含用于构建 iOS 的脚本: `$GOROOT/misc/ios/clangwrap.sh` [3], 但是该脚本仅针对生成 arm64, 而 x86_64 iOS Simulator 也需要该脚本。 因此 , 我们将创建自己的 clangwrap.sh :
#!/bin/sh# go/clangwrap.shSDK_PATH=`xcrun --sdk $SDK --show-sdk-path`CLANG=`xcrun --sdk $SDK --find clang`if [ "$GOARCH" == "amd64" ]; thenCARCH="x86_64"elif [ "$GOARCH" == "arm64" ]; thenCARCH="arm64"fiexec $CLANG -arch $CARCH -isysroot $SDK_PATH -mios-version-min=10.0 "$@"不要忘记让它可执行:
chmod +x clangwrap.sh现在 , 我们可以为每种体系结构构建库 , 并使用该 lipo 工具(通过 Makefile)合并为 Fat 二进制文件:
# go/Makefileios-arm64: CGO_ENABLED=1 \ GOOS=darwin \ GOARCH=arm64 \ SDK=iphoneos \ CC=$(PWD)/clangwrap.sh \ CGO_CFLAGS="-fembed-bitcode" \ go build -buildmode=c-archive -tags ios -o $(IOS_OUT)/arm64.a ./cmd/libfooios-x86_64: CGO_ENABLED=1 \ GOOS=darwin \ GOARCH=amd64 \ SDK=iphonesimulator \ CC=$(PWD)/clangwrap.sh \ go build -buildmode=c-archive -tags ios -o $(IOS_OUT)/x86_64.a ./cmd/libfooios: ios-arm64 ios-x86_64 lipo $(IOS_OUT)/x86_64.a $(IOS_OUT)/arm64.a -create -output $(IOS_OUT)/foo.a cp $(IOS_OUT)/arm64.h $(IOS_OUT)/foo.h