Как написать цикл в Makefile?

Я хочу выполнить следующие команды:

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

Как написать эту вещь как цикл в Makefile?

Ответ 1

Следующее будет выполнено, если, как я полагаю, используя ваш ./a.out, вы находитесь на платформе типа UNIX.

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

Проверьте следующее:

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

дает:

1
2
3
4

Для больших диапазонов используйте:

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

Это выводит от 1 до 10 включительно, просто измените условие завершения while с 10 до 1000 для гораздо большего диапазона, как указано в вашем комментарии.

Вложенные петли могут быть выполнены таким образом:

qwert:
    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

производства:

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

Ответ 2

Если вы используете GNU make, вы можете попробовать

NUMBERS = 1 2 3 4
doit:
        $(foreach var,$(NUMBERS),./a.out $(var);)

который будет генерировать и выполнять

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

Ответ 3

Основной причиной использования IMHO является флаг -j. make -j5 будет запускать сразу 5 команд оболочки. Это хорошо, если у вас есть 4 процессора, и хороший тест любого make файла.

В принципе, вы хотите сделать что-то вроде:

.PHONY: all
all: job1 job2 job3

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

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

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

Это -j дружественный (хороший знак). Можете ли вы определить котельную? Мы могли бы написать:

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

для одного и того же эффекта (да, это то же самое, что и предыдущая формулировка в отношении make, немного более компактная).

Дальше бит параметризации, чтобы вы могли указать ограничение в командной строке (нужный как make не имеет хороших арифметических макросов, поэтому я буду обманывать здесь и использовать $(shell ...))

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

Вы запускаете это с помощью make -j5 LAST=550, с LAST по умолчанию 1000.

Ответ 4

Я понимаю, что вопрос несколько лет, но этот пост может по-прежнему быть полезен для кого-то, поскольку он демонстрирует подход, который отличается от вышеизложенного, и не зависит от операций оболочки или от необходимости расширять возможности разработчика из строковой строки числовых значений.

встроенный макрос $(eval....) - ваш друг. Или может быть как минимум.

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)

Это упрощенный пример. Он не будет масштабироваться довольно для больших значений - он работает, но по мере того, как строка ITERATE_COUNT будет увеличиваться на 2 символа (пробел и точка) для каждой итерации, когда вы встанете на тысячи, для подсчета слов требуется более продолжительное время. Как написано, он не обрабатывает вложенную итерацию (вам понадобится отдельная итерационная функция и счетчик для этого). Это чисто gnu make, без требования оболочки (хотя очевидно, что OP каждый раз запускал программу - здесь я просто показываю сообщение). Если внутри ITERATE предполагается уловить значение 0, потому что $(word...) в противном случае будет ошибочно.

Обратите внимание, что растущая строка, служащая в качестве счетчика, используется потому, что встроенный $(words...) может обеспечивать арабский счет, но этот make не поддерживает другие математические операции (вы не можете присваивать 1 + 1 чему-либо и получите 2, если вы не вызываете что-то из оболочки, чтобы выполнить его для вас, или используя операцию с одинаковой степенью макросов). Это отлично работает для счетчика INCREMENTAL, но не так хорошо для DECREMENT. Однако.

Я не использую это сам, но в последнее время мне пришлось написать рекурсивную функцию для оценки зависимостей библиотек в многобиблиотечной, многобиблиотечной среде сборки, где вам нужно знать, чтобы вносить в ДРУГИЕ библиотеки, когда вы включаете некоторая библиотека, которая сама имеет другие зависимости (некоторые из которых различаются в зависимости от параметров сборки), и я использую метод $(eval) и счетчика, аналогичный описанному выше (в моем случае счетчик используется, чтобы гарантировать, что мы не так или иначе в бесконечный цикл, а также как диагностику, чтобы сообщить, сколько итераций было необходимо).

Что-то еще ничего не стоит, хотя и не имеет значения для OP Q: $(eval...), дает метод обхода сделать внутреннее отвращение к циклическим ссылкам, что все хорошо и прекрасно, чтобы обеспечить, когда переменная является типом макроса (intialized with =), по сравнению с непосредственным назначением (инициализируется с помощью = =). Иногда вы хотите иметь возможность использовать переменную в пределах своего собственного назначения, а $(eval...) позволит вам это сделать. Важно рассмотреть здесь, что в то время, когда вы запускаете eval, переменная становится разрешенной, и эта часть, которая разрешена, больше не рассматривается как макрос. Если вы знаете, что делаете, и вы пытаетесь использовать переменную в RHS для присваивания себе, это вообще то, что вы хотите в любом случае.

  SOMESTRING = foo

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

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

default:
  @echo ${SOMESTRING}

Счастливый make'ing.

Ответ 5

Для кросс-платформенной поддержки сделайте разделитель команд (для выполнения нескольких команд в одной строке).

Если вы используете MinGW на платформе Windows, например, разделитель команд &:

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

Выполняет конкатенированные команды в одной строке:

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

Как упоминалось в другом месте, на платформе * nix используйте CMDSEP = ;.

Ответ 6

Это не совсем чистый ответ на вопрос, но разумный способ обойти такие проблемы:

вместо написания сложного файла просто делегируйте элемент управления, например, bash script, например: Makefile

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

и script.sh выглядит так:

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

Ответ 7

Возможно, вы можете использовать:

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

Ответ 8

Вы можете использовать set -e в качестве префикса для цикла for. Пример:

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

make немедленно выйдет с кодом выхода <> 0.

Ответ 9

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