openSUSE:打包 Ruby

跳转到:导航搜索


打包 Ruby 本身

Ruby 打包,直至包括 13.1

Ruby 直至 openSUSE 13.1 为止,被打包为“多版本”

  • ruby - 一个包装包,设置指向实际二进制文件的符号链接
  • ruby20, ruby19, ... - 实际二进制文件,安装特定版本的版本,例如 /usr/bin/ruby20, /usr/bin/ruby19
  • ruby-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

  • 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 包没有遵循,并且开发人员使用 rvmrbenv 来实现相同的效果。
从构建服务的角度来看,这种拆分带来的麻烦比它提供的价值更多。

  • Ruby 是 inst-sys (用于 YaST) 的一部分

如你所知,大小很重要。查看 ruby20 包,它的安装大小为 18MB。
然而,du -sh /usr/share/doc/packages/ruby20 报告仅文档就占 5.9 MB。

  • 更好的维护支持

二进制文件、共享库和 Ruby stdlib 之间的拆分是可取的。
将共享库放在单独的包中也符合 openSUSE 共享库打包策略

从之前的 openSUSE 版本更新

安装新的 ruby 包将删除你以前的多版本 Ruby 包。

我想并行使用多个 Ruby 版本!

如果需要使用多个 Ruby 版本,请使用 rvmrbenv

对于需要不同 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:rubydevel: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 依赖项(参见下文)。

此外,RequiresProvides 字段通过 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:rubyruby-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_64rubygem-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 打包
  • 此帖子 开头的讨论有很多关于当前策略的背景细节。