Условные переменные (ifeq) в foreach для явных правил make

Я не могу понять правильный синтаксис для использования условно определенной переменной с циклом foreach в GNU Make 3.81.

Простой make-файл

SET := A B C

define da_loop

ifeq ($(S), A)
    T := equals_A
else
    T := not_equals_A
endif

out_$(S):
    echo "$(S) $$(T)"

endef

$(foreach S, $(SET), $(eval $(call da_loop, $S)))

Ожидаемый результат:

$ make out_A out_B out_C
echo "A equals_A"
A equals_A
echo "B not_equals_A"
B not_equals_A
echo "C not_equals_A"
C not_equals_A

Фактический результат:

$ make out_A out_B out_C
echo "A not_equals_A"
A not_equals_A
echo "B not_equals_A"
B not_equals_A
echo "C not_equals_A"
C not_equals_A

Изменение «eval» на «info» выглядит так, как будто это должно работать:

ifeq (A, A)
        T := equals_A
else
        T := not_equals_A
endif

out_A:
        echo "A $(T)"


ifeq (B, A)
        T := equals_A
else
        T := not_equals_A
endif

out_B:
        echo "B $(T)"


ifeq (C, A)
        T := equals_A
else
        T := not_equals_A
endif

out_C:
        echo "C $(T)"

Я перепробовал все комбинации дополнительных "$", кавычек, = vs :=, о которых я только мог подумать, но ни одна из них еще не сработала. Любые идеи?


person dlove    schedule 25.07.2015    source источник


Ответы (2)


Проблема не имеет ничего общего с циклами, eval и т. д. Проблема проще и более фундаментальна: в make переменные глобально определены, и все правила вызываются только после того, как весь makefile будет проанализировано. Трюк info на самом деле ДЕЙСТВИТЕЛЬНО показал вам проблему. Давайте немного упростим:

T := equals_A
out_A:
        echo "A $(T)"

T := not_equals_A
out_B:
        echo "B $(T)"

T := not_equals_A
out_C:
        echo "C $(T)"

Проблема в том, что все присвоение переменных происходит при чтении make-файла, но расширение рецептов происходит намного позже, когда make создает цели и решает запустить рецепт. Таким образом, ваш make-файл может быть эквивалентно написан так:

T := equals_A
T := not_equals_A
T := not_equals_A

out_A:
        echo "A $(T)"
out_B:
        echo "B $(T)"
out_C:
        echo "C $(T)"

Теперь вы можете понять, почему у вас такое поведение.

Есть множество способов «исправить» это, и какой из них наиболее подходит, зависит от того, что вы действительно хотите сделать в своем реальном make-файле. Очень простой вариант — использовать целевые переменные, например это:

SET := A B C

define da_loop

ifeq ($(S), A)
    out_$(S): T := equals_A
else
    out_$(S): T := not_equals_A
endif

out_$(S):
        echo "$(S) $$(T)"

endef

$(foreach S, $(SET), $(eval $(call da_loop, $S)))

Используя это, вы указали, что каждая переменная T привязана к области действия этой конкретной цели, и каждая цель может иметь другое значение. Другими вариантами могут быть сконструированные имена переменных или просто расширение значения непосредственно в рецепте с помощью функции $(if ...), а не установка его как отдельной переменной.

person MadScientist    schedule 25.07.2015
comment
Целевые переменные работают! К вашему сведению: моя цель — добавить в команду дополнительный флаг, когда $(T) = A, но не когда $(T) = B или C. - person dlove; 30.07.2015

image
ifeq конечно имеет другой синтаксис (с кавычками),
где внутри цитируемого текста не выполняется удаление пробелов!

person Yaal Kagam    schedule 25.07.2015