Linux From Scratch XXXVII: Gestión de paquetes con pacman II

En el artículo anterior, hemos dejado instalado el gestor de paquetes pacman de ArchLinux en nuestro Linux From Scratch. Ha quedado pendiente crear la base de datos de paquetes instalados para que pacman pueda gestionar lo que tenemos instalado, así como convertir los paquetes que hemos generado en paquetes de pacman. Vamos a hacer eso ahora.

Para crear esa base de datos, necesitamos obtener toda la información que hemos visto en el anterior artículo. Principalmente descripciones de los paquetes, URLs, licencias y dependencias. Una opción podría ser introducir toda esa información a mano, pero es bastante información y mucha más cuantos más paquetes tengamos, así que he pensado en un método para generarla más o menos automáticamente.

Los PKGBUILDs

ArchLinux tiene un sistema de compilación basado en PKGBUILDs. Los PKGBUILDS son unos scripts que contienen la información y los comandos necesarios para generar un paquete de ArchLinux automáticamente descargando y compilando el código fuente. Cada paquete tiene su PKGBUILD y en esos ficheros está la información que necesitamos ahora. Lo que voy a hacer es extraer la información de los PKGBUILDs de ArchLinux.

Los PKGBUILDs se pueden descargar desde la web de ArchLinux, pero hay algunos paquetes específicos de nuestro sistema que no están en ArchLinux, algunos de ellos puede ser que estén obsoletos, como udev. Otro problema es que hay PKGBUILDs cuya información resulta complicada de extraer. Por eso, lo primero que vamos a hacer es crear los PKGBUILD que no están en ArchLinux o que no podemos descargar desde su web. Hay que crearlos en el mismo directorio en el que tenemos los paquetes que hemos ido generando a medida que compilamos cosas para LFS.

[~/lfs]$ cat > lfs-bootscripts-packages-PKGBUILD << "EOF"
> pkgdesc="Scripts to start/stop the LFS system at bootup/shutdown"
> url="http://www.linuxfromscratch.org/lfs"
> license=('MIT')
> arch=('any')
> depends=('coreutils' 'util-linux' 'sysvinit' 'e2fsprogs' 'psmisc')
> EOF
[~/lfs]$ cat > linux-headers-packages-PKGBUILD << "EOF"
> pkgdesc="Header files for Linux kernel"
> url="http://www.kernel.org/"
> license=('GPL2')
> arch=('x86_64')
> depends=('linux')
> EOF
[~/lfs]$ cat > sysklogd-packages-PKGBUILD << "EOF"
> pkgdesc="System Logging Daemon"
> url="http://www.infodrom.org/projects/sysklogd/"
> license=('GPL2')
> arch=('x86_64')
> depends=('linux')
> EOF
[~/lfs]$ cat > udev-packages-PKGBUILD << "EOF"
> pkgdesc="Programs for dynamic creation of device nodes"
> url="http://anduin.linuxfromscratch.org/sources/other/"
> license=('GPL2')
> arch=('x86_64')
> depends=('glibc' 'util-linux')
> EOF
[~/lfs]$ cat > pacman-packages-PKGBUILD << "EOF"
> pkgdesc="A library-based package manager with dependency support"
> arch=('x86_64')
> url="http://www.archlinux.org/pacman/"
> license=('GPL')
> depends=('bash' 'glibc' 'libarchive' 'curl')
> EOF
[~/lfs]$ cat > initrd-packages-PKGBUILD << "EOF"
> pkgdesc="Initial ramdisk for Linux From Scratch"
> arch=('x86_64')
> url="http://www.kernel.org/"
> license=('GPL')
> depends=('linux')
> EOF

El script mkpkgdb

Una vez conseguidos los PKGBUILDs necesarios, vamos a crear un script que nos va a generar la base de datos de paquetes instalados y va a convertir nuestros paquetes en paquetes de ArchLinux generando también la cache de paquetes. Todo de una vez.

