Jump to content

[UNIX] Makefile


Ricardo

Recommended Posts

Este tutorial tem o propósito de ajudar os programadores de C que se estão a iniciar agora a aprender a trabalhar com Makefiles. Não pretendo que isto seja nenhuma bíblia de Makefiles, é apenas para facilitar a vida a quem se depara com árduas tarefas de compilação e recompilação de programas e desconhece esta ferramenta.

Por isso não vou, até porque não tenho tempo, aprofundar muito a questão, fica apenas o essencial... depois é explorar.

Neste tutorial os exemplos que vou dar serão basicamente só em C, mas as Makefiles NÃO são uma ferramenta exclusiva desta linguagem. Vou admitir que se encontram num terminal Linux, e que existem os comando básicos (incluindo o make).

Vamos então começar:

O Make é um programa de computador que tem o intuito de automatizar a compilação de programas que usam diversos ficheiros.

As instruções que o Make executa estão todas dentro de um ficheiro chamado 'Makefile' (ou 'makefile').

Esta é uma ferramenta (na minha opinião) indispensável para qualquer programa que se faça, pois a (re)compilação é das partes mais chatas do desenvolvimento de um programa.

Um pouco de história: (Wikipedia)

Make was originally created by Dr. Stuart I. Feldman in 1977.

Dr. Feldman was working at Bell Labs at the time. Since it is old, many derived tools have appeared that work better.

Among these are BSD make, GNU make and A-A-P. In 2003 Dr. Feldman received the ACM Software System Award for the invention of this important tool.

Modo de Funcionamento:

O programa Make lê a Makefile na directoria em que se está a trabalhar (por defeito), se não for passado nenhum nome ao comando Make, este vai procurar um ficheiro chamado makefile, e caso não exista vai procurar Makefile (tudo isto na directoria em que se está a trabalhar).

É de referir que estes ficheiros podem ser 'hidden'.

O Make apenas compila/recompila ficheiros que precisam de ser (re)compilados, por exemplo, ficheiros que não foram modificados desde a última compilação não serão recompilados. O que torna o processo de (re)compilação muito mais simples e rápido, visto que muitas vezes existem programas que são formados por muitas dezenas de ficheiros.

Exemplo de uma Makefile:

foo.o: foo.c foo.h
     gcc -o foo.o foo.c

O que se passa aqui?

Bem, nesta Makefile o que se passa é o seguinte:

  • (1ª linha) Se os ficheiros 'foo.c' e 'foo.h' forem mais recentes que o ficheiro 'foo.o', então...
  • (2ª linha) o ficheiro 'foo.o' é recompilado usando o comando "gcc -o foo.o foo.c".

Neste exemplo diz-se que o ficheiro 'foo.o' depende dos ficheiros 'foo.c' e 'foo.h'.

Há pontos importantes a serem respeitados na sintaxe das Makefiles.

Na 2ª linha do exemplo o primeiro carácter é um TAB, isto é essencial. É o que precede um comando a realizar.

Na 1ª linha do exemplo está o ficheiro de saída (chamemo-lo assim) e depois ':' e aí vêm as suas dependências. Na linha seguinte o comando a realizar caso seja necessário (questão dos ficheiros terem sido ou não modificados).

[To Be Continued]

Link to comment
Share on other sites

Acho que está bom e vou também dar o meu contributo. 😄

Vou passar a explicar a minha MAKEFILE para este projecto, uma vez que assim podem testar e tentar compreender como realmente funciona:

OBJECTS = AUDIO.o UTIL.o BHEAP.o TIMER.o KEYB.o DISPLAY.o MAIN.o FRAME.o
CFLAGS = -g -Wall

main: $(OBJECTS)
ld $(OBJECTS) -Tlink.cmd -u start

clean:
-rm $(OBJECTS) main main.exe


TIMER.o : TIMER.ASM
nasm -t -f coff TIMER.ASM -o TIMER.o

tools:
nasm -f bin -o bootload.bin bootload.asm
gcc  copyboot.c	-o copyboot.exe
gcc  ldscript.c -o ldscript.exe
copyboot a:
ldscript > link.cmd

cleanall:
-rm $(OBJECTS) main *.exe *.bin link.cmd

boot:
copy embedded.bin a:

Sintaxe da Makefile

