domingo, 22 de enero de 2017

Introducción a las autotools

Si alguna vez has usado Unix como desarrollador, probablemente has instalado software invocando este conjuro mágico:



    $ ./configure
    $ make
    $ make install


Yo lo he usado muchas veces, pero en mis primeros tiempos usando Linux tampoco sabía que significaba esto exactamente. Solo sabía que si quería instalar software, era el conjuro que tenía que recitar.

Recientemente he pensado que sería buena idea empaquetar las herramientas Unix que desarrollo usando este proceso de instalación estándar que resulta familiar a muchos usuarios de Unix. Por tanto, es el momento de bucear en la magia de Unix y averiguar lo que hace este conjuro.






=== Los fuentes del proyecto ===

Vamos a ver como funciona todo este proceso, creando un proyecto "helloworld" basado en las GNU autotools, desde cero. La estructura de directorios habitual de tu proyecto, en general tendrá los siguientes directorios:

    fuentes         src/
    documentación   doc/
    paginas man     man/
    scripts         scripts/ (en general, código que se instala pero no se compila)

En este documento propongo esta estructura de proyecto:

    helloworld-1.0.0
        ├── doc
        │   └── README.md
        ├── man
        │   └── helloworld.8
        ├── scripts
        │   └── helloworld.sh
        └── src
            ├── main.c
            └── main.h

Puedes crear esta estructura creando estos directorios:

    $ mkdir -p helloworld-1.0.0/{doc,man,scripts,src}

Entra dentro del directorio raíz del proyecto

    $ cd helloworld-1.0.0/

Y crea todos los ficheros necesarios:

    $ touch doc/README.md
    $ touch man/helloworld.8
    $ touch scripts/helloworld.sh
    $ touch src/main.c
    $ touch src/main.h

Rellena los ficheros con los requisitos de tu propio proyecto.


=== Crear las distintas plantillas Makefile.am ===

La herramienta automake necesita un fichero Makefile.am en la raíz del proyecto, que será el punto de partida de todo el proceso. Editamos el fichero Makefile.am en la raíz del proyecto y le añadimos este contenido:

    $ cat Makefile.am
    AUTOMAKE_OPTIONS = foreign
    SUBDIRS = src doc man scripts

La linea AUTOMAKE_OPTIONS establece el layout del proyecto. La opción "foreign" significa que es un proyecto externo que no sigue el layout de GNU. Esta opción se usa habitualmente para evitar que aparezcan los mensajes de warning de GNU indicando que los ficheros del proyecto están organizados de forma distinta a lo esperado por el GNU coding standard.

La linea SUBDIRS muestra una lista de subdirectorios por los que debe continuar el trabajo de automake. automake entrará en cada uno de esos subdirectorios, buscando nuevos ficheros Makefile.am con los que trabajar. Vamos a preparar el fichero Makefile.am para cada uno de los subdirectorios indicados en SUBDIRS.

Creamos el Makefile.am para el directorio de los ficheros fuente. Este es el Makefile.am mas complicado, ya que los ficheros que referencia deben ser compilados por el compilador de C.

    $ cat src/Makefile.am
    # flags you pass to the C compiler & linker
    AM_CFLAGS = -W -Wall
    AM_LDFLAGS =

    # this lists the binaries to produce
    bin_PROGRAMS = main
    main_SOURCES = main.c main.h

La variable AM_CFLAGS define los flags que se pasarán al compilador, mientras que la variable AM_LDFLAGS define los flags que se pasarán al linker.

Las lineas que empiezan con el formato prefix_SUFFIX se componen de dos partes: un prefijo en minúsculas y un sufijo en mayúsculas. El sufijo "SUFFIX" indica qué debe hacerse con el argumento, y el prefijo "prefix" indica el directorio donde se instala. Por ejemplo, la linea bin_PROGRAMS instala los binarios listados en el directorio $(PREFIX)/bin y la linea sbin_PROGRAMS instala los binarios listados en el directorio $(PREFIX)/sbin.

Continuamos creando el Makefile.am para las paginas del manual:

    $ cat man/Makefile.am
    man_MANS = helloworld.8

El sufijo MANS indica a automake que se trata de manuales, que deben instalarse en el directorio adecuado.

Creamos ahora el Makefile.am para los scripts:

    $ cat scripts/Makefile.am
    bin_SCRIPTS = helloworld.sh

El sufijo "SCRIPTS" indica a automake que son scripts que deben ser instalados sin ser compilados.

Por último, creamos el Makefile.am para la documentación:

    $ cat doc/Makefile.am
    docdir = $(datadir)/doc/@PACKAGE@
    doc_DATA = README.md

Este código cambia el directorio por defecto para instalar la documentación. La variable @PACKAGE@ se expande al nombre del paquete. El significado de la variable $(datadir) (y de otras variables similares) lo puedes encontrar pinchando aquí:


=== Crear y customizar el fichero configure.ac ===

Ahora ejecuta autoscan, sin argumentos:

    $ autoscan

El comando autoscan genera un fichero configure.ac (la entrada de autoconf) realizando un análisis simple de los ficheros contenidos en el paquete. Tras ejecutar el comando, se generan dos ficheros nuevos:

    - autoscan.log
    - configure.scan

Renombra configure.scan como configure.ac:

    $ mv configure.scan configure.ac

Hay que realizar algunos ajustes a mano en el fichero configure.ac. Primero localiza esta linea dentro del fichero:

    AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])

Y cámbiala por lo que corresponda a tu proyecto:

    AC_INIT(helloworld, 1.0.0, al004140@gmail.com)

Para ejecutar las macros internas requeridas para el funcionamiento adecuado de los Makefile generados, añade esta macro en el fichero configure.ac, justo después de la macro AC_INIT:

    AM_INIT_AUTOMAKE

Por ultimo, borra todos los corchetes abiertos '[' y cerrados ']' del fichero.


=== Crear el script de configure ===

Tomando como entrada el fichero configure.ac, utiliza la herramienta aclocal para generar como salida el fichero de macros aclocal.m4:

    $ aclocal

Es necesario generar un fichero config.h.in para que el proceso continúe sin errores. Para ello, ejecuta la herramienta autoheader:

    $ autoheader

Expande las macros de aclocal.m4, generando el directorio autom4te.cache y el script configure. Para ello, ejecuta la herramienta autoconf:

    $ autoconf

El directorio autom4te.cache se usa para acelerar el trabajo de las herramientas autotools, y puede borrarse antes de liberar tu paquete a la comunidad. El script configure es el shell script invocado por los usuarios finales.


=== Crea los Makefile.in ===

La herramienta automake parte de los ficheros Makefile.am creados previamente. A partir de estos ficheros, genera unas plantillas Makefile.in que autoconf se encarga de traducir para generar el Makefile final. Ejecuta la herramienta automake para generar todos los Makefile.in a partir de los Makefile.am creados previamente:

    $ automake --add-missing


=== Configura, compila e instala ===

Los tres pasos que nos quedan para concluir el proceso son:

  1. Configurar la compilación del software
  2. Compilar el software
  3. Instalar el software compilado


1. Configurar la compilación del software

El script configure es responsable de preparar todo para compilar el software para tu sistema target. Se asegura de resolver todas las dependencias para el resto del proceso de compilación e instalación, averiguando todo lo que necesita saber para usar esas dependencias.

Los programas en Unix se escriben habitualmente en C, por lo que necesitamos un compilador de C. En estos casos, el script configure comprobará que tu sistema tiene en efecto un compilador de C instalado, averigua como se llama y en que directorio lo tienes instalado.

Configura la compilación del proyecto, lanzando el script del configure:

    $ ./configure

Este script hace dos cosas:

    * escanea las dependencias basándose en las macros AC_ indicadas en el
      configure.ac, mostrando un error apropiado si encuentra algo mal o
      que falte por instalar en el sistema.

    * por cada Makefile requerido en la macro AC_CONFIG_FILES, traduce la
      plantilla Makefile.in generada por automake en un paso anterior, para
      generar el Makefile final. El Makefile principal provee los target
      mas comunes como install, clean, etc.



2. Compilar el software

Una vez el script configure ha hecho su trabajo, habrá generado todos los Makefile, por lo que ya podemos invocar al comando make para compilar el software.

    $ make

Esto ejecuta una serie de tareas definidas en el fichero Makefile para compilar el programa a partir de su código fuente.


3. Instalar el software

Una vez que el software se ha compilado y esta listo para ejecutarse, los ficheros se pueden copiar en su directorio destino final. El comando make install copiará el programa compilado, librerías, ficheros .h de header y documentación, a los directorios adecuados. Para instalar los fuentes del proyecto en un directorio destino DESTDIR, haremos esto:

    $ make install DESTDIR=/path/to/tmp

Puesto que las tareas a realizar durante la etapa de "install" se definen en el Makefile, el directorio destino donde se instala el software se puede cambiar basándose en opciones pasadas al script del configure, o en cosas que el script de configure pueda averiguar sobre tu sistema.

Dependiendo del directorio de instalación, puede que necesites permisos de root para este paso para que puedas copiar ficheros a directorios del sistema. El truco aquí consiste en usar el comando sudo.



== Testear el binario ===

Prueba a ejecutar el binario generado:

    $ /path/to/tmp/usr/local/bin/main
    Hello World!

Como era de esperar, el proyecto muestra el mensaje "Hello World!".

Visitas:

Seguidores