Compilación de aplicaciones a partir de código fuente en sistemas UNIX y GNU/Linux

Compilar aplicaciones desde el código fuente es una competencia fundamental de los administradores de sistemas. No sólo por los beneficios que nos aporta en cuanto a personalización de instalación y optimización para una arquitectura específica, sino porque en ocasiones nos encontramos con aplicaciones que o bien no tienen binarios para nuestra plataforma, o simplemente es distribuida únicamente en forma de código fuente. Puede también darse el caso de que una determinada versión de una aplicación -especialmente si está en alpha o beta aún- sólo haya publicado las fuentes.

A la hora de instalar aplicaciones y servidores en un sistema GNU/Linux, tendremos distintas opciones:

  • Repositorios de la distribución(CentOS, Debian, RHEL, etc …) con herramientas avanzadas de paquetería como yum, apt-get, aptitude, etc. Tendremos la seguridad de que funcionará sin problemas, ya los repositorios oficiales de cada distribución han sido testeados más que de sobra.
  • Paquetes binarios compilados: como puedan ser .rpm para Red Hat o derivadas, o .deb para Debian o derivadas.
  • Código fuente: más personalizable. Podemos optimizar para la plataforma en la que instalamos durante la compilación. Pueden surgir más problemas que al instalar desde repositorios (incompatibilidad de librerías, integración del servidor con el SO, etc). Mayor portabilidad entre sistemas operativos y plataformas de hardware. Por otro lado, actualizar el software se vuelve más tedioso ya que tendremos que recompilar la aplicación en cuestión.

En organizaciones de mediano y gran tamaño los accesos desde la red interna al exterior estarán muy limitados. Esto es así tanto por razones de seguridad (por ejemplo evitar ataques externos o evitar vulnerabilidades que puedan generarse desde dentro) como de funcionamiento y administración interna (sincronizaciones NTP, actualizaciones de sistema centralizadas con Windows Server Update Services, etc). Por unas razones o por otras, poniéndonos en el peor de los casos, no tendremos acceso desde nuestros servidores a los repositorios externos. Por ello resulta básico saber instalar determinadas aplicaciones y servidores desde código fuente.

No obstante, yo recomendaría que para instalar determinadas aplicaciones a partir de paquetes .deb o .rpm sueltos, es recomendable por razones de salud mental (pasar por un infierno de dependencias rpm puede hacer mella) un acceso temporal a repositorios oficiales para resolver las dependencias con apt-get o yum, más que nada para evitar problemas de compatibilidad y versionado a largo plazo.

Documentación aportada en el código fuente

Junto al código fuente encontraremos ficheros de texto tipo README o INSTALL que incluyen instrucciones específicas sobre la compilación de la aplicación. Es muy recomendable echarles un vistazo para evitarnos sorpresas y tener en cuenta parámetros y requisitos específicos señalados por los programadores. También podemos ver distintos métodos para optimizar el proceso de compilación y funcionamiento final de la aplicación una vez compilada.

Proceso de compilación

Realmente es muy sencillo en teoría, pero siempre debemos tener presente la Ley de Murphy:

Si algo puede salir mal, saldrá mal

