make

make [opções] alvo…

Descrição

O utilitário make é utilizado para verificar quais arquivos precisam ser compilados (foram alterados desde a última compilação ou nunca foram compilados) e para emitir a ordem de compilação (e de linkedição).

Algumas opções do comando

  • -f arquivo : usa o arquivo especificado como entrada do comando.
  • -e : as variáveis de ambiente possuem precedência sobre as variáveis do arquivo makefile.

Makefile

  • Ao iniciar o comando make sem a especificação de um arquivo (opção -f), o utilitário faz a seguinte sequência de busca no diretório atual:
    1. GNUmakefile
    2. makefile
    3. Makefile

    Apenas o primeiro arquivo encontrado é executado pelo utilitário.

  • O comando make usa o conceito de alvo para definir o que deve ser executado. Entenda por alvo um conjunto de instruções. Um alvo possui a estrutura abaixo dentro de um arquivo makefile.

nome_alvo : [dependências]
comandos

onde temos a identificação do alvo, os pré-requisitos necessários para executar o alvo e os comandos associados ao alvo em questão. Um nome de alvo sempre inicia no começo da linha e é seguido por um sinal de dois-pontos (“:“). Um alvo pode ter como dependência uma lista de outros alvos ou uma lista de arquivos. Quando a dependência é uma lista de alvos, então cada alvo da lista deve ser executado antes do alvo especificado. Quando a dependência é uma lista de arquivos, então cada um dos arquivos da lista deve existir para o alvo poder ser executado.

  • Outra importante observação em relação a estrutura mostrada acima diz respeito a posição onde são escritas as linhas de ação (comandos). Em um arquivo make, os comandos devem iniciar com um caractere de tabulação. Portanto, deve-se pressionar a tecla Tab, antes de digitar os comandos do alvo.
  • Na linha do comando make pode-se definir um ou mais alvos. Caso nenhum alvo seja definido, o utilitário executa o primeiro alvo especificado dentro do arquivo.

Exemplo

Vejamos um exemplo.

1 Objetivo = Estudo do makefile
2 alvo1: alvo3
3 @echo $(Objetivo)
4 alvo2:
5 @echo este eh o alvo2 do $(Objetivo)
6 alvo3:
7 @echo alvo3 do makefile

Inicialmente é definida no makefile acima uma macro chamada Objetivo. Em seguida, são definidos três alvos: alvo1, alvo2 e alvo3. Note que apenas o alvo1 tem uma dependência: alvo3. Isto significa que antes de executar alvo1, o utilitário deverá executar alvo3. Além disso, observe que cada alvo usa o comando echo que exibe strings e conteúdo de macros (o símbolo @ antes do comando echo apenas evita que o comando também seja exibido).

No primeiro alvo temos o símbolo $ seguido pelo nome da macro entre parênteses. O símbolo $ informa ao comando @echo para substituir o nome da macro pelo seu conteúdo (os parênteses não são exigidos se o nome da macro tem apenas uma letra). No segundo alvo temos uma string seguida pela macro Objetivo. E no terceiro alvo temos apenas uma string. Abaixo mostramos possíveis linhas de comando e suas saídas para este makefile.

1) make

alvo3 do makefile
Estudo do makefile

2) make alvo1

alvo3 do makefile
Estudo do makefile

3) make alvo2

este é o alvo2 do Estudo do makefile

4) make alvo3

alvo3 do makefile

5) make alvo1 alvo2 alvo3

alvo3 do makefile
Estudo do makefile
este eh o alvo2 do Estudo do makefile
make: ‘alvo3’ is up to date.

6) make alvo3 alvo2 alvo1

alvo3 do makefile
este eh o alvo2 do Estudo do makefile
Estudo do makefile

7) make -e Objetivo=teste

alvo3 do makefile
teste

