Tutorial de Empaquetamiento 2: GNU Hello

Este tutorial demuestra el empaquetamiento RPM empaquetando el programa GNU Hello. Si bien el programa es sencillo, viene con la mayoría de los componentes periféricos de un proyecto FOSS: entorno de configuración/compilación/instalación, documentación, internacionalización, etc.

Este tutorial es la segunda parte del tutorial de empaquetamiento de Fedora. Sigue la misma estructura básica que la parte 1. Si no la ha completado todavía, hágalo antes de continuar con este tutorial.

Los pasos en esta parte son similares a los de la parte 1. Sin embargo, GNU Hello tiene más peculiaridades que Banner, por lo que se necesitan algunos rodeos y pasos personalizados. Las peculiaridades de cada paquete son únicas, por lo que es mejor verlos como ejemplos del tipo de problemas que pueden surgir al empaquetar.

Instalar Herramientas de Empaquetador

Crear el directorio del paquete

Para GNU Hello, el paquete puede llamarse simplemente hello. Este es también el nombre oficial del paquete GNU Hello de Fedora.

$ mkdir hello && cd hello

Dentro de un Archivo de Especificaciones

Cree un archivo llamado hello.spec y pegue el siguiente archivo de especificaciones mínimo. Este ya tiene etiquetas BuildRequires para gcc y make, puesto que este tema ya fue tratado en la parte 1. Del mismo modo, la sección %files ya incluye el binario resultante, las páginas de manual, la documentación y el archivo de licencia. Para GNU Hello, todos estos archivos son muy parecidos a los archivos correspondiente en Banner por lo que ya están cubiertos en la parte 1.

También, por razones que son demasiado complejas para explicar en un tutorial, en algunas situaciones también es necesario el paquete texinfo y de este modo se añade una línea BuildRequires para él.

Name:           hello
Version:        2.10
Release:        %autorelease
Summary:        Produces a familiar, friendly greeting
License:        GPL-3.0-or-later
URL:            https://www.gnu.org/software/hello/
Source:         http://ftp.gnu.org/gnu/hello/hello-%{version}.tar.gz
BuildRequires:  gcc
BuildRequires:  make
BuildRequires:  texinfo

%description
The GNU Hello program produces a familiar, friendly greeting. Yes, this is
another implementation of the classic program that prints "Hello, world!" when
you run it.

%prep
%autosetup

%build
%configure
%make_build

%install
%make_install

%files
%{_bindir}/hello
%{_mandir}/man1/hello.1.*
%doc AUTHORS ChangeLog NEWS README THANKS TODO
%license COPYING

%changelog
%autochangelog

Descargar la fuente

Descargar las fuentes y comprobar que ahora las tiene:

$ spectool -g hello.spec
$ ls *.tar.gz
hello-2.10.tar.gz

Construir el Paquete

$ fedpkg --release f40 mockbuild

Este comando falla debido a archivos no empaquetados.

Instalar archivos

Como en la parte 1, iremos a través de la lista de archivos uno por uno. Ejecute`fedpkg --release f40 mockbuild` otra vez después de cada cambio para ver el progreso.

Páginas Texinfo

Installed (but unpackaged) file(s) found:
/usr/share/info/dir
/usr/share/info/hello.info.gz

Estas son las páginas Texinfo. Texinfo es un sistema de documentación parecido a las páginas de manual. El directorio está definido por la macro predeterminada {_infodir}, de modo que el manual Texinfo se puede añadir como sigue:

%files
⋮
%{_infodir}/hello.info.*

The dir file generated by GNU Hello build script indexes all texinfo pages in your system. Because the installed pages differ among systems, the file cannot be prebuilt and packaged. Instead it needs to be created and updated when the package is installed. The update is automatically performed by RPM triggers in info binary package of texinfo source package.

To prevent from installing the dir file, remove it from the buildroot at the end of the %install section with rm command.

However, GNU Hello build script only generates the dir file if info package is installed during the build. Blindly removing the file would raise an error if the hello package were built on a system without info package. To handle both cases, delete the file if it exists:

%install
⋮
test -f %{buildroot}/%{_infodir}/dir && rm %{buildroot}/%{_infodir}/dir

Translations

Installed (but unpackaged) file(s) found:
/usr/share/locale/bg/LC_MESSAGES/hello.mo
/usr/share/locale/ca/LC_MESSAGES/hello.mo
/usr/share/locale/da/LC_MESSAGES/hello.mo
⋮

Since our program uses translations and internationalization, we are seeing a lot of undeclared i18n files. The recommended method to declare them is:

  1. Add the required build dependency with BuildRequires: gettext.

  2. Find the filenames in the %install step with %find_lang %{name}.

  3. Install the files with %files -f %{name}.lang.

After these changes, build succeeds.

Running tests

GNU Hello, like many other projects, includes an automated test suite in the sources. If at all possible, the test suite should be run during the rpm build. This helps ensuring that a working build was produced. This is done by adding the test suite invocation to specfile %check% section, which comes after %install in order. In GNU Hello’s case:

%check
make check

Run a mockbuild again and check the output to ensure that the tests were actually run. Something like this should be somewhere in the output:

============================================================================
Testsuite summary for GNU Hello 2.10
============================================================================
# TOTAL: 5
# PASS:  4
# SKIP:  1
# XFAIL: 0
# FAIL:  0
# XPASS: 0
# ERROR: 0
============================================================================

Fixing automagic

Now the package successfully builds. But that does not mean that the .spec file is correct.

Listing all build-time dependencies

If you carefully read a build output, you can discover lines which mention sed command:

