openSUSE:Specfile guidelines

Jump to: navigation, search
RPM itself places very few limitations on what you can do in spec files, so they can quickly become difficult to read and maintain. These guidelines try to minimize the variations in specific areas so spec files are maintainable.

General Rules

All spec files must be comprehensible. If other packagers are unable to read the spec file, it will be impossible to perform a review and to collaborate on the package.

Specfile Template

When writing a package from scratch, you should base your spec file on the spec file template (see rpmdevtools). A basic setup for a package MYPACK can be generated by

osc-plugin-install devel:tools rpmdevtools

or (change URL according to your version of openSUSE)

sudo zypper in osc rpmdevtools

Once you have done so:

# A folder already created for a project, like home:user.
cd MYPROJECT
# Create a new package.
osc mkpac MYPACK
cd MYPACK
# Download source tarball from upstream (the group that develops the code).
wget http://upstream.example.org/source/.../MYPACK-1.0.tar.gz
# Create a spec file template for MYPACK. rpmdev-newspec -t lib MYPACK # Record changes to the MYPACK package. osc vc # Edit spec file. vim MYPACK.spec

and then later:

# Test it builds correctly
osc build
# Checkin/commit to OBS server
osc ci

Please try to conform to this template as much as possible.

It is not the only way to write a spec file, but doing so makes it easier for QA to spot mistakes and quickly understand what you are trying to do.

Spec files for specific languages can often be created with specialized tools like cpanspec or gem2rpm-opensuse. See also

Specfile Encoding

Use ASCII characters unless necessary (if so, save as UTF-8).

If you are in doubt as to what characters are ASCII, please refer to an ASCII chart.

Specfile Licensing

For legal reasons, spec files must have a license header.

Use the following template:

#
# spec file for package $YOUR_PACKAGE
#
# Copyright (c) $CURRENT_YEAR $YOUR_NAME_WITH_MAIL_ADDRESS
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.

# Please submit bugfixes or comments via http://bugs.opensuse.org/
#

If there is no such header yet, you can run:

osc service run format_spec_file

to have one added automatically for you.

Macros

Use macros instead of hard-coded directory names.

A list of macros and what they do is available at openSUSE:Packaging Conventions RPM Macros. Having macros in a Source: or Patch: line is a matter of style. Some people enjoy the ready readability of a source line without macros. Others prefer the ease of updating for new versions when macros are used. In all cases, remember to be consistent in your spec file and verify that the URLs you list are valid. If you need to determine the actual string when it contains macros, you can use rpm. For example, to determine the actual Source: value, you can run:

$ rpm -q --specfile foo.spec --qf "$(grep '^Source[0-9]*:' foo.spec)\n"

Or you can just use rpmdev-spectool from rpmdevtools package:

$ rpmdev-spectool -~sources foo.spec
Icon-warning.png
Warning: Caution: the above command works with %{name} and %{version}, but may fail with user defined macros.

Macros vs RPM variables

rpm re-exports a few of its macros as shell variables implicitly at the start of %prep, %build and %install. Excerpt from /usr/lib/rpm/macros:

%___build_pre   \
  RPM_SOURCE_DIR=\"%{u2p:%{_sourcedir}}\"\
  RPM_BUILD_DIR=\"%{u2p:%{_builddir}}\"\
  RPM_OPT_FLAGS=\"%{optflags}\"\
  RPM_ARCH=\"%{_arch}\"\
  RPM_OS=\"%{_os}\"\
  RPM_BUILD_NCPUS=\"%{_smp_build_ncpus}\"\
  export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS RPM_BUILD_NCPUS\
  RPM_DOC_DIR=\"%{_docdir}\"\
  export RPM_DOC_DIR\
  RPM_PACKAGE_NAME=\"%{NAME}\"\
  RPM_PACKAGE_VERSION=\"%{VERSION}\"\
  RPM_PACKAGE_RELEASE=\"%{RELEASE}\"\
  export RPM_PACKAGE_NAME RPM_PACKAGE_VERSION RPM_PACKAGE_RELEASE\
  LANG=C\
  export LANG\
  unset CDPATH DISPLAY ||:\
  %{?buildroot:RPM_BUILD_ROOT=\"%{u2p:%{buildroot}}\"\
  export RPM_BUILD_ROOT}\
  %{?_javaclasspath:CLASSPATH=\"%{_javaclasspath}\"\
  export CLASSPATH}\
  PKG_CONFIG_PATH=\"${PKG_CONFIG_PATH}:%{_libdir}/pkgconfig:%{_datadir}/pkgconfig\"\
  export PKG_CONFIG_PATH\

