#makefile #gnu-make
Вопрос:
У меня есть два списка файлов в качестве предварительных условий
input_i.xx
config_j.yy
и мне нужно запустить все их комбинации. Один из них выглядит так:
input1_config3.output: input1.xx config3.yy
run_script $^
Также на самом деле их имена не пронумерованы, но у меня уже есть их основы, определенные в INPUTS
и CONFIGS
. С помощью этого я могу сгенерировать все цели вместе
TARGETS:=$(foreach input,$(INPUTS),$(foreach config,$(CONFIGS),$(input)_$(config).output))
Но у меня возникли трудности с необходимыми условиями. Кажется, мне нужно
- получить базовое имя
- разделиться на
_
- добавьте расширения
.xx
и.yy
.SECONDEXPANSION
$(TARGETS): $(basename $@)
run_script $^
Может кто-нибудь показать мне, как это сделать? Не уверен, что это правильный путь, может быть, проще идти снизу вверх?
Ответ №1:
make
на самом деле не подходит для отслеживания матрицы результатов M x N. Фундаментальная проблема заключается в том, что в правиле не может быть двух основ, поэтому вы не можете сказать что-то вроде
# BROKEN
input%{X}_config%{Y}.output: input%{X}.xx config%{Y}.yy
В качестве приблизительного приближения вы могли бы использовать рекурсивное make
правило для задания нескольких параметров и взять его оттуда, но это довольно неуклюже.
.PHONY: all
all:
$(MAKE) -$(MAKEFLAGS) X=1 Y=6 input1_config6.output
$(MAKE) -$(MAKEFLAGS) X=1 Y=7 input1_config7.output
$(MAKE) -$(MAKEFLAGS) X=2 Y=6 input2_config6.output
:
input$X_config$Y.output: input$X.xx config$Y.yy
run_script $^
Ответ №2:
Было бы намного проще, если бы вы предоставили полный примерный пример с полным набором целей и предварительных условий и именно тем, что вы хотели, чтобы произошло.
Использование .SECONDEXPANSION
может сработать, но вы используете его неправильно; пожалуйста,перечитайте документацию. Критический аспект .SECONDEXPANSION
заключается в том, что вам нужно избегать переменных, которые вы хотите избежать расширения до второго прохода. В вашем примере вы ничего не избежали, поэтому .SECONDEXPANSION
на самом деле здесь вообще ничего не делаете. Однако, как указывает @tripleee, нелегко использовать несколько значений переменных в одном целевом объекте.
Чтобы сделать это проще, вы, вероятно, захотите использовать eval
. Что-то вроде этого:
define DECLARE
$1_$2.output: $1.xx $2.yy
TARGETS = $1_$2.output
endef
TARGETS :=
$(foreach input,$(INPUTS),$(foreach config,$(CONFIGS),$(eval $(call DECLARE,$(input),$(config)))))
$(TARGETS):
run_script $^
Комментарии:
1. спасибо, я только что понял другое решение:
include
еще один файл makefile, который еще не существует, и используйте цикл bash, чтобы записать его содержимое в качестве цели2. Да, это тоже сработает. Единственная проблема заключается в том, чтобы убедиться, что включенный файл помечен как устаревший и перестроен, если/когда вы измените содержимое переменных
INPUTS
илиCONFIGS
.
Ответ №3:
У меня есть другое решение, использующее include
for
цикл and bash.
include trees.mk
trees.mk:
@for input in $(INPUTS); do
for config in $(CONFIGS); do
echo ${input}_$config.output : ${input}.xx $config.yy;
echo -e 't run_scipt $^ ';
done
done > $@
В начале, trees.mk
не существует. Двойные for
циклы записывают правило для целевого объекта с помощью перенаправления файлов >$@
.
Я получил эту идею от Управления Проектами с помощью GNU Make, Третье Издание Роберта Мекленбурга, на стр. 56