Packaging/RpmLint

From openSUSE

Contents

RPM Lint

As an experimental new feature, rpmlint is run after a successful package build. In this setup it runs a custom configuration that scores commonly valid rpmlint warnings with a badness score, and optionally fails the build if it surpasses a threshold. The idea behind that is that big regressions don't enter the distribution, but are catched early.

RPMLint comes out of the box with a heap of checks that are sometimes very useful, sometimes questionable. In addition, the SUSE rpmlint package contains additional checks that were written for our own use. For example, there is a checker trying to validate compliance to the SUSE Shared Library Packaging Policy.

The Badness concept

In order to get an overview, and limit the impact on packaging, only selected RPMLint messages are considered to be an error. The set of these messages is configurable. Everything else is degraded to a warning, and warnings will not fail the build.

A couple of checks have been assigned a badness rating. Each build will accumulate badness, and if it surpasses a threshold, might fail the build. As long as you're below the threshold, even errors will not cause a build failure. However, they're listed to guide your work and you should give errors a higher priority in fixing (or alternatively, surpressing them).

Supressing False Positives

In cases where you want to disable a false positive that is specific to your package, you can add a custom suppression to your source dir. For that to work, add a file named %{name}-%{version}-rpmlintrc to your Source list in the spec file, and add the following content to it:

  addFilter("perl devel-file-in-non-devel-package")

This example filter suppresses any hits of the warning named devel-file-in-non-devel-package when the (sub-)package name is perl. In general, you should write the check to be as specific as possible and as unspecific as needed. So, in order to suppress only a hit about one particular file, you should write something like

  addFilter("spurious-executable-perm .*/usr/share/doc/packages/cups/PrintAnalyzer")

In general, you can use any valid Perl/Python regular expression for filtering, and it will match it directly on the line of text as it was printed by rpmlint.

As this suppression file is however not part of the binary package, running rpmlint manually on the resulting rpm will again print even the suppressed warnings. This is by intention. If there is a systematic rpmlint warning that should be removed in general, please file a bug report.

Testing your package

The SUSE version of RPM Lint is frequently updated and contains many customisations that are not upstream (and some of them being SUSE specific probably will never be). The one in SuSE Factory is the one that is used for Factory builds, but you don't actually have to rebuild your package to test it against rpmlint. It can be called from command line

 rpmlint <list of rpm packages>

For that, you should install the rpmlint from the openSUSE build service

RPM Lint checks during Factory build

The following is a list of checks that currently have badness assigned inside SUSE Factory build.


arch-dependent-file-in-usr-share

This package installs an ELF binary in the /usr/share hierarchy, which is reserved for architecture-independent files. Binaries that are only usable on a certain architecture (e.g. compiled C code), should be installed under %libdir/package.

As an exception for certain packages, this check tries to avoid a warning if the path is architecture specific, like for example /usr/share/tetex/bin/linux/x86/tetex. The use of this exception is however not recommended.

wrong-script-interpreter

This script uses an incorrect interpreter in its shebang. the script interpreter should be a full path to a packaged interpreter, which means it shouldn't point to /usr/local/bin for example.

arch-independent-package-contains-binary-or-object

Your package has been marked as noarch, but contains a binary that is architecture specific.

library-without-ldconfig-postin

The package installs a library, but doesn't call ldconfig in its %post install. without an updated ldconfig cache, the library cannot be used. This is a frequent problem during installation, because following packages might require the library in their %post script.

files-duplicated-waste

The package contains some files duplicate, which wastes installation space and rpm size. consider using the %fdupes macro.

summary-not-capitalized

Package summaries should start with a capital letter.


executable-docs

Documentation under %docdir contains executable files. Documentation should not be executable.

spurious-executable-perm

Files that are likely not supposed to be executable are marked as such. This can be subject of false positives, so carefully review if those are correct or if not, suppress them.

devel-file-in-non-devel-package

The package contains content that should be in a -devel subpackage. The advantage of cleaning your package up and splitting it properly into a -devel package is twofold:

  1. it will keep installation size for customers/users who do not compile stuff on their own small
  2. it will allow you to add proper build dependencies to the -devel subpackage as requires, which makes life of your customer easier: In order to install the -devel package, he also has to install all the tools that are commonly required for this package, making troubleshooting a lot easier for them.

