make 변수 문법

출처 : http://blog.daum.net/english_100/10

6. 변수 사용법

 

변수란 문자열을 함유하고 있는 makefile 내에 정의된 이름이다. 이 값은 타깃이나 prerequisite, recipe 등 makefile의 다른 부분에서 치환되어 쓰이게 된다. 변수나 함수는 recipe 에서만 제외하고 makefile을 읽어들일 때 '='의 오른쪽 부분이나 define 지시자의 몸통부분의 값으로 펼쳐진다.

변수에는 파일이름, 컴파일러에게 건네줄 옵션, 실행할 프로그램, 검색할 디렉토리 등등 상상할 수 있는 여러가지가 담길 수 있다.

변수이름은 ':', '#', '='등이 포함되지 않은 문자열로 만들어지며 알파벳문자, 숫자, 밑줄이외의 문자는 피해야 한다. 왜냐하면 그런 문자는 특수한 의미를 갖을 수 있으며 또한 어떤 쉘은 이런 문자를 하위 make에 전달하지 못한다.

변수명은 대소문자가 구분되므로 'foo', 'FOO', 'Foo'는 모두 다른 이름이 된다.

 

6.1 변수 참조의 기초

 

변수값을 치환해 사용하기 위해서는 $(foo), ${foo} 처럼 달러 기호와 괄호로 묶인 변수명을 써주면 된다.

변수참조는 타깃, prerequisite, recipe, 지시자, 다른 변수값내 등 어디든 쓰일 수 있다. 다음 예를 보자 :

objects = program.o foo.o utils.o

program : $(objects)

cc -o program $(objects)

$(objects) : defs.h

 

여기서 변수는 파일이름들을 함유하고 있으며 이는 곧바로 치환된다. 다음 예는:

foo = c

prog.o : prog.$(foo)

$(foo)$(foo) -$(foo) prog.$(foo)

 

이 예는 모든 foo가 'c'로 치환되어 정상적으로 작동한다. 하지만 이런식으로 makefile을 작성해서는 안된다!

$x 형태의 달러기호와 한개의 문자로 일루어진 형태는 역시 x라는 변수를 참조하지만 이런 용례는 바람직하지 않다. 왜냐하면 자동변수와 헤깔릴 수 있기때문

 

6.2 변수의 두가지 풍미

 

GNU make에서 변수가 값을 할당받는 방법은 두가지가 있다. 이를 변수의 두가지 풍미라 부른다.

변수의 첫번째 풍미는 순환적으로 펼쳐지는 변수로 '='나 define 지시자를 이용해 정의된다. 예를 들어 :

foo = $(bar)

bar = $(ugh)

ugh = Huh?

all : ; echo $(foo)

 

이 예는 펼쳐지고 또 펼쳐져 결국 'Huh?'를 출력한다.

이 형태는 장 단점이 존재하는데 장점은 :

CFLAGS = $(include_dirs) -O

include_dirs = -lfoo -lbar

 

'CFLAGS'가 recipe에서 사용될 때 '-lfoo -lbar -O'로 원하는 대로 펼쳐진다. 하지만 이 방식은 변수의 끝에 다른 값을 첨부할 수가 없다. 예를 들어 :

CFLAGS = $(CFLAGS) -O

 

왜냐하면 이는 변수 펼침에 있어 무한루프를 형성하기 때문이다 (make가 무한루프를 감지하고 에러를 출력함). 또다른 단점은 변수 정의 내에서 참조된 함수는 변수가 펼쳐질 때마다 실행된다는 것이다. 이는 make를 느려지게 할 뿐 아니라 엉뚱한 결과를 만들어 내게 된다.

이러한 순환 펼침 변수의 문제점을 피하기위해 다른 풍미를 가진 변수 즉 단순 펼침 변수가 생겨났다. 단순 펼침 변수는 ':='를 이용해 정의되며 그 값은 텍스트로 편쳐진 결과값이 된다. 이 변수는 변수가 정의된 바로 그때의 값을 갖는다. 예를 들어 :

x := foo

y := $(x) bar

x := later

 

다음과 동일 하다.

y := foo bar

x := later

 

쉘과 연결해서 ':='의 사용을 설명하는 좀 더 복잡한 예를 살펴보자. 여기에는 MAKELEVEL 변수가 사용되는데 이는 각 레벨에서 레벨로 내려가며 그 값이 변화한다.

     ifeq (0,${MAKELEVEL})
     whoami    := $(shell whoami)
     host-type := $(shell arch)
     MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
     endif

 

