Building multiple packages with RPM
2018-01-14In my previous blog post, I described how to build a basic RPM package. This article picks up where we left off, by showing how to create multiple packages using one spec file – which allows us to split a program into multiple packages – and how to handle potential conflicts.
If you're wondering as to why one would want to split a program into different packages, there are a couple factors to consider:
-
Separating architecture-dependent and independent content: programs that come with a lot of non-executable data, like games, can be split into a package that contains the game's executable, and an achitecture-independent, universal package that contains all the game's assets. This way, if we need to support multiple architectures, we reduce the amount of disk space used for storing the packages.
-
Separating package core and optional content: a lot of programs we may want to package are modular to some extent; splitting the program into several packages allows the users to install only the parts they need, saving them disk space and bandwidth. (Also saving our bandwidth!) Even without modules, there's still the issue of library headers (
-dev
or-devel
packages) and documentation. -
Licensing issues: related to the two previous points, some programs may consist of several parts, each subject to a different licence. Some parts may be marked as "redistributable only, no modification permitted". If we're sensitive to licensing issues, we may want to split those parts into separate packages.
- Different widget kits: some programs may use a GUI abstraction library that allows them to be built with different widget kits, e.g. GTK, Qt, WxWidgets. Instead of keeping a separate spec file for each of these and having to sync changes, we can create several packages from one spec file.
Now that we've listed some possible reasons for splitting a package, let's get to work.
Defining subpackages
To define a subpackage, we need to use the %package
macro. It takes a single argument, which is the name of the subpackage we want to create.
This will get appended to the main package name, in a main-sub
format. Should we want to override this, we can use the %package -n fullname
form.
Probably the most common use case for this is Python packaging,
where it's usual to see %package -n python2-blah
, followed by %package -n python3-blah
.
%package data
Summary: Data files for %{name}
BuildArch: noarch
Traditionally, after defining a subpackage, we write any tags that we want to specify. The version and release number, together with the licence, are inherited from the main package, so we don't need to repeat them if they're the same. The only tag that is required to be present for each subpackage is the Summary: tag, but if we want, we can provide separate architecture settings, dependencies, provides, and others.
You may be wondering about the BuildArch: tag. The way it's used in the example above is probably the most common – being used to mark a package
as noarch
, which means no architecture, or putting it differently – architecture-independent. As stated before, this can be used to
package non-executable resources like game assets, but is also used for packaging programs written in interpreted languages – like shell, Perl, Python, or PHP.
Apart from the summary, we also need to provide the description and list of files to be used by our subpackage.
%desciption data
Data files (graphics, music, sounds, et cetera) required to play %{name}.
%build
make
%install
make install DESTDIR=%{buildroot}
%files
%{_bindir}/%{name}
%files data
%{_datadir}/%{name}
An important thing to note is that the only sections that should be repeated for each subpackage are the desciption and the list of files. The %prep, %build, %install and %check sections can appear only once, which means that we cannot have separate build / install scripts for each of the subpackages and must do it all in one go. Of course, if need be, we can just use comments to say "we finished installing main package files, now copying -data".
Handling conflicting files
There may be times where your packages will conflict with each other by providing a different version of the same file. An example might be an application compiled using different GUI widget kits, that's built in a way which makes allowing the two (or more) versions to co-exist difficult. To solve duplicate file conflicts, we can use the postfixes removal feature.
%package gtk3
RemovePathPostfixes: .gtk3
%package qt
RemovePathPostfixes: .qt
%files gtk3
%{_bindir}/%{name}.gtk3
%files qt5
%{_bindir}/%{name}.qt5
As demonstrated by the example above, we need to specify the RemovePathPostfixes: tag; its value should be a list of space-separated postfixes to remove from files during packaging. Though you can use any string as the postfix, it's common to begin these with a dot or a hyphen.
Omitting the main package
One issue that may come up in specific circumstances is that you may not want to build the main package – say, when you're building python2-mylibrary
and python3-mylibrary
,
it probably won't make sense to have a mylibrary
package. If you want to omit the main package, just leave its %files section empty – but don't remove it!
Otherwise, rpmbuild
will complain that the section is missing.
Comments
Do you have some interesting thoughts to share? You can comment by sending an e-mail to blog-comments@svgames.pl.