você está aqui: Home  → Arquivo de Mensagens Workshop Gratuito sobre Programação Shell Linux

Utilizando Gnu/Autotools em Projetos

Colaboração: Alcione Ferreira

Data de Publicação: 26 de outubro de 2015

Na elaboração de projetos de software utilizamos várias formas de padronizar a compilação e instalação.

Neste artigo vou explicar como montar facilmente um autoconfigurador para sistemas GNU/Linux, onde podemos fazer a checagem das dependências necessárias para compilar nosso projeto.

Conteúdo

  • Estrutura_Padrão
  • Arquivo:_configure.ac
  • Arquivo:_Makefile.am
  • Arquivo:_src/Makefile.am
  • Arquivo:_src/main.cpp
  • Gerando_script_'configure'
  • Executando_'./configure'
  • Compilando_com_o_'make'
  • Testando_execução_'./src/Exemplo'
  • Referências

Estrutura_Padrão

Primeiramente vamos entender a estrutura básica do padrão GNU/Autotools.

  • raiz
    • src
      • # Makefile.am
    • AUTHORS
    • ChangeLog
    • COPYING
    • configure.ac
    • INSTALL
    • Makefile.am
    • NEWS
    • README
    • CREDITS

Os arquivos AUTHORS, ChangeLog, NEWS e README devem ser criados mesmo que vazios, ou com as seguintes informações:

AUTHORS Nomes dos autores do projeto.
ChangeLog Log de alteração do projeto, desde o seu início.
NEWS Novidades de uma versão para outro.
README Informações necessárias para a utilização do sistema ou instalação.

Os arquivos COPYING e INSTALL são gerados na primeira execução da geração do configurador, e contêm as seguintes informações:

COPYING Licença de utilização, no caso é uma cópia da GPL v3.
INSTALL Instruções de configuração, compilação e instalação segundo o padrão GNU.

Os arquivos Makefile.am e configure.ac são os necessários para gerar o arquivo script configure que irá checar as dependências e gerar automaticamente o Makefile com o padrão de compilação e instalação do seu projeto.

Arquivo:_configure.ac

Este arquivo é responsável por toda a checagem e nele é especificado o nome do projeto, versão, autor, e-mail para reportar bugs e página do desenvolvedor, além das checagem do compilador, de bibliotecas e arquivos instalados e as saídas que a configuração irá gerar.

