Автоматическая переменная не расширяется должным образом в Makefile - PullRequest
0 голосов
/ 09 мая 2019

Я использую следующий код:

HELLO_WORLD=hello

$(HELLO_WORLD): $(addsuffix .c,$@)
        gcc $< -o $@

Однако, когда я запускаю код, я получаю следующую ошибку, подразумевая, что $ <ничего не оценивает: </p>

gcc  -o hello
gcc: fatal error: no input files

Когда я использую следующий код ...

HELLO_WORLD=hello

$(HELLO_WORLD): $(addsuffix .c,$@)
        gcc $(addsuffix .c,$@) -o $@

... Makefile вычисляет следующую команду ...

gcc hello.c -o hello

... это именно то, что я хотел бы. Однако я не хочу использовать adduffix дважды. Я хотел бы использовать $ <в случае, если я изменяю предварительное условие. Как мне это сделать? </p>

Ответы [ 2 ]

4 голосов
/ 09 мая 2019

Проблема не с расширением $< в рецепте. Проблема заключается в расширении $@ в списке предварительных требований.

Автоматические переменные, такие как $@, определяются только в рецепте, но не в списках целей или предварительных требований. Это выделено в разделе Руководство по GNU Make для автоматических переменных :

Распространенной ошибкой является попытка использовать $@ в списке предварительных условий; это не сработает.

Тот факт, что hello.c на самом деле отсутствует в списке предварительных условий, не мешает вам вызывать make hello. Это просто означает, что make hello всегда будет вызывать компилятор, даже если hello.c не был изменен. Но это означает, что $< будет таким же пустым, как и вычисленный список предварительных условий.

В GNU make есть функция, позволяющая вам выполнить второе расширение предварительных условий; это объясняется в руководстве. Но более простое решение - просто не полагаться на $@ в списке обязательных компонентов. Если вы пытаетесь создать свой собственный общий рецепт компиляции C, используйте шаблонное правило для целей объектного файла (.o). Для конечного исполняемого файла перечислите все предварительные условия для конечного исполняемого файла (который почти наверняка будет состоять из нескольких файлов).

Обычно это делается с использованием отдельной переменной с именами, такими как SRCS и OBJS (или SOURCES и OBJECTS, если вы не против печатать гласные). Обычно вы создаете предварительные условия для объектных файлов для конечного исполняемого файла (который будет являться операцией связи), поскольку каждый отдельный исходный файл будет иметь свои собственные предварительные условия заголовка.

1 голос
/ 09 мая 2019

Основная проблема заключается в том, что автоматические переменные определяются только в рецепте. Таким образом, в предварительном условии $ @ не определено. Поскольку $ <будет относиться к выражению, которое зависит от $ @, которое не существует, $ <поэтому не будет существовать. </p>

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

HELLO_WORLD=hello

SECONDARYEXPANSION:
$(HELLO_WORLD): $(addsuffix .c,$$@)
        gcc $< -o $@

Более правильный способ сделать это включает в себя реструктуризацию Makefile и использование шаблонных правил. Это дает нам общий рецепт для создания любого файла C. Используя следующий Makefile, мы можем запустить make или make hello для сборки исполняемого файла.

HELLO_WORLD=hello

all:
        $(MAKE) $(HELLO_WORLD)

%: %.c
        gcc $< -o $@
...