Неожиданное выполнение цели «все» в моем makefile

#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