¿Cómo escribir un bucle en un Makefile?

Quiero ejecutar los siguientes comandos:

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

¿Cómo escribir esto como un bucle en un Makefile ?

Lo siguiente lo hará si, como supongo al usar ./a.out , está en una plataforma de tipo UNIX.

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

Pruebe de la siguiente manera:

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

produce:

 1 2 3 4 

Para rangos más grandes, use:

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

Esta salida de 1 a 10 inclusive, solo cambia la condición de terminación de 10 a 1000 para un rango mucho más grande como se indica en su comentario.

Los bucles nesteds se pueden hacer así:

 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 

productor:

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

Si usas GNU make, podrías probar

 NÚMEROS = 1 2 3 4
 hazlo:
         $ (foreach var, $ (NUMBERS),. / a.out $ (var);)

que generará y ejecutará

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

LA razón principal para usar make IMHO es la bandera -j . make -j5 ejecutará 5 comandos de shell a la vez. Esto es bueno si tiene 4 CPUs y una buena prueba de cualquier archivo MAKE.

Básicamente, desea hacer ver algo como:

 .PHONY: all all: job1 job2 job3 .PHONY: job1 job1: ; ./a.out 1 .PHONY: job2 job2: ; ./a.out 2 .PHONY: job3 job3: ; ./a.out 3 

Esto es -j amigable (una buena señal). ¿Puedes ver la placa de la caldera? Podríamos escribir:

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

para el mismo efecto (sí, esto es lo mismo que la formulación anterior en lo que respecta a make , un poco más compacta).

Un poco más de parametrización para que pueda especificar un límite en la línea de comandos (tedioso como make no tiene ninguna buena macro aritmética, así que voy a hacer trampa aquí y usar $(shell ...) )

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

Ejecuta esto con make -j5 LAST=550 , con LAST predeterminado a 1000.

Me doy cuenta de que la pregunta tiene varios años, pero esta publicación aún puede ser útil para alguien, ya que demuestra un enfoque que difiere de lo anterior, y no depende de las operaciones shell ni de la necesidad del desarrollador de descifrar un código rígido. cadena de valores numéricos.

la macro comstackda $ (eval ….) es tu amiga. O puede ser al menos.

 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) 

Ese es un ejemplo simplista. No se escalará bastante para valores grandes; funciona, pero como la cadena ITERATE_COUNT boostá en 2 caracteres (espacio y punto) para cada iteración, a medida que aumente en miles, se tardará más tiempo en contar las palabras. Como está escrito, no maneja la iteración anidada (necesitaría una función de iteración separada y un contador para hacerlo). Esto es puramente gnu make, sin requisito de shell (aunque obviamente el OP estaba buscando ejecutar un progtwig cada vez, aquí, simplemente estoy mostrando un mensaje). El if dentro de ITERATE está destinado a capturar el valor 0, porque de lo contrario $ (palabra …) saldrá.

Tenga en cuenta que la cadena creciente para servir como contador se emplea porque el $ (words …) built-in puede proporcionar un conteo árabe, pero que no admite operaciones matemáticas (no puede asignar 1 + 1 a algo y obtener 2, a menos que estés invocando algo del caparazón para lograrlo por ti, o usando una macro operación igualmente intrincada). Esto funciona muy bien para un contador INCREMENTAL, no tan bien para un DECREMENTO sin embargo.

Yo no uso esto yo mismo, pero recientemente, tuve que escribir una función recursiva para evaluar las dependencias de la biblioteca en un entorno de comstackción multi-binario y multi-biblioteca donde necesita saber traer OTRAS bibliotecas cuando incluye alguna biblioteca que tiene otras dependencias (algunas de las cuales varían dependiendo de los parámetros de comstackción), y yo uso un método $ (eval) y contador similar al anterior (en mi caso, el contador se usa para asegurar que de alguna manera no entremos en un sinfín loop, y también como un diagnóstico para informar cuánta iteración fue necesaria).

Algo más que no vale nada, aunque no es significativo para el OP. Q: $ (eval …) proporciona un método para eludir el aborrecimiento interno de make a las referencias circulares, lo cual es bueno y correcto para aplicar cuando una variable es un tipo macro (inicializado con =), frente a una asignación inmediata (inicializado con: =). Hay momentos en los que desea poder usar una variable dentro de su propia asignación, y $ (eval …) le permitirá hacer eso. Lo importante a tener en cuenta aquí es que, en el momento en que ejecuta la evaluación, la variable se resuelve y la parte que se resuelve ya no se trata como una macro. Si sabes lo que estás haciendo e intentas usar una variable en el RHS de una tarea para sí mismo, generalmente es lo que quieres que suceda de todos modos.

  SOMESTRING = foo # will error. Comment out and re-run SOMESTRING = pre-${SOMESTRING} # works $(eval SOMESTRING = pre${SOMESTRING} default: @echo ${SOMESTRING} 

Feliz maquillaje.

Para soporte multiplataforma, configure el separador de comandos (para ejecutar múltiples comandos en la misma línea).

Si está utilizando MinGW en una plataforma de Windows, por ejemplo, el separador de comandos es & :

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

Esto ejecuta los comandos concatenados en una línea:

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

Como se mencionó en otra parte, en una plataforma * nix use CMDSEP = ; .

Esto no es realmente una respuesta pura a la pregunta, sino una forma inteligente de evitar estos problemas:

en lugar de escribir un archivo complejo, simplemente delegue el control para, por ejemplo, un script bash como: makefile

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

y script.sh se ve así:

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

Tal vez puedas usar:

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

Puede usar set -e como prefijo para for-loop. Ejemplo:

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

make saldrá inmediatamente con un código de salida <> 0 .

 #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.