Vous êtes sur la page 1sur 11

o Paulo

Universidade de Sa
ncias Matema
ticas e de Computac
o
Instituto de Cie
a

Uma breve introducao a` criacao de


bibliotecas e makefiles em C/C++

Moacir Pereira Ponti Jr.

2011

Arquivos de cabecalho e bibliotecas

muito interessante criar bibliotecas cujas funcoes (classes e metodos, no caso de linE
guagens orientadas a objetos) possam ser reutilizadas em diversos programas. Voce pode
inclusive distribuir essas bibliotecas, com a opcao de esconder o codigo fonte utilizado
nas funcoes. Geralmente ao programar em C/C++, ja utilizamos muitas bibliotecas com
funcoes prontas como quando inclumos <stdio.h> ou <iostream>.
Para criarmos nossa propria biblioteca e preciso ter arquivos de cabecalho (com extensao .h) e arquivos de biblioteca (com extensao dependente do compilador e sistema
operacional, no gcc a extensao geralmente utilizada e .a).
Vamos mostrar um exemplo em C de biblioteca contendo uma u
nica funcao, que receba
um valor inteiro e retorne seu fatorial. O arquivo cabecalho sera funcoes.h, e o arquivo
com o codigo fonte da funcao sera nomeado funcoes.c e tem o seguinte conte
udo:
#include "funcoes.h"
int fatorial(int x) {
int a, fatx=1;
for (a=x; a>1; a--) {
fatx = fatx*a;
}
return fatx;
}
Veja que ja inclumos funcoes.h. Isso sera necessario para o compilador reconhecer
a funcao como sendo parte da biblioteca.
O arquivo cabecalho ira conter informacoes apenas da interface, tipicamente as assinaturas (ou prototipos) das funcoes. A extensao .h vem da palavra header (cabecalho
em ingles). Nesse caso teremos o arquivo cabecalho, nomeado funcoes.h, definido da
seguinte forma:
#ifndef FUNCOES_H
#define FUNCOES_H
int fatorial(int);
#endif
As primeiras linhas (#ifndef e #define) tem a funcao de verificar se o arquivo
cabecalho ja foi includo num projeto, antes de inclu-lo novamente de forma desnecessaria.
1

Temos entao dois arquivos: funcoes.c e funcoes.h. Para utiliza-los, devemos gerar
o arquivo objeto da biblioteca:
$ gcc -c funcoes.c -o funcoes.o
Se desejar criar um arquivo .a, que possa ser distribudo, utilize o comando:
$ ar -cru libfuncoes.a funcoes.o
Os arquivos .a sao bibliotecas estaticas, que tem a vantagem de poder carregar varios
objetos. Nesse caso nao faz muita diferenca, mas o comando e bastante u
til em projetos
maiores. Se quiser saber mais sobre o ar entre em seu manual digitando $ man ar.
Agora podemos copiar todos os arquivos para um diretorio separado, por exemplo
./biblioteca.

1.1

Utilizando a biblioteca

Agora, sempre que for necessario usar funcoes definidas no arquivo funcoes.c, incluimos
o arquivo funcoes.h no programa que vamos implementar. Abaixo um exemplo de codigo
fonte, que iremos nomear programa.c, que utiliza a biblioteca funcoes:
#include <stdio.h>
#include "funcoes.h"
int main(void) {
int b;
printf("Valor para calcular o fatorial: ");
scanf("%d", b);
printf("\n O fatorial de %d = %d", b, fatorial(b));
return 0;
}
Repare que nao utilizamos os sinais de menor/maior para incluir funcoes.h, como na
biblioteca stdio.h. Eles sao usados quando o arquivo cabecalho estiver instalado num
diretorio padrao do sistema.
Agora ha duas opcoes de compilacao: uma usando o arquivo objeto e outra usando a
biblioteca estatica
Usando biblioteca est
atica: e preciso instruir o compilador com as opcoes de
includes e edicao de ligacoes (linker ) para que a biblioteca possa ser includa no programa
executavel. No gcc isso e feito utilizando:
2

$ gcc programa.c -I./biblioteca -L./biblioteca -lfuncoes -o programa


Onde os flags significam:
-I inclui diretorios onde existam cabecalhos utilizados no codigo fonte.
-L inclui diretorios onde existam bibliotecas estaticas que devem ser includas no
programa.
-lfuncoes utiliza o arquivo de biblioteca criado, libfuncoes.a
-o programa gera como sada o executavel programa
Usando o arquivo objeto: e preciso instruir o compilador com as opcoes de includes
e manualmente indicar onde esta o arquivo objeto. No gcc isso e feito utilizando:
$ gcc programa.c ./biblioteca/funcoes.o -I./biblioteca -o programa
Repare que, nesse caso, nao utilizamos os flags de diretorios de biblioteca -L nem
inclumos a biblioteca estatica -lfuncoes, pois utilizamos o arquivo objeto diretamente.

Compilac
ao e makefiles

A compilacao e ligacao de codigos fonte pode ser uma tarefa complexa quando se utiliza
referencias a diversas bibliotecas e quando temos muitos arquivos fonte (.c, .cpp, ...)
para juntar a` compilacao e gerar o programa. A forma mais simples de compilar arquivos
e obter um executavel utilizando o gcc e, por exemplo:
$ gcc programa.c biblioteca.c funcoes.c -o programa
Repare que, nesse exemplo, para obtermos o programa precisamos de tres arquivos de
codigo fonte. Muitas vezes temos tambem que adicionar o caminho para bibliotecas que
usamos no nosso programa, o que aumenta a linha de comando, como por exemplo:
$ gcc programa.c biblioteca.c funcoes.c -o programa -L/home/lib
Ainda, quando alteramos apenas um dos fontes, geralmente compilamos tudo novamente, de forma manual, para obter o programa desejado. Para minimizar esse esforco,
podemos utilizar arquivos makefile em conjunto com o utilitario make.
Makefiles sao arquivos com um formato especial que auxiliam na compilacao e ligacao
de projetos. Make e um programa especialmente criado para ler esses arquivos, que
contem as instrucoes para tudo o que deve ser feito (make).
Se voce executar:
3

