Programming

[Make] PHONY / FORCE

steloflute 2014. 11. 10. 23:30

http://pinocc.tistory.com/131


이 내용은 ftp://ftp.gnu.org/pub/pub/old-gnu/Manuals/make-3.79.1/html_chapter/make_4.html 중 일부를 번역한 내용이다.


1. Phony Targets(포니 타겟)

포 니 타겟은 실제 파일 이름을 나타내는 타겟 이름이 아니다. 이것은 명백하게 make 요청을 하는 경우에 실행되는 명령을 위한 목적으로 사용된다. 포니 타겟을 사용하는 이유는 두가지가 있는데, 첫번째는 동일한 이름의 파일을 사용한 충돌을 피하기 위함이고, 두번째는 make 성능향상을 위함이다.

만일 실제 타겟 파일을 생성하지 않는 명령을 갖는 규칙을 만든다고 한다면, 그 명령은 make 시에 매번 해당 target 을 실행하게 될 것이다. 예를 들어보자

clean:

rm *.o temp

rm 명령은 clean 이라는 이름의 파일을 생성하지 않기 때문에, 그런 파일은 존재하지 않을 것이다. 그래서 rm 명령은 make clean 을 수행할 때, 매번 실행될 것이다.

하 지만, 타겟 clean 은 다른 누군가가 디렉토리에 clean 이라는 이름의 파일을 생성하게 된다면, 작업을 멈추게 될 것이다. clean 파일은 이전에 존재하지 않았기 때문에, 필연적으로 최신이라고 간주될 것이다. 그리고 rm 명령은 실행되지 않을 것이다. 이러한 문제를 해결하기 위해서, 다음과 같이 특수 타겟 .PHONY를 사용해서, 해당 타겟을 명백하게 가짜로(phony) 선언할 수 있다.

.PHONY : clean

일단 이렇게 되면, make clean 명령은 clean 이라는 파일이 존재하는지 여부와 상관없이 명령을 수행할 것이다.

.PHONY 특수 타겟 지시자는 포니 타겟 이 다른 파일들로 부터 다시 만들어질 수 있는 실제 파일이름을 지칭하는 것이 아니라는 것을 알고 있기 때문에, 포니 타겟(이 부분은 Using Implicit Rules 를 참고해야 한다.)들을 위한 암시적 규칙 검색(implicit rule search) 부분을 건너뛴다. 이것이 포니 타겟으로 선언하는 것이 실제 파일이 존재하는 것을 신경쓰지 않아도 될 뿐만 아니라, 좋은 성능을 보여주는 이유가 된다.

그래서 clean 이 포니 타겟이라는 것을 알려주는 문장을 먼저 알려줘야 한다. 그리고 나서 다음과 같이 해당 규칙을 기록한다.

.PHONY: clean
clean:
        rm *.o temp

또 다른 포니 타겟의 또다른 유용한 예제는 make 의 재귀적 호출과 관련되어서 사용하는 경우이다. 재귀적 호출을 사용하는 경우 makefile 은 빌드되어야할  많은 하위 디렉토리 목록을 갖는 변수를 포함하게 된다. 이러한 경우를 하나의 규칙을 사용해서 다루기 위한 한가지 방법은 하위디렉토리에 대한 쉘의 루프 명령을 사용하는 것이다. 다음을 구문을 보자.

SUBDIRS = foo bar baz

subdirs:
        for dir in $(SUBDIRS); do \
          $(MAKE) -C $$dir; \
        done

이러한 방법은 몇가지 문제점을 가지고 있다. 우선 submake 에서 에러가 발생하는 경우에 이 규칙으로는 그러한 에러가 리포트되지 않는다. 그래서 어느 한 부분이 에러가 발생하더라도 디렉토리의 마지막까지 빌드를 계속진행할 것이다. 이것은 쉘 명령에 에러를 리포트하고 exit 하도록 추가함으로써 수정할 수 있다. 하지만, 불행히도 make 가 -k (keep going) 옵션을 사용하여 수행된 경우에도 멈추게 될 것이다. 그리고 더 중요한 것은, 단 하나의 규칙이기 때문에 이런 방법으로는 make 를 병렬로 build 하는 장점을 이용할 수 없게 된다.

