Multi lib tricks
Architecture independent files
File conflicts
For architecture independent files conflicts should be avoided, so the files should be identical among arches. It may involve some work with upstream when header files include architecture specific files, for example header files which contains autoconf conditionals like HAVE_INT32.
Another problem that sometimes appears is with gzipped files. Normally gzip will embed the timestamp of the file in its output, and this can cause a multilib conflict. The way to avoid this is to use gzip's '-n' option whenever files are gzipped at build time.
If you are not sure what the reason for a particular conflict is, fetch the packages from two multilibbed architectures (e.g. i386 and x86_64) and unpack them like this:
mkdir i386 x86_64 rpm2cpio blah-1.2-3.i386.rpm | (cd i386; cpio -idv) rpm2cpio blah-1.2-3.x86_64.rpm | (cd x86_64; cpio -idv)
Then you can use the diff or cmp utilities to discover exactly what is different between the conflicting files.
Architecture dependent header files
Some packages install header files (typically named *config.h
, usually generated by build system) which will have something in them relating to the byte size of some types at compile time.
Such files then have different contents on multilib-counterpart architectures (e.g. x86_64 vs. i386) while those still share the same installation path. This kind of API specification is bad in most cases, however, since an API should be completely platform independent.
The best believed solution for this issue is to have it fixed once and forever upstream – for example use integer types of a length independent from the platform (integers like int32_t from inttypes.h can be used). For many projects this is infeasible.
One possible way to get around this problem downstream is (considering that the architecture-dependent file is named config.h
and we build on x86_64 platform) to move the config.h
file into config-64.h
and install architecture-independent "stub" header file config.h
:
#include <bits/wordsize.h> #if __WORDSIZE == 32 #include "config-32.h" #elif __WORDSIZE == 64 #include "config-64.h" #else #error "Unknown word size" #endif
There is convenient RPM macro '%multilib_fix_c_header' in multilib-rpm-config package that allows you to automatize this, typically you can use this pattern:
BuildRequires: multilib-rpm-config ... %install ... make install DESTDIR=%{buildroot} ... # Replace arch-dependent header file with arch-independent stub (when needed). %multilib_fix_c_header --file %{_includedir}/package/config.h %files %{_includedir}/package
Doxygen footers can include the date. This leads to conflict since
doc files are not generated at the exact same time. To use a footer without the date, it is possible to modify the dox file used to
drive the documentation generation, and set HTML_TIMESTAMP
to NO
. For example:
HTML_TIMESTAMP = NO
This is the default setting in current Doxygen, but some projects may supply their own config file with this set to YES.
PNG images
PNG images generated for example by doxygen can include the date as part of the PNG format. This leads to conflicts since the *.png files are not generated at the exact same time. To solve this, you can set the internal timestamps of PNG images to a reference timestamp which can be achieved by using the python script png-mtime .
Put this command into your spec file so that it applies to the PNG files where the reference date/file shall be used.
Timestamps
Files should also have the same timestamps. For most of the files this means taking care to keep the timestamps (which should be done in every package). For autoconf based packages this is in general achieved by doing something along:
make install DESTDIR=$RPM_BUILD_ROOT INSTALL="%{__install} -p"
For the architecture independent files generated at build time it is possible to use the timestamp of a reference file. For example:
touch -r ../cernlib.in src/scripts/cernlib
or
touch -r NEWS $RPM_BUILD_ROOT%{_includedir}/Xbae/patchlevel.h
Multiarch, binaries and compilation scripts
In multilib environments there is a preferred architecture, 64 bit over 32 bit in x86_64, 32 bit over 64 bit in ppc64 and sparc64. When a conflict is found between two packages corresponding with different arches, the installed file is the one from the preferred arch. This is very common for executables in /usr/bin, for example. If the file /usr/bin/foo is found in an x86_64 package and in an i386 package, the executable from x86_64 will be installed.
These implicit conflicts are accepted in Fedora, though they are considered bad by some contributors. There may be some long-term solution for these issues, but before that there are some tricks that may allow to avoid those conflicts that are presented below. Once again they are optional.
- In compilation scripts (in general named along mylib-config) it should be advisable to remove
-L$libdir
when $libdir is the default library directory on this platform. Indeed this is automatically added when linking with the gcc compiler (it may be needed when linking with ld, but using ld is wrong in general, so a user linking with ld should add the flag by himself).
- binaries may be put outside of the packages selected to be included in multilib repositories. In general the devel subpackages and their dependencies are included in multilib repositories. A typical split of a package is:
- foo for the binaries
- foo-libs for the libraries
- foo-devel for the development headers, and development symlinks
foo-devel and foo both requires foo-libs, and foo isn't
present in multilib repository.
- wrapper scripts may be used to run a binaries based on which one is present. Here is a script example (adapted from firefox)
ARCH=$(uname -m) case $ARCH in x86_64 | sparc64 | s390x | ppc64) LIB_DIR=/usr/lib64 SECONDARY_LIB_DIR=/usr/lib ;; * ) LIB_DIR=/usr/lib SECONDARY_LIB_DIR=/usr/lib64 ;; esac if [ ! -x $LIB_DIR/package-0.0.1/foo ] ; then if [ ! -x $SECONDARY_LIB_DIR/package-0.0.1/foo ] ; then echo "Error: $LIB_DIR/package-0.0.1/foo not found" if [ -d $SECONDARY_LIB_DIR ] ; then echo " $SECONDARY_LIB_DIR/package-0.0.1/foo not found" fi exit 1 fi LIB_DIR=$SECONDARY_LIB_DIR fi
Another way to handle those conflicts could be to have a different directory for each architecture, even for executables, enabling Fedora to be multiarch and not only multilib.
Splitting libraries into separate packages
If you have binaries, libraries, configuration, and data files all in the same package, conflicts can also arise. One way to fix this is to split libraries off into a separate lib<foo> or <foo>-libs package. However, some care must be done when doing this in order to ensure proper upgrades.
For example, if you previously had:
foo-1.1-1
that had binaries, libraries, etc. and had file conflcits, and you split the package into: into:
foo-1.1-2 foo-libs-1.1-2
you should add:
Obsoletes: foo < 1.1-2
in the %package section for foo-libs. This ensures that an upgrade with yum will remove the older foo for any alternative architecture.