Aula 25: Compilação de software — make, gcc e build-essential
A compilação de software é um dos pilares da administração avançada de sistemas Linux. Ao contrário da instalação via pacotes binários — como apt, dnf ou yum —, compilar um programa a partir do código-fonte coloca você no controle total do processo: você pode habilitar ou desabilitar funcionalidades, otimizar o binário para uma arquitetura de CPU específica, aplicar patches de segurança antes de o pacote oficial ser lançado ou instalar versões que não estão disponíveis nos repositórios da sua distribuição. Nesta aula, você vai dominar o ciclo completo de compilação no Linux, do GCC ao Make, incluindo a instalação do conjunto de ferramentas build-essential e a compilação de um software real a partir do zero.
Esta é a Aula 25 do curso Linux — Do Zero ao Avançado, e o nível é avançado. Nas aulas anteriores, você aprendeu a gerenciar pacotes, manipular processos, trabalhar com permissões e editar arquivos de configuração. Agora, vamos unir esses conhecimentos para entender o que acontece nos bastidores quando um programa é transformado de código-fonte em um arquivo executável. Você verá exatamente quais ferramentas são necessárias, como instalá-las em diferentes famílias de distribuições e como usá-las de forma profissional.
O domínio da compilação de software é um diferencial crítico para profissionais de infraestrutura e segurança da informação. Em nossos projetos na JRT Technology Solutions, nossos especialistas utilizam diariamente esses procedimentos para personalizar aplicações, corrigir vulnerabilidades e otimizar o desempenho de servidores de produção. Seja para compilar um kernel, instalar um servidor web com módulos não padrão ou simplesmente testar um programa em C, o conhecimento que você adquirirá aqui é indispensável para avançar na carreira técnica.
Nesta aula, você vai aprender o papel do GCC (GNU Compiler Collection), do Make e do pacote build-essential no ecossistema GNU/Linux. Vamos instalar o ambiente de compilação em distribuições baseadas em Debian/Ubuntu e em CentOS/RHEL/Rocky Linux. Em seguida, você criará um programa em linguagem C, compilará manualmente com o GCC, automatizará o processo com um Makefile e, por fim, compilará um software real a partir de um tarball de código-fonte. Tudo será mostrado passo a passo, com comandos reais, saídas esperadas e explicações linha por linha.
Ao final desta aula, você terá autonomia para compilar qualquer software open source no Linux, diagnosticar erros de compilação, entender as mensagens do compilador e aplicar boas práticas de automação com make. Como bônus, veremos como desinstalar corretamente softwares compilados manualmente e como evitar conflitos com pacotes gerenciados pelo sistema.
O que você vai aprender nesta aula
Antes de colocar a mão na massa, vamos definir os objetivos de aprendizagem desta aula. Eles foram desenhados para que você desenvolva competência prática real, não apenas conhecimento teórico. Ao concluir todos os passos propostos, você terá executado de forma independente todos os procedimentos descritos.
- Compreender o processo de compilação de software: do código-fonte ao executável binário, passando por pré-processamento, compilação, assembly e ligação.
- Instalar o ambiente de desenvolvimento completo em sistemas Ubuntu/Debian com o pacote build-essential e em CentOS/RHEL/Rocky Linux com o grupo Development Tools.
- Utilizar o GCC para compilar programas em C e entender as principais flags de compilação, como
-Wall,-O2e-g. - Criar e configurar um Makefile completo, com variáveis, regras, dependências e alvos de limpeza.
- Compilar um software real a partir do código-fonte, utilizando a sequência clássica
./configure,makeemake install. - Verificar se o ambiente de compilação está funcionando corretamente, testar o executável gerado e validar as versões das ferramentas.
- Diagnosticar e corrigir erros comuns durante a compilação, como bibliotecas ausentes, permissões incorretas e dependências de desenvolvimento.
- Aplicar boas práticas de otimização, desinstalação segura e automação em projetos de compilação de software.
Pré-requisitos e Ambiente
Para aproveitar ao máximo esta aula, você precisa ter concluído as aulas anteriores do curso ou possuir conhecimentos equivalentes. Especificamente, é essencial que você saiba navegar pela estrutura de diretórios do Linux, editar arquivos de texto com editores como nano ou vim, gerenciar permissões com chmod e instalar pacotes usando o gerenciador da sua distribuição. Esses conceitos foram abordados em profundidade nas Aulas 8 a 15 deste curso.
O ambiente de teste recomendado é uma máquina virtual ou um servidor limpo com uma das seguintes distribuições: Ubuntu Server 22.04 ou 24.04 LTS, Debian 12, CentOS Stream 9, RHEL 9 ou Rocky Linux 9. A compilação de software é um processo que exige recursos de CPU e memória; portanto, evite usar uma instância muito pequena. Recomendamos pelo menos 2 vCPUs e 2 GB de RAM para os exemplos desta aula.
Por se tratar de uma aula com muitos comandos de instalação e compilação, é altamente recomendado executar os procedimentos em um ambiente que você possa reiniciar ou restaurar sem risco. Em nossos treinamentos na JRT Technology Solutions, sempre utilizamos snapshots ou máquinas descartáveis para prática de compilação, pois é comum que erros aconteçam durante o aprendizado. Você deve ter acesso como usuário com privilégios de sudo para instalar pacotes e gravar em diretórios do sistema.
Outro pré-requisito importante é o acesso à internet para baixar o código-fonte de exemplo. Se você estiver em um ambiente restrito, pode substituir o download pelo código-fonte de um projeto local, mas recomendamos fortemente que você siga o exemplo real para vivenciar o processo completo, incluindo a resolução de dependências. Além disso, certifique-se de que o relógio do sistema está correto, pois alguns processos de compilação e verificação de assinaturas podem falhar em caso de divergência de horário.
Por fim, prepare o terminal. Todos os comandos desta aula devem ser executados em um shell bash ou compatível. Recomendamos o uso de um terminal com pelo menos 80 colunas por 24 linhas para facilitar a leitura das saídas. Se você estiver usando SSH, verifique se a conexão está estável, pois alguns passos de compilação podem demorar vários minutos.
Compilação de software: como o código-fonte vira executável
Antes de executar qualquer comando, é fundamental entender o que acontece nos bastidores da compilação de software. Um programa escrito em linguagens como C, C++ ou Rust não pode ser executado diretamente pela CPU; ele precisa ser traduzido para linguagem de máquina. Esse processo de tradução é dividido em quatro fases principais: pré-processamento, compilação, assembly e linkagem. Cada fase é executada por ferramentas especializadas que, juntas, formam o toolchain de compilação.
No pré-processamento, o compilador processa diretivas que começam com #, como #include e #define. Os arquivos de cabeçalho (.h) são inseridos no código-fonte, macros são expandidas e comentários são removidos. O resultado é um arquivo de código-fonte puro, pronto para ser compilado. Na fase de compilação propriamente dita, o código é traduzido para assembly, uma linguagem simbólica de baixo nível que representa as instruções da CPU.
A fase de assembly converte o código assembly em código de máquina, gerando arquivos-objeto com extensão .o. Esses arquivos contêm instruções binárias, mas ainda não podem ser executados, pois podem referenciar funções e variáveis de outros módulos ou bibliotecas compartilhadas. É na fase de linkagem que o linker combina todos os arquivos-objeto, resolve referências externas e produz o executável final ou a biblioteca dinâmica. O GCC integra todas essas fases em um único comando, mas permite controlar cada etapa individualmente.
O comando make, por sua vez, não é um compilador; é uma ferramenta de automação de build. Ele lê um arquivo chamado Makefile, que contém regras, dependências e comandos para compilar um projeto de forma incremental. O make analisa os timestamps dos arquivos-fonte e objetos, compilando apenas o que foi modificado, o que acelera drasticamente o processo em projetos grandes, como o kernel Linux ou o servidor Apache. É esse trabalho em conjunto entre GCC e Make que permite a compilação de projetos complexos com milhares de arquivos.
O pacote build-essential é um meta-pacote presente nas distribuições Debian e Ubuntu que instala o conjunto mínimo de ferramentas necessárias para compilar a maioria dos softwares em C e C++. Ele inclui o gcc, o g++, o make, o libc6-dev (headers da biblioteca C) e outras ferramentas essenciais. Em distribuições da família Red Hat, o equivalente é o grupo de pacotes Development Tools, que também instala o GCC, o Make, o autotools e outras ferramentas de desenvolvimento. Entender essa correspondência é essencial para trabalhar com diferentes distribuições.
Em nossos projetos na JRT Technology Solutions, encontramos frequentemente servidores que precisam de uma versão específica de um software que não está disponível nos repositórios oficiais. Nesses casos, a compilação de software é a única saída. Ter clareza sobre as quatro fases do processo permite que nossos especialistas interpretem corretamente as mensagens de erro e tomem decisões rápidas sobre qual dependência está faltando ou qual flag de otimização deve ser ajustada.
O toolchain: GCC, Make e build-essential
Agora que você compreende a teoria, vamos detalhar cada componente do toolchain de compilação. O GCC (GNU Compiler Collection) é um compilador multi-linguagem que suporta C, C++, Objective-C, Fortran, Ada e Go. Apesar do nome, ele é parte essencial do sistema básico da maioria das distribuições Linux, embora nem sempre venha instalado por padrão em imagens mínimas. O GCC é extremamente configurável e oferece centenas de flags de compilação para controle de otimização, debugging, warnings e geração de código.
O Make é uma ferramenta de automação de build originalmente criada por Stuart Feldman em 1976. Ela permite definir regras no Makefile que especificam como derivar um arquivo a partir de outro. O make resolve automaticamente dependências entre alvos e executa os comandos necessários para atualizar o alvo final. Por exemplo, se você definir que o programa meuapp depende de main.o e utils.o, e que cada .o depende do respectivo .c, o make compilará apenas os fontes que foram alterados desde a última execução. Isso pode reduzir o tempo de build de um projeto de minutos para segundos.
O pacote build-essential é a forma mais rápida de instalar todas as ferramentas essenciais de compilação em distribuições baseadas no Debian. Ele possui dependências que garantem a presença do gcc, g++, make, dpkg-dev, libc6-dev e linux-libc-dev. Sem essas ferramentas, você não conseguirá compilar nem os programas mais simples em C. Já em distribuições baseadas em RPM, como RHEL, CentOS e Rocky Linux, o pacote equivalente é o grupo Development Tools, instalado via dnf groupinstall.
Além do GCC e do Make, a maioria dos projetos open source utiliza o sistema Autotools, composto por autoconf, automake e libtool. Essas ferramentas geram scripts configure que verificam o ambiente do sistema, detectam bibliotecas instaladas e adaptam o Makefile para a arquitetura local. Quando você executa ./configure antes de make, está usando o Autotools para preparar o build especificamente para o seu sistema. Instalaremos também essas ferramentas em ambas as famílias de distribuições.
Outro componente frequentemente necessário é o pkg-config, uma ferramenta que ajuda o compilador a encontrar bibliotecas de desenvolvimento instaladas em locais não padrão. Ele consulta arquivos .pc que contêm flags de compilação e linker para cada biblioteca. O pkg-config é indispensável para compilar softwares que usam GTK, OpenSSL, SQLite e outras bibliotecas populares. Veremos exemplos de como ele é usado durante o procedimento de compilação real.
Para complementar, o gdb (GNU Debugger) e o strace são ferramentas de debug e rastreamento que ajudam a identificar problemas em tempo de execução. Embora não sejam obrigatórios para compilar, são extremamente úteis para diagnosticar falhas em softwares compilados manualmente. Em nosso time de infraestrutura na JRT Technology Solutions, o uso combinado de GCC, Make, pkg-config e gdb resolve a maioria dos problemas de compilação e execução que encontramos no dia a dia.
Instalando o ambiente de compilação no Ubuntu e Debian
Vamos começar a prática instalando todo o ambiente de compilação em sistemas Ubuntu 22.04/24.04 LTS e Debian 12. O procedimento é simples, mas deve ser executado com atenção. Primeiro, atualize os índices de pacotes do APT para garantir que você está instalando as versões mais recentes disponíveis nos repositórios oficiais. Em seguida, instale o pacote build-essential, que trará automaticamente todas as dependências necessárias.
Além do build-essential, vamos instalar algumas ferramentas complementares que serão úteis nos exemplos posteriores desta aula: autoconf, automake, libtool, pkg-config e gdb. O pacote git também será instalado, pois muitos projetos disponibilizam o código-fonte através de repositórios Git. Por fim, instalaremos o checkinstall, que permite criar pacotes .deb a partir de compilações manuais, facilitando a desinstalação segura. Cada comando é explicado em detalhe nas linhas comentadas abaixo.
# Atualiza a lista de pacotes disponíveis nos repositórios configurados
sudo apt update
# Instala o meta-pacote build-essential (gcc, g++, make, libc6-dev, etc.)
sudo apt install -y build-essential
# Instala ferramentas do Autotools para projetos que usam ./configure
sudo apt install -y autoconf automake libtool
# Instala pkg-config para localização de bibliotecas de desenvolvimento
sudo apt install -y pkg-config
# Instala o debugger GDB para análise de falhas em tempo de execução
sudo apt install -y gdb
# Instala o checkinstall para criar pacotes .deb a partir de compilações manuais
sudo apt install -y checkinstall
# Instala o Git para clonar repositórios de código-fonte
sudo apt install -y git
Após a execução dos comandos acima, todos os pacotes serão baixados e instalados automaticamente. O APT resolverá as dependências e mostrará um resumo das operações. Em sistemas Ubuntu, o pacote build-essential normalmente ocupa cerca de 50 MB de espaço em disco após a instalação. Em Debian, o tamanho pode variar ligeiramente, mas o conjunto de ferramentas é essencialmente o mesmo. Não é necessário reiniciar o sistema; o ambiente estará pronto imediatamente.
É importante destacar que o pacote build-essential é um meta-pacote, ou seja, ele não contém arquivos próprios, mas depende de outros pacotes. Por isso, ao removê-lo com apt remove build-essential, as dependências não são removidas automaticamente; você precisaria usar apt autoremove para limpar pacotes órfãos. No entanto, recomendamos manter essas ferramentas instaladas em servidores de desenvolvimento, pois a remoção pode quebrar softwares que dependem delas.
Abaixo, apresentamos um exemplo de saída esperada do comando sudo apt install -y build-essential em um Ubuntu 24.04 recém-instalado. A saída varia conforme a versão e os pacotes já instalados, mas deve conter as informações principais de instalação:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
g++ g++-13 gcc gcc-13 libc6-dev libstdc++-13-dev make
Suggested packages:
gcc-multilib autoconf automake libtool flex bison gdb gcc-doc
Recommended packages:
libc6-dev libstdc++-13-dev
The following NEW packages will be installed:
build-essential g++ g++-13 gcc gcc-13 libc6-dev libstdc++-13-dev make
0 upgraded, 8 newly installed, 0 to remove and 3 not upgraded.
Need to get 29.8 MB of archives.
After this operation, 98.6 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y
...
Setting up build-essential (12.10ubuntu1) ...
Processing triggers for man-db (2.12.0-4build2) ...
Observe que a saída lista os pacotes adicionais que serão instalados como dependências do build-essential. O APT solicita confirmação, que foi respondida com Y automaticamente pela flag -y. No final, o sistema executa os gatilhos de atualização do banco de dados de manuais. Agora seu ambiente está pronto para compilar programas em C e C++.
Instalando o ambiente de compilação no CentOS, RHEL e Rocky Linux
Nas distribuições da família Red Hat, como CentOS Stream 9, RHEL 9 e Rocky Linux 9, o processo de instalação do ambiente de compilação é um pouco diferente, mas igualmente direto. Utilizamos o gerenciador de pacotes dnf (ou yum em versões mais antigas, como CentOS 7) para instalar o grupo Development Tools, que inclui o GCC, o Make, o Autotools e outras ferramentas essenciais.
Primeiro, atualize os repositórios do sistema. Em seguida, liste os grupos disponíveis para confirmar o nome exato do grupo de desenvolvimento. Nos sistemas mais recentes, o grupo chama-se Development Tools; em versões antigas, podia aparecer como Development tools. Após a confirmação, execute o comando dnf groupinstall "Development Tools". Esse comando instalará mais de 100 pacotes, incluindo gcc, gcc-c++, make e autoconf.
Vamos também instalar o pkg-config, o gdb e o git, que não estão necessariamente no grupo padrão. O pkg-config é frequentemente necessário para encontrar bibliotecas de desenvolvimento em projetos que usam o Autotools. O gdb permite depurar programas em caso de falhas. O git é útil para clonar repositórios de código-fonte. Por fim, instalaremos o rpm-build, que é o equivalente ao checkinstall e permite criar pacotes RPM a partir de compilações manuais.
# Atualiza os repositórios e pacotes do sistema
sudo dnf update -y
# Lista os grupos disponíveis filtrando por "Development"
sudo dnf group list | grep -i development
# Instala o grupo completo de ferramentas de desenvolvimento
sudo dnf groupinstall -y "Development Tools"
# Instala ferramentas complementares individualmente
sudo dnf install -y pkg-config gdb git rpm-build
A instalação do grupo Development Tools é mais demorada que a do build-essential no Ubuntu, pois inclui uma quantidade maior de pacotes, como o flex, bison, cmake e várias bibliotecas de desenvolvimento. Em um servidor com boa conexão, o processo pode levar de 2 a 5 minutos. Após a conclusão, o sistema estará pronto para compilar software, e você não precisará reiniciar a máquina.
Uma diferença importante em relação ao Debian é que, no RHEL e derivados, o compilador GCC e as bibliotecas de desenvolvimento são frequentemente divididos em pacotes separados. Por exemplo, o pacote gcc contém apenas o compilador para a linguagem C, enquanto gcc-c++ é necessário para compilar C++. Ao instalar o grupo Development Tools, ambos são instalados automaticamente. Isso é relevante quando você precisa compilar um software que usa apenas C e deseja economizar espaço em disco, embora o grupo completo seja recomendado para a maioria dos casos.
Em nossos projetos na JRT Technology Solutions, padronizamos a instalação do grupo Development Tools em todos os servidores de build e homologação que usam distribuições Red Hat. Essa padronização evita surpresas durante a compilação de software de terceiros e garante que todos os desenvolvedores tenham as mesmas ferramentas disponíveis. Documente sempre o procedimento de instalação no seu repositório de infraestrutura como código para facilitar a reprodução.
Abaixo, um exemplo de saída esperada ao executar sudo dnf groupinstall -y "Development Tools" no Rocky Linux 9. A listagem de pacotes foi truncada para economia de espaço, mas a saída real contém todos os pacotes do grupo:
Last metadata expiration check: 0:03:22 ago on Fri 21 Aug 2026 09:42:13 AM -03.
Dependencies resolved.
================================================================================
Package Arch Version Repository Size
================================================================================
Installing group/module packages:
autoconf noarch 2.69-38.el9 baseos 710 k
automake noarch 1.16.2-13.el9 baseos 741 k
bison x86_64 3.7.4-5.el9 appstream 1.0 M
cmake x86_64 3.20.2-8.el9 appstream 7.2 M
gcc x86_64 11.4.1-3.el9 appstream 28 M
gcc-c++ x86_64 11.4.1-3.el9 appstream 12 M
make x86_64 1:4.3-7.el9 baseos 556 k
...
Transaction Summary
================================================================================
Install 136 Packages
Total download size: 215 M
Installed size: 650 M
Downloading Packages:
...
Complete!
Note que o DNF exibe um resumo completo da transação, incluindo o número de pacotes, o tamanho do download e o espaço em disco necessário. A palavra Complete! no final indica que a instalação foi bem-sucedida. Agora, independentemente da distribuição utilizada, você tem todas as ferramentas necessárias para iniciar a compilação de software.
Compilando seu primeiro programa com GCC
Com o ambiente instalado, vamos criar um programa simples em C e compilá-lo manualmente com o GCC. Esse exercício permite visualizar cada etapa do processo de compilação e entender as flags básicas. Crie um diretório de trabalho chamado projeto1 em seu home e, dentro dele, um arquivo chamado hello.c com o conteúdo mostrado abaixo. Use o editor de sua preferência: nano, vim ou gedit.
# Cria o diretório de trabalho no home do usuário
mkdir -p ~/projeto1
# Entra no diretório criado
cd ~/projeto1
# Cria o arquivo-fonte hello.c usando o editor nano
nano hello.c
Dentro do editor, escreva o seguinte conteúdo completo. Este pequeno programa em C inclui a biblioteca padrão de entrada e saída, define a função principal main e imprime uma mensagem no terminal. É o exemplo mínimo para demonstrar a compilação de software:
/* Programa hello.c - Exemplo mínimo para compilação com GCC */
#include <stdio.h>
int main(void) {
printf("Hello, compilação de software no Linux!\n");
return 0;
}
Salve o arquivo e saia do editor. Agora, vamos compilar o programa usando o comando gcc. A sintaxe básica é: gcc [opções] [arquivo-fonte] -o [arquivo-de-saída]. A opção -o especifica o nome do executável de saída; se omitida, o GCC criará um arquivo chamado a.out. Vamos usar também as flags -Wall e -Wextra, que ativam avisos de compilação sobre possíveis erros no código, e -O2, que aplica otimizações de nível 2.
# Compila o hello.c gerando o executável "hello"
gcc -Wall -Wextra -O2 hello.c -o hello
# Lista o diretório para confirmar a criação do executável
ls -l
A execução do primeiro comando não deve exibir nenhuma mensagem, pois a compilação foi bem-sucedida e não há erros nem avisos. O segundo comando lista os arquivos do diretório, mostrando o arquivo-fonte hello.c e o novo executável hello. Note que o executável aparece com permissão de execução (-rwxr-xr-x) e, em sistemas com GNU coreutils, é exibido em cor verde para diferenciá-lo de arquivos comuns.
-rwxrwxr-x 1 aluno aluno 15984 Aug 21 10:15 hello
-rw-rw-r-- 1 aluno aluno 148 Aug 21 10:12 hello.c
A saída mostra que o arquivo hello tem 15.984 bytes e permissão de execução, enquanto o hello.c tem 148 bytes. Agora, execute o programa para ver a saída. O comando ./hello informa ao shell que o executável está no diretório atual, pois o diretório atual normalmente não está no PATH por motivos de segurança.
# Executa o programa compilado
./hello
A saída esperada do programa é a mensagem que definimos no código-fonte. Se você vir essa mensagem, o processo de compilação e execução funcionou corretamente, e você acaba de compilar seu primeiro software no Linux:
Hello, compilação de software no Linux!
Vamos agora explorar as etapas intermediárias da compilação. O GCC permite gerar o código assembly, o arquivo-objeto e o executável separadamente. Isso é útil para entender o que acontece em cada fase e para diagnosticar problemas. Execute os comandos abaixo para gerar o código assembly (-S), o arquivo-objeto (-c) e, por fim, linkar o objeto para criar o executável final:
# Gera o código assembly de hello.c em hello.s
gcc -S hello.c -o hello.s
# Gera o arquivo-objeto hello.o sem linkar
gcc -c hello.c -o hello.o
# Linka o objeto hello.o e gera o executável hello
gcc hello.o -o hello
# Lista todos os arquivos gerados em cada etapa
ls -l
Após esses comandos, o diretório conterá quatro arquivos: hello.c (fonte), hello.s (assembly), hello.o (objeto) e hello (executável). O arquivo hello.s pode ser aberto com qualquer editor de texto para visualizar as instruções assembly geradas. O arquivo hello.o é binário e não deve ser editado. Experimente visualizar o assembly com cat hello.s para ver as instruções de baixo nível. Esse conhecimento será valioso quando você precisar otimizar código em projetos avançados.
Automatizando a compilação com Make e Makefile
Em projetos com múltiplos arquivos-fonte, compilar manualmente cada arquivo com o GCC é tedioso e propenso a erros. É aqui que entra o Make e o arquivo Makefile. O Makefile define regras que descrevem como gerar os arquivos de um projeto, permitindo compilação incremental e automação completa. Vamos criar um projeto com três arquivos de código C e um Makefile que automatizará todo o processo de compilação de software.
Primeiro, crie um novo diretório projeto2 e os arquivos main.c, utils.c e utils.h. O arquivo main.c contém a função principal e chama uma função definida em utils.c. O arquivo utils.h é um cabeçalho que declara a função para que o compilador saiba sua assinatura. Esse modelo de separação é padrão em projetos reais de desenvolvimento em C.
# Cria e entra no novo diretório de projeto
mkdir -p ~/projeto2
cd ~/projeto2
# Cria o arquivo main.c
nano main.c
# Cria o arquivo utils.c
nano utils.c
# Cria o arquivo utils.h
nano utils.h
O conteúdo completo de cada arquivo é mostrado abaixo. Observe que main.c inclui o cabeçalho utils.h e chama soma() e multiplica(). O arquivo utils.c implementa essas funções, e o utils.h declara seus protótipos. Essa separação entre declaração e implementação é uma boa prática e permite compilação separada de módulos.
/* main.c - Função principal do programa */
#include <stdio.h>
#include "utils.h"
int main(void) {
int a = 10, b = 5;
printf("Soma: %d\n", soma(a, b));
printf("Multiplicação: %d\n", multiplica(a, b));
return 0;
}
/* utils.c - Implementação das funções utilitárias */
#include "utils.h"
int soma(int x, int y) {
return x + y;
}
int multiplica(int x, int y) {
return x * y;
}
/* utils.h - Cabeçalho com protótipos das funções */
#ifndef UTILS_H
#define UTILS_H
int soma(int x, int y);
int multiplica(int x, int y);
#endif
Agora, crie o arquivo Makefile. Este arquivo conterá as regras de compilação, dependências e comandos. Vamos definir variáveis para o compilador, as flags e os arquivos do projeto. Em seguida, definimos alvos: all (padrão), projeto2 (executável final), main.o, utils.o e clean. Cada regra possui uma linha de dependências e uma ou mais linhas de comando, que devem começar com um tab, não com espaços. Esse detalhe é a causa mais comum de erros em Makefiles.
# Makefile - Automação da compilação do projeto2
# Variáveis de configuração
CC = gcc
CFLAGS = -Wall -Wextra -O2
TARGET = projeto2
OBJS = main.o utils.o
# Alvo padrão: compila o projeto completo
all: $(TARGET)
# Regra para gerar o executável final a partir dos objetos
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
# Regra para compilar main.c em main.o
main.o: main.c utils.h
$(CC) $(CFLAGS) -c main.c -o main.o
# Regra para compilar utils.c em utils.o
utils.o: utils.c utils.h
$(CC) $(CFLAGS) -c utils.c -o utils.o
# Alvo de limpeza: remove arquivos gerados
clean:
rm -f $(OBJS) $(TARGET)
# Declara esses alvos como phony para evitar conflito com arquivos de mesmo nome
.PHONY: all clean
Vamos analisar este Makefile linha por linha. As variáveis CC, CFLAGS, TARGET e OBJS centralizam as configurações, facilitando manutenção. O alvo all é o primeiro e, portanto, o padrão quando você executa make sem argumentos. A regra $(TARGET): $(OBJS) informa que o executável depende dos arquivos-objeto; se qualquer .o for mais novo que o executável, o make executará o comando para linkar. As regras para main.o e utils.o têm dependência também no cabeçalho .h, garantindo recompilação quando o cabeçalho for modificado.
O alvo clean remove os arquivos gerados, permitindo reconstrução limpa. A diretiva .PHONY declara que all e clean não são arquivos reais, evitando conflito caso exista um arquivo chamado clean no diretório. Agora, execute o make para compilar o projeto. O make lerá o Makefile, verificará as dependências e executará os comandos necessários na ordem correta.
# Executa o make para compilar o projeto usando o Makefile
make
A saída esperada do comando make mostra as etapas de compilação. O make exibe cada comando à medida que o executa, permitindo ver exatamente o que está acontecendo. Primeiro, compila main.c para main.o; depois, compila utils.c para utils.o; por fim, linka os objetos e gera o executável projeto2:
gcc -Wall -Wextra -O2 -c main.c -o main.o
gcc -Wall -Wextra -O2 -c utils.c -o utils.o
gcc -Wall -Wextra -O2 -o projeto2 main.o utils.o
Agora, execute o programa compilado para verificar seu funcionamento. O comando ./projeto2 executará o binário e exibirá o resultado das operações de soma e multiplicação. A saída esperada é apresentada abaixo:
# Executa o programa compilado pelo make
./projeto2
Soma: 15
Multiplicação: 50
Um dos maiores benefícios do make é a compilação incremental. Se você executar make novamente sem modificar nenhum arquivo, o make detectará que o executável está atualizado e não executará nenhum comando, exibindo uma mensagem informativa. Isso economiza tempo em projetos grandes. Modifique o arquivo utils.c e execute make novamente; apenas utils.o será recompilado e o executável relinkado. Experimente também make clean para remover os arquivos gerados e depois make para reconstruir tudo.
Compilando software real a partir do código-fonte
Agora que você domina os conceitos básicos de GCC e Make, vamos aplicar esse conhecimento na compilação de software real. Utilizaremos o htop, um monitor de processos interativo amplamente utilizado por administradores de sistemas, como exemplo prático. O htop é ideal porque seu código-fonte é relativamente pequeno, a compilação é rápida e as dependências são mínimas, tornando-o perfeito para um ambiente de aprendizado.
Baixe o tarball contendo o código-fonte do htop a partir do GitHub. O comando wget fará o download do arquivo .tar.gz. Em seguida, descompacte o tarball com tar -xzf, que extrai o conteúdo para um diretório. Após a extração, entre no diretório do código-fonte e liste os arquivos para ver a estrutura do projeto, incluindo o script configure e o Makefile.in.
# Baixa o código-fonte do htop versão 3.3.0 do GitHub
wget https://github.com/htop-dev/htop/releases/download/3.3.0/htop-3.3.0.tar.gz
# Extrai o tarball para o diretório atual
tar -xzf htop-3.3.0.tar.gz
# Entra no diretório do código-fonte extraído
cd htop-3.3.0
# Lista os arquivos do projeto para ver a estrutura
ls -l
A saída do ls -l mostrará os arquivos do projeto, incluindo configure, Makefile.in, README, htop.c e vários outros arquivos de código-fonte. O script configure é o responsável por preparar o build para o seu sistema. Antes de
Quer aprender na prática com especialistas?
A JRT Technology Solutions oferece treinamentos e implementação de Linux para equipes corporativas.