.:: Menu Rápido ::.

Linux | C/C++ | Downloads | SDL | [×]

quarta-feira, 26 de setembro de 2007

Compilando com gcc e Makefile

Mesmo que você seja daqueles que não abre mão de uma IDE (Ambiente de Desenvolvimento Integrado), seria muito interessante saber o que está rolando a nível de compilador.
Uma IDE serve para agilizar um projeto, mas dependendo do nível de entendimento do programador ela pode fazer papel de mascaradora, ocultando todos os passos que são necessários para se compilar um projeto.

» Post Completo...

# gcc -o hello_world hello_world.c

Este comando irá criar um aplicativo (hello_world) compilando o código hello_world.c.
Muito simples, mas esse uso simplificado compila não mais do que um hello_world ! Pois um projeto possui mais arquivos e passos diferentes de compilação.

É recomendado, quando se tem um projeto maior, o uso de vários arquivos (*.h e *.c) para que se tenha uma maior organização.

*** Lincando OBJ'S ***
Imagine o seguinte projeto:

main.c - Possui a função main e o código de interação com o usuário.

08: #include "funcoes.h"
10: int main(void){
--: ...
16: return 0;
17: }

funcoes.h - Uma biblioteca de funções.

06: #ifndef funcoes_H
07: #define funcoes_H
09: #include <math.h>
11: float areaRetangulo(float base,float altura);
12: float areaCirculo(float raio);
14: #endif

funcoes.c - A implementação da biblioteca de funções.

06: #include "funcoes.h"
08: float areaRetangulo(float base,float altura){
09: return base*altura;
10: }
12: float areaCirculo(float raio){
13: return 2*M_PI*raio;
14: }

Para compilar este projeto nós vamos precisar de mais de um comando.
Primeiro vamos criar os obj's (*.obj ou *.o), que são um tipo de código intermediário.

# gcc -o main.o -c main.c
# gcc -o funcoes.o -c funcoes.c

E agora devemos lincar estes obj's para criar um arquivo único. Irei chamar de projeto.

# gcc main.o funcoes.o -o projeto

Os obj's também vão te servir quando você não quiser compilar todos os arquivos novamente, mas é preciso ter em mente que uma alteração em um arquivo c (que não será incluído em nenhum outro arquivo) não causa alteração em outros arquivos, mas uma alteração em um arquivo h causa, pois este será incluído em outros arquivos e assim será alterada a estrutura destes. O bom é sempre que puder, apagar todos os obj's e recompilar tudo.
Por exemplo, o que seria necessário se eu alterar o código do:
  • main.c
  • # gcc -o main.o -c main.c
    # gcc main.o funcoes.o -o projeto

  • funcoes.c
  • # gcc -o funcoes.o -c funcoes.c
    # gcc main.o funcoes.o -o projeto

  • funcoes.h
  • # gcc -o main.o -c main.c
    # gcc -o funcoes.o -c funcoes.c
    # gcc main.o funcoes.o -o projeto

    *** Makefile ***
    Makefile é apenas o nome do arquivo que o comando make utiliza para compilar o seu projeto.
    Imagine o Makefile como um arquivo shell script com a vantagem extra de resolver problemas de dependência de arquivos.
    Para compilar nosso projeto nós vamos escrever o Makefile da seguinte maneira:

    05: OBJS=main.o funcoes.o
    07: all: projeto
    09: projeto: $(OBJS)
    10: gcc $(OBJS) -o $@
    12: main.o: main.c
    13: gcc -c main.c -o main.o
    15: funcoes.o: funcoes.c
    16: gcc -c $< -o $@

    Foi criada a variável OBJS na linha 05 para facilitar seu uso nas linhas 09 e 10.
    Pode parecer estranho, mas a linhas 13 e 16 fazem praticamente a mesma coisa.

    Para compilar o arquivo funcoes.c basta usar o comando:

    # make funcoes.o

    1. O make irá ler o arquivo Makefile e identificar que funcoes.o tem como dependência funcoes.c.
    2. Caso funcoes.c seja mais recente que funcoes.o ele irá executar o código logo abaixo (O TAB no começo da linha é obrigatório).
    Para compilar o projeto você pode usar um dos 3 comandos:

    # make projeto
    # make all
    # make

    *** Diretivas de Compilação ***
    Não achou estranho eu usar #ifndef, #define e #endif no arquivo funcoes.h ?!
    Estes comandos são chamados diretivas de compilação e são interpretadas pelo pré-processador. É o código antes do código.
    Com #define podemos incluir um arquivo em outro, e com #ifndef e #endif criamos um bloco condicional.
    Entendendo melhor, não podemos declarar o escopo de uma função mais de uma vez, e quando incluímos um arquivo *.h mais de uma vez é exatamente isto que ocorre.
    No nosso exemplo, a diretiva #ifndef serve para garantir que o escopo das funções só sejam incluídos em em arquivo caso a #define não exista. A #define passa a existir logo em seguida e o bloco é finalizado em #endif.

    *** Arquivos ***
  • Código fonte do projeto

  • *** LINK'S ***
  • Wiki IDE (Ambiente de Desenvolvimento Integrado)
  • Wiki GCC (GNU Compiler Collection)
  • make
  • shell script
  • 2 comentários:

    Anônimo disse...

    Excelente! Muito bem explicado! Eu ainda completaria com um 'make clean' e com e com um 'make install'

    PROGNAME=projeto
    BINDIR=/usr/local/bin

    clean:
    @echo "cleaning..."
    rm -f $(OBJS)

    install:
    @echo "installing..."
    cp $(PROGNAME) $(BINDIR)

    Luiz Sergio disse...

    Ajudou bastante! Valeu mesmo :)