+ /usr/bin/make -O -j4 V=1 VERBOSE=1
rm -f lib/arg-nonnull.h-t lib/arg-nonnull.h && \
sed -n -e '/GL_ARG_NONNULL/,$p' \

Therefore you need to add this line close to other BuildRequires lines:

BuildRequires:  sed

Similarly, studying configure script in the unpackaged sources, which is executed by %configure macro, reveals it’s a /bin/sh script:

$ head configure
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for GNU Hello 2.10.

Thus you also should record this dependency on bash:

BuildRequires:  bash

Why on bash? Because /bin/sh program is provided by bash package:

$ rpm --queryformat '%{name}\n' --query --file /bin/sh
bash

Specifying all used dependencies helps to make the .spec file resilient against changes in the build environment. If e.g. sed package were removed from the environment, this GNU Hello package would fail to build.

Listing all build options

The GNU Hello build script, configure has many build options which enable or disable optional features. Their nondefault forms can be listed with --help option:

$ ./configure --help
`configure' configures GNU Hello 2.10 to adapt to many kinds of systems.

Usage: ./configure [OPTION]... [VAR=VALUE]...
⋮
Optional Features:
  --disable-option-checking  ignore unrecognized --enable/--with options
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-silent-rules   less verbose build output (undo: "make V=1")
  --disable-silent-rules  verbose build output (undo: "make V=0")
  --enable-dependency-tracking
                          do not reject slow dependency extractors
  --disable-dependency-tracking
                          speeds up one-time build
  --disable-nls           do not use Native Language Support
  --disable-rpath         do not hardcode runtime library paths

Some of them are already specified within %configure and %make_build macros. The rest of the options, if they are important for the built package, should be explicitly written in the .spec file to prevent from their sudden and unnoticed changes. Either because a new Hello version changes the default, or because a package which they depend on appears of disappears from the build environment.

Therefore modify %configure invocation in %build section like this:

%configure --enable-nls --disable-rpath

Checking the result with rpmlint

Check with fedpkg lint reveals a problem:

$ fedpkg --release f40 lint
hello.x86_64: W: file-not-utf8 /usr/share/doc/hello/THANKS

In order to ensure a pure utf-8 installation, the file needs to be converted in %prep. This can be done with the iconv utility which is provided by glibc-common package, and mv tool from coreutils:

BuildRequires:  coreutils
BuildRequires:  glibc-common
⋮
%prep
⋮
mv THANKS THANKS.old
iconv --from-code=ISO-8859-1 --to-code=UTF-8 --output=THANKS THANKS.old

Run fedpkg lint again and observe that the warning is fixed.

A Complete hello.spec File

Here is the final version of hello.spec:

Name:           hello
Version:        2.10
Release:        %autorelease
Summary:        Produces a familiar, friendly greeting

License:        GPL-3.0-or-later
URL:            http://ftp.gnu.org/gnu/%{name}
Source:         http://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.gz

BuildRequires:  bash
BuildRequires:  coreutils
BuildRequires:  gcc
BuildRequires:  gettext
BuildRequires:  glibc-common
BuildRequires:  make
BuildRequires:  sed
BuildRequires:  texinfo

%description
The GNU Hello program produces a familiar, friendly greeting. Yes, this is
another implementation of the classic program that prints "Hello, world!" when
you run it.

%prep
%autosetup
mv THANKS THANKS.old
iconv --from-code=ISO-8859-1 --to-code=UTF-8 --output=THANKS THANKS.old

%build
%configure --enable-nls --disable-rpath
%make_build

%install
%make_install
test -f %{buildroot}/%{_infodir}/dir && rm %{buildroot}/%{_infodir}/dir
%find_lang %{name}

%check
make check

%files -f %{name}.lang
%{_mandir}/man1/hello.1.*
%{_infodir}/hello.info.*
%{_bindir}/hello
%doc AUTHORS ChangeLog NEWS README THANKS TODO
%license COPYING

%changelog
%autochangelog

With this .spec file, you should be able to successfully complete the build process, and create the source and binary RPM packages.

Checking the result

You can now check the result with rpm, like was done in part 1.

Files

List the files contained in the package:

$ rpm --query --package --list results_hello/2.10/1.fc40/hello-2.10-1.fc40.x86_64.rpm
/usr/bin/hello
/usr/lib/.build-id
/usr/lib/.build-id/39
/usr/lib/.build-id/39/c97ecb15c6292ce23e8b00e15e6e72a61e5072
/usr/share/doc/hello
/usr/share/doc/hello/AUTHORS
⋮
/usr/share/doc/hello/TODO
/usr/share/info/hello.info.gz
/usr/share/licenses/hello
/usr/share/licenses/hello/COPYING
/usr/share/locale/bg/LC_MESSAGES/hello.mo
⋮
/usr/share/locale/zh_TW/LC_MESSAGES/hello.mo
/usr/share/man/man1/hello.1.gz

You can see that all the files listed in the specfile %files section are included, including the automatically processed locale files. Also the .build-id file is there, just like in part 1.

Requires and Provides

You can list list the package’s runtime dependencies and the capabilities it provides with the following two commands. The output is similar to corresponding output in part 1.

$ rpm --query --package --requires results_hello/2.10/1.fc40/hello-2.10-1.fc40.x86_64.rpm
$ rpm --query --package --provides results_hello/2.10/1.fc40/hello-2.10-1.fc40.x86_64.rpm

Installing

As a final check, the package can be installed and ran:

$ sudo dnf -C -y install ./results_hello/2.10/1.fc40/hello-2.10-1.fc40.x86_64.rpm
$ hello --greeting="Hello, rpm!"
Hello, rpm!

To clean up your system, undo the installation:

$ sudo dnf -C -y history undo last