Compilando código C/C++ com Makefile

Neste post mostramos como criar um Makefile para realizar a compilação de programas C/C++.

Makefiles são muito bons pra automatizar a execução de comandos, evitando ter que digitá-los toda vez.

Também gerenciam dependências entre arquivos, recompilando apenas o que é necessário.

Instalando do Make no Ubuntu Linux

Usamos o comando abaixo para realizar a instalação do GCC (compilador para C), G++ (compilador para C++) e do Make no Ubuntu 18.04.

sudo apt install gcc g++ make

Códigos de exemplo

Primeiro vamos criar os códigos de exemplo.

Segue o código de uma função simples, que retorna o valor absoluto de um inteiro.

// biblioteca.cpp
#include "biblioteca.h"

int absoluto(int val) {
  if (val >= 0)
    return val;
  else
    return -val;
}

Segue o cabeçalho correspondente a esta função.

// biblioteca.h
int absoluto(int val);

Segue código da função main(), que retorna 1 (o valor absoluto de -1).

// main.cpp
#include <stdio.h>
#include "biblioteca.h"

int main() {
  int num = -1;
  printf("absoluto(%d) = %d\n", num, absoluto(num));
  return 0;
}

Estrutura de um Makefile

Um arquivo Makefile diz ao comando make como ele deve realizar a compilação do seu programa.

A estrutura básica de um Makefile é mostrada a seguir.

# Makefile

# Comentários começam com o símbolo jogo-da-velha
# e terminam no fim da linha

NOMEDASECAO: DEPENDENCIAS
	COMANDOS

OUTRASECAO: DEPENDENCIAS
	COMANDOS

SECAOSEMDEPENDENCIAS:
	COMANDOS

Temos comentários, obviamente servem para descrever o que  está sendo feito e desabilitar partes do código.

Cada seção é composta por seu nome (que corresponde a um arquivo principal sendo gerado pela seção), as dependências (que correspondem a outros arquivos utilizados para gerar o principal) e os comandos (que utilizam geram o arquivo principal).

Os comandos de uma seção devem ser precedidos por um caractere TAB (tabulação). Não podem ser espaços!

Funcionamento do Makefile

O nome da seção corresponde a um arquivo principal. As dependências correspondem a outros arquivos ou seções.

Se o arquivo principal não existe, então os comandos da seção são executados.

Se o arquivo principal existe, então é verificado se alguma das dependências são mais novas que o arquivo existente. Sendo alguma dependência mais nova, os comandos da seção são executados.

Se uma dependência for outra seção, o mesmo é feito para tal seção, gerando as dependências antes de executar os comandos da seção atual.

Criando o Makefile para compilação

Queremos que o Makefile gerencie a compilação dos códigos-objeto e executável; ele também deve gerenciar a limpeza (exclusão dos códigos-objeto e executável).

O código-objeto main.o (compilado a partir de main.cpp) deve ser recompilado sempre que main.cpp ou biblioteca.h forem modificados. Por isso nomeamos a seção main.o (arquivo de saída da compilação) e declaramos os arquivos main.cpp e biblioteca.h como dependências. O comando de compilação fica na linha abaixo com um caractere TAB inicial.

De forma semelhante, o código-objeto biblioteca.o (compilado a partir de biblioteca.cpp) deve ser recompilado sempre que biblioteca.cpp ou biblioteca.h forem modificados. Nomeamos a seção biblioteca.o, declaramos as dependências como biblioteca.cpp e biblioteca.h e definimos o comando de compilação.

Para gerar o executável main (linkado a partir de main.o e biblioteca.o) deve ser recompilado sempre que main.o ou biblioteca.o forem recompilados. Nomeamos a seção main, declaramos as dependências main.o e biblioteca.o e definimos o comando de likagem.

Definimos também a seção de nome clean para realizar a limpeza (excluir os códigos-objeto e do executável). A seção clean não possui dependências.

# Makefile

# Linka códigos-objeto (cria executável main)
main: main.o biblioteca.o
	g++ -o main main.o biblioteca.o

# Compila main.cpp (cria código-objeto main.o)
main.o: main.cpp biblioteca.h
	g++ -c -o main.o main.cpp

# Compila biblioteca.cpp (cria código-objeto biblioteca.o)
biblioteca.o: biblioteca.cpp biblioteca.h
	g++ -c -o biblioteca.o biblioteca.cpp

# Remove executável e códigos-objeto
clean:
	rm -f main *.o

Com esse arquivo salvo com o nome Makefile (inicial deve ser maiúlscula), basta executar o comando make.

Executando o comando make

O comando make realiza a leitura do arquivo Makefile e executa as seções presentes nas opções. Quando não fornecemos nenhuma opção ao comando make ele executará a primeira seção, correspondente a criação do executável main.

make
--
g++ -c -o main.o main.cpp
g++ -c -o biblioteca.o biblioteca.cpp
g++ -o main main.o biblioteca.

Podemos então executar o programa.

./main
--
absoluto(-1) = 1

Para limpar o executável e os códigos-objeto, basta executar o comando make com opção clean.

make clean
--
rm -f main *.o

Quero mais!

Agora que já teve o pontapé inicial, é fácil prosseguir por conta:

Veja a documentação sobre a ferramenta GNU Make.

Até mais!

Leia mais:

Veja mais posts da Categoria Programação!

Autor: Djones Boni

Engenheiro Eletricista.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *