Packaging/Guidelines
Contents
- 1 Build Environment
- 2 Packaging Guidelines
- 3 Macros
- 4 Environments
- 5 Credits
Build Environment
The Build environment consists of a minimal set of packages needed to initiate building a package based on information provided in the spec file. The Build environment is recreated every time a package build is initiated and depending on the number of dependencies it can take some time to set up the environment and make it ready for building the package.
Build Environment Initialization Speedup
A few things can be done to speed up the initialization and setup of the build environment.
- Install only the most basic packages: do not install anything that is uncommon among majority of packages
- Install only needed files: There is no need to install documentation files or translation of the tool-chain and the console tools, this just takes space and time and those files are never used during the build process.
Packaging Guidelines
Legal
Installing License files
License files must be installed with the package. Up to Tizen 2.x, license files need to be installed as documents:
%files %doc LICENSE COPYING
Starting with rpm 4.11, you can install the license using a new file macro %license, this will separate the license files from documents and install them in a special directory in /usr/share
%files %license LICENSE COPYING
License Tag
Having gone through the majority of the packages in Tizen we notice that developer use different names for the same licenses which makes it very difficult to do any surveys and evaluate the licenses being used in Tizen. There are short and long formats, sometimes official full names are used, other times abbreviations. Some licenses are quoted with the version number of the license, other are not. We need a consistent way to identify the licenses in Tizen.
To solve this problem, we started using SPDX license list and identifiers. SPDX is the "Software Package Data Exchange" (http://spdx.org/licenses/) which has a list of commonly used licenses in open-source with a short form that can be used in packages. Using this short form will help us unify and cleanup the license naming mess we currently have.
In addition to the use of the predefined syntax for declaring licenses, the following license grammar should be used in spec files to specify multiple licenses. This can be achieved using operators such as 'and' to declare an aggregation of licenses, or 'or' to show alternative licenses a package can follow. You can also use brackets to gather licenses, for example:
License: (MIT or GPL-2.0) and LGPL-2.1+
which is the declaration for a package with an executable binary and a corresponding LGPL-2.1+ licensed library.
Please also note that in the case of sub-packages, the sub-packages will inherit the license from the main package if no license is specified for the sub-packages.
Missing licenses and licenses special to Tizen will be added as an extension to the SPDX list. If you find such a license, please let us know and we can propose a short form that can be used in all packages.
Package Naming
- Dash '-' must be used as the delimiter for name parts.
- Do NOT use an underscore '_', a plus '+', or a period '.' as a delimiter.
- The spec file should be named using the %{name}.spec scheme which should also correspond to the package name within a project in the build system.
Version and Release
Package Versions look like : X.Y.Z-R.B
- X.Y.Z is the 'Version' number - determined by the source package.
- R is the 'Release' number which is automatically incremented by OBS whenever a source/packaging changes (eg a check-in or request acceptance)
- B is the build number which is incremented when the package is rebuilt due to a dependency change.
Version
The Version field in the spec file is where you should put the current version of the software being packaged. There are four cases where the version contains non-numeric characters:
- Pre-release packages: Packages released as "pre-release" versions, prior to a "final" version. Example tags include "alpha", "beta", "rc", "cvs", "git", "svn", etc... Details can be found below: Non-Numeric Version.
- Post-release packages: Packages released after a "final" version. These packages contain the same numeric version as the "final" version, but have an additional non-numeric identifier. This mechanism may also be used for packaging only changes to an upstream package.
- Snapshot packages: Packages built from SCM snapshots. These packages could be either "pre" or "post" release packages.
Release
This field is handled by the build system to be able to manage automated builds.
The initial setting in the spec file is used by the build system but in many cases it does not need to be changed.
There is no need for the %{dist} macro in the release field. This is also handled directly by the build system.
The release number is set to zero (Release: 0) with any version update.
It is increased by one with any change in the package.
Note: A release number set to one is also acceptable (Release: 1).
We can put letters into the version tag, so we do not use the Release field for this. Details can be found above.
If you build the package outside of the OBS or if you copy a package then you will of course not get the correct Release or Build values.
Tags
- The Packager tag should not be used in spec files. The identities of the packagers are evident from the changelog entries. By not using the Packager tag, you also avoid seeing bad binaries rebuilt by someone else with your name in the header. See also the Maximum RPM definition of the Packager tag at www.rpm.org . If you need to include information about the packager in the rpms you built, use
%packager
in your~/.rpmmacros
instead. - The Vendor tag should not be used. It is set automatically by the build system.
- Usually, the Pre
Req tag should be replaced by plain Requires. For more info, see Maximum RPM snapshot's fine grained dependencies chapter .
- The Source tag documents where to find the upstream sources for the rpm. In most cases this should be a complete URL to the upstream tarball.
Summary Tag
The summary is a single line string describing the package. The maximum length is 80 characters. It should fit all standard situations and not assume any special context. It should be helpful alone, in alphabetically sorted or unsorted lists of some selected packages, and in alphabetically sorted or unsorted lists of all packages.
It should describe the package's main function and point out any special properties of the package to support the user comparing similar packages. For example, the two words "Web Browser" summarize any web browser, but using additional adjectives (like minimalistic, complex, GNOME, KDE, text-based, fast, or author's) helps characterize a specific package.
The RPM spec file contains only the English version to keep the RPM database small.
- The Summary tag value should not end in a period. If this bothers you from a grammatical point of view, sit down, take a deep breath, and get over it.
Group Tag
Note: To simplify packaging and maintenance of groups we now require one group defined per package which should be applied on all sub-packages. Sub packages however need to follow a strict naming convention to allow grouping of supporting files based on function and content provided inside the sub-packages.
- Git tree name SHOULD always correspond to the the upstream project name.
- The main package name SHOULD always correspond to the the upstream project name.
- The main package MIGHT be virtual and DOES NOT have to produce a binary package.
- The main package in most cases will contain services and configuration files.
- Libraries: Libraries SHOULD be packaged individually in lib<LIB NAME> sub-packages where it makes sense. Multiple package CAN be packages in the same lib<PKG NAME> package.
- Utilities and Tools: Utilities SHOULD be package as part of a -tools sub-package.
- Development: Development files and headers SHOULD be packaged in -devel sub-packages
- Documentation: Documentation files and auto-generated documentation SHOULD be packaged in -docs sub-packages
- Testing: Testing scripts and files SHOULD be packaged in a -test sub-package
- Locale: Locale files that are not directly part of any sub-package SHOULD be installed as part of a -locale sub-package (see %lang_package macro).
The following groups have been defined based on the architecture. rpmlint is checking for them, so using different groups increases the "badness" of a package and may cause the package to be rejected when the "badness" goes beyond a certain threshold. The following list is what rpmlint in OBS currently checks for. However, it is outdated and may get updated at some point (see https://bugs.tizen.org/browse/TC-1499):
Application Framework/Alarm Application Framework/Application State Management Application Framework/Database Application Framework/Notifications Application Framework/Package Management Application Framework/Settings Application Framework/API Application Framework/Configuration Application Framework/Development Application Framework/Documentation Application Framework/Libraries Application Framework/Other Application Framework/Service Application Framework/Testing Application Framework/Utilities Applications/Core Applications Applications/Game Applications/Messaging Applications/Multimedia Applications/Music Applications/Native Applications Applications/Navigation Applications/Network Applications/Other Applications/PIM Applications/Photo Applications/Social Applications/Tasks Applications/Telephony Applications/Video Applications/Web Applications/Web Applications Automotive/API Automotive/Configuration Automotive/Development Automotive/Documentation Automotive/Hardware Adaptation Automotive/Libraries Automotive/Other Automotive/Service Automotive/Testing Automotive/Utilities Base/API Base/Compression Base/Configuration Base/Development Base/Device Management Base/Documentation Base/File Systems Base/Hardware Adaptation Base/IPC Base/Libraries Base/Other Base/Package Management Base/Service Base/Startup Base/Testing Base/Toolchain Base/Utilities Network & Connectivity/API Network & Connectivity/Bluetooth Network & Connectivity/Configuration Network & Connectivity/Connection Management Network & Connectivity/DNS Network & Connectivity/Development Network & Connectivity/Documentation Network & Connectivity/HTTP Network & Connectivity/Hardware Adaptation Network & Connectivity/Libraries Network & Connectivity/NFC Network & Connectivity/Other Network & Connectivity/Service Network & Connectivity/Testing Network & Connectivity/Utilities Network & Connectivity/Wireless Social & Content/API Social & Content/Configuration Social & Content/Development Social & Content/Documentation Social & Content/Libraries Social & Content/Other Social & Content/Service Social & Content/Testing Social & Content/Utilities Social & Content/Calendar Social & Content/Contacts Development/Building Development/Cross Compilation Development/Languages Development/Perl Development/Python Development/Testing Development/Tools Graphics & UI Framework/API Graphics & UI Framework/Configuration Graphics & UI Framework/Development Graphics & UI Framework/Documentation Graphics & UI Framework/Fonts Graphics & UI Framework/Hardware Adaptation Graphics & UI Framework/Input Graphics & UI Framework/Input Service Framework Graphics & UI Framework/Libraries Graphics & UI Framework/Other Graphics & UI Framework/Service Graphics & UI Framework/Testing Graphics & UI Framework/Utilities Graphics & UI Framework/Voice Framework Graphics & UI Framework/Wayland Window System Graphics & UI Framework/X Window System Graphics & UI Framework/Mobile UI Graphics & UI Framework/Automotive UI Location/API Location/Configuration Location/Development Location/Documentation Location/Geolocation Location/Hardware Adaptation Location/Libraries Location/Other Location/Service Location/Testing Location/Utilities Messaging/API Messaging/Configuration Messaging/Development Messaging/Documentation Messaging/Email Messaging/Instant Messaging Messaging/Libraries Messaging/Other Messaging/SMS Messaging/Service Messaging/Testing Messaging/Utilities Multimedia/API Multimedia/Audio Multimedia/Camera Multimedia/Configuration Multimedia/Development Multimedia/Documentation Multimedia/Framework Multimedia/Hardware Adaptation Multimedia/Libraries Multimedia/Other Multimedia/Policy Management Multimedia/Service Multimedia/Testing Multimedia/Utilities Multimedia/Video SDK/Configuration SDK/Development SDK/Documentation SDK/Hardware Adaptation SDK/Libraries SDK/Other SDK/Service SDK/Testing SDK/Utilities Security/API Security/Access Control Security/Accounts Security/Certificate Management Security/Configuration Security/Crypto Libraries Security/DRM Security/Development Security/Documentation Security/Libraries Security/Network Security/Other Security/Secure Storage Security/Service Security/Testing Security/Utilities System/API System/Audio System/Configuration System/Development System/Documentation System/Hardware Adaptation System/Kernel System/Libraries System/Localization System/Logging System/Management System/Monitoring System/Network System/Other System/Power Management System/Sensor Framework System/Service System/System Info System/Testing System/Utilities Telephony/API Telephony/Cellular Telephony/Configuration Telephony/Development Telephony/Documentation Telephony/Hardware Adaptation Telephony/Libraries Telephony/Other Telephony/Service Telephony/Testing Telephony/Utilities Web Framework/API Web Framework/Configuration Web Framework/Development Web Framework/Documentation Web Framework/Libraries Web Framework/Other Web Framework/Service Web Framework/Testing Web Framework/Utilities Web Framework/Web Engine Web Framework/Web Run Time
BuildRequires Tag
Please put every single package requirement in a new line. This will make the spec file readable and would make it easier to review changes to the package dependencies.
Also prefer using "BuildRequires: pkgconfig(foo) >= 42" than devel packages "BuildRequires: foo-devel >= 42" ... same for typelib(bar) ...
Requires Tag
Please put every single package requirement in a new line. This will make the spec file readable and would make it easier to review changes to the package dependencies.
PreReq
Packages should not use the PreReq tag. Once upon a time, in dependency loops PreReq used to "win" over the conventional Requires when RPM determined the installation order in a transaction. This is no longer the case.
BuildRoot Tag
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
is not longer needed. This is done by RPM automatically.
Patches
Each problem should be solved in a separate patch. To allow easy maintenance of patches, every patch should have a header providing the following information:
- Authors' names
- Detailed description of the fixed problem
- URL of the original source of the patch if any
The name of a patch file consists of:
- The name and version of the source tarball from which the patched file is derived
- Some words that characterize the patch content
- The filename suffix
.patch
Patches are in the unified format (diff -u) and should be applied with 1 strip level in the spec file (%patch -p1). The only exceptions are the patches obtained from an another primary source site. The original name, suffix, and format is preserved in this case.
Each patch should be compressed with bzip2 if its size is greater than 100kB. The macros %name
and %version
should be used whenever possible.
Example:
Source: %{name}-%{version}.tar.bz2 Patch0: %{name}-%{version}-autoconf.patch Patch1: %{name}-%{version}-gcc31.patch
For the patches to be applied, the patches should be mentioned under %setup. For the above example, this could be done as
%setup -q %patch0 -p1 %patch1 -p1
Patches have to be marked as such in the spec file and should be applied using the internal patch routines available in rpm. Use of alternate patch management system not supported by rpm is not allowed.
%build section
Please use
make %{?_smp_mflags}
instead of
make %{?jobs:-j%jobs}}
%install section
%install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT rm $RPM_BUILD_ROOT%{_libdir}/libxxx.la
can be replaced with a single macro:
%make_install
Note: If you want to keep any static libraries, you should not use this macro. This macro will delete all .a and .la files.
%clean section
%clean rm -rf $RPM_BUILD_ROOT
is no longer needed, this is done by RPM automatically.
%files section
%files %defattr(-,root,root,-) %doc AUTHORS COPYING COPYING.lib LICENSE
%defattr(-,root,root,-) is not needed, it is automatically inserted for all packages.
Whenever possible (and feasible), Tizen Packages containing libraries should build them as shared libraries. In addition, every binary RPM package which contains shared library files (not just symlinks) in any of the dynamic linker's default paths, must call ldconfig in %post
and %postun
. If the package has multiple subpackages with libraries, each subpackage should also have a %post/%postun
section that calls /sbin/ldconfig
. An example of the correct syntax for this is:
%post -p /sbin/ldconfig %postun -p /sbin/ldconfig
Note that this specific syntax only works if /sbin/ldconfig
is the only call in %post
and %postun
. If you have additional commands to run during the scriptlet, call /sbin/ldconfig
at the beginning of the scriptlet, like this:
%post /sbin/ldconfig /usr/bin/foo --add %postun /sbin/ldconfig /usr/bin/foo --remove
Handling Locale Files
If the package includes translations, add
BuildRequires: gettext
If you don't, your package could fail to generate translation files in the buildroot.
Tizen includes an rpm macro called %find_lang
. This macro will locate all of 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 of the locales. %find_lang
should be run in the %install section of your spec file, after all of the files have been installed into the buildroot. The correct syntax for %find_lang
is usually:
%find_lang %{name}
In some cases, the application may use a different "name" for its locales. You may have to look at the locale files and see what they are named. If they are named myapp.mo
, then you will need to pass myapp
to %find_lang
instead of %{name
}.
After %find_lang
is run, it will generate a file in the active directory (by default, the top level of the source dir). This file will be named based on what you passed as the option to the %find_lang
macro. Usually, it will be named %{name}.lang
. You should then use this file in the %files
list to include the locales detected by %find_lang
. To do this, you should include it with the -f parameter to %files
.
%files -f %{name}.lang %defattr(-,root,root,-) %{_bindir}/foobar ...
If you are already using the -f parameter for the %files
section where the locales should live, just append the contents of %{name}.lang
to the end of the file that you are already using with -f. (Note that only one file may be used with %files -f
.)
Here is an example of proper usage of %find_lang
, in foo.spec
:
... %prep %setup -q %build %configure --with-cheese make %{?_smp_mflags} %install make DESTDIR=%{buildroot} install %find_lang %{name} %files -f %{name}.lang %defattr(-,root,root,-) %doc LICENSE README %{_bindir}/foobar
Why do we need to use %find_lang?
Using %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 locales have lots of locales. Using
%find_lang
is much easier in the spec file than having to do:
%{_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.
Keep in mind that usage of %find_lang
in packages containing locales is a MUST.
Scriptlets
Great care should be taken when using scriptlets in Tizen packages. If scriptlets are used, those scriptlets must be sane.
Scriptlets requirements
Do not use the Requires(pre,post)
style notation for scriptlet dependencies, because of two bugs in RPM. Instead, they should be split like this:
Requires(pre): ... Requires(post): ...
For more information, see www.redhat.com .
Running scriptlets only in certain situations
When the rpm command executes the scriptlets in a package it indicates if the action preformed is an install, erase, upgrade or reinstall by passing an integer argument to the script in question according to the following:
install erase upgrade reinstall %pre 1 - 2 2 %post 1 - 2 2 %preun - 0 1 - %postun - 0 1 -
This means that for example a package that installs an init script with the chkconfig
command should uninstall it only on erase and not upgrade with the following snippet:
%preun if [ $1 -eq 0 ] ; then /sbin/chkconfig --del %{name} fi
See also /usr/share/doc/rpm-*/triggers
, which gives a more formal, generalized definition about the integer value(s) passed to various scripts.
Scriplets are only allowed to write in certain directories
Build scripts of packages (%prep, %build, %install, %check and %clean) may only alter files (create, modify, delete) under %{buildroot}, %{_builddir} and valid temporary locations like /tmp, /var/tmp (or $TMPDIR or %{_tmppath} as set by the rpmbuild process) according to the following matrix
/tmp, /var/tmp, $TMPDIR, %{_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: That should hold true irrespective of the builder's uid.
Use of Epochs
The Epoch tag in RPM is to be used only as a last resort, and should be avoided whenever possible. However, it is sometimes necessary to use an Epoch to handle upstream versioning changes or to ease transition from third party repositories.
Configurations and Documentation
RPM keeps special track of files within a package that hold documentation or configuration data. You need to identify these files with special directives. The %doc directive marks a file as a documentation file. For example:
%files /usr/X11R6/bin/xtoolwait %doc /usr/X11R6/man/man1/xtoolwait.*
This example lists all the included files in /usr/X11R6/man/man1 as documentation files. If you don’t include the full path to a documentation file or files, the RPM system will create a special documentation directory for the package, and place those files into that directory. For example:
%doc README NEWS
This example places the files README and NEWS into a newly created package-specific directory, typically a subdirectory under /usr/share/doc or /usr/doc. The %docdir directive names a directory that holds documentation. All files under that directory in the package will get automatically marked as documentation files. For example:
%files /usr/X11R6/bin/xtoolwait %docdir /usr/X11R6/man/man1 /usr/X11R6/man/man1/xtoolwait.*
Note: In addition to the marked directories, the standard Linux documentation directories, such as /usr/share/man, are automatically assumed to be documentation directories. Similar to the %doc directive, the %config directive marks a file as configuration. For example:
%files /sbin/ypbind %config /etc/rc.d/init.d/* %config /etc/yp.conf %doc README NEWS
A special option to the %config directive, noreplace, tells RPM not to overwrite, or replace a configuration file. For example:
%files /sbin/ypbind %config /etc/rc.d/init.d/* %config(noreplace) /etc/yp.conf %doc README NEWS
Use this option to help protect local modifications. If you use %config(noreplace), the file will not overwrite an existing file that has been modified. If the file has not been modified on disk, the rpm command will overwrite the file. But, if the file has been modified on disk, the rpm command will copy the new file with an extra file-name extension of .rpmnew. Similarly, %config(missingok) means that the file does not have to exist on disk. You can use this modifier for files or links that are created during the %post scripts but will need to be removed if the package is removed. Another special modifier, %ghost, tells the rpm command that the file should not be included in the package. You can use this to name the needed attributes for a file that the program, when installed, will create. For example, you may want to ensure that a program’s log file has certain attributes.
RPM spec files have a macro, %config, that is used to mark config files so that edits to config files won't get lost during a subsequent upgrade. Without this, the config files from an upgrade would tend to overrite the edited files from the previous version. %config can also apper as %config(noreplace). There are three things that can vary about files in an RPM that is being upgraded: how the files are marked in the spec file (default, %config, or %config(noreplace)), whether the file itself changed between RPM versions, and whether the file on disk has been edited between installing one version of the RPM and the next.
The following table shows what we ended up with after installing an RPM, optionally editing the resulting files, and then upgrading the RPM.
File marked as | Changed in update RPM? | On-disk file untouched | On-disk file edited |
---|---|---|---|
[default] | No | File from update | File from update |
Yes | File from update | File from update | |
%config | No | File from update | Edited file |
Yes | File from update | File from update, edited file in .rpmsave | |
%config(noreplace) | No | File from update | Edited file |
Yes | File from update | Edited file, file from the update in .rpmnew |
For the two cases where (noreplace) has an effect, there is also the question of what happens if the status of the file as defined in the spec file changes. And the answer is:
File marked as | Changed in update RPM? | On-disk file edited |
---|---|---|
Was %config(noreplace), becomes %config | Yes | File from update, edited file in .rpmsave |
Was %config, becomes %config(noreplace) | Yes | Edited file, file from the update in .rpmnew |
In summary: if a file is not marked as a config file, or if a file has not been altered since installation, then it will be silently replaced by the version from an update RPM. If a config file has been edited on disk, but is not actually different from one RPM to another then the edited version will be silently left in place. It is only when a config file has been edited and is different from one RPM to the next that what happens depends on the(noreplace) option. If absent, the new file will be installed, and the the old edited version will be renamed with a .rpmsave suffix. If present, the edited version will be left in place, and the new version will be installed with a .rpmnew suffix. I don't know what happens if RPM needs to create an .rpmsave or .rpmnew file and one already exists - at least in some cases it seems that the new file isn't written under these circumstances.
This suggests that in general config files should be marked (noreplace), unless the change being implemented is sufficiently major that a config file derived from a previous install is simply not going to work. Even then it seems questionable to me if installing a new 'default' configuration files is better or worse than leaving behind an edited one that may not work.
Comment a macro
you can't just use a '#' at the beguining of the line to comment a rpm macro (comment a rpm raise a rpmlint error).
a good case is %cmake
rpm -E "#%cmake"
so even comment the rpm macro is expended and execute ...
If you want to comment a rpm macro :
%cmake
should become
#%%cmake
Macros
The following macros are widely used in many of the packages:
Documentation Macros
- %remove_docs: Remove all documentation files from well known locations where documentation is usually installed
- %docs_package: create a -doc sub-package will documentation from standard documentation directories
Locale
- %lang_package: package translations into a sub-package (-locale). This package will assume a file with <package name>.lang that has all the locale files. Usually this file is created with the %find_lang macro.
This macro takes 2 arguments for special handing of packages with inconsistent naming:
- -n <name>: Then the main package is virtual and does not have files, you will need the locale package to match the main package that actually get installed on the system
- -f <file>: point to a different file name with locale files (by default %name.lang is taken)
Environments
Python Packages
All Python module packages, whether pure Python or C-based, should be called python-modulename. modulename should be the name of this module on the Python Package Index, the official third-party software repository for the Python programming language.
Credits
Some of the guidelines above are based on both opensuse and fedora packaging guidelines.