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.