O ficheiro MAKEFILE consiste num conjunto de regras do tipo:

targets: dependencias
<TAB>Comando da construção dos targets apartir das dependencias

A Makefile controla se determinada dependência está ou não actualizada, pela "data de modificação" dos ficheiros das dependências e do target. Se as datas das dependências for posterior à data de modificação do ficheiro que constitui o target, os comandos dessa regra são executados.

No nosso exemplo: reparem nas targets main, clean, boot.

Dependencias de *.o e *.c

Todos os *.o dependem dos correspondentes *.c, quer dizer que "seja qual for a dependência" esta regra é implícita e não necessita de ser escrita.

No nosso exemplo: Por exemplo, no nosso ficheiro não é necessário escrever:

AUDIO.o: AUDIO.c
gcc -c AUDIO.c

pois já está implícito.

Variáveis em Makefiles

Para facilitar a manutenção de Makefiles, estas possuem variáveis. As variáveis são declaradas e usadas da seguinte forma:

#declaracao
nome_varivel = valor

${nome_varivel}

No nosso exemplo: reparem na variável OBJECTS.

Como executar uma Makefile

Para correr uma Makefile, escreve-se na linha de comandos assim:

make <target>

O <target> corresponde ao target na Makefile e é chamado o "Comando da construção dos targets a partir das dependências".

Se não definirem um <target> ele constrói o primeiro target que encontra.

No nosso exemplo: Se fizessem make sem argumentos, ele construía o target main.

Já fiz a minha parte, próximo! 😄

Fontes:

Utilização de Makefiles

Minha cabeça

Link to comment
Share on other sites

Mais alguma coisa sobre Makefiles...

Comentários em Makefiles

Não sei se parece esquisito comentários em Makefiles, mas podem crer que por vezes dá muito jeito.

Os comentários em Makefile são feitos precedendo o texto do comentário por um carácter #, isto em cada linha individualmente.

# texto de comentário...
# agora numa nova linha...

foo.o: foo.c foo.h
     gcc -o foo.o foo.c

Expressões condicionais

Por vezes é util a compilação condicional de alguns targets, para isso são usadas expressões condicionais... if, else

Um exemplo...

libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
      $(CC) -o foo $(objects) $(libs_for_gcc)
else
      $(CC) -o foo $(objects) $(normal_libs)
endif

Nesta Makefile é testado se o compilador é o gcc, em caso afirmativo são usadas bibliotecas específicas deste, caso contrário são usadas outras. Estas bibliotecas são indicadas através de variáveis (já falado anteriormente pelo [PT]Devilishly).

As directivas condicionais usadas são:

O ifeq que inicia a condição e especifica-a. Contém dois argumentos os quais são comparados para verificar se são iguais. Nesta directiva é possível o uso de variáveis definidas à priori. Se os dois argumentos forem iguais então são executadas as linhas a baixo desta directiva, caso contrário são ignoradas.

O else faz com que sejam executadas as linhas a seguir a este, caso a condição não se tenha verificado. Verifica-se então que esta directiva é opcional.

O endif termina a condição, todas as condições têm de terminar com o endif.

Fonte de 'inspiração' : GNU/Make

Abraços!!

PS: Obrigado [PT]Devilishly pela ajuda!! 😛

Link to comment
Share on other sites

  • 3 years later...

Junto anexo o meu antiguinho makefile.

Atenção que este makefile pode ser perigoso.

Ele descobre os ficheiros automaticamente e compila-os.

###########################################################################
# Authors:
#	Samuel Silva <silva.samuel@gmail.com>
###########################################################################
# Date: 2006/03/29
# Revision: 2008/04/09
# Description:
#    Makefile Multi-language {C,Haskell}
###########################################################################

