O que vem a ser Make?
Make é um programa que trabalha com uma arquivo chamado
makefile ou
Makefile. Dentro deste arquivo são declaradas variáveis, dependências e regras que servirão para o propósito de compilação de vários tipos diferentes de arquivos (C, C++, Phyton, PostScript, etc). A utilização do make traz grandes vantagens para o programador, tais como:
- Evitar a digitação de comandos longos toda vez que um projeto tiver que ser recompilado;
- Evitar o esquecimento de parâmetros utilizados raramente na compilação e que possivelmente podem ser esquecidos;
- Manter o ambiente de desenvolvimento consistente;
- Automatizar o processo de construção/compilação.
Formato dos Makefiles - VariáveisPara efetuar a declaração de variáveis dentro de um makefile, simplesmente deve-se colocar o nome desejado para ela em letras maiúsculas seguido do sinal de igualdade e posteriormente o valor desejado
NOMEVAR = Valor
Assim é feita a atribuição de Valor à variável NOMEVAR. Pode-se definir, ainda, como valores de variáveis parâmetros de compilação ou mesmo o nome do compilador escolhido para ser utilizado.
PARAM = -o
CXX = g++
Isto atribui o parâmetro de compilação -o à variável PARAM e o nome do compilador g++ à variável CXX. Para acessar o conteúdo de uma variável, colocasse um cifrão($) seguido do nome da variável entre parentesis.
$(NOMEVAR)
Retornando, assim, o valor Valor. Da mesma maneira pode-se accesar o valor da variável PARAM e do compilador escolhido.
$(PARAM)
$(CXX)
Formato dos Makefiles - DependênciasDependencias são o "coração" dos makefiles. Sem eles nada será feito. O formato geral para declaração de dependências é como se segue:
dependencia: dependencia1 dependencia2 ... dependenciaN
comandos para a dependencia
É importante ressaltar que antes dos comandos para a
dependencia é necessária a colocação de uma tabulação(
tab), e não espaços, como pode parecer. A forma de leitura desse arquivo é exatamente verificar cada dependência e as dependências que ele possui. O que o make faz é verificar
dependencia e perceber que ela é dependende de
dependencia1, dependencia2 ... dependenciaN. Após isso ele vai atras da definição de cada uma dessas dependências para poder, ao final, executar os comandos do alvo1. Por exemplo:
main: programa.cc
g++ programa.cc -o programa
Este é um exemplo bem simples, onde pode-se perceber a maneira que o make atua. Ao chamar o make ele automaticamente compila as dependências de main, por que é a primeira dependencia contido no arquivo makefile. Com isso, ele checa programa.cc para verificar quando se deu a última modificação nesse arquivo e determinar se é necessário executar os comandos correspondentes (g++ programa.cc -o programa). Se o arquivo não tiver sido modificado após a última compilação, nada será executado.
Um outro exemplo, onde pode-se perceber essas dependências mais claramente é no desenvolvimento de vários arquivos, onde o principal depende de vários outros. Sendo assim, na declaração da primeira dependência coloca-se como dependentes os outros arquivos objeto que são gerados dos outros arquivos, e na dependência desses respectivos arquivos os comandos de compilação para eles. Fazendo como a seguir:
programa: main.cc subarquivo.o classe.o
g++ main.cc subarquivo.o classe.o
subarquivo.o: subarquivo.cc classe.o
g++ -c main.cc subarquivo.o classe.o
classe.o: classe.cc
g++ -c classe.cc
Obervemos as dependências aqui. Quando o make é executado, a dependência programa é a primeira a ser chamada. Então, a dependência subarquivo.o é encontrada, logo após, classe.o é encontrada dentro de subarquivo.o. Classe.cc é, então, compilada, a seguir subarquivo.o é compilado, então, finalmente podemos retornar à dependência programa. Todas as três dependências estão compiladas e, finalmente, os comandos de programa são efetuados para criar a.out.
Amarrando variáveis e dependênciasPodemos usar valores de variáveis dentro de dependências muito facilmente. No próximo exemplo, utilizamos duas variáveis, uma para definir o compilador e outra para definir as opções de compilação. Isto facilita muito a vida quando queremos, por exemplo, habilitar o depurador para todo o código ou desabilitá-lo, se assim preferirmos.
CXX = g++
FLAGS = -g
program: main.cc subarquivo.o classe.o
$(CXX) $(FLAGS) main.cc subarquivo.o classe.o
subarquivo.o: subarquivo.cc classe.o
$(CXX) $(FLAGS) -c main.cc subarquivo.o classe.o
classe.o: classe.cc
$(CXX) $(FLAGS) -c classe.cc
Surge a pergunta: como faremos para limpar isto? A resposta é simples, basta adicionar ao makefile uma nova dependencia chamada "clean", a qual se encarregará de limpar todo nosso código e recomeçar do que tinhamos antes, ou seja, somente os arquivos fonte. A dependência é feita como se segue:
CXX = g++
FLAGS = -g
program: main.cc subarquivo.o classe.o
$(CXX) $(FLAGS) main.cc subarquivo.o classe.o
subarquivo.o: subarquivo.cc classe.o
$(CXX) $(FLAGS) -c main.cc subarquivo.o classe.o
classe.o: classe.cc
$(CXX) $(FLAGS) -c classe.cc
clean:
rm -rf *.o a.out
Assim, quando digitamos “make clean” na linha de comando, ele removerá todas os arquivos .o e o executável a.out que nós temos.
ConclusõesOs Makefiles são muito muito úteis ao tratar dos projetos. A primeira coisa que você deve fazer quando fazer um projeto é críar um makefile, ele torna sua vida muito mais fácil.
Referências[1]
Criando makefiles - Gazeta do linux[2]
Makefile Getting Started