2009-03-12 6 views
1

Je ne sais pas grand-chose sur la création de Makefiles, mais j'ai lu le make manual et j'ai fait quelques progrès. Mon Makefile fonctionne et fait ce que je veux.Comment pourrais-je améliorer ce Makefile?

Ma situation implique généralement entre 1 et 3 programmes différents doivent être compilés et envoyés à mon TA pour le marquage et tels via un formulaire Web. La structure de chaque application est 'prog.c', 'prog_lib.h' et 'prog_lib.c'. Dans le passé, j'ai créé des répertoires séparés pour chaque programme et créer des Makefiles séparés pour chaque répertoire pour construire le programme contenu dans. Je goudron alors chaque dossier et les soumets séparément. Récemment, les TA ont demandé que tous les fichiers sources soient dans un répertoire et un fichier Makefile avec les différentes cibles à construire pour que leurs applications de marquage puissent fonctionner sans aucune intervention humaine. Je me demandais comment quelqu'un de plus expérimenté améliorerait ce Makefile et comment ma situation en général est généralement résolue? Je voudrais réduire la quantité de frappe que je dois faire quand je passe à ma prochaine tâche et que je dois mettre à jour plusieurs endroits.

Voici mon Makefile:

ASSIGNMENT = 3 
TARNAME = Assignment$(ASSIGNMENT).tar.bz2 

CC = gcc 
CFLAGS = -O2 -Wall -ansi -pedantic -W # I like warnings 
LDFLAGS = -lm 
DEBUG = -g # to resolve symbols in GDB and valgrind 

FREQ_OUT = frequency_table 
FREQ_SOURCES = frequency_table.c frequency_table_lib.c 
FREQ_OBJECTS = frequency_table.o frequency_table_lib.o 

DECODE_OUT = decode 
DECODE_SOURCES = decode.c decode_lib.c 
DECODE_OBJECTS = decode.o decode_lib.o 

SOURCES = $(FREQ_SOURCES) $(DECODE_SOURCES) 
OBJECTS = $(FREQ_OBJECTS) $(DECODE_OBJECTS) 
OUT = $(FREQ_OUT) $(DECODE_OUT) 

.PHONY: info 
info: 
    @echo -e "make info\n" \ 
      "\tmake all \t\t\tMake all targets\n" \ 
      "\tmake frequency_table \t\tMakes frequency table\n" \ 
      "\tmake decode \t\t\tMakes decode\n" \ 
      "\tmake dist \t\t\tMakes tar archive of sources and Makefile\n" \ 
      "\tmake clean \t\t\tRemoves all the object files and executables\n" \ 
      "\tmake distclean \t\t\tPerforms clean and removes tar archive" 

.PHONY: all 
all: $(OUT) 

$(FREQ_OUT): $(FREQ_OBJECTS) 
    $(CC) $(CFLAGS) $(DEBUG) $(LDFLAGS) $(FREQ_OBJECTS) -o [email protected] 

$(DECODE_OUT): $(DECODE_OBJECTS) 
    $(CC) $(CFLAGS) $(DEBUG) $(LDFLAGS) $(DECODE_OBJECTS) -o [email protected] 

.o: 
    $(CC) -c $(CFLAGS) -o [email protected] $< 

.PHONY: dist 
dist: $(SOURCES) 
    @echo "Creating tar archive. See $(TARNAME)" 
    tar cvjf $(TARNAME) $(SOURCES) $(wildcard *_lib.h) Makefile 

.PHONY: clean 
clean: 
    rm -f $(OUT) $(OBJECTS) 

.PHONY: distclean 
distclean: clean 
    rm -f $(TARNAME) 
+0

ne ressemble pas à "Je ne sais pas grand chose sur la création de Makefiles" à partir du code :) – Lazer

Répondre

3

Vous n'avez vraiment pas besoin des lignes $(CC) $(CFLAGS) $(DEBUG) $(LDFLAGS) $(FREQ_OBJECTS) -o [email protected]. make sait déjà comment construire des binaires.

Si vos noms sont constants pour les différents binaires (binary.c et binary_lib.c), vous pouvez également créer une règle générale pour que:

FOO := $(shell ls *_lib.c) 
BIN = $(FOO:%_lib.c=%) 

$(BIN) : % : %.o %_lib.o 

EDIT: Voilà comment cela fonctionne:

  1. FOO est la liste de tous les fichiers se terminant par _lib.c
  2. BIN est la même liste, avec les suffixes « de _lib.c » enlevé, il est donc la liste de vos binaires
  3. La dernière ligne est votre règle de création. La règle stipule chaque foo $ (BIN) dépend de foo.o et foo_lib.o
+0

Cela fonctionne très bien, mais je ne sais pas exactement pourquoi cela fonctionne. Pourriez-vous préciser un peu? Plus précisément, la ligne $ (BIN). À quoi sert le seul%? –

+0

Merci pour l'explication. J'utilise cette méthode et ça marche très bien. –

+2

l'analyse de la sortie de 'ls' est une très mauvaise pratique, dans les scripts shell et Makefiles. 'FOO: = $ (wildcard * _lib.c)' est beaucoup mieux. – MestreLion

2

Dans mon expérience, vous ne devriez pas avoir la cible .o là-dedans, c'est implicite.

En outre, la version implicite inclut normalement la valeur de $(CPPFLAGS), qui est l'endroit où vous devez mettre tout -Ichemin ou -Dmacro options que vous pourriez avoir besoin.

J'inclurais également le $(DEBUG) dans $(CFLAGS), plutôt que de le lister explicitement dans l'une des cibles de construction.

1

Exemple:

# CC, CFLAGS, etc. go here 

# Object variables go here 
MYPROG_OBJECTS = main.o someother.o 

# all, info, etc. targets go here 

# first actual target: 
myprog: $(MYPROG_OBJECTS) 
    <Do the linking stuff> 

# This will take care of the objects, sources are placed in src directory: 
%.o: src/%.c 
    <The usual compilation commands> 

# Automatic dependency magic: 
%.d: src/%.c 
    $(CC) -MM [email protected] $< 

-include (MYPROG_OBJECTS:%.o=%.d) 
3

Faire le « tout » cible le premier, sauf si vous êtes vraiment sûr que votre les utilisateurs devraient taper quelque chose après 'make' pour obtenir le projet à construire. La cible 'info' est sympa mais conventionnelle.

(J'ai un fichier makefile où la cible par défaut n'est pas tout - dans un répertoire avec le code source pour plus de 100 commandes.) Je ne veux pas que 'make all' soit la valeur par défaut. deux que je veux construire.Il y a un «tout». Mais c'est très inhabituel. Normalement, la valeur par défaut doit être 'all'.)

De même, ni '$ (FREQ_OUT)' ni '$ (DECODE_OUT)' ne sont des cibles PHONY; ce sont de vrais programmes, n'est-ce pas? Les cibles 'all', 'info', 'dist', 'clean', 'realclean' etc. - ce sont des fausses. Mais les programmes que vous construisez ne le sont pas.

+0

Merci pour le conseil et correct sur les cibles .PHONY qui sont réelles. J'ai supprimé cette partie et mis à jour l'exemple. –

Questions connexes