Отменить makefile, если переменная не установлена

Как я могу прервать выполнение make/makefile на основе переменной makefile, не установленной/значащей?

Я придумал это, но работает только в том случае, если вызывающий объект явно не запускает цель (т.е. работает только make).

ifeq ($(MY_FLAG),)
abort:   ## This MUST be the first target :( ugly
    @echo Variable MY_FLAG not set && false
endif

all:
    @echo MY_FLAG=$(MY_FLAG)

Я думаю, что что-то вроде этого было бы хорошей идеей, но ничего не нашел в руководстве:

ifndef MY_FLAG
.ABORT
endif

Ответ 1

Используйте функцию error:

ifndef MY_FLAG
$(error MY_FLAG is not set)
endif

Обратите внимание, что строки не должны иметь отступ. Точнее, никакие вкладки не должны предшествовать этим строкам.

Общее решение

Если вы собираетесь тестировать множество переменных, стоит определить вспомогательную функцию для этого:

# Check that given variables are set and all have non-empty values,
# die with an error otherwise.
#
# Params:
#   1. Variable name(s) to test.
#   2. (optional) Error message to print.
check_defined = \
    $(strip $(foreach 1,$1, \
        $(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
    $(if $(value $1),, \
      $(error Undefined $1$(if $2, ($2))))

И вот как его использовать:

$(call check_defined, MY_FLAG)

$(call check_defined, OUT_DIR, build directory)
$(call check_defined, BIN_DIR, where to put binary artifacts)
$(call check_defined, \
            LIB_INCLUDE_DIR \
            LIB_SOURCE_DIR, \
        library path)


Это приведет к ошибке:

Makefile:17: *** Undefined OUT_DIR (build directory).  Stop.

Проверка по конкретным объектам

Также можно расширить решение, чтобы можно было потребовать переменную, только если вызывается определенная цель.

$(call check_defined, ...) изнутри рецепта

Просто переместите чек в рецепт:

foo :
    @:$(call check_defined, BAR, baz value)

Ведущий знак @ отключает команду эхо-сигнала, а : - действительная команда, оболочка no-op stub.

Отображение имени цели

Функция check_defined может быть улучшена для вывода целевого имени (предоставляется через переменную [email protected]):

check_defined = \
    $(strip $(foreach 1,$1, \
        $(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
    $(if $(value $1),, \
        $(error Undefined $1$(if $2, ($2))$(if $(value @), \
                required by target `[email protected]')))

Итак, теперь неудавшаяся проверка производит хорошо отформатированный вывод:

Makefile:7: *** Undefined BAR (baz value) required by target `foo'.  Stop.

check-defined-MY_FLAG специальная цель

Лично я использовал бы простое и прямое решение выше. Однако, например, этот ответ предлагает использовать специальную цель для выполнения фактической проверки. Можно попытаться обобщить это и определить цель как правило неявного шаблона:

# Check that a variable specified through the stem is defined and has
# a non-empty value, die with an error otherwise.
#
#   %: The name of the variable to test.
#   
check-defined-% : __check_defined_FORCE
    @:$(call check_defined, $*, target-specific)

# Since pattern rules can't be listed as prerequisites of .PHONY,
# we use the old-school and hackish FORCE workaround.
# You could go without this, but otherwise a check can be missed
# in case a file named like `check-defined-...` exists in the root 
# directory, e.g. left by an accidental `make -t` invocation.
.PHONY : __check_defined_FORCE
__check_defined_FORCE :

Использование:

foo :|check-defined-BAR

Обратите внимание, что check-defined-BAR указан как только для заказа (|...).

Плюсы:

  • (возможно) более чистый синтаксис

Минусы:

Я считаю, что эти ограничения можно преодолеть, используя eval magic и вторичное расширение, хотя я не уверен, что это того стоит.

Ответ 2

Используйте функцию оболочки test:

foo:
    test $(something)

Использование:

$ make foo
test 
Makefile:2: recipe for target 'foo' failed
make: *** [foo] Error 1
$ make foo something=x
test x