Compilación y empaquetado en GNU/Linux Debian y derivados

Nota: Esta NO PRETENDE ser una guía oficial, ni tampoco un HOWTO de como compilar oficialmente para Debian siguiendo los Debian Policy, es simplemente una guía para personalizar y/u optimizar la compilación de paquetes personales en GNU/Linux Debian y derivados (Trisquel, Ubuntu).

Esta guía nace del día Hackmate realizado en los «jueves técnicos» entre NetLynx y Mundo Accesible, dictado por Alejandro Garrido Mota (alias mogaal), en el cual se habló de empaquetamiento Debian.

¿Qué es empaquetamiento?

Empaquetamiento es la posibilidad de tomar el código fuente de una aplicación y compilarlo, generando un «paquete» que no es más que un instalador para Debian (formato .deb).

Al empaquetar, contamos con una aplicación instalable desde su .deb con el comando:

dpkg -i paquete.deb

además, si la agregamos a un repositorio personalizado (usando reprepro) podremos contar con la resolución automática de dependencias y todas las posibilidades de aptitude.

Previas básicas de compilación

Qué es compilación?

Proceso de traducción de un código fuente (escrito en un lenguaje de programación de alto nivel) a lenguaje máquina (código objeto) para que pueda ser ejecutado por la computadora.

Cada computadora, cada arquitectura, requerirá un binario completamente distinto.

¿Qué se usa para compilar en GNU/Linux?

Para Compilar en GNU/Linux se usan las herramientas de compilación tradicionales de GNU, como GNU C (o también llamado GCC: GNU C Compiler), bison, auto-tools (crean y/o modifican los archivos configure y Makefile), etc.

Esa serie de herramientas se instalan en Debian con el meta-paquete build-essential:

aptitude install build-essential

Instalando lo necesario para compilar y empaquetar en Debian

Se necesita instalar todo lo necesario para compilar y además, crear paquetes en Debian, para esto ejecutamos:

build-essential dh-make devscripts fakeroot debhelper debian-policy ccache dh-autoreconf autotools-dev

Con esto, ya estamos listos para compilar y empaquetar en GNU/Linux Debian.

Optimizando nuestro paquete

Una de las características de Debian, es que al ser una meta-distribución GNU/Linux, sus paquetes están «semi-optimizados» o «nulamente-optimizados» para aprovechar las características del hardware, por ejemplo, cuando instalamos una versión de 32 bits para arquitecturas x86, los binarios vienen para utilizar cualquier versión de CPU desde i386 en adelante (algunos *someramente* vienen para pentium4), esto es a veces una desventaja cuando queremos aprovechar al máximo nuestro Core 2 Centrino Duo o un Opteron Quad-Core.

«mArch» y «mCPU»

Los parámetros de optimización de la arquitectura son los más «usuales», al decirle al compilador que *aproveche* nuestra arquitectura, este utilizará algunas normas de optimización heredadas de esos CPU (ejemplo, usar amd64 habilitará opciones como fast-math o el uso de directivas de aceleración 3dNow), los beneficios de rendimiento pueden pasar desde un 1-4% hasta un 30% en algunos casos.

La tabla siguiente muestra las distintas «arquitecturas» y el nombre de su valor en «march».