It is technically possible to use either; they will resolve to the same values in all (sensible) scenarios. However, not all macros, such as %{_bindir}, are mapped to a shell variable. Do not mix the two styles in openSUSE packages, as it is bad from a QA and usability point of view.

RPM macros are expanded before a section is run, and it is slower for variables to be read by sh during runtime.

Macros vs shell commands

Don't use the macro version of shell commands.

For many commonly used shell commands, there is a macro equivalent. For example (excerpt from /usr/lib/rpm/macros),

%__cat                  /usr/bin/cat
%__chgrp                /usr/bin/chgrp
%__chmod                /usr/bin/chmod
%__chown                /usr/bin/chown
…

These macros are meant to improve portability with non-GNU platforms such as Solaris or FreeBSD (where the GNU utilities may have names like gchgrp instead of chgrp). Since openSUSE only supports the GNU/Linux platform, plain shell commands are much easier to read than macros. The spec file cleaner bot even converts macros to their shell command counterpart automatically.

Conditionals

See openSUSE:RPM conditional builds.

For a list of version macros for different openSUSE releases, see openSUSE RPM distro version macros.

For example, to detect Leap 42.1:

%if 0%{?sle_version} == 120100 && 0%{?is_opensuse}
# perform Leap 42.1 specific actions here
%else
# something else
%endif

For other distributions, see openSUSE:Build_Service_cross_distribution_howto.

Preamble

Naming / versioning

See openSUSE:Package naming guidelines.

Metadata Tags

Do not use the following tags:

  • Copyright: deprecated, use License instead (licensing guidelines).
  • Packager: rebuilding the RPM package elsewhere forces this value onto the new packager, leading to subsequent confusion about who to contact. The identities of the packagers should be specified with changelog entries instead.
  • Vendor: same reason as for Packager. If you want to override the default set by the openSUSE Build Service, use a prjconf-level %define.

Description Section (%description)

This should basically answer what the software does and why someone would need or want to have the package. See openSUSE:Package description guidelines for more information.

Dependencies

See openSUSE:Package dependencies.

Patches

All patches in openSUSE spec files SHOULD have a comment within the patch file (or above its respective "PatchN:" tag) about its status. For details see openSUSE:Packaging Patches guidelines.

Preparation Section (%prep)

Quiet %setup

The -q option should be passed to the %setup macro.

This will reduce the size of the build logfile significantly, especially with source archives that extract a lot of files.

Build Section (%build)

Compiler flags

Compilers used to build packages should honor the applicable compiler flags set in the system rpm configuration.

In practice, this means %{optflags} (variable: $RPM_OPT_FLAGS, see above) for C, C++, and Fortran compilers. Honoring means that the contents of that variable is used as the basis of the flags actually used by the compiler during the package build. Adding to and overriding or filtering parts of these flags is permitted if there is a good reason to do so; the rationale for doing so should be reviewed and documented in the specfile especially in the override and filter cases.

Werror

In some cases upstream might decide to add -Werror to their CFLAGS/CXXFLAGS/etc to ensure upstream is informed about potential code issues by the compiler.

This is a good idea for upstream developers, but should be disabled by the packager. New compiler versions will generate different warnings (which will now be errors!), which makes it difficult to upgrade the compiler toolchain and support multiple openSUSE versions.

To remove -Werror there might be configure switches like --disable-werror or one has to patch the -Werror switch out of the various Makefiles.

Verbose mode

It's recommended to use a make option to print full invocation of commands run during build process. It helps to identify which compile flags are used and whether %{optflags} are properly applied. Typical example: make V=1.

Parallel make

Whenever possible, invocations of make should be done in parallel.

This should be done using:

make %{?_smp_mflags}

This is also available as the %make_build macro, but it is not available for openSUSE 13.2, Leap 42.2 and SLE 12 SP2 (rpm < 4.12).

This generally speeds up builds, especially on SMP machines. It is much preferable to make %{?jobs:-j%jobs}, as the former allows alternate make flags to be used, such as make -lN, and not hardcoding -jN. Do make sure, however, that the package builds cleanly this way; some make files do not support parallel building, or have broken dependencies.

If a package does not support parallel building, please explicitly write -j1.