Y ese mal puede ser que no tengamos las librerías necesarias instaladas en el sistema, o que no sean de la versión que la aplicación necesita. En cualquier caso, nada que no tenga solución. Evidentemente para que una compilación finalice exitosamente tendremos que cumplir previamente con todas las dependencias y requisitos. Vamos a aclarar algunos términos antes del ejemplo que pondré posteriormente:

  • Configure: en este paso configuramos el posterior proceso de compilación. Se crea el fichero Makefile que controla el proceso de compilación y que contiene las referencias a dependencias, bibliotecas, módulos, etc… necesarios.

    Normalmente la instalación de programas compilados desde código fuente se realiza en /usr/local/, por razones de convención simplemente, ya que es recomendable para llevar un cierto control del software que instalamos en el sistema. Para especificar la localización de instalación, utilizaremos el parámetro prefix de configure. Por ejemplo:

    ./configure --prefix=/usr/local/apache

    Hará los checkings correspondientes para comprobar si las dependencias de herramientas y librerías necesarias están instaladas en el sistema.

    Para ver todos los parámetros que acepta configure:

    ./configure --help
  • Make clean: borra restos de intentos previos de compilación. Recomendable en cualquier caso para hacer siempre una instalación limpia.
  • Make: construye el programa binario que vamos a instalar, a partir del Makefile anteriormente creado, enlazando librerías y módulos.

    Los warnings no son importantes, pero los avisos tipo error sí son críticos, ya que terminan de forma abrupta la compilación del programa. En tal caso, tendremos que subsanarlos y volver a iniciar el proceso de compilación desde el principio con configure, habiendo hecho antes un make clean por si las moscas.

  • Make install: ejecuta make con el objetivo install, es decir, instala el programa con los parámetros especificados (localización, módulos, etc)
  • Make uninstall: desinstala la aplicación. Si no hay objetivo uninstall en el Makefile del programa, entonces habrá que borrar la aplicación manualmente donde la hayamos instalado (por eso, como te dije al principio, es recomendable tener todo nuestro software compilado en un sitio concreto como puede ser /usr/local)

Ejemplo práctico: compilación de Apache desde código fuente

Requisitos

  • Compilador gcc y herramienta make instalados en el sistema.
  • Incluir las rutas a gcc y make en el PATH. Normalmente suelen estar en /usr/bin/

Las podemos buscar con whereis:

whereis -b gcc
gcc: /usr/bin/gcc 

whereis -b make
make: /usr/bin/make

Dependiendo de dónde esté (normalmente /usr/bin como es mi caso), tendremos que incluirlo en el PATH si es que no lo está:

export PATH=$PATH:/usr/bin
  • Si necesitáramos alguna aplicación/librería más, nos avisaría al intentar compilar, y procederíamos a instalarla.

Manos a la obra

En este caso voy a compilar un Apache 2.2.24. Procedos a desempaquetar y descomprimir el tarball:

tar -zxvf httpd-2.2.24.tar.gz

Nos situamos en el directorio del código fuente:

cd httpd-2.2.24

Configuramos la compilación:

./configure --prefix=/usr/local/apache --enable-mods-shared=all --enable-ssl --enable-proxy --enable-proxy-connect --enable-proxy-ftp --enable-proxy-http --enable-proxy-scgi --enable-proxy-ajp --enable-proxy-balancer --with-mpm=worker

Donde:

  • prefix: directorio de instalación. Nuestro usuario deberá tener permisos sobre dicho directorio. Si estamos como root, no habrá problemas.
  • enable-mods-shared: para instalar todos los módulos dinámicos de Apache y aprovechar las funcionalidades DSO (Dynamic Shared Objects) que nos permiten cargar/descargar módulos de forma dinámica con la directiva LoadModule.
  • enable-ssl: mod_ssl, para posteriormente configurar el servidor Apache con seguridad por https y certificados SSL.
  • Todas las opciones “proxy” nos permitirán posteriormente configurar el servidor Apache como frontal web y redirecciones a otros servidores web, servidores de aplicaciones, etc.
  • with-mpm=worker: gestión de procesos con MPM worker. Si la página web que se va a alojar en el servidor Apache hace uso de PHP ya te digo que esta opción la quites, el MPM prefork por defecto es el recomendable por cuestiones de compatibilidad.

Construimos el binario e instalamos:

make && make install

Con make construímos el software a partir del código fuente y las opciones que pusimos en “configure”. Con make install, procedemos a la instalación en nuestro sistema. Los parámetros (comandos) del objetivo “install” los podemos encontrar en el fichero Makefile

Si todo ha ido bien, tras estos sencillos pasos, tendremos nuestra instalación del servidor web Apache en /usr/local/apache.