web-dev-qa-db-fra.com

Comment écrire en boucle dans un Makefile?

Je veux exécuter les commandes suivantes:

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

Comment écrire cette chose sous forme de boucle dans un Makefile?

194
avd

Ce qui suit le fera si, comme je suppose par votre utilisation de ./a.out, vous êtes sur une plate-forme de type UNIX.

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

Testez comme suit:

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

produit:

1
2
3
4

Pour les plus grandes gammes, utilisez:

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

Les sorties 1 à 10 inclusivement, il suffit de changer la condition de terminaison while de 10 à 1000 pour une plage beaucoup plus large comme indiqué dans votre commentaire.

Nested boucles peuvent être faites ainsi:

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

produisant:

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
4 1
4 2
4 3
245
paxdiablo

Si vous utilisez GNU make, vous pouvez essayer

 NUMBERS = 1 2 3 4 
 Doit: 
 $ (Foreach var, $ (NUMBERS),./A.out $ (var);) 

qui va générer et exécuter

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

LA principale raison d'utiliser make IMHO est l'indicateur -j. make -j5 exécutera 5 commandes à la fois. C'est bien si vous avez 4 processeurs, et un bon test de tout makefile.

En gros, vous voulez faire voir quelque chose comme:

.PHONY: all
all: job1 job2 job3

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

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

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

Ceci est -j amical (un bon signe). Pouvez-vous repérer la plaque chauffante? Nous pourrions écrire:

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

pour le même effet (oui, c'est la même chose que la formulation précédente en ce qui concerne make, juste un peu plus compact).

Un peu plus de paramétrage pour que vous puissiez spécifier une limite en ligne de commande (fastidieux comme make n'a pas de bonnes macros arithmétiques, je vais donc tricher ici et utiliser $(Shell ...))

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

Vous exécutez ceci avec make -j5 LAST=550, avec LAST par défaut à 1000.

100
bobbogo

Je me rends compte que la question date de plusieurs années, mais ce message peut toujours être utile à quelqu'un, car il illustre une approche différente de ce qui précède et ne dépend pas non plus des opérations de Shell, ni de la nécessité pour le développeur de définir une méthode codée en dur. chaîne de valeurs numériques.

la macro intégrée $ (eval ....) est votre amie. Ou peut être au moins.

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)

C'est un exemple simpliste. Cela ne sera pas beau pour les grandes valeurs - cela fonctionne, mais comme la chaîne ITERATE_COUNT augmente de 2 caractères (espace et point) à chaque itération, au fur et à mesure que vous atteignez des milliers, le comptage des mots prend de plus en plus long. Comme écrit, il ne gère pas l'itération imbriquée (vous aurez besoin d'une fonction d'itération séparée et d'un compteur pour le faire). C’est purement génial, pas d’exigence de Shell (bien que, de toute évidence, le PO cherchait à exécuter un programme à chaque fois - ici, je n’affiche qu’un message). Si dans ITERATE est destiné à intercepter la valeur 0, sinon $ (Word ...) générera une erreur.

Notez que la chaîne croissante servant de compteur est utilisée parce que la commande $ (words ...) peut fournir un compte arabe, mais que make ne prend pas en charge les opérations mathématiques (vous ne pouvez pas affecter 1 + 1 à quelque chose et obtenir 2, à moins que vous n'invoquiez quelque chose à partir du shell pour l'accomplir pour vous, ou que vous utilisiez une opération de macro également compliquée). Cela fonctionne très bien pour un compteur INCREMENTAL, pas très bien pour un compteur DECREMENT cependant.

Je ne l'utilise pas moi-même, mais récemment, j'avais besoin d'écrire une fonction récursive pour évaluer les dépendances de bibliothèque dans un environnement de construction multi-binaire, multi-bibliothèque où vous devez savoir pour importer d'autres bibliothèques lorsque vous incluez une bibliothèque qui elle-même a d’autres dépendances (dont certaines varient en fonction des paramètres de construction), et j’utilise une méthode $ (eval) et un compteur similaires à ceux décrits ci-dessus (dans mon cas, le compteur est utilisé pour s’assurer que nous n’allons pas dans une infinité boucle, et aussi comme diagnostic pour indiquer combien d’itération était nécessaire).

Quelque chose d’autre ne vaut rien, même si cela n’a pas d’importance pour Q: $ (eval ...) du PO fournit une méthode pour contourner l’horreur interne de make en références circulaires, ce qui est tout à fait judicieux à appliquer lorsqu’une variable est un type macro (initialisé avec =), par opposition à une affectation immédiate (initialisée avec: =). Vous voudrez parfois pouvoir utiliser une variable dans sa propre affectation, et $ (eval ...) vous permettra de le faire. La chose importante à considérer ici est qu'au moment où vous exécutez l'évaluation, la variable est résolue et la partie résolue n'est plus traitée comme une macro. Si vous savez ce que vous faites et que vous essayez d'utiliser une variable sur le RHS d'une affectation à elle-même, c'est généralement ce que vous voulez faire de toute façon.

  SOMESTRING = foo

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

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

default:
  @echo ${SOMESTRING}

Heureux de faire.

20
s.straw

Pour la prise en charge multiplate-forme, configurez le séparateur de commande (pour exécuter plusieurs commandes sur la même ligne).

Si vous utilisez MinGW sur une plate-forme Windows par exemple, le séparateur de commande est &:

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

Ceci exécute les commandes concaténées sur une ligne:

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

Comme mentionné ailleurs, sur une plate-forme * nix, utilisez CMDSEP = ;.

17
cherno

Ce n'est pas vraiment une réponse pure à la question, mais un moyen intelligent de contourner de tels problèmes:

au lieu d'écrire un fichier complexe, déléguez simplement le contrôle à, par exemple, un script bash tel que: makefile

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

et script.sh ressemble à:

for number in 1 2 3 4
do
    ./a.out $number
done
5
Willem Van Onsem

Vous pouvez utiliser set -e comme préfixe de la boucle for. Exemple:

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

make quittera immédiatement avec un code de sortie <> 0.

3

Peut-être que vous pouvez utiliser:

xxx:
    for i in `seq 1 4`; do ./a.out $$i; done;
2
Donwellus