Podemos fazer algumas observações em relação ao exemplo acima:

  1. Os comandos make e make alvo1 são idênticos pois quando o alvo não é especificado na linha de comando, o utilitário sempre executa o primeiro alvo especificado no arquivo makefile.
  2. O quinto exemplo acima mostra uma mensagem de advertência do make. Este exemplo solicita a execução de alvo1, alvo2 e então alvo3. Só que alvo3 é pré-requisito de alvo1 e portanto, o utilitário executa alvo3, alvo1 e alvo2, quando chega para executar o alvo3, ele verifica que este já foi executado e exibe a mensagem de advertência.
  3. O sexto exemplo executa inicialmente alvo3 e alvo2. Quando vai executar alvo1, verifica que este requer a execução de alvo3 antes de executar alvo1. Como alvo3 já foi executado, alvo1 é executado imediatamente.
  4. O último exemplo define uma variável de ambiente também chamada Objetivo; o uso da opção -e com o comando make, informa que o conteúdo desta variável de ambiente deve sobrepor o conteúdo da macro de mesmo nome definida dentro do arquivo makefile.

Observações

Embora não exista um padrão formal para nomes de alvos, normalmente, são adotados os seguintes nomes de alvos nos arquivos makefiles:

  • all : nome do primeiro alvo e portanto, o nome padrão.
  • clean : usado para apagar arquivos-objeto (*.o) e outros arquivos temporários.
  • clobber : usado para apagar arquivos executáveis.
  • install : usado para instalar programas.
  • distclean : usado para apagar todos os arquivos que não fazem parte da distribuição original.

No exemplo mostrado acima, usamos uma macro de nome Objetivo. Esta macro foi definida pelo usuário dentro do makefile. Porém, podemos também fazer uso de macros embutidas, ou seja, de macros internamente mantidas pelo make e que portanto, podem ter o seu conteúdo alterado durante a execução e não precisam ser definidas pelo usuário. São alguns exemplos de macros embutidas:

  • $* : nomes dos arquivos dependentes sem sufixo (extensão).
  • $@ : nome do arquivo alvo com sufixo (extensão).
  • $< : nomes dos arquivos dependentes com sufixo (extensão).

O comando make baseia-se bastante no uso de sufixos de arquivo. São alguns sufixos de arquivos reconhecidos pelo make:

  • .c : arquivo-fonte em linguagem C
  • .cc : arquivo-fonte em linguagem C++
  • .h : arquivo de cabeçalho em linguagem C
  • .o : arquivo-objeto compilado
  • .s : arquivo-fonte assembler
  • .sh : arquivo shell

A partir dos sufixos de arquivo podemos definir regras de inferências nos arquivos makefiles. Estas regras definem como arquivos são gerados a partir de outros arquivos. Podemos, por exemplo, escrever

.c.o : gcc -c -o $@ $<

A regra de inferência acima define como um arquivo com sufixo .o é gerado a partir de um arquivo .c. Note que usamos duas macros embutidas: a primeira macro ($@) fornece o nome do arquivo final com sufixo .o e a segunda ($<) fornece o nome do arquivo original com sufixo .c. Para a regra de inferência mostrada acima poderíamos, por exemplo, criar o seguinte alvo

teste.o : teste.c funcoes.h

Neste caso, o comando make teste.o faz com que o utilitário make execute o seguinte comando

gcc -c -o teste.o teste.c funcoes.h

onde a opção -c define apenas compilação e a opção -o define o nome do arquivo de saída. Os arquivos teste.c funcoes.h são os arquivos de entrada. Uma observação final em relação a regras de inferência é que não se deve definir dependências (pré-requisitos) para regras de inferência. Em algumas plataformas UNIX, a regra com pré-requisitos é interpretada como alvo.

Mais exemplos

Vejamos agora um exemplo completo de um makefile. O exemplo mostrado abaixo gera o executável solv a partir dos arquivos solvers.o, option_1.o, option_2.o, option_3.o e misc.o.

