ITADN

Bundler standalone setup assumes `defined?(Gem)` implies `Gem.ruby_api_version` is available

#9586Openledsun 创建于 2026-06-02
Bundler
L
ledsuncommented
### Describe the problem as clearly as you can Bundler standalone setup assumes that if the `Gem` constant is defined, RubyGems API helpers such as `Gem.ruby_api_version` and `Gem.extension_api_version` are also available. This assumption can fail in ruby.wasm. In ruby.wasm, after Ruby VM initialization, we can observe a state where `Gem` is already defined, but RubyGems has not been fully loaded yet. In that state: ```ruby defined?(Gem) #=> "constant" Gem.respond_to?(:ruby_api_version) #=> false Gem.respond_to?(:extension_api_version) #=> false ``` Bundler standalone setup generated by `bundle install --standalone` only checks `unless defined?(Gem)` before defining fallback helpers: https://github.com/ruby/rubygems/blob/884d80fd158fead18bb4f021796a2af062a45ac9/bundler/lib/bundler/installer/standalone.rb#L76 So, when `Gem` is partially defined, the fallback helpers are not defined, but the generated load paths still use `Gem.ruby_api_version` and `Gem.extension_api_version`. This can fail with `NoMethodError` before application code starts. This is currently known as a ruby.wasm-specific issue. I do not think this affects normal CRuby usage, where VM initialization appears to leave `Gem` and RubyGems in a consistent state. ruby.wasm has worked around this by requiring RubyGems before loading the generated standalone setup when `Gem` is defined but `Gem.ruby_api_version` is missing: https://github.com/ruby/ruby.wasm/pull/622 I would like to ask whether Bundler standalone setup should be more defensive here, for example by checking whether the specific methods it needs are available, instead of checking only `defined?(Gem)`. ### Did you try upgrading rubygems & bundler? Yes. I checked the current Bundler standalone implementation on the `rubygems` main branch, and the generated fallback still appears to use only `unless defined?(Gem)`: https://github.com/ruby/rubygems/blob/master/bundler/lib/bundler/installer/standalone.rb So I believe this behavior is still present. ### Post steps to reproduce the problem This is not known to reproduce on normal CRuby. It is observed in ruby.wasm, where the Ruby VM can be initialized with `Gem` defined but RubyGems not fully loaded. ### Which command did you run? In ruby.wasm packaging, Bundler is invoked with standalone mode, roughly: ```sh bundle install --standalone --target-rbconfig path/to/target/rbconfig.rb ``` Then, at runtime, ruby.wasm loads the generated setup file: ```ruby require "/bundle/bundler/setup.rb" ``` ### What were you expecting to happen? I expected the generated standalone `setup.rb` not to fail just because the `Gem` constant already exists. More specifically, if `setup.rb` needs `Gem.ruby_api_version` and `Gem.extension_api_version`, I expected it to either: - define fallback versions of those methods when they are missing, or - clearly rely on RubyGems being fully loaded before `setup.rb` is required. If Bundler expects the second behavior, then ruby.wasm should probably require RubyGems before loading the generated standalone setup file. I would like to confirm which behavior Bundler expects. ### What happened instead? The generated standalone setup can fail with `NoMethodError` because `Gem.ruby_api_version` or `Gem.extension_api_version` is missing. ```text NoMethodError: undefined method `ruby_api_version' for module Gem ``` ### If not included with the output of your command, run `bundle env` and paste the output below This is observed in ruby.wasm rather than a normal local CRuby environment, so `bundle env` from the host environment may not represent the runtime where the failure occurs. Relevant context: - Runtime: ruby.wasm - Mode: WASI component model flow - Bundler feature involved: `bundle install --standalone` - ruby.wasm workaround: https://github.com/ruby/ruby.wasm/pull/622 This may be related to #7545, which also describes a standalone setup case where `Gem` exists but RubyGems is not fully available. The missing method is different there (`Gem.try_activate`), while this issue is about `Gem.ruby_api_version` and `Gem.extension_api_version` used by the generated standalone load paths.
0 条评论