2012-10-10 14 views
5

Sono uno studente di ingegneria informatica e devo ancora un altro programma C da implementare. Fino ad ora creo sempre un nuovo Makefile dai miei progetti precedenti e questo richiede tempo per essere configurato ogni volta. C'è una tela Makefile potrei usare che è così potente che tutti avrei bisogno di fornire è il nome del file C che contengono una funzione principale?Alla ricerca del perfetto tela Makefile

La mia idea sarebbe che per ogni nuovo progetto, creo una cartella contenente quello Makefile, una cartella bin e una cartella src. Vorrei quindi modificare alcune variabili al Makefile (i file C contenenti una funzione main()) e poi make vorrei costruire automaticamente tutto, tenendo conto delle dipendenze.

fa a sapere se un tale Makefile esiste?

[modifica via Alexandre C.]: Automake/Autoconf sono overkills per questo tipo di progetti che utilizzano solo librerie standard ed eseguono su unix standard os '. Per i progetti di cui abbiamo bisogno per implementare, le dipendenze (per i file da essere collegati) può essere sempre deduce dalla ".h" include e non v'è in genere molto pochi file coinvolti.

+1

Ho creato diversi modelli di Makefile in passato, con più o meno elementi (generazione automatica delle dipendenze, ecc.). Il meglio è ancora quello di lanciare il tuo usando l'ottima documentazione di Gnu Make. Puoi anche usare cose come 'src = $ (shell find. -name" * .c ")', o '$ (grep -H" main (")'. Non è molto difficile, e ti senti come se controllassi il tuo ambiente –

+0

@AlexandreC. potresti fornire uno script o un Makefile che potrei usare per scrivere il mio? – Mat

+1

@Mat: Ce ne sono molti su internet (esempio [http://en.wikipedia.org/wiki/Media/ makefile/makefile-automatic-dependencies-with-makedepend.html)). Vi consiglio caldamente di consultare la documentazione di Gnu Make.Ci vuole meno di un pomeriggio per diventare un esperto di Make, e questa conoscenza sarà molto utile nella tua carriera. In particolare, impara a usare le regole integrate (in modo da sapere quando lasciarle). –

risposta

0

mi si avvicinò con la seguente soluzione:

# Author Matthieu Sieben (http://matthieusieben.com) 
# Version 23/10/2012 
# 
# This Makefile is licensed under the Creative Commons Attribution 
# Partage dans les Mêmes Conditions 2.0 Belgique License. 
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/2.0/be/. 
# Use at your own risk. 
# 
# Use Makefile.inc to write you own rules or to overwrite the default values defined here. 

SRCDIR = ./src 
BINDIR = ./bin 

SHELL = /bin/bash 
CC  = /usr/bin/gcc 
LIBS = 
CFLAGS = -Wall -Wextra -Werror 
LDFLAGS = 

.PHONY: default all clean distclean 
.SUFFIXES: .c .o 

# make "all" the default target 
default: all 

-include Makefile.inc 
-include Makefile.d 

CFLAGS += -I$(SRCDIR) 
ifeq ($(DEBUG), 1) 
    CFLAGS += -DDEBUG=1 -ggdb 
else 
    CFLAGS += -DDEBUG=0 -O2 
    LDFLAGS += -O2 
endif 

%.o: %.c 
    @echo " Compiling `basename $<`"; 
    @$(CC) $(CFLAGS) -o [email protected] -c $<; 

clean: 
    @echo "Cleaning compiled files"; 
    @find $(SRCDIR) -name "*.o" -exec rm {} \; 
    @[ -e Makefile.d ] && rm Makefile.d; 

distclean: clean 
    @echo "Removing executables"; 
    @find $(BINDIR) -type f -exec rm {} \; 

Makefile.d: $(shell find $(SRCDIR) -type f -name "*.c") 
    @echo "Building dependencies"; 
    @for file in `find $(SRCDIR) -name "*.c" -print`; do\ 
     $(CC) $(CFLAGS) -MM -MT $${file/%.c/.o} $$file | tr -d "\n\\" >> Makefile.d.tmp;\ 
     echo -e "\n" >> Makefile.d.tmp;\ 
    done;\ 
    for file in `find $(SRCDIR) -name "*.c" -exec grep -Hs "main(" {} \; | cut -f1 -d':'`; do\ 
     execname=`basename $$file .c`;\ 
     objs="$${file/%.c/.o}";\ 
     for header in `$(CC) $(CFLAGS) -MM $$file | tr " " "\n" | grep ".h$$" | sort | uniq | tr "\n" " "`; do\ 
      if [ -f $${header/%.h/.c} ]; then\ 
       objs+=" $${header/%.h/.o}";\ 
      fi;\ 
      for obj in `grep "^$${header/%.h/.o}" Makefile.d.tmp | tr " " "\n" | grep ".h$$" | tr "\n" " "`; do\ 
       if [ -f $${obj/%.h/.c} ]; then\ 
        objs+=" $${obj/%.h/.o}";\ 
       fi;\ 
      done;\ 
     done;\ 
     objs=`echo -n "$$objs" | tr " " "\n" | sort | uniq | tr "\n" " "`;\ 
     echo "all: $$execname" >> Makefile.d.tmp;\ 
     echo "$$execname: $(BINDIR)/$$execname" >> Makefile.d.tmp;\ 
     echo "$(BINDIR)/$$execname: $$objs" >> Makefile.d.tmp;\ 
     echo " @echo \"Linking $$execname\";" >> Makefile.d.tmp;\ 
     echo " @\$$(CC) \$$(LDFLAGS) -o \[email protected] $$objs \$$(LIBS);" >> Makefile.d.tmp;\ 
     echo >> Makefile.d.tmp;\ 
    done;\ 
    mv Makefile.d.tmp [email protected];\ 

Non è molto robusta, quindi sentitevi liberi di fornire un miglioramento. Ecco come funziona. L'idea qui è, per ogni file .c in $(SRCDIR), cercare quelli contenenti una funzione principale (grep "main(" src/*.c). Poi, per ogni zona includono #include "myfile.h" aggiungere myfile.o nella lista degli oggetti per quella eseguibile e quindi scrivere le righe sopra nella Makefile.d

Modifica Questa nuova versione dello script supporta le dipendenze per il collegamento di file bin.

+0

Beh ... questo non è ancora perfetto dato che i file non vengono caricati in modo ricorsivo. Se qualcuno ha un'idea ... – Mat

3

Il più vicino a un magico e perfetto Makefile è quello di utilizzare lo standard di fatto per i programmi portatili C e le librerie: Automake.

Come esempio sollevato da this page, questo è l'esempio di quello che si potrebbe aggiungere al Makefile.am:

bin_PROGRAMS = zardoz 
zardoz_SOURCES = main.c head.c float.c vortex9.c gun.c 
zardoz_LDADD = $(LIBOBJS) 

L'equivalente di un obiettivo marca che stiamo aggiungendo qui si chiama zardoz. Le origini sono specificate in zardoz_SOURCES e tutte le librerie aggiuntive che devono essere collegate sono specificate in zardoz_LDADD. Non è necessario specificare dove risiede la funzione main, poiché il linker lo troverà automaticamente nella fase di collegamento. Se non è presente, il collegamento fallirà semplicemente.

+1

Puoi fornire un esempio di lavoro? –

+4

@AlexBrown: Nessuno può fare un esempio operativo per automake :-( –

+1

Automake è probabilmente eccessivo per i progetti degli studenti che funzionano in ambienti stereotipati.Gli Autotools sono per garantire la portabilità (e questo ha un grande costo di installazione e una curva di apprendimento) –

0

Anche io raccomando gli autotools, imparerai ad apprezzarlo alla fine e ne vale sicuramente la pena, puoi cercare un tutorial su come usarlo o, se non ti dispiace passare attraverso un piccolo libro, ti suggerisco di leggere il autobook tuttavia, se si desidera solo un semplice modello di Makefile, io uso questo:

BIN  = <binary name> 
OBJ  = <object files *.o> 
LIB  = <libraries> 
CFLAGS = -O2 -Wall -I. 
LDFLAGS = <ld flags> 

CC = gcc 
LD = ld 
RM = rm -f 

all:: $(BIN) 

$(BIN): $(OBJ) 
    $(CC) $(LDFLAGS) $(OBJ) $(LIB) -o $(BIN) 

clean: 
    $(RM) $(BIN) *.o 

%.o: %.c %.h 
    $(CC) $(CFLAGS) -c $< 
1
 


    PROGRAM = test-program 
    CFLAGS = -c -Wall 

    OBJECTS += $(patsubst %.c, %.o, $(wildcard ./src/*.c)) 
    OBJECTS += $(patsubst %.c, %.o, $(wildcard ./src/more-sources*.c)) 

    all: $(OBJECTS) 
     gcc -o ./bin/$(PROGRAM) $(OBJECTS) 

    %.o: %.c %.h 
     gcc $(CFLAGS) $< -o [email protected] 

 

Qualcosa di simile farà il trucco. Ovviamente non l'ho ancora provato .. Puoi opzionalmente sovrascrivere la compilazione predefinita in base al file.

+0

Preferisco 'src = $ (shell find. -name" * .c ")', perché è più flessibile di '$ (carattere jolly)', ma ci sono molti modi qui. –

+0

Interessante. Funzionerebbe su Windows che esegue MinGW o simili? – Honeymustard

+0

Hai bisogno di una shell abbastanza grande e del programma di ricerca. Questo dovrebbe funzionare con cygwin, o qualunque cosa fornisca bash e i findutils. –

1

È possibile scrivere facilmente il proprio "pacchetto" di macro che esegue questa operazione.Ad esempio, creare questo file come testo standard, lo chiamano program.mk e metterlo in una posizione centrale all'interno del vostro albero:

lang.c.objs = $(patsubst %.c,%.o,$(1)) 
lang.c.link = $(CC) $(CFLAGS) $(LDFLAGS) -o $(1) $(2) 
lang.c++.objs = $(patsubst %.cpp,%.o,$(1)) 
lang.c++.link = $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(1) $(2) 

define make-program 
    program.$(1).lang ?= c 
    program.$(1).objects ?= $$(call lang.$$(program.$(1).lang).objs,$$(program.$(1).sources)) 
    $$(program.$(1).name): $$(program.$(1).objects) $$(program.$(1).extra-deps) 
     $$(call lang.$$(program.$(1).lang).link,[email protected],$$^ $$(program.$(1).ldlibs)) 
    CLEANABLE += $$(program.$(1).name) 
    ALL_PROGRAMS += $$(program.$(1).name) 
endef 

# If the user didn't specify a list of programs, build them all 
ifndef PROGRAMS 
    PROGRAMS = $(foreach p,$(filter program.%.name,$(.VARIABLES)),\ 
    $(patsubst program.%.name,%,$(p))) 
endif 

# Generate the rule to build each program 
$(foreach p,$(PROGRAMS),$(eval $(call make-program,$(p)))) 

.PHONY: all clean 
all: $(ALL_PROGRAMS) 

clean: ; rm -f $(CLEANABLE) 

.DEFAULT_GOAL := all 

Ora, in ogni directory in cui si vuole costruire un programma, makefile può essere qualcosa di simile :

program.p.name = Program 
program.p.sources = Program1.c Program2.c 

include path/to/program.mk 

Un simile library.mk può essere utilizzato per le librerie. Questo approccio è abbastanza potente e molto facile da estendere.

0

Se sei interessato agli ambienti UNIX standard, perché non limitarti semplicemente a ciò che fornisce POSIX make?

# Lean and mean. 
.POSIX: 

CFLAGS = -DNDEBUG -O 
LDFLAGS = -s 

TARGETS = abc def 

all: $(TARGETS) 

clean: 
    rm -f $(TARGETS) 

# Some dependencies. 
abc: def.h 

# Some special rules. 
def: def.c 
    $(CC) $(CFLAGS) $(LDFLAGS) -o [email protected] def.c -l m 

Per la maggior parte dei progetti di piccole dimensioni, non è necessario alcunché di più elaborato rispetto ai valori predefiniti; probabilmente dovresti concentrarti sul progetto stesso invece di perdere tempo a scrivere complicate logiche di makefile.

Una limitazione di POSIX make è che non esistono regole di modello e le regole di suffisso non possono gestire le directory; pertanto, la diffusione di file di origine in directory diverse richiederà una singola regola per ogni file o una creazione ricorsiva. Personalmente, per i piccoli progetti non mi preoccupo di spostare altrove i file sorgente. L'adesione a POSIX make garantisce inoltre la portabilità su altri sistemi di build compatibili con POSIX (ad esempio BSD make).

Per quanto riguarda il calcolo della dipendenza automatica, consultare lo gcc -M. Genera makefile contenenti regole di dipendenza. Puoi quindi importare include nel tuo makefile. Tuttavia, a mio parere, un'idea migliore è tenere traccia di tutte le dipendenze importanti in un singolo file di intestazione e fare in modo che ogni file dipenda da questa intestazione attraverso una regola di suffisso. Non è una gran seccatura e risparmi ancora una certa portabilità.

Riassumendo, il mio suggerimento è di evitare di preoccuparmi troppo delle complicazioni. Raramente pagano. In particolare, non ho mai amato lo autotools e certamente non lo sarò mai. UNIX è tutto basato sulla semplicità. Complicare le cose inutilmente è contro lo spirito UNIX. :)