번 호 : 201
게시자 : 임대영 (RAXIS )
등록일 : 1995-11-04 05:28
제 목 : [강좌] make [4] .. make 마지막.
---------------------------------------------------------------------
make 유틸리티 강좌 - 4 -
작성 : RAXIS ( 임대영 )
---------------------------------------------------------------------
목차
7. Makefile 의 실제 예제.
7.1 프로그램 제작에 쓰일수 있는 Makefile
7.2 라이브러리의 제작에 쓰일수 있는 Makefile
7.3 latex 에서 쓰일수 있는 Makefile
8. make 수행시에 나타나는 에러들
-------------------------------------------------------------------
7. Makefile 의 실제예제
-------------------------------------------------------------------
7.1 프로그램 제작에 쓰일수 있는 Makefile
여기서는 우선 가장많이 사용되는 C 와 C++ 에서의 Makfile 을 소개하기로 한다. 여러개의 화일들을 컴파일해서 하나의 실행화일을 만드는 예제틀이 바로 [예제7.1]이다.
[에제7.1] ----------------------------------------------------------------
.SUFFIXES = .c .o
CC = gcc
INC = <-- include 되는 헤더화일의 패스를 추가한다
LIBS = <-- 링크시에 필요한 라이브러리를 추가한다.
CFLAGS = -g $(INC) <-- 컴파일에 필요한 각종옵션을 추가한다.
OBJS = <-- 오브젝트 화일의 이름을 적는다.
SRCS = <-- 소스화일의 이름을 적는다.
TARGET = <-- 링크후에 생성될 실행화일의 이름을 적는다.
all : $(TARGET)
$(TARGET) : $(OBJS)
$(CC) -o $@ $(OBJS) $(LIBS)
dep :
gccmakedep $(INC) $(SRCS)
clean :
rm -rf $(OBJS) $(TARGET) core
new :
$(MAKE) clean
$(MAKE)
[예제7.1] 에서 바꿔야 할부분은 표시를 해두었다. 자신의 화일들로 적당히 고쳐준 다음 make dep 를 수행시켜본다. 그러면 자동으로 의존관계가 생성된다.
% make dep <-- 자동으로 의존관계 생성
% make <-- make 동작
지금까지의 강좌를 이해하고 있다면 위의 Makefile 의 독해란 어렵지 않을 것이다. 개락적인 사항만 설명하기로 한다.
o .SUFFIXES = .c .o
make 내부에서 정의된 접미사 규칙을 이용하기 위한것이다. make는 자동적으로 .c 와 .o 로 끝나는 화일들간에 정의된 규칙이 있는지 찾게 되고 적당한 규칙을 찾아서 수행하게 된다.
o CFLAGS = -g $(INC)
CFLAGS 매크로를 재정의하고 있다. -g 는 디버그 정보를 추가하라는 것이고 $(INC)는 컴파일시에 필요한 include 패스를 적어두는 곳이다.
o all : $(TARGET)
make 는 Makefile 을 순차적으로 읽어서 가장 처음에 나오는 규칙을 수행하게된다. 여기서 all 이란 더미타겟(dummy target)이 바로 첫번째 타겟으로써 작용하게 된다. 관습적으로 all이란 타겟을 정의해 두는것이 좋다. 결과화일이 많을때도 all의 의존관계(dependency)로써 정의해두면 꽤 편리하다.
o dep :
gccmakedep $(INC) $(SRCS)
의존관계를 자동적으로 생성해주기 위한 것이다. 헤더화일의 패스까지 추가 되어야한다는 것에 주의하기 바람. 이것은 내부적으로 gcc 가 작동되기 때문이다.
[예제7.1] 을 C++ 화일에 이용하기 위해서는 .SUFFIEX , CC, CFLAGS 그리고 타겟에 대한 명령어를 아래의 내용과 같이 바꾸면 된다.
[예제7.2] -------------------------------------------------------------
.SUFFIXES = .cc .o
CXX = g++
CXXFLAGS = -g $(INC)
$(TARGET) : $(OBJS)
$(CXX) -o $@ $(OBJS) $(LIBS)
------------------------------------------------------------------------
물론 각자의 취향에 따라서 Makefile 을 만들어도 된다. 여기서 제시하고 있는 Makefile 은 어디까지나 필자의 관점에서 만든 Makefile 을 소개하고 있는 것 뿐이니까..
7.2 라이브러리와의 링크가 필요한 필요한 Makefile
라이브러리를 만들기위한 Makefile 은 어떤 차이가 있을까. [예제7.1]과 거의 흡사하다. 다만 TARGET 이 실행화일이 아니고 라이브러리라는 것 뿐. 그리고 라이브러리 만드는 방법을 알고 있어야 한다는것.. 참고로 라이브러리 만드는 방법을 소개하기로 한다. read.o , write.o 를 libio.a 로 만들어 보자. 라이브러리를 만들기 위해서는 ar 유틸리티와 ranlib 유틸리티가 필요하다. ( 자세한 설명은 man 을 이용 )
% ar rcv libio.a read.o write.o
^^^^^^^ ^^^^^^^^^^^^^^
a - read.o <-- 라이브러리에 추가 (add)
a - write.o
% ranlib libio.a <-- libio.a 의 인덱스(index)를 생성
그럼 위의 과정을 Makefile 로 일반화시킨다면 어떻게 될까.. 아주 조금만 생각하면 된다. [예제 7.1]에서 TARGET를 처리하는 부분만 아래와 같이 바꾸어 보자.
[예제7.3]--------------------------------------------------------------------
TARGET = libio.a
$(TARGET) : $(OBJS)
$(AR) rcv $@ $(OBJS) <-- ar rcv libio.a read.o write.o
ranlib $@ <-- ranlib libio.a
-----------------------------------------------------------------------------
ELF 기반하에서 동적 라이브러리( dynamic library, shared library, 윈도우즈의 DLL)를 만들어 보기로 하자. ELF 상에서는 동적 라이브러리 만드는 방법이 이전에 비해 아주 간단해 졌다. ( 옛날에 모티프 소스 가지고 동적라이브러리 만든다고 고생한것에 비하면 세상이 너무 좋아진것 같음 ) BSD계열의 유닉스를 사용해본 사람이라면 비슷하다는것을 느낄것이다. 그럼 read.c write.c 을 컴파일해서 libio.so.1 을 만들어보자. (so는 shared object 를 의미, 뒤의 .1은 동적 라이브러리의 버전을 의미)
% gcc -fPIC -c read.c <-- -fPIC 를 추가해서 컴파일한다.
% gcc -fPIC -c write.c
% gcc -shared -Wl,-soname,libio.so.1 -o libio.so.1 read.o write.o
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^
동적라이브러리를 만들기 위한 옵션
위와 같이 하면 libio.so.1 가 생성된다. 사용자가 만든 동적 라이브러리를 사용하는 방법에 대해서는 간단히만 언급하기로 한다. 우선 libio.so.1 을 /usr/lib 로 옮겨서 ldconfig -v 해서 캐시를 갱신해주던지, 아니면 LD_LIBRARY_PATH 를 지정해두면 된다. 아래는 test.c를 libio.so.1과 링크시키는 예제이다.
% gcc -c test.c
% gcc -o test test.o -L. -lio <-- 현재 디렉토리에 있다고 가정
^^^^
[예제7.1] 를 약간 고쳐서 동적라이브러리를 자동적으로 만들어보자. 이번엔 완전한 내용의 Makefile 을 소개한다.
[에제7.4]------------------------------------------------------------------
.SUFFIXES = .c .o
CC = gcc
INC =
LIBS =
CFLAGS = -g $(INC) -fPIC <-- -fPIC 추가
OBJS = read.o write.o
SRCS = read.c write.c
TARGET = libio.so.1 <-- libio.so.1 이 최종화일
all : $(TARGET)
$(TARGET) : $(OBJS)
$(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS)
dep : gccmakedep $(INC) $(SRCS)
clean :
rm -rf $(OBJS) $(TARGET) core
-----------------------------------------------------------------------------
아주 조금밖에 바뀌지 않았다. 따라서 이글을 읽는 여러분은 이제 각자의 목적에 맞게 Makefile 을 구성할수 있을것이다. 대부분 접미사 규칙과 최종화일을 생성해내기 위한 명령어가 무엇인지 알고있으면 Makefile 을 자기 개성껏 꾸밀 수 있을것이다.
7.3 latex 에서 쓰일수 있는 Makefile
Makefile 이 쓰일수 있는 다른 예로써 가장 대표적인 것이 latex 를 사용 할 때이다. 이미 이전 강좌에서 여러차례 소개 된적도 있다. 그럼 doc.tex 를 doc.ps 로 만들어 보기로 하자. 약간 어렵게 하기 위해서 doc.tex 는 내부적 으로 intro.tex 와 conclusion.tex 를 포함하고 있다고 가정한다. ( 논문 같은것을 작성할때는 이렇게 .tex 화일이 많아지게 된다. )
% latex doc.tex <-- doc.dvi 의 생성
% dvips doc.dvi -o <-- doc.tex 의 생성
위와 같은 일을 수행하는 Makefile 을 한번 살펴보자.
[예제7.5]--------------------------------------------------------------------
.SUFFIXES = .tex .dvi <-- 접미사 규칙
TEX = latex
OBJ = doc.dvi
SRC = doc.tex
TARGET = doc.ps <-- 결과 화일
all : $(TARGET)
$(TARGET) : $(OBJ)
dvips $(OBJ) -o <-- dvips doc.dvi -o
new : <-- 강제적으로 다시 make
touch $(SRC) ; $(MAKE)
doc.tex : intro.tex conclusion.tex <-- 의존관계 설정
-------------------------------------------------------------------------------
참고로 gccmakedep 는 latex 화일은 지원을 하지 않는것 같다. 따라서 의존관계같은 것은 우리가 직접 적어주어야 한다.
여기에서 제시된 응용말고 그외의 다른 응용에 대해서는 각자가 한번 생각해보기 바란다. 타이핑하기 귀찮은것은 모조리 make 를 이용할수 있다는 것을 염두. 약간은 원인,결과를 따지는 논리(?)가 적용된다고 볼수도 있는 것이 make 라는 것을 기억하기 바람. ( 삼단논법정도만 안다면야..)
8. make 수행시에 나타나는 에러들
make 를 수행하게 되면 이상한 에러에 당황을 하게 되는 경우가 많아, 도대체 어디가 틀렸는지 감을 못잡는 경우가 허다하다. 그런데 make메뉴얼에도 에러에 대한 종류와 그 대처방안에 대해서는 거의 언급이 없는 관계로 이 부분은 필자의 경험에 의거해서 작성한다. ( 에러의 원인,대처방안이 모두 다 틀렸을수도 있다는 것을 염두에 두기 바랍니다. )
에러1)
Makefile:17: *** missing separator. Stop.
Makefile 을 작성할때 명령어(command)부분은 모두 TAB 문자로 시작해야한다고 강좌[1]부터 강조하였다. 위의 에러는 TAB 문자를 쓰지 않았기 때문에 make 가 명령어인지 아닌지를 구별못하는 경우이다.
대처 1) 17번째줄(근처)에서 명령어가 TAB 문자로 시작하게 바꾼다.
에러2)
make: *** No rule to make target `io.h', needed by `read.o'. Stop.
위의 에러는 의존관계에서 문제가 발생했기 때문이다. 즉 read.c가 io.h에 의존한다고 정의되어 있는데, io.h 를 찾을수없다는 에러이다.
대처2) 의존관계에서 정의된 io.h 가 실제로 존재하는지 조사해본다. 없다면 그 이유를 한번 생각해본다. make dep 를 다시 실행시켜서 의존관계를 다시 생성시켜주는것도 하나의 방법이다.
에러3)
Makefile:10: *** commands commence before first target. Stop.
위의 에러는 '첫번째 타겟이 나오기 전에 명령어가 시작되었다'는 애매한 에러메시지 이다. 필자가 경험한 이 에러의 원인은 주로 긴 문장을 여러 라인에 표시를 하기 위해서 '\'를 사용할때, 이를 잘못 사용했기 때문인 것 같다. 즉 '\'부분은 라인의 가장 끝문자가 되어야 하는데 실수로 '\'뒤에 스페이스를 몇개 집어넣으면 여지없이 위의 에러가 발생한다.
대처3) 10번째줄(근처)에서 '\'문자가 있거든 이 문자가 라인의 가장 끝 문자가 되도록 한다. 즉 '\'문자 다음에 나오는 글자(스페이스가 대부분)는 모조리 없애버린다.
에러4)
make를 수행시키면 의도했던 실행화일은 안생기고 이상한 행동만 한다. 가령 make clean 했을때와 같은 행동을 보인다.
make는 천재가 아니라는 점을 생각해야 한다. make 는 Makefile 의 내용을읽다가 첫번째 타겟으로 보이는 것을 자신이 생성시켜야 할 결과화일이라고 생각한다. 따라서 clean 부분을 Makefile 의 첫번째 타겟으로 해버리면 위와 같은 결과가 나타나게 된다.
대처4) [예제7.1]에서 all 이라는 필요없는 타겟을 하나 만들어두었다. 이것은 make 가 all 을 첫번째 타겟으로 인식시키기 위함이었다. 따라서 자신이 생성시키고 싶은 결과 화일을 첫번째 타겟이 되게 하던지, 아니면 [에제 7.1]처럼 all과 같은 더미 타겟(dummy target)을 하나 만들어둔다. 그리고 make clean, make dep 같은 부분은 Makefile 의 끝부분에 만들어 두는 것이 안전하다.
에러5)
이미 컴파일했던 화일을 안 고쳤는데도 다시 컴파일한다.
이 행동은 make가 의존관계를 모르기 때문이다. 즉 사용자가 의존관계를 설정해주지않았다는 말이 된다. 따라서 make 는 무조건 모든 화일을 컴파일해서 실행화일을 만드는 일이 자신이 할 일이라고 생가하게 된다.
대처5) 오브젝트화일, 소스화일, 헤더화일들의 의존관계를 설정해주어야 한다. gccmakedep *.c 라고 하면 Makefile 의 뒷부분에 자동적으로 의존관계를 만들어준다. 그외의 다른 화일들에 대해서는 사용자가 적절하게 의존관계를 설정해주어야 한다.
main.o : main.c io.h
read.o : read.c io.h
write.o : write.c io.h
위의 예제는 강좌[1]에서도 제시했던건데.. TARGET : DEPENDENCY의 형식으로 의존관계를 작성한것이다. ( make 에게 의존관계를 알려주는 방법이죠 )
% 그외의 경우에 대해서는 각자가 한번 원인과 결과를 알아보기 바란다. 그리고 팁의 형식으로 글을 올린다면 다른 사람에게도 많은 도움이 될 것이다. 일단 make 에서 에러를 내기 시작하면 초보자는 원인조차 모르는 경우가 많기 때문이다.
─ 강좌를 마치면서 ─────────────────────────
이번 make 강좌는 make 유틸리티에 대한 전반적인 이해와 간단한 Makefile 작성을 목적으로 하였습니다. make 에 관한 모든것을다 소개하지는 않았습니다. ( 개인적인 능력의 한계 !! ) make 를 아주 잘쓰기 위해서는 make 자체에 대한 지식보다는 유닉스(리눅스)의 쉘 프로그램과 고난위도(?)의 명령어까지 알고 있어야 하기 때문이죠. 해커가 될려면 이런 지식을 많이 알고 있어야 합니다. 한가지 아쉬운게 imake 에 대해서 설명을 못한것입니다. imake 에 대해서는 [make 중급코스]란 이름하에 언젠가 강좌를 해보도록 하죠. ( imake 는 Makefile 을 생성시켜준다고 생각하세요 )
지금 make 다음의 강좌로 뭘 해야 할지 고민중입니다. 우선 X 강좌를 하시는 분(아직은 조용하네요)의 강좌의 이해를 높이기 위해서 X의 개념을 2회 정도의 강좌로 할 생각입니다. X 가 왜 X 이고 어떤 특징을 가지고 있는지 한번 살펴 보기로 하죠.
댓글 없음:
댓글 쓰기