금요일, 2월 06, 2015

make 유틸리티 강좌 (4/4)

make 유틸리티 강좌 (4/4)



번  호 : 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 의 실제예제
-------------------------------------------------------------------

지금까지 강좌를 진행하면서 Makefile 의 여러가지 예제들을 제시하였다. 강좌에 나온 예제들을 조금만 바꾸면 자신의 Makefile로써 사용할수 있다. 여기에서는 여러가지 Makefile 들의 기본틀 (template)들을 소개하고자 한다.

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 이고 어떤 특징을 가지고 있는지 한번 살펴 보기로 하죠.

댓글 없음:

댓글 쓰기