This facilitate grepping for such packages among a group of specfiles. There may be other reasons to use -j1, for example due to otherwise excessive memory usage. (Such a case occurs for example when building Boost for MINGW.) The common workers on build.opensuse.org have only 2 GB RAM assigned as of September 2013. In any case, consider adding a comment saying what led to the use of -j1, whether it is due to broken dependencies, or memory requirements, etc.

Building constraints

It is possible to use OBS _constraints file to limit the type of worker that will be used to build a given package. One can specify a minimum number of jobs, available memory, and memory per job.

Install Section (%install)

Running make install must not attempt to chown any files.

Since rpmbuild is run as an unprivileged user, the %install section must not run chown directly or indirectly, for example, `install -o root...`). File ownership shall be set in the later %files section instead.

Use %make_install instead of %makeinstall (without an underscore).

%make_install is a macro equivalent to make install DESTDIR="%{?buildroot}". Should you be creating a package for older RPM versions (rpm < 4.10, e.g. SLE 11), use the expanded version instead of the macro.

There is also a second macro with a confusingly similar name, %makeinstall (note: no underscore). Avoid using this.

In attempt to work with broken software, this macro expands to a very long line which sets all variables to %buildroot plus their final paths, but does not set DESTDIR itself, which causes certain broken software to actually fail.

An example of this is xapian-core-1.2.17, where:

# configure.ac contains:
incdir=$includedir
AC_SUBST(incdir)
# include/Makefile.mk contains:
inc_HEADERS = include/xapian.h

With this, %makeinstall's expansion, "make install ... includedir=/buildroot/usr/include" has no effect since the awkward Makefile requires incdir to be set instead of includedir. Because %makeinstall does not set DESTDIR, xapian-core will try to install xapian.h to the default location, /usr/include, rather than /buildroot/usr/include. A double failure, so to say.

SUSE's rpm redefines %makeinstall to be the same as %make_install, but as it is conceivable that your specfile may be used on other distro targets, it is best to avoid %makeinstall, and only ever use %make_install or its expanded form.

Removing the buildroot

Do not attempt to clean or remove the buildroot folder manually.

openSUSE marks it as bad coding style to have rm -rf %{buildroot} (or rm -rf $RPM_BUILD_ROOT) at the beginning of an %install section like this:

%install
rm -Rf "%buildroot"
mkdir -p "%buildroot/%_prefix/..." 

or

%install
rm -Rf "%buildroot"
make install

Why?

%{buildroot} is normally within /var/tmp and you just opened a trivial race condition to a local attacker on your machine to take over your account (or even root if you build as root). It is better not to `rm -rf %{buildroot}` in %install at all (and rely on %clean to do it).

Somewhat better would be the following, (do not do this, it is done automatically for you):

%install
rm -Rf "%buildroot";
mkdir "%buildroot";
mkdir -p "%buildroot/%_prefix/..."

or

%install
rm -Rf "%buildroot";
mkdir "%buildroot";
make install

In this case the mkdir %buildroot would fail and the build would abort if an attacker tries to replace the buildroot by his own symlink.

Clean Section (%clean)

The %clean section should not be included -- it is no longer necessary.

