这是迄今为止我尝试回答自己的问题的最佳结果。它似乎适用于 JRuby(在 Travis 和 RVM 下的本地安装中进行了测试),这是我的主要目标。但是,我对它在其他环境中工作的确认非常感兴趣,以及有关如何使其更通用和/或更强大的任何输入:
gem 安装代码需要一个Makefile
作为输出extconf.rb
,但对其中应包含的内容没有意见。所以extconf.rb
可以决定创建一个没做什么 Makefile
,而不是调用create_makefile
from mkmf
。在实践中,可能看起来像这样:
分机/foo/extconf.rb
can_compile_extensions = false
want_extensions = true
begin
require 'mkmf'
can_compile_extensions = true
rescue Exception
# This will appear only in verbose mode.
$stderr.puts "Could not require 'mkmf'. Not fatal, the extensions are optional."
end
if can_compile_extensions && want_extensions
create_makefile( 'foo/foo' )
else
# Create a dummy Makefile, to satisfy Gem::Installer#install
mfile = open("Makefile", "wb")
mfile.puts '.PHONY: install'
mfile.puts 'install:'
mfile.puts "\t" + '@echo "Extensions not installed, falling back to pure Ruby version."'
mfile.close
end
正如问题中所建议的,这个答案还需要以下逻辑来加载主库中的 Ruby 后备代码:
lib/foo.rb (摘录)
begin
# Extension target, might not exist on some installations
require 'foo/foo'
rescue LoadError
# Pure Ruby fallback, should cover all methods that are otherwise in extension
require 'foo/foo_pure_ruby'
end
遵循此路线还需要对 rake 任务进行一些调整,以便默认的 rake 任务不会尝试在我们正在测试的不具备编译扩展功能的 Rubies 上进行编译:
Rakefile(摘录)
def can_compile_extensions
return false if RUBY_DESCRIPTION =~ /jruby/
return true
end
if can_compile_extensions
task :default => [:compile, :test]
else
task :default => [:test]
end
请注意Rakefile
部分不必完全通用,它只需要覆盖我们想要在本地构建和测试 gem 的已知环境(例如所有 Travis 目标)。
我注意到一件令人烦恼的事情。默认情况下您将看到 Ruby Gems 的消息Building native extensions. This could take a while...
,并且没有迹象表明扩展编译被跳过。但是,如果您使用以下命令调用安装程序gem install foo --verbose
你确实看到消息被添加到extconf.rb
,所以还不错。