1. Goals * multiple versions in parallel * allow building ruby extension packages for all available ruby packages 2. Multiple ruby versions in parallel Some people suggested we dont need this. We should just upgrade the ruby to 1.9 and be done with it. While this might work for factory, it wont be a solution for e.g. ruby 1.9 on SLES. there we cant just say "then you have to pick on of the two versions". Given that some of our products think about moving to 1.9, we have to solve the problem of parallel installations. 2.1. Potential problems 2.1.1 Suffixed binaries and update-alternatives The only really overlapping part of 2 ruby installations are the binaries. this can be solved quite easily with ./configure --program-suffix=1.8 --with-soname=ruby1.8 ./configure --program-suffix=1.9 --with-soname=ruby1.9 It would need to be discussed if we want to use the ruby abi version was suffix. In that case the 1.9 commandline would change to: ./configure --program-suffix=1.9.1 --with-soname=ruby1.9.1 To offer the users a default /usr/bin/ruby, we would use update-alternatives to manage the symlinks. 2.1.2 Shebang lines and multiple ruby versions But with that we cant just leave the she bang lines in scripts at "/usr/bin/ruby". we would need to change it to the ruby version we build against. So this might require some build time patching. For gem packages and extensions installed via setup.rb this shouldnt be a problem, but scripts in other packages might be affected. 3. Extensions and multiple ruby versions Most of the ruby extensions come as gems nowadays. So i will mainly focus on gems here. Our goals for the extensions packaging: * handle ~> and the semanatics nicely * package gems for multiple ruby packages without duplicated spec files or a lot of manual work 3.1 Naming My suggestion for naming would be: ruby%{rb_suffix}-%{extension_name} ruby%{rb_suffix}-gem-%{gem_name}%{?gem_version_suffix} packages without %{rb_suffix} could be used as wrapper to pull our default version. 3.2 Gem versioning fun - or how we got the gem_version_suffix One of the greatest fun factors with gem packaging is the ~> operator: package ~> 1.0.0 -> package >= 1.0.0 && package < 1.1 package ~> 1.0 -> package >= 1.0 && package < 2 (From my experience so far we have mostly the first form with 3 digits.) RPM doesnt have an equivalent operator itself. The solution from gem2rpm upstream basically maps out the dependencies as shown in the explaination. But this leads to problems if you have multiple packages providing that symbol Requires: rubygem-foo >= 1.0.0 rubygem-foo < 1.1 rubygem-foo-0.9: Provides: rubygem-foo = 0.9 rubygem-foo-1.0: Provides: rubygem-foo = 1.0 rubygem-foo-1.1: Provides: rubygem-foo = 1.1 So on a system with 0.9 and 1.1 installed rubygem-foo-1.1 satisfies the >= 1.0.0 part and rubygem-foo-0.9 satisfies the < 1.1 part but neither of them is a version that would satisfy the gem dependency. A cleaner solution might be converting it to a "=" requirement but that adds a lot of work on the maintainer. Or adding the requires on the versioned package name Requires: rubygem-foo-1.0 >= 1.0.0 For the rails 3.1 packages[0] i went with the last solution. i have a gem2rpm branch at github[1], that automatically converts all ~> dependencies into the versioned pattern. Will push the package to dlre after todays discussion. I always used the rubygem-gemname-x_y version as I saw the form with 2 digits only once. For some packages (like rubygem-tzinfo) i went with a provides in the package as both rails version can be satisfied by the same version. But for requires we should always use the suffixed versions. 3.3 Packaging for multiple ruby packages Ideally we want to have exactly one spec file that will handle multiple ruby versions for us. We could adapt some of the work that was already done by the kernel team for KMP packages The first step will be to get a list of wanted ruby versions to build against. That could either be through /etc/rpm/macros.ruby%{rb_suffix} files appending their version to a %ruby_versions variable or through some prjconf entry. The ruby_version variable can be overwritten in the spec file to limit the number e.g. for 1.9 only gems. For each entry in the ruby_version variable we create a subpackage in the spec file, either via gem2rpm or automatically as with %lang_pack/%debug_package. Our build section could look something like this [[[ %build for $ruby_version in %{ruby_versions} ; do %gem_install %{S:0} %gem_cleanup done ]]] with the macros defined as [[[ %gem_install /usr/bin/gem${ruby_version} install --verbose --local --build-root=%{buildroot} %gem_cleanup /usr/bin/gem_build_cleanup %%{buildroot}%{_libdir}/ruby/gems/${ruby_version}/gems/%{mod_name}-%{version}/ ]]] That leaves us with finding a solution for the buildrequires/requires. At the moment the list of those 2 is created by gem2rpm when we create the spec. While the list of requires can be easily created at build time with hooking into rpms findprovides/-requires mechanic, we cant do the same easily for the buildrequires. In factory we can just drop a files into /usr/lib/rpm/fileattrs/ and are done. For older distros we would need to overwrite the findprovides/findrequires, but we can easily hook that into our existing rubygems_requires macro. That leaves us with the buildrequires. We could continue to generate them out in the gem2rpm phase. That means we would need some kind of external config for gem2rpm, where we would configure which ruby versions we want to build a package for and then generate the buildrequires for those. The same config could also be used to set the license header e.g. A macro based solution like BuildRequires: %rubygem(foo, '~>', '1.0.0') which expands to (pseudo code) BuildRequires: $ruby_versions.map {|rb_ver| Gem::Requirement.rpm_version_transform_opensuse("ruby${rb_ver}gem-#{$1}", $2, $3) }.join(" ") might not really be supported by the OBS (TODO: talk to mls about if we could add such feature to the OBS) The worst case solution would be generating out all the subpackages in gem2rpm. I dont thing we will ever have to go for multiple spec files for solving the problem. *knocks on wood* [0] https://build.opensuse.org/project/monitor?project=home:darix:rails31 [1] https://github.com/darix/gem2rpm/tree/opensusepatches