Ruby-paketoinnin ohjeet
JRuby Gems: Although Fedora has fully functioning JRuby integrated with system RubyGems, we have decided to not include the JRuby specific packaging guidelines here, as they need some more work. They will appear here as soon as we feel that we’ve got everything covered properly. You can contact us on Ruby-SIG mailing list in case of any questions about the prepared JRuby packaging guidelines. |
There are three basic categories of ruby packages: RubyGems, non-gem Ruby packages, and applications written in Ruby. These guidelines contain sections common to all of these as well as sections which apply to each one individually. Be sure to read all the guidelines relevant to the type of ruby package you are building.
Ruby Compatibility
Each Ruby package MUST indicate it depends on a Ruby interpreter (this does
not apply to RubyGems). Use ruby(release)
virtual requirement to
achieve that:
Requires: ruby(release)
If the package requires Ruby of certain version(s), make the requirement versioned like this:
Requires: ruby(release) >= 1.9.1
Alternate interpreters:
Alternate Ruby interpreters (currently JRuby)
also Provide: ruby(release) .
This implies, that pure RubyGems packages (these are shared among interpreters) SHOULD NOT have Requires: ruby or Requires: jruby to have their dependencies satisfied by any of these interpreters.
|
Over specified ruby(release) versioning:
Please note that if the ruby(release) version requirement is too specific,
it might cause an unexpected interpreter to be drawn in.
E.g. ruby(release) = 1.8 will require JRuby package,
since it is the only package that provides it.
|
Different Interpreters Compatibility
Most of the pure Ruby packages will work on all Ruby interpreters. There
are however cases when the packages use interpreter-specific functions (like
fork()
) and won’t run on other interpreters (JRuby). In this case, the
package SHOULD require that interpreter. For example, a package that uses
fork
SHOULD explicitly specify Requires: ruby
. In case of such package,
packager SHOULD file a bug to ask upstream to provide support for other
interpreter(s). This SHOULD be documented in specfile.
Shebang lines
On Fedora, /usr/bin/ruby
is implemented via
Rubypick. Rubypick is a tool similar
to RVM or rbenv. It allows choosing interpreter to execute Ruby script.
Rubypick routes anything executed via /usr/bin/ruby
to /usr/bin/ruby-mri
or /usr/bin/jruby
. By default, it runs MRI (Matz’s Ruby Implementation),
but user can explicitly specify the interpreter by using _mri_
or
_jruby_
as a first parameter. For example:
ruby _jruby_ jruby_script.rb gem _mri_ install foo rails _jruby_ s
Using the RUBYPICK
environment variable can achieve the same results. The
environment variable can be used to set one interpreter as the global
default:
export RUBYPICK=_jruby_ ruby jruby_script.rb # Will use jruby gem install foo # Will also use jruby
Ruby executables that are known to only run on one Ruby implementation
SHOULD use that specific implementation in their shebang
(#!/usr/bin/ruby-mri
or #!/usr/bin/jruby
) to ensure that they run
using that implementation. All other code SHOULD use #!/usr/bin/ruby
.
Naming Guidelines
-
Packages that contain Ruby Gems MUST be called
rubygem-%{gem_name}
. -
The name of a ruby extension/library package MUST start with the interpreter it is built for (ruby, jruby, etc.) and then the
UPSTREAM
name. For example:ruby-UPSTREAM
. If the upstream nameUPSTREAM
containsruby
, that SHOULD be dropped from the name. For example, the SQLite database driver for ruby is calledsqlite3-ruby
. The corresponding Fedora package SHOULD be calledruby-sqlite3
, and notruby-sqlite3-ruby
. -
Application packages that mainly provide user-level tools that happen to be written in Ruby MUST follow the general Naming Guidelines instead.
Macros
Non-gem ruby packages and ruby gem packages install to certain standard
locations. The ruby-devel
and rubygems-devel
packages contain macros
useful for the respective package types. Alternate ruby interpreters will
have equivalent locations (to be added to this table later).
Macro | Expanded path | Usage |
---|---|---|
From ruby-devel; intended for non-gem packages. |
||
|
|
Place for architecture specific (e.g. *.so) files. |
|
|
Place for architecture independent (e.g. *.rb) files. |
|
|
Place for local architecture specific (e.g. *.so) files. |
|
|
Place for local architecture independent (e.g. *.rb) files. |
From rubygems-devel; intended for gem packages. |
||
|
|
Top directory for the Gem structure. |
|
|
Directory with the actual content of the Gem. |
|
|
The |
|
|
The cached Gem. |
|
|
The Gem specification file. |
|
|
The rdoc documentation of the Gem. |
|
|
The directory for MRI Ruby Gem extensions. |
Interpreter independence and directory macros
You might have noticed that the table above has different directories for non-gem libraries on different ruby interpreters but only a single set of directories for rubygem libraries. This is because code written for one ruby interpreter will often run on all ruby interpreters that Fedora ships (ruby, jruby, etc.). However, some code uses methods that are not available on all interpreters (see Different Interpreters Compatibility). Rubygems have a facility to ship different versions of the code in the same gem so that the gem can run on all versions of the interpreter, so we only need to have one common directory for rubygems that all the interpreters can use.
The standard ruby %{vendorlib}
directories lack this facility. For this
reason, non-gem libraries need to be placed in per-interpreter directories
and MUST have a separate subpackage (or package depending on upstream) for
each interpreter that they support.
Libraries
These guidelines only apply to Ruby packages whose main purpose is providing a Ruby library; packages that mainly provide user-level tools that happen to be written in Ruby MUST follow the Ruby applications Guidelines instead.
RubyGems
RubyGems are Ruby’s own packaging format. Gems contain a lot of the same metadata that RPM’s need, making fairly smooth interoperation between RPM and Gems possible. This guideline ensures that Gems are packaged as RPMs in a way that such RPMs fit cleanly with the rest of the distribution and makes it possible for the end user to satisfy dependencies of a Gem by installing the appropriate RPM-packaged Gem.
Both RPM’s and Gems use similar terminology; there are specfiles, package names, dependencies, etc. for both. To keep confusion to a minimum, terms relating to Gem concepts will be explicitly referred to with the word "Gem" prefixed, e.g., "Gem specification" (.gemspec). An unqualified "package" in the following always means an RPM.
-
Spec files MUST contain a definition of
%{gem_name}
which is the name from the Gem’s specification. -
The
Source
of the package MUST be the full URL to the released Gem archive; the version of the package MUST be the Gem’s version. -
The package MUST have
BuildRequires: rubygems-devel
to pull in the macros needed to build. -
There SHOULD NOT be any rubygem
Requires
norProvides
listed, since those are autogenerated. -
There SHOULD NOT be
Requires: ruby(release)
, unless you want to explicitly specify Ruby version compatibility. The automatically generated dependency on RubyGems (Requires: ruby(rubygems)
) is enough.
Filtering Requires and Provides
Runtime requires and provides are automatically generated by RPM’s dependency generator. However, it can sometimes throw in additional dependencies contrary to reality. To fix this, the dependency generator needs to be overridden so that the additional dependencies can be filtered out. See AutoProvidesAndRequiresFiltering for details.
Building gems
Since gems aren’t just an archive format but instead encapsulate both an archive and information used for building the Ruby library, building an RPM from a gem looks a little different from other RPMs.
A sample spec for building gems would look like this:
%prep %setup -q -n %{gem_name}-%{version} # Modify the gemspec if necessary # Also apply patches to code if necessary %patch0 -p1 %build # Create the gem as gem install only works on a gem file gem build ../%{gem_name}-%{version}.gemspec # %%gem_install compiles any C extensions and installs the gem into ./%%gem_dir # by default, so that we can move it into the buildroot in %%install %gem_install %install mkdir -p %{buildroot}%{gem_dir} cp -a ./%{gem_dir}/* %{buildroot}%{gem_dir}/ # If there were programs installed: mkdir -p %{buildroot}%{_bindir} cp -a ./%{_bindir}/* %{buildroot}%{_bindir} # If there are C extensions, copy them to the extdir. mkdir -p %{buildroot}%{gem_extdir_mri} cp -a .%{gem_extdir_mri}/{gem.build_complete,*.so} %{buildroot}%{gem_extdir_mri}/
%prep
RPM (as of 4.14) can directly unpack gem archives, so we can call gem
unpack
to extract the source from the gem. Then we call %setup -n
%{gem_name}-%{version}
to tell rpm what the directory the gem has unpacked
into. At the same directory level, the %{gem_name}-%{version}.gemspec file
is created automatically as well. This .gemspec
file will be used to
rebuild the gem later. If we need to modify the .gemspec
(for instance, if
the version of dependencies is wrong for Fedora or the .gemspec
is using
old, no longer supported fields) we would do it here. Patches to the code
itself can also be done here.
%build
Next we build the gem. Because %gem_install
only operates on gem
archives, we next recreate the gem with gem build
. The gem file that is
created is then used by %gem_install
to build and install the code into
the temporary directory, which is ./%{gem_dir}
by default. We do this
because the %gem_install
command both builds and installs the code in
one step so we need to have a temporary directory to place the built sources
before installing them in %install
section.
%gem_install
macro accepts two additional options:
- -n <gem_file>
-
Allows to override gem used for installation. This might get useful for converting legacy spec, so you might specify %{SOURCE0} as a gem for installation.
- -d <install_dir>
-
Might override the gem installation destination. However we do not suggest to use this option.
The %gem_install macro MUST NOT be used to install into the
%{buildroot}
|
%install
Here we actually install into the %{buildroot}
. We create the
directories that we need and then copy what was installed into the temporary
directories into the %{buildroot}
hierarchy. Finally, if this ruby gem
creates shared objects the shared objects are moved into the arch specific
%{gem_extdir_mri}
path.
Patching required gem versions
One common patching need is to change overly strict version requirements in
the upstream .gemspec
. This could be because upstream’s .gemspec
only
mentions versions that they’ve explicitly tested against but we know that a
different version will also work or because we know that the packages we
ship have applied fixes for problematic behavior without bumping the version
number (for instance, backported fixes). To adjust such dependencies, you
can use the %gemspec_add_dep
and %gemspec_remove_dep
macros.
For example, if you wanted to use any version of Aruba instead of the overly
specific version requested by upstream, you could use in %prep
section
following two lines:
%gemspec_remove_dep -g aruba "~> 0.14.2" %gemspec_add_dep -g aruba
Use macros only on top of generated .gemspec: The %gemspec_add_dep and %gemspec_remove_dep macros work reliably only on .gemspec generated using the ruby spec command. Please don’t use the macros on upstream .gemspec files.
|
Be sure to test: Do not simply change versions without testing that the new version works. There are times the upstream is overly strict but there are also times when the version requirement was specified because a specific bug was encountered or the API changed in a minor release. |
Packaging for Gem and non-Gem use
Packaging for non-Gem use is no longer needed: Originally, rubygem modules were not placed in ruby’s library path, so we packaged rubygems for use with both gems and non-gems. This allowed code that used require('ARubyModulePackagedAsAGem'); to function. The current rubygem module adds all gems to the ruby library path when it is require’d. So, packagers MUST NOT create non-Gem subpackages of rubygems for new packages. Since the majority of Ruby packages in Fedora are now packaged as installed gems, you might need to patch the code to use require('rubygem') as early in the program as possible to ensure that these ruby components are properly found. Packages for ruby gems that currently create a non-gem subpackage SHOULD be adapted to stop shipping the non-gem subpackage (with a proper Obsoletes and Provides in the main rubygem package).
|
Non-Gem Packages
Non-Gem Ruby packages MUST require ruby-devel package at build time with a
BuildRequires: ruby-devel
, and MAY indicate the minimal ruby version they
need for building.
Build Architecture and File Placement
The following only affects the files that the package installs into
%{ruby_vendorarchdir}
and %{ruby_vendorlibdir}
(the actual Ruby
library files). All other files in a Ruby package MUST adhere to the general
Fedora packaging conventions.
Site versus Vendor: Previously, %{ruby_sitelibdir} and %{ruby_sitearchdir} were used. However, as they are meant only for local installations, please use %{ruby_vendorlibdir} and %{ruby_vendorarchdir} instead.
|
Pure Ruby packages
Pure Ruby packages MUST be built as noarch packages.
The Ruby library files in a pure Ruby package MUST be placed into
%{ruby_vendorlibdir}
(or its proper subdirectory). The specfile MUST use
this macro.
Ruby packages with binary content/shared libraries
For packages with binary content, e.g., database drivers or any other Ruby bindings to C libraries, the package MUST be architecture specific.
The binary files in a Ruby package with binary content MUST be placed into
%{ruby_vendorarchdir}
(or its proper subdirectory). The Ruby files in
such a package SHOULD be placed into %{ruby_vendorlibdir}. The specfile
MUST use these macros.
For packages which create C shared libraries using extconf.rb
export CONFIGURE_ARGS="--with-cflags='%{optflags}'"
SHOULD be used to pass CFLAGS
to Makefile
correctly. Also, to place the
files into the correct folders during build, pass --vendor
to extconf.rb
like this:
extconf.rb --vendor
Sovellukset
Applications are
-
programs that provide user-level tools or
-
web applications, typically built using Rails, Sinatra or similar frameworks.
The RPM packages MUST obey FHS rules. They SHOULD be installed into
%{_datadir}
. The following macro can help you:
%global app_root %{_datadir}/%{name}
These packages typically have no Provides
section, since no other
libraries or applications depend on them.
Here’s an abbreviated example:
%global app_root %{_datadir}/%{name} Summary: Deltacloud REST API Name: deltacloud-core Version: 0.3.0 Release: 3%{?dist} Group: Development/Languages License: ASL 2.0 and MIT URL: http://incubator.apache.org/deltacloud Source0: http://gems.rubyforge.org/gems/%{name}-%{version}.gem Requires: rubygem-haml #... Requires(post): chkconfig #... BuildRequires: rubygem-haml #... BuildArch: noarch %description The Deltacloud API is built as a service-based REST API. You do not directly link a Deltacloud library into your program to use it. Instead, a client speaks the Deltacloud API over HTTP to a server which implements the REST interface. %package doc Summary: Documentation for %{name} Group: Documentation Requires:%{name} = %{version}-%{release} %description doc Documentation for %{name} %prep %setup -q -n %{name}-%{version} %build %install mkdir -p %{buildroot}%{app_root} mkdir -p %{buildroot}%{_initddir} mkdir -p %{buildroot}%{_bindir} cp -r * %{buildroot}%{app_root} mv %{buildroot}%{app_root}/support/fedora/%{name} %{buildroot}%{_initddir} find %{buildroot}%{app_root}/lib -type f | xargs chmod -x chmod 0755 %{buildroot}%{_initddir}/%{name} chmod 0755 %{buildroot}%{app_root}/bin/deltacloudd rm -rf %{buildroot}%{app_root}/support rdoc --op %{buildroot}%{_defaultdocdir}/%{name} %post # This adds the proper /etc/rc*.d links for the script /sbin/chkconfig --add %{name} %files %{_initddir}/%{name} %{_bindir}/deltacloudd %dir %{app_root}/ %{app_root}/bin #... %files doc %{_defaultdocdir}/%{name} %{app_root}/tests %{app_root}/%{name}.gemspec %{app_root}/Rakefile %changelog #...
Note, that although the source is a RubyGem, we have to install the files
manually under %{_datadir}/%{name}, %{_bindir}, etc. to follow FHS and
general packaging guidelines. If additional Fedora specific files (systemd
.service
files, configurations) are required, they SHOULD be
-
added via another
%SOURCE
tags
Source1: deltacloudd-fedora
-
placed into appropriate locations during
%install
stage
install -m 0755 %{SOURCE1} %{buildroot}%{_bindir}/deltacloudd
Running test suites
If there is test suite available for the package (even separately, for
example not included in the gem but available in the upstream repository),
it SHOULD be run in %check
. The test suite is the only automated tool
which can assure basic functionality of the package. Running it is
especially helpful when mass rebuilds are required. You MAY skip test suite
execution when not all build dependencies are met but this MUST be
documented in the specfile. The missing build dependencies to enable the
test suite SHOULD be packaged for Fedora as soon as possible and the test
suite re-enabled.
The tests SHOULD NOT be run using Rake, as Rake almost always draws in some unnecessary dependencies like hoe or gemcutter. For similar reasons, a dependency on Bundler SHOULD be avoided. Also, code coverage frameworks such as SimpleCov and Coveralls SHOULD be avoided.
Testing With Different Ruby Implementations
To run tests with different Ruby implementation such as JRuby, add
BuildRequires: jruby
. Then use Rubypick’s interpreter switching:
ruby _jruby_ -Ilib -e 'Dir.glob "./test/test_*.rb", &method(:require)'
If your package is running unittests for ruby-mri and it is intended to run under alternate interpreters then it needs to run the unittests under all alternate interpreters as well. This is the only method we have to check compatibility of the code under each interpreter. The same rules apply that you can omit this if libraries you need are unavailable for a specific alternate interpreter but you MUST have a comment to explain.
Testing frameworks usage
The Ruby community supports many testing frameworks. The following sections demonstrate how several to execute test suites using the more common of them.
MiniTest / Test::UNIT
MiniTest as well as Test::UNIT are shipped with Ruby. To use them, you need
to use BuildRequires: rubygem-minitest
or BuildRequires:
rubygem-testunit
respectively. To execute the test suite you can use
something like:
%check ruby -Ilib -e 'Dir.glob "./test/**/*_test.rb", &method(:require)'
You might need to adjust -Ilib
to be -Ilib:test
, or you could need
to use a slightly different matching pattern for test_*.rb
,
etc. Packagers are expected to use the right pattern for each gem.
Test suites not included in the package
Sometimes you have to get the tests separately from upstream’s gem
package. As an example lets suppose you’re packaging rubygem-delorean
,
version 1.2.0, which is hosted on Github. Tests are not included in the Gem
itself, so you need to get them and adjust the specfile accordingly:
# git clone https://github.com/bebanjo/delorean.git && cd delorean # git checkout v1.2.0 # tar -czf rubygem-delorean-1.2.0-specs.tgz spec/ Source1: %{name}-%{version}-specs.tgz # ... %prep %setup -q -n %{gem_name}-%{version} -b 1 # ... %check pushd ./%{gem_instdir} # Link the test suite into the right place in source tree. ln -s %{_builddir}/spec . # Run tests rspec spec popd # ...
-
Make sure to include the version of the tests in the source name, so that when updating to new version, rpmbuild will fail because it won’t find the proper
%{SOURCE1}
(and this will remind you to update the tests, too). -
Add the commands you used to get the tests into the specfile as comments. This will make it a lot easier the next time you will need to get them.
-
Run the tests as you normally would.