Atreviéndome con las GNU Autotools (a la una…)
Como continuación a la anterior entrada acerca de las autotools, vamos a ver en ésta como se haría para incluir otro paquete de forma que se configure, compile e instale como si formase parte de nuestro proyecto. También se intentará dar alguna idea acerca de la manera de añadir opciones al configure para que el usuario pueda habilitarlas o desabilitarlas a su gusto utilizando ./configure – -enable-cosa o ./configure – -disable-cosa, pero no contentos con eso, además intentaremos dar alguna pista sobre cómo podríamos buscarnos la vida para chequear en el configure algo que no utilice pkg-config.
Reiteramos que ésto en ningún modo pretende ser un manual exaustivo ni mucho menos, sino simplemente unos apuntes personales compartidos a través de la Red con la idea de dar algunas pistas a quién quiera y no sepa muy bien por donde empezar a “meterle mano” al bicho. En la Red hay mucha e infinitamente mejor información que lo que aquí ofrezco, aunque recomiendo buscarla en inglés.
Incluyendo otro paquete dentro del nuestro
Supongamos que, por la razón que sea, nos vendría bien hacer uso de un determinado programa con licencia GPL y queremos que se instale como si formase parte de nuestro proyecto (se da por supuesto que el programa a añadir contiene sus propios configure.ac y Makefile(s).am). La manera de proceder sería:
Primeramente descomprimir el paquete dentro de nuestro dirbase (ver la primera parte) y seguidamente tomar buena nota del nombre de la carpeta donde se ha descomprimido.
A continuación añadiríamos la siguiente línea a nuestro configure.ac a partir del punto donde esté situada la macro AC_CONFIG_HEADERS.
AC_ARG_ENABLE(progExterno, [--disable-progExterno no incluye progExterno])
Con la anterior línea habilitamos la opción ./configure –disable-progExterno, por lo que más adelante tendremos que tener en cuenta que la opción por defecto sea incluir progExterno. La expresión “no incluye progExterno” será lo que aparecerá, al efectuar un ./configure – -help, como explicación a la opción que desabilita la inclusión de progExterno en la compilación e instalación de nuestro proyecto.
Luego, antes de la macro AC_CONFIG_FILES pondríamos:
if test "x$enable_progExterno" = "xno"; then SUBDIRS_VALIDOS="src imgs doc" else SUBDIRS_VALIDOS="src imgs doc directorio_progExterno" AC_CONFIG_SUBDIRS(directorio_progExterno) fi AC_SUBST(SUBDIRS_VALIDOS)
Pero aquí hemos introducido una modificación a la forma en que tratábamos los subdirectorios a incluir en los Makefile.am de la primera parte. Es lo que pasa con GNU/Linux, que raramente hay una única manera de hacer las cosas.
La modificación consiste en exportar los subdirectorios (o carpetas) válidos a través de la variable SUBDIRS_VALIDOS, por lo que en el Makefile.am, en lugar de las líneas:
SUBDIRS = src imgs DIST_SUBDIRS = src imgs doc
pondremos simplemente:
SUBDIRS = @SUBDIRS_VALIDOS@
y listo. Nuestro paquete compilará e instalará por defecto progExterno y dará al usuario la opción de desabilitar esta acción ejecutando:
~/dirbase$ ./configure - -disable-progExterno
También podría ser necesario ejecutar dentro de directorio_progExterno su propio autogen.sh, o incluso crearlo si no existiera y distribuirlo junto con nuestro proyecto. Para asegurarnos de que se ejecuta y tratar de ahorrarle algo de trabajo al usuario final, bien podríamos añadir al autogen.sh de nuestro dirbase algunas líneas extras como:
cd directorio_progExterno
./autogen.sh
cd ../
Comprobando algo que no utiliza pkg-config
Para el caso vamos a suponer que queremos utilizar en nuestra aplicación las funciones de base de datos de PostgreSQL, los archivos de cabecera de CUPS, para localizar las impresoras del sistema, y los paquetes latex2html y pdflatex para generar los achivos de ayuda al usuario que hemos decidido crear con LaTeX (no los de documentación técnica de desarrollo, ya que éstos los creamos con Doxygen), en html y pdf y que se generen al momento de compilar, asegurándonos de esa forma de que el html y el pdf generados estén siempre al día y, de paso, ahorrando algo de espacio y de tiempo al enviar nuestras actualizaciones al repositorio.
Inmediatamente antes de las macros AC_SUBST(dependPKG_CFLAGS) y
AC_SUBST(dependPKG_LIBS), escribiríamos las siguientes líneas:
# Comprobación de que los archivos de cabecera de postgre y cups # están instalados:
AC_CHECK_HEADERS(postgresql/libpq-fe.h, [dependPKG_CFLAGS+="-I$(pg_config --includedir) "], [AC_MSG_ERROR([No se encuentra libpq-fe.h ¿libpq-dev no instalado?])], []) AC_CHECK_HEADERS(cups/cups.h, [dependPKG_CFLAGS+="$(cups-config --cflags) "], [AC_MSG_ERROR([No se encuentra cups.h ¿libcups2-dev no instalado?])], [])
# Comprobación de que también están los binarios
AC_CHECK_LIB(pq, main, [dependPKG_LIBS+="-L$(pg_config --libdir) -lpq "], [AC_MSG_ERROR([No se encuentra libpq ¿libpq no instalado?])], []) AC_CHECK_LIB(cups, main, [dependPKG_LIBS+="$(cups-config --libs) "], [AC_MSG_ERROR([No se encuentra libcups ¿libcups2 no instalado?])], [])
Aunque las bibliotecas no contengan la función main(), si la misma se le pasa como argumento a la macro AC_CHECK_LIB ésta se contentará con buscar si el paquete existe.
A continuación irían las macros:
AC_SUBST(dependPKG_CFLAGS) AC_SUBST(dependPKG_LIBS)
Y ahora tocaría comprobar que están instalados latex2html y pdflatex, que serán los que utilicemos para compilar la ayuda escrita en LaTeX.
# Comprueba la existencia de latex2html
printf "checking for latex2html... " if test -e /usr/bin/latex2html; then printf "yes\n" else printf "AVISO: No hallado /usr/bin/latex2html\n" printf "La documentación de ayuda no podrá compilarse ni instalarse.\n" fi
# Comprueba la existencia de pdflatex (pdftex)
printf "checking for pdflatex (pdftex)... " if test -e /usr/bin/pdflatex || test -e /usr/bin/pdftex; then printf "yes\n" else printf "no\n" printf "configure: AVISO: No hallado /usr/bin/pdflatex o /usr/bin/pdftex\n" printf " ¿texlive-latex-base no instalado?\n\n" printf " La ayuda no podrá compilarse en formato pdf.\n" printf " Esto no tiene importancia.\n" printf " Puede ignorar los errores de Make a este respecto.\n\n" fi
La utilidad de las macros AC_CHECK_HEADERS y AC_CHECK_LIB queda patente en el ejemplo: La primera se encarga de comprobar la existencia de los archivos de cabecera y la segunda de los binarios. No creo que haga falta decir (pero lo digo por si acaso) que al incluir en nuestra aplicación funciones de una biblioteca (o librería) externa, necesitamos tanto los archivos llamados de cabecera (los .h) para incluirlos en nuestros fuentes y poder así hacer uso de las funciones de la biblioteca, como los binarios, que son necesarios para enlazar nuestro ejecutable con la biblioteca en cuestión.
Pero tal vez alguien se pregunte: ¿De dónde salen pg_config y cups-config?
Pues de aquí: http://www.postgresql.org/docs/9.0/interactive/app-pgconfig.html
y de aquí: http://www.cups.org/documentation.php/doc-1.4/man-cups-config.html
En cada uno de esos enlaces vemos los parámetros a utilizar según lo que queramos hacer, en este caso chequear la instalación de las cabeceras (- -includedir y - -cflags) y de las bibliotecas (- -libdir y – -libs), según las especificaciones de la documentación de cada paquete.
Pensamos que queda suficientemente claro la importancia de leerse la documentación de todo aquello que vayamos a necesitar para completar nuestro proyecto, y que cuando nos dicen: “lee, lee, y lee“, no lo dicen por gusto ni por dar una cómoda respuesta aleccionadora, sino que, realmente, hay que leer documentación si uno quiere tener algunas posibilidades de llevar a buen puerto cualquier cosa que intente hacer con estas, y otras muchas, herramientas, y nadie hará el trabajo duro por nosotros. Nos darán más o menos pistas o podrán ser más o menos generosos, pero el aprendizaje hay que currárselo
.
Por el contrario, lo de latex2html y pdflatex se ha puesto para buscarlos pura y duramente en el directorio /usr/bin, por lo que habrá que adaptar esos trozos de código si tuviésemos que localizarlos en ubicaciones distintas.
Ya que hemos decidido añadir documentación para el usuario final, podemos situarla en un nuevo directorio llamado, por ejemplo, ayuda, así que habrá que crearle su propio Makefile.am destinado a conseguir los objetivos de generar la documentación en html y pdf y de instalarla allí donde podamos localizarla en nuestro programa cuando el usuario dé un clic en el botón “Ayuda” o lo que hayamos dispuesto al efecto. También hemos decidido que el formato por defecto de la ayuda sea html, dejando el pdf como opcional, por lo que situaremos los archivos html directamente en dirbase/ayuda, guardando los fuentes latex en dirbase/ayuda/LaTeX y el resultado en pdf en dirbase/ayuda/pdf.
Creamos los directorios a partir de dirbase:
~/dirbase$ mkdir -p ayuda/LaTeX ayuda/pdf
Creamos el Makefile.am para ayuda:
SUBDIRS = LaTeX ayudadir = $(datadir)/@PACKAGE@/ayuda ayuda_DATA = *.html *.png *.css EXTRA_DIST = ./LaTeX/*.tex ./LaTeX/imagenes/* ./pdf/*.pdf
Hemos incluido el directorio imagenes (dirbase/ayuda/LaTeX/imagenes) porque es muy normal que tengamos alguna captura de pantalla en la documentación de ayuda. El incluirlo de ésta forma hará que todo su contenido pase a formar parte del paquete, no siendo necesario crear un Makefile.am en él. (Sí, hay varias formas de hacer las cosas en GNU/Linux, ya lo hemos dicho antes).
El de ayuda/LaTeX:
# Genera la ayuda en html con latex2html y en pdf con pdflatex.
ARCH_PRAL = gfc.tex CC_HTML = latex2html CC_PDF = pdflatex OPCI_HTML = -split +2 -iso_language ES.ES -toc_stars -dir ../ OPCI_PDF = -output-directory ../pdf all: html pdf clean: rm -f ../*.html rm -f ../*.png rm -f ../*.css rm -f ../*.pl rm -f ../images.* rm -f ../WARNINGS rm -f ../pdf/* html: $(CC_HTML) $(OPCI_HTML) $(ARCH_PRAL) > /dev/null rm -f ../*.old
# Se compila dos veces para generar el índice correctamente.
pdf: $(CC_PDF) $(OPCI_PDF) $(ARCH_PRAL) > /dev/null $(CC_PDF) $(OPCI_PDF) $(ARCH_PRAL) > /dev/null
El anterior es un típico archivo Makefile, por lo que hay que tener en cuenta que cada comando ejecutable debe ir precedido de una tabulación (no de espacios en blanco), y las demás reglas de sintaxis de los archivos Makefile, las cuales pueden ser fácilmente encontradas en la Red y por tanto no nos vamos a extender con más explicaciones acerca de ellas.
Y no nos olvidemos de actualizar los cambios en el configure.ac:
SUBDIRS_VALIDOS="src imgs doc ayuda"
..//..
SUBDIRS_VALIDOS="src imgs doc ayuda directorio_progExterno"
y también:
AC_CONFIG_FILES([Makefile doc/Makefile doc/html/Makefile doc/html/search/Makefile imgs/Makefile src/Makefile ayuda/Makefile ayuda/Latex/Makefile])
El directorio_progExterno no se incluye en AC_CONFIG_FILES, puesto que ya lo está a través de AC_CONFIG_SUBDIRS.
Tampoco nos olvidamos de ejecutar:
~/dirbase$ ./autogen.sh
~/dirbase$ ./configure
~/dirbase$ make
Y por último make install en el caso de que queramos instalar la ayuda para localizar los archivos desde nuestros fuentes y make dist-bzip2 si queremos comprobar que todo lo que tiene que funcionar funciona.
Para terminar, veamos cómo quedarían nuestros archivos configure.ac y Makefile.am una vez aplicadas las modificaciones propuestas en esta segunda parte (pero ya sin comentarios):
El configure.ac
AC_INIT(miprograma, 0.1.9) AC_CONFIG_AUX_DIR(config) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADERS([config.h]) AC_ARG_ENABLE(progExterno, [--disable-progExterno no incluye progExterno]) AC_PROG_CC AM_INIT_AUTOMAKE AM_SANITY_CHECK PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES(dependPKG, [gtk+-3.0 libxml-2.0]) AC_CHECK_HEADERS(postgresql/libpq-fe.h, [dependPKG_CFLAGS+="-I$(pg_config --includedir) "], [AC_MSG_ERROR([No se encuentra libpq-fe.h ¿libpq-dev no instalado?])], []) AC_CHECK_HEADERS(cups/cups.h, [dependPKG_CFLAGS+="$(cups-config --cflags) "], [AC_MSG_ERROR([No se encuentra cups.h ¿libcups2-dev no instalado?])], []) AC_CHECK_LIB(pq, main, [dependPKG_LIBS+="-L$(pg_config --libdir) -lpq "], [AC_MSG_ERROR([No se encuentra libpq ¿libpq no instalado?])], []) AC_CHECK_LIB(cups, main, [dependPKG_LIBS+="$(cups-config --libs) "], [AC_MSG_ERROR([No se encuentra libcups ¿libcups2 no instalado?])], []) AC_SUBST(dependPKG_CFLAGS) AC_SUBST(dependPKG_LIBS) printf "checking for latex2html... " if test -e /usr/bin/latex2html; then printf "yes\n" else printf "AVISO: No hallado /usr/bin/latex2html\n" printf "La documentación de ayuda no podrá compilarse ni instalarse.\n" fi printf "checking for pdflatex (pdftex)... " if test -e /usr/bin/pdflatex || test -e /usr/bin/pdftex; then printf "yes\n" else printf "no\n" printf "configure: AVISO: No hallado /usr/bin/pdflatex o /usr/bin/pdftex\n" printf " ¿texlive-latex-base no instalado?\n\n" printf " La ayuda no podrá compilarse en formato pdf.\n" printf " Esto no tiene importancia.\n" printf " Puede ignorar los errores de Make a este respecto.\n\n" fi AC_CHECK_HEADERS([stdlib.h string.h]) AC_FUNC_MALLOC AC_CHECK_FUNCS([floor modf pow]) if test "x$enable_progExterno" = "xno"; then SUBDIRS_VALIDOS="src imgs doc ayuda" else SUBDIRS_VALIDOS="src imgs doc ayuda directorio_progExterno" AC_CONFIG_SUBDIRS(directorio_progExterno) fi AC_SUBST(SUBDIRS_VALIDOS) AC_CONFIG_FILES([Makefile doc/Makefile doc/html/Makefile doc/html/search/Makefile imgs/Makefile src/Makefile ayuda/Makefile ayuda/LaTeX/Makefile]) AC_OUTPUT
El Makefile.am de dirbase:
SUBDIRS = @SUBDIRS_VALIDOS@ EXTRA_DIST = \ Changelog \ doc/html/* \ doc/html/search/* \ autogen.sh
A lo que añadimos los nuevos de dirbase/ayuda y dirbase/ayuda/LaTeX y el resto se quedan igual que en la primera parte.