Topic: ABI and packaging

Hi,

By ABI I mean Application Binary Interface, that is a consistent binary binding to a shared library that will not break across releases.

I have been having a look at the building of the shared library lbnfc.so* and how to deliver a consistent binary package so that end users can get the latest build but do not have to compile the binaries.

In order to meet these goals we need to avoid breaking existing binaries.

The API needs to freeze, or at least existing APIs need to continue to work but new APIs can be added for later revisions. For example, I have noticed that for each major release the nfc_initiator_select seems to change. Similarly structs must not change.

Exported entrypoints from shared libraries should be limited to only those explicitly wanted to be exposed and supported, so internal non-static functions should be hidden. With the GNU toolchain this can be done with -Wl,-retain-symbols-file,nfc.exp where nfc.exp is a file containing a simple list of the exported entry points. You can check the actual exports with

nm libnfc.so | grep " T "

This currently shows many acr122_*, arygon_*,pn53x_* and mirror* functions that should be private.

I have put together a couple of scripts to build an RPM and a Debian style package.

Regards

snapdev


mkfs.sh - make an installed directory layout

#!/bin/sh -ex

if test -d root
then
    chmod -R +w root
    rm -rf root
fi

mkdir -p root/usr/local/lib root/usr/local/include/nfc

find .. -name libnfc.so | while read N
do
    (
        cd `dirname $N`
        tar cf - libnfc.so*    
    ) | (
        cd root/usr/local/lib
        tar xvf -
    )
done

find .. -name nfc.h | while read N
do
    (
        cd `dirname $N`
        tar cf - *.h    
    ) | (
        cd root/usr/local/include/nfc
        tar xvf -
    )
done

find root -type f | xargs chmod -w

find root | xargs ls -ld

deb.sh - create a debian package

#!/bin/sh -ex
#
# dpkg -i libnfc*.deb
#
# dpkg -r libnfc*.deb

./mkfs.sh

GTAR=tar
VERSION=1.0
APPNAME=libnfc

rm -rf control
mkdir -p control

(
    cd root

    if test "$?" != 0
    then
        exit 1
    fi
    
    $GTAR --owner=0 --group=0 --create --file ../data.tar  ./*
)

ARCH=`dpkg --print-architecture`

PACKAGE_NAME="$APPNAME"_"$VERSION"_"$ARCH".deb

(
    cd control

    if test "$?" != 0
    then
        exit 1
    fi

    cat >control <<EOF
Package: $APPNAME
Version: $VERSION
Architecture: $ARCH
Maintainer: romuald@libnfc.org
Recommends:
Conflicts: $APPNAME
Provides: $APPNAME
Section: misc
Priority: extra
Description: libnfc
 Near Field Communication Library
 .
EOF

    cat >preinst <<EOF
#!/bin/sh -e
EOF

    cat >prerm <<EOF
#!/bin/sh -e
EOF

    cat >postrm <<EOF
#!/bin/sh
EOF

    cat >postinst <<EOF
#!/bin/sh
EOF

    chmod +x preinst prerm postinst postrm

    if test "$?" != 0
    then
        exit 1
    fi

    $GTAR --owner=0 --group=0 --create --file ../control.tar  ./*
)


for d in control data
do
    gzip $d.tar
done

if test "$?" != 0
then
    exit 1
fi

find root -type f | xargs chmod +w

rm -rf "$PACKAGE_NAME"

echo "2.0" >debian-binary

ar r "$PACKAGE_NAME" debian-binary data.tar.gz control.tar.gz

rm -rf data data.tar.gz control control.tar.gz debian-binary root

if test "$?" != 0
then
    exit 1
fi

rpm.sh -  build an RPM

#!/bin/sh -ex

# to install
# rpm -i libnfc.rpm

# to remove
# rpm -e libnfc

RPMBUILD=rpm
VERSION=1.0

if rpmbuild --help >/dev/null
then
    RPMBUILD=rpmbuild
fi

if $RPMBUILD --help >/dev/null
then
    echo RPMBUILD=$RPMBUILD
else
    exit 0
fi

./mkfs.sh

BUILDROOT=`pwd`/root
TGTPATH=rpms
SPECFILE=rpm.spec

cat >"$SPECFILE" <<EOF 
Summary: libnfc
Name: libnfc
Version: $VERSION
Release: 1
Group: Applications/System
License: LGPL
Prefix: /usr/local

%description
Near Field Communications Library

%files
%defattr(-,root,root)
/usr/local/lib/libnfc.so
/usr/local/lib/libnfc.so.1
%attr(555,root,root) /usr/local/lib/libnfc.so.1.0.0
%attr(555,root,root) /usr/local/include/nfc/nfc.h
%attr(555,root,root) /usr/local/include/nfc/nfc-types.h
%attr(555,root,root) /usr/local/include/nfc/nfc-messages.h
EOF

rm -rf $TGTPATH
mkdir -p $TGTPATH

$RPMBUILD --buildroot "$BUILDROOT" --define "_rpmdir $TGTPATH" -bb "$SPECFILE"

find "$TGTPATH" -name "*.rpm" | while read N
do
    mv "$N" .
done

find "$BUILDROOT" -type f | xargs chmod +w

rm -rf "$SPECFILE" "$TGTPATH" "$BUILDROOT"

Re: ABI and packaging

snapdev wrote:

Hi,

By ABI I mean Application Binary Interface, that is a consistent binary binding to a shared library that will not break across releases.
[...]
In order to meet these goals we need to avoid breaking existing binaries.

Totally agree.

snapdev wrote:

The API needs to freeze, or at least existing APIs need to continue to work but new APIs can be added for later revisions. For example, I have noticed that for each major release the nfc_initiator_select seems to change. Similarly structs must not change.

That's right, we have changed nfc_initiator_select and other functions to fit to the new usages we made with libnfc (ie. LLCP support).

snapdev wrote:

Exported entrypoints from shared libraries should be limited to only those explicitly wanted to be exposed and supported, so internal non-static functions should be hidden. With the GNU toolchain this can be done with -Wl,-retain-symbols-file,nfc.exp where nfc.exp is a file containing a simple list of the exported entry points.

That's a interesting feature to expose only supported and maintened functions.

I have opened a new issue to remember to do this on next package release.
http://code.google.com/p/libnfc/issues/detail?id=183

BTW, we are trying to stabilise the API, considering all usages of libnfc users want to, in the libnfc-1.5-new-api branch, did you check it ? We hope to have a stabilised API this way so feel free to discuss about it and give your point of view.

Romuald Conty