[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2. 메이크파일에 대한 소개

make가 무슨 일을 할지 알려주려면 메이크파일(makefile)이라고 하는 파일이 필요하다. 많은 경우에, 메이크 파일은 make가 어떻게 프로그램을 컴파일하고 링크해야 할 지 알려준다.

이 장에서는, 여덟개의 C 소스 파일과 세개의 헤더 파일로 이루어진 문서 편집기를 어떻게 컴파일하고 링크할 지 기술해 놓은 간단한 메이크파일에 대해 공부할 것이다. 이 메이크파일은 명시적으로 요청되었을 경우, make가 여러가지 잡다한 명령어들을 실행하도록 할 것이다 (예를 들어, 새롭게 지우는 작업에서 파일들을 지우려고 할 때). 보다 복잡한 메이크 파일의 예를 보고 싶으면 복잡한 메이크파일 예제을 본다.

make가 이 편집기를 다시 컴파일할 때마다, 변경된 각 C 소스 파일이 다시 컴파일되야 한다. 만약 헤더 파일이 변경될 경우, 안전하게 하려면 이 헤더 파일을 포함하는 C 소스 파일도 다시 컴파일되야 한다. 각 컴파일 작업은 소스 파일에 해당하는 오브젝트 파일을 만든다. 마지막으로, 모든 소스 파일이 다시 컴파일되었으면, 모든 오브젝트 파일은 (다시 컴파일되었든 전에 컴파일되었든 간에) 같이 링크되어 새로운 편집기 실행 파일을 만들어야 한다.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.1 룰이 어떻게 생겼나

간단한 메이크 파일은 다음과 같은 모양의 “룰(rule)”로 구성되어 있다:

 
타겟 … : 의존성명령
        …
        …

타겟(target)은 프로그램이 만들어 내는 파일의 이름이다; 타겟의 예는 실행 파일 혹은 오브젝트 파일의 이름이다. 타겟은 ‘clean’과 같이 수행할 동작을 가리키는 이름이 될 수도 있다. (see section 포니 타겟).

의존성(dependency)는 타겟을 만들때 쓰이는 입력파일이다. 한개의 타겟이 여러개의 파일에 의존하기도 한다.

명령(command)는 make가 수행하는 동작이다. 한개의 룰은 두 개 이상의 명령어로 구성될 수도 있고, 한 줄에 한 개씩의 명령어를 쓴다. 주의: 각 명령의 줄의 시작 부분에 탭 문자를 넣어야 한다! 겉보기에 불분명하기 때문에 부주의하면 쉽게 실수할 수 있다.

보통 룰 안에 의존성과 함께 있는 명령어는 의존성중의 하나라도 바뀔 때마다 타겟 파일을 새로 만드는 일을 한다. 하지만, 의존성이 필요없는 타겟의 경우 룰에서는 명령어만을 지정한다. 예를 들어, ‘clean’ 타겟과 연결된 지우는 명령어가 들어 있는 룰은 의존성이 없다.

즉, 은, 그 룰의 타겟인 파일들을 언제 어떻게 다시 만들어 낼지를 설명한다. make는 의존성에 따라 타겟을 새로 만들거나 업데이트할 명령어를 실행한다. 룰은 어떤 동작을 언제 어떻게 수행할지 설명하기도 한다. See section 룰 작성하기.

메이크파일은 룰 이외의 다른 것들도 들어 있지만, 간단한 메이크파일은 이러한 룰들만 있으면 도니다. 룰은 위의 틀에서 보여준 것보다 훨씬 복잡할 수도 있지만, 모든 룰은 위의 패턴에 정확히 들어 맞는다.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.2 간단한 메이크파일

다음은 edit라고 하는 실행 파일이 여덟개의 오브젝트 파일에 의존하고, 차례로 이 오브젝트 파일은 여덟개의 C 소스 파이로가 세개의 헤더 파일에 의존하는 경우를 기술한 간단한 메이크파일이다.

이 예에서, 모든 C 파일은 ‘defs.h’ 파일을 포함하고, 편집 명령을 정의한 몇개 파일들만 ‘command.h’ 파일을 포함하고, 편집 버퍼를 변경하는 낮은 레벨의 파일들만 ‘buffer.h’를 포함한다.

 
edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o

우리는 긴 줄을 백슬래쉬-줄바꿈을 이용해 여러 줄로 쪼개 놓았다; 이건 하나의 긴 줄을 사용한 것과 다를 바가 없지만, 읽기 쉽다.

이 메이크 파일을 이용해 ‘edit’라는 실행 파일을 만드려면, 다음과 같이 타이프한다:

 
make

이 메이크 파일을 이용해 디렉토리 내의 실행 파일과 모든 오브젝트 파일을 지우려면, 다음과 같이 타이프한다:

 
make clean

위의 메이크파일 예에서, 타겟은 실행 파일인 ‘edit’, 그리고 오브젝트 파일인 ‘main.o’, ‘kbd.o’와 같은 것들이다. 의존성은 ‘main.c’와 ‘defs.h’와 같은 파일들이다. 사실, 각 ‘.o’ 파일들은 타겟이기도 하고, 의존성이기도 하다. 명령어는 ‘cc -c main.c’와 ‘cc -c kbd.c’와 같은 것들이다.

타겟이 파일이면, 그 의존성이 바뀔 때마다 다시 컴파일되거나 다시 링크되어야 한다. 여기에 추가로, 의존성이 자동으로 만들어 지는 경우, 그 파일들이 먼저 업데이트되야 한다. 이 예에서, ‘edit’는 각 여덟개의 오브젝트 파일에 의존한다; 오브젝트 파일 ‘main.o’는 ‘main.c’ 소스 파일과 ‘defs.h’ 헤더 파일에 의존한다.

타겟과 의존성이 들어 있는 줄 바로 다음에 셸 명령어가 나타난다. 이 셸 명령어는 어떻게 타겟 파일을 업데이트해야 할지 알려준다. 명령어가 있는 각 줄의 맨 처음에는 반드시 탭 문자를 써서, 메이크파일의 다른 줄과 구분할 후 있어야 된다. (make는 명령어가 어떻게 동작하는지에 대해서는 아무것도 모른다는 사실을 명심하라. 타겟 파일을 제대로 업데이트할 수 있는 명령어를 쓰는 건 여러분에게 달려 있다. make가 하는 일은 지정된 룰에 따라 타겟 파일이 업데이트되야 할 순간에 명령어를 실행하는 것 뿐이다.)

clean’ 타겟은 파일이 아니라, 어떤 동작의 이름일 뿐이다. 보통 상황에서는 이 룰에 쓰여 있는 명령을 수행하지 않을 것이므로, ‘clean’은 다른 어떠한 룰의 의존성도 아니다. 결과적으로, make는 직접 지정하지 않는 한 이 룰에 있는 명령을 절대로 실행하지 않는다. 즉 이 룰의 유일한 목적을 지정한 명령어들을 실행하는 것 뿐이다. 파일과 관련이 없고, 어떤 동작을 하는 것뿐인 타겟을 포니 타겟(phony target)이라고 부른다. 이러한 타겟에 대한 정보는, See section 포니 타겟. rm과 같은 명령어에서 나오는 애러를 make가 무시하도록 만드는 방법을 보려면, See section Errors in Commands.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.3 어떻게 make가 Makefile을 처리하는가

기본사항으로, make는 맨 첫번째 타겟(그 이름이 ‘.’으로 시작하지 않는 타겟)에서부터 시작한다. 이 타겟을 디폴트 골(default goal)이라고 한다. (()은 make가 최종적으로 업데이트하고자 하는 타겟을 말한다. See section Arguments to Specify the Goals.)

앞 절의 간단한 예제에서, 디폴트 골은 ‘edit’ 실행 파일을 업데이트하는 것이다; 다시 말해서, 우리는 이 룰을 맨 처음에 써 놓았다.

그러므로, 다음 명령을 실행하면:

 
make

make는 현재 디렉토리의 메이크파일을 읽고 맨 첫번째에 나오는 룰을 처리하기 시작한다. 이 예에서, 이 룰은 ‘edit’를 다시 링크하는 것이다; 하지만 make가 이 룰을 완전히 처리할 수 있으려면, ‘edit’가 의존하고 있는 파일들에 대한 룰, 즉 이 경우에는 오브젝트 파일들에 대한 룰들을 먼저 처리해야 한다. 이 룰들은 각 소스 파일을 컴파일해서 각 ‘.o’ 파일을 업데이트하는 법에 대해 쓰여 있다. 소스 파일, 혹은 의존성에 이름이 쓰여진 어떤 헤더 파일이라도 오브젝트 파일보다 나중에 변경되었을 경우, 혹은 오브젝트 파일이 없을 경우에만 이 재컴파일 과정이 실행된다.

그 타겟이 골의 의존성 부분에 나타났기 때문에 그 외의 룰들이 처리된다. 골이 의존하지 않는 룰이 있다면 (혹은 아무것도 여기에 의존하지 않는 경우), 그렇게 하라고 일부러 make에게 알려주지 않는 한 (make clean과 같은 명령으로), 그 룰은 처리되지 않는다.

오브젝트 파일을 컴파일하기 전에, make는 그 의존성, 즉 소스 파일과 허데 파일을을 업데이트해야 할지 생각한다. 이 메이크파일은 여기에 대해서 무슨 일을 할지 지정하지 않는다—‘.c’와 ‘.h’ 파일은 어떤 룰의 타겟도 아니다—즉 make는 이 파일에 대해 아무것도 하지 않는다. 하짐나 make는 Bison이나 Yacc가 만들어 내는 파일들 처럼, 자동으로 만들어지는 C 프로그램과 같은 경우에는 지정된 룰에 따라 업데이트한다.

필요한 오브젝트 파일이 모두 다시 컴파일된 다음에, make는 ‘edit’를 다시 링크할지 아닐지 결정하게 된다. ‘edit’ 파일이 없거나, 오브젝트 파일중의 하나라도 ‘edit’ 파일보다 최근에 수정된 경우 다시 링크해야 한다. 오브젝트 파일이 막 컴파일된 경우, ‘edit’ 파일보다 최근에 수정되었을 테니, ‘edit’가 다시 링크된다.

그래서, ‘insert.c’ 파일을 변경하고 make를 실행한 경우, make는 ‘insert.c’를 컴파일해서 ‘insert.o’ 파일을 업데이트하고, ‘edit’를 링크한다. ‘command.h’ 파일을 바꾸고 make를 실행하면, make는 ‘kbd.o’, ‘command.o’, 그리고 ‘files.o’ 오브젝트 파일을 다시 컴파일하고 ‘edit’ 파일을 링크할 것이다.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.4 변수는 메이크파일을 간단하게 만든다

우리가 사용하고 있는 예에서, ‘edit’의 룰에 있는 오브젝트 파일을 두번 반복해서 열거해야 한다 (여기에 다시 한번 반복한다):

 
edit : main.o kbd.o command.o display.o \
              insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

이렇게 중복해서 쓰면 애러가 발생하기 쉽다: 만약 새로운 오브젝트 파일이 이 시스템에 추가될 경우, 어떤 한 부분에만 추가하고, 다른 부분에서는 잊어버릴 수도 있다. 변수를 사용해서 이런 위험을 없애고 메이크파일을 간단하게 만들 수 있다. 변수(variable)는 일단 한번 어떤 문자열로 지정되면 나중에 여러 군데에서 그 문자열 대신에 사용될 수 있다 (see section 변수 사용하는 법)

각 메이크 파일이 objects, OBJECTS, objs, OBJS, obj, 혹은 OBJ라는 이름의 변수가 있어서 모든 오브젝트 파일의 이름의 리스트를 갖고 있는 것이 표준적으로 쓰이는 방법이다. 메이크 파일에 다음과 같은 줄로 이러한 objects 변수를 정의할 수 있을 것이다:

 
objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

그 다음에, 오브젝트 파일의 이름을 쓰고 싶은 곳마다, ‘$(objects)’라고 쓰면 변수의 값으로 대체된다. (see section 변수 사용하는 법).

다음은 오브젝트 파일에 대한 변수를 사용했을 경우 이 간단한 메이크 파일이 어떻게 되는지 보여준다:

 
objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)
main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit $(objects)

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.5 make의 명령어 줄이기

