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:
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:
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:
ruby/ruby.wasm#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:
bundle install --standalone --target-rbconfig path/to/target/rbconfig.rb
Then, at runtime, ruby.wasm loads the generated setup file:
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.
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:
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.
Describe the problem as clearly as you can
Bundler standalone setup assumes that if the
Gemconstant is defined, RubyGems API helpers such asGem.ruby_api_versionandGem.extension_api_versionare also available.This assumption can fail in ruby.wasm.
In ruby.wasm, after Ruby VM initialization, we can observe a state where
Gemis already defined, but RubyGems has not been fully loaded yet. In that state:Bundler standalone setup generated by
bundle install --standaloneonly checksunless defined?(Gem)before defining fallback helpers:rubygems/bundler/lib/bundler/installer/standalone.rb
Line 76 in 884d80f
So, when
Gemis partially defined, the fallback helpers are not defined, but the generated load paths still useGem.ruby_api_versionandGem.extension_api_version. This can fail withNoMethodErrorbefore 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
Gemand RubyGems in a consistent state.ruby.wasm has worked around this by requiring RubyGems before loading the generated standalone setup when
Gemis defined butGem.ruby_api_versionis missing:ruby/ruby.wasm#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
rubygemsmain branch, and the generated fallback still appears to use onlyunless 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
Gemdefined but RubyGems not fully loaded.Which command did you run?
In ruby.wasm packaging, Bundler is invoked with standalone mode, roughly:
Then, at runtime, ruby.wasm loads the generated setup file:
What were you expecting to happen?
I expected the generated standalone
setup.rbnot to fail just because theGemconstant already exists.More specifically, if
setup.rbneedsGem.ruby_api_versionandGem.extension_api_version, I expected it to either:setup.rbis 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
NoMethodErrorbecauseGem.ruby_api_versionorGem.extension_api_versionis missing.If not included with the output of your command, run
bundle envand paste the output belowThis is observed in ruby.wasm rather than a normal local CRuby environment, so
bundle envfrom the host environment may not represent the runtime where the failure occurs.Relevant context:
bundle install --standaloneThis may be related to #7545, which also describes a standalone setup case where
Gemexists but RubyGems is not fully available. The missing method is different there (Gem.try_activate), while this issue is aboutGem.ruby_api_versionandGem.extension_api_versionused by the generated standalone load paths.