The Joy of Autotools!

Toda a vez que eu leio Autotools eu lembro dos Autobots, não sei porque a associação. Mas esse post não é sobre isso, e talvez ele renda mais do que um post. Nas últimas semanas ajudei a migrar dois projetos que utilizavam Makefiles hand-crafted para utilizarem todo o pacote das Autotools.O primeiro passo para que a migração fosse tranquila foi ensinar os conceitos básicos das Autotools para todos os envolvidos no projeto.

Existem diversos tutoriais, livros e blogs explicando como utilizar estas ferramentas, mas a maioria exige bastante tempo para deglutir todas as informações expostas e conseguir utilizar o sistema. Para facilitar, criei uma página na wiki interna mostrando os arquivos necessários para se criar um projeto básico utilizando autotools. Este post é uma tentativa de extender este conteúdo e divulgar o uso das Autotools.

Alguns arquivos essênciais

Basicamente todo o conjunto de ferramentas necessita de dois arquivos para funcionar:

  • configure.ac: diretivas para gerar o script de configuração, testar a existência de bibliotecas, headers, comandos, etc.
  • Makefile.am: o que deve ser compilado, bibliotecas, binários, sub-diretórios, programas de teste, etc.

Com esses dois arquivos na raiz do projeto e mais alguns Makefile.am nos subdiretórios, é tudo o que se necessita para utilizar as Autotools. Obviamente isto é só o inicio da brincadeira, diversos detalhes devem ser adicionados para deixar o sistema de build de forma adequada ao projeto.

Aos arquivos!

configure.ac

AC_INIT([project_name],[version],[maintainer_email])
AC_CONFIG_SRCDIR([.])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])

AM_INIT_AUTOMAKE([foreign])
AM_MAINTAINER_MODE

AC_PROG_LIBTOOL
AC_PROG_CC

AC_CONFIG_FILES([Makefile])
AC_OUTPUT

Bom, olhando assim, esses comandos parecem uma grande sopa de letrinhas, sem sentido algum. Então, como Jack faria (piada fraca, eu sei), vamos por partes. A primeira linha inicializa o autoconf, e portanto ali estão definidos o nome do projeto, a versão, e o endereço de email do mantenedor. As próximas três linhas definem configurações gerais do autoconf, como diretório onde se encontram os arquivos de source, onde devem ser guardadas e lidas as macros utilizadas pelo autoconf, e finalmente qual é o nome do arquivo de header que o autoheader irá gerar.

Destas diretivas, duas devem ser destacadas,AC_CONFIG_SRCDIR que define qual é o diretório onde se encontram os arquivos fontes do programa, que é de escolha do programador; e a AC_CONFIG_MACRO_DIR que define o diretório onde estão as macros do autoconf, no exemplo, os arquivos são salvos no subdiretório m4. É importante que este diretório exista, pois caso contrário, gerará um erro quando rodarmos o autoreconf – mais sobre esta ferramenta no final do post.

Depois de inicializado o autoconf deve-se inicializar o automake, uma vez que optamos por utilizar todo o conjunto das Autotools. As duas diretivas iniciadas em AM são responsáveis por inicializar o automake e por optar pelo modo maintainer, que evita alguns arquivos sejam refeitos quando distribuidos para o usuário final. A diretiva AC_PROG_CC é responsável por testar a existência de um compilador C instalado no sistema. Já a AC_PROG_LIBTOOL testa e inicializa o uso da ferramenta libtool, necessária para gerar bibliotecas.

As últimas duas linhas são extremamente importantes, e deve-se tomar bastante cuidado para que todos os arquivos que se deseja gerar após rodar um ./configure estejam listados na diretiva AC_CONFIG_FILES.

Makefile.am

Com o arquivo configure.ac escrito, é hora de definir o que será gerado: um programa, uma biblioteca, ambos, arquivos extras que devem ser instalados, etc. Para isso é necessário escrever as diretivas necessárias no Makefile.am.

ACLOCAL_AMFLAGS = -I m4

bin_PROGRAMS = myproject
lib_LTLIBRARIES = libproject.la

myproject_SOURCES = \
	myproject.c \
	mymem.c 

libproject_la_SOURCES = \
	myhash.c \
	mylib.c
libproject_ladir = $(includedir)/libproject
libproject_la_HEADERS = \
	mylib.h

A primeira diretiva só adiciona a flag -I m4 quando o aclocal é chamado, indicando qual o diretório que contêm os script m4. Se o seu projeto possuiu um hierarquia de makefiles você só precisa adicionar essa diretiva no Makefile.am da raiz do projeto.

As próximas duas linhas definem o que será gerado pelo automake. A primeira linha define os executáveis a serem gerados, já a segunda define as bibliotecas geradas pelo projeto. Para programas gerados, podemos usar alguns prefixos diferentes a diretiva PROGRAMS como: bin, sbin, libexec e pkglib, isso instruirá o autotools o local onde o binário será instalado. Também há a opção de não instalar o programa usando o prefixo noinst, ou ainda check que só irá gerar o binário quando executado make check. Para bibliotecas os prefixos possíveis são lib, pkglib e noinst, seguidos da diretiva LTLIBRARIES. O que segue após o sinal de igualdade são os nomes dos binários ou bibliotecas que serão gerados.

Para cada objeto a ser gerado é necessário definir quais são os arquivos fontes necessários. Também é possível definir flags, biblotecas, etc. específicas para cada objeto, para mais informações sobre estas outras variáveis leia aqui. O mais comum é usar a diretiva SOURCES com uma lista de arquivos fonte necessários para gerar o objeto em questão. Para a biblioteca também foi definido o diretório de instalação dos headers (ladir) e o header da biblioteca – necessário para outros projetos poderem linkar com a nossa biblioteca.

Com isso temos um Makefile.am básico, hora de gerarmos os scripts de configuração.

Gerando os arquivos!

Tendo os dois arquivos no diretório do projeto (configure.ac e Makefile.am) é hora de gerar o script de configuração e o Makefile do projeto. Por muito tempo os diversos projetos que usam Autotools mantiveram scripts próprios para rodar as diversas ferramentas necessárias (automake, autoconf, autoheader, aclocal, etc) para gerar todo o sistema de build, os famosos scripts autogen.sh. Atualmente não é mais necessário utilizar estes scripts, basta chamar o programa autoreconf. Eu gosto de utilizar as flags -i, para copiar os arquivos necessários para o diretório do projeto, e -v para exibir as mensagens dos programas executados.

autoreconf -iv

Wrap up & Links

Este post talvez nunca esteja pronto, e irá evoluir aos poucos. Seguem alguns link úteis!