각 C 소스 파일을 컴파일하는 명령을 매번 쓸 필요가 없다. 왜냐하면 make가 이미 알고 있기 때문이다: ‘.o’ 파일에 해당되는 ‘.c’ 파일에서부터 ‘cc -c’ 명령을 사용해서 ‘.o’ 파일을 업데이트하는 암묵적인 룰(implicit rule)이 있다. 예를 들어, ‘main.c’를 컴파일해서 ‘main.o’를 만들 때는 ‘cc -c main.c -o main.o’ 명령을 사용할 것이다. 그러므로 오브젝트 파일에 대한 룰에서 명령어들을 생략할 수 있다. See section 암묵적인 룰 사용하기.

.c’ 파일을 이렇게 자동으로 사용할 경우, 의존성 리스트에도 자동으로 추가된다. 그러므로 명령어를 생략한 것처럼, 의존성에서 ‘.c’ 파일도 생략할 수 있다.

다음은 이 두가지 변경사항을 포함한 예제 메이크 파일의 전체인데, 여기서 변수 objects는 전에서 제시된 것처럼 변경되었다:

 
objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
        -rm edit $(objects)

이것이 실제 활용할 때 메이크 파일을 작성하는 방법이다. (‘clean’과 관련된 복잡한 사항들은 다른 곳에서 설명한다. 포니 타겟, and see section Errors in Commands을 보기 바란다.)

