De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
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.
Primeiramente vamos entender a estrutura básica do padrão GNU/Autotools.
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.
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. |
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. |
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). |
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); }
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.
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.
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.
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/
Sou professor universitário e publiquei originalmente este artigo em meu site
This policy contains information about your privacy. By posting, you are declaring that you understand this policy:
This policy is subject to change at any time and without notice.
These terms and conditions contain rules about posting comments. By submitting a comment, you are declaring that you agree with these rules:
Failure to comply with these rules may result in being banned from submitting further comments.
These terms and conditions are subject to change at any time and without notice.
Comentários