$ make
esse programa ira procurar por um arquivo chamado Makefile no diretorio atual, e
ira executar utilizando as instrucoes desse arquivo. Se voce tiver mais do que um makefile
ou seu arquivo possuir um nome diferente, voce pode usar o comando:
$ make -f MeuMakefile
que especifica o arquivo que voce quer utilizar para gerar seu programa.

2.1

Arquivos Makefile

Um makefile e um arquivo texto composto basicamente de alvos (target), dependencias


(dependencies) e comandos (system commands), na forma:
target: dependencies
<TAB>system command
onde <TAB> representa uma tabulacao. No arquivo voce realmente precisa usar essa
tabulacao para o programa identificar corretamente os comandos a serem executados.
Podemos definir um arquivo da seguinte forma:
all:
<TAB>gcc programa.c biblioteca.c funcoes.c -o programa -L/home/lib
e salva-lo com o nome Makefile. Para executar:
$ make
No exemplo acima usamos o alvo padrao, chamado all. Esse alvo sera executado sem
qualquer dependencia, ou seja, nao precisamos que nenhum outro arquivo exista para
executar all. Nesse caso, a linha de comando e executada diretamente.

2.2

Depend
encias

Podemos definir m
ultiplos alvos, cada um com dependencias diferentes, para que o make
possa executar alvos a partir das dependencias, ou entao para que possamos escolher qual
alvo desejamos executar.
Suponha que tenhamos apenas dois arquivos para compilar, por exemplo, programa.c
e funcoes.c, e queremos gerar o executavel programa. Podemos definir o Makefile da
seguinte forma:

programa: funcoes.o programa.o


