2

What is best practice for RPM packaging something that provides a library which in name shadows a system library?

Background: For an RPM-backed Linux system, I am packaging a "self-contained" app foo that wants to live under /opt and supply as many of its own dependencies as it can, so as not to rely on the versions installed on the main system. (foo uses LD_LIBRARY_PATH to avoid system libraries.)

Unfortunately, one of the foo dependencies is a library grunk, whose version is exactly that of the libgrunk.so required for the main system. RPM's automatic Provides: generation then identifies two RPMs as providing this library. (That's not really true — only the system package makes the library available to any program whatsoever, whereas the foo package makes it available only to foo.)

This confounds automatic dependency resolution sometimes. For example, yum might install the wrong package or only one package when two are needed.

Example:

grunk-libs-1.2-3.$arch.rpm              # System Library
  Files:
    %{_libdir}/libgrunk.so.1
    %{_libdir}/libgrunk.so.1.2
  Provides:
    libgrunk.so.1()(%{_arch})

foo-x.y.z-r.$arch.rpm                   # App
   Files:
     /opt/foo
     /opt/foo/bin/...
   Requires:
     libgrunk.so.1()(%{_arch})          # foo needs grunk, but wants its own instance

foo-grunklibs-1.2-1.$arch.rpm           # App Dependency
  Files:
    /opt/foo/libs/libgrunk.so.1
    /opt/foo/libs/libgrunk.so.1.2
  Provides:
    libgrunk.so.1()(%{_arch})           # <-- same as from system "grunk-libs"

I've disabled AutoReqProv for now, but I want to be able to take advantage of RPM's automatic capabilities and dependency generation. I want RPM to know that foo.rpm does require foo-grunklibs.rpm by virtue of the latter's libgrunk.so.1, but also know that the same library from the main system rpm won't suffice.

I think I'm at the limits of conventional use. Is there a packaging technique I can use here to resolve this?

pilcrow
  • 449
  • 5
  • 19

1 Answers1

1

After considering this for a while, I think the more correct approach is to use RPM virtual resource "namespaces". E.g.:

Provides:  resource               # 'resource' is available to the whole system
Provides:  namespace(resource)    # 'resource' is specifically available only to "namespace"

The RPMs end up looking like this:

grunk-libs.rpm
  Provides:  libgrunk.so.1()(%{_arch})       # System library.  unchanged

foo.rpm
  Requires:  /bin/sh
  Requires:  libc.so.6()(%{_arch})
  Requires:  foo(libgrunk.so.1()(%{_arch}))  # Note special foo(...) namespace

foo-grunklibs.rpm
  Provides:  foo(libgrunk.so.1()(%{_arch}))  # Note special foo(...) namespace

In this way the foo dependency on the special build of libgrunk cannot be satisfied by the ordinary grunk-libs RPM, but must be satisfied by the foo-specific build of that library.

My proof of concept looks like this:

Name:     foo
...
Source99: find-namespaced-requires     # Here's the magic

%define    _use_internal_dependency_generator 0
%define    __find_requires %{SOURCE99} foo /bin/sh glibc

Where the script find-namespaced-requires wraps the standard RPM %{_rpmconfigdir}/find-requires and filters its output. It's first argument is the namespace ("foo(...)") to use, and subsequent arguments are dependencies or RPMs providing dependencies which should not be namespaced. In this example, I want foo to require the main system's /bin/sh and also the ordinary libc, libdl, libm, etc. from glibc, but every other dependency should be assumed to be specific to foo.

That could be made a little cleaner and more generic by wrapping up most of the above in a build-time RPM and saying something like:

Name:          foo
BuildRequires: special-deps-macros

%define _namespace            foo      # defaults to %{name} ?
%define _generic_dependencies ...      # defaults to /bin/sh glibc libselinux ?
pilcrow
  • 449
  • 5
  • 19