V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Mark24
V2EX  ›  Ruby

使用 Ruby-build 在 MacOS 上 编译 Portable Ruby

  •  
  •   Mark24 · 183 天前 · 800 次点击
    这是一个创建于 183 天前的主题,其中的信息可能已经有所发展或是发生改变。

    RubyChina 讨论: https://ruby-china.org/topics/43710

    我的 Blog

    大家好,我是 Mark24 。

    分享下我的笔记,使用 Ruby-build 在 MacOS 上 编译 Portable ruby

    设想一下,如果 ruby 可以变成 portable 的,放在 U 盘上就可以带走,传输到任何一台电脑上就可以执行。

    Portable Ruby + 你的 Ruby 代码 的 zip 包,就像一个行走的独立软件。就像 Go 打包的一样。

    你还可以把他们塞入 一些壳软件里。就像 Electron 那样运行(内部是个浏览器)。

    当然 Ruby 社区曾经有很多方案 Traveling Ruby 、Ruby Packer , 都用各自的方式实现类似的效果,不过都不维护了。

    下面用一个简单的方法来制作 Portable Ruby 。


    截止 2024-05-27 最新版本是 3.3.1 。 每个版本因为特性的不同构建是一个动态的过程。就以 3.3.1 为例。

    过程偷懒,建立在 ruby-build( https://github.com/rbenv/ruby-build) 的基础上。

    不论是 asdf 、rvm …… 他们的背后都是 ruby-build 一个方便安装的 standalone 的工具。ruby-build 解决了大部分的问题,我们只需要找到合适的构建参数。

    一、前置依赖

    1.安装 Mac 的基础工具集

    终端输入 xcode-select --install

    2.安装上 homebrew

    https://brew.sh/

    获得 类似于 Linux 上的包管理工具

    3.安装 Ruby 编译需要的前置依赖

    # 安装前置依赖
    # ruby-build 是安装工具
    # openssl@3 readline libyaml gmp 是必要的依赖
    # rust 是 YJIT 必要的依赖,不装就不会构建 YJIT 功能
    
    brew install ruby-build openssl@3 readline libyaml gmp rust
    

    二、编译

    0.知识点

    C 语言( CRuby 是 C 语言项目)编译一般分为 3 个基本过程
    
    1 )预处理:处理一些前置的宏替换
    2 )编译:把 .c 代码文件翻译成 .o 机器码文件目标文件
    3 )链接:把 .o 文件和系统的底层库(比如标准输入输出)正确的关联起来。生成可执行文件
    
    链接这部,有两个基本的实现
    
    1 )静态链接
    2 )动态链接
    
    静态链接比较简单,就是把所有用到的代码打包成一个整体。软件就像一个 exe 文件,带到哪儿都可以执行。
    优点就是,随处执行。缺点就是体积大,更新困难,比如你依赖的系统部分有安全缺陷。你必须整体替换。
    
    动态链接,就是软件把用到公共部分(系统、上游 lib )的部分,指他们的动态库( linux 是 so 文件,windows 是 dll 文件,mac 里是 dylib 文件)。
    优点:体积小, 如果公共部分有安全漏洞,系统更新,只需要更新动态链接库文件,所有引用的软件都会获得更新。
    缺点:除了无法 portable ,软件运行的前提是系统拥有相应的 库。
    
    动态链接是常态,不论是 Linux 、MacOS 、Windows 。动态链接的实践这么多年运行的一直很好。通常库都是按照动态链接库方向来设计的。没有提供静态库。
    
    MacOS 还禁止系统动态库进行 静态链接。
    
    1. 最简单的编译

    关键参数:

    • $HOME/portable-ruby 是你存放的目录
    • --enable-load-relative 地址是相对目录,这对我们移动很重要
    • --with-static-linked-ext 静态链接
    RUBY_CONFIGURE_OPTS="--enable-load-relative --with-static-linked-ext" ruby-build 3.2.2 $HOME/portable-ruby
    

    2.一些优化选项

    可以参考 https://github.com/rbenv/ruby-build

    额外的选项

    • --with-out-ext=win32,win32ole 去掉 MacOS 上不需要的拓展
    • --disable-install-doc 关闭文档,减小体积
    • --disable-install-rdoc
    • --disable-dependency-tracking
    RUBY_CONFIGURE_OPTS="--enable-load-relative --with-static-linked-ext --with-out-ext=win32,win32ole --disable-install-doc --disable-install-rdoc --disable-dependency-tracking " ruby-build 3.2.2 $HOME/portable-ruby
    

    ruby-build 能做的更多,比如支持交叉编译

    三、Portable Ruby

    编译正确完成,你应该获得了 portable ruby

    在拥有 依赖库的电脑上(对,我们前面解释了,系统部分是禁止 静态链接的)。

    你的可以把你的 ruby 代码 + portable ruby 放在一个文件夹里。 用 一个 shell 脚本,通过相对路径连接起来执行。

    比如这样

    #!/usr/bin/env bash
    ./portable-ruby/bin/ruby ./main.rb
    

    某种意义上,Portable Ruby + Ruby Script 和 Go 、Crystal 打包的可执行文件,是一样的。就是大了一点 :D

    我的 Blog

    2 条回复    2024-10-29 14:16:49 +08:00
    Kobayashi
        1
    Kobayashi  
       153 天前
    这依然是动态链接的,只能保证扩展静态编译,libruby dylib 相对路径链接。

    ```
    ❯ otool -L /Users/wyh/.local/share/rbenv/versions/3.2.2/bin/ruby
    /Users/abc/.local/share/rbenv/versions/3.2.2/bin/ruby:
    /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1953.255.0)
    @executable_path/../lib/libruby.3.2.dylib (compatibility version 3.2.0, current version 3.2.2)
    /opt/local/lib/libgmp.10.dylib (compatibility version 16.0.0, current version 16.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
    ```
    set
        2
    set  
       28 天前
    类似的问题,编译后打包发到另一个没装 brew 的机器, 运行时提示找不到文件:
    dyld[11749]: Library not loaded: /opt/homebrew/opt/gmp/lib/libgmp.10.dylib
    Referenced from: <50730BE9-9AC0-373D-ABB4-791010CB96AF> /Users/jqt/portable-ruby/bin/ruby (built for macOS 14.0 which is newer than running OS)
    Reason: tried: '/opt/homebrew/opt/gmp/lib/libgmp.10.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/opt/gmp/lib/libgmp.10.dylib' (no such file), '/opt/homebrew/opt/gmp/lib/libgmp.10.dylib' (no such file)
    z
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1023 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 21:39 · PVG 05:39 · LAX 13:39 · JFK 16:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.