1. 개요
make는 command generator이다. Description file과 몇 개의 일반적인 템플릿을 사용하여 UNIX shell이 수행 가능한 command sequence를 만든다. 이러한 command는 software development project를 구성하는 파일들의 유지 관리와 연관되어 있다. 여기서 유지 관리는 다수의 프로그램 소스로 구성된 프로그래밍 시에 현재 상태를 보고, 임시화일을 제거, 최종 프로그램의 실행화일을 만드는 전과정을 망라하는 작업을 말한다.
make는 화일간에 존재하는 종속성(dependency)을 정리하는데 사용할 수가 있다. 비교적 적은 수의 화일을 포함하는 소프트웨어 프로젝트라도 프로젝트내에 존재하는 화일간에는 여러 형태의 종속성이 존재할 수 있다. 예를 들어 프로그램은 오브젝트화일과 라이브러리를 링크하여 생성이 되고, 오브젝트는 다시 C나 Pascal같은 고급언어나 assembly언어의 소스에서 생성이 된다. 만약 하나 이상의 소스화일을 변경하는 경우 다시 컴파일하고 다시 링크시켜야 한다. 이러한 일련의 일은 프로젝트 수행 시 빈번히 발생한다. 이러한 일들을 자동화하는 것이 make utility이다. 화일간의 종속관계에 대한 description file을 작성하면 make가 관련된 update를 수행한다. 이러한 update는 최종 갱신된 관련화일을 화일 시스템을 통하여 검사함으로써 file A가 file B에 종속적이고 file B가 file A의 갱신 이후에 변경이 된다면 file A를 다시 구성(recompile, relink, edit,library교환)하는 메커니즘에 의해 수행이 된다.
2. Makefile의 작성법
make가 사용하는 description file은 임의의 이름을 가질 수가 있지만 기본적으로 Makefile또는 makefile이라는 이름을 사용한다. 다음과 같은 명령어
$make program
은 program의 최신 버전을 만들고자 할 때 사용한다. program이 실행 가능한 화일이라면 이러한 명령어를 통하여 실행화일을 만드는데 필요한 컴파일, 링크등을 수행하게 된다. 많은 cc 컴파일러 명령어를 커맨드 라인상에서 입력하는 대신에 make를 통하여 이러한 작업을 자동적으로 수행하게 된다.
앞으로의 설명을 위하여 다음과 같은 몇 가지 용어를 우선 정의한다. 먼저 target은 위에서 말한 program과 같이 만들고자 하는 대상이고, dependents(또는 prerequisite)는 이러한 target에 만들기 위하여 참여하는 객체를 말한다. 이러한 dependent는 다른 dependent의 target이 될 수도 있다.
make는 source-to-object, object-to-executable같은 종속관계의 계층적 구조(hierarchy of dependency)를 인식하여 필요한 작업을 수행하며, 이러한 종속관계의 계층적 구조는 앞에서 언급한 Makefile(또는 makefile)이라 불리우는 description file에 기술한다. make는 이러한 종속관계의 계층적 구조 뿐만 아니라 수행환경(environment)에서 정의된 환경 변수(environment variable)의 값을 사용하고, make에 존재하는 종속성을 결정할 수도 있다. 이러한 일련의 작업은 file의 존재 여부, file의 최종 갱신 시간과 make에 내장된 규칙을 사용하여 수행이 된다.
2.1 Description file
다음과 같은 예를 보자.
예)
다음과 같은 화일로 구성되는 프로그램 prog에 대하여
C language source file - main.c, iodat.c, dorun.c
Assembly language code - lo.s
library routines in - /usr/kimkk/lib/crtn.a
명령어 입력을 통하여 프로그램 prog을 생성하기 위해
$ cc -c main.c
$ cc -c iodat.c
$cc -c dorun.c
$as -o io.o io.s
$cc -o prog main.o iodat.o dorun.o lo.o /usr/kimkk/lib/crtn.a
의 명령어 입력이 필요하다면 다음과 같은 description file (Makefile)을 작성하여 이러한 작업을 자동화할 수가 있다.
1 prog : main.o iodat.o dorun.o lo.o /usr/kimkk/lib/crtn.a <- dependency line
2 cc -o prog main.o iodat.o dorun.o lo.o /usr/kimkk/lib/crtn.a <- command(rule) line
3 main.o : main.c
4 cc -c main.c
5 iodat.o : iodat.c
6 cc -c iodat.c
7 dorun.o : dorun.c
8 cc -c dorun.c
9 lo.o : lo.s
10 as -s lo.o lo.s
앞의 라인 번호는 설명의 편의를 위하여 삽입한 것이고 실제로는 삽입하지 않는다. 먼저 첫번째 줄과 같이 콜론(:)으로 구성된 줄을 dependency line(또는 rule line)이라고 하며 콜론의 좌측에 target이 콜론의 우측에 target의 생성에 관련되는 dependents를 나열한다. 다음 두 번째 줄과 같이 TAB으로 시작하는 줄을 command line이라 하며 command line을 바로 윗줄의 target을 생성하기 위하여 수행하여야 할 작업을 기술한 것이다. 그러므로 다음과 같은 명령어에 의하여
$make prog
을 통하여 prog를 만드는 수작업을 자동적으로 수행할 수가 있다. (description file명이 Makefile또는 makefile이 아닌 경우 -f option을 사용하여 지정할 수가 있다.)
2.2 Dependency checking
Make의 자동 수행의 이면에 make가 수행하는 다음과 같은 dependency checking이 있기 때문에 가능한 것이다. 앞의 description file을 사용하여 make가 prog를 생성하는 경우 바로 두 번째 줄의 command line이 수행되지는 않는다. 먼저 make는 prog화일의 존재 여부를 검사하여 존재하는 경우 main.o, iodat.o , dorun.o, lo.o와 /usr/kimkk/lib/crtn.a의 최종 갱신 시각을 검사하여 이들 중 하나라도 prog의 생성시간보다 뒤에 있는 것이 있는지를 검사한다. 이러한 검사는 운영체제의 화일시스템에서 유지하고 있는 모든 화일들에 있는 최종 갱신 시각을 사용하여 가능하다. prog의 생성시간이 모든 dependents의 생성 시각보다 뒤라면 (latest)라면 prog는 다시 만들 필요가 없고 그렇지 않다면 두 번째 줄의 command line을 사용하여 prog을 생성한다. 물론 재귀적으로 dependents도 다시 검사가 되어야 한다.
2.3 Minimizing Rebuild
다음의 예는 이러한 rebuild를 최소화하는데 make를 사용한 예이다. 다음과 같은 경우를 가정해보자. 멍텅구리 터미널(dumb terminal)또는 bit-mapped workstation에서 수행 가능한 data-plotting프로그램을 작성할 때 계산(calculation), 화일조작과 같은 일들이 두 가지 버전에서 공통적으로 상당 부분을 차지하고 이러한 부분의 작업은 basic.c에 저장하고 멍텅구리 터미널에서 사용하는 user input모듈은 prompt.c에 workstation에서 사용하는bit-mapped interface는 window.c에 저장된 경우 다음과 같은 description file을 사용하여 이러한 작업을 간단히 하고 rebuild도 최소화할 수 있다.
plot_prompt : baisc.o prompt.o
cc -o plot_prompt basic.o prompt.o
plot_win : basic.o window.o
cc -o plot_win basic.o window.o
baisc.o : basic.c
cc -c basic.c
prompt.o : prompt.c
cc -c prompt.c
window.o : window.c
cc -c window.c
먼저 workstation용의 프로그램을 만들 경우에는
$make plot_prompt
멍텅구리 터미널용 프로그램을 만들 때는
$make plot_win
을 사용하여 만들 수가 있다.
이 경우 처음 한번만 baisic.c의 컴파일이 필요하고 변경이 없는 한 이후에 컴파일 과정은 일어나지 않는다. 위의 description file도 커맨드 라인상의 입력에 비하면 많은 양의 일이 줄어든 것이지만 다음에서 설명할 매크로와 suffix rule을 사용하여 좀 더 간단해질 수가 있다.
2.4 make의 호출
앞의 예에서는 다음과 같은 점을 가정하였다.
. description file을 포함하는 모든 projetc file들이 동일한 디렉토리에 존재한다.
. description file명은 Makefile또는 makefile이다
. 이러한 모든 화일들은 make를 수행하는 시점에서 현재 디렉토리이다.
이상과 같은 상황에서 description내의 임의의 target을 생성하려면 다음과 같은 명령어를 입력하면 된다.
$make target
target을 생성하기 위한 관련된 rule line이 수행되고 이러한 수행의 상황은 터미널에 디스플레이된다. 만약 중간 화일의 일부가 존재하고 update-to-date한 경우에는 make는 관련된 command를 skip한다. 즉 make는 target을 생성하는데 필요한 최소한의 명령만을 수행한다. 어떠한 prerequisite도 갱신되거나 제거되지 않은 경우에는 make는 다음과 같은 메시지를 낸다.
'target' is up to date
생성하려 하는target이 description file내에 존재하지 않고 suffix rule에서 설명할 default rule에서 처리되지 못하는 경우 make는 다음과 같은 message를 낸다.
$make nontarget
make : Don't know how to make nontarget. Stop
또는
$make nontarget
'nontarget' is up to date
현재 디렉토리에 nontarget이라는 화일명이 존재하는 경우에 위와 같은 메시지를 받는다.
또한 다음과 같이 한번의 make호출에 여러 개의 target를 지정할 수가 있다. 이렇게 함으로써 여러 번의 make호출과 동일한 일을 수행할 수가 있다.
$make main.o dorun.o io.o
마지막으로 다음과 같이 target을 지정하지않는 경우에는
$make
description file내의 첫번째 taget을 생성한다.
2.5 기본적인 구문 규칙
description file내에서 모든 명령어는 탭(tab)으로 시작하여야 한다. Makefile과 관련된 가장 초보적이면서도 틀리기 쉬운 것이 명령어가 탭으로 시작하여야 한다는 것이다. 그러므로 명령어 라인이 아닌 주석(comment)나 dependency line은 탭으로 시작하면 안된다.
이러한 탭을 보기 위해서는 다음과 같은 cat명령어 옵션을 사용할 수 있다.
$ cat -v -t -e makefiel
where -v : print non-printing character
-t : print tab as ^I
-e : print end of line as $
또는 vi(Visual editor)에서 set list를 사용하여 탭과 end of line을 시각적으로 볼 수 있다.
한 줄 이상에 걸쳐서 기술할 때에는 라인의 끝에서 역슬래쉬(\)를 삽입하여 라인이 계속 이어짐을 나타낸다.
make는 빈 줄을 무시하고, #(compound sign)으로 시작하는 줄은 주석으로 인식한다.
명령어가 별개의 줄에 있을 필요는 없다. Dependency line에 명령어를 기술하고자 할 때는 세미콜론(;)으로 분리하면 된다.
plot_prompt : prompt.0 ; cc -o plot_prompt prompt.o
(명령어 라인이 탭으로 시작하지 않아도 되는 유일한 예외이다.)
단일 타겟이 여러 dependency lines에 걸쳐서 나타날 수도 있다.
file.o : /usr/src/file.c
cc -c /usr/src/file.c
...
...
file.o : global.h date.h
file.o를 실제로 생성하는 dependency line은 첫번째 dependency line이다. 이 경우 file.c의 변경이 없었다 하여도 *.h중 하나에 변경이 있었다면 file.o는 make의 입장에서 out of date하므로 다시 컴파일을 수행하게 된다. 여기서 주목할 점은 다중의 dependency lines을 사용한다 하여도 명령어와 연관된 dependency line만이 수행된다는 점이다.
Prerequisite이 없는 target(이 경우에도 콜론은 명시하여야 한다) 이것의 예로는 GNU package의 makefile에 흔히 있는 make clean의 clean target이다.
clean :
/bin/rm -f core *.o
make clean 명령 clean이라는 화일이 존재하지 않는 한 명령어 라인을 실행한다. 이는 make가 존재하지 않는 타겟을 out-of-target으로 취급하기 때문이다.
3 매크로(macro)의 사용
실제적인 description file에서는 앞에서 기술한 것보다 더욱 간졀하게 기술이 된다. 이는 매크로(macro)와 접미사 규칙(suffix rule)을 사용하여 가능한 것이다.
이 절에서는 매크로에 대하여 살펴본다.
먼저 매크로의 정의는 다음과 같이 한다.
name = text string
매크로 정의 이후에 매크로를 참조는 다음과 같이 한다.
$(name)
또는
${name}
$(name)또는 ${name}은 text string으로 해석되어 진다.
예)
LIBS = -lX11
objs = drawable.o plot_point.o root_date.o
CC = /user2/home/kimkk/bin/cc
23 = "This is the 23rd run"
OPT =
DEBUG_FLAG = #empty now, but assign -g for debugging
BINDIR=/user/home/kimkk/bin
plot : ${objs}
${CC} -o plot {DEBUG_FLAG} {objs} ${LIBS}
mv plot ${BINDIR}
은 다음과 같이 매크로 확장이 된다.
/user2/home/kimkk/bin/cc -o plot drawable.o plot_point.o root_date.o -lX11
mv plot /usr/local/bin
예에서 보듯이 description file내에서 매크로를 사용하면 반복적으로 사용되는 화일이나 명령어 옵션을 편리하게 참조하여 사용할 수가 있고 inconsistency없이 description file을 쉽게 수정 변경이 가능하다는 장점이 있다
3.1 매크로 구문규칙
매크로 정의는 equal sign(=)을 포함하는 줄이다. make는 equal sign앞의 매크로 이름과 equal sign이후의 text string을 연관시킨다. Test string은 single quote또는 double quote로 묶을 필요는 없다.( 23의 매크로정의에서는 string부분을 명시하기 위하여 quote를 사용한 것이다.)
매크로의 정의가 한 줄 이상일 경우에는 라인의 끝에서 마찬가지로 back slash(\)로 마크하여 라인이 계속 이어짐을 나타낼 수 있고, #으로 라인 끝까지 주석문으로 처리가 가능하다.
매크로 이름은 일반적으로 대문자로 사용하며 한 개의 문자로 이루어진 매크로 정의는 다음과 같이 참조할 수 있다.
A = XYZ
$A, $(A) 또는 $({A}로 참조
또한 매크로 정의 내에 다른 매크로의 이름을 포함할 수 있다.
ABC =XYZ
FILES = TEXT.${ABC}
OPT나 DEBUG_FLAG와 같이 equal sign이후에 스트링이 없는 경우에는 널스트림(Null string)이 매크로 이름에 할당이 된다.
3.2 내부적으로 정의된 매크로
make는 자주 사용되는 명령어에 대하여 매크로로 내부적으로 정의한다. 예를 들어 ${CC}는 C compiler로 {LD}는 linker로 인식한다.
다음과 같은 경우
basic.o = basic.c
${CC} -c basic.c
는 아래와 동일하다.
basic.o = basic.c
cc -c basic.c
내부적으로 정의된 매크로를 보려면
make -p
로써 내부적으로 정의된 모든 매크로를 볼 수가 있다.
3.3 Command line에서의 매크로 정의
다음과 같이 make 명령어 라인에서 매크로를 정의할 수 있다.
$ make kim_w DIR=/user2/home/kimkk/bin
make 명령어 라인에서 매크로를 정의할 때 2개 이상의 단어로 이루어지는 경우에는 quote로 묶어야 한다.
3.4 Shell variable의 사용
실행환경내에서 정의되고 있는 쉘 변수나 환경 변수를 description file내에서 사용 가능하고 이때 참조는 ${shell_variable}또는 $(shell_variable)형태로 한다.
%setenv DIR /user2/home/kimkk/bin <- csh, tcsh의 경우
또는
$ DIR=/user2/home/kimkk/bin; export DIR <- bsh인 경우
make description file내에서의 참조는
SRC = ${DIR}/../src
all :
cc -o kimkk_w ${SRC}/*.c
3.5 매크로 정의의 우선순위
다음과 같은 순서로 우선순위가 높아진다.
- make가 내부적으로 정의한 매크로 정의
- 현재 쉘의 환경 변수
- description file내의 매크로 정의
- make명령어 라인에서 입력한 매크로 정의
환경 변수가 낮은 우선순위를 가지는 것이 일반적으로 바람직하지만 다수의 프로젝트의 default definition을 변경하고자 할 때는 make를 여러 번 실행해야 한다. 이러한 경우에는 환경 변수를 셋팅하고 환경 변수가 우선순위가 높도록 -e 옵션을 make에 사용할 수 있다.
-e 옵션을 사용할 때 make가 사용하는 매크로의 우선순위는 다음과 같다
.
- make가 내부적으로 정의한 매크로 정의
- description file내의 매크로 정의
- 현재 쉘의 환경 변수
- make명령어 라인에서 입력한 매크로 정의
예)
description file
TEST = default_test
test : srcfile.c
${TEST} srcfile.c
환경 변수 셋팅
$ TEST=new_test; export TEST 또는 setenv TEST new_test
$make test
$make -e test
3.6 매크로 스트링 치환
다음과 같은 매크로 정의가 있는 경우
SRC = def.c redraw.c calc.c
description file내의 다음 명령어
ls ${SRC:.c=.o}
는
ls def.o redraw.o calc.o와 동일하다.
${SRC: .c=.o}의 수행을 살펴보면 make는 우선 ${SRC}를 evaluate하고 콜론의 앞에서 기술한 스트링을 찾아서 콜론 뒤의 스트링으로 치환한다.
주의할 점은 스트링 치환은 매크로의 끝이나 공백 앞에서만 일어난다. 또 콜론 뒤의 스트링이 널이 될 수도 있다.
예)
SRC = def.c redraw.c calc.c
FINAL_OBJS = ${SRC:.c=.o}
DEBUG_OBJ=$PSRC:.c=_dbg.o}
3.7 Prerequisite과 target을 위한 내부 매크로
make는 dependency line을 읽을 때마다 자신의 매크로를 정의한다. 이러한 매크로들은 description file을 간결하게 만들 수 있고 엔트리에 대한 변경을 용이하게 한다.
$@, $$@는 현재 target를 의미한다.(단 $$@는 dependency line에서만 사용 가능하다)
plot_prompt : basic.o prompt .o => plot_prompt : basic.o prompt .o
cc -o $@ basic.o prompt.o cc -o plot_prompt basic.o prompt.o
$?는 현재 target보다 newer인 prerequisite의 리스트를 의미한다
liobps : interact.o sche.o gen.o => liobps : interact.o sche.o gen.o
ar r $@ $? ar r liobps interact.o sche.o gen.o
4. 접미사 규칙(suffix rule)
UNIX system에서 C 언어의 소스화일은 .c, FORTRAN의 소스화일은 .f ASSEMBLY의 소스는 .s, 오브젝트 화일은 .o등의 접미사를 따른다. 이러한 규칙을 make에서 이용할 수가 있다.
4.1 접미사 규칙이란?
실제적으로 접미사 규칙이란 기정의된 일반화된 description file entry이다.
.SUFFIXES : .o .c .s # 사용하고자 하는 접미사의 기술
.c.o : # *.c에서 *.o를 생성하는 접미사 규칙
${CC} ${CFLAGS} -c $<
.s.o : # *.c에서 *.o를 생성하는 접미사 규칙
${AS} ${ASFLAGS} -o $@ $<
make가 사용할 접미사는 .SUFFIX를 이용하여 정의한다. .SUFFIX에 기술한 접미사를 포함하는 prerequisite에 대한 사용자가 제공하는 명시적인 명령어가 없는 경우에는 make는 접미사 규칙을 찾아 수행한다.
예) OBJS = main.o iodat.o dorun.o lo.o .SUFFIXES : .o .c .s
LIBS = /usr/proj/lib/crtn.a .c.o :
program : ${OBJS} ${LIB} ${CC} ${CFLAGS} -c $<
${CC} -o $@ ${OBJS} ${LIBS} .s.o :
main.o : main.c ${AS} ${ASFLAGS} -o $@ $<
${CC} -c $? OBJS = main.o iodat.o dorun.o lo.o
iodat.o : iodat.c LIBS = /usr/proj/lib/crtn.a
${CC} -c $? program : ${OBJS} ${LIB}
dorun.o : dorun.c ${CC} -o $@ ${OBJS} ${LIBS}
${CC} -c $?
lo.o : lo.s
${AS} -c $?
4.2 내부 매크로
$<: 접미사 규칙에서만 사용하는 내부 매크로로써 현재 target보다 newer한 prerequiste의
리스트를 나타낸다.
$* : 접미사 규칙에서 prerequisite의 접미사를 제외한 화일 이름 부분
예)
현재 접미사 규칙에서의 prerequiste 이 main.c라면
cp $< $*.tmp는 다음과 동일하다.
cp main.c main.tmp
4.3 Double colon
라이브러리를 구축하는 경우에는 서로 다른 화일에 서로 다른 명령어를 사용하여야 하는 경우가 발생한다. 일반적인 경우 make에서는 동일한 target이 여러 번 기술되고 그 기술마다 명령어가 연관되는 것은 허용되지 않는다. (동일한 target이 여러 번 기술되고 단 한번의 명령어 연관이 있는 경우에는 허용이 된다. 2.5절을 참조하라) 이러한 상황에서 double colon을 사용한 dependency line의 기술이 필요한 것이다. 다음의 예를 보자
예) libops :: interact.c
${CC} -O -c -DENTRY interact.c
ar r $@ interact.o
rm -f interact.o
libops :: sched.c
${CC} -O -c -DRECRD sched.c
ar r $@ sched.o
rm -f sched.o
prerequisite이 모두 libops보다 newer하다면 두개의 target기술의 명령어 행은 모두 실행이 된다.
4.4 Default값의 출력
macro나 suffix rule의 값을 살펴보면 좀더 description file을 명확하게 이해하고 수정할 수가 있을 것이다. make의 -p 옵션을 사용하면 suffix, macro의 값을 볼 수가 있다.
$make -p < dev/null >& make_defalut_values
는 description file이 널이므로 description file에 영향을 받지 않은 상태에서의 make의 suffix와 macro의 default 값을 볼 수 있다.
$make -pns >& make_default_values
-n : prevent make from executing commands
-s : prevent make from echoing the commands
는 기본 description file (Makefile또는 makefile, 다른 이름의 description file을 사용하고자 할 경우에는 -f 옵션을 사용하라)을 적용한 상황에서의 suffix와 macro의 값을 볼 수가 있다. 이는 description file의 디버깅 시에 유용하다.
4.5 Suffixes와 관련된 기타사항
Suffixes의 우선결합을 위하여 dependency chain의 앞쪽에 있는 화일들은 .SUFFIXES 리스트의 뒤쪽에 둔다. 이렇게 함으로써 불필요한 rebuild를 최소화할 수 있다.
다음과 같이 .SUFFIXES를 널로 하면 현재 사용하고 있는 모든 suffixes에 대한 것을 삭제한다.
.SUFFIXES : # delete all currently recognized suffixes
이를 이용하면 description file중간에서 .SUFFIXES를 재정의할 수가 있다.
.SUFFIXES : #delete all currently recognized suffixes
.SUFFIXES : .q .w .t #redefine suffixes list
5. 명령어(commands)
Description file내의 모든 명령어는 그 명령어들이 shell내에서 호출된 것처럼 실행이 된다. 그러므로 화일이름 확장(filename expansion)이나 출력 재지정(output redirection)같은 shell에서 제공하는 기능들을 description file내에서도 사용이 가능하다.
grep '^Error:' ../err{a-z}* | sort >> errorfile
그러나 description file내의 각각의 명령어들은 매번 독립적이 shell내에서 수행이 되므로 일반적인 shell script나 shell environment에 관련된 사항은 주의해서 사용하여야 한다.
5.1 화일이름 패턴 매칭
Shell의 화일이름 패턴 매칭 문자인 * ? [ ]는 명령어 라인과 dependency line의 콜론 우측에서 확장이 된다.
print : *.c
lpr *.c
date > $@
그러나 실제로 위와 같은 형태의 prerequisite은 make에서 제공하는 다수의 특정 prerequisite지정하는 방법으로 하는 것이 더욱 신뢰성이 있으므로 일반적으로 많이 사용하지는 않는다.
5.2 명령어 라인상에서의 newline의 영향
앞에서도 언급한 바와 같이 make는 각각의 명령어 라인을 개별적인 shell내에서 수행한다. 그러므로 다음과 같은 세가지 형태는 동일한 의미이다
터미널상의 입력 description file description file
cd output cd outputl; rm * cd output;\
rm * rm *
백 슬래시를 사용하여 newline을 command terminator로 해석하는 것을 중지시킴으로써 2줄 이상의 명령어를 한 줄의 명령어로 만들 수가 있다.
cd나 exit같은 쉘 내장 명령어를 사용하는 경우 그 효과는 그들이 위치한 곳에서만 영향을 미친다. 이는 각 명령어들이 각각의 독립적인 쉘에서 수행이 되기 때문이다.
마찬가지 이유로 쉘 변수에 붙이는 dollar($) sign은 두개를 붙여서 description file에서 사용하는 매크로 참조와 구분한다. make는 자동으로 두개의 dollar($) sign을 스트라이핑하여 쉘 변수로 인식한다. 여기서 사용되는 쉘 변수는 make내에서 생성된 쉘에서 사용하는 쉘 변수에 한정한다. 왜냐하면 환경 변수는 매크로와 마찬가지로 make script내에서 참조가 가능하기 때문이다. ( 3. 매크로의 사용을 참조하라.)
5.3 Error와 Exit Status
명령어 수행 시 에러가 발생하는 경우( non zero exit status)에 전체 make의 진행은 중지된다. make가 제공하는 이러한 기능은 대개의 경우 바람직한 특성이지만, 에러를 예견하고 에러 후에도 계속해서 진행을 하고자 할 때가 있다. make는 이런 경우를 위하여 다음과 같은 두 가지 방법을 제공한다.
첫째 description 화일내에서 명령어 앞에 하이픈(-)을 둠으로써 에러가 발생하여도 다음 명령어의 실행으로 진행할 수 있다.
plot :
cc -o $@ basic.c prompt.c
- cp $@ /usr/local # cp명령어 앞의 하이픈은 다음 명령어인 rm이
rm -f *.o # 항상 실행되게 보장한다.
두 번째 방법으로 .IGNORE라는 특별한 타겟을 description 화 일 내에 둠으로써 모든 명령어 앞에 하이픈을 붙인 것과 같은 효과를 얻을 수 있다.
한번에 독립적인 다수의 타겟을 만들고자 할 경우에는 make의 명령어 라인 옵션인 -k를 사용할 수가 있다.
make -k plot_prompt plot_win
이 옵션을 사용하면 다수의 타겟을 만드는 중에 독립적인 한 타겟에서 에러가 발생하는 경우 에러가 발생한 타겟과 그 타겟과 종속적인 prerequisite에 대한 모든 작업은 중지하고 독립적인 다른 타겟을 만드는 작업을 진행한다. make는 지정된 다수의 타겟 중 만들지 못한 대한 보고를 한다.
Error를 무시하지 않고 적절한 처리를 하고자 할 경우에는 쉘의 if문과 같은 것을 사용하여 적절한 처리를 할 수가 있다.
if \
cp plot /usr/local \
then \
rm -f plot \
else
echo 'Cannot move to new location
fi
5.4 쉘의 선택
대부분의 description 화일들은 bourn shell의 명령어를 포함하고 있다. 그러므로 표준적인 bourn shell을 사용함으로써 이식성을 최대화할 수 있고 적절한 프로그래밍 construct를 이용할 수가 있다.
description 화일이 이식성이 있도록 하기 위해서는 사용하는 특정 make implementation이나 login shell feature에 있는 기능은 되도록이면 사용하지 않는 것이 좋다. 그러므로 표준적인 bourn shell을 사용하여 프로그래밍하고 bourn shell이 사용됨을 명시적으로 description화일내에 다음과 같이 제일 처음에 기술하도록 한다
SHELL = /bin/bsh
부득이 하게 다른 쉘이나 perl 또는 awk하에서 수행하는 명령어를 사용해야 하는 경우에는 그러한 명령어를 별도의 화일로 분리하여 description 화일내에서 호출하는 것이 안전하다.
Appendix
make 사용법 요약
- 0 명령어 라인의 make 사용
make [ -f descfile ] [ options ] [ targets ] [ macro definition ]
options, targets, macro definition의 순서는 무관하며 여러 개의 옵션을 하나의 하이픈
으로 연결 가능하다.( 예를 들어 -fsn )
macro definition의 형식은 다음과 같다.
Name = string
표준 옵션
-b make의 이전 구현의 description 화일을 사용한다.
-d 디버깅 모드로 실행
내부 플래그와 화일의 최종 갱신 시각 등에 대한 정보를 제공
-e 환경 변수(environment variable)의 값을 description화일내의 매크로 정의
값보다 우선하여 사용한다.
-f Makefile이나 makefile이 아닌 다른 이름의 description화일을 사용함
화일 이름대신 - 를 사용하는 경우 stdin을 의미한다.
-i 에러가 발생하여도 무시하고 진행한다.
Description화일내에 .IGNORE와 동일하다.
-k 다수의 타겟을 만들 때 에러가 발생한 현재 타겟은 중지하고 다른 타겟
생성을 계속한다.
-n 명령어 라인을 실행하지는 않고 화면에 출력만 한다
-p 매크로 정의,suffixes, suffixes rule과 외부 description화일 엔트리를
출력한다
-q 타겟화일의 최근성 여부에 따라 zero또는 nonzero status를 리턴한다.
-r default rule을 사용하지 않는다.
-s 명령어라인을 echo하지 않는다.
Description 화일내의 .SILENT와 동일하다.
-t 명령어를 실행하지 않고 타겟화일을 touch한다
즉 타겟의 최종갱신시각을 현재시각으로 변경한다.
- 1 Description화일
#으로 시작하는 이후의 것은 주석(comment)이며 빈 라인은 무시한다. 백슬래시로 끝나는 라인은 다음 줄로 계속 이어진다. 백슬래시 전후의 모든 white space(blank,tab, new line)등은 한 개의 공백 문자로 해석된다.
- 1.0 Dependency line
형식
target : [:] [ prerequisites ] [; [ command ] ]
타겟을 만들기 위하여 필요한 prerequisite을 지정한다. 타겟들과 prerequisites은 공백이나 탭으로 분리하여야 한다, 콜론의 앞뒤에 공백은 선택적으로 둘 수도 있다. 타겟 앞에는 탭 문자가 와서는 안된다. 두개의 콜론을 사용하여 동일한 타겟을 다수 사용하여 다수의 명령을 각각의 dependency line에서 수행할 수가 있다. 세미콜론 후에 필요한 명령어를 기술할 수도 있다.
- 1.1 접미사규칙(Suffix Rule)
형식
.suffix[.suffix] :[ : ]
suffix만 다르고 동일한 이름을 가지는 두개의 화일에 대하여 두 번째 지정된 suffix를 가지는 화일은 첫번째 suffix를 가지는 화일에서 생성됨을 지정한다. suffixes간에는 공백 문자가 있으면 안된다. 콜론 뒤의 내용은 무시된다. 두개의 콜론은 dependency line과의 일관성을 위한 것일뿐 아무런 영향도 없다.
- 1.2 명령어(command)
형식
tab [- @] command
타겟을 생성하기 위하여 수행하는 명령어를 기술한다. 명령어는 반드시 탭으로 시작하여야 한다. 탭으로 시작한 이후에 추가적인 공백이나 탭은 허용된다. 명령어 앞에 하이픈(-)을 추가하여 명령어 실행 시 에러가 발생한 경우 에러를 무시하고 make의 수행을 계속하도록 한다. At-sign(@)을 명령어 앞에 추가하면 -n 명령어 옵션을 make수행 시 지정하지 않는 한 실행되는 명령어의 에코를 하지 않는다.
- 1.3 매크로 정의(macro definition)
형식
name = string
string을 name으로 할당한다. name의 앞에 추가적인 공백이 있을 수 있지만 명령어 라인과의 구분을 위하여 탭으로 시작하여서는 안된다. Equal sign(=)앞뒤에 공백 문자가 올 수 있지만 make에 의하여 제거된다. string이 백슬래시와 newline으로 이어지면 make는 다음 라인으로 string을 계속하여 인식한다. make는 백슬래시와 newline을 한 개의 공백 문자로 치환한다.
- 1.4 인클루드 문(include statement)
형식
include file
지정된 화일을 읽어 들여 그 화일의 내용을 description 화일로 해석한다.
A.2 매크로(macros)
다음은 make가 사용하는 내부적인 매크로에 대한 설명이다.
$? 현재 타겟을 만들기 위한 prerequisite중에서 현재 타겟의 최종 갱신 시각보다
이전의 갱신 시각을 가진 prerequisite의 리스트
Suffix rule에서는 사용할 수 없음
$@ 현재 생성하려 하는 타겟
$< 현재 타겟을 만들기 위한 prerequisite중에서 현재 타겟의 최종 갱신 시각보다
이전의 갱신 시각을 가진 prerequisite
Suffix rule과 .DEFAULT에서만 사용 가능
$* 현재 타겟을 만들기 위한 prerequisite중에서 현재 타겟의 최종 갱신 시각보다
이전의 갱신 시각을 가진 prerequisite의 suffix를 제외한 이름
Suffix rule에서만 사용 가능
$$@ Dependency line의 콜론 우측에서만 사용이 가능하며 현재 타겟의 이름이다.
$% 현재 타겟이 라이브러리 모듈일 경우에 대응하는 .o 화일의 이름이다
D $?를 제외한 내부 매크로의 디렉토리 부분이다.
${*D}, ${<D}, ${@D}, $${@D}로 사용한다.
F $?를 제외한 내부 매크로의 화일부분이다.
${*F}, ${<F}, ${@F}, $${@F}로 사용한다.
A.3 매크로 스트링 치환
형식
${macro:s1=s2}
${macro}의 정의 중에서 공백이나 탭의 바로 앞뒤 또는 매크로 정의의 끝에 있는 s1을 s2로 치환한다.
A.4 특별한 매크로
SHELL
명령어를 해석하기 위한 쉘을 지정한다. 이 매크로가 지정이 되지 않은 경우 명령
어 해석을 위하여 사용되는 쉘은 make구현에 따라 차이가 있다.
VPATH
prerequisite이 현재 디렉토리 이외의 곳에 있는 경우에 그 디렉토리를 지정하는데
사용된다.
A.5 특별한 타겟이름
.DEFAULT
생성하고자 하는 타겟과 관련이 있는 suffix rule이나 description화일 엔트리가 없
는 경우에 타겟과 연관된 명령어만 수행한다.
.IGNORE
에러 코드를 무시한다. -i옵션과 동일하다.
..PRECIOUS
make를 kill하거나 명령어가 에러를 발생하여 make가 abort되더라도 이 타겟에서
지정한 화일을 제거되지 않는다.
.SILENT
명령어의 수행을 하지만 그 과정을 에코하지 않는다. -s 옵션과 동일하다
.SUFFIXES:
make의 suffix rule에서 사용할 suffix의 리스트를 기술한다. suffix의 리스트를 기술
하지 않으면 기존의 .SUFFIXES의 값을 널이 된다.