하나의 프로젝트에서 여러 파일을 다루고 다른 사람들과 함께 작업해 나가는 것은 개발자에게는 당연한 과정이며 꼭 필요한 부분이다. 그런 점에서 make를 사용하는 방법을 배우는 것은 상당히 중요한 일이라 할 수 있다.
make는 명령을 생성하는 유틸리티이다. 이들 명령은 주로 소프트웨어 개발 프로젝트를 수행하면서 작성되는 여러 파일들을 관리하는 것과 관련이 있다. 여기서 말하는 '파일 관리'란 상태를 보고하고 임시 파일을 제거하는데서부터 복잡한 프로그램 그룹의 최종 실행 파일을 만드는데 이르기까지 일련의 작업을 가리킨다.
콜론(:)을 포함하는 종속행과 탭 문자로 시작하는 하나 이상의 명령 행이 있다. 종속행에서 콜론 왼쪽은 target(program)이며, 콜론 오른쪽은 target의 필요 항목(prerequisite)이다. 탭 문자로 시작하는 명령 행은 필요 항목을 가지고 target을 작성하는 방법을 제시한다.
file.o : file.c --> 종속행(target : dependencies) (프로그램 : 필요 항목)
cc -c file.c --> 명령행(필요 항목을 가지고 target을 작성하는 방법을 제시)
탭 문자
make에서 가장 중요한 구문 규칙은 모든 명령들은 탭 문자로 시작한다는 것이다.
긴 행
행이 길어질 경우, 끝에 역슬래시(\)를 추가하여 계속 이어갈 수 있다. 역슬래시 뒤에는 어떤 여백 문자도 두어서는 안된다. 행을 계속 이어가기 위해 역슬래시를 사용하는 경우, make는 이를 하나의 공백 문자로 치환한다.
변수(매크로) 정의
변수(variable)란 텍스트 문자열(변수 값)을 표현하기 위해서 makefile에 정의된 이름이다. make의 어떤 버전에서는 변수를 매크로(macro)라고 부른다. 변수 정의는 등호를 포함하는 하나의 문장(행)이다. 문자열은 작은 따옴표('')나 큰 따옴표("")로 구분해서는 안된다. 변수 이름은 대소문자를 구분하므로 'foo', 'FOO', 'Foo'는 모두 서로 다른 변수가 된다. 변수 값 앞에 있는 공백은 변수 할당에서 무시된다. 변수 이름에 대해서 대문자 알파뱃을 사용하는 것이 전통적이지만 makefile 안에서 내부적인 목적으로 사용되는 변수 이름은 소문자 알파벳을 사용한다.
name = text string
변수를 참조할 때, 즉 make가 변수를 그 정의로 치환토록 할 때는 변수를 괄호(())나 중괄호({})로 둘러싸고, 그 앞에 달러 표시($)를 붙인다. $의 특별한 의미 때문에 단일 달러 기호의 효과를 가지기 위해서는 반드시 '$$'를 써야 한다.
$(name) 또는 ${name}
문자 하나로 이루어진 변수 이름에는 괄호나 중괄호를 사용할 필요가 없다.
A = XYZ
$A와 $(A) 그리고 ${A}는 모두 동일하다.
변수를 정의하지 않고 참조할 경우, make는 그것을 널 문자열로 치환한다. 따라서 정의하지 않은 변수를 사용해도 오류 메세지는 생기지 않는다. 변수는 반드시 사용할 항목보다 먼저 정의해야 한다.
다양한 소스에서, 한 변수에 서로 다른 정의를 내린 경우, 다음과 같은 우선 순위를 매겨 사용한다.
1. make 명령 입력시 make 명령 다음에 입력한 변수
2. 기술 파일의 변수 정의
3. 현재 셸 환경 변수
4. make의 내부(기본) 정의
변수의 특징
= 다른 변수에 대해 재귀적으로 확장
:= 단순 확장
+= 기존 변수에 값 추가
컴파일에 필요한 경로는 변수로 설정할 수 있다. 이를 통해 설치가 변경되더라도 쉽게 수정할 수 있다. 변수에는 두 가지 종류가 있다. 재귀적으로 확장되는 변수(Recursively expanded variable)와 간단히 확장되는 변수(Simply expanded variable)가 그것이다. 이 두 변수는 그것이 확장될 때 무엇을 하는가와 정의된 방법에 따라 구분된다.
재귀적으로 확장되는 변수(Recursively expanded variables)
재귀적으로 확장되는 변수는 이름과 값 사이에 '='를 사용하고 변수가 필요할 때 그 값이 결정된다. 이 변수는 재귀적으로 정의 될 수 없다. 왜냐하면 무한 루프에 빠지게 되기 때문이다. 이 변수가 다른 변수에 대한 참조를 담고 있다면 이런 참조는 그 변수가 대입될 때마다 확장된다. 이것이 일어날 때 이것을 recursive expansion이라고 부른다.
● OK: class_example_d = $(class_d)/example
(정의문에서 다른 변수를 참조한 경우)
● Not OK: class_d = $(class_d)/example
(재귀적 정의: 정의문에서 자신을 참조한 경우)
예를 들어,
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all :
@echo $(foo)
이것은 'Huh?'를 에코한다: '$(foo)'는 '$(bar)'로 확장된다. 그리고 이것은 '$(ugh)'로 확장된다.
이것의 장점은 다음과 같다:
CFLAGS = $(include_dirs) -O
include_dirs = -lfoo -lbar
이것은 'CFLAGS'가 명령행에서 의도된대로 '-lfoo -lbar -O'로 확장될 것이다. 그러나 가장 큰 단점은 변수의 마지막에 무언가를 덧 붙일 수 없다는 것이다. 다음과 같이:
CFLAGS = $(CFLAGS) -O
이것은 변수 확장 과정에서 무한 루프를 발생시킨다(실제로 make는 무한 루프를 검출하고 에러를 보고한다).
단순하게 확장되는 변수(Simply expanded variables)
단순하게 확장되는 변수는 이름과 값 사이에 ':='를 사용해서 정의하고 make가 이 변수를 만날 때 바로 값이 평가된다. 즉, 변수가 정의될 때 다른 변수와 함수에 대한 참조를 확장하면서 단 한번만 스캔된다. 재귀적 정의도 가능하며, 이때 오른쪽에 있는 변수 값은 이 변수의 이전 값이 된다. 단순하게 확장되는 변수는 GNU make의 고유한 기능이며 다른 버전의 make에는 존재하지 않을 수도 있다.
● OK: class_example_d := $(class_d)/example
● OK: class := $(class)/example
(이전에 class가 정의되어 있다면 OK)
예를 들어,
x := foo
y := $(x) bar
x := later
all :
@echo $(y)
@echo $(x)
는 다음과 같다:
y := foo bar
x := later
단순하게 확장된 변수가 참조될 때 그것의 값은 있는 그대로 대입된다. 이것은 일반적으로 복잡한 makefile 프로그래밍을 좀 더 예측 가능하도록 만든다.
조건 변수 할당 연산자(Conditional variable assignment operator)
변수가 아직 정의되지 않았을 경우에만 효력을 가진다.
FOO ?= bar
다음과 정확하게 동일하다:
ifeq ($(origin FOO), undefined)
FOO = bar
else
endif
변수에 텍스트를 덧 붙이기
이미 정의된 변수의 값에다 텍스트를 추가할 때, 다음과 같이 '+='을 사용한다.
object += another.o
이것은 objects 변수의 값을 취해서 텍스트 'another.o'를 그것에 더한다(앞에 하나의 공백을 붙인다). 그래서:
objects = main.o foo.o bar.o utils.o
objects += another.o
이것은 objects를 'main.o foo.o bar.o utils.o another.o'로 설정한다.
'+='을 사용하는 것은 다음과 비슷하다:
objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o
변수 대입 참조(Substitution References)
대입 참조는 한 변수의 값을 지정하는 다른 값으로 대입한다. '$(var:a=b)' (또는 '${var:a=b}' 형식이며, 이것의 의미는 변수 var의 값을 취해서 한 단어의 뒤에 있는 각각의 a를 각각의 b로 교체하고 결과 문자열을 대입한다는 것을 의미한다.
"한 단어의 뒤에 있는"이라고 말할 때, a가 대체되기 위해서는 이것이 공백 문자가 뒤에 있도록 또는 그 값이 마지막에 있도록 나타나야 한다는 것을 의미한다. 그 값안에서 다른 위치에 있는 a는 변경되지 않을 것이다.
예를 들어:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
이것은 'bar'를 'a.c b.c c.c'로 설정한다.
아래는 또한 같은 결과를 나타낸다.
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
이것은 'bar'를 'a.c b.c c.c'로 설정한다.
변수 문자열 치환
SRCS = def.c redraw.c calc.c
FINAL_OBJS = ${SRCS:.c=.o}
DEBUG_OBJS = ${SRCS:.c=_dbg.o}
위 예제의 경우, make는 {SRCS}를 평가하고 콜론 바로 뒤에 오는 문자열을 찾은 다음, 등호 기호 뒤의 문자열로 치환한다. 이 기능을 이용하면 마지막에 첨부된 문자만 다른 여러 파일로 이뤄진 그룹을 쉽게 관리할 수 있다. 따라서 나중에 소스 파일을 추가하거나 제거해도, SRCS 변수 정의만 바꿔주면 된다.
${FINAL_OBJS} --> def.o redraw.o calc.o
${DEBUG_OBJS} --> def_dbg.o redraw_dbg.o calc_dbg.o
단순히 ${DEBUG_OBJS}의 항목들만 이용하여 실행 파일을 작성할 경우, 항목을 다음과 같이 사용하면 된다.
plot_debug: ${DEBUG_OBJS}
${CC} -o plot_debug ${DEBUG_OBJS}
문자열 치환은 매우 엄격하게 제한된다. 즉 변수의 마지막 부분이나 공백 문자 바로 앞까지만 적용된다.
LETTER = xyz xyzabc abcxyz
...
echo ${LETTERS:xyz=DEF}
--> DEF xyzabc abcDEF
이는 abc가 xyz의 존재를 숨기기 때문이다.
치환에서 두 번째 문자열은 널 문자열이 될 수 있지만, 첫 번째 문자열은 불가능하다.
SOURCES = src1.c src2.c glob.c
EXEC = $(SOURECS:.c=)
echo ${EXEC}
--> src1 src2 glob
필요 항목과 target용 내부 변수
$@ 현재 target의 전체 이름(명령행에서만). 일반 기술 파일 항목과 확장자 규칙에서 사용할 수 있다.
OBJS = basic.o prompt.o
plot_prompt : basic.o prompt.o
cc -o $@ ${OBJS} --> cc -o plot_prompt basic.o prompt.o
주의! cc 명령은 반드시 프로그램을 구성하는 모든 오브젝트 모듈을 링크해야 한다.
위 $@ 예제에서 ${OBJS} 대신 $?를 사용하면 안된다.
$?는 target보다 나중에 수정된 필요한 파일들만 표시할 뿐이다.
$? 현재 target보다 최근에 변경된 필요 항목들의 리스트(명령행에서만). 일반 기술 파일 항목에서만 사용할 수 있다.
libops : interact.o sched.o gen.o
ar r $@ $? --> ar r libops sched.o (나중에 sched.o 만 갱신되었다고 가정)
$$@ 현재 target의 이름(종속행의 필요 항목에서만). 하나의 소스 파일을 갖는 실행 파일들의 수가 많을 때
--> cc -O dd.c -o dd (나중에 dd.c 만 갱신되었다고 가정)
$< 현재 target보다 최근에 변경된 필요 항목의 전체 이름. 확장자 규칙과 .DEFAULT 항목에서만 사용될 수 있다는 점을 제외하면 $?와 유사하다.
$* 현재 target보다 최근에 변경된 필요 항목의 확장자를 제외한 이름. 확장자 규칙에서만 사용할 수 있다.
$% 현재 target이 라이브러리 모듈일 때 .o 파일에 대응되는 이름. 일반 기술 파일 항목과 확장자 규칙에서 사용할 수 있다.
$^ 모든 필요 항목의 리스트. 디렉토리 이름을 포함하고 이들 사이는 공백으로 구분된다. 일반 기술 파일 항목에서 사용할 수 있다.
또한 한 문자로 구성된 변수의 구문 규칙을 적용할 수도 있다. 즉, $@을 $(@)나 ${@}으로 쓸 수 있는 것이다.
확장자 규칙
미리 정의해 놓은 일반화한 기술 파일 항목. 필요 항목이 몇몇 확장자 가운데 하나를 갖거나, 사용자가 제공한 그 어떤 명시적인 명령도 가지고 있지 않을 경우, make는 적절한 확장자 규칙을 찾게 된다. 즉, program이 만들어지기 전에 필요 항목 파일 가운데 어떤 파일이 만들어져야 하는지 확인한다.
.SUFFIXES : .o .c .s
.SUFFIXES 행은 make가 중요하게 여길 확장자를 나타낸다.
.c :
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
.c 확장자의 파일로부터 널 확장자의 파일(실행 프로그램)을 만드는 방법을 설명한다.(널 확장자가 .c와 콜론 사이에 있다고 생각) 이 규칙은 하나의 소스 파일에서 작성할 때 오브젝트 파일을 다루지 않도록 해준다.
.c.o :
$(CC) $(CFLAGS) -c $<
이 규칙은 .c에서 .o파일을 만드는 방법을 설명하고 있다.
OBJS = main.o iodat.o dorun.o lo.o
LIB = /usr/proj/lib/crtn.a
program : ${OBJS} ${LIB}
${CC} -o $@ ${OBJS} ${LIB}
$ make program --> cc -o program main.o iodat.o dorun.o io.o /usr/proj/crtn.a
어떤 확장자에 대해 확장자 규칙이 정의되어 있더라도 .SUFFIXES 리스트에도 함께 정의되어야 효력이 있다. make는 .SUFFIXES 리스트의 확장자 순서에 따라 파일을 검색한다.
make는 현재의 작성에서 사용한 모든 변수와 확장자 규칙, 그리고 target을 출력하는 유용한 -p 옵션을 제공한다.
명령옵션
CFLAGS와 ASFLAGS 변수는 명령에 대한 플래그 옵션을 전달한다.
make program CFLAGS=-g --> cc -g -c main.c
make의 명령 행에서 단일 변수에 여러 옵션들을 주려면, 전체 정의에 큰따옴표("")를 쳐주면 된다.
make program "CFLAGS=-g -DTRACE" --> cc -g -DTRACE main.c
기본으로 정의된 CFLAGS는 무효화한다.
주의! 기술 파일 항목에서 라이브러리 옵션들을 변경한 다음, target들에 대해 make를 실행하면, 최신 버전의 target들이 이미 존재할 경우, make는 그것을 다시 작성하지는 않는다. make는 무엇보다도 파일 이름과 변경 시간에서만 정보를 얻는다.
라이브러리
각 오브젝트 파일을 갱신한다.
libops : interact.o sched.o gen.o
ar r $@ $?
사용자들이 라이브러리 내에서 각 파일을 지정할 수 있다.
libops(interact.o), ${LIB}(interact.o)
괄호를 중복 사용하여 라이브러리 내의 특정 모듈을 참조할 수 있다.
libops((_parse))
구문 내에서는 여백 문자를 사용해서는 안된다.
확장자 규칙은 또한 라이브러리를 다시 생성하는 것도 지원한다. 다음은 C 소스 파일이 변경되었을 때 라이브러리를 갱신하는 기본 규칙을 보여준다.
.c.a :
$(CC) -c $(CFLAGS) $<
ar rv $@ $*.o
rm -f $*.o
특정 파일에 의존 관계들을 지정해주기만 하면 된다.
prog : libops(interact.o) libops(sched.o) prog_call.o
${CC} -o $@ prog_call.o libops
파일을 정확히 알지 못하더라도 다음과 같이 모듈을 지정할 수 있다.
prog : libops((_parse)) libops((_waitloop)) prog_call.o
${CC} -o $@ prog_call.o libops
이처럼 모듈을 지정하는 것은 make에 해당 파일을 가리켜 줄 뿐, 해당 모듈이 변경되었는지 여부를 점검토록 해주지는 않는다. interact.c 내에 있는 어떤 모듈 하나라도 바뀐 경우, _parse에만 관심이 있더라도 make는 libops(interact.o) 파일 전체를 다시 작성해야 한다.
libops가 마지막으로 작성된 이후 interact.c가 변경되었다고 가정해보자. 처음 세 명령은 .c.a 규칙에 의해 실행되었으며, 마지막 명령은 기술 파일에 명시되어 있다(ar 명령 다음 줄은 ar 명령이 라이브러리에서 interact.o를 치환할 때 표시하는 메세지이다).
$ make prog
cc -c -O interact.c
ar rv libops interact.o
r - interact.o
rm -f interact.o
cc -o prog prog_call.o libops
.c.a 규칙에 따라 libops를 작성하는 동안, make는 처음에는 내부 변수들을 아래와 같이 설정한다.
$@ = libops
$< = interact.c
$* = interact
공통된 정의(문장)의 사용
대형 프로젝트에서 기술 파일이 여러 군데서 사용되면 프로젝트 팀에서는 변수 정의와 확장자 규칙을 한 곳에 모으는 것에 대해 생각해 보아야만 한다. 이것을 지원하기 위해 make는 include문을 제공한다.
include file
여기서 include라는 단어는 반드시 줄의 처음에서 시작해야 하며 다음에 공백 문자나 탭이 따라와야 한다. 'file'의 모든 행을 현재의 기술 파일로 옮겨 적는 것과 동일하다.
include 파일에 의존 항목 행을 둔다면, 변수를 참조하는 의존 항목 행 이전에 해당 변수가 정의되어 있어야만 한다. 정의는 다음 중 하나의 위치에 있어야만 한다.
1. include 파일 내에 의존 항목 행 이전
2. 사용자 기술 파일에서 include문 이전
3. make 명령 행
4. 사용자의 환경 변수
에코
일반적으로 make는 각 명령 라인이 실행되기 전에 디스플레이한다. 이것을 echoing이라고 부른다.
어떤 라인이 '@'으로 시작할 때, 그 라인의 에코는 나타나지 않는다. '@'는 명령이 셸에 전달되기 전에 버려진다. Makefile을 통해서 진행되는 상황을 표시하기 위해서 echo와 같은 명령을 사용한다.
@echo About to make distribution files
make는 '-n' 또는 '--just-print' 플래그를 받으면 실행없이 에코만 한다. 이런 경우에는 '@'으로 시작하는 명령들 조차도 출력된다. make는 '- s' 또는 '-- silent' 플래그를 받으면 모든 명령이 마치 '@'으로 시작하는 것처럼 모든 에코를 금지시킨다.
와일드 카드
하나의 파일 이름을 wildcard character를 사용해서 많은 파일을 지정할 수 있다. make에서 와일드 카드 문자는 '*', '?', '[...]'이다. 예를 들어, '*.c'는 이름이 '.c'로 끝나는 모든 파일(현재 작업 디렉토리 안에서)의 리스트를 지정한다.
와일드 카드 문자의 특별한 용도는 그 앞에 역슬래시를 붙임으로써 없어진다.
다음은 의도한 바대로 작동하지 않는 와일드 카드 확장을 사용하는 간단한 예제이다. 실행 파일 'foo'가 모든 오브젝트 파일들로 부터 만들어지도록 하기 위해 다음과 같이 작성했다고 가정하자:
objects = *.o
foo : $(objects)
cc -o foo $(CFLAGS) $(objects)
objects의 값은 실제 문자열 '*.o'이다. 와일드 카드 확장은 'foo'를 위한 규칙에서 일어난다. 그래서 각 현존하는 '.o' 파일은 'foo'의 필요 항목이 되고 필요하면 재컴파일 될 것이다. 그런데 모든 '.o' 파일이 지워져 버렸다면 어떻게 될 것인가. 와일드 카드가 어떤 파일과도 매치되지 않으면 이것은 있는 그대로 남게 된다. 그래서 'foo'는 이상한 이름의 파일 '*.o'에 의존하게 될 것이다. 이런 파일은 존재하지 않을 것이기 때문에 make는 '*.o'를 만드는 방법을 알 수가 없다는 에러를 낼 것이다. 이것은 의도하는 바가 아니다.
$(wildcard pattern...)
이 문자열은 makefile 안의 어떤 곳에서도 사용된다. 주어진 파일 이름 패턴들 중에서 하나와 매치되는 현존하는 파일 이름들을 공백으로 분리된 리스트로 만들어 대체한다. 어떤 파일 이름도 패턴과 매치되는 것이 없으면 그 패턴은 wildcard 함수의 결과로부터 제거된다.
wildcard 함수의 한가지 사용법은 한 디렉토리에 있는 모든 C 소스 파일들의 리스트를 획득하는 것이다. 다음과 같이:
$(wildcard *.c)
C 소스 파일들의 리스트를 가지고 `.c' 접미사를 `.o'로 변경해서 오브젝트 파일들의 리스트로 변경할 수 있다. 다음과 같이:
$(patsubst %.c, %.o, $(wildcard *.c))
그래서 한 디렉토리에 있는 모든 C 파일을 컴파일하고 이들을 모두 모아서 링크하려고 하는 makefile은 다음과 같이 작성될 수 있다:
objects := $(patsubst %.c, %.o, $(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
텍스트 변환을 위한 함수(Functions for Transforming Text)
$(subst from, to, text)
text에 대해서 from을 to로 바꾼다. 예를 들어서
$(subst ee, EE, feet on the street)
의 결과는 'fEEt on the strEEt'이다.
$(patsubst pattern, replacement, text)
text 안에서 공백 문자로 분리된 단어 중에서 pattern과 매치되는 단어를 찾아 그것들을 replacement로 변경한다.
여기서 pattern은 와일드 카드 역활을 하는 '%'를 가질 수 있는데, 이것은 어떤 단어 내에 있는 임의 개수의 문자들과 매치된다.
replacement도 역시 '%'를 가질 수 있는데 '%'는 pattern 안에서 '%'와 매치된 텍스트로 대치된다.
$(findstring find, in)
in에서 find를 찾아 있다면 값은 find가 되고, 없다면 빈 값이 된다. 이 함수를 조건에서 사용하면 주어진 문자열 안에서 특정 문자열의 존재 여부를 검사할 수 있다. 그래서 다음과 같은 두 예제들은:
$(findstring a, a b c)
$(findstring a, b c)
'a'와 ''(빈 문자열) 각각을 만든다.
$(filter pattern...,text)
pattern 단어들 중의 임의의 것과 일치하지 않는, text 내의 공백 문자로 분리된 단어들을 모두 제거하고 일치하는 단어들만을 리턴한다. 패턴은, 위의 patsubst 함수에서 사용된 패턴과 마찬가지로, `%'을 사용하여 작성된다. filter 함수는 한 변수 안에서 (파일 이름과 같은) 다른 타입의 문자열들을 분리 제거하는데 사용될 수 있다. 예를 들어서:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
는 `foo'가 `foo.c', `bar.c', `baz.s' 그리고 `ugh.h' 에 의존하지만 `foo.c', `bar.c' 그리고 `baz.s' 만이 명령안에서 컴파일러에 대해 지정되어야 한다는 것을 말한다.
$(origin variable)
variable은 질의하고 있는 변수의 이름으로 그 변수를 참조하는 것은 아니다. 그러므로 '$'나 괄호를 쓰지 않는다. 이 함수의 결과는 변수 variable이 정의된 방법을 말하는 문자열을 가진다:
'undefined'
변수 variable이 전혀 정의된 바가 없다면 이 값을 가진다.
'default'
변수 variable이 CC나 기타 등등처럼 디폴트 정의를 갖고 있다면 이 값을 가진다.
'environment'
variable이 환경 변수로써 정의된 것이고 '-e' 옵션이 켜진 것이 아니라면 이 값을 가진다.
'environment override'
variable가 환경 변수로 정의되었고 '-e' 옵션이 켜졌다면 이 값을 가진다.
'file'
variable가 makefile에서 정의된 것이라면 이 값을 가진다.
'command line'
variable가 명령행에서 정의된 것이라면 이 값을 가진다.
'override'
variable가 override 지시어로 makefile에서 정의된 것이라면 이 값을 가진다.
'automatic'
variable가 각 규칙의 명령의 실행에 대해서 정의된 자동 변수라면 이 값을 가진다.
헤더 파일(필요 항목을 자동으로 생성하기)
make는 숨겨진 의존 관계를 살펴보기 위해 파일 내부를 들여다 볼 수 없다. 따라서 어떠한 #include 지시자가 파일 내에 있는지 알지 못하며, 헤더 파일이 변경된 것을 확인하여 오브젝트 파일을 다시 작성할 수 없다. 이런 문제를 해결하기 위해 대부분의 기술 파일은 의존 관계를 직접 지정한다.
main.o: defs.h
make가 'defs.h'가 변할 때마다 'main.o'를 반드시 다시 만들어야 한다는 것을 알게 하기 위해 이 규칙이 필요하다. 어떤 커다란 프로그램에 대해서는 makefile 안에 그런 규칙 수십 개를 작성해야 할 것이다. 그리고 #include를 더하거나 제거할 때마다 makefile을 조심스럽게 업데이트 해야 한다.
대부분의 현대 C 컴파일러는 소스 파일에서 #include를 찾아 기술 파일에 포함하기 적합한 의존 파일 리스트를 만들어 내는 '-M' 옵션을 포함하고 있다. 예를 들어서 다음 명령은:
cc -M main.c
다음과 같은 출력을 생성한다:
main.o: main.c defs.h
이것은 .o가 종속하고 있는 모든 소스와 헤더 파일을 .o에 종속하도록 만든다. GNU C 컴파일러는 '-M' 플래그 대신 '-MM'을 사용하여 시스템 헤더 파일에 대해서는 종속물을 생략하도록 한다. 의존 파일 리스트를 만들었다면 그것을 모두 읽어 들이기 위해 include 지시어를 사용한다. 예를 들어:
include depend
.c.o :
$(CC) $(CFLAGS) -c $<
depend : Makefile
$(CC) -MM $(CFLAGS) $(wildcard *.c) > $@
명령에서 에러(Errors in Commands)
각 쉘 명령이 리턴한 후, make는 그것의 종료 상태 값을 조사한다. 그 명령이 성공적으로 종료하면 다음 명령 라인이 새로운 쉘에서 실행된다; 마지막 명령 라인이 종료된 후 그 규칙은 종료한다.
에러가 있으면(종료 상태 값이 0이 아니면), make는 현재 규칙을 그리고 아마도 모든 규칙들을, 포기한다.
때때로 어떤 명령의 실패가 문제를 가리키는 것은 아니다. 예를 들어서 mkdir 명령을 사용해서 어떤 디렉토리의 존재 유무를 검사할 수도 있다. 그런 디렉토리가 이미 존재한다면 mkdir은 에러를 보고할 것이다. 그러나 여러분은 아마도 make가 그것을 신경쓰지 않고 계속하기를 원할 것이다.
명령 라인에서 에러들을 무시하기 위해서 그 라인 텍스트의 시작부분(초기 탭 문자 뒤에)에 `-' 하나를 넣는다. `-'는 명령이 실행을 위해서 쉘로 전달되기 전에 무시된다.
예를 들어서,
clean:
-rm -f *.o
이것은 rm이 어떤 파일을 제거할 수 없다하더라도 계속하도록 한다.
여러분이 make를 `-i'나 `--ignore-errors' 플래그를 써서 실행할 때 에러들이 모든 규칙의 모든 명령들에서 무시된다. makefile에 있는 특수 타겟 .IGNORE에 대한 규칙은, 종속물들이 없을 때, 동일한 효과를 낸다. 에러를 무시하는 이런 방법들은 `-'가 좀 더 유연하기 때문에 구식이 되버렸다.
에러들이 무시되어야 할 때, `-'나 `-i' 플래그 때문에, make는 에러 리턴을 성공처럼 취급한다. 단, 여러분에 그 명령이 exit한 상태 코드를 말해주는 메시지를 출력하고 에러가 무시되었다는 것을 말하는 것외는 그렇다.
출처: Managing Projects with make, O'REILLY 발췌
http://www.viper.pe.kr/docs/make-ko/make-ko_toc.html
%: force
@$(MAKE) -f Makefile $@
force: ;
이것이 작동하는 방법은 패턴 규칙이 `%'와 같은 패턴을 가지고 있어서 임의의 타겟 어떤 것이든 매치된다는 것이다. 이 규칙은 `force'라는 종속물을 가지고 있도록 지정하고 있으며 이것은 타겟 파일이 이미 존재하더라도 명령들이 실행되도록 보장하기 위한 것이다. 우리는 make가 `force' 타겟을 빌드하기 위한 묵시적 규칙을 찾지 못하도록 금지하는, 빈 명령들을 주었다---그렇게 하지 않으면 `force' 자신에다 동일한 match-anything 규칙을 적용해서 종속물 루프를 만들것이다.
'Computer_language > Linux' 카테고리의 다른 글
초보자를 위한 리눅스 커널의 메모리 관리 (0) | 2009.01.12 |
---|---|
GNU Make: 재컴파일을 지휘하는 프로그램(A Program for Directing Recompilation) (0) | 2009.01.12 |
우분투 사용 총집합서 (0) | 2009.01.12 |
우분투에서 수도 (sudo) 사용자 추가방법, sudoers (0) | 2009.01.12 |
우분투에 메신저를 설치하다 (네이트온,MSN 등) (0) | 2009.01.12 |