':='를 사용하는 이점은 다음과 같이 디렉토리를 따라 내려갈 때 나타난다 :

${subdirs}

${MAKE} -C $@ all

 

단순 펼침 변수는 일반 프로그래밍 언어에서처럼 작동되기 때문에 복잡한 makefile에 대해 훨씬 예측가능성을 높여준다. 변수를 재정의할 수 있게 해주고 펼침 함수를 더욱더 효율적으로 사용할 수 있게 해준다.

 

중략

 

변수값 할당을 위한 또 다른 연산자로 '?='이 있는데 이를 조건 변수 할당 연산자라고 부른다. 그 이유는 변수가 아직 정의되지 않았을 때에만 이 연산이 효력을 발휘할 수 있기 때문이다. 예를 들어 :

FOO ?= bar

 

는 다음과 완전히 동일하다.

ifeq ( $(origin FOO), undefined)

FOO =bar

endif

 

6.3  변수 참조에 대한 또 다른 특성

 

6.3.1 치환 참조

 

치환 참조 (Substitution References)는 변수값을 원하는 값으로 바꾸는 것으로 '$(var: a=b)'형태를 갖는다. 의미는 var라느 변수값의 끝에 붙어있는 'a'를 'b'로 바꾸라는 말이다. 맨 끝에 붙어있는 'a'만 바뀔 뿐 중간에 나타나는 'a'에는 변화가 없다. 다음 예는 :

foo := a.o b.o c.o

bar := $(foo: .o=.c)

 

bar의 값은 'a.c b.c c.c'로 바뀐다.

치환참조는 patsubst 펼침 함수의 단축형이라 할 수 있다. 위 예를 달리 표시하면 다음과 같은데 이는 $(patsubst a,b,$(bar))와 동일하다 :

foo := a.o b.o c.o

bar := $(foo:%.o=%.c)

 

6.3.2 계산된 변수명

 

계산된 변수명은 복잡한 makefile 프로그래밍에 필요한 개념으로 많은 경우 쓰이진 않지만 꼭 알고 싶으면  http://www.gnu.org/software/make/manual/make.html#Advanced (Computed bariable names) 참조

 

6.4 변수가 값을 할당 받는 방법

 

- make가 실행될 때 옵션을 이용해 할당받을 수 있다.

- makefile 내에서 변수값을 명기함으로써 할당받는다.

- 환경 변수도 make 변수가 될 수 있다.

- 규칙 내에서 자동 변수는 새로운 값을 할당 받는다.

- 몇몇 변수는 애초에 상수를 할당 받는다.

 

6.5 변수값 설정하기

 

makefile 내에서 변수값을 설정하는 방법은 '='나 ':='을 이용해서 이루어진다. 예를 들어 :

objects = main.o foo.o bar.o utils.o

 

이 예는 objects라는 이름의 변수에 값을 할당하는데 여기서 변수명 앞뒤의 공백이나 '=' 직후의 공백은 무시된다.

변수는 '='에 위해 값이 할당되면 순환펼침 변수, ':='에 의하면 단순펼침 변수라 하는데 이는 변수값 펼침의 시기에 차이가 있다.

변수 길이는 한계가 없으며 변수 길이가 길면 backslash-newline을 이용해 여러 행으로 나누면 makefile을 읽기에 편해진다.

변수가 존재하지 않았으면 값을 할당하고 존재하면 아무일도 않게 하려면 '?='를 사용하며 이는 origin 함수 기능과 동일하다.

FOO ?= bar

 

ifeq ($(origin FOO), undefined)

FOO =bar

endif

 

는 동일하다.

 

6.6 변수에 값 덧붙이기

 

이미 값이 주어진 변수에 다른 값을 덧붙이는 것이 가능한데 이는 '+='를 이용해 이루어진다.

objects += another.o

 

이는 원래의 값에 another.o를 덧붙이는 것이다. 다음 예는 :

objects = main.o foo.o bar.o utils.o

objects += another.o

 

의 결과로 objects는 'main.o foo.o bar.o utils.o another.o'가 된다.

위의 예는 다음과 비슷한다 :

objects = main.o foo.o bar.o utils.o

objects = $(objects) another.o

 

변수가 아직 정의되지 않은 경우 '+='는 '='와 같이 작동한다. 즉 순환펼침 변수를 정의하는 것이다. 하지만 이미 정의되어 있는 경우에는 그 변수가 애초에 어떻게 정의되었는지에 따라 변수 종류가 달라진다.

 