이런 경우 하위 디렉토리들을 포니 타겟으로 선언함으로써 이러한 문제를 해결할 수 있다. (이 경우 주의할 점은 해당 하위 디렉토리가 반드시 존재해야 한다. 그렇지 않으면 빌드가 되지 않는다.)

SUBDIRS = foo bar baz .PHONY: subdirs $(SUBDIRS) subdirs: $(SUBDIRS) $(SUBDIRS): $(MAKE) -C $ foo: baz

여기서 선언한 대로라면, foo 하위 디렉토리는 baz 하위 디렉토리가 컴파일될 때까지 빌드될 수 없다. 이러한 종류의 관계 선언(relationship declaration) 은 빌드를 병렬화하려고 할때, 특히 중요하다.

포 니 타겟은 실제 타겟 파일이 전제되지 않아야 하기 때문에, 만일 그렇다면, 해당 명령은 make 가 그 파일을 업데이트하기 위해서 매번 수행된다. 포니 타겟이 실제 타겟을 절대 전제로 하지 않는한, 포니 타겟 명령은 포니 타겟이 목적으로 지정되는 경우에만 수행될 것이다.(이 부분은 Arguments to Specify the Goals 참조)

포 니 타겟도 전제 조건(prerequisites)을 가질 수 있다. 한 디렉토리가 여러개의 프로그램을 포함할 때, 모든 프로그램을 하나의 makefile `./Makefile` 에 포함하여 기술하는 것이 가장 편하다. 기본적으로 재작성(remade) 되는 타겟은 makefile 의 첫번째 타겟이기 때문에, 포니 타겟의 이름을 all 로 주고, 모든 프로그램을 all 타겟의 전제조건으로 주는 것이 보통이다.  다음의 예제를 보자.

all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
        cc -o prog1 prog1.o utils.o

prog2 : prog2.o
        cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
        cc -o prog3 prog3.o sort.o utils.o

이제 단순히 `make` 라고 하면 위 3개 프로그램을 재 컴파일할 수 있다. 또는 재컴파일할 프로그램을 인수로 지정할 수 있다.(`make prog1 prog3 과 같이)

하나의 포니 타겟이 또 다른 전제조건인 경우에 이것은 다른 것의 하위 루틴처럼 동작한다. 아래의 예에서 `make cleanall` 은 오브젝트 파일과, diff 파일, 그리고 모든 프로그램을 지울 것이다.

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
        rm program

cleanobj :
        rm *.o

cleandiff :
        rm *.diff


2. 명령이나 전제조건이 없는 규칙(rules without Commands or Prerequisites)

만 일 규칙이 전제조건이나 명령을 갖고 있는 않고, 그 규칙의 타겟이 존재하지 않는 파일이라면, make 는 이 타겟을 규칙이 수행될 때마다 업데이트되어야 할 타겟으로 간주한다. 이 규칙과 관련된 모든 타겟은 항상 명령을 수행할 것이라는 것을 의미한다. 예를 들어보면

clean: FORCE
        rm $(objects)
FORCE:

FORCE 라는 타겟은 특별한 조건을 만족한다. FORCE 에 의존적인 타겟 `clean` 은 rm 명령을 강제로 수행한다. `FORCE` 라는 이름에 대해서 특별한 것이 없지만, 이런 경우에 사용되는 일반적인 이름이다.

보시다시피, `FORCE` 를 이런 식으로 사용함으로써 `.PHONY: clean` 을 사용하는 것과 동일한 결과를 갖는다.

`.PHONY` 를 사용하는 것은 좀 더 명백하고 좀 더 효율적이다. 하지만, make 의 다른 버전은 .PHONY 를 지원하지 않는다. 그래서 `FORCE` 가 많은 makefile 에서 등장한다.


'Programming' 카테고리의 다른 글

Pythonic Clojure  (0) 2015.02.09
(emacs lisp) date/time  (0) 2015.02.03
Understanding “extern” keyword in C  (0) 2014.11.08
Smashing The Stack For Fun And Profit  (0) 2014.11.07
emacs tags 사용법  (0) 2014.10.31