Vamos entender sua composição através do exemplo abaixo:

  AC_INIT(Projeto, 1.0.0, sombra@alcionesytes.net , projeto , http://www.alcionesytes.net/)
  AC_PREREQ([2.50])
  AM_INIT_AUTOMAKE([gnu 1.11 -Wall])
  AC_PROG_CXX
  AC_LANG([C++]
  AC_HEADER_STDC
  AC_CHECK_HEADERS([iostream string fstream vector])
  PKG_CHECK_MODULES([GTKMM], [gtkmm-2.4 >= 2.10])
  AC_CHECK_PROGS([PG_CONFIG], [pg_config])
  if test x"$PG_CONFIG" = x; then
   AC_MSG_ERROR([$PACKAGE requires pg_config])
  fi
  AC_SUBST(PG_LIBS, [`$PG_CONFIG  libdir`])
  AC_SUBST(PG_CPPFLAGS, [`$PG_CONFIG  cppflags`])
  AC_SUBST(PG_LDFLAGS, [`$PG_CONFIG  ldflags`])
  AC_DEFINE([CONF_FILE],["conf/db.conf"],[Arquivo de Configuração])
  AC_TYPE_SIZE_T
  AC_PREFIX_DEFAULT([~/bin])
  AC_CONFIG_HEADERS([config.h])
  AC_CONFIG_FILES([Makefile src/Makefile])
  AC_OUTPUT
  AC_INIT

Define o cabeçalho do arquivo e é onde informamos o nome do projeto, versão, e- mail para reportar bugs, nome do arquivo tar e site do desenvolvedor.

AC_PREREQ Informamos a versão do autoconf utilizada.
AM_INIT_AUTOMAKE Iniciamos a checagem do automake, os parametros são:
* gnu padrão GNU/Linux de projetos
* 1.11 versão do automake necessária
* -Wall flag de compilação para o gcc ou g++
AC_PROG_CXX Checa se o compilador do C++ está instalado no sistema, para checar o compilador do C retiramos o XX.
AC_LANG Checa se determinada linguagem de programação está presente no sistema no caso do exemplo a linguagem C++.
AC_HEADER_STDC Checa se os cabeçalhos da Standard C estão presentes, esta checagem pode ser omitida se não for utilizar headers que são presentes em C e C++.
AC_CHECK_HEADERS Checa as headers necessárias para o seu programa, nesse ponto você deve listar todos as headers que deseja incluir no seu projeto.
PKG_CHECK_MODULES Esse macro é utilizado para checar a instalação de uma determinada biblioteca, que é mantida em uma lista pelo comando 'pkg-config'. Após checado ele gera variáveis internas para serem utilizadas nos 'Makefile.am' utilizando o prefixo informado, no exemplo o GTKMM, seguido dos sufixos _CPPFLAGS, _LDFLAGS.
AC_CHECK_PROGS Esse macro serve para testar a existência de um programa que será utilizado, veja que sua estrutura é formada por duas partes, a primeira você nomeia a variável local que irá armazear o caminho do programa, e a segunda é o nome do programa que irá ser checado no PATH padrão do seu sistema. Usando o 'if', observe que essa estrutura condicional está utilizando o padrão BASH, você pode testar se obteve sucesso na checagem da existência do programa testado, e enviar uma mensagem de erro caso o mesmo não foi encontrado.
AC_SUBST Esse macro serve para você declarar variáveis que podem ser utilizadas nos Makefile.am para auxiliar na compilação do programa.
AC_DEFINE Utilizamos esse macro para definir 'macros' da linguagem C/C++ para checagem do preprocessador.
AC_TYPE_SIZE_T Se estiver utilizando a biblioteca time.h é recomendado utilizar esse macro para checar a declaração do size_t na linguagem.
AC_PREFIX_DEFAULT Aqui nos definimos o diretório onde será instalado o programa após compilado, ou seja, quando executarmos o 'make install', ele irá utilizar a informação contida nessa linha para determinar onde será instalado o programa, porém se omitido podemos modificá-lo na hora do './configure' utilizando o argumento '--prefix='.
AC_CONFIG_HEADERS Esse macro é utilizado para definirmos o nome do arquivo headers que será gerado ao final de toda a checagem feita, ou seja, a listagem da existências dos headers checados, dos defines criados e além disso o nome do projeto, juntamente com as informações passadas no ínicio do 'configure.ac'.
AC_CONFIG_FILES Aqui nos colocamos os arquivos que serão gerados, geralmente os caminhos dos Makefiles, utilizando as informações preprocessadas.
AC_OUTPUT Finalizamos com esse macro para dizer para o script (configure) que já pode gerar os outputs necessários que já foram definidos acima.

Arquivo:_Makefile.am

Nesse arquivo especificaremos quais serão as pastas compiladas e quais os arquivos que pertencem ao projeto.

Vejamos um exemplo básico:

  SUBDIRS = src
  
  Projetodir = $(prefix)
  Projeto_DATA = README\
     COPYING\
     AUTHORS\
     ChangeLog\
     INSTALL\
     NEWS\
     CREDITS
  EXTRA_DIST = $(Projeto_DATA)

Vamos observar alguns detalhes importantes nesse arquivo:

SUBDIRS: Essa variável vai receber a lista de todos os diretórios que tem outros Makefile.am, cada diretório é separado por um espaço, podendo existir mais de um diretório.

Os outros rótulos são formados com o nome de um macro, definido a critério, seguido de um sufixo, cada sufixo tem uma função, vejamos para que servem cada um:

dir Nesse rótulo informamos onde serão instalados os arquivos listados no rótulo _DATA, geralmente utilizamos a variável já definida $(prefix).
_DATA Nesse rótulo listamos os arquivos que serão direcionados para o diretório informado no rótulo anterior, podemos separá-los por espaço ou infomar um por linha conforme o exemplo.
EXTRA_DIST Essa variável vai receber a lista de os arquivos que serão incluídos no projeto quando formos gerar o arquivo compactado para envio, podemos observar que podemos utilizar variáveis já definidas anteriormente.

Arquivo:_src/Makefile.am

Diferente do Makefile.am da raiz o que está nesta pasta será um exemplo de montagem para a compilação, ou seja, aqui definimos qual será o nome do executável e suas dependências.

Todas as checagens e variáveis criadas no 'configure.ac' podem ser utilizadas, vejamos o exemplo.

  bin_PROGRAMS = Exemplo
  
  Exemplodir = $(includedir)
  
  Exemplo_SOURCES = main.cpp
  Exemplo_HEADERS = bibliotecas.h
  
  Exemplo_CPPFLAGS = $(CPPFLAGS) $(GTKMM_CFLAGS) $(PG_CPPFLGAS)
  Exemplo_LDFLAGS = $(LIBS) $(LDFLAGS) $(GTKMM_LIBS) -L$(PG_LIBS) -lpq $
  (PG_LDFLAGS)
  Exemplo_LDADD = $(LIBS) $(LDFLAGS) $(GTKMM_LIBS) -L$(PG_LIBS) -lpq $
  (PG_LDFLAGS)
  bin_PROGRAMS

Nesse rótulo definimos o nome do executável que será gerado, podem ser gerados mais de um executável, fazemos isso separando os nomes com espaço.

Os outros rótulos são formados com o nome do executável seguido se um sufixo, cada sufixo tem uma função, vejamos para que servem cada um:

dir Nesse rótulo informamos onde serão instalados os cabeçalhos (.h), geralmente utilizamos a variável já definida $(includedir).
_SOURCES Nesse rótulo listamos os arquivos-fonte da linguagem que serão compilados, cada arquivo deve ser separado do outro utilizando espaços.
_HEADERS Nesse rótulo listamos os arquivos cabeçalho (.h) que fazem parte do projeto, para serem incluídos no pacote e quando for instalado serão copiados para o caminho especificado no rótulo dir.
_CPPFLAGS Nesse rótulo listamos todas as variáveis checadas que tem informações para a compilação do objeto (.o), a principal é a $(CPPFLAGS), mas também devemos colocar as geradas pelo pkg-config, no nosso exemplo é a $(GTKMM_CFLGAS), as definidas pelo AC_SUBST também devem ser listadas, exemplo $(PG_CPPFLGAS).
_LDFLAGS e _LDADD Nesses dois rótulos listamos todas as variáveis checadas que tem informações para a linkagem do objeto gerando o executável, as principais são a $(LIBS) e $ (LDFLAGS), mas também, como fizemos anteriormente, colocar as geradas pelo pkg- config e as definidas pelo AC_SUBST, no nosso exemplo $(GTKMM_LIBS), $(PG_LIBS) e $(PG_LDFLAGS).

Arquivo:_src/main.cpp

Vamos utilizar para exemplificar nosso projeto um exemplo retirado do site oficial do GTKmm.org que é a API para C++ que utilizo no desenvolvimento de sistemas desktop.

  /**
   * main.cpp
   * Qua outubro 14 19:32:22 2015
   * Copyright 2015 Alcione Ferreira
   *
   */
  #include < gtkmm.h>
  #include < iostream>
  
  using namespace std;
  
  int main (int argc, char **argv)
   {
   Gtk::Main kit(&argc, &argv);
   Gtk::Window *janela;
   Gtk::Label *texto;
  
   janela = new Gtk::Window();
   janela->set_title(Glib::ustring("Olá Mundo!"));
   janela->set_size_request (200,200);
   texto = new Gtk::Label(Glib::ustring("Programa Exemplo!"));
   janela->add(*texto);
   janela->show_all();
  
   Gtk::Main::run(*janela);
  
   return (0);
   }

Gerando_script_'configure'

Agora executaremos os passos para gerar o script de configuração, na documentação eles passam uma sequência de comandos que podem ser executados, porém podemos abreviar essa sequência utilizando o seguinte comando:

  autoreconf -f -i

As opções -f e -i significam, respectivamente, forçado e instalação, que quer dizer para fazer a reconfiguração do projeto de forma a ignorar versões obsoletas e para copiar as informações auxiliares de instalação, onde será colocado o texto base da documentação GNU para projetos OpenSource.

Vejamos o resultado dessa execução:

  alcione@sombra:~/Documentos/Artigo-Gnu-Autotools$ autoreconf -f -i
  configure.ac:5: installing './install-sh'
  configure.ac:5: installing './missing'
  src/Makefile.am: installing './depcomp'

Pronto nosso projeto já tem o seu script de configuração pronto, agora iremos executá-lo para gerar o Makefile adaptado ao nosso Sistema Operacional, para posterior geração do executável, vejamos a seguir.

Executando_'./configure'

Ao executar o script de configuração o sistema será checado em todas as partes que especificamos no 'configure.ac', vamos rodá-lo para ver o resultado.

  alcione@sombra:~/Documentos/Artigo-Gnu-Autotools$ ./configure
  checking for a BSD-compatible install... /usr/bin/install -c
  checking whether build environment is sane... yes
  checking for a thread-safe mkdir -p... /bin/mkdir -p
  checking for gawk... gawk
  checking whether make sets $(MAKE)... yes
  checking whether make supports nested variables... yes
  checking for g++... g++
  checking whether the C++ compiler works... yes
  checking for C++ compiler default output file name... a.out
  checking for suffix of executables...
  checking whether we are cross compiling... no
  checking for suffix of object files... o
  checking whether we are using the GNU C++ compiler... yes
  checking whether g++ accepts -g... yes
  checking for style of include used by make... GNU
  checking dependency style of g++... gcc3
  checking how to run the C++ preprocessor... g++ -E
  checking for grep that handles long lines and -e... /bin/grep
  checking for egrep... /bin/grep -E
  checking for ANSI C header files... yes
  checking for sys/types.h... yes
  checking for sys/stat.h... yes
  checking for stdlib.h... yes
  checking for string.h... yes
  checking for memory.h... yes
  checking for strings.h... yes
  checking for inttypes.h... yes
  checking for stdint.h... yes
  checking for unistd.h... yes
  checking iostream usability... yes
  checking iostream presence... yes
  checking for iostream... yes
  checking string usability... yes
  checking string presence... yes
  checking for string... yes
  checking fstream usability... yes
  checking fstream presence... yes
  checking for fstream... yes
  checking vector usability... yes
  checking vector presence... yes
  checking for vector... yes
  checking for pkg-config... /usr/bin/pkg-config
  checking pkg-config is at least version 0.9.0... yes
  checking for GTKMM... yes
  checking for pg_config... pg_config
  checking for size_t... yes
  checking that generated files are newer than configure... done
  configure: creating ./config.status
  config.status: creating Makefile
  config.status: creating src/Makefile
  config.status: creating config.h
  config.status: executing depfiles commands

Pronto os Makefile's já estão gerados e aptos para compilar o fonte e gerar o executável, se houvesse algum problema, e não conseguisse checar alguma especificação teríamos alguma mensagem de erro, mas como não foi o caso, vamos para a próxima etapa que é a geração do nosso programa.

Compilando_com_o_'make'

Para gerar nosso programa (executável), utilizaremos o comando 'make', que irá, recursivamente, executar as instruções passadas nos Makefile.am que fizemos, e que geraram os Makefile correspondentes, vejamos agora o comportamento no projeto:

  alcione@sombra:~/Documentos/Artigo-Gnu-Autotools$ make
  make all-recursive
  make[1]: Entering directory '/home/alcione/Documentos/Artigo-Gnu-Autotools'
  Making all in src
  make[2]: Entering directory '/home/alcione/Documentos/Artigo-Gnu-Autotools/src' g++ -DHAVE_CONFIG_H -I. -I.. -pthread -I/usr/include/cairomm-1.0 -I/usr/lib/ cairomm-1.0/include -I/usr/include/gtkmm-2.4 -I/usr/lib/x86_64-linux-gnu/gtkmm-2.4/include -I/usr/include/atkmm-1.6 -I/usr/include/gtk-unix-print-2.0 -I/usr/ include/gtk-2.0 -I/usr/include/gdkmm-2.4 -I/usr/lib/x86_64-linux-gnu/gdkmm-2.4/ include -I/usr/include/giomm-2.4 -I/usr/lib/x86_64-linux-gnu/giomm-2.4/include -I/usr/include/pangomm-1.4 -I/usr/lib/x86_64-linux-gnu/pangomm-1.4/include -I/ usr/include/glibmm-2.4 -I/usr/lib/x86_64-linux-gnu/glibmm-2.4/include -I/usr/ include/sigc++-2.0 -I/usr/lib/x86_64-linux-gnu/sigc++-2.0/include -I/usr/ include/gtk-2.0 -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include -I/usr/include/gio-unix-2.0/ -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/libpng12 -I/usr/ include/gdk-pixbuf-2.0 -I/usr/include/libpng12 -I/usr/include/pango-1.0 -I/usr/ include/harfbuzz -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/ x86_64-linux-gnu/glib-2.0/include -I/usr/include/freetype2 -g -O2 -MT Exemplo -main.o -MD -MP -MF .deps/Exemplo-main.Tpo -c -o Exemplo-main.o `test -f 'main.cpp' || echo './'`main.cpp
  mv -f .deps/Exemplo-main.Tpo .deps/Exemplo-main.Po
  g++ -g -O2 -lgtkmm-2.4 -latkmm-1.6 -lgtk-x11-2.0 -lgdkmm-2.4 -lgiomm-2.4 -lpangomm-1.4 -lglibmm-2.4 -lcairomm-1.0 -lsigc-2.0 -lgtk-x11-2.0 -lgdk-x11-2.0 -lpangocairo-1.0 -latk-1.0 -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lfontconfig -lfreetype -L/usr/pgsql/lib -lpq -Wl,--as-needed -o Exemplo Exemplo-main.o -lgtkmm-2.4 -latkmm-1.6 -lgtk-x11-2.0 -lgdkmm-2.4 -lgiomm-2.4 -lpangomm-1.4 -lglibmm-2.4 -lcairomm-1.0 -lsigc-2.0 -lgtk-x11-2.0 -lgdk-x11-2.0 -lpangocairo-1.0 -latk-1.0 -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lfontconfig -lfreetype -L/usr/pgsql/lib -lpq -Wl,--as-needed
  make[2]: Leaving directory '/home/alcione/Documentos/Artigo-Gnu-Autotools/src'
  make[2]: Entering directory '/home/alcione/Documentos/Artigo-Gnu-Autotools'
  make[2]: Leaving directory '/home/alcione/Documentos/Artigo-Gnu-Autotools'
  make[1]: Leaving directory '/home/alcione/Documentos/Artigo-Gnu-Autotools'

Pronto nosso programa já está compilado, observem que a quantidade de informação que é criada é muito maior que a que a configuramos. Essa é a mágica do autotools, você especifica o que precisa e o script inclui o necessário.

Vamos ver o resultado desse trabalho.

Testando_execução_'./src/Exemplo'

Para chamar o executável que criamos, partindo da raiz do nosso projeto, digitamos ./src/Exemplo, lembrando que esse exemplo foi o que definimos no src/Makefile.am.

Pronto! Com isso encerramos esse artigo, espero que tenham aproveitado mais esse conhecimento!

Vou disponibilizar o projeto que trabalhamos gerado pela execução do comando 'make dist' na pasta raiz do projeto: projeto-1.0.0.tar.gz (Cópia local).

Até o próximo artigo e viva o conhecimento!

Alcione Ferreira <<sombra (a) alcionesytes net>>
www.alcionesytes.net / http://moodle.alcionesytes.net/

Referências:

Sou professor universitário e publiquei originalmente este artigo em meu site


Veja a relação completa dos artigos de Alcione Ferreira