본문 바로가기
IT 기술/개발환경_유틸 관련 팁

[Make] PHONY / FORCE

by 땅뚱 2012. 11. 12.

이 내용은 Phony Targets과 Rules without Recipes or Prerequisites 내용을 번역한 것이다.

(이전 포스트의 링크가 깨져서 새로운 링크를 달고 내용을 확인해서 수정하였다. 2021.4.13)

 

Phony Targets (GNU make)

4.6 Phony Targets A phony target is one that is not really the name of a file; rather it is just a name for a recipe to be executed when you make an explicit request. There are two reasons to use a phony target: to avoid a conflict with a file of the same

www.gnu.org

 

Force Targets (GNU make)

4.7 Rules without Recipes or Prerequisites If a rule has no prerequisites or recipe, and the target of the rule is a nonexistent file, then make imagines this target to have been updated whenever its rule is run. This implies that all targets depending on

www.gnu.org

1. Phony Targets(포니 타겟)

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

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

clean:
	rm *.o temp

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

하지만, 디렉토리에 clean이라는 이름의 파일을 생성하게 되면, 타겟 clean 은 제대로 동작하지 않는다. clean 파일은 선행조건이 없기 때문에, 항상 최신이라고 간주될 것이고, clean 레시피는 실행되지 않을 것이다. 이러한 문제를 해결하기 위해서, 다음과 같이 명백하게 .PHONY 지시자를 사용하여 특별한 타겟의 전제조건으로 만들어 준다.

.PHONY : clean

clean:
	rm *.o temp

참고: Special Built-in Target Names

 

Special Targets (GNU make)

The targets which .PRECIOUS depends on are given the following special treatment: if make is killed or interrupted during the execution of their recipes, the target is not deleted. See Interrupting or Killing make. Also, if the target is an intermediate fi

www.gnu.org

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

 

또한 포니 타겟은 make의 재귀적 호출과 관련되어 유용하게 사용된다. (참고 : Recursive Use of make) 재귀적 호출을 사용하는 경우, makefile 은 빌드되어야 할  많은 하위 디렉토리 목록을 갖는 변수를 자주 포함하게 될 것이다. 이러한 경우를 다루기 위한 간단한 방법 중 하나는 하위 디렉토리를 순회하는 레시피를 사용하여 규칙을 정의하는 것이다. 다음을 구문을 보자.

 

Recursion (GNU make)

5.7 Recursive Use of make Recursive use of make means using make as a command in a makefile. This technique is useful when you want separate makefiles for various subsystems that compose a larger system. For example, suppose you have a sub-directory subdir

www.gnu.org

SUBDIRS = foo bar baz

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

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

 

Parallel (GNU make)

5.4 Parallel Execution GNU make knows how to execute several recipes at once. Normally, make will execute only one recipe at a time, waiting for it to finish before executing the next. However, the ‘-j’ or ‘--jobs’ option tells make to execute many

www.gnu.org

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

SUBDIRS = foo bar baz

.PHONY: subdirs $(SUBDIRS)

subdirs: $(SUBDIRS)

$(SUBDIRS):
	$(MAKE) -C $@

foo: baz

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

포니 타겟에 대해서는 암시적 규칙 검색(Implicit rule search)이 적용되지 않는다(skipped) (참고 : Implicit Rules)

포니 타겟으로 선언하여 암시적 규칙 검색을 건너뛰는 것은, 실제 파일이 존재하는 것을 신경 쓰지 않아도 될 뿐만 아니라, 좋은 성능을 보여주는 이유가 된다.

 

Implicit Rules (GNU make)

10 Using Implicit Rules Certain standard ways of remaking target files are used very often. For example, one customary way to make an object file is from a C source file using the C compiler, cc. Implicit rules tell make how to use customary techniques so

www.gnu.org

포니 타겟은 실제 타겟 파일의 전제 조건(prerequisite)이 되지 않아야 한다. 만일 실제 타겟의 전제조건이 된다면, 해당 명령은 make 가 실제 타겟 파일이 업데이트 될 때마다 매번 수행된다. 포니 타겟이 실제 타겟의 전제조건이 되지 않는다면, 포니 타겟 명령은 포니 타겟이 명백하게 타겟(TARGET)으로 지정되는 경우에만 수행될 것이다.(참고 :Arguments to Specify the Goals)

 

Goals (GNU make)

Like ‘clean’, but may refrain from deleting a few files that people normally don’t want to recompile. For example, the ‘mostlyclean’ target for GCC does not delete libgcc.a, because recompiling it is rarely necessary and takes a lot of time.

www.gnu.org

포니 타겟도 전제 조건(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` 과 같이) PHONY 속성은 상속되지 않는다. PHONY 타겟의 전제 조건은 명백하게 PHONY 타겟으로 선언하지 않는다면 PHONY 가 아니다.

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

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
	rm program

cleanobj :
	rm *.o

cleandiff :
	rm *.diff

 

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

만일 규칙이 전제조건이나 레시피(recipe)가 없고, 그 규칙의 타겟이 존재하지 않는 파일이라면, make는 이 타겟을 규칙이 수행될 때마다 업데이트되어야 할 타겟으로 간주한다. 이것은 이러한 규칙과 관련된 모든 타겟은 항상 명령을 수행할 것이라는 것을 의미한다. 다음의 예가 잘 설명해준다.

clean: FORCE
	rm $(objects)
FORCE:

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

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

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