Mark24
Ruby打包技术之旅
结论:
似乎找到了 2 个 Portable Ruby 实例
-
[Windows Ruby (Portable) 3.3.1.1 ](https://community.chocolatey.org/packages/ruby.portable) -
[MacOS homebrew/portable-ruby ](https://github.com/Homebrew/homebrew-portable-ruby/pkgs/container/portable-ruby%2Fportable-ruby)
原文:
背景
大家好,我是 Mark24。
设想一下,如果你在用 Ruby 开发一个 GUI 应用,或者是 游戏。如何把产物可以送到你用户的手中。尽可能的轻松跑起来?
我目前感兴趣的是游戏应用。所以后面都是建立在游戏跑在终端的角度考虑。
虽然我们在讨论 Ruby ,但是对于所有动态脚本语言的思路是通用的。
解决打包动态语言的问题。最后一公里,如何送到用户手中。
思路一: 编译并静态链接,经典二进制包
1. 像静态语言一样,获得直接的二进制文件 ❌
比如 Go、Rust、Crystal 的构建产物。
结论:
Go、Rust、Crystal …… 他们依然是在有限条件下运行。只不过这种条件实际上特别宽泛,好像他们的产物可以在各种系统下运行。
实际上 MacOS、Linux、Windows 的底层都是不鼓励静态链接。并且一些关键的包,也不提供静态链接需要的库。
这是为了体积考虑,也是为了安全更新考虑。
这些能够相对来说把自己打成静态链接的语言,实际上都做了大量的工作,自己实现了底层需要的部分。
动态语言无法直接把代码打包成这样。 这条路是违背原理的。
2:极致的静态方向 ✅
这个思路是 MRuby。
MRuby 是一个轻量级的 Ruby 为嵌入式设计。它可以交叉编译成不同的架构。被设计的尽可能的少依赖,多拓展。
一定程度上,MRuby 就像是 Go。
可以用 MRuby 来构建应用、游戏。 MRuby 也有 SDL 的绑定
2.1 Dragon Ruby ✅
这里有篇演讲, Dragon Ruby 的游戏引擎设计者,如何使用 MRuby 来构建一个应用。
Dragon Ruby 从 IDE 到 游戏产物全部是静态二进制。
但是具体的原理不详。依然不知道 Dragon Ruby 是如何做到的。
2.2 Taylor ✅
Taylor 是个个人开源框架,试图挑战 Dragon Ruby。
Taylor 的思路也是经典思路,容器中构建一个可以被静态的环境,绕过系统(MacOS 不允许静态链接系统 lib)。
这些代码可能很难理解,在于他们究竟如何在发挥具体作用。
Taylor 正在重大重构中,但是目前的版本,是完全可以工作的!
思路二:解释器+项目代码 => 压缩包
这个思路需要一个 可以移动执行的 Ruby 解释器。
1. 静态编译 ruby 为 portable ruby ✅
如果拥有了 Portable Ruby,那么 软件包 = (Portable Ruby + 项目代码)。
这条路相对可行。
还是前面的问题,Ruby 没有像 Go 等实现了全部的底层依赖的静态库。所以 编译 != portable。
Portable 的重点就是,尽可能的不依赖。如果实在无法避开的依赖,比如 Linux 中的 glibc(系统底层),需要使用较低版本来编译。 因为 glibc 永远是高版本兼容低版本,所以这样尽可能的获得兼容性。
Crystal、Go …… 他们一样。也只能工作在有限的 glibc 中。
Crystal 给出了平台很好体现了这一点:Crystal Platform Support。
用户不需要安装 Ruby,但是需要安装 Ruby 需要的底层库。来获得动态链接库。
这个思路获得了成功。
- 1)让你本地安装 lib;或者直接安装 ruby(过程中就获得了需要的 lib)
- 2)打包 portable ruby
-
3)使用 Mac 的 App 壳应用
- App 壳应用 Gosu
可以获得一个 Mac 的应用。
1.1 Portable rub + Portable libs 🤔 ✅
前面说了,如果可以创造出 静态的包。Ruby 也可以像 Go、Java 一样。这里参考这样一个项目,尝试在容器中模拟一个这样的环境。尽可能把所需的依赖全部集成起来,产出 portable ruby
不过这个产物我没怎么跑起来。但是这个是经典思路,是完全可行的。
app code + (Portable Ruby + lib) = software
思路三: 普通思路,前置安装器 ✅
用户安装 Ruby 运行游戏。由于前面无法实现彻底的静态打包,即使是安装依赖库,整个过程是差不多的。用户依然要安装。
如果这样避不开。推荐常见的处理办法 —— 前置的安装器(Installer)。解决环境依赖问题。
在 Windows 上 Ruby 是需要 安装包来安装。整个过程就像这样。
这一点,在 Windows 上也成功实现了:
- Ruby2D 的 demo
- Raylib-bindings 的 demo
构建过程和 Sample Project: ruby-windows-example
思路四: 切换可以打包的语言
1. 使用 静态语言 Crystal ✅ 🕘
Crystal 的语法和 Ruby 非常相似,也有 游戏库、GUI 的绑定。
可以做到类似的事情。这一点就像 C++
但是缺点是 Crystal 目前还在建设中。
Crystal 对 MacOS ARM、Windows 的支持还不足。
现在无法当作一个成熟方案。
2. 使用 JRuby(Java) ✅
Java 其实采用了类似的思路,自己实现了底层。所以 Java 自身可以打包成静态的二进制。
我们可以把打包工作建立在 Java 的基础上。
这个实践方向是 Glimmer
Glimmer DSL for SWT 能够在 JRuby 之上将 Ruby 应用程序打包到原生安装程序(如 Mac DMG/PKG/APP、Windows MSI/EXE 和 Linux RPM/DEB)中,使开发者能够给最终用户(非程序员)一个单一的文件来运行,以安装所有需要的内容,比如 JRuby(可以运行任何 Ruby 代码)、它的 JVM 依赖项,以及正在安装的应用程序:
Glimmer DSL for LibUI,它直接在 Ruby 上运行而不是 JRuby,也有一个关于打包 Ruby 应用程序的部分,你可能想要查看(它提到了 Windows 和 Mac 的打包解决方案):
以下是使用 Glimmer DSL for SWT 打包的应用程序示例,这些应用程序由最终用户安装,没有问题:
- https://github.com/AndyObtiva/MathBowling
- https://github.com/AndyObtiva/glimmer-cs-gladiator
- https://github.com/AndyObtiva/are-we-there-yet
- https://github.com/AndyObtiva/dcr
- https://github.com/AndyObtiva/glimmer_klondike_solitaire
- https://github.com/AndyObtiva/glimmer_metronome
- https://github.com/AndyObtiva/glimmer_wordle
这些都是作者发来的例子。尝试跑了几个,没有成功。 还需要研究研究。
总结
如何把 Ruby 带到终端,其实一直不停的有人研究。项目生生死死。这里列举一些,供参考。
1)容器打包, 静态链接 portable ruby 思路:
- phusion/traveling-ruby 已经不维护
- YOU54F/traveling-ruby 后继者
- HellRok/Taylor MRuby 容器打包
- ahogappa0613/kompo 2024 新项目,拦截的方式,修改 Ruby 解释器,静态打包。我没跑起来;
2)临时文件系统思路:
- pmq20/ruby-packer 已经不维护
3)JRuby 思路:
- AndyObtiva/glimmer 是 Gosu、Shoes 的继承者,还在开发
4)Portable Ruby 思路:
- gosu/ruby-app 不维护
5)只打包应用脚本,指定系统 Ruby
- platypus 只打包你的脚本,封装成 app,只适合简单脚本
6)静态语言
使用 Crystal , Ruby 语法的 Go like 语言开发应用
7)使用 Zig
这是一个问号,Zig 作为一个新语言可以作为 C 的环境,而且自己实现了所有的静态库。
不知道 Zig 作为 CRuby 的编译器会如何? 但是 Zig 目前依然在发展中。
8)使用容器
容器技术是任何语言的一个打包工具。
对于开发者友好,但是终端用户还是有门槛的。
不适合游戏应用。
9)Gem
如果都能接受用户总归要自己安装 Ruby 的设定。
把游戏、应用,封装成 gem,可以自动处理依赖、版本问题。
10)切换 Ruby 的实现: CRuby 无法实现静态打包
补充:
-
[Windows Ruby (Portable) 3.3.1.1 ](https://community.chocolatey.org/packages/ruby.portable) -
[MacOS homebrew/portable-ruby ](https://github.com/Homebrew/homebrew-portable-ruby/pkgs/container/portable-ruby%2Fportable-ruby)
补充资料: