Cuando nos enfrentamos a un proyecto nuevo siempre debemos crear un sistema automático para compilar nuestra "obra". Este paso puede ser más o menos complejo dependiendo del tamaño de nuestro proyecto y de otros factores tales como su portabilidad, etc. Unas herramientas muy apañadas que facilitan en gran medida esto (cuando las conoces, si no se pueden volver un tanto "engorrosas") son las autotools. Y de eso va esta receta... a petición del pueblo, el tonto'las'autotools hará un pequeño programilla que será compilado con estas herramientas.

Qué son las autotools

Bueno, como dijimos en la introducción, las autotools son un conjunto de herramientas de GNU que facilitan la compilación de proyectos software en plataformas tipo Unix, MacOS-X, Cygwin e incluso Windows. Las herramientas concretamente son:
  • Autoconf: genera el "famoso" script configure a partir de unas macros en lenguaje M4.
  • Automake: a partir de unas sencillas reglas descritas en Makefile.am, genera un complejo Makefile.in con el que configure creará los Makefiles finales.
  • Autoheader: crea el archivo config.h.in con el que configure generará un archivo config.h que contendrá una serie de macros dependientes de la arquitectura que podremos usar en nuestro proyecto.
  • Libtool: si nuestro proyecto no es programa, sino una librería, estas herramientas nos facilitarán en gran medida su compilación e instalación.

Cómo trabajar con las autotools

Una vez tenemos las herramientas (los paquetes Debian/Ubuntu tienen el mismo nombre...) tenemos que usarlas, para ello primero explicaré cómo se debe hacer (o cómo lo hago yo) y luego haremos un pequeño ejemplo. Las Autotools trabajan con una importante cantidad de scripts, macros y archivos de definiciones... sin embargo (y aquí viene lo bueno) nosotros sólo debemos crear DOS archivos. Además una herramienta nos generará automágicamente una versión inicial de uno de esos dos ficheros. Los ficheros en concreto son:
  • configure.ac: contiene información relativa al proyecto en general.
  • Makefile.am: contiene instrucciones simples sobre los componentes que forman el proyecto.
