Conditional Directives

조건 지시자는 C/C++ 언어의 전처리기 에서처럼 특정 코드를 조건에 따라 포함하거나 제외하고자 할 때 사용합니다. makefile 의 모든 부분에서 적용할 수 있으나 recipe 가 실행되기 전에 처리가 완료되므로 recipe 실행 중에 처리를 위해서는 사용할 수 없습니다. 기본적으로 ifxxxx ~ else ~ endif 형식을 가지는데 else 지시자는 옵션으로 사용되지 않을 경우 제외할 수 있습니다.

recipe 실행 중에 automatic 변수와 함께 조건문을 사용하려면 Conditional built-in 함수를 사용합니다.

ifeq , ifneq . . . else . . . endif

값 비교에는 다음과 같은 3 가지 형식을 사용할 수 있습니다. quotes 을 사용하는 형식은 quotes 이 중첩될 경우 오류가 발생할 수 있으므로 주의해야 합니다.

ifeq (arg1,arg2)             ifneq (arg1,arg2)
ifeq "a" "b"                 ifneq "a" "b"
ifeq 'a' 'b'                 ifneq 'a' 'b'

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

# shell 함수에서 사용하는 quotes 과 중첩되어 오류가 발생한다.
ifeq "$(shell echo "foo bar")" "foo bar"
$(info 11111 )
endif
실행결과: Makefile:1: *** unterminated call to function 'shell': missing ')'.  Stop.

# 이때는 괄호형을 사용하거나 변수에 대입해서 사용하면 됩니다.
ifeq ($(shell echo "foo bar"),foo bar)
$(info 11111 )
endif
실행결과: 11111

( ) 괄호형을 사용할 때는 가운데 공백은 값에 포함되지 않지만 양끝에 공백은 값에 포함되므로 주의해야 합니다. 따라서 다음 세 번째 예제의 경우 실제 값은 " foo""foo " 가 되어 거짓이 됩니다.

$ make -f- <<\@                $ make -f- <<\@                 $ make -f- <<\@
ifeq (foo,foo)                 ifeq (foo  ,   foo)             ifeq (  foo,foo  )
  $(info equal)                  $(info equal)                   $(info equal)
else                           else                            else
  $(info not equal)              $(info not equal)               $(info not equal)
endif                          endif                           endif
@                              @                               @

equal                          equal                           not equal

ifdef , ifndef . . . else . . . endif

ifdef, ifndef 지시자는 flavor, origin 함수에서 사용되는 undefined 상태를 체크하는 것이 아니고 값이 empty 인지 아닌지를 체크합니다. 따라서 변수가 존재하나 값이 empty 일 경우와 undefined 상태일 경우 모두 ifndef 에서는 참이 됩니다.

ifdef variable                    ifndef variable

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

AA :=    

ifndef AA         # 변수가 존재하지만 empty 이므로 참이된다.
$(info AA is not defined or empty )
endif

#######  실행 결과  ######
AA is not defined or empty

and , or 연산

조건 지시자에는 기본적으로 and, or 연산자가 없습니다. 따라서 다음과 같이 사용해야 합니다.


ifeq (arg1,arg2)              ---- (1)
ifeq (arg1,arg2)              ---- (2)
    # (1) and (2) 가 참이면 실행
endif
endif

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

ifeq (arg1,arg2)              ---- (1)
    # (1) 이 참이면 실행
else ifeq (arg1,arg2)         ---- (2)
    # (1) 이 거짓이고 (2) 는 참이면 실행
else
    # (1), (2) 모두 거짓이면 실행
endif

strip 함수의 사용

if 함수의 condition 부분이 아닌 반환값에 사용되는 then, else 파트 부분에서는 공백도 하나의 값으로 인식됩니다. 따라서 \ 문자를 이용해 개행을 해서 작성할때는 반드시 strip 함수를 사용해야 합니다.

aa := foo                               aa := foo                                 
bb :=                                   bb :=
                                        # strip 함수 사용                            
ifneq "$(if $(aa), $(bb))" ""           ifneq "$(strip $(if $(aa), $(bb)))" ""
$(info 1111111111)                      $(info 1111111111)
endif                                   endif

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

$ make                                  $ make
1111111111                              $
------------------------------------------------------------------------------

# if 함수의 then, else 파트 부분에 공백이 있어서 ifneq 조건 지시자는 무조건 참이 된다.
ifneq "$(if $(MAKECMDGOALS), \
            $(filter all clean all.% clean.%,$(MAKECMDGOALS)), \
            $(filter all clean all.% clean.%,$(.DEFAULT_GOAL)))" ""

# 값으로 인식되는 공백을 제거하기 위해 strip 함수를 사용해야 한다.
ifneq "$(strip $(if $(MAKECMDGOALS), \
               $(filter all clean all.% clean.%,$(MAKECMDGOALS)), \
               $(filter all clean all.% clean.%,$(.DEFAULT_GOAL))))" ""

사용 예제 )

1 . recipe 에서 사용할 경우

# recipe 에서 실행되므로 shell 명령을 작성할 때는 앞에 tab 문자를 붙입니다.
prog : $(OBJS)
ifeq "$(MYVAR)" "foo"
    $(CC) $+ $(libs_for_foo) -o $@    # tab
else
    $(CC) $+ $(normal_libs) -o $@
endif

2 . global 영역에서 사용할 경우

ifeq "$(MYVAR)" "foo"

prog : foo.o bar.o zoo.o
    $(CC) $+ -o $@

else

prog : xxx.o yyy.o zzz.o
    $(CC) $+ -o $@

endif