'+='를 통해 변수 값을 첨가할 때 make는 원래 값에 다른 값을 포함시키는 형태를 취한다. 애초에 변수가 ':='(단순펼침)를 통해 정의되었다면 '+=' 또한 ':=' 방식으로 덧붙여지는데 이는 값이 덧붙여지기 전에 펼쳐짐을 의미한다. 즉 :

variable := value

variable += more

 

는 다음과 완전히 동일하다.

variable := value

variable := $(variable) more

 

이와는 달리 변수가 애초에 '='(순환펼침)에 의해 정의되었다면 조금 다른 방식으로 처리된다. 순환펼침 변수가 정의 될었을 때 이 값은 즉시 펼쳐지는 것이 아니라 나중에 그 변수에 대해 참조될 때 펼쳐진다.  즉 :

variable = value

variable += more

 

는 temp라는 변수가 실제로 정의된게 아니라는 것만 빼면 다음과 같다고 할 수 있다.

temp = value

variable = $(temp) more

 

중요한 문제는 처음 정의한 변수에 다른 변수 참조가 포함되어 있을 때 생긴다. 예를 들어 :

CFLAGS = $(include) -0

...

CFLAGS += -pg # enable profiling

 

첫행의 CFLAGS가 다른 변수 참조 $(inlcude)를 포함하고 있다. '='를 사용했다는 것은 이 변수가 순환펼침 변수이고 make가 이 변수의 정의를 처리할때는 '$(include) -0'가 아직 펼쳐지지 않음을 의미한다. 그러므로 include는 CFLAGS가 이후에 어디선가 참조되기 전까지만 정의되면 문제가 없다. 여기서 만약 다음과 같이 값을 추가하면 어떻게 될까?

CFLAGS := $(CFLAGS) -pg # enable profiling

 

이는 비슷하기는 하지만 우리가 원하는것과는 다르다. ':='를 사용했으므로 CFLAGS는 단순펼침 변수가 되었고 이는 make로 하여금 당장 '$(CFALGS) -pg'를 펼치도록 한다. 만약 include가 아직 정의 되어있지 않다면 이 변수는 단지 '-0 -pg' 값을 갖게 될것이다. 하지만 '+='를 이용하면 CFLAGS는 아직 펼쳐지지 않은 상태를 유지하다가 이후에 include가 정의되면 정상적으로 이용될 수 있다.

 

6.7 override 지시자

 

만약 명령행에서 명령 옵션으로 변수값을 할당하면 makefile 내의 정상적인 변수값 할당은 모두 무시된다. 만약에 명령행 옵션으로 변수가 설정되었어도 makefile 내에서 다시 할당해서 사용하고 싶은 경우가 있는데 이때 override 지시자를 사용한다.

override variable = value

또는

override variable := value

 

명령행에서 정의된 변수에 다른값을 첨부하고 싶다면 다음과 같이 사용할 수 있다.

override variable += more text

 

예를 들어 C 컴파일러를 실행할 때 명령행 변수정의을 통해 그때 그때 원하는 옵션을 사용하지만  '-g' 옵션만은 항상 사용하길 원한다면 다음과 같이 정의 해 주면된다 :

override CFLAGS += -g

 

6.8 복수 행 변수 정의하기

 

또하나의 변수값 할당 방법은 define 지시자를 사용하는것이다. 이 방법은 newline 문자를 허용하기 때문에 canned 명령이나 eval 함수 사용을 위한 makefile 문법 섹션에 유용하다.

define 지시자는 다음과 같은 형태로 사용된다 :

define variable =

...

endef

 

일반적인 변수값은 newline 문자를 포함할 수 없으나 define 지시자 내에 있는 newline 문자는 변수값의 일부가 된다. 하지만 마지막 endef 직전의 newline 은 무시된다.

다음의 정의가 :

define two-lines =

echo foo

echo $(bar)

endef

 

실제 recipe에 사용되면 기능적으로 다음과 동일하다 :

two-lines = echo foo; echo $(bar)

 

세미콜론에 의해 분리된 명령은 서로 다른 쉘 명령으로 행동한다. 하지만 서로 다른 행으로 분리된 명령은 독립적인 두개의 쉘에 의해 기동됨을 명심하자.

명령행에 의해 주어진 변수에 우선하는 변수를 정의하기 위해 override 지시자를 사용한다.

override define two-lines =

foo

$(bar)

endef

 

6.9 변수 정의 무효화하기

 

