make 유틸리티 강좌
목차.
1. make ( 만든다? )
1.1 make 유틸리티.
1.2 make의 필요성.
2. 간단한 Makefile
2.1 Makefile의 내부구성.
2.2 Makefile 예제
2.3 매크로의 사용
2.4 레이블의 이용
1. make (만든다?)
1.1 make 유틸리티.
영어사전에서 make란 뜻은 누구나 알듯이 '만들다'라는 뜻의 동사이다. 그럼 make 유틸리티는 왜 이름이 make인지 알 필요가 있을 것 같다. man으로 찾아보면 make에 대해 다음과 같이 설명하고 있다.
make - GNU make utility to maintain groups of programs
The purpose of the make utility is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them.
우리말로 하면 make는 프로그램 그룹을 유지하는데 필요한 유틸리티이다. make 유틸리티의 목적은 프로그램 그룹 중에서 어느 부분이 새롭게 컴파일 되어야 하는지를 자동적으로 판단해서 필요한 커맨드(gcc 따위)를 이용해서 그들을 재컴파일 시킨다고 되어 있다.
make는 일련의 프로그램 개발에만 쓰이지 않고, 컴파일러처럼 일종의 명령어 방식으로 처리되는 모든 곳에서 쓰일 수가 있다. 가령 latex와 같은 경우도 .tex 파일에서 .dvi 파일을 만들고 다시 .ps 파일로 만드는 과정을 make를 사용해서 간단하게 만들 수가 있다.
--------------------------------------------------------------------
고 - 쉽게 말하면 다음과 같은 경우에 make 를 쓰면 유리합니다.
. 입력파일이 바뀌면 자동적으로 결과파일을 바꿔줬음 좋을 때. 기왕이면 좀 지능적으로 일을 수행하고 말입니다.
. 위의 latex 파일처럼 자동적으로 프로그램이 수행이 되었으면 좋을 때.(batch의 개념이죠)
make는 위의 두 가지 개념을 모두 포함하고 있다고 봅니다. 보통 리눅스 프로그램에서 make all 하면 자세한 내막은 모르지만 자기가 알아서 다하죠.. 그 다음이 make install 이구요..
--------------------------------------------------------------------
GNU make는 보통 GNUmakefile, Makefile, makefile 중에서 하나가 있으면 그 파일을 읽게 된다. 하지만 일반적으로 Makefile을 추천을 하게 되는데 그 이유는 우선 GNUmake file은 기존의 make에서 인식을 못한다는 단점이 있고, makefile은 보통 소스파일에 묻혀서 잘 안보이게 되기 때문이다.
Makefile은 make가 이해할 수 있도록 일종의 쉘 스크립트 언어 같이 되어 있다.(makefile database라고도 한다) 이 파일에는 결과 파일을 생성시키기 위한 파일들 간의 관계, 명령어 등을 기술하고 있는데 이 강좌의 주된 목적이 바로 Makefile의 작성에 있다.
1.2 make의 필요성
우선은 make의 사용을 프로그램 개발과 유지쪽으로 국한시키기로 한다. 보통 라인수가 많아지면 여러 개의 파일로 나누어(모듈로 나누어) 개발을 하게 된다. 이들은 알게 모르게 서로 관계를 가지고 있는데, 어느 하나를 필요에 의해 바꾸게 되었을 때 그 파일에 있는 함수를 이용하는 다른 파일도 새롭게 컴파일 되어야 한다.
하지만 파일수가 많은 경우 이를 일일이 컴파일을 하게 될 때, 그 불편함과 함께 컴파일 하지 않아도 될 것도 컴파일을 하게 될 수도 있고, 컴파일 해야 할 것도 미처 못하게 되는 경우가 있다. (링크에러의 원인이 되기도 하는데 에러의 원인을 제대로 찾기가 힘이 든다.)
앞에서도 얘기했듯이 이런 상황에서 지능적으로 관계 있는 것만 새롭게 갱신을 할 필요가 있을 때 make 파일은 빛을 발하게 된다.
2. 간단한 Makefile
2.1 Makefile 의 내부구성
Makefile은 기본적으로 아래와 같이 목표(target), 의존관계(dependency), 명령(command)의 3개로 이루어진 기분적인 규칙(rule)들이 계속적으로 나열되어 있다고 봐도 무방하다. make가 지능적으로 파일을 갱신하는 것도 모두 이 간단한 규칙에 의하기 때문이다.
target ... : dependency ...
command
...
...
여기서 목표(target) 부분은 명령(command)이 수행이 되어서 나온 결과 파일을 지정한다. 당연히 목적파일(object file)이나 실행파일이 될 것이다.
명령(command)부분에 정의된 명령들은 의존관계(dependency)부분에 정의된 파일의 내용이 바뀌었거나, 목표부분에 해당하는 파일이 없을 때 이곳에 정의되 것들이 차례대로 실행이 된다. 일반적으로 쉘상에서 쓸 수 있는 모든 명령어들을 사용할 수가 있으며 bash에 기반하는 쉘 스크립트도 지원한다.
--------------------------------------------------------------------
참고 - 참고로 목표부분에는 결과파일만 올 수 있는 것이 아니고, 보통 make clean에서와 같이 간단한 레이블(label) 기능을 제공하기도 한다.
명령 부분은 꼭 TAB 글자로 시작해야 한다. 그냥 스페이스등을 사용하면 make 실행 중에 에러가 난다. 명심하세요. make가 명령어인지 아닌지를 TAB 가지고 구별하기 때문이죠
--------------------------------------------------------------------
2.2 Makefile 예제.
간단한 Makefile을 만들어 본다. 우리가 만들려고 하는 프로그램은 main.c read.c write.c 로 구성되어 있고 모두 io.h 라는 헤더파일을 사용한다고 가정한다. (구성을 간단화 시킵시다.) 이들을 각각 컴파일 해서 test라는 실행파일을 생성시킨다.
% gcc -c main.c
% gcc -c read.c
% gcc -c write.c
% gcc -o test main.o read.o write.o
위의 방식은 make를 쓰지 않고 그냥 명령어를 주는 방식이다. 파일의 수가 작아서 오히려 더 간단하게 보일 수 있으나, 파일이 100개 정도 된다고 가정하면 .... 아찔...
리고 아래는 위와 똑같은 일을 수행하는 Makefile 의 내용이다.
[예제1]
est : main.o read.o write.o
gcc -o test main.o read.o write.o
main.o : io.h main.c
gcc -c main.c
read.o : io.h read.c
gcc -c read.c
write.o: io.h write.c
gcc -c write.c
(대충 알아보시겠어요? , 참 TAB 문자 쓰는거 있지 마세요)
make는 Makefile의 내용을 보고, 내부적으로 어떻게 파일들이 의존하고 있는지 조사한다. 위의 Makefile을 바탕으로 의존관계를 나타내보면 아래와 같다.
test가 만들어지기 위해서는 main.o read.o write.o가 필요하고, 각각의 목적 파일들은 모두 자신의 소스파일과 io.h에 의존함을 알 수가 있다. 가령 main.c를 고쳤다고 생각한다면 main.o가 컴파일 되서 다시 생기고, test도 다시 링크되어서 갱신된다. 만약 io.h가 바뀌었다고 가정하면 모든 파일들이 컴파일 되어서 목적파일이 생기고, 그것들이 링크가 되어 test가 생긴다. 위와 같이 파일들을 구성한 다음 Makefile을 실행시켜보자. Makefile의 실행은 그냥 make 라고 치면 된다.
% make
gcc -c main.c
gcc -c read.c
gcc -c write.c
gcc -o test main.o read.o write.o <-- OK
--------------------------------------------------------------------
참고 - 그냥 테스트기 때문에 read.c write.c io.h는 모두 내용 없이 파일만 만들어두기로 하고, main.c에 간단히 printf 함수만 적어봅시다. 정말 위와 같이 됨을 실감할 꺼에요.. 신기하게.
--------------------------------------------------------------------
2.3 매크로의 사용
간단한 메크로 기능을 사용해보자. main.o read.o write.o라는 것을 OBJECTS 라는 매크로로 바꾸는 것이 아래의 [예제2]에 나와있다.
[예제2]
OBJECTS = main.o read.o write.o
test : $(OBJECTS)
gcc -o $(OBJECTS)
main.o : io.h main.c
gcc -c main.c
read.o : io.h read.c
gcc -c read.c
write.o: io.h write.c
gcc -c write.c
위에서 보다시피 매크로는 그냥 프로그램 짤 때와 같이 사용해서 값을 대입한다. 대신 사용할 때는 반드시 $(..) 안에 넣어서 사용한다. 매크로 치환을 위한 특수한 방법이 아닐까.. 히.. 매크로의 사용법은 위와 같이 간단하므로 다양하게 정의해서 사용할 수 있다. 매크로에 대한 자세한 설명은 다음 강좌에서 언급하기로 한다.
2.4 레이블의 사용
목표부분에 해당하는 부분이 그냥 레이블과 같이 사용될 수도 있다고 이미 설명하였다. [예제2]에다가 목적파일들을 모두 삭제하는 명령어를 추가하기로 한다.
예제3]
OBJECTS = main.o read.o write.o
test : $(OBJECTS)
gcc -o $(OBJECTS)
main.o : io.h main.c
gcc -c main.c
read.o : io.h read.c
gcc -c read.c
write.o: io.h write.c
gcc -c write.c
clean :
rm $(OBECTS)
레이블로 사용될 때는 당연히 의존관계 부분은 없어도 된다. 그리고 clean을 실행시키려면 아래와 같이 한다.
% make clean
rm main.o read.o write.o <-- OK