Условни променливи (ifeq) във foreach за изрични правила за създаване

Не мога да разбера правилния синтаксис за използване на условно дефинирана променлива с цикъл foreach в GNU Make 3.81.

Прост makefile

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 променливите са глобално дефинирани и всички правила се извикват едва след като целият make файл е бил анализиран. Трикът 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 изгражда цели и решава да изпълни рецептата. Така че вашият makefile може да бъде написан еквивалентно като:

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)"

Сега можете да видите защо получавате поведението, което правите.

Има много начини да "поправите" това, кой от тях е най-подходящ зависи от това какво наистина искате да направите във вашия истински makefile. Много проста опция е да използвате специфични за целта променливи, като това:

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
Специфичните за целта променливи работят! FYI: целта ми е да поставя допълнителен флаг в командата, когато $(T) = A, но не и когато $(T) = B или C. - person dlove; 30.07.2015

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

person Yaal Kagam    schedule 25.07.2015