변수를 지우기 위해서는 빈문자를 할당하면 된다. 빈문자 변수의 경우 정의되지 않았건 정의되었건 펼침에 있어서의 결과는 동일하다. 하지만 flavor 함수나 origin 함수를 사용했을 때 정의되지 않은 경우와 빈문자로 세팅된 경우는 다른 결과를 만든다.

foo := foo

bar = bar

undefine foo

undefine bar

$(info $(origin foo))

$(info $(flavor bar))

 

이 예는 모두 'undefined'를 프린트한다.

명령행 변수를 undefine하고 싶다면 override 지시자를 사용하면 된다.

override undefine CFLAGS

 

6.10 환경 변수

 

make가 사용하는 변수 중에는 환경에서 건너온것들도 있다. 환경변수는 make가 시작할 때 넘겨 받게 되는데 만약 makefile 내에나 명령행을 통해 같은 이름의 면수가 명기된다면 이들이 환경 변수에 우선한다. 하지만 '-e' 플래그를 사용한 경우에는 환경변수가 이들에 우선하게 된다. 실제 사용은 안하는 편이 좋음.

예를 들어 환경에서 CFLAGS 변수를 세팅한 후 대부분의 makefile 내의 모든 C 컴파일러가 이를 사용하게 할 수 있다. make 가 recipe를 실행할 때 makefile 내에 정의된 변수들은 각 쉘의 환경속으로 포함된다. 이 변수를 하위 make로 전달 할 수가 있는데 기본적으로 환경변수와 명령행에서 정의된 변수만이 순환 쉘이 기동할 때 전달된다. 다른 변수에 대해서는 export 를 통해 전달할 수 있다.

환경변수의 다른 용도의 사용은 추천할 만하지 않다. 왜냐면 makefile이 외부에서 들어온 환경변수에 너무 좌지우지되는건 바람직하지 않기 때문이다. 환경변수라는 것은 유저들 마다  다를 수 있어 같은 makefile에 대해 다른 결과를 야기할 수도 있다.

비슷한 문제가 SHELL 변수를 사용할 때 생긴다. 이 일반적으로 환경내에서 사용자기 사용중인 쉘을 가리킨다. 그러므로 make의 수행에 영향을 주는 이런 변수를 외부에서 받아들이는 것은 바람직하지 않다. 그래서 make는 특별한 방법으로 SHELL 환경 변수를 다룬다.

 

6.11 타깃에 한정된(target-specific) 변수값

 

일반적으로 변수값은 어디에나 효력이 미치지만 그 예외로서 자동 변수가 있다. 또 하나의 예외는 타깃-한정 변수값인데 이는 타깃에 따라 같은 변수의 값이 다르게 되는것을 말한다. 타깃-한정 변수값은 다음과 같이 설정되는데 :

target ... : variable-assignment

 

이 변수값은 export, override provate 등을 사용하여 할당할 수도 있다. 복수 타깃인 경우는 각 타깃에 따른 타깃-한정 변수값을 할당하게 된다.

변수값 할당은 '=', ':=', '+=', '?=' 등 어떤것을 사용해서도 가능하다.

타깃-한정 변수는 다른 makefile 변수들과 같은 우선권을 가진다. 명령행을 통해 제공된 변수는 다른것들 보다 우선하며 override 지시자를 사용해 이를 거스를 수 있게 해준다.

타깃-한정 변수에 특별한 특성이 있는데 이는 타깃-한정 변수가 정의되면 그 변수값은 해당 타깃의 prerequisite들과 다시 그들의 prerequisite에까지 효력을 미친다는 것이다. 예를 들어 :

prog : CFLAGS = -g

prog : prog.o foo.o bar.o

 

비록 CFLAGS는 prog라는 타깃에서 값이 할당되었지만 prog.o, foo.o, bar.o를 만들기 위한 recipe에도 같은 효과를 발휘하게 된다.

만약 하나의 prerequisite 이 여러 타깃의 prerequisite으로 쓰이는 경우 처음 생성되는 타깃의 타깃-한정 변수 값이 prerequisite에 계승되고 나머지 타깃의 값은 무시된다.

 

6.12 패턴-한정(pattern-specific) 변수값

 

패턴-한정 변수는 패턴에 맞는 타깃에 변수값을 할당하는 것이다.

 

pattern ... : variable-assignment

 

패턴은 %-pattern 형식이며 타깃-한정 변수값에서처럼 가능한 할당 방법을 모두 사용가능하다. 예를 들어 :

%.o : CFLAGS = -0

 