Una herramienta, llamada autoscan nos generará una versión inicial de configure.ac muy fácil de modificar y adaptar a nuestras necesidades. La secuencia de trabajo (cuando tenga tiempo subiré un impresionante diagrama PNG) será la siguiente:
  1. Ejecutar autoscan para obtener el configure.ac incial.
  2. Modificar configure.ac para adaptarlo a nuestras necesidades.
  3. Crear Makefile.am según las necesidades de nuestro proyecto.
  4. Y ya está! a partir de ahora todos los pasos son automáticos:
    1. Ejecutar aclocal para generar las macros M4 utilizadas en los scripts para nuestro proyecto.
    2. Lanzar autoheader para generar el archivo config.h.in basado en nuestro configure.ac.
    3. Ejecutar automake para generar los ficheros Makefile.in utilizados por configure para crear los Makefiles.
    4. Finalmente lanzamos autoconf para generar el script configure.
    Después de todo esto, cualquier usuario que obtenga nuestro proyecto con todos esos ficheros generados, podrá compilar e instalar nuestro proyecto simplemente haciendo:
    $ ./configure
    $ make
    $ sudo make install
    Opcionalmente (y muy recomendado) se suele incluir un script llamado autogen.sh que realiza todos los pasos automáticos hasta obtener el configure. Ese archivo puede tener el siguiente aspecto:
    #!/bin/sh
    aclocal
    autoheader
    automake -ac
    autoconf
    Sin embargo hay por ahí muchos autogen.sh mejores que hacen cosas como limpiar todos los archivos generados, etc. Algunos paquetes fuente de Debian tienen estos scripts que podéis probar.

    Nuestro ejemplo

    Vamos a suponer que nuestro proyecto consta de un programa fuente, llamado mir_kernel.c. Lo primero es hacer un poco de limpieza, si tenemos el directorio ~/mir con dicho fuente, crearemos el subdirectorio src y dejaremos ahí el fichero (el nombre es un simple convenio):
    tobias@nasa:~/mir$ mkdir src
    tobias@nasa:~/mir$ mv mir_kernel.c src/
    Ahora vamos a generar nuestro configure.ac:
    tobias@nasa:~/mir$ autoscan
    tobias@nasa:~/mir$ ls
    autoscan.log  configure.scan  src
    Tenemos dos ficheros: autoscan.log que podemos ir borrando (o cotilleando) y el configure.scan, que tiene el siguiente aspecto:
    #                                               -*- Autoconf -*-
    # Process this file with autoconf to produce a configure script.
    
    AC_PREREQ([2.64])
    AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
    AC_CONFIG_SRCDIR([src/mir_kernel.c])
    AC_CONFIG_HEADERS([config.h])
    
    # Checks for programs.
    AC_PROG_CC
    
    # Checks for libraries.
    
    # Checks for header files.
    
    # Checks for typedefs, structures, and compiler characteristics.
    
    # Checks for library functions.
    
    AC_OUTPUT
    Todo eso que vemos ahí son macros M4, en los enlaces del final podéis ver de qué va eso (aunque para usar autotools no es necesario). La primera macro obliga a que la versión de autotools necesaria para ejecutar estos scripts sea la 2.64, podemos bajarla un poco si no queremos ser demasiado restrictivos, puesto que no todo el mundo tiene porqué usar Debian Unstable :P. La siguiente macro establece el nombre del proyecto, su versión y una dirección de e-mail a la que envíar bugs. Rellenemos los campos con la información apropiada. Las dos macros siguientes establecen el directorio de nuestro código y cómo se debe llamar el archivo de macros C por si queremos usarlo en nuestro proyecto (por ejemplo, crea una macro con la versión que indiquemos arriba para poder usarlo en nuestro código). El resto de macros sirve para dar instrucciones sobre dependencias exernas, por ejemplo: AC_PROG_CC indica que nuestro proyecto requiere un compilador de C. Existen muchas macros más, por ejemplo: AC_PROG_INSTALL comprueba que exista un programa que nos permita instalar nuestro proyecto en el sistema. Además, existen otras macros útiles, por ejemplo:
    • AM_INIT_AUTOMAKE: indica que, como salida, autotool debe generar un Makefile. Esto será necesario en nuestro caso.
    • AC_CONFIG_AUX_DIR(directorio): por defecto, todos los ficheros generados por las Autotools estarán en la raíz del proyecto, con esta macro especificamos otro directorio.
    • AC_CONFIG_MACRO_DIR(directorio): los archivos con las descripciones de las macros M4 los alojará en este directorio.
    • AC_CONFIG_FILES([Makefile src/Makefile]): indica los archivos de salida que creará Autotools.
    Existen más macros muy útiles, echad un vistazo al manual si queréis más información. Nosotros modificaremos nuestro configure.scan de la siguiente manera:
    #                                               -*- Autoconf -*-
    # Process this file with autoconf to produce a configure script.
    
    AC_PREREQ([2.64])
    AC_INIT([MIR Kernel], [2.0], [billgates@vigilando.org])
    AC_CONFIG_AUX_DIR([config])
    AC_CONFIG_MACRO_DIR([m4])
    AM_INIT_AUTOMAKE
    AC_CONFIG_SRCDIR([src/mir_kernel.c])
    AC_CONFIG_HEADERS([config.h])
    
    # Checks for programs.
    AC_PROG_CC
    AC_PROG_INSTALL
    # Checks for libraries.
    AC_CHECK_LIB([bluetooth])
    # Checks for header files.
    
    # Checks for typedefs, structures, and compiler characteristics.
    
    # Checks for library functions.
    AC_CONFIG_FILES([Makefile src/Makefile])
    AC_OUTPUT
    Y este archivo será nuestro configure.ac:
    tobias@nasa:~/mir$ mv configure.scan configure.ac
    Nótese que en el archivo añadimos una linea:
    AC_CHECK_LIB([bluetooth])
    Ahí podemos indicar una serie de librerías con que debe contar nuestro sistema para poder compilar y ejecutar nuestro proyecto. Ahora escribimos nuestro Makefile.am, uno por cada subdirectorio de nuestro proyecto. Primero el del directorio padre:
    SUBDIRS=src
    Sólo tenemos que indicar dónde hay subdirectorios con otros Makefile.am. Ahora el del del directorio hijo:
    mirprgdir=/usr/local/bin/
    mirprg_PROGRAMS=mir
    mir_SOURCES=mir_kernel.c
    Este archivo indica lo siguiente:
    • Directorio donde el programa mir se instalará. El nombre de la variable es importante, si definimos la variable pruebaprgdir, automake buscará la variable pruebaprg_PROGRAMS.
    • De qué programas consta el proyecto. La siguiente variable establece una lista de ejecutables que hay que crear en el proyecto. Por cada programa que especifiquemos, automake buscará una variable con nombre programa_SOURCES.
    • Qué archivos fuentes son necesarios para compilar un programa. La última variable contendrá una lista de archivos fuente necesarios para crear nuestro ejecutable.
    • Opcionalmente podemos especificar librerías externas que necesite nuestro programa (en nuestro configure.ac deberíamos haber comprobado que existan) :
      mir_LIBADD=-lbluetooth
    • También podremos indicar opciones de linkado y compilación adicionales para cada programa mediante las variables mir_LDFLAGS y mir_CFLAGS.
    En este punto, si trabajamos con repositorios, podemos subir todos los archivos que tenemos en ~/mir puesto que son los mínimos, a partir de los cuales se generarán los demás (y que por tanto no son necesarios en el repositorio). Ahora, para preparar nuestro proyecto para distribuir, ejecutamos lo siguiente:
    tobias@nasa:~/mir$ mkdir config
    tobias@nasa:~/mir$ aclocal
    tobias@nasa:~/mir$ autoheader
    tobias@nasa:~/mir$ automake -ac
    configure.ac:8: installing `config/install-sh'
    configure.ac:8: installing `config/missing'
    src/Makefile.am: installing `config/depcomp'
    Makefile.am: installing `./INSTALL'
    Makefile.am: required file `./NEWS' not found
    Makefile.am: required file `./README' not found
    Makefile.am: required file `./AUTHORS' not found
    Makefile.am: required file `./ChangeLog' not found
    Makefile.am: installing `./COPYING' using GNU General Public License v3 file
    Makefile.am:     Consider adding the COPYING file to the version control system
    Makefile.am:     for your code, to avoid questions about which license your project uses.
    ¡Hemos obtenido un error! ¿Qué ha pasado? :O Pues fácil, existen una serie de ficheros de texto, en plan README que deberían acompañar todos los proyectos, estos archivos tendremos que crearlos "a mano", podéis echar un vistazo a algunos de los que incluyen los paquetes fuente de Debian para que os hagáis una idea del formato y contenido. Podéis crear archivos vacíos si queréis aunque es aconsejable seguir las mismas pautas que todo el mundo sigue... Además, automake tiene una serie de plantillas para algunos de ellos, como por ejemplo para COPYING usa la GPLv3 por defecto. Una vez que los hayáis creado:
    tobias@nasa:~/mir$ automake -ac
    tobias@nasa:~/mir$ autoconf
    tobias@nasa:~/mir$ ls
    aclocal.m4  autom4te.cache  config  config.h.in  configure  configure.ac  COPYING  INSTALL  Makefile.am  src
    Y ese directorio, tal cual, está listo para ser distribuído por Internet, todo el mundo podrá compilar e instalar tu proyecto con las instrucciones de siempre:
    tobias@nasa:~/mir$ ./configure
    tobias@nasa:~/mir$ make
    tobias@nasa:~/mir$ sudo make install

    Conclusiones

    Quizás os haya podido parecer un poco enrevesado, pero haced vosotros el ejemplo, veréis que todo esto se resume en modificar el fichero configure.ac y crear los archivos Makefile.am que son muy simples. Acto seguido ejecutamos las Autotools (buscad un autogen.sh que están más chulos) y listo. Muy sencillo. :P

    Enlaces

    Trabajo futuro

    • Corregir y completar esta receta (help!) :P
    • Generar librerías con libtool


blog comments powered by Disqus