#c #shell #makefile #target #automatic-variable
#c #оболочка #makefile #цель #автоматическая переменная
Вопрос:
У меня есть makefile, который, как предполагается, повторяет выполнение кода c (BootstrapCL.c) один раз для каждого файла.csv в каталоге. Для каждого выполнения он должен вводить ввод в код c (как argv) 2 строки: имя с расширением и без расширения входного csv-файла, используемого в текущем выполнении. Это содержимое makefile:
SRCS := $(wildcard *.csv)
BINS := $(SRCS:%.csv=%)
all: ${BINS}
%: BootstrapCL.c
gcc -Wall BootstrapCL.c -lm -o BootstrapCL
./BootstrapCL $@.csv $@
Проблема в том, что после выполнения всей группы csv-файла (я бы хотел выполнить только цель внутри списка $ {BINS}), он также запускает последнее выполнение со «всей» целью. Конечно, у меня нет файла all.csv в моей папке; Я думаю, что я неправильно использую $ @, но я не понимаю, почему и как решить проблему, есть идеи?
Ответ №1:
У меня немного другой взгляд на решение, чем у бета-версии:
SRCS := $(wildcard *.csv)
BINS := $(SRCS:%.csv=%)
# All rule - requires binaries to be generated. This does not generate anything, therefore it is PHONY
.PHONY: all
all: ${BINS}
# Generates the binaries - requires BootstrapCL exe to be generated first
${BINS}: BootstrapCL
./BootstrapCL $@.csv $@
# Generates BootstrapCL
BootstrapCL:
gcc -Wall BootstrapCL.c -lm -o BootstrapCL
Из того, что я могу сказать — вам нужно скомпилировать BootstrapCL.c один раз, чтобы сгенерировать исполняемый файл BootstrapCL. Затем, используя это, вы создаете несколько двоичных файлов — один для файла eacn .csv.
Итак, в обратном порядке:
- У вас есть
all
цель, для которой требуются ячейки. - У вас есть
BINS
цель, которая генерирует ячейки, но сначала требуется BootstrapCL — это будет выполняться один раз для каждого файла .csv. - У вас есть
BootstrapCL
цель, которая генерирует исполняемый файл BootstrapCL — это будет выполняться только один раз.
Комментарии:
1. почему цель BootstrapCL будет выполняться только один раз таким образом?
2. Поскольку правило для создания
BootstrapCL
определено — поэтому, как только файлBootstrapCL
существует, make увидит, что он существует, и не будет пытаться создать его снова (если временная метка новее, чем исходный файл). Это ключевая особенность makefiles.
Ответ №2:
Проблема в том, что у вас нет рецепта all
и правила, для %
которого что-либо соответствует.Когда Make завершает выполнение предварительных условий ( ${BINS}
), он пытается выполнить сборку all
, ищет подходящий рецепт и находит %
.
Существует (по крайней мере) два способа решения проблемы. Быстрый и грязный способ — добавить (нулевой) рецепт для all
:
all: ${BINS}
@:
Другой способ, на мой взгляд, лучше — ужесточить второе правило:
BINS := $(SRCS:%.csv=%.phony)
all: ${BINS}
%.phony: BootstrapCL.c
gcc -Wall BootstrapCL.c -lm -o BootstrapCL
./BootstrapCL $*.csv $*
Просто чтобы быть аккуратным, я предлагаю вам также добавить строку:
.PHONY: $(BINS)
Чтобы сообщить Make, что .phony
цели не являются реальными файлами, которые будут созданы.
Возможны некоторые другие небольшие улучшения. Я бы подумал о BootstrapCL
том, создает ли он файл, который можно использовать в качестве реальной цели.
Комментарии:
1. Спасибо за ваш бета-ответ; это помогает мне лучше понять, что происходит; теперь у меня есть только одно сомнение. «Когда Make завершит выполнение предварительных условий ($ {BINS}), он пытается собрать все». Но у all нет рецепта, так зачем присваивать «all» рецепт другой цели ($ {BINS})?
Ответ №3:
%
Правило слишком общее и улавливает все, даже пытается перестроить сам Makefile. Более того, он компилируется BootstrapCL
каждый раз, что кажется ненужным:
$ make
gcc -Wall BootstrapCL.c -lm -o BootstrapCL
./BootstrapCL Makefile.csv Makefile
gcc -Wall BootstrapCL.c -lm -o BootstrapCL
./BootstrapCL foo.csv foo
gcc -Wall BootstrapCL.c -lm -o BootstrapCL
./BootstrapCL bar.csv bar
gcc -Wall BootstrapCL.c -lm -o BootstrapCL
./BootstrapCL all.csv all
Я бы использовал правило шаблона для генерации двоичных файлов, так как это также приведет к перестройке двоичного файла в случае изменения файла .csv. Я бы также разделил BootstrapCL
генерацию на другое правило, чтобы скомпилировать его только один раз, но сохранить его в списке зависимостей, чтобы цели могли их восстановить в случае BootstrapCL
изменений:
$ cat Makefile
SRCS := $(wildcard *.csv)
BINS := $(SRCS:%.csv=%)
all: ${BINS}
BootstrapCL: BootstrapCL.c
gcc -Wall
lt;
-lm -o $@
%: %.csv BootstrapCL
./BootstrapCL
lt; $@
Вывод:
$ make
gcc -Wall BootstrapCL.c -lm -o BootstrapCL
./BootstrapCL foo.csv foo
./BootstrapCL bar.csv bar