는 %.o 패턴에 맞는 모든 타깃에 대해 CFLAGS 에 '-0'을 할당한다.

만약 하나의 타깃이 여러개의 패턴과 일치하면 어떻게 되나? 이런때는 더긴 stem을 갖는 패턴-한정 변수가 채택된다. 다시 말해 좀더 자세한 변수값이 채댁되는 것이다. 예를 들어 :

%.o: %.c

$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

lib/%.o : CFLAGS := -fPIC -g

%.o : CFLAGS := -g

all : foo.o lib/bar.o

 

이 예에서 lib/bar.o를 갱신하기 위해서는 첫번째 CFLAGS 변수값이 쓰인다. 일치된 패턴이 같은 길이의 stem을 갖을 때는 makefile에서 정의된 순서로 결정한다.

 

6.13 상속 제한하기

 

앞에서 언급했듯이 make 변수들은 prerequisite에 상속된다. 이런 특성을 이용해 prerequisite 이 어느 타깃에 의해 기동되는가에 따라 그 prerequisite의 행동을 변경할 수 있다. 예를 들어 debug라는 이름의 타깃에 정의된 타깃-한정 변수는 'make debug'를 실행하면 debug의 모든 prerequisite에 상속된다. 반면 'make all'을 실행하면 이 타깃-한정 변수가 정의되지 않음으로써 prerequisite의 수행에도 변화가 생길 수 있다.

종종 이 변수가 상속되는 것을 원치 않을 수도 있다. 이 때 private 변환자를 사용하면 되는데 이 변경자는 모든 변수 할당시 사용가능하지만 타깃-한정, 패턴-한정 변수에 사용할 때 가장 의미가 있다. private 변환자로 지시된 변수는 그 타깃에서만 보일 뿐 prerequisite에는 보이지 않는다. 마찬가리로 전역변수가 private로 지정되면 단지 전역에서만 보일뿐 각 타깃에서 보이지 않는다. 즉 어떤 recipe에서도 사용될 수 없다는 뜻이된다.

예를 들어 :

EXTRA_CFLAGS =

prog : private EXTRA_CFLAGS = -L/usr/local/lib

prog : a.o b.o

 

여기서 private 변환자가 있으므로 a.o와 b.o는 EXTRA_CFLAGS를 상속받지 않는다.

 

6.14 다른 특수 변수들

 

GNU make는 특별한 성질을 갖는 몇몇 변수들을 지원한다.

MAKEFILE_LIST

make에 의해 해석된 makefile들의 이름을 포함한다. 첫번째 이름은 처음기동된 Makefile이 될것이다.

name1 := $(lastword $(MAKEFILE_LIST))

include inc.mk

name2 := $(lastword $(MAKEFILE_LIST))

all :

@echo name1= $(name1)

@echo name2= $(name2)

결과는

name1= Makefile

name2= inc.mk

 

.DEFAULT_GOAL

명령행에서 목표 타깃을 명기하지 않았으때 실행할 타깃을 알려준다. 이 .DEFAULT_GOAL 변수값을 클리어하거나 특정 타깃명을 명기함으로써 목표타깃을 정한다. 예를 들어 :

          # Query the default goal.
          ifeq ($(.DEFAULT_GOAL),)
            $(warning no default goal is set)
          endif  
          .PHONY: foo
          foo: ; @echo $@          
          $(warning default goal is $(.DEFAULT_GOAL))          
          # Reset the default goal.
          .DEFAULT_GOAL :=          
          .PHONY: bar
          bar: ; @echo $@          
          $(warning default goal is $(.DEFAULT_GOAL))          
          # Set our own.
          .DEFAULT_GOAL := foo

 

이 결과는 :

no default goal is set

default goal is foo

default goal is bar

foo

이 변수에 여러개의 타깃명을 할당하면 에러 발생

MAKE_RESTARTS

 

.RECIPEPREFIX

recipe를 시작해야하는 첫 문자를 설정한다. 이 변수가 설정되지 않으면 기본값인 탭문자가 recipe의 첫문자가 되어야 함

 

.VARIABLES

지금까지 정의된 모든 전역 변수이름을 함유한다. 빈문자 변수와 빌트인 변수도 포함

 

.FEATURES

make가 지원하는 특성

'archives'

'check-symlick'

'else-if'

'jobserver'

'second-expansion'

'order-only'

'target-specific'

 

.INCLUDE_DIRS

make가 include할 makefile을 찾아 헤멜 디렉토리 리스트

위로 스크롤