У меня есть функция в Makefile, где я пытаюсь оценить входные параметры в операторах ifeq, как показано ниже:
define set_target_dts
ifeq ($(1),virgo)
@echo "Building for Virgo"
ifeq ($(2),soc_prototype)
@echo "Building for soc prototype"
DTS_FILE_NAME := virgo_soc_proto.dts
else
@echo "Building for soc"
DTS_FILE_NAME := virgo.dts
endif
else ifeq ($(1),gemini)
@echo "Building for Gemini"
ifeq ($(2),soc_prototype)
DTS_FILE_NAME := gemini_soc_proto.dts
else
DTS_FILE_NAME := gemini.dts
endif
else
$(info Selected Target is:$(1) $(2))
$(error Please select the correct target...)
endif
endef
Всякий раз, когда я вызываю эту функцию, используя
$(call set_target_dts,virgo,soc_prototype)
Это выдает мне эту ошибку:
Selected Target is:virgo soc
Makefile:48: *** Please select the correct target.... Stop.
Это означает, что ни ifeq не оценивается правильно, ни значение второго параметра не получается правильно.
Пожалуйста, подскажите мне мои ошибки. Я никогда раньше не создавал функции Makefile.

условные операторы не оцениваются внутри функции Makefile
Вроде, как бы, что-то вроде. Условные операторы анализируются и выполняются немедленно во время анализа make-файла. Таким образом, все, что появляется в теле функции, контролирует фактическое определение функции на основе оценки их состояния во время анализа определения функции. Они уже оказали весь свой эффект, прежде чем любые вызовы функции можно будет расширить.
Это означает, что ни ifeq не оценивается правильно, ни значение второго параметра не получается правильно.
Нет, это значит, что ifeq работает не так, как вы думали. В более общем плане функции GNU make работают иначе, чем вы, вероятно, думаете. make's не является языком сценариев. Makefile — это не сценарии. make правила не являются функциями, как и GNU make «функции», на самом деле. make функции, особенно определяемые пользователем, лучше характеризуются как параметризованные макросы.
В частности, ...
функция в Makefile, [которая] оценивает входные параметры в операторах ifeq
... невозможно в GNU make.
Не пытайтесь писать make-файлы, как если бы они были скриптами. В конечном итоге вы получите неидиоматические make-файлы, часто с серьезными проблемами. В частности, я чувствую себя комфортно, рекомендую вам вообще не использовать пользовательские функции, поскольку я сам никогда не находил в этом необходимости, а идея «функций» имеет тенденцию ставить человека в неправильное пространство.
В вопросе представлено недостаточно информации, чтобы рекомендовать, что вам следует делать вместо этого в вашем случае, но, предполагая, исходя из формы функции, я предполагаю, что либо создание нескольких правил, из которых вы сейчас пытаетесь использовать одно, либо выполнение какого-либо расширения на основе по целевым именам.
Мне не нужно было использовать функции, а только цель. Я использовал специальные условия $(if $(filter...)...) и т. д., чтобы это работало..
Вначале я оставлю в стороне вашу второстепенную проблему:
ни второе значение параметра не получено правильно.
У меня это не воспроизводится, когда я запускаю вызов make
$(call set_target_dts,virgo,soc_prototype)
с определением set_target_dts, которое вы опубликовали. Я получил:
Selected Target is:virgo soc_prototype
Makefile:24: *** Please select the correct target.... Stop.
как и должно быть, а не:
Selected Target is:virgo soc
Makefile:48: *** Please select the correct target.... Stop.
как ты это сделал. Мне кажется, там что-то не очень связанное с маркой.
И так далее
Вы неправильно понимаете, как расширяются define-переменные и как
вызовы функций расширены. «Пользовательская функция»
в GNU Make обычно представляет собой переменную с многострочным определением, заключенным в define varname и endef, с отложенным раскрытием вложенного тела, так что аргументы могут быть заменены параметрами $(n) в этом теле, когда конструкция вызова функции $(func,args,...)
расширенный1. make не распознает никакой «логики» в теле определения переменной при ее расширении. (Хотя make может распознать такую логику позже, если make сам воспользуется расширением).
Сначала вам следует внимательно прочитать Руководство по GNU Make: 3.7 Как make читает Makefile.
Поняв разницу между немедленным и отсроченным расширением,
обратите внимание, что схема, которая соответствует вашему определению set_target_dts:
define *immediate*
*deferred*
endef
Расширение тела определения откладывается до тех пор, пока не будет использована переменная - например. к
расширение вызова функций $(call foo,args...). Это правильный выбор. Вы увидите из документации
что теоретически вы могли бы адаптировать свое определение, скажем, к схеме:
define *immediate* :=
*immediate*
endef
Но если бы вы это сделали, то определение было бы нежизнеспособным для
вызовы функций, потому что определение будет заморожено со всеми $(n)
параметры пустые. Отложенное расширение необходимо, чтобы дать вам возможность
поставка args....
Имея это в виду, проработанный пример лучше всего прояснит то, что вы имеете.
заметил, и что заблуждается в определении set_target_dts.
Рассмотрим этот make-файл:
$ cat Makefile
define foo
ifeq (1,0)
$(info In condition 1 = 0)
$(error In condition 1 = 0)
else
$(info In condition 1 ~= 0)
$(info $$(1) = [$(1)])
echo "Parameter $$(1) = [$(1)]"
endif
endef
$(info $$(foo,bar) = <<<$(call foo,bar)>>>)
all:
Давайте просто запустим его:
$ make
In condition 1 = 0
Makefile:12: *** In condition 1 = 0. Stop.
Обратите внимание: тот факт, что вызов $(error ...) находился в пределах невыполнимого
условие ifeq (1,0) не помешало звонку $(error ...)
смертельно казнен.
Давайте удалим вызов ошибки, чтобы двигаться дальше. Я также помечу строки
в пределах определения foo, чтобы я мог говорить через них:
$ cat Makefile
define foo
ifeq (1,0) # A
$(info In condition 1 = 0) # B
else # C
$(info In condition 1 ~= 0) # D
$(info $$(1) = [$(1)]) # E
echo "Parameter $$(1) = [$(1)]" # F
endif # G
endef
$(info $$(foo,bar) = <<<$(call foo,bar)>>>)
all:
Мы знаем, что все отмеченные строки попадают в отложенное расширение. В нашем Makefile это произойдет через:
<<<$(call foo,bar)>>>
(Этого не произойдет в $$(foo,bar), поскольку это выражение экранировано.)
При расширении определения make работает следующим образом:
$(n) заменяется n-м аргументом, если таковой имеется, в противном случае — пустой строкой.$(name[...]) расширяется.$$... расширяется до $...$, сохраняется без изменений.Итак, вот что будет происходить построчно по мере расширения make$(call foo,bar):
$. Добавьте его в расширение без изменений.$(info ...) сейчас. Это действие теперь выводит ... на стандартный вывод.
и возвращает пустую строку. Добавьте эту пустую строку в расширение.$$(1) заменяется буквальным $(1). Несбежавший $(1)
заменен на bar. Полученная строка добавляется в расширение.Таким образом, окончательное расширение $(call foo,bar):
ifeq (1,0) # A
# B
else # C
# D
# E
echo "Parameter $(1) = [bar]" # F
endif # G
Давайте подтвердим это, запустив исправленный Makefile:
$ make
In condition 1 = 0
In condition 1 ~= 0
$(1) = [bar]
$(foo,bar) = <<< ifeq (1,0) # A
# B
else # C
# D
# E
echo "Parameter $(1) = [bar]" # F
endif # G>>>
make: Nothing to be done for 'all'.
Обратите внимание, что расширение:
$(info In condition 1 = 0) # B => prints "In condition 1 = 0"
был раскрепощен тем, что, по-видимому, находился в сфере неудовлетворительного
условие ifeq (1,0) и что в расширенном определении оно
похоже, даже не в этой области: единственный текст в этой области
это <empty_string># B.
По аналогии теперь вы должны понять, почему $(error ...) так называется.
мы удалили, было выполнено, несмотря на то, что оно находилось «в пределах области действия» ifeq (1,0).
В контексте расширения определения foo это условие
просто некоторый текст, который сам по себе не требует расширения и просто
добавлено расширение вызова функции. Звонок $(error ...) какой-то
больше текста, который требует расширения, и это немедленно прекращается
make с неудачей.
И в этом свете вы можете увидеть это в своем собственном случае,
$(call set_target_dts,virgo,soc_prototype)
выполнит:
$(info Selected Target is:$(1) $(2))
а также:
$(error Please select the correct target...)
независимо от какой-либо окружающей условной «логики make», поскольку в
контекст расширения вызова функции make не волнует, что
окружающий текст: здесь не используется никакая логика make.
Но...
Если вы собираетесь вызвать свою функцию в контексте рецепта,
тогда, конечно, имеет значение, какой будет текст расширения,
поскольку рецепты выполняются оболочкой. И
поскольку ваше определение set_target_dts содержит команды оболочки (echo), вы, очевидно, намереваетесь использовать его таким образом. Наши make-файлы на данный момент
также содержали команду echo, намекающую на то же намерение.
Давайте сделаем окончательную версию нашего Makefile, чтобы проверить это:
$ cat Makefile
define foo
ifeq (1,0) # A
$(info In condition 1 = 0) # B
else # C
$(info In condition 1 ~= 0) # D
$(info $$(1) = [$(1)]) # E
echo "Parameter $$(1) = [$(1)]" # F
endif # G
endef
$(info $$(foo,bar) = <<<$(call foo,bar)>>>)
all:
@echo "Building all"
$(call foo,bar)
$ make
In condition 1 = 0
In condition 1 ~= 0
$(1) = [bar]
$(foo,bar) = <<< ifeq (1,0) # A
# B
else # C
# D
# E
echo "Parameter $(1) = [bar]" # F
endif # G>>>
In condition 1 = 0
In condition 1 ~= 0
$(1) = [bar]
Building all
ifeq (1,0) # A
/bin/sh: 1: Syntax error: word unexpected (expecting ")")
make: *** [Makefile:15: all] Error 2
Здесь вы видите, что $(call foo,bar) в рецепте для цели all, как и предыдущий в <<<$(call foo,bar)>>>,
расширяется перед выполнением рецепта (до «Постройки всего»). Это
должно быть, не так ли?
Затем расширение передается оболочке для выполнения и, конечно же,
оболочка захлебывается в строке A: ifeq (1,0) не имеет смысла для оболочки.
Если определение set_target_dts должно быть таким, что
после функционального расширения с аргументами получится команда оболочки или выражение
то вы должны написать это определение так, чтобы после разложения вы ничего не получили
а команда или выражение оболочки. Make-logic не «выполнит свою работу и исчезнет» в
контекст расширения определения переменной, предназначенный для выполнения рецепта. Это будет
не выполняет свою работу и не исчезнет.
1 @MadScientist правильно прокомментировал: «Обратите внимание: определяемая пользователем функция в make не обязательно должна быть многострочной или создаваться с помощью define. Любой макрос можно использовать с $(call ...). Обычно любой макрос, ссылающийся на хотя бы одна переменная (например, $1, $2) является определяемой пользователем функцией. Вы можете даже иметь нулевые аргументы, но имеет смысл использовать n-арные макросы только тогда, когда n > 0: $(call foo) допустимо, но является то же самое, что и просто $(foo)».
Отметим, что определяемая пользователем функция в make не обязательно должна быть многострочной или создаваться с помощью define. С $(call ...) можно использовать любой макрос. Обычно любой макрос, который ссылается хотя бы на одну переменную (например, $1, $2), является определяемой пользователем функцией. Вы можете даже иметь нулевые аргументы, но имеет смысл использовать n-арные макросы только там, где n > 0: $(call foo) допустимо, но это то же самое, что и просто $(foo).
В качестве последнего комментария я скажу, что если вы хотите использовать операции препроцессора make, такие как ifeq и т. д., которые хранятся в переменной, вам нужно делать это в контексте функции $(eval ...). Но учтите, что, как вы заметили, вам нужно «скрыть» функции, которые будут расширены с помощью $(call ...), чтобы они не расширялись до $(eval ...), поэтому $$(error foo) и т. д. Лично я думаю, что это слишком сложно, и я уверен, что есть более простые способы делайте все, что хочет ОП, но, к сожалению, они не описали требования, а только неудачную попытку.
@MadScientist принял к сведению — включил ваш первый комментарий в сноску к сообщению.
Пожалуйста, покажите вызов функции (
$(call...)) в реальном контексте. Находится ли он вне каких-либо правил, как кажется здесь? Или это используется в рецепте? Или...? В make-файлах контекст имеет решающее значение.