gcc programa.o funcoes.o -o programa
programa.o: programa.c
gcc -c programa.c
funcoes.o: funcoes.c
gcc -c funcoes.c
Repare que nao usamos mais o indicador <TAB>, mas voce devera utilizar uma tabulacao para que o arquivo funcione. Agora temos tres alvos. O utilitario make ira tentar
resolver as dependencias dentro de cada alvo disponvel, executando o comando para gerar cada arquivo necessario. Caso um dos arquivos ja exista e nao tenha sido modificado,
ele nao e recompilado.
No exemplo, inicialmente o make ira tentar executar o alvo programa. Como este
possui duas dependencias: programa.o e funcoes.o, sera preciso antes executar os respectivos alvos para satisfazer as dependencias e compilar o programa.
Supondo que no diretorio existam apenas os arquivos .c, ao executarmos make, a
sequencia de comandos seria:
gcc -c funcoes.c
gcc -c programa.c
gcc programa.o funcoes.o -o programa
Ou seja, primeiro e preciso criar o arquivo objeto funcoes.o, a seguir o arquivo
objeto programa.o, para finalmente realizar a ligacao e obter o executavel. Experimente
alterar apenas um dos arquivos e executar make para verificar que ele recompila apenas
as dependencias de arquivos que foram alterados.
Podemos definir dependencias com funcoes especficas como, por exemplo:
programa: funcoes.o programa.o
gcc programa.o funcoes.o -o programa
programa.o: programa.c
gcc -c programa.c
funcoes.o: funcoes.c
gcc -c funcoes.c
clean:
rm -rf programa *.o
5

Veja que adicionamos o alvo clean que e responsavel por apagar o binario programa,
todos os objetos , ou seja, todos os arquivos com extensao .o. Para que o make execute
apenas a limpeza, utilizamos:
$ make clean
Definir esse tipo de dependencia e u
til quando queremos rapidamente excluir arquivos
para recompilar completamente o projeto.

2.3

Vari
aveis, coment
arios e detalhes

possvel utilizar variaveis e comentarios u


E
teis quando queremos definir caminhos, opcoes
de compilacao, entre outros. Por exemplo:
# Comentario do makefile
# utilizaremos a variavel CP para definir o compilador a ser utilizado
CP=gcc
# a variavel COPT sera utilizada para adicionar opcoes a compilacao
COPT=-c -Wall
programa: funcoes.o programa.o
$(CP) programa.o funcoes.o -o programa
programa.o: programa.c
$(CP) $(COPT) programa.c
funcoes.o: funcoes.c
$(CP) $(COPT) funcoes.c
clean:
rm -rf *o programa
A variavel e criada simplesmente atribuindo um valor. E pode ser usada utilizando o
operador cifrao, na forma $(VARIAVEL). Nesse exemplo, poderamos facilmente trocar o
compilador para g++ ou alterar alguma opcao de compilacao.
Se uma linha e muito grande, voce pode inserir uma barra invertida, seguida imediatamente
por uma quebra de linha (ou seja, pressionando ENTER). Um exemplo:
CP=gcc
6

p1:
$(CP) p1.o biblioteca.h biblioteca.o funcoes.h funcoes.o \
lista.h lista.o -o p1

2.4

Criando mais do que um execut


avel

Para criar mais do que um executavel podemos usar o alvo all. Por exemplo, para criar
tres executaveis p1, p2 e p3:
all: p1 p2 p3
p1: funcoes.o prog1.o
gcc prog1.o funcoes.o -o p1
p2: biblioteca.o prog2.o
gcc prog2.o biblioteca.o -o p1
p3: biblioteca.o funcoes.o prog3.o
gcc prog3.o biblioteca.o funcoes.o -o p1

Nesse caso cada executavel tem suas dependencias, que devem ser definidas, se necessario no arquivo.

2.5

Gerando um arquivo tar

Muitas vezes precisamos gerar um arquivo compactado com todos os arquivos do projeto.
Adicionando um alvo extra podemos gerar por exemplo um arquivo tar:
tar:
tar cfv programa.tar funcoes.h funcoes.c biblioteca.h \
biblioteca.c lista.h lista.c programa.c
Nesse caso, assim como no clean, nao ha dependencias, sendo o arquivo programa.tar
criado quando chamado o comando:
$ make tar

2.6

Erros comuns

