출처 : http://blog.daum.net/english_100/9
5. 규칙속 recipe 작성하기
recipe 는 한개 이상의 명령 행으로 구성되며 이 명령들은 쓰여진 순서대로 차례차례 실행된다. 일반적으로 이 명령들의 결과로 타깃이 갱신되어 최신상태로 변화하게 된다. 사용자가 사용하는 여러 쉘프로그램 중에 makefile 내에 특별히 언급하지 않으면 /bin/sh 을 이용하게 된다.
5.1 Recipe 문법
Makefile 은 한 파일 내에 서로 다른 두가지 문법을 사용하는 독특한 특성을 가지고 있다. Makefile 내의 대부분은 make 문법을 사용하지만 recipe는 쉘에 의해 해석되어 실행되어야하기 때문에 쉘 문법을 사용한다. make 프로그램이 쉘 문법을 이해 할 필요는 없으며 recipe를 쉘에게 건내주기 전에 약간의 번역만 수행하면 된다.
recipe 행은 첫 번째 recipe 행 만이 타깃-prerequisite 행의 끝에 ';'를 쓰고 이어쓸 수 있다는 것을 제외하면 모두 탭 문자로 시작한다. (또는 .RECIPEPREFIX 변수에 설정된 문자로; 특수 변수 참조).
makefile 내의 어떤 행이건 탭문자로 시작하고 "rule context" (규칙이 시작된 다음 다른 규칙이 시작하기 전이나 다른 변수가 정의되기 전) 내에 존재하면 모두 recipre로 간주된다. 빈 행이나 주석 행이 사이에 있을 수 있지만 모두 무시된다.
몇가지 결론을 정리하면:
- 탭 문자로 시작하는 빈 행은 그냥 공백이 아니라 빈 recipe이다 (빈 recipe 참조)
- recipe에 포함된 주석은 make 주석이 아니다. 왜냐하면 있는 그대로 쉘에게 전달될 것이기 때문이다. 이것이 주석으로 처리될지 아닐지는 사용하는 쉘에 달려있다.
- "rule context" 사이에 탭문자 이후 정의된 변수는 make 변수가 아니라 recipe의 일부로 쉘에게 전달된 변수이다.
- "rule context" 사이에 탭문자 이후 쓰여진 조건문 표현은 recipe의 일부로 갖주되어 쉘에게 전달된다.
5.1.1 recipe 행 쪼개기
make 가 recipe를 해석하는 방법 중 하나는 newline 앞에 backslash가 있는지 없는 체크하는 것이다. 정상적인 makefile 내에서 하나의 논리적 recipe 행을 여러 행으로 나누기 위해서는 newline 앞에 '\'를 놓으면 된다. 이렇게 만들어진 일련의 행들은 하나의 recipe 행으로 간주되고 하나의 쉘 인스턴스가 기동하여 이를 실행한다.
newline 과 backslash는 함께 쉘에게 전달되는데 이들이 어떻게 처리되는지는 사용하는 쉘에 따라 다르다. backslash-newline 문자 다음에 오는 첫 문자가 recipe 접두 문자 (일반적으로 탭 문자)이면 그 문자는 자동으로 삭제되며 그외의 공백문자는 추가될 수 있다.
예를 들어 다음의 recipe는
all :
@echo no\
space
@echo no\
space
@echo one \
space
@echo one\
space
실제로 4개의 쉘 명령으로 구성되어 있다.
nospace
nospace
one space
one space
좀더 복잡한 예를 보자 :
all : ; @echo 'hello \
world' ; echo "hello \
world"
이 예는 하나의 명령에 하나의 쉘을 기동하여 다음과 같은 출력을 내놓을 것이다.
hello \
world
hello world
큰 따옴표 ("...") 사이에서는 backslash-newline 제거되었지만 작은 따옴표 ('...') 사이에서는 제거되지 않고 표시되었음을 주목하자. 이는 디폴트 쉘인 /bin/sh이 backslash-newline을 처리하는 방법에 따른것으로 다른 쉘에서는 달리 처리될 수 있다.
여 러행으로 나누어진 작은따옴표 내의 긴 문장에 대해 backslash-newline이 표시되지 않게 하려면 어떻게 할까? 이런 일은 스크립트를 Perl과 언어에 전달해 처리할 때 발생할 수 있는데 이 때 스크립트내의 backslash는 그 의미를 바꿔버리거나 문법에러를 발생시킬 수 있다. 한가지 해결 방법은 문자열이나 아니면 전체 명령문을 하나의 make 변수로 정의하는 것이다. 이렇게 하면 makefile에 대한 newline 처리 방식이 사용되어 backslash-newline은 제거되게 된다. 예제를 다시 써보면 :
HELLO = 'hell \
world'
all : ; @echo $(HELLO)
이에 대한 출력은 다음과 같이 원하는 대로 출력된다. :
hello world
5.1.2 recipe 내에 변수 사용하기
make 가 recipe처리하는 또 다른 방법은 변수를 이용하는 것이다 (변수 참조의 기초 참조). 변수의 펼침은 make가 makefile들을 모두 읽어들인 다음 그리고 타깃이 낡은 것으로 판명난 다음에 이루어진다. 그러므로 recipe가 수행되지 않는 경우에는 펼침도 발생하지 않는다.
recipe 속의 변수와 함수 참조는 makefile 내에서 동일한 문법과 의미를 갖으며 인용규칙 (quoting rule) 또한 동일하다. 즉 recipe 내에 달러 기호를 표시하고 싶다면 '$$'로 표시해야 한다. 쉘에 있어 달러 기호는 변수를 나타내므로 우리가 사용하는 변수가 make 변수 (달러 기호 하나)인지 쉘 변수인지 확실히 기억하고 있어야 한다. 예를 들어 :
LIST = one two three
all :
for i in $(LIST); do \
echo $$i; \
done
결과는 다음과 같은 명령을 쉘에게 전달하는 것이고 :
for i in one two three; do \
echo $i; \
done
예상되는 결과는 다음과 같다.
one
two
three
5.2 Recipe Echoing
일반적으로 make는 각 recipe 행을 수행하기 전에 먼저 프린트한다. 이것을 우리는 echoing이라 부르는데 그 이유는 이것이 마치 우리 자신이 입력한 것같이 보이기 때문이다. 명령행이 '@'로 시작하면 에코잉이 억제되는데 이 '@'는 쉘에게 전달됙 전에 제거된다.
make에 '-n' 또는 '--just-print' 옵션이 주어지면 모든 recipe는 실행되지 않고 에코잉만 수행되는데 이경우에는 '@'로 시작하는 행도 프린트 된다 (옵션 요약 참조). 이는 recipe를 실재 실행하지 않고 그 recipe가 필요한지 아닌지를 알아보기 위해 유용한 옵션이다.
'-s' 나 '--silent' 옵션은 make로 하여금 모든 에코잉을 금지하는데 이는 마치 모든 recipe가 '@'로 시작하는 것과 같다. 특별 타깃인 .SILENT에 아무런 prerequisite도 없는 경우 같은 효과를 얻을 수 있다. 하지만 '@'의 기능이 더욱 유용해 지면서 .SILENT는 더이상 설 자리가 줄어들고 있다.
5.3 Recipe의 실행
타깃을 갱신하기 위해 recipe를 실행시킬때 특별 타깃 .ONSHELL 이 효력을 발휘하지 않는 한 recipe의 각 행은 새로운 하위 쉘을 기동시킴으로써 명령을 수행한다 (하나의 쉘 사용하기).
꼭 기억할 것 : 각 recipe마다 새로운 하위 쉘을 기동한다는 것은 쉘변수를 설정하고 쉘명령을 기동하는 것이 recipe 내의 다음 행에 영향을 주지 못함을 의미한다. 쉘명령이 계속 영향력을 갖게 하고자 한다면 모든 명령을 한 행에 기술하면 된다. 그렇게하면 make가 전체 행을 위한 하나의 쉘을 기동할 것이고 그 쉘은 명령문들을 차례로 실행해 나갈것이다.
예를 들면 :
foo : bar/lose
cd $(@D) && gobble $(@F) > ../$@
여기서 우리는 쉘 AND 연산자 '&&'를 사용하여 만약 cd 명령이 실패한다면 스크립트가 다른 디렉토리에 있는 gobble 명령을 수행할 수 없게 한다.
5.3.1 한개의 쉘 사용하기
하 나의 쉘이 기동해 모든 recipe들을 수행한는 것이 좋을 때가 있다. 이런 경우 많은 명령행으로 이루어진 makefile이 추가의 쉘 기동 부담을 줄일 수 있으므로 성능이 향상되고, 또한 경우에 따라 newline이 recipe 명령에 포함되길 원하는 경우 유용한데 이는 SHELL과 다른 해석기를 사용할 때 발생한다. makefile 내의 어디에든 .ONESHELL이라는 특별 타깃이 쓰여지면 한개의 타깃에 해당하는 모든 recipe 행들은 하나의 쉘에 의해 실행된다. 예를 들어 :
.ONESHELL:
foo : bar/lose
cd $(@D)
gobble $(@F) > ../$@
는 비록 여러 명령 행이 존재하지만 하나의 쉘만이 기동되어 실행된다.
이후 생략
5.3.2 쉘 선택하기
쉘 로 사용될 프로그램은 SHELL 변수에 의해 지정되며 Makefile내에 이 변수가 설정되어 있지 않으면 /bin/sh 이 자동 사용된다. 쉘로 전달할 옵션은 .SHELLFLAGS을 통해 이루어지는데 .SHELLFLAGS의 디폴트 값은 '-c'이거나 POSIX-conforming 모드에서 '-ec'이다.
대부분의 변수와는 다르게 SHELL 변수는 사용자 환경으로부터 설정되어서는 안된다. 왜냐하면 SHELL 변수는 개인적으로 사용하는 쉘 프로그램에 대해 사용되기 때문이다. (사용자환경으로부터 설정되는 변수 참조).
생략
5.4 병렬 처리
GNU make는 몇 개의 recipe를 동시에 실행시킬 수 있다. make는 보통 한번에 한 recipe씩 하나가 끝날 때 까지 기다렸다가 다음 것을 실행하는 방식이지만 '-j' 나 '--jobs' 옵션을 씀으로써 make로 하여금 동시에 여러 recipe를 실행할 수 있게 한다. makefile 내에 .NOTPARALLEL 유사타깃이 명기되어 있으면 병렬처리 기능은 더 이상 사용할 수 없다.
'-j' 옵션에 이어 숫자가 쓰여지는 경우 이 숫자는 한번에 몇 개의 recipe를 동시에 수행할 수 있는지를 알려준다. 이 숫자를 job slots 수라고 하는데 이 숫자가 명기되지 않으면 동시에 수행되는 recipe의 수에는 제한이 없음을 말한다.
동 시에 여러 recipe가 실행됨으로 인한 달갑지 않은 점은 각 recipe가 실행된 출력이 각각 완료된 시점에 마음대로 출력되기 때문에 순서에 따라 정리되지 않고 흐트러진다는 것이다. 또다른 문제는 두개의 프로세스가 같은 장치로부터 입력을 받아들일 수 없다는 것이다. 그래서 한번에 한 recipe만이 단말장치로부터 입력을 받아들이기 위해 한 recipe를 제외한 모든 입력스트림을 무효화 시켜버려야 한다. 이는 여러개의 자식 프로세스가 있을 때 이들 중 대부분이 표준 입력 스트림을 읽들이려 하면 치명적 에러를 발생하게 된다는 의미가 된며, 또한 어떤 recipe가 유효한 표준 입력 스트림을 받아들이게 될지 예측할 수 없음을 의미하기도 한다. 결국 어떤 recipe 이건 처음 실행된 것이 처음으로 입력을 받아들이고 이것이 끝난 후에야 실행을 시작한 recipe가 다음을 받아들이는 방식이 된다.
이후생략
5.5 Recipe 에러
각 쉘이 수행을 마치고 반환되었을때 make는 그 반환상태값을 살핀다. 성공적으로 수행을 마쳤다면 (반환 상태가 0 이면) 새로운 쉘을 기동하여 다음 행의 명령을 수행하고 마지막 행이 종료된 후 그 규칙에 대한 실행이 완료된다.
에 러가 발생하면 (반환 상태가 0가 아니면) make는 현재의 규칙을 포기하거나 모든 규칙의 실행을 포기한다. 어떤 때는 recipe 해에서의 에러가 문제가 되지 않는 경우도 있는데 예를 들면 디렉토리가 존재하는 지를 체크하기 위해 mkdir을 사용하는 것이다. 디렉토리가 이미 존재하면 mkdir은 에러를 보고하지만 make는 상관없이 일을 이어나갈 것이다.
recipe 행의 에러를 무시하려면 각 recipe 행을 '-'로 시작하면 된다. 예를 들어
clean :
-rm -f *.o
이 예는 rm 이 파일을 삭제할 수 없는 일이 발생해도 make로 하여금 일을 계속 진행하도록 한다.
make 에 '-i' 나 '--ignore-errors' 옵션을 주어 실행하면 모든 규칙의 모든 recipe의 에러가 무시되며, makefile 내에서 .IGNORE 라는 특수타깃이 prerequisited 없이 쓰였을 때에도 같은 효과를 얻을 수 있다. 하지만 '-'의 사용이 더욱 편리하기 때문데 다른 에러 무시방법들은 쓸모가 아주 적다.
'-' 나 '-i'옵션을 통해 에러가 무시되었을 때 make는 쉘이 반환한 상태값에 대한 메시지를 프린트하고 또한 이 에러가 무시되었음을 알려 주지만 반환된 상태값은 성공으로 처리한다.
에 러 무시 설정이 되어있지 않은 상태에서 에러가 발생하면 이는 현재의 타깃 뿐아니라 타깃에 의존하는 다른 어떤것도 제대로 갱신될 수 없음을 의미한다. 이런 상황에서 make는 일반적으로 즉시 모든 상황을 포기하고 0이 아닌 상태값을 반환한다. 하지만 '-k' 나 '--keep-going' 옵션이 사용되는 상태라면 make는 nonzero 상태값을 반환하고 종료하기 전에 현재 진행중인 타깃의 갱신을 계속한다. 예를 들어 오브젝트 파일을 컴파일하는 중 에러가 발생한 경우 'make -k'는 링크가 불가능한 줄 알면서도 다른 오브젝트 파일들의 컴파일을 계속한다.
생략
5.6 make 인터럽트하기 또는 죽이기
make가 쉘을 실행하는 동안 치명적 신호를 받게 되면 recipe가 갱신하고자 했던 타깃 파일을 살펴 본후 이 파일의 변경 시간이 처음 체크했을 때보다 변해 있으면 파일을 삭제해 버린다.
타 깃을 삭제하는 이유는 다음에 make가 실행될 때 문제가 있었음을 확인시키기 위해서다. 컴퓨터가 돌아가고 있는 동안 오브젝트 파일 foo.o를 쓰고있는 중에 CTRL-c를 입력했다고 생각해 보자. CTRL-c는 컴퓨터를 죽이게 되고 불완전한 파일의 최후 변경시간은 foo.c 파일보다 새로워 질것이다. 하지만 이때 make도 바로 그 CTRL-c 신호를 받을 것이므로 이 불완전한 파일을 지워버린다. 만약 make가 이런일을 수행하지 않으면 다음번 make를 수행할 때 foo.o가 재작성되지 않아도 된다고 생각할 것이고 완전치 않은 오브젝트 파일로 링크를 수행하다가 실패하여 에러를 발생시키게 될것이다.
여 기서 특수 타깃 .PRECIOUS를 이 타깃에 종속되게 함으로써 타깃 파일을 삭제하지 못하게 할 수 있다. make가 타깃을 재작성하기 전에 .PRECIOUS의 prerequisite에 그 이름이 있는지를 확인하고 그 결과에 따라 삭제할지 말지를 결정한다. 이런 기능을 사용하는 이유는 타깃이 원자 방식으로 깅신된다거나, 변경 시간만을 기록하기 위해 존재한다거나 또는 다른 문제를 발생기키지 않도록 항상 존재해야만 한다거나 할 때 유용하지 때문이다.
5.7 make의 순환적 사용
make 의 순환적 사용이란 makefile 내에서 make를 다시 명령어로 사용하는 것이다. 이 기술은 커다란 시스템을 형성하는 다양한 하위시스템에 대해 각각의 makefile을 사용하고자 할 때 유용하다. 예를 들어 자신의 makefile을 가지고 있는 subdir 이라는 하위 디렉토리가 있고 현재 디렉토리의 makefile이 하위 디렉토리에서 make를 수행하고자 하는 경우를 가정하자. 이는 다음과 같이 작성할 수 있다 :
subsystem:
cd subdir && $(MAKE)
또는 다음과 같다.
subsystem:
$(MAKE) -C subdir
순 환적 make 기법을 사용하기 위해서는 위의 예를 복사해 사용하면 된다. 하지만 아직 이 기법이 어떻게 작동하고 하위-make가 최상위-make와 어떻게 상호 작용하는지 등 할아야 할 것들이 많다. 또한 순환적 make 명령을 수행하는 타깃은 '.PHONY'로 선언하면 유용하다는걸 발견하게 될 것이다.
GNU make가 시작될 때 (또는 -C 옵션으로 시작할 때) CURDIR 변수에는 현재 작업중인 디렉토리가 저장된다. 이 값은 이후에 make에 의해 다시는 변경되지 않는다. 특히 다른 디렉토리의 파일을 읽어들일 때에도 CURDIR 값은 변함이 없음을 기억해야 한다. 이 값은 makefile 내에서 설정된다 해도 같은 우선권을 가지며 make의 수행에 아무런 영향도 주지 못한다. (make의 작업 디렉토리는 변하지 않는다.)
5.7.1 MAKE 변수가 작동되는 방식
순환 make 명령은 항상 MAKE 변수를 사용해야지 원래의 이름인 'make'를 사용해서는 안된다.
subsystem:
cd subdir && $(MAKE)
이 변수의 값은 make가 기동된 실제 프로그램 이름을 갖는다. 그 파일명이 /bin/make 였다면 위 recipe는 'cd subdir && /bin/make'를 실행하게 되는 것이다. 즉 최상위-make 가 특정 버전의 make를 사용한다면 하위-make는 모두 통일한 make를 사용하게 된다. 특별한 성질로서, recipe에 MAKE 변수를 사용하면 '-t' ('--touch'), '-n' ('--just-print'), '-q' ('--question' 등의 옵션 효과가 사라지는데 이는 '+' 문자로 시작하는 recipe행의 효과와 동일하다. 이 특별한 성질은 MAKE 변수가 recipe 내에 직접 쓰일때 나타나며 다른 변수에 의해 참조되어 나타나는 경우에는 적용되지 않는다. 이런 경우에는 '+'를 사용해서만 효과를 얻을 수 있다.
위 예문에서 'make -t' 명령이 사용되었을 때를 생각해 보자 ('-t' 옵션은 recipe를 실제로 실행하지 않고 타깃을 최신 상태로 바꾼다). 이 명령은 subsystem 이라는 파일을 생성하고는 아무런 일도 하지 않을 것이다. 여기서 우리가 진정 원하는 것은 'cd 녀약 && make -t'를 실행하는 것인데 recipe를 실행코자 해도 '-t' 옵션이 실행하지 말라고 한다. 그래서 위의 특성이 이를 가능하게 해주는 것이다. recipe 행이 MAKE 변수를 포함하고 있는 한 '-t', '-n' '-q' 등의 옵션은 힘을 잃어 버리는 것이다. 다시말해 MAKE 파일이 포함된 recipe는 실행을 방해하는 대부분의 옵션을 무력화 시키는 힘이 있다고 할 수 있다. 일반적으로 MAKEFLAGS 체계가 옵션을 하위-make에게 전달해 주는데 이때 touch나 print 등의 요구도 함께 전달된다.
5.7.2 하위-make로 변수 전달하기
최상위-make의 변수값은 명시적 요청이 있는 경우 하위-make로 전달될 수 있다. 이 변수 값들은 하위-make에서 디폴트값으로 정의되며 '-e' 옵션이 주어지지 않는한 하위-make에 명기된 변수들에 우선할 수는 없다.
변수를 하위-make로 하달하기위해 make는 변수와 그 값을 환경에 추가시킨다. 그러면 하위-make는 그 환경을 보고 자신의 변수값의 테이블을 초기화한다.
명 시적으로 요청하여 변수를 전달을 방법 외에 변수가 애초에 환경상에 정의되었거나, 명령행 상에 쓰여진 변수로서 그 변수명이 일반 문자, 숫자, 밑줄로 이루어져있으면 전달되어질 수 있다. 반면 어떤 쉘은 이런 문자들로 이루어진 환경 변수를 차리하지 못하는 경우도 있다.
make 변수인 SHELL의 값은 밖으로 전달될 수 없지만 대신에 시초의 환경상에 정의된 변수인 SHELL은 하위-make로 전달된다. make 변수 SHELL도 export 지시자를 이용하면 역시 하부로 전달 가능하다.
특수 변수인 MAKEFLAGS는 unexport 되지 않는 한 항상 export된다.
make는 명령행에 정의된 변수값을 자동으로 하달하는데 이 값들은 MAKEFLAGS 변수안에 넣어진다. 반면 make에 의해 디폴트로 만들어지는 변수들은 정상적으로 전달되지 않는다.
특정 변수를 하위-make에 하달하기 위해서는 export 지시자를 이용하고 :
export variable ...
반면 어떤 변수가 밖으로 전달되는 것을 막기위해서는 unexport 지시자를 사용한다.
unexport variable ...
편의를 위해 변수를 정의하고 동시에 내보내는 것이 가능하다.
export variable = value
이는 다음과 동일하다.
variable = value
export variable
그리고
export variable := value
는 다음과 동일하다.
variable := value
export variable
비슷하게
export variable += value
은 다음과 같다.
variable += value
export variable
여기서 make 내의 export와 unexport의 용법이 쉘에서의 용법과 동일함을 알 수 있다.
만약 모든 변수들이 디폴트로 내보내지게 하려면 단지 export만 써주면 된다.
exprot
이 는 make로 하여금 export나 unexport로 명기되지 않은 모든 변수를 내보내도록 지시한다. export 단독사용에 의한 디폴트 변수 내보내기에 있어 알파벳문자, 숫자문자, 밑줄문자 이외의 문자로 이루어진 변수는 export로 특별 언급이 없는 한 내보내지지 않는다.
어떤 recipe에서는 변수를 내보내고 다른 recipe에서는 금지시키기 위해 export와 unexport를 중복사용할 수는 없고 단지 마지막에 나오는 export나 unexprot가 전체 make를 지배하게 된다.
특 별한 기능으로 레벨을 하나 내려갈 수록 그 값이 변하는 MAKELEVEL 변수가 있다. 이 변수의 값은 레벨의 깊이를 나타내는 십진수 문자열로서 최고 레벨 make의 경우 '0' 값을, 그 아래 make는 '1', 그 다음은 '2' 등의 값이 된다. 즉 make가 recipe를 수행하기 위해 새로운 환경을 설정할 때마다 값이 1 증가하는 것이다.
MAKELEVEL는 조건 지시자에서의 테스트를 위해 주로 사용되며 이 방식을 통해 조건부 실행이 가능한 makefile을 만들 수 있다.
MAKEFILES 이란 변수를 사용할 수도 있는데 이 변수는 모든 하위-make로 하여금 추가 makefile을 사용하도록 한다. MAKEFILES의 변수값은 공백문자로 분리된 이름들의 나열이며 이 변수가 어느 레벨의 makefile 내에 정의되면 환겨을 통해 하위로 전달되어 그 하위-make가 다른 어떤 것 보다 먼저 읽어들이는 추가 makefile 리스트로 작용한다. (MAKEFILES 변수 참조)
5.7.3 하위-make에 옵션 전달하기
'-s' 나 '-k' 같은 옵션들은 MAKEFLAGS 변수를 통해 하위-make에게 자동으로 전달할 수 있다. 만약 'make -ks' 명령을 사용하면 make가 받아들인 옵션 문자열은 자동으로 MAKEFLAGS에 세팅되어 'ks'란 값을 갖게 된다.
결과적으로 모든 하부-make들은 MAKEFLAGS 값을 갖게 되고 그 옵션을 이용해 작업을 수행하게 된다. (옵션 요약 참조)
명령행에서 정의된 변수는 MAKEFLAGS를 통해 하위-make에게 전달되다. '='를 포함하는 MAKEFLAGS 값은 변수 정의와 동등하게 간주된다.
반대로 '-C', '-f', '-o', '-W' 등의 옵션들은 MAKEFLAGS에 들어가지 않으므로 하위-make로 전달 되지 않는다.
'-j' 옵션은 특별한 경우로서 옵션뒤에 숫자 N이 따라오면 상위 make와 하위 make 사이에 서로 통신하여 동시에 N 개의 작업만이 수행될 수 있게 한다. (병렬 실행 참조)
만약에 다른 옵션을 전달하고 싶지 않다면 MAKEFLAGS를 다음과 같이하면 된다 :
subsystem :
cd subdir && $(MAKE) MAKEFLAGS=
명 령행에서의 변수 정의는 실질적으로 MAKEOVERRIDES 변수에 나타나게 되는데 MAKEFLAGS 는 이른 참조하는 것이다. 옵션들을 정상적으로 전달하길 원하지만 명령행 변수는 전달하고 싶지 않다면 MAKEOVERRIDES 를 리셋시키면 된다.
MAKEOVERRIDES =
이 것이 그렇게 유용한 것은 아니나 어떤 시스템은 환경 변수의 갯수에 대한 상한이 작기 때문에 MAKEFLAGS에 너무 많은 정보가 실릴 경우 초과 상황이 발생할 수 있다. 이런 문제가 발생할 때 'Arg list too long' 이라는 에러 메시지가 출력된다.
예전 버젼과의 호환을 위해 비슷한 변수 MFLAGS를 사용하는데 이 변수는 명령행 변수 선언을 포함한지 않는것을 제외하고는 MAKEFLAGS와 동일하며 통상적으로 다음과 같이 명기해서 사용한다 :
subsystem :
cd subdir && $(MAKE) $(MFLAGS)
하지만 MAKEFLAGS로 인해 중복 사용이 된다. 단지 예전 make 프로그램과의 호환을 위해 이런 형태로 쓸수 있으며 최신 버전에서도 문제없이 작동된다.
생략
5.7.4 '--print-directory' 옵션
몇 단계에 걸친 하위 레벨 make를 기동시킬 때 '-w' 나 '--print-directory' 옵션은 make가 어느 디렉토리에서 프로세스를 시작하고 끝내는를 보여줌으로써 출력에 대한 이해도를 높여준다. 예를 들어 'make -w'가 /u/gnu/make에서 실행되고 있다면 make는 제일 먼저 다음과 같은 형태의 출력을 내보낸다 :
make : Enter directory '/u/gnu/make'.
그런 다음 그 디렉토리에서의 작업이 완료되었을 때는 다음을 출력한다.
make: Leaving directory '/u/gnu/make'.
보
통 '-w' 옵션은 '-C' 옵션을 사용하거나 하위-make를 사용할 때자동으로 켜지기 때문에 직접 명기할 필요는 없다. 하지만
'-s'나 '--no-print-directory' 옵션을 사용하면 자동으로 켜지지 않으므로 주의해야 한다.
5.8 Canned recipe 정의하기
여 러개의 타깃에서 동일한 명령 시퀀스를 사용하는 경우가 있는데 이때 define 지시자를 사용해 이들 명령 시퀀스를 묶음으로 만든 후 여러 타깃에서 사용할 수 있다. 묶음 시퀀스는 사실 하나의 변수이므로 다른 변수명과 충돌이 일어나서는 안된다.
define run-yacc =
yacc $(firstword $^)
mv y.tab.c $@
endef
여 기서 run-yacc는 정의될 변수 이름이고 endef는 정의의 끝을 표시며 그 사이가 명령 시퀀스이다. 첫 명령은 누가 이 묶음을 사용하던 첫 prerequisite에 Yacc를 실행시키는 것이다. 결과 파일명이 y.tab.c가 되므로 다음 명령에서 타깃 파일명으로 이름을 변경한다.
이 묶음 시퀀스를 사용하는 법은 다음과 같다 :
foo.c : foo.y
$(run-yacc)
묶 음 시퀀스의 각 행은 앞에 탭문자가 붙어 있는것으로 간주되며 특히 make는 각 행을 각각의 하부 쉘을 기동시켜 실행한다. 그러므로 묶음 시퀀스의 각 행은 '@', '-', '+' 등의 특수 문자를 앞에 붙일 수 있으며 그 예는 다음과 같다 :
define frobnicate =
@echo "frobnicating target $@"
frob-step-1 $< -o $@-step-1
frob-step-2 $@-step-1 -o $@
endef
첫행에 대한 에코잉은 금지되지만 다음 두 행은 에코잉이 일어난다. 다른 방법으로 묶음 시퀀스를 참조하는 곳에서 접두문자를 첨부할 수도 있는데 이 때는 모든 행에 대한 에코잉이 이루어지지 않는다.
frob.out : frob.in
@$(frobnicate)
5.9 빈 recipe 사용하기
가끔 아무것도 하지않는 빈 recipe가 유용할 때가 있다. 예를 들어 :
target : ;