openSUSE:Build Service cross distribution howto

Jump to: navigation, search

Overview

This howto lists special hints to work with one spec file for different distributions. It is not a beginners guide for packagers, please visit the Build Service Tutorial for this instead. Also, this document does not cover distributions that use Debian packages.

The Build Service can reliably package rpms for not only openSUSE, but also recent SLES, CentOS, Fedora, Red Hat Enterprise Linux, Ubuntu, Debian and Mandriva/Mageia distributions.

What are the limitations? The only practical limit is not having certain dependencies satisfied for Fedora or Mandriva, where they can be easily satisfied for SUSE distributions by linking or aggregating to another Build Service project. An example: It is easy to build packages for SUSE which require the latest versions of Qt4 or GTK2, as these dependencies can be satisfied by other repos on the Build Service which provide backports of newer Qt4 packages for older SUSE versions, as well as SLES. This can only be overcome by also packaging the needed dependencies within your own project. Even then, this is only an issue if you wish to support as many distributions as possible.

Some 'gotcha' differences between distributions which you need to pay attention to:

  • Installing desktop files. Each of the five distros use somewhat different mechanisms for installing desktop files and then creating the menu entries. Mandriva even has a special rpm macro %update_menus to accomplish this, which uses a Debian-like setup, different from SUSE or Fedora. Example:
%post
%update_menus

%postun
%clean_menus
  • Package naming for dependencies. See below for more specific hints and tricks to make this work in your spec file.
  • Subtle differences in rpm macros. Link with a table showing the variations below.

Can you point me to good example of a cross-platform package ?

Amilcar's KDevelop Package

Detect a distribution flavor for special code

You can add

%if %{defined suse_version}
%if %{undefined suse_version}

or more portable:

%if 0%{?suse_version}
  <suse stuff here>
%else
  <other distros>
%endif

to check if the current build is for a SUSE distribution or not. Note that [there is no '%elseif' to chain multiple tests at the same level. Note that Fedora uses "%{fedora}" without "_version". You can also check for which SUSE distribution by using

%if 0%{?suse_version} > 1130

for instance to execute something for everything after SUSE Linux 11.3. Similar checks for other distributions exist as well.

%if %{defined fedora}
%if %{defined mdkversion}
%if 0%{?fedora} < 5
%if 0%{?mdkversion} > 2006

Note that rpm prefers:

>= 

vs.

=>

You can also exclude one particular version:

%if 0%{?rhel_version} != 406

You can combine multiple distributions in a single if block like this:

%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos_version}
 <yourstuff>
%endif

To group conditionals, split the conditional to several lines, like this:

# mono:  only on suse, sle only from sle10
%if 0%{?suse_version}
%if 0%{?suse_version} >= 1010
BuildRequires:  mono-core
%endif
%endif

Another method is to use parentheses, for example:

# mono:  only on suse, sle only from sle10
%if 0%{?suse_version} && ( 0%{?suse_version} == 0 || 0%{?suse_version} >= 1010 )
BuildRequires:  mono-core
%endif

Grouping with parentheses might fail on older RHEL-based distributions like Fedora, RHEL and CentOS. Anything RHEL-5-based and higher should support grouping. For older versions, split up your conditions and nest them properly.

You can also select a special architecture using e.g.:

%if 0%{?suse_version} == 1310
%ifarch x86_64
 <yourstuff>
%endif
%endif