El script depende de que los nombres de los paquetes tengan el fomato NOMBREPRINCIPAL-VERSIÓN.txz. No puede haber guiónes (““) en el número de versión. Si no es así no funcionará. Debemos renombrar los paquetes que tengamos y que no cumplan esta condición. Una opción es cambiarlos por guiónes bajos (“_“). Además, los nombres principales de los paquetes deben ser distintos. No basta solamente con que tengan distinto número de versión. En mi caso, he tenido que hacer estos ajustes:

[~/lfs]$ mv linux-3.2.0-2-debian.txz linux-debian-3.2.0_2.txz
[~/lfs]$ mv linux-3.8.4-1-ARCH.txz linux-arch-3.8.4_1.txz
[~/lfs]$ mv linux-3.5.2-full.txz linux-full-3.5.2.txz

Revisad bien eso, ya que depende de los nombres que hayáis puesto vosotros a los paquetes. Es posible que tengáis que hacer cambios diferentes de los míos. Si los nombres de los paquetes no son correctos es posible que el script no funcione, que no genere algunos paquetes o que los genere y luego pacman no funcione con ellos.

El script es el siguiente. Tenéis que copiarlo en un fichero llamado mkpkgdb. Hay que configurar las dos primeras lineas poniendo el nombre de la arquitectura para la que habéis compilado LFS, que puede ser i686 para 32 bits y x86_64 para 64 bits, y el nombre y la dirección del responsable de mantener los paquetes, que es lo mismo que habéis puesto en el makepkg.conf en el artículo anterior. El script se descarga automáticamente de la web de ArchLinux los PKGBUILDs que le falten.

#!/bin/bash

# Configurar las dos lineas siguientes.
ARCH=x86_64
PACKAGER="hexborg <hexborg@borg.collective.unimatrix.0023>"

gendesc()
{
cat >$dbpath/$pkgnamever-1/desc <<EOF
%NAME%
$pkgname

%VERSION%
$pkgver

%DESC%
$pkgdesc

%URL%
$url

%LICENSE%
$(printf "%s\n" "${license[@]}")

%ARCH%
$arch

%BUILDDATE%
$builddate

%INSTALLDATE%
$installdate

%PACKAGER%
$PACKAGER

%SIZE%
$size

%REASON%
1

%DEPENDS%
$(printf "%s\n" "${lfsdepends[@]}")
EOF
}

geninfo()
{
cat >.PKGINFO <<EOF
pkgname = $pkgname
pkgver = $pkgver
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = $PACKAGER
size = $size
arch = $arch
$(printf "%s\n" "${license[@]/#/license = }")
$(printf "%s\n" "${lfsdepends[@]/#/depend = }")
EOF
}

installdate=$(date +%s)
dbpath=pkgdb/var/lib/pacman/local
cachepath=pkgdb/var/cache/pacman/pkg
mkdir -p $dbpath $cachepath
mkdir -p /tmp/pkgtmp

declare -A deptrans
deptrans=([sh]=bash [awk]=gawk [linux-api-headers]=linux-headers)

