Special Targets
rule 을 정의할 때 사용하는 target : prerequisites
형식을 사용하는 타겟으로
특별한 의미를 가집니다.
타겟 중에는 prerequisites 을 사용하는 타겟이 있고 사용하지 않는 타겟이 있습니다.
.DEFAULT:
prerequisites 을 빌드 하는데 필요한 rule 을 찾지 못했을 경우 default 로 실행할 rule 을 설정합니다.
.DEFAULT 타겟의 prerequisites 은 사용되지 않습니다.
prog: foo bar # prerequisites 에 해당하는 foo, bar 를 만드는데 필요한 rule 이 없다.
@echo target: $@
.DEFAULT: # default rule 이 실행된다.
@echo default: $@
###### 실행 결과 #######
default: foo # '$@' 값은 각 타겟으로 설정됩니다.
default: bar
target: prog
다음은 match-anything rule 인 %
과 .DEFAULT
룰을 비교한 것인데
%
룰의 경우에는 foo 타겟을 빌드 하는데 필요한 recipe 가 없을 경우에도 실행되지만
.DEFAULT
룰의 경우는 prerequisites 에 해당하는 bar 타겟에 대해서만 실행되는 것을 볼 수 있습니다.
foo : bar foo : bar
% : .DEFAULT :
@echo % : $@ @echo default : $@
#### 실행 결과 #### #### 실행 결과 ####
% : bar default : bar
% : foo # foo 타겟에 대해서도 실행된다.
.DELETE_ON_ERROR:
이것은 .PRECIOUS
타겟과 비슷하지만 차이점은 signal 에의해 비정상 종료할 때가 아닌
recipe 실행 중에 shell 명령이 오류로 종료하여 make 실행이 중단될 경우를 말합니다.
원래는 이 경우에도 타겟 파일이 자동으로 삭제되어야 하지만 역사적인 이유로 직접 설정해야
한다고 합니다.( 다시 말해서 default 는 삭제가 되지 않습니다. )
.DELETE_ON_ERROR
타겟을 Makefile 에 추가하면 recipe 실행 중에 오류로 make 이 종료될때
타겟이 update 되었는지 체킹 한 후 update 되었을 경우 해당 타겟 파일을 삭제합니다.
.DELETE_ON_ERROR 타겟의 prerequisites 은 사용되지 않습니다.
.DELETE_ON_ERROR:
foo :
touch $@
false
@echo target: $@
########## 실행 결과 ##########
touch foo # 타겟 파일을 update
false
make: *** [Makefile:7: foo] Error 1
make: *** Deleting file 'foo' <--- 오류로 종료시 타겟파일이 update 되었으면 삭제한다.
.EXPORT_ALL_VARIABLES:
모든 변수들을 export 합니다.
기본적으로 변수명이 대, 소문자, 숫자, _
로 구성된 변수만 export 됩니다.
원래 old make 에서는 이것이 디폴트 동작 방식이었다고 합니다.
export 지시자를 단독으로 사용하는 것도 동일하게 동작하지만 old make 에서는 오류가 나므로
이 타겟을 사용하면 됩니다.
.IGNORE:
recipe 에서 명령이 오류로 종료하여 make 실행이 종료되는 것을 ignore 할때 사용합니다.
이 타겟을 이용하면 rule 별로 ignore 설정을 할 수 있습니다.
prerequisites 을 사용하지 않으면 명령 라인에서 -i
( --ignore-errors ) 옵션을 사용한 것과 같습니다.
.IGNORE: foo bar # foo, bar 타겟에만 적용
.IGNORE: # 모든 타겟에 적용
----------------------------------------------------
.IGNORE: foo
foo :
touch $@
false
@echo target: $@
########## 실행 결과 ##########
touch foo
false
make: [Makefile:6: foo] Error 1 (ignored) # shell 명령의 오류가 ignored 되고
target: foo # 마지막 명령도 실행된다.
.INTERMEDIATE:
실행 중에 implicit rule 에의해 생성되는 중간 파일을 intermediate file 이라고 합니다. 다음을 보면 hello.y 로부터 hello.c 가 생성되고 hello.c 로부터 hello.o 가 생성되고 마지막으로 hello.o 로부터 hello 가 생성됩니다. 매칭되는 hello.y 는 현재 존재하는 파일이고 hello 와 hello.o 는 rule 에 explicit 하게 정의되어 있으므로 intermediate file 이 되지 않지만 hello.c 같은 경우는 중간에 implicit rule 에의해 생성되는 intermediate file 이 됩니다. 기본적으로 intermediate file 은 make 이 종료될 때 자동으로 삭제가 됩니다.
.INTERMEDIATE 타겟의 prerequisites 으로 패턴은 사용할 수 없습니다.
sh$ touch hello.y # 먼저 hello.y 파일을 생성
-------------------------------------------------------
%.c : %.y
@echo making $@ from $<
@touch $@
%.o : %.c
@echo making $@ from $<
@touch $@
hello : hello.o
@echo making $@ from $<
@touch $@
######### 실행 결과 #########
sh$ make
making hello.c from hello.y
making hello.o from hello.c
making hello from hello.o
rm hello.c <---- intermediate 파일인 hello.c 는 자동 삭제된다.
hello.o 같이 explicit 하게 지정된 파일이라도
.INTERMEDIATE
타겟에 등록하면 자동으로 삭제됩니다.
# 위 Makefile 에 다음 라인을 추가하고 실행
.INTERMEDIATE: hello.o
######### 실행 결과 #########
sh$ make
making hello.c from hello.y
making hello.o from hello.c
making hello from hello.o
rm hello.o hello.c <---- hello.o 파일도 자동으로 삭제된다.
.LOW_RESOLUTION_TIME:
stat
shell 명령을 이용하면 특정 파일의 Modify timestamp
정보를 볼 수 있습니다.
make 은 이정보를 이용하여 파일들을 비교한 후에 타겟 파일을 update 할지 판단합니다.
이 정보는 보통 초 단위 이하까지 표시되는 high resolution time 이 사용되는데요.
그런데 특정 시스템에서 사용되는 명령 중에는 시간을 설정할때 초 단위까지만
사용하는 low resolution time 을 사용한다고 합니다.
그러면 파일을 비교할때 초 단위 이하 값이 잘려나가서 실제는 최신 상태임에도 불구하고 약간 old 상태가 되는데요.
이와 같은 문제가 발생할 경우 해당 파일을 .LOW_RESOLUTION_TIME
타겟에
등록하면 초 단위 비교를 하게 되어 문제를 해결할 수 있습니다.
archive 파일 멤버의 경우는 항상 low resolution time 을 사용하므로 따로 이타겟에 등록할 필요는 없습니다.
.LOW_RESOLUTION_TIME: dst
dst: src
cp -p src dst
.NOTPARALLEL:
이 타겟이 설정되어 있으면 make 명령 라인에서 -j
옵션을 사용해도 Makefile 이 serially 실행됩니다.
이것은 타겟이 설정된 해당 Makefile 단위로 적용되는 것으로 sub-make 에는 영향을 주지 않습니다.
.NOTPARALLEL 타겟의 prerequisites 은 사용되지 않습니다.
.ONESHELL:
recipe 에서는 명령이 실행될 때 라인 단위로 shell 프로세스가 생성되어 실행됩니다.
.ONESHELL
타겟을 설정하면 recipe 에있는 모든 명령들이 하나의 shell 프로세스에서 실행되고
here document 같은 multi-line 명령도 사용할 수 있게됩니다.
이 타겟은 룰 별로는 설정할 수 없고 한번 설정하면 전체 makefile 에 적용됩니다.
@
, -
, +
prefix 문자를 사용할 경우는 첫 라인에 한번만 위치시키면 됩니다.
.ONESHELL 타겟의 prerequisites 은 사용되지 않습니다.
foo : bar foo : bar
@echo $@ shell PID: $$$$ @echo $@ shell PID: $$$$
@echo $@ shell PID: $$$$ echo $@ shell PID: $$$$
bar : .ONESHELL:
@echo $@ shell PID: $$$$
@echo $@ shell PID: $$$$ bar : # heredoc 을 사용할 수 있다.
@cat <<END
$@ shell PID: $$$$
$@ shell PID: $$$$
END
######### 실행 결과 ######### ######### 실행 결과 #########
bar shell PID: 27442 bar shell PID: 27551
bar shell PID: 27443 각기 다른 PID bar shell PID: 27551
foo shell PID: 27444 foo shell PID: 27552 # rule 별로 동일한 PID
foo shell PID: 27445 foo shell PID: 27552
.PHONY:
phony 는 사전에서 찾아보면 가짜라는 뜻을 가지고 있는요.
말 그대로 파일이 아닌 가짜 타겟을 지정하는데 사용됩니다.
.PHONY
타겟으로 지정을 하면 update 체킹도 하지 않고
타겟 파일이 존재하던 안하던 상관없이 항상 rule 이 실행되게 됩니다.
.PHONY 타겟의 prerequisites 으로 pattern 은 사용할 수 없습니다.
.PHONY 타겟으로 지정하더라도 makefile 실행시 같은 rule 이 중복 실행되지는 않습니다.
.PHONY: all foo bar # all, foo, bar 는 가짜 타겟으로 항상 실행된다.
all : bar foo
foo : bar # phony 타겟으로 지정하더라도 bar 가 두번 실행되지는 않는다.
@echo fooooo
bar :
@echo barrrr
#### 실행 결과 ####
barrrr
fooooo
.PRECIOUS:
recipe 가 실행 중에 signal 에의해 비정상 종료될 경우 ( 예를 들면 ctrl-c 에의해 )
make 은 먼저 타겟 파일이 update 되었는지 체킹 한 후에 update 된 상태라면 해당 타겟 파일을 삭제합니다.
이것이 필요한 이유는 만약에 빌드 중간에 부분적으로 타겟 파일이 수정된 상태에서
종료가 돼버리면 해당 타겟 파일은 사용할 수 없는 상태임에도 불구하고 최신으로 update 된 상태가 되므로
나중에 다시 make 을 실행할 때 해당 룰이 실행되지 않게 되기 때문입니다.
.PRECIOUS:
타겟에 등록하면 이와 같은 signal 에의한 삭제와 implicit rule 에서 intermediate 파일의 삭제 처리가 disable 됩니다.
.PRECIOUS 타겟의 prerequisites 으로 패턴을 사용할 수 있습니다.
foo :
touch $@
sleep 10
@echo target: $@
###### 실행 결과 ######
touch foo # 타겟 파일을 update
sleep 10 # sleep 상태에서 ctrl-c 로 종료
^Cmake: *** Deleting file 'foo' # 타겟 파일이 update 된 상태이므로 삭제된다.
make: *** [Makefile:5: foo] Interrupt
----------------------------------------------------------------
# 이번에는 Makefile 에 다음을 추가하고 실행
.PRECIOUS: foo
###### 실행 결과 ######
touch foo
sleep 10
^Cmake: *** [Makefile:6: foo] Interrupt # 타겟 파일이 삭제되지 않는다.
.PRECIOUS 의 prerequisites 으로 패턴을 사용할 수 있습니다.
.PRECIOUS: %.o # %.o 패턴을 사용
%.o :
touch $@
sleep 10
@echo target: $@
###### 실행 결과 ######
sh$ make foo.o
touch foo.o
sleep 10
^Cmake: *** [Makefile:5: foo.o] Interrupt
.SECONDARY:
explicit rule 에서 파일을 .SECONDARY
로 등록하면
implicit rule 에서 intermediate 파일처럼 동작합니다.
다시 말해서 chain 중간에 파일이 없다고 해서 새로 생성하지 않습니다.
다음 왼쪽의 일반적인 경우는 foo.c 파일을 삭제하면 foo.c 와 foo.o 가 새로
생성되지만 오른쪽 예제에서처럼 foo.c 를 .SECONDARY
로 등록하면 새로 생성되지 않습니다.
.SECONDARY 타겟의 prerequisites 으로 패턴은 사용할 수 없습니다.
.SECONDARY: foo.c # foo.c 를 SECONDARY 로 등록
foo.o: foo.c foo.o: foo.c
@echo ">>> $@ from $<" @echo ">>> $@ from $<"
touch $@ touch $@
foo.c: foo.y foo.c: foo.y
@echo ">>> $@ from $<" @echo ">>> $@ from $<"
touch $@ touch $@
foo.y: foo.y:
@echo ">>> $@" @echo ">>> $@"
touch $@ touch $@
####### 실행 결과 ####### ####### 실행 결과 #######
sh$ make foo.o sh$ make foo.o
>>> foo.y >>> foo.y
touch foo.y touch foo.y
>>> foo.c from foo.y >>> foo.c from foo.y
touch foo.c touch foo.c
>>> foo.o from foo.c >>> foo.o from foo.c
touch foo.o touch foo.o
sh$ rm -f foo.c sh$ rm -f foo.c
sh$ make foo.o sh$ make foo.o
>>> foo.c from foo.y make: 'foo.o' is up to date.
touch foo.c # foo.c 는 secondary 이므로
>>> foo.o from foo.c # foo.o foo.c 가 새로 생성되지 않는다.
touch foo.o
또한 .SECONDARY
타겟은
implicit rule 에서 intermediate file 이 자동으로 삭제되는 것을 방지합니다.
# intermediate 파일인 hello.c 를 SECONDARY 로 등록
.SECONDARY: hello.c
%.c : %.y
@echo making $@ from $<
@touch $@
%.o : %.c
@echo making $@ from $<
@touch $@
hello : hello.o
@echo making $@ from $<
@touch $@
######### 실행 결과 #########
sh$ touch hello.y
sh$ make
making hello.c from hello.y
making hello.o from hello.c
making hello from hello.o <--- intermediate 파일인 hello.c 가 자동으로 삭제되지 않는다
.SECONDEXPANSION:
이 타겟이 설정된 위치 이후로 존재하는 prerequisites 들은 두 번 확장됩니다.
( targets 은 포함되지 않습니다. )
.SECONDEXPANSION
이 갖는 기능은 두 가지 입니다.
첫째는 타겟 라인은 recipe 가 실행되기전 global 영역에서 처리되므로 기본적으로
prerequisites 에서 automatic 변수를 사용할 수 없는데요.
하지만 .SECONDEXPANSION
을 이용하면 가능합니다.
.SECONDEXPANSION 타겟의 prerequisites 은 사용되지 않습니다.
main_SRCS := main.c try.c test.c
lib_SRCS := lib.c api.c
.SECONDEXPANSION:
# 1. $$(patsubst %.c,%.o,$$($$@_SRCS)) --> $(patsubst %.c,%.o,$($@_SRCS))
# 2. $(patsubst %.c,%.o,$($@_SRCS)) --> main.o try.o test.o
main lib: $$(patsubst %.c,%.o,$$($$@_SRCS))
@echo $^
.DEFAULT: ;
########### 실행 결과 ###########
sh$ make main
main.o try.o test.o
sh$ make lib
lib.o api.o
둘째는 타겟 라인은 global 영역에서 변수들과 마찬가지로 작성 순서대로 처리됩니다.
그러므로 해당 타겟 라인 이전에 정의된 변수는 사용할 수 있지만
이후에 정의된 변수는 prerequisites 에 사용할 수 없습니다.
하지만 .SECONDEXPANSION
을 이용하면 global 영역에서 마지막으로 설정된 변수값을 사용할 수 있습니다.
# AA 변수 사용가능 # AA 변수 사용불가 # AA 변수 사용가능
.SECONDEXPANSION:
AA := foo bar zoo main : $(AA) main : $$(AA) # $$(AA)
@echo $@ : $^ @echo $@ : $^
main : $(AA)
@echo $@ : $^ AA := foo bar zoo AA := foo bar zoo
.DEFAULT: ; .DEFAULT: ; .DEFAULT: ;
#### 실행 결과 #### #### 실행 결과 #### #### 실행 결과 ####
main : foo bar zoo main : main : foo bar zoo
.SILENT:
recipe 에서 명령이 실행되기 전에 명령문이 출력되는 것을 disable 할때 사용합니다.
이 타겟을 이용하면 rule 별로 silent 설정을 할 수 있습니다.
prerequisites 을 사용하지 않으면 명령 라인에서 -s
( --silent ) 옵션을 사용한 것과 같습니다.
.SILENT: foo bar # foo, bar 타겟에만 적용
.SILENT: # 모든 타겟에 적용
.SUFFIXES:
suffix rules 을 정의할 때 사용하고자 하는 확장자를 이 타겟에 등록해야 합니다. suffix rules 은 예전에 implicit rules 을 정의할 때 사용하던 방법으로 지금은 obsolete 으로 구버전 호환성을 위해서만 존재합니다. 현재는 implicit rules 을 정의할 때 pattern rules 이 사용됩니다.
make 에 디폴트로 등록되어 있는 suffixes 는
SUFFIXES
변수를 통해 알아볼 수 있습니다.-r
( --no-builtin-rules ) 옵션을 사용하면 디폴트 suffixes 가 삭제됩니다.
.SUFFIXES: .md .html # .md .html 확장자를 등록해 사용
.md.html :
prog --in $< --out $@
---------------------------------------------------
# 다음과 같이하면 default suffixes 값이 모두 삭제됩니다.
.SUFFIXES: