2012-12-27 20 views
6

Co to jest najlepsza praktyka pisania kodu wielokrotnego użytku w Makefile s?Najlepsza praktyka pisania kodu wielokrotnego użytku

Załóżmy, że mam Makefile:

.PHONY: all task01-all task01-clean task01-run task02-all task02-clean task02-run 

all: task01-all task02-all 

############################################################################### 
task01-all: task01-clean task01 task01-run 

task01-clean: 
    rm task01 task01.{exi,o} -f 

task01: 
    compiler task01.ext -O2 --make 

task01-run: 
    ./task01 

############################################################################### 
task02-all: task02-clean task02 task02-run 

task02-clean: 
    rm task02 task02.{exi,o} -f 

task02: 
    compiler task02.ext -O2 --make 

task02-run: 
    ./task02 

Teraz chcę dodać nową rodzinę zadań (task03), i muszę CopyPaste całą sekcję, należy s/02/03/ dla niego i dodać je do .PHONY sekcji - to głośny, obrzydliwy i po prostu nie w porządku.

Jak mogę tego uniknąć? Czy mogę na nowo zdefiniować wszystkie zadania za pomocą szablonów, aby mieć ładny mechanizm dodawania nowej grupy zadań w jednym wierszu?

Odpowiedz

9

Ponieważ pytanie jest o pisaniu kodu wielokrotnego użytku w Makefile, dam przykład jak używać reguł wzorców w GNU make (wygląda właśnie tak właśnie używasz, odkąd wspomniałeś o celu .PHONY). Jednakże, jeśli nie używasz żadnej z kontroli zależności sprawiają, może on być prostsze to zrobić ze skryptem powłoki - coś jak:

#!/bin/sh 
TASKS="task01 task02 task03" 

for i in $TASKS; do 
    rm $i $i.ext $i.o -f; 
    compiler $i.ext -O2 --make; 
    ./$i; 
done 

Jednak, aby rozszerzyć zasadę zrobić, mamy kolejny problem do rozwiązania. Linie postaci:

task01-all: task01-clean task01 task01-run 

nie mów Bądź w jakim celu stworzenia warunków wstępnych - wystarczy, że wszystkie one muszą być wykonane przed task01-all zostanie zbudowany. Zamiast tego każdy krok do uruchomienia powinien zależeć od kroku poprzedzającego. Coś takiego:

TASKS=task01-run task02-run task03-run 

.PHONY: all $(TASKS) $(TASKS:run=clean) 

all: $(TASKS) 

$(TASKS:run=clean): %-clean: 
    rm $* $*.ext $*.o -f 

%: %.ext | %-clean 
    compiler $< -O2 --make 

$(TASKS): %-run: % 
    ./$< 

Przepisy z % nazywane są „reguły Pattern”, a są one doskonałym narzędziem, aby uniknąć ponownego pisania tej samej reguły wielokrotnie do różnych celów. Jednym zastrzeżeniem jest to, że Make zwykle nie sprawdza reguł wzoru dla celu .PHONY; mówimy do Make, aby to zrobić jawnie, dodając te reguły do ​​listy celów i drugiego dwukropka (np. $(TASKS):).

Od task01-run wymaga task01 w celu pracy, tworzymy %-run celów zależy od %. Ponadto twój plik Makefile pokazuje, że chcesz za każdym razem czyścić, dlatego robimy % zależą od %-clean. Ponieważ %-clean w rzeczywistości nie generuje żadnych danych wyjściowych, robimy to jako zależność "tylko od zamówienia" - Make nie będzie szukać znacznika czasu ani niczego, po prostu uruchomi regułę %-clean za każdym razem, gdy potrzebuje uruchomić % reguła. „Tylko porządku” Zależności są umieszczone po |:

%: %.ext | %-clean 

Warto wspomnieć, że jedną z największych zalet sprawiają, jest to, że może to zaoszczędzić czas, nie powtarzając pracę, że nie trzeba powtarzać - czyli to działa tylko wtedy, gdy zależności są nowsze niż cel.Tak więc, można zostawić off zależność od %-clean, który spowodowałby zrobić tylko uruchomić compiler $< -O2 --make jeśli %.ext jest nowszy niż %:

%: %.ext 
    compiler $< -O2 --make 

Można by następnie dodać regułę tylko do uruchomienia wszystkich %-clean celów:

.PHONY: all $(TASKS) $(TASKS:run=clean) clean 

clean: $(TASKS:run=clean) 

Ostatnia rzecz: używam specjalnych zmiennych w przepisach. [email protected] oznacza budowany cel. $< oznacza pierwszą zależność. $* oznacza "rdzeń" reguły wzoru (tj. Część zgodną z %).

+0

Przyjemne wyjaśnienia. Dzięki! –

+0

Podanie przykładu dla "|" było miło, dzięki. –

2

Wygląda na to, czego szukam:

ALLS=task01-all task02-all 
BUILDS=${ALLS:-all=-build} 
CLEANS=${ALLS:-all=-clean} 
RUNS=${ALLS:-all=-run} 

.PHONY: all $(ALLS) $(CLEANS) $(BUILDS) $(RUNS) 

all: $(ALLS) 

############################################################################### 
$(ALLS): $(CLEANS) $(BUILDS) $(RUNS) 

$(CLEANS): 
     rm ${@:-clean=} ${@:-clean=}.{ext,o} -f 

$(BUILDS): 
     compiler ${@:-build=}.ext -O2 --make 

$(RUNS): 
     ./${@:-run=} 
Powiązane problemy