Jak napisać pętlę w pliku Makefile?


217

Chcę wykonać następujące polecenia:

./a.out 1
./a.out 2
./a.out 3
./a.out 4
.
.
. and so on

Jak napisać to w pętli Makefile?


Podobne, ale nieco bardziej ogólne:
Wieloliniowe

Odpowiedzi:


273

Poniższe czynności zrobią to, jeśli, jak zakładam, korzystasz z ./a.outplatformy typu UNIX.

for number in 1 2 3 4 ; do \
    ./a.out $$number ; \
done

Testuj w następujący sposób:

target:
    for number in 1 2 3 4 ; do \
        echo $$number ; \
    done

produkuje:

1
2
3
4

W przypadku większych zakresów użyj:

target:
    number=1 ; while [[ $$number -le 10 ]] ; do \
        echo $$number ; \
        ((number = number + 1)) ; \
    done

Daje to od 1 do 10 włącznie, wystarczy zmienić whilewarunek zakończenia z 10 na 1000 dla znacznie większego zakresu, jak wskazano w komentarzu.

Zagnieżdżone pętle można wykonać w następujący sposób:

target:
    num1=1 ; while [[ $$num1 -le 4 ]] ; do \
        num2=1 ; while [[ $$num2 -le 3 ]] ; do \
            echo $$num1 $$num2 ; \
            ((num2 = num2 + 1)) ; \
        done ; \
        ((num1 = num1 + 1)) ; \
    done

produkujący:

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
4 1
4 2
4 3

1
qwert to tylko nazwa docelowa, która prawdopodobnie nie będzie prawdziwym plikiem. Reguły makefile wymagają celu. Jeśli chodzi o błąd składniowy, brakuje niektórych rzeczy - zobacz aktualizację.
paxdiablo

2
Ponieważ zakładasz, że jego powłoka rozpoznaje ((...)), dlaczego nie użyć o wiele prostszego dla ((i = 0; i <WHATEVER; ++ i)); robić ...; Gotowe ?
Idelic

1
Zauważ, że seqpolecenie generujące sekwencję liczb istnieje w większości (wszystkich?) Systemach unixowych, więc możesz pisaćfor number in ``seq 1 1000``; do echo $$number; done (Umieść pojedynczy backstick po każdej stronie polecenia seq, a nie dwa, nie wiem jak poprawnie sformatować to przy użyciu składni stackoverflow)
Suzanne Dupéron

3
Dzięki, brakowało mi podwójnego $$, aby odwołać się do zmiennej pętli for.
Leif Gruenwoldt

1
@Jonz: znak kontynuacji linii jest taki, że makenormalnie traktuje każdą linię jako rzecz do uruchomienia w osobnej podpowłoce. Bez kontynuacji próbowałby uruchomić podpowłokę za pomocą (na przykład) for number in 1 2 3 4 ; do, bez reszty pętli. Dzięki temu skutecznie staje się pojedynczą linią formularza while something ; do something ; done, co jest kompletnym zestawieniem. Odpowiedź na $$pytanie tutaj: stackoverflow.com/questions/26564825/... , z pozornie wyodrębnionym kodem z tej samej odpowiedzi :-)
paxdiablo

265

Jeśli używasz GNU make, możesz spróbować

LICZBY = 1 2 3 4
Zrób to:
        $ (foreach var, $ (NUMBERS),. / a.out $ (var);)

które wygenerują i wykonają

./a.out 1; ./a.out 2; ./a.out 3; ./a.out 4;

27
Ta odpowiedź jest lepsza IMHO, ponieważ nie wymaga użycia żadnej powłoki, jest to czysty plik makefile (nawet jeśli jest specyficzny dla GNU).
Jocelyn delalande

7
średnik ma kluczowe znaczenie, w przeciwnym razie wykona się tylko pierwsza iteracja
Jeremy Leipzig

7
To rozwiązanie ukrywa kod wyjścia ./a.out 1. ./a.out 2zostanie wykonany niezależnie.
bobbogo

1
@Alexander: czy mógłbyś wyjaśnić, jakie ograniczenia masz na myśli?
Idelic