1 CC=gcc
2 INCLUDEDIR=.
3 FLAGS=-g -I${INCLUDEDIR} 
4 .c.o:
5 ${CC} ${FLAGS} -c -o $@ $< 
6 OBJS = solvers.o option_1.o option_2.o option_3.o misc.o 
7 solv: objects
8 ${CC} -o $@ ${OBJS} -g -lm
9 objects: ${OBJS}
10 solvers.o: solvers.c define.h misc.c
11 option_1.o: option_1.c define.h misc.c
12 option_2.o: option_2.c define.h misc.c
13 option_3.o: option_3.c define.h misc.c
14 misc.o: misc.c define.h
15 clean: 
16 rm -fr *.o

Inicialmente, são definidas 3 macros: CC, INCLUDEDIR e FLAGS. Na linha 4 é definida uma regra de inferência para gerar os arquivos com sufixo .o a partir dos arquivos com sufixo .c. Na linha 6 é definida a macro OBJS. Nas linhas de 7 a 16 são definidos 8 alvos: solv, objects, solvers.o, option_1.o, option_2.ooption_3.omisc.o e clean. O primeiro alvo (solv) é o alvo padrão, ou seja, o alvo que será executado caso o usuário digite make. O alvo solv tem como pré-requisito o alvo objects e como linha de ação o comando gcc. O alvo objects não tem linhas de ação, apenas pré-requisitos: os alvos definidos nas linhas de 10 a 14. Note que estes alvos lista apenas as dependências, portanto será utilizada a regra de inferência definida nas linhas 4 e 5 na execução de cada alvo. Por exemplo, na geração do arquivo solvers.o o seguinte comando é executado

gcc -g -I. -c -o solvers.o solvers.c define.h misc.c

onde -g-I e -c correspodem, respectivamente, as opções de depuração, inclusão de diretório no caminho de busca e apenas compilação. O alvo objects é usado para gerar os arquivos definidos na macro OBJS. Após a conclusão do alvo objects, o comando retorna ao alvo solv que executa a seguinte linha de ação

gcc -o solv solvers.o option_1.o option_2.o option_3.o misc.o

e finaliza a execução do makefile. Caso o usuário tenha digitado make clean, o alvo clean é executado. Este alvo apaga todos os arquivos com sufixo .o do diretório. Note que também é possível utilizar o makefile para gerar apenas os arquivos .o, pois existem alvos definidos com os nomes desses arquivos. Por exemplo, o comando

make option_1.o

gera o arquivo option_1.o.

Vejamos um segundo exemplo de makefile. O exemplo mostrado abaixo gera o executável teste a partir dos arquivos teste.o, prog1.o e prog2.o.

1 CC = gcc
2 OBJS = teste.o prog1.o prog2.o
3 .c.o:
4 $(CC) -c -o $@ $<
5 all: $(OBJS)
6 $(CC) $(OBJS) -o teste
7 clean:
8 rm -f *.o core
9 clobber: clean
10 rm -f teste 

Inicialmente, são definidas 2 macros: CC e OBJS. Nas linha 3 e 4 é definida uma regra de inferência para gerar os arquivos com sufixo .o a partir dos arquivos com sufixo .c. Nas linhas de 5 a 10 são definidos 3 alvos: all, clean e clobber. O primeiro alvo (all) é o alvo executado caso o usuário digite apenas make. Este alvo tem como pré-requisito a lista dos arquivos com extensão .o. Para gerar estes arquivos o make fará uso da regra de inferência definida nas linhas 3 e 4. Por exemplo, na geração do arquivo teste.o o seguinte comando é executado:

gcc -c -o teste.o teste.c

Após a geração dos arquivos teste.o, prog1.o e prog2.o,make gera o executável teste com o seguinte comando (linha 6)

gcc teste.o prog1.o prog2.o -o teste

O alvo clean (linhas 7 e 8) é utilizado para apagar os arquivos com extensão .o e o arquivo core (gerado pelo sistema em caso de erro de execução). O alvo clobber(linhas 9 e 10) é utilizado também para apagar estes arquivos além do arquivo executável teste (note que o alvo clobber tem o alvo clean como pré-requisito).

 

Sumário      |      Topo