Arch Procesador
i386 Intel i386 y compatibles
i486 Intel i486, AMD 486
i586 Intel Pentium (sin mmx)
pentium-mmx Intel Pentium con soporte de instrucciones MMX
pentiumpro Intel Pentium Pro
i686 Procesadores basados en Pentium Pro
pentium2 Intel Pentium II basados en Pentium Pro y soporte de instrucciones MMX
pentium3, pentium3m Intel Pentium III basados en Pentium Pro con soporte instrucciones MMX y SSE
pentium-m Intel Centrino con soporte de instrucciones MMX, SSE y SSE2
pentium4, pentium4m Intel Pentium 4 con soporte de instrucciones MMX, SSE y SSE2
prescott Intel Pentium 4 con soporte de instrucciones MMX, SSE, SSE2 y SSE
nocona Intel Pentium 4 con extensión de 64bits con soporte de instrucciones MMX, SSE, SSE2 y SSE3
core2 Intel Core2 con extensión de 64bits y soporte de instrucciones MMX, SSE, SSE2, SSE3 y SSSE3
k6 AMD K6 con soporte de instrucciones MMX
k6-2, k6-3 AMD K6-2 y AMD k6-3 con soporte de instrucciones MMX y 3DNow!
athlon, athlon-tbird AMD Athlon y AMD Thunderbird con soporte de instrucciones MMX, 3DNow!, enhaced 3DNow! y soporte parcial de SSE
Athlon-4,athlon-xp, athlon-mp AMD Athlon XP y Semprom con soporte de intrucciones MMX, 3DNow, enhaced 3DNow! y SSE
k8, opteron, athlon64, athlon-fx AMD K8, Opteron, Athlon 64, Athlon FX, Semprom 64, Athlon X2 con soporte de instrucciones x86_64 (este tag setea automáticamente el soporte para las instrucciones MMX, 3DNow!, SSE, SSE2, enhaced 3DNow! E instrucciones de 64 bits)
k8-sse3, opteron-sse3, athlon64-sse3 Opteron, AMD Athlon 64, Athlon 64 X2 con soporte de instrucciones SSE3

mCPU es idéntico a mArch y si especificamos mArch no necesitamos indicar mCPU.

La directiva MAKEOPTS

Hay una directiva especial, de uso únicamente en momento de compilación, MAKEOPTS, podemos indicar el número de cores a utilizar para el proceso de compilación, por defecto viene en:

MAKEOPTS=-j2

Donde indica 1 CPU, (la regla es cantidad de CPUs + 1)

MAKEOPTS=-j3 permitirá utilizar mis dos CPU cores.

¿Cómo sabemos nuestro CPU?

Si ejecutamos:

dmidecode –type 4

Nos responderá algo como esto:

# dmidecode 2.9
SMBIOS 2.4 present.
Handle 0x0006, DMI type 4, 35 bytes
Processor Information
Socket Designation: None
Type: Central Processor
Family: Other
Manufacturer: GenuineIntel
ID: FB 06 00 00 FF FB EB BF
Version: Intel(R) Core(TM)2 Duo CPU     L7500  @ 1.60GHz
Voltage: 1.1 V
External Clock: 200 MHz
Max Speed: 1600 MHz
Current Speed: 1600 MHz
Status: Populated, Enabled
Upgrade: None
L1 Cache Handle: 0x000A
L2 Cache Handle: 0x000C
L3 Cache Handle: Not Provided
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified

Nuestro procesador es un Intel core 2 Duo, además si ejecutamos:

cat /proc/cpuinfo | grep flags

Veremos que nuestro CPU soporta flags de optimización como: sse2, sse3 o ht (hyper-threading).

Las FLAGS de compilación

Cada vez que algo ha de ser compilado, nuestro compilador lee 4 FLAGS que definen el modo como se compilará la aplicación:

CFLAGS: Opciones del Compilador C, incluyen las directivas de «pre-compilación» u optimización del código fuente, como por ejemplo march, mtune, mcpu, el nivel de optimización (-O), etc.

La directiva DEB_BUILD_OPTIONS, si está declarada en «noopt» deshabilita cualquier opción de optimización.

CPPFLAGS: Opciones del Pre-Procesador de C, permite «modificar» la ubicación de los include, ejemplo:

CPPFLAGS="-I/usr/local/openssl-0.9.7b/include

Cambia el include a una versión «compilada local» de openssl.

LDFLAGS: permite hacer lo mismo que CPPFLAGS, pero para enlace a ejecutables o librerías, esto con el fin de utilizar una versión específica de alguna instalada (cuando hay varias versiones), ejemplo:

LDFLAGS="-L/usr/local/openssl-0.9.7b/lib

Acá, usaremos la versión compilada de la librería que está en la ruta designada.

CXXFLAGS: Opciones del compilador C++ (GNU CPP), son idénticas a las de CFLAGS.

La variable de optimización -O