Warning: The versions listed below can change! To check for the latest version, go to the project page and check the configuration file (e.g. for RHEL 5, look for the content "%rhel_version": https://build.opensuse.org/project/prjconf?project=RedHat%3ARHEL-5). Please update this page if versions doesn't match.

Note: The "sles_version" macro is no longer set in SLE 12.


openSUSE:Packaging_for_Leap#RPM_Distro_Version_Macros help to choose the right settings for openSUSE/SUSE. One can also use osc buildconfig to check macro definitions for a specific build configuration.

Locally you can also run the following to get your value:

 grep suse_version /usr/lib/rpm/suse/macros

Otherwise the following table may help to choose correct settings, including non-(open)SUSE distros:

Distribution Variable Comment
openSUSE Tumbleweed %if 0%{?suse_version} > 1600 current upcoming release (changing)
openSUSE ALP %if 0%{?suse_version} == 1600 currently developed next gen product family
openSUSE Leap 15.5 %if 0%{?sle_version} == 150500 && 0%{?is_opensuse}
openSUSE Leap 15.4 %if 0%{?sle_version} == 150400 && 0%{?is_opensuse}
openSUSE Leap 15.3 %if 0%{?sle_version} == 150300 && 0%{?is_opensuse}
openSUSE Leap 15.2 %if 0%{?sle_version} == 150200 && 0%{?is_opensuse}
openSUSE Leap 15.1 %if 0%{?sle_version} == 150100 && 0%{?is_opensuse}
openSUSE Leap 15.0 %if 0%{?sle_version} == 150000 && 0%{?is_opensuse} could also be Backports:SLE-15¹
openSUSE Leap 42.3 %if 0%{?sle_version} == 120300 && 0%{?is_opensuse} "leap_version" is deprecated, see below
openSUSE Leap 42.2 %if 0%{?sle_version} == 120200 && 0%{?is_opensuse} "leap_version" is deprecated, see below
openSUSE Leap 42.1 %if 0%{?sle_version} == 120100 && 0%{?is_opensuse}
openSUSE Leap 42.x %if 0%{?suse_version} == 1315 && 0%{?is_opensuse} use comparisons above to distinguish between specific versions
openSUSE 13.2 %if 0%{?suse_version} == 1320
openSUSE 13.1 %if 0%{?suse_version} == 1310
openSUSE 12.3 %if 0%{?suse_version} == 1230
openSUSE 12.2 %if 0%{?suse_version} == 1220
openSUSE 12.1 %if 0%{?suse_version} == 1210
openSUSE 11.4 %if 0%{?suse_version} == 1140
openSUSE 11.3 %if 0%{?suse_version} == 1130
openSUSE 11.2 %if 0%{?suse_version} == 1120
openSUSE 11.1 %if 0%{?suse_version} == 1110 could also be SLE11*
openSUSE 11.0 %if 0%{?suse_version} == 1100
openSUSE 10.3 %if 0%{?suse_version} == 1030
openSUSE 10.2 %if 0%{?suse_version} == 1020
SUSE Linux 10.1 %if 0%{?suse_version} == 1010 could also be SLE10*
SUSE Linux 10.0 %if 0%{?suse_version} == 1000
SUSE Linux 9.3 %if 0%{?suse_version} == 930
Backports:SLE-15 %if 0%{?sle_version} == 150000 && 0%{?is_backports}
Backports:SLE-12-SP5 %if 0%{?sle_version} == 120500 && 0%{?is_backports}
Backports:SLE-12-SP4 %if 0%{?sle_version} == 120400 && 0%{?is_backports}
Backports:SLE-12-SP3 %if 0%{?sle_version} == 120300 && 0%{?is_backports}
Backports:SLE-12-SP2 %if 0%{?sle_version} == 120200 && 0%{?is_backports}
Backports:SLE-12-SP1 %if 0%{?sle_version} == 120100 && 0%{?is_backports}
Backports:SLE-12 %if 0%{?sle_version} == 120000 && 0%{?is_backports}
SLE 15 SP4 %if 0%{?sle_version} == 150400 && !0%{?is_opensuse}
SLE 15 SP3 %if 0%{?sle_version} == 150300 && !0%{?is_opensuse}
SLE 15 SP2 %if 0%{?sle_version} == 150200 && !0%{?is_opensuse}
SLE 15 SP1 %if 0%{?sle_version} == 150100 && !0%{?is_opensuse}
SLE 15 GA %if 0%{?sle_version} == 150000 && !0%{?is_opensuse}
SLE 12 SP5 %if 0%{?sle_version} == 120500 && !0%{?is_opensuse}
SLE 12 SP4 %if 0%{?sle_version} == 120400 && !0%{?is_opensuse}
SLE 12 SP3 %if 0%{?sle_version} == 120300 && !0%{?is_opensuse}
SLE 12 SP2 %if 0%{?sle_version} == 120200 && !0%{?is_opensuse}
SLE 12 SP1 %if 0%{?sle_version} == 120100 && !0%{?is_opensuse}
SLE 12 GA %if 0%{?sle_version} == 120000 && !0%{?is_opensuse}
SLE 12 %if 0%{?suse_version} == 1315 && !0%{?is_opensuse} remove "is_opensuse" check to get both Leap 42 and SLE 12
SLE 11 %if 0%{?sles_version} == 11 can also check that "suse_version" is 1110
SLE 10 %if 0%{?sles_version} == 10 can also check that "suse_version" is 1010
SLES 9 %if 0%{?sles_version} == 9 can also check that "suse_version" is 910
CentOS 5 %if 0%{?centos_version} == 505
CentOS 6 %if 0%{?centos_version} == 600
CentOS 7 %if 0%{?centos_version} == 700
RHEL 4 %if 0%{?rhel_version} == 406
RHEL 5 %if 0%{?rhel_version} == 505
RHEL 6 %if 0%{?rhel_version} == 600
RHEL 7 %if 0%{?rhel_version} == 700
RHEL 8 %if 0%{?rhel} == 8
Scientific Linux 6 %if 0%{?scientificlinux_version} == 600
Scientific Linux 7 %if 0%{?scientificlinux_version} == 700
Fedora 6 with Extras %if 0%{?fedora_version} == 6
Fedora 7 with Extras %if 0%{?fedora_version} == 7
Fedora 8 with Extras %if 0%{?fedora_version} == 8
Fedora 9 with Extras %if 0%{?fedora_version} == 9
Fedora 10 with Extras %if 0%{?fedora_version} == 10
Fedora 11 with Extras %if 0%{?fedora_version} == 11
Fedora 15 %if 0%{?fedora_version} == 15
Fedora 16 %if 0%{?fedora_version} == 16
Fedora 17 %if 0%{?fedora_version} == 17
Fedora 18 %if 0%{?fedora_version} == 18
Fedora 19 %if 0%{?fedora_version} == 19
Fedora 20 %if 0%{?fedora_version} == 20
Fedora 21 %if 0%{?fedora_version} == 21
Fedora 36 %if 0%{?fedora} == 36 The fedora_version macro is not available.
Mandriva 2006 %if 0%{?mdkversion} == 2006
Mandriva 2007 %if 0%{?mdkversion} == 2007
Mandriva 2008 %if 0%{?mdkversion} == 2008
Mandriva 2009.0 %if 0%{?mdkversion} == 2009
Mandriva 2009.1 %if 0%{?mdkversion} == 200910
Mandriva 2010.0 %if 0%{?mdkversion} == 201000
Arch Linux %if 0%{?arch_linux} Only works in project configuration of course
Debian %if 0%{?debian} Only works in project configuration of course

* Use 0%{?is_opensuse} to avoid conflicts with SLE versions

¹ Use %if !0%{?is_backports} to differentiated between Leap 15 and Backports:SLE-15

leap_version macro (deprecated)

The "leap_version" macro is now deprecated and has been removed from Leap 15.0.

This macro should not be used for new RPM spec files: use the "sle_version" and "is_opensuse" macros together instead.

See table below for possible values. If a distribution is not listed, the macro is not defined.

Distribution Value
openSUSE Leap 42.1 NOT DEFINED
openSUSE Leap 42.2 420200
openSUSE Leap 42.3 420300
openSUSE Leap 15.0 NOT DEFINED

ServicePacks can only be distinguished for SLES12. SLES11 SP1 sets exactly the same variables as SLES11.

You should also have a look for Leap/SLES here: openSUSE:How_to_contribute_to_Leap

Install info files

Info files should get installed by using the %info_add and %info_del macros. For example

%post
%info_add %{name}.info
%preun
%info_del %{name}.info

Please note that the info files get compressed on some distros as .gz and as .bz2 or even .lzma (recent Mandriva/Mageia). You can use %ext_info for the file suffix in the file list.

Install man files

The man files are compressed on some distros as .gz and as .bz2 or even .lzma (recent Mandriva/Mageia). You can use %ext_man for the file suffix in the file list.

The macros for ext_info & ext_man are not defined for Mageia or Fedora, so you will need to add them to the prjconf.

%if 0%{?mageia} == 6
Macros:
%ext_info .xz
%ext_man .xz

:Macros
%endif

%if 0%{?fedora_version} && 0%{?fedora_version} >= 25
Macros:
%ext_info .gz
%ext_man .gz

:Macros
%endif

Handling dependencies

Different distributions often use different names for packages, so the Requires: and BuildRequires: tags may need to vary from repository to repository. It is possible to specify package name substitutions in the configuration of a project e.g.

%if 0%{?fedora_version}
  Substitute: libnetcdf-devel netcdf
%endif

This will "replace" libnetcdf-devel by netcdf in all affected specfiles.

In order to change your project configuration, use osc meta prjconf <project-name> -e, which starts $EDITOR. In the web client this is hidden in the Advanced section of the central project menu.

In case you are doing this on build.opensuse.org and you think your substitution is of interest to others as well (and might be added to the central configuration) drop a message on opensuse-buildservice@opensuse.org.

If you want to have a look which Substitutes for a special distro already exist, have a look at the project configuration of that distro, e.g. https://build.opensuse.org/project/prjconf?project=CentOS%3ACentOS-6

Recommends: and Suggests:

Suggests: is a feature available in SUSE (>=10) and Fedora (>=24). This is a weak version of "Requires:".

# the package does not need vi or graphviz to run.
%if 0%{?suse_version} >= 1000
Suggests:       vim graphviz
%endif

The same applies to Recommends:

update-alternatives package is SUSE only

update-alternatives is a SUSE package, but not available on Fedora/RHEL, where the functionality provided by the chkconfig package. Solution: just use the full path to the binary:

Requires(post):   /usr/sbin/update-alternatives
Requires(postun): /usr/sbin/update-alternatives

instead of

# Requires(post):   update-alternatives
# Requires(postun): update-alternatives

The other solution is to add the following to the project configuration:

%if ! 0%{?suse_version}
Substitute: update-alternatives chkconfig
%endif

Finding Qt 3.x on Fedora/RHEL

Redhat/Fedora uses a different naming scheme and build setup to use Qt to build other apps. Here is an example from a spec file to build Qt based apps on both SUSE and Fedora in one spec file:

BuildRequires:  cups cups-devel python-devel shared-mime-info libart_lgpl-devel libtiff-devel libxml2-devel
BuildRequires:  fontconfig-devel openssl-devel pkgconfig desktop-file-utils qt-devel

%if 0%{?fedora} >= 5
BuildRequires:  libstdc++-devel gcc-c++ lcms-devel >= 1.12 qt
%endif

%if 0%{?suse_version} > 910
BuildRequires:  update-desktop-files 
%endif

Then:

%if 0%{?fedora} >= 5
source "%{_sysconfdir}/profile.d/qt.sh"
%endif
%configure \
%if 0%{?fedora} >= 5
--with-xinerama \
--with-extra-libs=%{_libdir} \
%endif
%if 0%{?suse_version} > 910
--with-qt-libraries=/usr/%_lib/qt3/%_lib \
--with-docdir=%{prefix}/share/doc/packages/scribus \
%ifarch x86_64 ppc64 s390x
 --enable-libsuffix=64 \ 
%endif
%endif
<programoptions>

Finding Qt4 on Mandriva

By default, Qt3 will be found before Qt4, so simply add this to the %build section of your spec file:

%build  
 
%if 0%{?mandriva_version} > 2006  
export PATH=/usr/lib/qt4/bin:$PATH  
export QTDIR=%{_prefix}/lib/qt4/  
%endif  
  • This may have changed on Mdv 2008

Handling Mandriva Provides/Obsoletes

Special care is needed when handling Mandriva, as unlike CentOS, RHEL, Fedora or SUSE, they often create separate library rpms for many apps which use these libraries privately. Application foo may also have a dependency on libfoo or libfoo-0. Thus, you need to check Mandriva original spec files if they have explicit Provides and Obsoletes to prevent conflicts on install. Even though RPM handles dependencies automatically, you may still need to explicitly added these like below:

%if 0%{?mandriva_version}
Provides: foo libfoo-0
Obsoletes: foo libfoo-0
%endif

Building on Older Distros

While sources in a package can build on older distros, the way packages are built sometimes evolve in an incompatible way.

For example, some spec files sometimes call `autoreconf -fi`, it is possible that the tarball's versions of the config.sub, config.guess and ltmain.sh files are replaced with the copies that live in the autoconf package of the build host as a result of the -i option. When the build host software is sufficiently old and the to-be-built software sufficiently modern, this can cause a downgrade of the config files, which may have negative effects, and even lead to a miscompile, for example when building libapr1 on SLE10.

Thus, it might be better, or even required, that you call autoheader, autoconf or whatever is needed by hand instead of autoreconf on targets older than Factory. An example would be:

%if 0%{?suse_version} > 1010
  autoreconf -fi
%else
  autoheader
  autoconf
%endif

Debian and xUbuntu packages

For these kind of packages, please read the debian builds page.

CentOS and RHEL EPEL repositories

For CentOS and RHEL you might want to use the EPEL repositories. They are available in OBS at Fedora:EPEL:7

Arch Linux packaging guides

Creating packages and Arch packaging standards

Distro RPM Macros and Documentation

openSUSE Packaging Conventions

Fedora Packager's Guide

ROSA's Build RPMs - Quick Start Guide

Mageia's Packaging for Beginners