构建ruby gem并有条件地指定依赖项

时间:2012-01-20 10:54:14

标签: ruby dependencies gem conditional

我正在研究一个gem,它需要在安装gem时有条件地设置依赖项。我做了一些挖掘

看起来我并不孤单。

Rubygems: How do I add platform-specific dependency?

这是一个长线程

http://www.ruby-forum.com/topic/957999

我可以看到向gem添加依赖项的唯一方法是在.gemspec文件中的Gem :: Specifiction块中使用add_dependency方法

Gem::Specification.new do |s|

  # ... standard setup stuff

  # conditionally set dependencies
  s.add_dependency "rb-inotify", "~> 0.8.8" if RUBY_PLATFORM =~ /linux/i
  s.add_dependency "rb-fsevent", "~> 0.4.3.1" if RUBY_PLATFORM =~ /darwin/i
  s.add_dependency "rb-fchange", "~> 0.0.5" if RUBY_PLATFORM =~ /mswin|mingw/i

end

根据我在网上找到的所有文档和主题,我希望如果你安装了宝石

  • Linux,然后,rb-inotify将成为依赖项并自动安装
  • 将安装Mac-rb-fsevent
  • Windows - 将安装rb-fchange

然而,似乎并非如此。块中的“if”语句在构建和打包gem时进行评估。因此, 如果你在Linux上构建和打包gem,那么,rb-inotify被添加为依赖项,Mac,然后是rb-fsevent,Windows-rb-fchange。

仍然需要一个解决方案,我在rubygems代码中进行了挖掘,看起来以下内容是对所发生的事情的广泛了解。

  • 为您的gem构建所有代码:foo.gem
  • 创建一个foo.gemspec文件
  • 构建,打包并将gem发布到gem服务器,例如rubygems.org
  • 让大家都知道
  • 开发人员通过以下方式在本地安装:gem install foo
  • 下载,解压缩并安装foo.gem文件。所有依赖项都已安装。
  • 应该设置一切,我们可以使用gem。

似乎在构建和发布gem时,加载了foo.gemspec文件并评估了Gem :: Specification块并将其转换为YAML,压缩为 metadata.gz,包含在foo.gem中。 ruby代码被压缩成data.tar.gz并包含在内。当gem安装在本地开发人员计算机上时, YAML从metadata.gz中提取并转换回Gem :: Specification块,但不会将其转换回原始块。

相反,你会看到如下内容:

Gem::Specification.new do |s|

  if s.respond_to? :specification_version then
    s.specification_version = 3

    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
      s.add_runtime_dependency(%q<rb-inotify>, ["~> 0.8.8"])
    else
      s.add_dependency(%q<rb-inotify>, ["~> 0.8.8"])
    end
  else
    s.add_dependency(%q<rb-inotify>, ["~> 0.8.8"])
  end

end

确定。因此,我对该过程有一个鸟瞰图,但这并没有改变我构建单个gem的愿望,并有条件地为一系列OS目标指定依赖关系。

如果除了为每个目标操作系统构建多个.gemspec文件之外,还有其他人有解决方案...我都是耳朵!!

3 个答案:

答案 0 :(得分:1)

我自己从未这样做过,但有些平台特定版本可用的宝石:http://rubygems.org/gems/libv8/versions

据我所知它只是一个命名的东西,可以通过设置你的gemspec的平台选项来配置。看看文档:{​​{3}}

答案 1 :(得分:1)

我过去偶然发现了这个问题。我能找到的唯一解决方法是创建一个Rake任务来安装依赖项。当然,在那个阶段,你可能只是想让用户根据他收到的错误信息找出他自己缺少哪个宝石。在我的例子中,有几个依赖于平台的依赖项要安装,所以这不是一个选项。

Rake文件:

task :install do |t|
  require './lib/library/installer'
  Library::Installer.install
end

安装程序:

module Library::Installer

  require 'rubygems/dependency_installer'

  def self.install
    installer = Gem::DependencyInstaller.new
    dependency = case RUBY_PLATFORM
      when /darwin/i then ["rb-fsevent", "~> 0.4.3.1"]
      when /linux/i then ["rb-inotify", "~> 0.8.8"]
      when /mswin|mingw/i then ["rb-fchange", "~> 0.0.5"]
    end
    installer.install(*dependency)        
end

然后,用户可以使用rake install来安装适当的依赖项。

RubyGems严重缺少条件依赖安装(不仅仅基于平台,而是基于用户输入)。我们希望将来能够实施它!

答案 2 :(得分:0)

我也研究了这个问题,并得出了设计无法实现的结论。对于所有平台都有一个“巨型宝石”会导致在下载和安装gem之前不知道是否支持平台的问题。 Gem必须足够智能,以确定根据平台安装的正确方法。如果一个平台根本不受支持,宝石可能会失败,打开一大堆蠕虫。在安装gem之后,由于同样的原因,删除了一个回调,没有魔法可以正确安装gem。有些人使用mkmf攻击了这个,但我建议按照每个平台的宝石路径作为更好的解决方案。

基于此,在为ruby和jruby构建gem的项目中,我必须手动创建每个gem并将它们上传到RubyGem。使用Jeweler这就像指定Gemfile一样简单,但每次打包gem时我都必须重建gem规范。在仅支持2个平台时相当微不足道,但构建过程非常简单,可以自动化以支持多个平台宝石。