openSUSE:Packaging guidelines
- It is the reviewer's responsibility to point out specific problems with a package and a packager's responsibility to deal with those issues. The reviewer and packager work together to determine the severity of the issues — whether they block a package or can be worked on after the package is in the repository.
- The packaging guidelines are a collection of common issues and the severity that should be placed on them. While these guidelines should not be ignored, they should also not be blindly followed. If you think that your package should be exempt from part of the guidelines, please bring the issue to the openSUSE-packaging mailing list.
- Please also note that, in the Build Service, a lot of the rules will be enforced, or warned about, after a successful build, by a tool called rpmlint. The packager should always check its output to be notified of common packaging errors and to get hints where the packaging could be improved. See Packaging checks for explanations about most of the rpmlint warnings. That page also contains instructions how false positives can be suppressed.
- If there is a need to change the guidelines please follow the change process as outlined.
General guidelines
There are some general rules that must be followed.
No inclusion of pre-built binaries or libraries
All binaries or libraries included with openSUSE packages must have been built from source code included in the source package. This is a requirement for the following reasons:
- Security: Pre-packaged binaries and libraries not built from source could include anything, malicious or dangerous content, or just being plain broken. Also, these are functionally impossible to patch and bugfix.
- Compiler flags: Pre-packaged binaries and libraries not built from source probably do not have the standard openSUSE compiler flags for security and optimization.
If you are in doubt as to whether something is considered a binary or library, here is some helpful criteria:
- Is it executable? If so, it is probably a binary.
- Does it contain a .so, .so.#, .so.#.# or .so.#.#.# extension? If so, it is probably a library.
- If in doubt, ask on the openSUSE-packaging mailing list.
Packages which require non-open source components to build are also not permitted (e.g. proprietary compiler required).
Exceptions
- Some software (usually related to compilers or cross-compiler environments) cannot be built without the use of a previous toolchain or development environment (open source). If you have a package which meets this criteria, contact the openSUSE Packaging Committee for approval.
- An exception is made for binary firmware, as long as it meets the documented requirements: BinaryFirmware
Bundling of multiple projects
Packages in openSUSE should make every effort to avoid having multiple, separate, upstream projects bundled together in a single package.
Specfile guidelines
The rules that apply to content of specfiles are in a separate document called the Specfile guidelines.
Architecture support
All openSUSE packages must successfully compile and build into binary RPM files on at least one of the supported architectures. The packagers should make every effort to make the package build for supported architectures. Content — code which does not need to be compiled or built — and architecture independent code (noarch) are notable exceptions.
Relocatable packages
The use of RPM's facility for generating relocatable packages is strongly discouraged. It is difficult to make it work properly, impossible to use from the installer or from zypper and yast, and not generally necessary if other packaging guidelines are followed. However, in the unlikely event that you have a good reason to make a package relocatable, you MUST state this intent and reasoning in the request for package review.
Legal
Banned software
Applications (or other software) listed on the Build Service application blacklist are banned from being packaged.
Donation requests
Packaging content (descriptions/summaries/comments/...) should never contain direct donation requests neither for upstream project nor for the packager. Even if we do not deny need for donations with such projects they should market the campaign on their website and not use downstream packaging for such purposes.
If you find package that is asking for donations at runtime it is up to the packager to keep this code or patch it out.
Licensing
Software licenses should comply with the Open Source Definition (which is currently at version 1.9). If you are in doubt as to whether a particular license conforms with the Open Source Definition, you should file a bug using the category SUSE Tools -> SUSE Linux Legal Issues.
Spec Files
Packagers usually come into contact with software licensing through the spec file. A spec file can declare the license(s) for the entire package or for individual subpackages. Licenses should be declared using SPDX shortname format. SPDX and its associated list of licenses is relatively young, however, the list is limited and does not cover all licenses an openSUSE packager will typically have to address. To temporarily work around this limitation, we have created a spreadsheet which contains not only the existing SPDX licenses but also many other licenses which are acceptable for either openSUSE Factory or openSUSE NonFree. If you still can't find a suitable license short name there, you should file a bug using the category SUSE Tools -> SUSE Linux Legal Issues.
As well as using a predefined syntax for declaring licenses, openSUSE also recognizes a kind of license grammar in spec files. You can use operators such as 'and' to declare an aggregation of licenses, or 'or' to show that either one or the other license should be chosen. You can also use brackets to gather licenses, for example
License: (MIT or GPL-2.0) and LGPL-2.1+
could be the declaration for a package with an executable binary and a corresponding LGPL-2.1+ licensed library.
Another thing to be aware of when you are writing spec files is that subpackages will inherit the main license of the package if the packager omits to enter a license specifically for that subpackage. This is not always ideal. For example, if you decide to create a separate subpackage for documentation, you should check if the documentation is licensed under e.g. GFDL-1.1 rather than the (e.g.) GPL-2.0+ license of the main package. It is advisable to always add a license to a subpackage - even if the subpackage is coincidentally under the same license as the main package. This makes it immediately obvious to anybody reading the spec file later.
Finally, license texts should always be copied into the package. This is usually done by adding the filename to the %files section using the %license macro in the spec file. Licenses are often found in files with names such as COPYING, COPYING.LIB, LICENSE.txt etc. See https://lists.opensuse.org/opensuse-factory/2016-02/msg00167.html for a discussion around that.
Code vs Content
It is important to make distinction between computer executable code and content. While code is permitted (assuming, of course, that it has an open source compatible license, is not legally questionable, etc.), only some kinds of content are permissible. The rule is:
If the content enhances the user experience, then the content is OK to be packaged in openSUSE. This means, for example, that things like: fonts, themes, clipart, and wallpaper are OK.
Content still has to be reviewed for inclusion. It must have an open source compatible license and must not be legally questionable. In addition, there are several additional restrictions for content:
- Content must not be pornographic, or contain nudity, whether animated, simulated, or photographed. There are better places on the Internet to get porn.
- Content should not be offensive, discriminatory, or derogatory. If you are not sure if a piece of content is one of these things, it probably is.
Some examples of content which is permissable:
- Clipart for use in office suites
- Wallpaper (non-offensive, non-discriminatory, with permission to freely redistribute)
- Fonts (under an open source license, with no ownership/legal concerns)
- Game levels are not considered content, since games without levels would be non-functional.
- Sound or graphics included with the source tarball that the program or theme uses (or the documentation uses) are acceptable.
- Game music or audio content is permissible, as long as the content is freely distributable without restriction.
- Example files included with the source tarball are not considered content.
Some examples of content which are not permissible:
- Comic book art files
- Religious texts
If you are unsure if something is considered approved content, ask on the openSUSE-packaging mailing list.
Package features
Systemd Services
openSUSE uses systemd to start services. Detailed guidelines how to package service files are at openSUSE:Systemd_packaging_guidelines.
Desktop files
If a package contains a GUI application, then it needs to also include a properly installed .desktop file. For the purposes of these guidelines, a GUI application is defined as any application which draws an X window and runs from within that window. Installed .desktop files MUST follow the desktop-entry-spec, paying particular attention to validating correct usage of the Name, GenericName, Categories and StartupNotify entries.
Icon tag in Desktop Files
The icon tag has to be the basename of the icon file(s), because it allows for icon theming:
Icon=comical
It assumes .png by default, then tries .svg and finally .xpm.
.desktop file creation
If the package does not already include and install its own .desktop file, you need to make your own, and include it as a source (e.g. Source3: %name.desktop
). The contents of a sample .desktop file (comical.desktop) are:
[Desktop Entry] Name=Comical GenericName=Comic Archive Reader Comment=Open .cbr & .cbz files Exec=comical Icon=comical Terminal=false Type=Application Categories=Graphics;
%suse_update_desktop_file usage
It is not simply enough to just include the .desktop file in the package. One MUST run %suse_update_desktop_file
in the %install
section, and have BuildRequires: update-desktop-files
enlisted, to help ensure .desktop file safety and spec-compliance. %suse_update_desktop_file
MUST be used if the package does not install the file or there are changes desired to the .desktop file (such as adding/removing categories, etc). Some examples of usage:
- check desktop file
%suse_update_desktop_file %{name}
- install desktop file and change categories
%suse_update_desktop_file -r %{name} System Utility Core GTK FileManager
More information about the macro can be found on the macro page.
Users and Groups
Note that for the moment, we primarily address the case where the mapping from user/group names to uids/gids is decided dynamically by target systems at package install time. Some options for system administrators for making this mapping static even though the package scriptlets use a dynamic scheme are also discussed below, and more are being investigated, including possibilities to make the mapping static at package build time.
System users, which are used by a variety of applications, by standard
filesystem directories or are standard users which should exist on
every Unix compatible system, should be provided by special RPMs.
This RPMs provides and the user and groups:
Provides: user(<name>) Provides: group(<name>)
This RPMs are also responsible to create and provide the home directory. Applications needing a special system user should require them:
Requires(pre): user(<name>) Requires(pre): group(<name>)
With this, the system users will only be created if they are needed. And an admin can easy find out, if a system user is still required or can be deleted.
systemd-sysusers (sysusers.d(5)) is used to create this accounts. This allows to verify how the system account should look like.
An example spec file for the uucp system user should contain the following lines:
Source1: system-user-uucp.conf BuildRequires: sysuser-tools %package -n system-user-uucp Summary: System user and group uucp %sysusers_requires %build %sysusers_generate_pre %{SOURCE1} uucp system-user-uucp.conf %install install -D -m 0644 %{SOURCE1} %{buildroot}%{_sysusersdir}/system-user-uucp.conf %pre -n system-user-uucp -f uucp.pre %files -n system-user-uucp %{_sysusersdir}/system-user-uucp.conf
%pre ... -f uucp.pre causes the content of uucp.pre to be taken as scriptlet. This file is generated at build time by the %sysusers_generate_pre-macro.
It is only necessary to add the sysusers config file into a subpackage, if the user should be available to other packages as well. If only the package itself requires the new user, then no subpackage is needed and the spec can be simplified as follows:
Source1: %{name}-user.conf BuildRequires: sysuser-tools %sysusers_requires %build %sysusers_generate_pre %{SOURCE1} %{name} %{name}-user.conf %install install -D -m 0644 %{SOURCE1} %{buildroot}%{_sysusersdir}/%{name}-user.conf %pre -f %{name}.pre %files %{_sysusersdir}/%{name}-user.conf
An example for a %{name}-user.conf file:
#Type Name ID GECOS Home directory Shell u uucp - "Unix-to-Unix CoPy system" /etc/uucp - m uucp lock - - -
More information can be found at the freedesktop.org page. On an installed system, you should also find some of these files below /usr/lib/sysusers.d.
Home directory
should usually be a directory created and owned by the package, with appropriate restrictive permissions. A good choice for the location of the directory is the package's data directory or directory under /var
like /var/lib/NAME
, in case it has one.
User accounts created by packages are rarely used for interactive logons, and should thus generally use -
for Shell
which sets /usr/sbin/nologin as the user's shell. Don't use /bin/false or /bin/true as a shell, change it to /usr/sbin/nologin as described above.
Use the %sysusers_requires macro, do not manually require sysuser-shadow due to changes in the specification from older versions of systemd.
Users or groups created by packages are never removed. There is no sane way to check if files owned by those users/groups are left behind — and even if there were, what would we do to them? Leaving those behind with ownerships pointing to then-nonexistent users/groups may result in security issues when a semantically unrelated user/group is created later and reuses the UID/GID. Also, in some setups, deleting the user/group might not be possible or/nor desirable, for example, when using a shared remote user/group database. Cleanup of unused users/groups is left to the system administrators to take care of, if they so desire.
In some cases it is desirable to create only a group without a user account. Usually, this is because there are some system resources to which we want to control access by using that group, and a separate user account would add no value. Examples of common such cases include (but are not limited to) games whose executables are setgid for the purpose of sharing high score files or the like, and/or software that needs exceptional permissions to some hardware devices and it would not be appropriate to grant those to all system users nor even only those logged in on the console. In these cases, apply only the g
line part of the sysusers.d specification.
Note that the practice of not creating users/groups if they already exist has a drawback of possibly unrelated but coincidentally same named existing system users and/or groups unnecessarily and undesirably getting access to things in a package that uses the same user/group names. This version of the users/groups guideline does not address that issue in any way, but it is possible that future revisions will if a good enough way to do that is found.
Migration / Upgrades
With the introduction of containerized deployments it is not a good idea to do all the configuration and generating of random information during the package installation but rather it should be done on the selected spawned container to ensure data consistency and stability.
Basically, it means to move all configuration tasks that are using the binaries and affecting resulting data (e.g. keychain generation, database migration) from scriptlet phases to be executed later on a live system. As our requirement is to be able to install packages using rpm directly we have several options how to achieve this:
systemd services (as a sample see mariadb
package)
Use the systemd services to actually do the initial configuration and deployment, alternatively even the migration of the already existing content in place prior start.
General systemd file is quite obvious:
[Unit] Description=MySQL server Wants=basic.target Conflicts=mysql.target After=basic.target network.target [Service] Restart=on-abort Type=simple ExecStartPre=/usr/lib/mysql/mysql-systemd-helper install ExecStartPre=/usr/lib/mysql/mysql-systemd-helper upgrade ExecStart=/usr/lib/mysql/mysql-systemd-helper start ExecStartPost=/usr/lib/mysql/mysql-systemd-helper wait [Install] WantedBy=multi-user.target
All the operations are done by the helper shell script that as it is described prior each start does installation and upgrade steps. To ilustrate these points will be different for each application but as a concept this is done in the mariadb:
# Create new empty database if needed mysql_install() { if [ ! -d "$datadir/mysql" ]; then echo "Creating MySQL privilege database... " mysql_install_db --user="$mysql_daemon_user" --datadir="$datadir" || \ die "Creation of MySQL databse in $datadir failed" echo -n "$MYSQLVER" > "$datadir"/mysql_upgrade_info fi }
# Upgrade database if needed mysql_upgrade() { # Run mysql_upgrade on every package install/upgrade. Not always # necessary, but doesn't do any harm. if [ -f "$datadir/.run-mysql_upgrade" ]; then echo "Checking MySQL configuration for obsolete options..." sed -i -e 's|^\([[:blank:]]*\)skip-locking|\1skip-external-locking|' \ -e 's|^\([[:blank:]]*skip-federated\)|#\1|' /etc/my.cnf # instead of running mysqld --bootstrap, which wouldn't allow # us to run mysql_upgrade, we start a full-featured server with # --skip-grant-tables and restict access to it by unix # permissions of the named socket echo "Trying to run upgrade of MySQL databases..." # Check whether upgrade process is not already running protected="$(cat "/run/mysql/protecteddir.$INSTANCE" 2> /dev/null)" if [ -n "$protected" && -d "$protected" ]]; then pid="$(cat "$protected/mysqld.pid" 2> /dev/null)" if [ "$pid" && -d "/proc/$pid" ] && [ -z "$(readlink "/proc/$pid/exe" | grep -q "mysql")" ]; then die "Another upgrade in already in progress!" else echo "Stale files from previous upgrade detected, cleaned them up" rm -rf "$protected" rm -f "/run/mysql/protecteddir.$INSTANCE" fi fi protected="$(mktemp -d -p /var/tmp mysql-protected.XXXXXX | tee "/run/mysql/protecteddir.$INSTANCE")" [ -n "$protected" ] || die "Can't create a tmp dir '$protected'" # Create a secure tmp dir chown --no-dereference "$mysql_daemon_user:$mysql_daemon_group" "$protected" || die "Failed to set group/user to '$protected'" chmod 0700 "$protected" || die "Failed to set permissions to '$protected'" # Run protected MySQL accessible only though socket in our directory echo "Running protected MySQL... " /usr/sbin/mysqld \ --defaults-file="$config" \ --user="$mysql_daemon_user" \ --skip-networking \ --skip-grant-tables \ $ignore_db_dir \ --log-error="$protected/log_upgrade_run" \ --socket="$protected/mysql.sock" \ --pid-file="$protected/mysqld.pid" & mysql_wait "$protected/mysql.sock" || die "MySQL didn't start, can't continue" # Run upgrade itself echo "Running upgrade itself..." echo "It will do some chek first and report all errors and tries to correct them" echo if /usr/bin/mysql_upgrade --no-defaults --force --socket="$protected/mysql.sock"; then echo "Everything upgraded successfully" up_ok="" rm -f "$datadir/.run-mysql_upgrade" [ -n "$(grep -q "^$MYSQLVER" "$datadir/mysql_upgrade_info" 2>/dev/null)" ] || \ echo -n "$MYSQLVER" > "$datadir/mysql_upgrade_info" else echo "Upgrade failed" up_ok="false" fi # Shut down MySQL echo "Shuting down protected MySQL" kill "$(cat "$protected/mysqld.pid")" for i in {1..30}; do /usr/bin/mysqladmin --socket="$protected/mysql.sock" ping > /dev/null 2>&1 || break done /usr/bin/mysqladmin --socket="$protected/mysql.sock" ping > /dev/null 2>&1 && kill -9 "$(cat "$protected/mysqld.pid")" # Cleanup echo "Final cleanup" if [ -z "$up_ok" ]; then rm -rf "$protected" "/run/mysql/protecteddir.$INSTANCE" else die "Something failed during upgrade, please check logs" fi fi }
crontab
Similar to the previous option but using crontab instead of systemd.
binary tweaks
Adjust binaries to perform all these configuration tasks by themselves on the first run/migration. But it could get complicated in order to persuade upstream projects to accept these changes.
Logrotate scripts
Logrotate requires the log files (or the directory they are in) to be owned as root:root, so it won't be prone to symlink tricks and other attacks.
If the log files have to be owned by a non-root user, the recently added "su" directive should be used. The logrotate process will switch user:group before rotating to match the permissions of the rotated log file.
Example:
/var/log/radius/radius.log { su radiusd radiusd [...] }
Patches
See Packaging Patches guidelines.
Supporting multiple versions of a package
See Supporting the installation of multiple package versions
Changelog
See Creating a (RPM) changes file. Please note that openSUSE uses a separate file for the RPM changelog.
Udev rules
Udev rules files must be placed into %{_udevrulesdir}.
Reloading udev in %post and %postun is not needed, because udev automatically detects changes in rules files (opensuse-packaging discussion).
GConf scriptlets
SuSEfirewall2 Service Definitions
Package types
Some applications have specific guidelines written for them, located on their own pages in the Packaging/ hierarchy.
Architecture crossover (baselib) packages
A.k.a. -32bit, -64bit, -x86, and so on.
→ openSUSE:Build_Service_baselibs.conf
Branding
How to create and package branding is explained in openSUSE:Packaging_Branding.
Debuginfo
Packages should produce useful -debuginfo packages, or explicitly disable them when it is not possible to generate a useful one, but where rpmbuild would do it anyway. Generation of -debuginfo packages happens automatically where possible, and can be explicitly disabled in the Build Service on a per-(project, repository) or per-(package, repository) basis. For example, look at the Debuginfo flags within the OBS web UI's Repository tab for the openSUSE:12.3:Update project or a package within that project. Of course you can also change these flags by using osc to edit the metadata belonging to the project or package. Whenever a -debuginfo package is explicitly disabled, an explanation why this was done so is required in the specfile.
If the debuginfo flag is enabled, bs-worker will trickle this down to build(1) (some env var?) and /usr/bin/build calls rpm with --define '_build_create_debug 1'.
/home/abuild/.rpmmacros is populated with the definition for _build_create_debug, and will cause an expansion of ungodly macro magic, including %debug_package. %debug_package is a standardized RPM component and marks the end of BS-specific logic.
NOTE: Older versions of rpm (still used for SLE-11 builds) don't support noarch sub-packages. This leads to debuginfo files being created but no -debuginfo being generated. When building for those versions, noarch must not be used for sub-packages.
Debuginfo packages are discussed in more detail in a separate document, [1].
Eclipse Plugins
Emacs
Add single elisp file (.el) in %{_datadir}/emacs/site-lisp
. If you add multiple file, make a subdir %{_datadir}/emacs/site-lisp/%{name}
and add a new initialization file to add the subdir to the load-path
list:
%define _sitedir %{_datadir}/emacs/site-lisp %define _startfile %{_sitedir}/suse-start-%{name}.el cat <<EOF > %{buildroot}%{_startfile} ;; %{_startfile} (add-to-list 'load-path "%{_sitedir}/%{name}") ;; %{_startfile} ends here EOF
TODO: byte-compilation & autoloads
Fonts
Fonts in general-purpose formats such as Type1, OpenType TT (TTF) or OpenType CFF (OTF) are subject to specific openSUSE:Packaging_Fonts, and should never be packaged in a private application directory instead of the system-wide font repositories.
Games
How to package games is explained in openSUSE:Packaging_Games.
GNOME
Go
How to package Go software is explained in openSUSE:Packaging_Go.
Haskell
How to package Haskell software is explained in openSUSE:Packaging_Haskell.
Java
How to package Java software is explained in openSUSE:Packaging_Java
Lisp
How to package Common Lisp software is explained in openSUSE:Packaging_Lisp
Lua
How to package Lua software is explained in openSUSE:Packaging_Lua
Mono
Mozilla
OCaml
OpenOffice.org extensions
PAM (Pluggable Authentication Modules)
Perl
How to package Perl software is explained in openSUSE:Packaging_Perl
PHP
How to package PHP software is explained in openSUSE:Packaging_PHP
Python
How to package Python software is explained in openSUSE:Packaging_Python
PyQt5 and SIP
How to package PyQt5 and software using SIP is explained in openSUSE:Packaging_PyQt5_and_SIP
R
How to package R software is explained in openSUSE:Packaging_R
Rust
How to package Rust software is explained in Packaging Rust Software
Ruby
How to package Ruby software is explained in openSUSE:Packaging_Ruby
wxWidgets
How to package wxWidgets software is explained in openSUSE:Packaging_wxWidgets
Libraries
openSUSE:Shared library packaging policy
GObject Introspection Bindings (.typelibs)
A good amount of libraries, mostly from the GNOME Stack, ship a .typelib file. This .typelib interfaces between various programming languages (seed, python, vala) and the library. Just like shared libraries, they are versioned and can potentially require to be installed in multiple versions.
The package name follows the convention typelib-<GIVersion>-<TypeLibName>-<TypeLibVersion>, where dots (.) are replaced with underscores (_)
Explanation of the <FIELDS>: typelib: Literal, as an identifier of the package type. <GIVersion>: Current gobject introspection version is 1.0, so 1_0 <TypeLibName>: The name of the actual binding, Case sensitive <TypeLibVersion>: The version of the binding
All these elements can be taken out of the filename. For example /usr/lib/girepository-1.0/Memphis-0.2.typelib results in the package name typelib-1_0-Memphis-0_2 When building such packages, make sure that the spec file contains BuildRequires: gobject-introspection, as this triggers the installation of an automatic dependency scanner in plus. As a result, the typelib-* package automatically requires its libraries and possibly other typelib-packages.
Static Libraries
Packages including libraries should exclude static libraries as far as possible (e.g. by configuring with the --disable-static switch). Static libraries should only be included in exceptional circumstances. Applications linking against libraries should, as far as possible, link against shared libraries, not static versions.
Libtool archives, foo.la files, should not be included. Packages using libtool will install these by default even if you configure with --disable-static, so they may need to be removed before packaging. Due to bugs in older versions of libtool or bugs in programs that use it, there are times when it is not always possible to remove *.la files without modifying the program. In most cases, it is fairly easy to work with upstream to fix these issues. Note that if you are updating a library in a stable openSUSE release and the package already contains *.la files, removing the *.la files should be treated as an API/ABI change, in other words, removing them changes the interface that the library gives to the rest of the world and should not be undertaken lightly.
Exception
We want to be able to track which packages are using static libraries so we can find which packages need to be rebuilt if a security flaw in a static library is fixed, for instance. If you really need to package static libraries, you have to follow this guideline.
Static libraries must be placed in a *-devel-static subpackage, which Requires
the *-devel subpackage. Separating the static libraries from the other development files in *-devel allow us to track this usage by checking which packages BuildRequire
the *-devel-static package. The intent is that, whenever possible, packages will move away from using these static libraries, to the shared libraries. When a package only provides static libraries, skip the Requires
on the *-devel subpackage (this assumes that the *-devel-static subpackage also ships the header files). Packages which explicitly need to link against the static version must then BuildRequire: foo-devel-static
, so that the usage can be tracked.
If (and only if) a package has shared libraries which require static libraries to be functional, the static libraries can be included in the *-devel subpackage. The devel subpackage must have a virtual Provide for the *-devel-static package, and packages dependent on it must BuildRequire
the *-devel-static package.
Duplication of system libraries
For several reasons, a package should not include or build against a local copy of a library that exists on a system. The package should be patched to use the system libraries. This prevents old bugs and security holes from living on after the core system libraries have been fixed.
Tcl
Web Applications
Web applications packaged in openSUSE should place their files in /etc, /usr, /var, etc., depending on type, just like any other application would do.
In general, a package must not install, remove or otherwise modify /srv content as its use is reserved to the admin according to FHS[2]. In openSUSE, it's OK to have a package create an empty directory in /srv if the default configuration of the software points there.
Vala
How to detect Vala version in specfile
We assume you're not building Vala itself. If so, the version of Vala is defined by youself and can be called simply using %{version} tag, the problem does not even exist. Here we're facing such an embarrassing situation:
The package is built with Vala, and is built well under all versions of Vala(So you don't have to gild the lily by BuildRequires: vala >= 0.14 tag). But you still have to detect and use the version number of Vala.
Here Build Service got a bottleneck. For now, Vala can not simply be included with BuildRequires: vala and exports its version simple and naive with automatic tag like %{py_ver} or %{perl_version} as Perl or Python does. Some package do need to know Vala's number to get its work done(eg.., Babl or Gegl will install .vapi file to /usr/share/vala by default, but that directory is meaningless in a standard openSUSE system. However, openSUSE use directory suffixed with a Vala version number like /usr/share/vala-0.14. Now it is you packager's responsibility to move them from package default directory to openSUSE's callable).
Of course you could, by using %if 0%{?suse_version} >= 1140 tag, move them to the directory suffixed with standard Vala version number, which is installed by default on that 1140 version of openSUSE. But it will make your specfile huge and unmaintainable(you have to add such section for almost every version of openSUSE).
Now we use the self defined function %define in specfile,as follows:
#import Vala dependency BuildRequires: vala #define a function to export Vala's version number %define vala_version %(rpm -q --queryformat='%%{VERSION}' vala | sed 's/\.[0-9]*$//g')
We use rpm -q --queryformat='%%{VERSION}' vala
to fetch Vala's full version number 0.14.0, which is installed in a standard RPM way before the build process actually starts, that's why such code works. And then use sed shell command with the power of Regex to cut the suffix .0 and export the result to a variable vala_version, which can be called easily by %{vala_version}. eg:
#create a new directory. -pv means export directories it made into logfile for futher debug #and create it automatically if the parent directory does not exist. mkdir -pv %{buildroot}%{_datadir}/vala-%{vala_version}/ #move the files mv %{buildroot}%{_datadir}/vala/* %{buildroot}%{_datadir}/vala-%{vala_version}/ #delete the original folder rm -Rf %{buildroot}%{_datadir}/vala/
Even in %files section, you could use:
%dir %{_datadir}/vala-%{vala_version}/ %{_datadir}/vala-%{vala_version}/*
to simplify your specfile.
Because every file under %{buildroot} will be packaged by hierarchy into the final output RPM, now your package's user friendliness pops up +1.