Os erros geralmente estao relacionados ao uso incorreto da tabulacao (<TAB>). Infelizmente elas sao difceis de visualizar. Uma dica e entrar no editor, posicionar o cursor no
incio da linha e mover o cursor para a frente uma vez, se ele pular varios espacos, temos
uma tabulacao.
Os erros mais comuns sao:
1. Esquecer de inserir a tabulacao antes do incio dos comandos,
2. Inserir uma tabulacao no incio de uma linha em branco,
3. Nao inserir uma quebra de linha logo apos uma barra invertida (quando se utiliza
esse recurso).

2.7

Compilando um projeto com subdivis


ao em diret
orios

Muitos projetos em C/C++ utilizam diretorios diferentes para armazenar o codigo fonte,
os objetos e bibliotecas. Geralmente utilizam a estrutura:
./projeto
./include
./obj
./lib
./src
No diretorio include estao todos os cabecalhos, no diretorio obj todos os arquivos objeto,
no diretorio lib as bibliotecas utilizadas e em src os fontes. O uso de make auxilia muito
nesses casos.
Suponha um projeto em C++ que ira possuir um u
nico executavel manage, e que
utilize uma biblioteca Product que contem a implementacao de uma classe de mesmo
nome. Assim, temos os seguintes arquivos e diretorios:
./projeto
./include
Product.h
./obj
./lib
./src
Product.cc
manage.cc

Queremos compilar manage.cc, gerando um executavel manage no diretorio do projeto


e ao mesmo tempo gerar uma biblioteca para Product, no diretorio lib. No final teremos:
./projeto
./include
Product.h
./obj
Product.o
./lib
libProduct.a
./src
Product.cc
manage.cc
Makefile
manage
Para isso criaremos um Makefile com as seguintes variaveis:
CC=g++
LIB=./lib
INCLUDE=./include
SRC=./src
OBJ=./obj
LIBFLAGS = -lProduct
FLAGS = -Wall
A primeira variavel define o g++ como compilador, as proximas quatro nos auxiliarao a
controlar os diretorios, e a seguir as variaveis de flag irao informar ao compilador quais
opcoes utilizar. Como iremos criar uma biblioteca para Product, inclumos a biblioteca
como se ja estivesse criada em LIBFLAGS (que devera conter todas as bibliotecas estaticas
a serem includas no projeto). Ja em FLAGS colocamos opcoes que desejamos incluir na
compilacao.
Como o objetivo e gerar um so executavel a partir de manage.cc e esse depende da
existencia de uma biblioteca, teremos:
manage: Product
$(CC) $(SRC)/manage.cc $(FLAGS) -I$(INCLUDE) -L$(LIB) $(LIBFLAGS) \
-o manage

Product:
$(CC) -c $(SRC)/Product.cc $(FLAGS) -I$(INCLUDE) -o $(OBJ)/Product.o
ar -cru $(LIB)/libProduct.a $(OBJ)/Product.o
Ao iniciar, o make ira tentar resolver manage, que depende de Product. O target
Product primeiro compila o arquivo Product.cc, gerando um objeto Product.o na pasta
obj.
A seguir, cria uma biblioteca no diretorio lib. Finalizada a dependencia, manage.cc
e compilado e um arquivo executavel e criado no diretorio do projeto.
Opcionalmente, podemos incluir uma opcao para limpar o projeto:
clean:
rm manage $(SRC)/*~ $(OBJ)/*o $(LIB)/*a
Apaga o executavel, todos os arquivos textos de backup, objetos e a biblioteca.
O arquivo Makefile final fica assim:
# compilador
CC=g++
# variaveis com diretorios
LIB=./lib
INCLUDE=./include
SRC=./src
OBJ=./obj
# opcoes de compilacao
LIBFLAGS = -lProduct
FLAGS = -Wall
manage: Product
$(CC) $(SRC)/manage.cc $(FLAGS) -I$(INCLUDE) -L$(LIB) $(LIBFLAGS) \
-o manage
Product:
$(CC) -c $(SRC)/Product.cc $(FLAGS) -I$(INCLUDE) -o $(OBJ)/Product.o
ar -cru $(LIB)/libProduct.a $(OBJ)/Product.o
clean:
rm *~ manage $(OBJ)/*o $(LIB)/*a
10

Vous aimerez peut-être aussi