Importante regla de optimización, conjuga una serie de modificaciones al código fuente para mejorar su desempeño,  las optimizaciones van del cero, al 3, la optimización adicional «s» es para reducir al mínimo el tamaño del ejecutable.

Las optimización por defecto en el fuente Debian es -O2, que es un equilibrio entre performance y estabilidad, un -O3 hace el código fuente aún más rápido pero no necesariamente más estable, por lo que es menester probar primero con esa compilación antes de distribuir un binario con -O3.

La mayoría de los paquetes para distribuciones embebidas (ARM, MIPS, MIPSEL) vienen con -Os para disminuir su tamaño, pero eso los hace lentos, si su problema no es el espacio en disco para estos dispositivos imbuidos (teléfonos, smartphones, routers, beagleboards, portátiles Lemote), entonces es mejor recompilar los paquetes usando -O2.

Otras optimizaciones interesantes

-fomit-frame-pointer:  Omitir los pointers, haciendo el debug mas dificil y el backtrace casi *imposible*, sin embargo, para un ejecutable ya probado y en producción, compensa el performance.

-pipe: Optimización al momento de compilar, usa PIPES en vez de caché en disco para los temporales, aumentando la velocidad de compilación, si acaso posee poca memoria, no use -pipe

-msse3: SSE (Streaming SIMD Extensions) es una extensión al grupo de instrucciones MMX para procesadores Pentium III, (fuente: Wikipedia)

Si habilitamos SSE, directivas de optimización de CPU para procesamiento de audio o video, etc, serán utilizadas.

-m64: Habilita el uso de directivas de 64 Bits.

-ffast-math: Permite que los cómputos de punto-flotante (muy usado por aplicaciones de audio o de video) sean más rápidos.

-funroll-loops: típicamente optimiza los loops DO mediante algunas técnicas, haciendo al código más rápido.

Algunas optimizaciones adicionales: http://www.delorie.com/gnu/docs/gcc/gcc_10.html y en la página de GNU C: http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/i386-and-x86-64-Options.html

Re-Empaquetando una aplicación

Todo codigo fuente «Debianizado», posee una carpeta «/debian» que contiene una serie de archivos importantísimos para que este pueda ser construido; explicaré algunos someramente:

changelog: agrega la resolución de bugs abiertos y los cambios realizados en el paquete desde que se incorporó a Debian.

control: describe el paquete, sus dependencias, arquitecturas particulares, etc

copyright: describe el autor y la licencia del paquete

patches: directorio que contiene todos los parches «Debian» que serán incorporados al fuente (opcional)

rules: archivo importantísimo, contiene las reglas de construcción del paquete, es acá precisamente, donde modificaremos las reglas de compilación.

Nuevo en Debian: dpkg-buildflags

El comando dpkg-buildflags es nuevo en Squeeze, y retorna las FLAGS como están definidas en Debian.

Si ejecutamos:

dpkg-buildflags --list

Retornará:

CFLAGS
CPPFLAGS
CXXFLAGS
FFLAGS
LDFLAGS

Un ejemplo de lo que retorna de dpkg-buildflags –get:

–get CXXFLAGS

-g -O2

–get CFLAGS

-g -O2

Entonces, para Debian Squeeze, se puede usar el paquete dpkg-buildflags, que retorna las directivas que usará dpkg-buildpackage, para el archivo debian/rules, algunos sugieren colocar así la regla:

CFLAGS=$(shell dpkg-buildflags –get CFLAGS)

Y modificar las reglas en el archivo:

/etc/dpkg/buildflags.conf

Colocando, por ejemplo:

CFLAGS=»-g -Wall -O3 -march=core2 -msse3″

Aunque esto es algo que podemos «exportar» con:

export CFLAGS=»-g -Wall -O3 -march=core2 -msse3″

export CXXFLAGS=»$CFLAGS»

Empaquetando y Optimizando el IDJC

He decidido usar como ejemplo el IDJC, un programa utilizado para streaming en RadioGNU, pueden ver una introducción del paquete acá:

http://wiki.radiognu.org/doku.php?id=idjc

Los pasos a seguir son los siguientes:

1.- Descargamos el fuente oficial de la página de SourceForge (version 0.8.3)