1
@Idelic, tak. Dla następującego makefile i shellscript, shellscript działa (przekazuje argument do ls), podczas gdy Makefile podaje błąd: make: execvp: / bin / sh: lista argumentów jest za długa Makefile: ix.io/d5L Shellscript: ix.io / d5M Problem ten występuje w funkcji foreach, z którą zetknąłem się w pracy, gdy do polecenia została przekazana niezwykle długa lista nazw plików (z długimi ścieżkami). Musieliśmy znaleźć obejście.
Alexander

107

Głównym powodem, aby make IMHO jest-j flaga.make -j5uruchomi 5 poleceń powłoki jednocześnie. Jest to dobre, jeśli masz 4 procesory i dobry test dowolnego makefile.

Zasadniczo chcesz, aby zobaczyłeś coś takiego:

.PHONY: all
all: job1 job2 job3

.PHONY: job1
job1: ; ./a.out 1

.PHONY: job2
job2: ; ./a.out 2

.PHONY: job3
job3: ; ./a.out 3

To jest -jprzyjazne (dobry znak). Czy dostrzegasz płytę kotła? Możemy napisać:

.PHONY: all job1 job2 job3
all: job1 job2 job3
job1 job2 job3: job%:
    ./a.out $*

dla tego samego efektu (tak, jest to taki sam, jak poprzedni sformułowanie, o ile marka obawia się, po prostu nieco bardziej zwarty).

Dalszy kawałek parametryzacji, abyś mógł określić limit w wierszu poleceń (żmudne jak make nie ma żadnych dobrych makr arytmetycznych, więc oszuka tutaj i użyję $(shell ...))

LAST := 1000
NUMBERS := $(shell seq 1 ${LAST})
JOBS := $(addprefix job,${NUMBERS})
.PHONY: all ${JOBS}
all: ${JOBS} ; echo "$@ success"
${JOBS}: job%: ; ./a.out $*

Uruchamiasz to make -j5 LAST=550z LASTdomyślną wartością 1000.


7
To zdecydowanie najlepsza odpowiedź z tego powodu, o którym wspominał bobbogo ( -j ).
dbn

Czy jest jakiś konkretny powód, dla którego używa przyrostków .PHONY? Czy są do czegoś potrzebne?
Seb

6
@seb: Bez polecenia .PHONY: allmake wyszuka plik o nazwie all. Jeśli taki plik istnieje, make sprawdzi następnie czas ostatniej zmiany tego pliku, a makefile prawie na pewno nie zrobi tego, co zamierzałeś. .PHONYDeklaracja mówi sprawi że alljest symbolicznym celem. Marka uzna zatem, że allcel jest zawsze nieaktualny. Idealny. Zobacz instrukcję.
bobbogo,

4
Jak działa ta linia: $ {JOBS}: job%: Do czego służy drugi średnik? Nic nie widziałem w gnu.org/software/make/manual/make.htm
Joe

2
@JoeS gnu.org/software/make/manual/make.html#Static-Pattern (myślę, że miałeś na myśli What for the 2nd * colon * for ). Nie myl ich z nieprzyjemnymi (IMHO) Regułami Wzorów . Statyczne zasady wzór są bardzo przydatne, gdy lista celów może być dopasowana przez jednego z make „s Noddy wzory (samo odnosi się do zależności). Tutaj używam jednego tylko dla wygody, że cokolwiek pasowało %do reguły, jest dostępne jak $*w przepisie.
bobbogo

22

Zdaję sobie sprawę, że pytanie ma kilka lat, ale ten post może nadal być dla kogoś przydatny, ponieważ pokazuje podejście, które różni się od powyższego i nie jest uzależnione od operacji powłoki ani potrzeby dewelopera wykreślenia na stałe ciąg wartości liczbowych.

wbudowane makro $ (eval ....) jest twoim przyjacielem. Lub może być przynajmniej.

define ITERATE
$(eval ITERATE_COUNT :=)\
$(if $(filter ${1},0),,\
  $(call ITERATE_DO,${1},${2})\
)
endef

define ITERATE_DO
$(if $(word ${1}, ${ITERATE_COUNT}),,\
  $(eval ITERATE_COUNT+=.)\
  $(info ${2} $(words ${ITERATE_COUNT}))\
  $(call ITERATE_DO,${1},${2})\
)
endef

