openSUSE:打包 Ruby
打包 Ruby 本身
Ruby 打包,直至包括 13.1
Ruby 直至 openSUSE 13.1 为止,被打包为“多版本”
ruby- 一个包装包,设置指向实际二进制文件的符号链接ruby20,ruby19, ... - 实际二进制文件,安装特定版本的版本,例如/usr/bin/ruby20,/usr/bin/ruby19ruby-common- 一组打包 Ruby GEM 所需的 RPM 宏
Ruby 打包方案,13.1 之后
以下打包方案适用于 openSUSE 13.1 之后
ruby- 这提供了二进制文件(ruby,irb,rake,gem, ...)和最少的一组文档(changelog,readme,news, ...)
作为用户,安装 ruby 即可开始使用。所有其他必需的软件包都通过依赖项安装。
以前的 ruby-common 包,包含用于打包 gem 的 RPM 宏,已合并到 ruby 中。
打包(非二进制)gem 现在不再需要 ruby-devel。
libruby2- 这提供了符合 openSUSE 共享库打包策略 的libruby2.1.so.2.0.0共享库
ruby-stdlib- 这提供了/usr/lib64/ruby/2.1.x/目录树。
将其分开允许在需要修复标准库时进行更小的维护更新。
ruby-doc- 这提供了完整的 Ruby 文档,包括示例,将旧 Ruby 包的大小减少了近 6MB。
ruby-devel,ruby-devel-extra,ruby-doc-ri- 这些包含使用 Ruby API 构建软件所需的所有文件。ruby-devel-extra包含 Ruby 内部 API 的包含文件,这些文件被一些 gem 使用。
该方案已在 opensuse-ruby 邮件列表 上讨论过
为什么在 openSUSE 13.1 之后更改了打包方案?
- 为了恢复
ruby,rubyXY, 和ruby-common的拆分
最初是为了允许并行使用多个 Ruby 版本。
这种能力没有被广泛使用,rubygem 包没有遵循,并且开发人员使用 rvm 或 rbenv 来实现相同的效果。
从构建服务的角度来看,这种拆分带来的麻烦比它提供的价值更多。
- Ruby 是
inst-sys(用于 YaST) 的一部分
如你所知,大小很重要。查看 ruby20 包,它的安装大小为 18MB。
然而,du -sh /usr/share/doc/packages/ruby20 报告仅文档就占 5.9 MB。
- 更好的维护支持
二进制文件、共享库和 Ruby stdlib 之间的拆分是可取的。
将共享库放在单独的包中也符合 openSUSE 共享库打包策略
从之前的 openSUSE 版本更新
安装新的 ruby 包将删除你以前的多版本 Ruby 包。
我想并行使用多个 Ruby 版本!
如果需要使用多个 Ruby 版本,请使用 rvm 或 rbenv。
对于需要不同 Ruby 版本的应用程序部署,Linux 容器和 Docker 提供了更好的解决方案。
如何将 Ruby gem 打包成 rpm
Ruby 库模块主要以 gem(Ruby 自己的包格式)的形式在 Ruby 社区中分发。在某些情况下,通过 开放构建服务 (参见 openSUSE:Ruby Gem Strategies 了解有关这样做优缺点的更多详细信息) 将 gem 重新打包为 rpm 对于 openSUSE 来说是有意义的。在这些情况下,你可以按照以下步骤打包 gem,这应该很简单。
打包 gem 的配方
对于 Tumbleweed
zypper in rubygem\(gem2rpm\)
对于其他版本,如果无法解决上述显示的依赖项,请添加 devel:languages:ruby 和 devel:languages:ruby:extensions 仓库。
在你的主项目(或最好是在子项目,例如 home:$USER:ruby)中添加一个新的包
osc mkpac rubygem-foo cd rubygem-foo
如果你一直在使用 rvm,请确保它不会干扰以下步骤,方法是确保你正在使用系统范围的 Ruby 安装
rvm use system
下载相应的 gem 文件并生成 RPM <code.spec 文件
gem2rpm -o rubygem-foo.spec --fetch foo
根据需要编辑 .spec 文件
vi rubygem-foo.spec
特别是,确保 License: 字段是正确的。gem2rpm 会故意省略其他 gem 上的 BuildRequires 依赖项(参见下文)。
此外,Requires 和 Provides 字段通过 ruby-common 包自动生成(尽管 你需要直接构建到 devel:languages:ruby 而不是构建到 openSUSE:12.2,以便构建使用 来自 devel:languages:ruby 的版本 1.0,而不是 来自主 12.2 仓库的版本 1.9.3),因此你只需要为非 gem 包包含手动 Requires / Provides。请参阅下文了解其工作原理。
添加 changelog 条目,在本地构建包,测试,然后提交你的包
osc vc .... osc build --local osc vc .... osc ci
如果你认为你的包已准备好更广泛地采用,请针对 devel:languages:ruby:extensions 构建服务仓库提交请求
osc sr devel:languages:ruby:extensions
将 gem 包更新到新版本
如果你想将 gem 包更新到 gem 的新版本,但其他包仍然依赖于旧版本,那么你应该确保旧版本包含包名称中的后缀。例如,当将 gem foo 从 0.6.3 更新到 0.7.1 时,如果另一个 gem 依赖于 foo ~> 0.6,那么应该有一个包含 0.6.3 的 rubygem-foo-0_6 包,以及包含最新版本 (0.7.1) 的 rubygem-foo 包。这种后缀方案 已知不是最优的,但在撰写本文时似乎存在一些惯性和抵制解决它的情况。
如果你想将使用后缀方案的 gem 包更新到 gem 的新版本,并且你确定没有其他包仍然依赖于旧版本,那么请利用机会删除后缀。但是,由于历史原因,有一些包,例如 rails,不应删除后缀。
(FIXME: 此部分需要更多信息)
gem2rpm 在 gemspec 中没有许可证时,会从输出文件获取许可证,因此可以通过以下方式更新现有的 .spec 文件
gem2rpm *.gem -o *.spec
然后按照上述相同的步骤操作。
省略 BuildRequires
gem2rpm 会故意省略其他 gem 上的 BuildRequires 依赖项,以减少 构建服务 中的构建延迟 - 参见 这篇文章 了解更多详细信息。为了防止由于缺少 gem install 时间的 gem 依赖项而在 %install 阶段发生 gem install 失败,从 gem2rpm 生成 .spec 文件的模板会调用 %gem_install 宏,并使用 -f 标志。
补偿 BuildRequires 的缺失
上述优化的缺点是 rpm 构建时不会检查 rpm 安装时间依赖树的边缘,因此你只有在实际安装 gem rpm 时才会发现损坏的依赖项。(rpm 在构建后立即安装,但 使用 --force --nodeps,因此忽略了依赖项。)
例如,如果 A 和 B 是项目中的两个 gem,并且 A 依赖于 B,那么 OBS 可能会在不知道 B 是否成功安装的情况下成功构建 A。B 可能具有无法自动生成的运行时依赖项(即 Requires)到其他 gem 或 Ruby 本身,或者无法手动指定的运行时依赖项,或者它可能只是与其他包(gem 或其他)的文件冲突。
作为一种解决方法,你可以通过创建一个本身 BuildRequires 项目中所有 gem 的小包来实现对所有 gem 的 rpm 安装时间依赖树的完整检查,该检查发生在构建时。这也有了检查依赖树独立分支上 gem 之间冲突的优点。
在 devel:languages:ruby:extensions 中有一个这样的包,名为 all-good。这意味着对于 rails 更新,调度程序只需要一轮更新:更新所有 gem,然后让调度程序计算是否可以扩展 all-good,或者缺少依赖项。(ruby-common 有 一个 generate_buildrequires.sh 脚本,它将自动为你更新 BuildRequires。)
gem 依赖项如何自动处理
来自 devel:languages:ruby 的 ruby-common 包包含各种 rpm 宏和脚本,它们结合起来自动计算并嵌入到 gem 包的 Requires: headers 中,这些 headers 反映了此 gem 对其他 gem 的依赖项,以及 Provides: headers,这些 headers 支持其他 gem 对此 gem 的任何必需依赖项。
gem 的 .spec 文件的 %install 部分应包含一行
%gem_install -f
这将触发在 /etc/rpm/macros.suse-ruby 中定义的宏,该宏会调用 /usr/lib/rpm/gem_install.sh,它会将 gem 安装到 rpmbuild 的安装根目录(也称为 buildroot),并将从 gem 文件中嵌入的 YAML 序列化的 metadata.gz 生成的 foo.gemspec 文件安装到相同的层次结构中的 specifications/ 子目录中。
在 rpmbuild 过程的 %install 阶段之后,RPM 的 自动依赖生成机制 开始发挥作用。 此时,在 /usr/lib/rpm/fileattrs/rubygems.attr 中定义的 %__rubygems_path 宏,它是一个正则表达式,匹配上面生成的 foo.gemspec 文件的完整路径,导致 rpm 运行在同一文件中定义的 %__rubygems_requires 和 %__rubygems_provides 宏。 这些宏运行 /usr/lib/rpm/rubygemsdeps.rb,它从 foo.gemspec 文件中提取依赖项,并将它们转换为 Requires: 和 Provides: 头。
请注意,目前存在 此系统无法处理的边界情况:如果 .gemspec 文件具有版本范围,例如
s.add_dependency "net-ssh", ">= 2.6.6", "< 2.8.0"
那么系统无法保证此约束,因为 zypper 可能会通过同时安装 rubygem-net-ssh-2_1-2.1.4-2.7.x86_64 和 rubygem-net-ssh-2.8.0-26.1.x86_64 来错误地满足它。 因此,修补它以收紧约束,使用 ~> 运算符,例如
s.add_dependency "net-ssh", "~> 2.6.6"
可能更安全。 rpm 的未来版本可能会支持版本范围,届时就可以解决此漏洞。
请注意,如果您的项目为具有 rpm < 4.9.0 的发行版构建,例如 openSUSE 11.4 和 SLE11,那么它将没有可用的 fileattrs 可插拔依赖生成器。 因此,自动生成只能通过使用修补后的 rpm 版本来触发。 这已为 openSUSE 11.4 和 SLE11 在 devel:languages:ruby:backports 项目 中完成。
自动生成的 Requires: 头格式
以下是 oa-openid gem 的 0.3.2 版本的示例。
自动生成的 Requires: 头是
ruby(abi) = 1.9.1 rubygem(1.9.1:oa-core) = 0.3.2 rubygem(1.9.1:rack-openid:1.3) >= 1.3.1 rubygem(1.9.1:ruby-openid-apps-discovery:1.2) >= 1.2.0
这等效于以下 Ruby 依赖项
oa-core = 0.3.2 rack-openid ~> 1.3.1 ruby-openid-apps-discovery ~> 1.2.0
事实上,这正是 https://rubygems.org.cn/gems/oa-openid/versions/0.3.2 上列出的内容,因此我们可以看到它工作得很好。 悲观的 ~> 版本约束运算符的实现方式存在微妙之处。 如果 gem 的 rack-openid 依赖项是
rack-openid => 1.3.1
那么相应的 Requires: 头将是
rubygem(1.9.1:rack-openid) >= 1.3.1
即,符号中没有 :1.3。
自动生成的 Provides: 头格式
自动生成的 Provides: 头以与 Requires 相同的格式提供符号
rubygem(1.9.1:oa-openid) = 0.3.2 rubygem(1.9.1:oa-openid:0) = 0.3.2 rubygem(1.9.1:oa-openid:0.3) = 0.3.2 rubygem(oa-openid) = 0.3.2
(当然,这是必要的,否则整个系统都会崩溃。)
但它们也涵盖了旧版本后缀的命名方案,以实现向后兼容性
rubygem-oa-openid-0 = 0.3.2 rubygem-oa-openid-0_3 = 0.3.2
我们希望将来可以删除这些后缀符号。
构建 rubygems 的常见问题
我的 rubygem-* 包构建失败
如果您的 rubygem-* 包在 Factory 上无法构建,请使用以下命令重新生成 .spec 文件:
gem2rpm <foo>.gem > rubygem-<foo>.spec
典型的 gem 打包问题
gem 包含二进制部分,安装失败因为它没有遵守新的路径
如上所述,使用新的 %gem_* 宏
gem 包含二进制部分,构建失败因为缺少 ruby.h
将 ruby-devel 添加到 BuildRequires(以前,所有 rubygems BuildRequired ruby-devel 通过 ruby-common。 但是,大多数 rubygems 没有二进制文件,并且只需安装 ruby 即可很好地构建/打包。)
历史
- openSUSE > 13.1 的新 Ruby 打包
- 以 此帖子 开头的讨论有很多关于当前策略的背景细节。