FFI and Requires

Posted on Nov 19, 2015

FFI is a method to interface with native libraries, which is becoming more and more popular in many scripting languages. Unlike with native extensions though, we have nothing that links the shared library. As such our requires generator for shared libraries doesn’t kick in and we get no requires on the shared library package.

To make matters worse with the shared library policy the soversion is dependent on against which distro you build against.

Solving the issue

rpm -qa somename*

This will list all packages matching this pattern for us. There is already a set of macros that allow us to say “require exactly the package i was built with”, but those are not passing “-a” to rpm. But of course we can borrow the logic and adapt it for our needs.

As we will use the base library package name a few times in the code, we should put it into a variable:

%define ffi_lib libsodium
BuildRequires:    %{ffi_lib}-devel

The first usage is right there, requiring the devel package. While we are not actually linking the library, it is a consistent way to get the shared library package pulled in. The next step is the macro based on %requires_eq

%(LC_ALL=C rpm -q -a --qf "Requires: %%{name}(%{__isa}) = %%{epoch}:%%{version}\n" '%{ffi_lib}*' | grep -v %{ffi_lib}-devel | sed -e 's/ (none):/ /' -e 's/ 0:/ /' | grep -v "is not")

It has a few small modifications as we could remove some quoting which is needed for the macros file. A careful reader will also notice that not all variables are using the %% quoting. The %{__isa} is not a valid tag for “rpm -q”. As such we pass it in directly from the spec file. Why do we need it at all?

Without it we would get something like

Requires:  libsodium17 = 1.0.6

This would mean the package manager could in theory pull in the i586 package on a x86_64 machine. But our FFI library wouldn’t be able to work with it. The %{__isa} makes sure we pull the native version of the library. Though this makes our normally arch independent package, into something arch dependent. You need to remove and “BuildArch: noarch” statements from your spec file.

The complete snippet

%define ffi_lib libsodium
BuildRequires:    %{ffi_lib}-devel

# make sure we require the libsodium version we were built with
# this is extracted from the requires_eq macro because we need the -a parameter to expand the wildcard in the package name.
%(LC_ALL=C rpm -q -a --qf "Requires: %%{name}(%{__isa}) = %%{epoch}:%%{version}\n" '%{ffi_lib}*' | grep -v %{ffi_lib}-devel | sed -e 's/ (none):/ /' -e 's/ 0:/ /' | grep -v "is not")