default:
  $(call ITERATE,5,somecmd)
  $(call ITERATE,0,nocmd)
  $(info $(call ITERATE,8,someothercmd)

To jest uproszczony przykład. Nie skaluje się ładnie przy dużych wartościach - działa, ale ponieważ łańcuch ITERATE_COUNT zwiększy się o 2 znaki (spacja i kropka) dla każdej iteracji, w miarę zwiększania się do tysięcy, policzenie słów zajmuje coraz więcej czasu. Jak napisano, nie obsługuje zagnieżdżonej iteracji (aby to zrobić, potrzebujesz osobnej funkcji iteracji i licznika). Jest to czysto gnu make, nie wymaga powłoki (chociaż oczywiście OP chciał uruchomić program za każdym razem - tutaj po prostu wyświetlam komunikat). Jeśli w ciągu ITERATE ma przechwycić wartość 0, ponieważ $ (słowo ...) w przeciwnym razie popełni błąd.

Zauważ, że rosnący ciąg znaków, który ma służyć jako licznik, jest wykorzystywany, ponieważ wbudowane $ (słowa ...) może zapewnić liczbę arabską, ale to make nie obsługuje operacji matematycznych (nie możesz przypisać 1 + 1 do czegoś i dostać 2, chyba że wywołujesz coś z powłoki, aby to zrobić, lub używasz równie skomplikowanej operacji makra). Działa to świetnie w przypadku licznika INCREMENTAL, ale nie tak dobrze w przypadku DECREMENT.

Nie używam tego osobiście, ale ostatnio musiałem napisać funkcję rekurencyjną, aby ocenić zależności bibliotek w wielo-binarnym, wielobibliotkowym środowisku kompilacji, w którym musisz wiedzieć, aby wprowadzić INNE biblioteki, jeśli dodasz bibliotekę, która sam ma inne zależności (niektóre z nich różnią się w zależności od parametrów kompilacji), a ja używam metody $ (eval) i counter podobnej do powyższej (w moim przypadku licznik jest używany, aby upewnić się, że nie przejdziemy w nieskończoność pętli, a także jako narzędzie diagnostyczne do zgłaszania, ile iteracji było konieczne).

Coś innego nic nie warte, choć nieistotne dla Q OP: $ (eval ...) zapewnia metodę obejścia wewnętrznej wstrętności make do odwołań cyklicznych, co jest dobre i dobre do egzekwowania, gdy zmienna jest typem makra (zainicjalizowana z =), a natychmiastowe przypisanie (zainicjowane za pomocą: =). Są chwile, kiedy chcesz mieć możliwość korzystania ze zmiennej w ramach jej własnego przypisania, a $ (eval ...) pozwoli ci to zrobić. Ważną rzeczą do rozważenia w tym miejscu jest to, że w momencie uruchomienia eval zmienna zostaje rozwiązana, a ta część, która jest rozwiązana, nie jest już traktowana jako makro. Jeśli wiesz, co robisz i próbujesz użyć zmiennej w RHS przypisania do siebie, na ogół i tak chcesz to zrobić.

  SOMESTRING = foo

  # will error.  Comment out and re-run
  SOMESTRING = pre-${SOMESTRING}

  # works
  $(eval SOMESTRING = pre${SOMESTRING}

default:
  @echo ${SOMESTRING}

Happy make'ing.


20

Aby umożliwić obsługę wielu platform, skonfiguruj separator poleceń (do wykonywania wielu poleceń w tym samym wierszu).

Jeśli używasz MinGW na przykład na platformie Windows, separatorem poleceń jest &:

NUMBERS = 1 2 3 4
CMDSEP = &
doit:
    $(foreach number,$(NUMBERS),./a.out $(number) $(CMDSEP))

Spowoduje to wykonanie połączonych poleceń w jednym wierszu:

./a.out 1 & ./a.out 2 & ./a.out 3 & ./a.out 4 &

Jak wspomniano w innym miejscu, na platformie * nix CMDSEP = ;.


7

To nie jest tak naprawdę czysta odpowiedź na pytanie, ale inteligentny sposób na obejście takich problemów:

zamiast pisać złożony plik, po prostu przekaż kontrolę na przykład skryptowi bash, np .: makefile

foo : bar.cpp baz.h
    bash script.sh

i script.sh wygląda następująco:

for number in 1 2 3 4
do
    ./a.out $number
done

Utknąłem o krok podczas pisania pliku Makefile. Mam następujący kod: set_var: @ NUM = 0; podczas gdy [[$$ NUM <1]]; do \ echo „Jestem tutaj”; \ echo $$ NUM dump $$ {NUM} .txt; \ var = "SSA_CORE $$ {NUM} _MAINEXEC"; \ echo $$ var; \ var1 = eval echo \$${$(var)}; \ echo $$ var1; \ ((NUM = NUM ​​+ 1)); \ done all: set_var tutaj SSA_CORE0_MAINEXEC to zmienna środowiskowa, która jest już ustawiona, więc chcę, aby ta wartość została oszacowana lub wydrukowana za pomocą zmiennej var1. Próbowałem, jak pokazano powyżej, ale nie działa. proszę pomóż.
XYZ_Linux

2
Jest to rzeczywiście łatwe obejście, jednak powstrzymuje cię od korzystania z przyjemnej opcji „make -j 4”, aby procesy działały równolegle.
TabeaKischka

1
@TabeaKischka: rzeczywiście. Nie był to „ zalecany ” sposób, ale bardziej znaczy, jeśli ktoś potrzebuje pewnych funkcji, które nie są oferowane przez Makefile, wówczas można wrócić do implementacji bashi tym samym korzystać z tych funkcji. Pętla jest jedną z cech, dla których można to wykazać.
Willem Van Onsem,


3

Możesz użyć set -ejako prefiksu dla pętli for. Przykład:

all:
    set -e; for a in 1 2 3; do /bin/false; echo $$a; done

makenatychmiast wyjdzie z kodem wyjścia <> 0.


0

Chociaż zestaw narzędzi tabeli GNUmake ma prawdziwą whilepętlę (cokolwiek to oznacza w programowaniu GNUmake z jego dwoma lub trzema fazami wykonania), jeśli potrzebna jest lista iteracyjna, istnieje proste rozwiązanie interval. Dla zabawy zmieniamy też liczby na szesnastkowe:

include gmtt/gmtt.mk

# generate a list of 20 numbers, starting at 3 with an increment of 5
NUMBER_LIST := $(call interval,3,20,5)

# convert the numbers in hexadecimal (0x0 as first operand forces arithmetic result to hex) and strip '0x'
NUMBER_LIST_IN_HEX := $(foreach n,$(NUMBER_LIST),$(call lstrip,$(call add,0x0,$(n)),0x))

# finally create the filenames with a simple patsubst
FILE_LIST := $(patsubst %,./a%.out,$(NUMBER_LIST_IN_HEX))

$(info $(FILE_LIST))

Wynik:

./a3.out ./a8.out ./ad.out ./a12.out ./a17.out ./a1c.out ./a21.out ./a26.out ./a2b.out ./a30.out ./a35.out ./a3a.out ./a3f.out ./a44.out ./a49.out ./a4e.out ./a53.out ./a58.out ./a5d.out ./a62.out

0

Proste, niezależne od powłoki / platformy, czyste rozwiązanie makro jest ...

%sequence = $(if $(word ${1},${2}),$(wordlist 1,${1},${2}),$(call %sequence,${1},${2} $(words _ ${2})))

$(foreach i,$(call %sequence,10),$(info ./a.out ${i}))

-1
#I have a bunch of files that follow the naming convention
#soxfile1  soxfile1.o  soxfile1.sh   soxfile1.ini soxfile1.txt soxfile1.err
#soxfile2  soxfile2.o   soxfile2.sh  soxfile2.ini soxfile2.txt soxfile2.err
#sox...        ....        .....         ....         ....        ....
#in the makefile, only select the soxfile1.. soxfile2... to install dir
#My GNU makefile solution follows:
tgt=/usr/local/bin/          #need to use sudo
tgt2=/backup/myapplication/  #regular backup 

install:
        for var in $$(ls -f sox* | grep -v '\.' ) ; \
        do \
                sudo  cp -f $$var ${TGT} ;     \
                      cp -f  $$var ${TGT2} ;  \
        done


#The ls command selects all the soxfile* including the *.something
#The grep command rejects names with a dot in it, leaving  
#My desired executable files in a list. 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.