###########################################################################
# Master Variables
EDITOR     = gvim
FILE       = Makefile
MAKEFLAGS  = -s
#################################################
# Commands Variables
CC         = gcc 
CFLAGS     =
#CFLAGS     = -Wextra -Wall -O2 -pg
GHC        = ghc
GHCFLAGS   = -Wall -O2
#################################################
# Filenames and Folders
CFiles      = $(wildcard *.c) #$(wildcard *.h)
HSFiles     = $(wildcard *.hs) $(wildcard *.lhs)
Folders     = $(wildcard */)
Files       = $(CFiles) $(HSFiles)
###########################################################################
# Static Targets [Name of targets that not are filesnames]
.PHONY: force default all compile cleanAll clean cleanTempFiles edit\
Folders CFiles HSFiles \
cleanFolders cleanCFiles cleanHSFiles 
###########################################################################
# Default target
default: all
# Main targets
all: $(FILE) compile 
compile: Folders CFiles HSFiles
Folders: $(Folders)
ifneq '$(Folders)' ''
@echo 'Creating files in $(CURDIR)'
endif
CFiles: $(basename $(filter %.c, $(CFiles))) \
$(addsuffix .o,$(basename $(CFiles)))
#	$(addsuffix .s,$(basename $(CFiles))) 
HSFiles: $(addsuffix .o,$(basename $(HSFiles))) \
 $(addsuffix .s,$(basename $(HSFiles))) \
 $(basename $(HSFiles)) 
#################################################
# Commands
# Folders
%/: %/$(FILE) force
@echo 'Creating files in $(patsubst %/,%,$(CURDIR)/$@)'
@$(MAKE) -C $@ #-f $(MYNAME)
#Object Files ***********************************
%.o: %.c $(filter %.h, $(CFiles))
@echo ' >Creating $@'
@$(CC) $(CFLAGS) -c $< -o $@
%.o: %.hs
@echo ' >Creating $@'
@$(GHC) $(GHCFLAGS) -c $< -o $@
%.o: %.lhs
@echo ' >Creating $@'
@$(GHC) $(GHCFLAGS) -c $< -o $@
#Assembly Files *********************************
%.s: %.c $(filter %.h, $(CFiles))
@echo ' >Creating $@'
@$(CC) $(CFLAGS) -S $< -o $@
%.s: %.hs
@echo ' >Creating $@'
@$(GHC) $(GHCFLAGS) -S $< -o $@
%.s: %.lhs
@echo ' >Creating $@'
@$(GHC) $(GHCFLAGS) -S $< -o $@
#Binary Files ***********************************
%: %.c $(filter %.h, $(CFiles))
$(CC) $(CFLAGS) $^ -o $@
%: %.hs
@echo ' >Creating $@'
$(GHC) $(GHCFLAGS) $^ -o $@
%: %.lhs
@echo ' >Creating $@'
$(GHC) $(GHCFLAGS) $^ -o $@
###########################################################################
# clean targets
cleanAll: clean 
clean: cleanFolders cleanTempFiles cleanCFiles cleanHSFiles
cleanFolders: $(foreach dir, $(Folders), $(dir).) 
ifneq '$(Folders)' ''
@echo 'Deleting files in $(CURDIR)'
endif
#################################################
# Clear Files
# Default Files *********************************
%/.: %/$(FILE) force
ifneq '$(Folders)' ''
@echo 'Deleting files in $(patsubst %/.,%,$(CURDIR)/$@)'
endif
@$(MAKE) -C $@ clean #-f $(MYNAME)
cleanTempFiles:
ifneq '$(addsuffix ~,$(Files))' ''
@echo ' >Deleting Temp Files'
@rm -vf $(addsuffix ~,$(Files))
endif
#Clear C Files **********************************
cleanCFiles: 
ifneq '$(CFiles)' ' '
@echo ' >Deleting C Files'
@rm -vf $(basename $(filter %.c, $(CFiles))) \
        $(addsuffix .o,$(basename $(CFiles)))\
        $(addsuffix .s,$(basename $(CFiles)))\
	$(addsuffix .h.gch,$(basename $(CFiles)))\
	a.out core.* gmon.out
endif
#Clear Haskell Files ****************************
cleanHSFiles:
ifneq '$(HSFiles)' ' '
@echo ' >Deleting Haskell Files'
@rm -vf  $(basename $(HSFiles)) \
	 $(addsuffix .o,$(basename $(HSFiles))) \
	         $(addsuffix .s,$(basename $(HSFiles))) \
	 $(addsuffix .hi,$(basename $(HSFiles)))
endif
###########################################################################
# edit targets
edit: $(Files)
@echo ' >Opening $(EDITOR) editor'
@$(EDITOR) $(Files) &
###########################################################################
#END_OF_FILE

Divirtam-se

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.