The %clean section, if specified, will be run after the binary and source RPMs have been generated. In the Open Build Service, this section is not necessary because chroots and VM environments used to build the package are generally torn down anyway. Building packages in environments not started from scratch is usually not supported for openSUSE packages (boo#176528#c4).

Starting from rpm 4.8 (openSUSE 11.2+), rpm defaults to "%clean: rm -Rf %{buildroot}" if the %clean section is completely absent from the spec file.

If a package contains a %clean section, it should be safe to remove. Check with another maintainer first if you are not sure.

Scriptlets

Great care should be taken when using scriptlets. Some common scriptlets are documented in openSUSE:Packaging scriptlet snippets.

Scriptlet requirements

Your package has to require everything you use in scriptlets.

The notation for %pre*/%post* scriptlets is as follows:

Requires(pre): ...
Requires(post): ...

Scriplets are only allowed to write in certain directories.

Build scripts of packages shall only alter (create, modify, delete) files under %buildroot, %_builddir and valid temporary locations like /tmp, /var/tmp (or %_tmppath as set by the rpmbuild process) according to the following table:

/tmp, /var/tmp, %_tmppath %_builddir %buildroot
%prep yes yes no
%build yes yes no
%install yes yes yes
%check yes yes no
%clean yes yes yes

Further clarification: This must hold true irrespective of the builder's uid.

Files Section (%files)

openSUSE follows the Filesystem Hierarchy Standard (FHS) with regards to filesystem layout, which defines where files should be placed on the system. Any deviation from the FHS should be justified in a comment in the spec file.

Ownership

Your package should own all files that are installed as part of the %install process.

Packages must not own files already owned by other packages (or, if they legitimately do, the package needs a Conflict: tag). The rule of thumb here is that the first package to be installed should own the files that other packages may rely upon. If you feel that you have a good reason to own a file or that another package owns, then please present that at package review time.

Directory ownership is a little more complex than file ownership. Although the rule of thumb is the same: own all the directories you create but none of the directories of packages you depend on, there are several instances where it is desirable for multiple packages to own a directory. Examples of this include:

Forward compatibility with future versions of a package

The package you depend on to provide a directory may choose to own a different directory in a later version and your package will run unmodified with that later version.

One common example of this is a Perl module. Assume perl-A-B depends on perl-A and installs files into /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi/A/B. The base Perl package guarantees that it will own /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi for as long as it remains compatible with version 5.8.8, but a future upgrade of the perl-A package may install into (and thus own) /usr/lib/perl5/vendor_perl/5.9.0/i386-linux-thread-multi/A. So the perl-A-B package needs to own /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi/A as well as /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi/A/B in order to maintain proper ownership.

Common directory for unrelated packages

Multiple packages may have files in a common directory, but are not necessarily dependent on one of the packages. Take the following example:

  • Package foo-animal-emu puts files into /usr/share/Foo/Animal/Emu
  • Package foo-animal-llama puts files into /usr/share/Foo/Animal/Llama

Neither package depends on the other one. Neither package depends on any other package which owns the /usr/share/Foo/Animal directory. In this case, each package must own the /usr/share/Foo/Animal directory.

This is to prevent unowned directories from being installed on a system.

An openSUSE package must not contain any duplicate files in the %files listing.

Files packaged more than once (in two or more subpackages) and files containing the same content are both considered to be duplicate files.

It is recommended to run %fdupes %buildroot (requires adding BuildRequires: fdupes) to smartly eliminate duplicate content by replacing those files with hardlinks to one another.

Permissions

Permissions on files must be set properly. Executables should be set with executable permissions, as should scripts (those with a #! line).

SUID bits

SUID bits set by default require explicit approval by the SUSE Security Team.

See the openSUSE package security guidelines for more information.

Documentation files

Any relevant documentation included in the source distribution should be included in the package.

Irrelevant documentation include build instructions, the omnipresent INSTALL file containing generic build instructions, for example, and documentation for non-Linux systems, e.g. README.MSDOS. Also pay attention about which subpackage you include documentation in, for example API documentation belongs in the -devel subpackage, not the main one. Or if there is a lot of documentation, consider putting it into a subpackage of its own. In this case, it is recommended to use *-doc as the subpackage name, and Documentation as the value of the Group tag.

Any file marked as %doc must not affect the runtime of the application.

That is, if it is in %doc, the program must run properly if it is not present.

Build documentation with Sphinx

To build a bundled documentation with Sphinx to HTML, add the following Build Requirement:

BuildRequires:  python3-Sphinx

In the build section we do the actual build

%build
...
pushd docs
%make_build html
rm _build/html/.buildinfo
popd

And in the files section we can add the built documentation:

%files
...
%doc docs/_build/html

If the documentation is too big, create a -doc sub-package.

License files

License files should be marked with %license for newer products. See https://lists.opensuse.org/opensuse-factory/2016-02/msg00167.html for reasoning. %doc should be avoided. To be backward compatible use a definition like:

%if 0%{?suse_version} < 1500
%doc LICENSE
%else
%license LICENSE
%endif

Configuration files

Configuration files must be marked as such in packages. As a rule of thumb, use %config(noreplace) instead of plain %config unless your best, educated guess is that doing so will break things. In other words, think hard before overwriting local changes in configuration files on package upgrades. An example case when not to use noreplace is when a package's configuration file changes so that the new package revision would not work with the config file from the previous package revision. Whenever plain %config is used, add a brief comment to the specfile explaining why.

Do not use %config or %config(noreplace) under /usr. /usr is deemed to not contain configuration files in openSUSE.

Furthermore, files should be listed only once. These statements in a files section would also mark the defaults.foo as noreplace. Note, that the globbing in the first line also matches the file from the second line.

%config(noreplace) *.foo
%config            defaults.foo

Correct alternatives are to list all files explicitly - or - dynamically create a file with the %files statements in it and then let the files macro read from it with %files -f filelist.txt.

Development files

If the software being packaged contains files intended solely for development, those files should be put in a -devel subpackage. The following are examples of file types which should be in -devel:

  • Header files (e.g. .h files)
  • Unversioned shared libraries (e.g. libfoo.so). Versioned shared libraries, e.g. libfoo.so.3, libfoo.so.3.0.0 should not be in -devel.
  • pkgconfig files. A reasonable exception is when the main package itself is a development tool, e.g. gcc or gdb.

Packages containing pkgconfig (.pc) files must utilize BuildRequires: pkg-config so that the proper runtime dependency Requires: pkg-config is added.

Locale files

openSUSE includes a couple of RPM macros, %lang_package and %find_lang, that become handy when dealing with software that provides translations. The %lang_package macro is useful when translations are bulky, taking up good space, and will automatically produce a %{name}-lang subpackage. It's usually placed right after the (sub)package(s) description(s), and before %prep:

%description
...

%lang_package

%prep
%autosetup -p1
For more details on %lang_package, visit Packaging Conventions RPM Macros.

Now, the %find_lang macro will locate all the locale files that belong to your package (by name), and put this list in a file. You can then use that file to include all the locales. %find_lang should be run in the %install section of your spec file, after all the files have been installed into the buildroot.

%install
...
%find_lang %{name} %{?no_lang_C}

Using both %lang_package and %find_lang helps keep the spec file simple, and helps avoid several other packaging mistakes.

  • Packages that use %_datadir/* to grab all the locale files in one line also grab ownership of the locale directories, which is not permitted.
  • Most packages that have translations have lots of locale files. Using %find_lang is much easier in the spec file than having to do:
%files lang
...
%_datadir/locale/ar/LC_MESSAGES/%name.mo
%_datadir/locale/be/LC_MESSAGES/%name.mo
%_datadir/locale/cs/LC_MESSAGES/%name.mo
%_datadir/locale/de/LC_MESSAGES/%name.mo
%_datadir/locale/es/LC_MESSAGES/%name.mo
...
  • As new locale files appear in later package revisions, %find_lang will automatically include them when it is run, preventing you from having to update the spec any more than is necessary.
  • To include the locale files found by %find_lang you have add the -f %{name}.lang option to the %files section. So the section header should look like %files -f %{name}.lang.
%files
...

%files lang -f %{name}.lang

%changelog
For more details on %find_lang, visit Packaging Conventions RPM Macros.

Non-ASCII filenames

Filenames that contain non-ASCII characters must be encoded as UTF-8.

Since there is no way to note which encoding the filename is in, using the same encoding for all filenames is the best way to ensure users can read the filenames properly. If upstream ships filenames that are not encoded in UTF-8, you can use a utility like convmv (from the convmv package) to convert the filename in your %install section.

Libexecdir

The GNU build system (autotools) offers a "libexecdir" variable/option, which specifies a location "for installing executable programs to be run by other programs rather than by users". Packages often create a subdirectory in libexecdir, for which they may also use the predefined convenient variable ${pkglibexecdir} in Automake files; one might find "pkglibexec_PROGRAMS = myhelper" in Makefile.am. On a default run, the directory will be /usr/local/libexec.

An unmodified copy of the rpm utility mirrors this behavior in that the %_libexecdir macro expands to %_prefix/libexec. (Because rpm commonly sets prefix=/usr, the resulting path in practice is /usr/libexec.)

The Filesystem Hierarchy Standard for a long time did not specify libexec, including the long-lived version 2.2 and 2.3, and SUSE products eschewed the use of /usr/libexec through a patch to rpm which declared %_libexecdir as /usr/lib instead. Other distributions and operating systems, such as from the RedHat/Linux line, or the BSDs, used libexec as originally intended.

FHS version 3.0 introduced (or maybe re-introduced) libexec into the specification, and openSUSE Tumbleweed from 2020-08-25 onwards, as well as products derived from there, expand to /usr/libexec again. This is to say that Leap 15.3 will remain on using /usr/lib.

Changelog section (%changelog)

openSUSE uses a separate changelog file instead of placing it in the spec file.

See HOWTO write good changes for more information.

More resources