No edit summary |
|||
Line 119: | Line 119: | ||
%install | %install | ||
mkdir -p %{buildroot}%{gem_dir} | mkdir -p %{buildroot}%{gem_dir} | ||
mkdir -p %{buildroot}%{gem_extdir} | mkdir -p %{buildroot}%{gem_extdir}/foo | ||
cp -a .%{gem_dir}/* %{buildroot}%{gem_dir}/ | cp -a .%{gem_dir}/* %{buildroot}%{gem_dir}/ | ||
mv %{buildroot}%{gem_instdir}/ | mv %{buildroot}%{gem_instdir}/foo/shared_object.so %{buildroot}%{gem_extdir}/foo/shared_object.so | ||
</pre> | </pre> | ||
{{admon/note|Library placement|Please note the <code>foo</code> subdirectory of <code>%{buildroot}%{gem_extdir}</code>. The <code>foo</code> '''must''' be replaced by first path defined by <code>require_paths</code> in Gem specification. Typically, it is replaced either by <code>lib</code> or <code>ext</code>.}} | |||
* The Gem package with C extension must own the <code>%{gem_extdir}</code> directory. | * The Gem package with C extension must own the <code>%{gem_extdir}</code> directory. | ||
* Installed C codes (usually under <code>%{gem_instdir}/ext</code>) '''may''' be removed even if <code>gem contents %{gem_name}</code> reports that installed C codes should be found there. | * Installed C codes (usually under <code>%{gem_instdir}/ext</code>) '''may''' be removed even if <code>gem contents %{gem_name}</code> reports that installed C codes should be found there. |
Revision as of 09:15, 18 January 2012
Ruby ABI
Each Ruby package must indicate the Ruby ABI version it depends on with a line like
Requires: ruby(abi) = 1.9.1
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 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 RPM's in a way that ensures (1) that such RPM's fit cleanly with the rest of the distribution and (2) make 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, whenever the term from the Gem world is meant, it is explicitly called the 'Gem specification'. An unqualified 'package' in the following always means an RPM.
- Spec files must contain definition of
%{gem_name}
, which is the name from the Gem's specification. - Packages that contain Ruby Gems must be called
rubygem-%{gem_name}
. - 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 a
Requires
onruby(rubygems)
and aBuildRequires
onrubygems-devel
. Therubygems-devel
package carries amacros.rubygems
file with following definitions:
Macro | Expanded path | Usage |
---|---|---|
%{gem_dir}
|
/usr/share/gems | Top directory for the Gem structure. |
%{gem_instdir}
|
%{gem_dir}/gems/%{gem_name}-%{version} | Directory with the actual content of the Gem. |
%{gem_libdir}
|
%{gem_instdir}/lib | The lib folder of the Gem.
|
%{gem_cache}
|
%{gem_dir}/cache/%{gem_name}-%{version}.gem | The cached Gem. |
%{gem_spec}
|
%{gem_dir}/specifications/%{gem_name}-%{version}.gemspec | The gemspec file of the Gem.
|
%{gem_docdir}
|
%{gem_dir}/doc/%{gem_name}-%{version} | The rdoc documentation of the Gem. |
%{gem_extdir}
|
%{_libdir}/gems/exts/%{gem_name}-%{version} | The directory for binary Gem extensions. |
- The package must provide
rubygem(%{gem_name})
wheregem_name
is the name from the Gem's specification. For every dependency on a Gem namedgemdep
, the package must contain aRequires
onrubygem(%{gemdep})
. Packager must ensure that the package works properly with its specified dependencies. Please note, that Fedora may carry different versions of Gems than those specified in Gem's.gemspec
, therefore the versions required in specfile may not match the dependencies in.gemspec
exactly. In that case, the.gemspec
file must be adjusted accordingly. - The
%prep
section should contain the localgem install
similar to this (assuming that the%{gem_name}-%{version}.gem
isSOURCE0
):
%setup -q -c -T mkdir -p .%{gem_dir} gem install \ -V \ --local \ --install-dir .%{gem_dir} \ --force \ --rdoc \ %{SOURCE0}
- If the Gem contains executable files, you must ad
--bindir .%{_bindir} \
option to thegem install
command. - The
%build
section of the specfile should be empty. - The
%install
section should then be used to copy the files to appropriate directory structure under%{buildroot}
, for example:
mkdir -p %{buildroot}%{gemdir} cp -a .%{gemdir}/* \ %{buildroot}%{gemdir}/
- If the Gem contains executable files, you must also add:
mkdir -p %{buildroot}%{_bindir} cp -a .%{_bindir}/* \ %{buildroot}%{_bindir}/
- The Gem must be installed into
%{gem_dir}
. - The package must own the following files and directories:
%dir %{gem_instdir} %{gem_libdir} %{gem_spec} %doc %{gem_docdir}
- Since the Gem is installed using RPM, you must exclude the .gem file. This file is used typically with 'gem pristine' command to restore the Gem into its original state, but this could be achieved by equivalent RPM command. Exclude the cached Gem like this:
%exclude %{gem_cache}
- If the Gem only contains pure Ruby code, it must be marked as
BuildArch: noarch
. If the Gem contains binary content (e.g., for a database driver), it must be marked as architecture specific.
RubyGem with extension libraries written in C
Some Ruby Gems may contain extension libraries written in C. These Gems carry an extensions section in their .gemspec
file. Their specfiles have to be slightly adjusted to reflect the presence of binary files.
- The %prep section is the same as for pure Ruby Gem, but it must add CONFIGURE_ARGS before the
gem install
command:
%setup -q -c -T mkdir -p .%{gem_dir} export CONFIGURE_ARGS="--with-cflags='%{optflags}'" gem install \ -V \ --local \ --install-dir .%{gem_dir} \ --force \ --rdoc \ %{SOURCE0}
- The
-V
option should be used withgem install
to check ifCFLAGS
is correctly honored. - During the
%install
section all architecture specific content must be moved from the%{gem_instdir}
to the%{gem_extdir}
. Usage of %{gem_extdir} assures that the binary files are placed under the proper directory under/usr
according to FHS.
%install mkdir -p %{buildroot}%{gem_dir} mkdir -p %{buildroot}%{gem_extdir}/foo cp -a .%{gem_dir}/* %{buildroot}%{gem_dir}/ mv %{buildroot}%{gem_instdir}/foo/shared_object.so %{buildroot}%{gem_extdir}/foo/shared_object.so
- The Gem package with C extension must own the
%{gem_extdir}
directory. - Installed C codes (usually under
%{gem_instdir}/ext
) may be removed even ifgem contents %{gem_name}
reports that installed C codes should be found there.
Packaging for Gem and non-Gem use
If the same Ruby library is to be packaged for use as a Gem and as a straight Ruby library without Gem support, it must be packaged as a Gem first. To make it available to code that does not use Ruby Gems, a subpackage called ruby-%{gem_name}
must be created in the rubygem-%{gem_name}
package such that
- The subpackage must require
rubygem(%gem_name) = %version
- The subpackage must provide
ruby(LIBRARY) = %{version}
where LIBRARY is the same as in the general Ruby guideline above. - All the toplevel library files of the Gem must be symlinked into
%{ruby_vendorlibdir}
. - The subpackage must own these symbolic links.
As an example, for activesupport
, the rubygem-activesupport
package would have a subpackge ruby-activesupport
:
%package -n ruby-activesupport ... Requires: rubygem(activesupport) = %version Provides: ruby(active_support) = %version # The underscore is intentional, not a typo ... %files -n ruby-activesupport %{ruby_vendorlibdir}/active_support %{ruby_vendorlibdir}/active_support.rb
Tips for Packagers
Gems carry a lot of metadata; gem2rpm is a tool to generate an initial specfile and/or source RPM from a Gem. The generated specfile still needs some hand-editing, but conforms to 90% with this guideline.
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.
Naming Guidelines
These naming guidelines only apply to non-Gem Ruby packages whose main purpose is providing a Ruby library; packages that mainly provide user-level tools that happen to be written in Ruby do not need to follow these naming guidelines, and should follow the general NamingGuidelines instead.
The name of a ruby extension/library package must be of the form ruby-UPSTREAM
. If the upstream name UPSTREAM
contains ruby
, that should be dropped from the name. For example, the SQLite database driver for ruby is called sqlite3-ruby
. The corresponding Fedora package should be called ruby-sqlite3
, and not ruby-sqlite3-ruby
.
A ruby extension/library package must indicate what it provides with a Provides: ruby(LIBRARY) = VERSION
declaration in the spec file. The string LIBRARY
should be the same as what is used in the require
statement in a Ruby script that uses the library. The VERSION
should be the upstream version of the library, as long as upstream follows a sane versioning scheme. For example, a Ruby script using the SQLite database driver will include it with require 'sqlite3'
. The specfile for the corresponding Fedora package must contain a line Provides: ruby(sqlite3) = 1.1.0
, assuming the package contains version 1.1.0 of the library.
Build Architecture and File Placement
The following only affects the files that the package installs into %{_libdir}/ruby/vendor_ruby
(architecture specific) and %{_datadir}/ruby/vendor_ruby
(architecture independent), i.e. Ruby library files. All other files in a Ruby package must adhere to the general Fedora Extras packaging conventions.
A list of the macros included in ruby-devel (file /etc/rpm/macros.ruby) package follows:
Macro | Expanded path | Usage |
---|---|---|
%{ruby_vendorarchdir}
|
/usr/lib{64}/ruby/vendor_ruby | Place for architecture specific (e.g. *.so) files. |
%{ruby_vendorlibdir}
|
/usr/share/ruby/vendor_ruby | Place for architecture independent (e.g. *.rb) files. |
%{ruby_sitearchdir}
|
/usr/local/lib{64}/ruby/site_ruby | Place for local architecture specific (e.g. *.so) files. |
%{ruby_sitelibdir}
|
/usr/local/share/ruby/site_ruby | Place for local architecture independent (e.g. *.rb) files. |
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.
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
The CONFIGURE_ARGS
definition applies to Ruby Gems, too.
Applications
By applications we mean:
- programmes that provide user-level tools or
- web applications, typically built using Rails, Sinatra or similar frameworks.
Their RPM packages should obey FHS rules, it means that they should be installed into %{_datadir}. 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.
A good example is the initial specfile of deltacloud-core package (shortened here):
%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 -c -T gem unpack -V --target=%{_builddir} %{SOURCE0} %build %install mkdir -p %{buildroot}%{app_root} mkdir -p %{buildroot}%{_initddir} mkdir -p %{buildroot}%{_bindir} cp -r %{_builddir}/%{name}-%{version}/* %{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, gem unpack
is used instead of common gem install
to extract the application and library files. These are then placed 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 suite
If there is test suite available for the package (even separately, for example not included in the Gem, but available in the upstream repository), you should run it. The test suite is the only tool which could assure basic functionality of package. This is helpful when mass rebuild is required for example. You could skip test suite execution only in case when not all build dependencies are met, but this must be documented in specfile. Nevertheless, the build dependencies should be imported into Fedora as soon as possible.
The tests should not be run using Rake - Rake almost always draws in some unnecessary dependencies like hoe or gemcutter.
Testing frameworks usage
Ruby community provides and supports various testing frameworks. Following paragraphs demonstrates how the test suite can be executed. Please note that the list is not exhaustive.
MiniTest
MiniTest is default testing framework shipped together with Ruby, however unbundled in Fedora (you must use BuildRequires: rubygem(minitest)). To run the tests using MiniTest, you can usually use something like
testrb -Ilib test
Test::UNIT
To run the tests using Test::Unit (you must use BuildRequires: rubygem(test-unit)
), you can usually use something like
testrb2 -Ilib test
RSpec
To run the tests using RSpec >= 2 (you must use BuildRequires: rubygem(rspec-core)
), you can usually use something like
rspec -Ilib spec
Test suite not included in package
Typical way of getting the tests separately from upstream follows - lets suppose you're packaging rubygem-delorean, version 1.2.0, which is hosted on Git. 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-spec-1.2.0.tgz spec/ Source1: %{name}-spec-%{version}.tgz # ... %check pushd .%{gem_instdir} tar xzf %{SOURCE1} 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.