There a several subchecks that could cause this check to be triggered.

  • A header file under /usr/include. It detects files ending as \.h and a couple of other extensions as header files
  • A .so symlink in %libdir that points to a versioned so file. Usually, those files are only needed during building. However, there can be the exception that the application tries to dlopen() the library for some reason, so there might be valid reasons for not splitting the .so file into a subpackage. However, this should be a rare event.

In most cases however, the error is triggered because of versioned libtool modules. Plugins should be build with -avoid-version and -module parameters in LDFLAGS, which will avoid triggering this check as well.

If for example the sole purpose of your the particular shared library is to be a plugin that is loaded by some other application (a good indication for this is that it is installed not directly in %libdir but a subdirectory of it, and this sub directory is not in /etc/ld.so.conf path), then you should validate the LDFLAGS that were used for building this module, and make sure that -module and -avoid-version were in it. To apply versioning, the subdirectory the plugins are installed to should be versioned instead.

wrong-file-end-of-line-encoding

Some of the files have windows style (CRLF) line ending. Under certain circumstances, this can cause incompatibilities with viewer applications. The check is scored very low, so you might just not care about it. If you care, you should try recoding the files in question to unix-style LF only.

hardlink-across-partition

Your package contains two files that are apparently hardlinked and that are likely on different partitions. Installation of such an RPM will fail due to RPM being unable to unpack the hardlink, if the files that are hardlinked are ending up on two different physical partitions. By policy, do not hardlink across the first two levels of a path, e.g. between /srv/ftp and /srv/www or /etc and /usr.

RPM Lint checks that might end up in Factory builds

There is a general group of checks that try to validate the SUSE Shared Library Packaging policy. Those checks start with "shlib-policy" and are non-fatal right now. If you introduce new packages, you should try to make them match the policy. For established packages, there is probably little reason to go through the additional hassle of renaming them and keeping the upgrade path working.

The remaining tests are documented below in more detail.

file-not-in-%lang

This usually indicates gettext translation files (those ending in .mo) that were not properly tagged by their language for rpm. We might use this information in the future to be able to leave out certain languages during installation (for example, other ideas are possible and are already in experimental implementation available). To properly tag them, use the %find_lang macro that is provided.

untranslated-desktop-file

We frequently get translation bugs that occur because .desktop files were not tagged for Novell translations. In order to do that, you have to run %suse_update_desktop_file on each of those files. Doing so will allow the openSUSE translators and the Novell translation team to add translations for your desktop files automatically during your package build.

Note that if this is a KDE3 or KDE4 package, the error is caused by a missing call to %kde_post_install at the end of the %install section.

init-script-without-%stop_on_removal-preun

The package contains an /etc/init.d script and the package that installs it doesn't contain a call to %stop_on_removal in the %preun script. This is required per SUSE packaging conventions.

init-script-without-%insserv_cleanup-postun

The package contains an /etc/init.d script and the package does not call %insserv_cleanup in the %postun script. This is required per SUSE packaging conventions.

no-prereq-on

The package contains a %pre/%post/%preun%postun script that references an external binary that is however not explicitely listed in the prerequires (PreReq:) or requires (Requires:) of the package. Fixing this is necessary so that installation of the package happens in the correct order, e.g. the packages necessary for executing the %post script are already installed.

Enabling rpmlint checks in buildservice repositories

Michal Marek has enabled rpmlint checks in his buildservice repository "home:michal-m:rpmlint" and includes all currently used rpmlint packages. So packagers who want to enable rpmlint checks for their repository just have to edit their project config using osc like

Image:Shellscript.png osc meta prj -e <your_project>

and add Michael's project as build repository. Have a look at the example below:

<repository name="openSUSE_Factory">
 <path repository="openSUSE_Factory" project="home:michal-m:rpmlint"/>
 <path repository="openSUSE_Factory" project="<yourproject>"/>
 <arch>i586</arch>
 <arch>x86_64</arch>
</repository>

This adds Michael's project as build repository to your openSUSE_Factory repository. As Michael's project just contains three different rpmlint versions, this shouldn't confuse you and your project config too much.

You can also add rpmlint manually using osc meta prjconf <your_project> and add something like:
%if 0%{?suse_version} >= 1010
Support: rpmlint-bs
%endif

This will install rpmlint-bs to all your builds and run the rpmlint command afterwards (you just need an up to date package rpmlint-bs).

Remember that you need to have a '%clean' section in the spec for rpmlint to run at all.

If you're interested in further information, have a look at the thread in the mailinglist.

False checks in rpmlint

This section lists checks that contradict suse packaging conventions. Please add one if you run accross one.