repo=packages
curdir=$PWD
for pkgfile in *.txz; do
        pkgnamever=${pkgfile%.txz}
        pkgname=${pkgnamever%%-[0-9]*}
        pkgver=${pkgnamever#$pkgname-}-1

        if [ ! -f $pkgname-$repo-PKGBUILD ]; then
                download=$pkgname
                [ "${pkgname%%-*}" = "linux" ] && download=linux
                pkgurl="https://projects.archlinux.org/svntogit/$repo.git/plain/trunk/PKGBUILD?h=packages/$download"
                wget -O $download-$repo-PKGBUILD "$pkgurl" || rm $download-$repo-PKGBUILD
                [ "${pkgname%%-*}" = "linux" ] && cp linux-$repo-PKGBUILD $pkgname-$repo-PKGBUILD 2>/dev/null
        fi

        [ ! -f $pkgname-$repo-PKGBUILD ] && continue

        echo "Generando paquete $pkgnamever"
        mkdir $dbpath/$pkgnamever-1 2>/dev/null

        pkgdesc=; url=; license=(); depends=(); arch=
        eval $(awk -F= '$1~/\<(pkgdesc|url|license|depends|arch)\>/ && NF>1 \
                {gsub("^[ \t]*","",$0);if(!l[$1]) {l[$1]=1; print $0}}' $pkgname-packages-PKGBUILD)
        [ "$arch" != "any" ] && arch=$ARCH

        lfsdepends=()
        for dep in "${depends[@]}"; do
                dep=${dep%%[<>=]*}
                dep=${deptrans[$dep]:-$dep}
                ls -- $dep-[0-9]*.txz >/dev/null 2>&1 && lfsdepends[${#lfsdepends[*]}]=$dep
        done

        builddate=$(date -r $pkgfile +%s)
        size=$(unxz -l --robot $pkgfile | awk '/totals/ { print $5 }')

        gendesc
        geninfo

        echo "%FILES%" > "$dbpath/$pkgnamever-1/files"
        tar tJf "$curdir/$pkgnamever.txz" >> "$dbpath/$pkgnamever-1/files"

        if [ ! -f "$cachepath/$pkgnamever-1-$arch.pkg.tar.xz" ]; then
                cd /tmp/pkgtmp
                tar xJf "$curdir/$pkgnamever.txz"
                cp "$curdir/.PKGINFO" .
                chmod -R 755 *
                rmdir -p var/tmp 2> /dev/null
                rm etc/mtab 2> /dev/null
                tar cJf "$curdir/$cachepath/$pkgnamever-1-$arch.pkg.tar.xz" .PKGINFO *
                rm -rf *
                cd "$curdir"
        fi
done

El script deja los ficheros generados en el directorio pkgdb organizados según la estructura que pacman necesita. No todos los paquetes quedan bien configurados, ya que hay algunos problemas sobre todo con las dependencias. Para evitar estos problemas tendríamos que haber ido creando todos estos datos a medida que ibamos instalando programas. De todos modos, de momento nos sirve. A medida que vayamos actualizando paquetes iremos poniendo bien las dependencias. Ahora damos permisos de ejecución y los ejecutamos.

[~/lfs]$ chmod +x mkpkgdb
[~/lfs]$ sudo ./mkpkgdb
[~/lfs]$ sudo bash -c 'lfsinst <(cd pkgdb; tar cJ *)'

La última linea es para instalar todos los ficheros de la caché y la base de datos en nuestro LFS. A mi me ha ocupado todo 181 Mb. Lo que más ocupa es la caché.

El problema de las dependencias

El principal problema que he encontrado a la hora de hacer esto es determinar adecuadamente las dependencias de cada paquete. Aunque me he basado en las de ArchLinux, esto no es ningúna garantía, ya que no tienen por qué coincidir exactamente. Los paquetes pueden estar compilados de forma diferente. Un paquete puede dividirse en varios y varios pueden agruparse en uno. Además pueden estar modificados.

Lo que tendríamos que haber hecho es estudiar detenidamente el código fuente de cada paquete para ver qué necesita para funcionar y determinar las dependencias. Esto no sería dificil si lo hubieramos hecho desde que empezamos a generar el primer paquete, pero ahora es un poco coñazo. Podemos ayudarnos de las dependencias de los paquetes de otras distros, pero eso solo es una ayuda.

Una posible forma de determinar las dependencias que podríamos usar después de haber instalado un paquete es comprobar con ldd qué librerías necesita cada uno de sus binarios y en qué paquete están. Esto sirve para librerías, pero no para otros paquetes que contengan ficheros de datos o de configuración ni para programas que estén hechos con scripts.

En definitiva: no hay nada que nos permita determinar las dependencias de un paquete de una forma fácil y rápida. Hay que hacer un trabajo cuidadoso. Por suerte el objetivo de este trabajo es aprender cómo se hacer estas cosas, así que no pasa nada por no tener las dependencias bien ahora mismo. Podemos asumirlo. Si estáis haciendo un trabajo más serio y queréis tener bien las dependencias ahora mismo, váis a tener que investigar más por vuestra cuenta. Yo las iré poniendo en sucesivos paquetes a medida que vaya haciendo actualizaciones hasta que lleguen a quedar bien todas. De momento me sirve así.

Este problema no lo tendríamos, o sería mucho más llevadero, si estuvieramos haciendo una distro derivada de otra en vez que partir de cero, ya que en ese caso sí que nos servirían las dependencias de la distro madre excepto en aquellas cosas que modifiquemos a propósito. Los cambios que tendríamos que hacer en las dependencias los conoceríamos porque dependen de las cosas que nosotros mismos hemos modificado, pero eso es demasiado fácil para nosotros, ¿verdad? 🙂

Probando

Vamos a entrar en nuestro LFS para hacer unas cuantas pruebas. Primero vamos a sacar la lista de paquetes instalados y pedir información sobre vim.

[~/lfs]$ sudo lfs
[root@tatooine:/]# pacman -Q
[root@tatooine:/]# pacman -Ql vim

Parece que funciona. Ahora vamos a desinstalar vim. Yo no me atrevería a usar pacman -Rsc, ya que las dependencias pueden estar mal puestas por el problema que os he comentado antes, así que lo vamos a hacer con pacman -R. A continuación podremos ver que vim está desinstalado comprobando que los comandos que lo usan dan error.

[root@tatooine:/]# pacman -R vim
[root@tatooine:/]# pacman -Ql vim
error: package 'vim' was not found
[root@tatooine:/]# ls /bin/vim
ls: cannot access /bin/vim: No such file or directory
[root@tatooine:/]# vim
bash: vim: command not found

Por ahora bien. Ahora vamos a volver a instalarlo desde la caché y ejecutarlo para comprobar que funciona. Ojo: No podemos instalar cosas con pacman -S porque ese comando usa repositorios remotos y de momento no tenemos ninguno. Tenemos que usar pacman -U y poner el nombre del fichero del paquete que queremos instalar.

[root@tatooine:/]# cd /var/cache/pacman/pkg/
[root@tatooine:/var/cache/pacman/pkg]# pacman -U vim-7.3-1-x86_64.pkg.tar.xz
[root@tatooine:/var/cache/pacman/pkg]# vim
[root@tatooine:/]# exit

Ha funcionado todo bien. Cuidado con hacer esto con paquetes importantes. Los paquetes que tenemos de momento, excepto unos pocos, son muy básicos e imprescindibles para el correcto funcionamiento del sistema, así que si nos dedicamos a desinstalar cosas sin saber lo que hacemos es posible que acabemos cargándonos algo. Vim es un buen paquete para probar, ya que no repercute en otras cosas. Si en lugar de vim habéis instalado algún otro editor de consola también es un buen candidato para pruebas.

Haciendo limpieza

Ahora mismo, nuestro LFS tiene todo lo necesario incluyendo los paquetes que hemos instalado. Si queremos liberar un poco de espacio, podemos borrar cosas de nuestro sistema anfitrión. Por ejemplo los PKGBUILDs que hemos generado y descargado y los ficheros generados por el script.

[~/lfs]$ sudo rm -rf *-PKGBUILD pkgdb

También podemos borrar los antiguos paquetes que ya no necesitaremos más.

[~/lfs]$ rm *.txz

Otra cosa que podemos borrar es el código fuente de todos los paquetes que tenemos en el directorio /source de LFS. En principio ya no lo necesitamos. Y en caso de que podamos necesitar alguno de ellos para compilar algo, siempre podemos volver a descargarlo.

[~/lfs]$ sudo lfs
[root@corellia:/]# rm -rf sources/*
[root@corellia:/]# exit

Fijaros que aunque he borrado los ficheros, me he quedado con el directorio. Puede ser un buen lugar de trabajo para compilar cosas.

Por supuesto, podemos hacer copias de seguridad de todos estos ficheros por si acaso o conservarlos si lo consideramos más prudente. Lo que sí recomiendo es conservar de momento los scripts lfs y lfsinst. Aunque a partir de ahora vamos a trabajar en nuestro LFS, es posible que en algún caso todavía nos resulten útiles. Sobre todo por si tenemos que entrar en el chroot para arreglar algo.

EOF

Anuncios