Conditional Functions

make 에는 조건문에 사용할 수 있는 Conditional Directives 가 있는데 이것의 단점은 recipe 가 실행되기 전에 global 영역에서 처리가 완료된다는 것입니다. 따라서 recipe 가 실행될 때 automatic 변수들과 함께 사용하려면 여기서 소개되는 함수를 사용해야 합니다. 참, 거짓은 condition 값이 empty 이면 거짓이 되고 그 외는 참이 됩니다. 존재하지 않는 변수는 empty 값으로 취급됩니다.

$( if condition , then-part [ , else-part ] )

다음 예제는 automatic 변수에 해당하는$< 파일이 $(GROUP_A) 에 존재하면 (1) 번이 실행되고 그렇지 않으면 (2) 번이 실행됩니다. recipe 에서 작성하는 것이므로 if 함수는 tab 문자로 시작해야 합니다. 그래야 선택된 명령 앞에 tab 문자가 붙게 됩니다.

$(OBJS_DIR)/%.o : %.c
    $(if $(filter $<, $(GROUP_A)), \
        $(CC) -c $(CFLAGS) -DFOO -o $@ $<, \       ---- (1)
        $(CC) -c $(CFLAGS) -DBAR -o $@ $< )        ---- (2)

다음은 리눅스 커널 소스에서 볼 수 있는 활용 예제입니다.

# linux/scripts/Kbuild.include 참조

ifeq ($(KBUILD_VERBOSE),2)
why =                                                                        \
    $(if $(filter $@, $(PHONY)),- due to target is PHONY,                    \
        $(if $(wildcard $@),                                                 \
            $(if $(newer-prereqs),- due to: $(newer-prereqs),                \
                $(if $(cmd-check),                                           \
                    $(if $(cmd_$@),- due to command line change,             \
                        $(if $(filter $@, $(targets)),                       \
                            - due to missing .cmd file,                      \
                            - due to $(notdir $@) not in $$(targets)         \
                         )                                                   \
                     )                                                       \
                 )                                                           \
             ),                                                              \
             - due to target missing                                         \
         )                                                                   \
     )

echo-why = $(call escsq, $(strip $(why)))
endif

$( or condition1 [ , condition2 [ , condition3...] ] )

or 함수는 condition 들이 차례로 실행되는데 그중에서 처음 non-empty 값이 발생하면 true 가 되어 중단되고 반환값으로 사용됩니다.

# 첫 번째 condition 은 empty 로 false 가되고 두 번째 condition 이 true 가된다.
aa :=
bb := bar
cc := zoo
$(info $(or $(aa), $(bb), $(cc)))

#######  실행 결과  #######

bar     # 두 번째 condition 값인 $(bb) 가 or 함수의 반환값이 된다

$( and condition1 [ , condition2 [ , condition3...] ] )

and 함수에서 반환값이 발생하려면 주어진 모든 condition 값들이 non-empty 로 true 가 되어야 합니다.
그중에서 마지막 값이 and 함수의 반환값으로 사용됩니다. 만약에 condition 값이 하나라도 empty 가되어 false 가되면 함수 반환값은 empty 가 됩니다.

# 세 개의 condition 이 모두 non-empty 로 true 이다.
aa := foo
bb := bar
cc := zoo
$(info $(and $(aa), $(bb), $(cc)))

#######  실행 결과  #######

zoo           # 마지막 condition 값인 $(cc) 가 and 함수의 반환값이 된다.
--------------------------------------------------------------------------

# 두 번째 condition 값이 empty 로 false 이다.
aa := foo
bb :=
cc := zoo
$(info xxx$(and $(aa), $(bb), $(cc))yyy )

#######  실행 결과  #######

xxxyyy        # and 함수는 condition 이 하나라도 false 가 되면 반환값은 empty 이다.
--------------------------------------------------------------------------

res := $(if $(and \
    $(filter command,$(word 1,$(origin VERBOSE))), \
    $(filter line,$(word 2,$(origin VERBOSE)))), \
    yes,no)

$(info $(res))

#######  실행 결과  #######

sh$ make VERBOSE=1
 yes

공백 사용시 주의할 점

기본적으로 조건 함수에서 condition 부분에서 공백은 값으로 인식되지 않습니다. 하지만 if 문에서 반환값에 사용되는 then, else 파트 부분에서는 공백도 하나의 값으로 반환되므로 주의해야 하고 필요에 따라서 strip 함수를 사용해야 합니다. 다음을 보면 $(bb) 값이 empty 이므로 아무런 메시지도 표시되어서는 안되지만 변수 앞에 있는 공백이 값으로 인식되어 두 경우 모두 메시지가 출력되는 것을 볼 수 있습니다.

aa := foo                                  aa := foo
bb :=                                      bb :=
                                           # strip 함수 사용
$(if $(if $(aa), $(bb)),$(info 11111))     $(if $(strip $(if $(aa), $(bb))),$(info 11111))

ifneq "$(if $(aa), $(bb))" ""              ifneq "$(strip $(if $(aa), $(bb)))" ""
$(info 22222)                              $(info 22222)
endif                                      endif

#######  실행 결과  #######                  #######  실행 결과  #######

sh$ make                                   sh$ make
11111
22222

기본적으로 condition 부분에서는 공백이 값으로 인식되지 않습니다.

aa :=
bb :=
cc :=

$(info $(if $(aa) ,yes,no))
결과 : no

$(info $(if $(and $(aa) , $(bb) , $(cc) ),yes,no))
결과 : no

$(info $(if $(or $(aa) , $(bb) , $(cc) ),yes,no))
결과 : no

다음과 같은 경우는 공백문자가 직접 변수에 값으로 설정되어 condition 에서 참이 됩니다.

AA := $(empty) $(empty)

$(info $(if $(AA),yes,no))
결과 : yes

--------------------------------------

foo = $2
$(info $(if $(call foo,1, ,3),yes,no))
결과 : yes

$(info $(if $(call foo,1,,3),yes,no))
결과 : no