openSUSE:Specfile guidelines
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
or (change URL according to your version of openSUSE)
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
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
- AutoReqProv: should not be used unless you want to turn off automatic dependency processing.
- Source: an HTTP link should be specified where possible (openSUSE:Package source verification).
- Group: only package groups listed in the package group guidelines should be used.
- Summary: should be a short description of what the package is (summary guidelines).
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.
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.
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
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
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
- RPM Spec Wizard - An interactive guide for creating RPM spec files