2.- Instalamos los paquetes «requeridos» para compilar IDJC:

aptitude install python2.6-dev libjack-dev python-gtk2-dev  \
libvorbis-dev libxine-dev libsamplerate0-dev libflac-dev libshout3-dev \
libsndfile1-dev libmad0-dev libavcodec-dev libavformat-dev libtwolame-dev

3.- Aunque podríamos «debianizar el fuente (ejecutando dh_make -f ../fuente.tar.gz)» voy a aprovechar que ya está «debianizado» en Sid, descargandome únicamente la carpeta «/debian» requerida para construir el paquete.

Para ello:

4.- buscamos el paquete en Debian Packages, lo he encontrado en SID: http://packages.debian.org/sid/idjc

5.- Me descargo la «debianización» del fuente:

http://ftp.de.debian.org/debian/pool/main/i/idjc/idjc_0.8.3-1.debian.tar.gz

6.- Descomprimo el paquete idjc_0.8.3-1.debian.tar.gz y muevo la carpeta debian al fuente del idjc:

tar xvf idjc_0.8.3-1.debian.tar.gz && mv debian /usr/src/idjc-0.8.3/

Luego de «debianizado» el fuente, vamos a personalizar la optimización.

Abrimos para ellos el archivo debian/rules.

Optimizando el paquete Debian

Al abrir el archivo debian/rules, nos encontramos con que el empaquetador ya ha hecho algunos «tunnings» necesarios, en dado caso debemos entender que:

Con override_dh_auto_configure podemos cambiar la configuración del paquete (como si pasaramos las líneas en el ./configure).

Con override_dh_auto_build, con esto cambiamos las FLAGS.

Encontramos en el debian/rules del idjc las directivas:

OPTI_FLAGS := -D_REENTRANT -O2 -fomit-frame-pointer -ffast-math -fstrength-reduce -funroll-loops -mmmx -msse -mfpmath=sse -march=pentium4

Y las he cambiado a:

OPTI_FLAGS := -D_REENTRANT -O3 -pipe -fomit-frame-pointer -ffast-math -fstrength-reduce -funroll-loops -mmmx -msse -msse3 -mfpmath=sse -march=core2 -mcpu=core2 -m64

Luego que modificamos el archivo debian/rules, solamente nos toca «construir» el paquete.

Para ello:

7.- Salimos de la carpeta «debian» y nos vamos a la raíz del fuente y ejecutamos:

dpkg-buildpackage -us -uc

Y este creará el paquete .deb.

Ahora a instalar!.

Salimos al directorio donde está el fuente (cd ..) y ahí estará una serie de archivos:

-rw-r–r– 1 root src    1752 sep 11 17:27 idjc_0.8.3-1_amd64.changes
-rw-r–r– 1 root src  605336 sep 11 17:27 idjc_0.8.3-1_amd64.deb
-rw-r–r– 1 root src  115867 sep 11 17:27 idjc_0.8.3-1.debian.tar.gz
-rw-r–r– 1 root src    1354 sep 11 17:27 idjc_0.8.3-1.dsc
-rw-r–r– 1 root src 1308809 sep 11 15:46 idjc_0.8.3.orig.tar.gz

El archivo .deb (nuestro paquete optimizado) lo podremos instalar con un simple:

dpkg -i idjc_0.8.3-1_amd64.deb

Y listo!.

Conclusiones

Además de las opciones de EXPORT, hay ciertas directivas DH (como DH_BUILD_OPTIONS) que aún estoy aprendiendo y pues me permitirán no solo optimizar paquetes sino mantenerme en el Debian Policy (al menos, dh_lintian no me generó error en el paquete optimizado).

Me imagino (como aclaré al inicio) que tengo algunas observaciones y errores en mi código, pero espero que algun Debianita Experto las aclare y permita hacer a esta guía más útil y correcta.

Disfruten sus .deb optimizados! .

12 comentarios sobre “Compilación y empaquetado en GNU/Linux Debian y derivados

  1. excelente articulo, una pregunta, si el archivo buildflags.conf no existe lo creo? uso debian 7 y al buscar el archivo veo que no existe

Deja un comentario