암묵적인 룰은 매우 편리하므로, 매우 중요하다. 여러분은 이 암묵적인 룰들이 자주 사용되는 걸 보게 될 것이다.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.6 메이크파일의 또 다른 스타일

메이크파일의 오브젝트 파일들이 암묵적인 룰에 의해 만들어 지는 경우, 또 다른 스타일의 메이크파일도 가능하다. 이 메이크파일의 스타일에서, 타겟 대신에 의존성들을 하나로 합친다. 이 스타일은 다음과 같다:

 
objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

defs.h’는 모든 오브젝트 파일의 의존성으로 주어졌다; ‘command.h’와 ‘buffer.h’는 거기에 열거된 지정된 오브젝트파일의 의존성이다.

이 스타일이 더 좋은지 아닌지는 취향의 차이이다: 이 스타일은 더 간단하지만, 각 타겟에 대한 정보를 한 군데에 쓰는 편이 더 깨끗하다고 생각하는 사람들은 이 스타일을 싫어한다.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.7 디렉토리를 청소하는 룰

프로글매을 컴파일하는것만이 룰을 작성하는 목적은 아니다. 메이크파일은 프로그램을 컴파일하는 것 이외의 동작을 어떻게 하는지 알려주기도 한다: 예를 들어, 모든 오브젝트 파일과 실행 파일을 지워서 디렉토리를 청소(‘clean’)하는 방법과 같은 것을 기능을 하는 룰이 있다.

다음은 우리의 예제 편집기를 청소하는 make 룰이다.

 
clean:
        rm edit $(objects)

실제 경우, 예상치 못한 상황에 대처할 수 있도록 좀 더 복잡하게 룰을 작성해야 할 것이다. 다음과 같이 한다:

 
.PHONY : clean
clean :
        -rm edit $(objects)

이렇게 하면 ‘clean’이라는 이름의 실제 파일이 있을 경우에도 make가 혼동되는 일이 없고 rm에서 애러가 발생해도 계속한다. (포니 타겟, and see section Errors in Commands 보라.)

이와 같은 룰은 메이크 파일의 맨 앞에 위치해서는 안된다. 이 룰이 기본 룰로 동작시키려고 하지는 않을 테니 말이다! 즉, 이 메이크파일 예제에서, 편집기를 다시 컴파일하는 edit 룰이 기본 골로 남이 있어야 할 것이다.

cleanedit의 의존성이 아니므로, make 명령을 인자 없이 실행시켰을 경우 이 룰은 실행되지 않을 것이다. 이 룰이 실행되도록 하려면 ‘make clean’이라고 타이프해야 한다. See section make 실행하는 법.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Autobuild on September 28, 2013 using texi2html 1.82.