금요일, 2월 06, 2015

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

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




번  호 : 197
게시자 : 임대영   (RAXIS   )
등록일 : 1995-10-27 02:16
제  목 : [강좌]  make  [3]                    

---------------------------------------------------------------------
                 make 유틸리티 강좌  - 3 -

                 작성 : RAXIS ( 임대영 )
---------------------------------------------------------------------

목차
        4. Makefile 작성시에 알면 좋은것들.
           4.1 긴명령어를 여러라인으로 표시하기.
           4.2 접미사 규칙의 이용 ( Use suffix rule !! )
           4.3 매크로 치환 ( Macro substitution )
           4.4 자동 의존관계 생성 ( Automatic dependency )
      4.5 다중 타겟 ( Multiple target )
           4.6 순환 make ( Recursive MAKE )
           4.7 불필요한 재컴파일 막기.

        5. make 중요 옵션 정리.

        6. Makefile 작성의 가이드라인

---------------------------------------
4. Makefile 작성시에 알면 좋은것들.
---------------------------------------

Makefile을 작성할때 기본적으로 알고 있으면 유익한 것들을 기술한다. 이전 강좌의 내용을 대체로 이해하고 있다면 좋은 팁이 될것이다. 메뉴얼에 나오는 광범위한 내용은 다루지 않고 기본적인 것들에 관심을 두기로 한다

4.1 긴명령어를 여러라인으로 표시하기.

Makefile을 작성중애 명령어가 한줄을 넘어 간다고 가정하자. 이때 그냥 줄줄 적는다면 읽기도 힘들고, 작성하는 사람도 조금 찜찜하다. 이때 '\' 문자를 이용해서 여러라인으로 나타낼수 있다. 이미 C언어에 익숙한 사람이라면 낯익은 기호일것이다. 아래의 예제를 보자.

[예제 13].............................................................

OBJS =  shape.o \
        rectangle.o \
        circle.o \
        line.o \
        bezier.o 
......................................................................


위의 예제는 OBJS = shape.o rectangle.o circle.o line.o bezier.o 라는 문장을 여러라인으로 표시한것이다. 보기에도 깔끔해 보이지 않은가.

4.2 접미사 규칙의 이용 ( Use suffix rule !! )

강좌 2 에서 접미사 규칙에 대해서 많이 설명을 했다. Makefile의 작성시 C, C++, tex 등의 화일은 이미 정의되어 있는 규칙을 이용하면 간단하고, 깔끔한 Makefile을 작성할수 있다. 강좌2에서 직접 우리가 규칙을 간단히 구현해보기도 했는데, 이것은 접미사 규칙의 개념을 설명하기 위함이었다.

어떤 화일들이 이미 규칙으로 정해져 있는지 한번 살펴보기로 한다. 아래에 열거된 화일들은 특별히 따로 정의하지 않은 상테에서 바로 이용할 수 있는 것들이다. ( GNU Make 메뉴얼에 바탕을 두고 작성되었다. )

   C 컴파일             ( XX.c -> XX.o )
   C++ 컴파일           ( XX.cc 또는 XX.C --> XX.o )
   Pascal 컴파일        ( XX.p --> XX.o )
   Fortran 컴파일       ( XX.f 또는 XX.F --> XX.o )
   Modula-2 컴파일      ( XX.def --> XX.sym )  
                       ( XX.mod --> XX.o )
   assembly 컴파일      ( XX.s --> XX.o )
   assembly 전처리      ( XX.S --> XX.s )
   single object file의 링크 ( XX.o --> XX )
   Yacc 컴파일(?)       ( XX.y --> XX.o )
   Lex 컴파일(?)        ( XX.l --> XX.o )
   lint 라이브러리 생성  ( XX.c --> XX.ln )
   tex 화일 처리        ( XX.tex --> XX.dvi )
   texinfo 화일처리     ( XX.texinfo 또는 XX.texi --> XX.dvi )
   RCS 화일 처리        ( RCS/XX,v --> XX )
   SCCS 화일처리        ( SCCS/XX.n --> XX )

위에 정의된 화일만이 make에서 처리할수 있는것은 아니다. 그밖의 화일에 대해서는 사용자가 직접 정의해주면 얼마든지 make를 사용할수 있다. 그럼 이젠 위와 같은 화일들을 처리하기 위한 명령어는 어떤 매크로로 정의되어 있는지 알아보자. 이미 말했듯이 아래에 열거된 매크로는 재정의 가능하다. 가령 TEX = tex 이지만 대부분 TEX = latex 로 재정의되어야 할 것이다.

AR = ar ( Archive maintaining program )
AS = as ( Assembler )
CC = cc ( = gcc , C compiler )
CXX = g++ ( C++ compiler )
CO = co ( extracting file from RCS )
CPP = $(CC) -E ( C preprocessor )
FC = f77 ( Fortran compiler )
LEX = lex ( LEX processor )
PC  = pc ( Pascal compiler )
YACC = yacc ( YACC processor )
TEX = tex ( TEX processor )
TEXI2DVI = texi2dvi ( Texiinfo file processor )
WEAVE = weave ( Web file processor )
RM  = rm -f ( remove file )

이미 강좌2에서 밝혔지만 위의 명령어에서 사용될 FLAG(옵션) 에 정의한 매크로에 대해서도 알아보기로 한다.

* ARFLAGS =     (ar achiver의 플래그 ) 
  ASFLAGS =     (as 어셈블러의 플래그 )
* CFLAGS = (C 컴파일러의 플래그 )
* CXXFLAGS =    (C++ 컴파일러의 플래그 ) 
  COFLAGS =     (co 유틸리티의 플래그 )
  CPPFLAGS =    (C 전처리기의 플래그 )
  FFLAGS =      (Fortran 컴파일러의 플래그 )
* LDFLAGS =     (ld 링커의 플래그 ) 
* LFLAGS =      (lex 의 플래그 )
  PFLAGS =      (Pascal 컴파일러의 플래그 )
* YFLAGS =      (yacc 의 플래그 )

위애서 '*"표시를 한것은 자주쓰이게 될 플래그들을 표시한것이다. 위에서 표시한 여러가지 매크로들을 재정의를 무조건 하라는 배려에서인지 대부분 값이 설정되어 있지 않다. 가령 C프로그램을 짤때 CFLAGS 를 재정의해야 할 것이다

4.3 매크로 치환 ( Macro substitution )

매크로를 지정하고, 그것을 이용하는것을 이미 알고 있다. 그런데 필요에
의해 이미 매크로의 내용을 조그만 바꾸어야 할 때가 있다. 매크로 내용의
일부만 바꾸기 위해서는 $(MACRO_NAME:OLD=NEW)와 같은 형식을 이용하면
된다.

MY_NAME   = Michael Jackson
YOUR_NAME = $(NAME:Jack=Jook)

위의 예제에서는 Jack 이란 부분이 Jook 으로 바뀌게 된다. 즉 YOUR_NAME이란 매크로의 값은 Michael Jookson 이 된다. 아래의 예제를 하나 더
보기로 한다.               ^^^^

OBJS = main.o read.o write.o
SRCS = $(OBJS:.o=.c)

위의 예제에서는 OBJS 에서 .c가 .o로 바뀌게 된다. 즉 아래와 같다.

SRCS = main.c read.c write.c 

위의 예제는 실제로 사용하면 아주 편할때가 많다. 가령 .o 화일 100개에 .c 화일이 각각 있을때 이들을 다 적을려면 무척이나 짜증나는 일이 될 것이다.

4.4 자동 의존관계 생성 ( Automatic dependency )

일반적인 make의 구조는 아래와 같이 target, dependency,command가 연쇄적으로 정의되어 있는 것과 같다고 하였다.

target : dependency 
        command ....

그런데 위에서 command가 없이 타겟과 의존관계만 표시가 되면 이는 타겟이 어느 화일에 의존하고 있는지 표시해주는 정보의 역할을 한다. 이런 정보는 Makefile 을 작성할 때 없어서는 안되는 부분이다. (이 부분이 없으면, make 는 증말 바보처럼 행동합니다. ) 그런데 일일이 이런 정보를 만든다는것은 쉬운 일이 아니다. 화일이 1000 개라고 할때 이것을 어케 다 표시하누.. 이런 단조롭고 귀찮은 일을 자동으로 해주는 좋은 유틸리티가 있다. 우선gccmakedep가 있는지 확인해 보자. gccmakedep 는 어떤 화일의 의존관계를 자동으로 조사해서 Makefile의 뒷부분에 자동으로 붙여주는 유틸리티이다. gccmakedep가 없다면 gcc -M XX.c 라고 해보자. 그러면 XX.c의 의존관계가 화면에 출력됨을 알수 있을것이다. ( gccmakedep 도 내부적으로 gcc -M 을 사용한다. )

프로그램 인스톨할때 make dep 라는 것을 친 기억이 있을것이다. 화일들의 의존관계를 작성해준다는 의미이다. 그럼 우리의 Makefile에도 이런 기능을 첨가해보기로 한다.

[예제 14].............................................................

.SUFFIXES = .c .o 
CFLAGS = -O2 -g

OBJS = main.o read.o write.o 
SRCS = $(OBJS:.o=.c)

test : $(OBJS)
        $(CC) -o test $(OBJS)

dep :
        gccmakedep $(SRCS)
......................................................................

위의 Makefile을 이해할수 있다면 이제 Makefile에 대해서 어느정도 도가 텃다고 해도 무방할것이다. 위의 예제에서 화일들간의 의존관계가 없다. 그럼 이제 make dep 을 써서 자동적으로 생성시켜보자.

% make dep 
% vi(emacs) Makefile

Makefile 의 뒷부분에 다음과 같은 내용이 붙어있는것을 알게 될것이다.

[예제 15].............................................................

# DO NOT DELETE
main.o: main.c /usr/include/stdio.h /usr/include/features.h \
 /usr/include/sys/cdefs.h /usr/include/libio.h \
 /usr/include/_G_config.h io.h
read.o: read.c io.h
write.o: write.c io.h
......................................................................

main.o 에는 조금 자질구래한 헤더화일까지 붙어있다. 이것은 헤더화일 안에서 include 하는 화일들을 다 찾다보니까 그런것이다. 별로 신경쓸 것은 없고..  대충 우리가 지금까지 손으로 작성해온 것과 거의 흡사함을 알 수 있다. 아니 오히려 더 정확함을 알수 있다. ( 이제부터 Make 는 스마트하게 동작한다. )

4.5 다중 타겟 ( Multiple target )

하나의 Makefile 에서 꼭 하나의 결과만 만들어 내라는 법은 없다. 가령 결과 화일이 3개가 필요하다고 하자. 아래의 예제를 보기로 한다.

[예제 15].............................................................

.SUFFIXES = .c .o 
CC     = gcc
CFLAGS = -O2 -g

OBJS1 = main.o test1.o   <-- 각각의 매크로를 정의
OBJS2 = main.o test2.o 
OBJS3 = main.o test3.o 
SRCS = $(OBJS1:.o=.c) $(OBJS2:.o=.c) $(OBJS3:.o=.c)   

all : test1 test2 test3  <-- 요기에 주의 

test1 : $(OBJS1)
        $(CC) -o test1 $(OBJS1) 

test1 : $(OBJS2)
        $(CC) -o test2 $(OBJS2)

test1 : $(OBJS3)
        $(CC) -o test3 $(OBJS3)

dep :
        gccmakedep $(SRCS)
......................................................................

위의 프로그램은 make all 을 함으로써 동작한다. 실제로 동작시켜보면 아래 와 같은 결과가 나온다.

% make all ( 또는 make )
gcc -O2 -g   -c  main.c -o main.o
gcc -O2 -g   -c  test1.c -o test1.o
gcc -o test1 main.o test1.o             <-- test1 의 생성
gcc -O2 -g   -c  test2.c -o test2.o
gcc -o test2 main.o test2.o             <-- test2 의 생성
gcc -O2 -g   -c  test3.c -o test3.o
gcc -o test3 main.o test3.o             <-- test3 의 생성

4.6 순환 make ( Recursive MAKE )

규모가 큰(?) 프로그램들은 화일들이 하나의 디렉토리에 있지 않는 경우가 많다. 여러개의 서브시스템이 전체 시스템을 구성한다고 가정하면 각 서브 시스템에 Makefile이 존재한다. (서브시스템 = 서브디렉토리 ) 따라서 여러개의 Makefile을 동작시킬 필요가 있도록 Makefile 을 고쳐보자. 서브 디렉토리에 있는 Makefile을  동작시키는 방법은 의외로 간단하다. 아래의 간단한 예제를 보자.

[예제 16].............................................................

subsystem:
        cd subdir; $(MAKE)      .....(1)

subsystem:
        $(MAKE) -C subdir       .....(2)
......................................................................


위의 예제에서 (1)과 (2)는 동일한 명령을 수행한다 (1)을 기존으로 동작을 한번 묘사해보자. 우리가 만들 시스템의 타겟이 subsystem이다. (이름은 아무래도 상관없다) 우선 subdir이라는 곳으로 가서, 거기에 있는 Makefile 을 동작시키게 된다. ( 간단하죠 ) MAKE라는 것은 그냥 make 라는 명령어를 표시하는 매크로일뿐..  그럼 완전한 예제를 한번 구성해 보기로 한다.

[예제 16].............................................................

.SUFFIXES = .c .o
CC     = gcc
CFLAGS = -O2 -g

all :  DataBase Test      <-- 요기에 집중.

DataBase:
        cd db ; $(MAKE)   <-- db 로 이동해서 make 실행

Test: 
        cd test ; $(Make) <-- db 로 이동해서 make 실행
......................................................................

위의 예제에서 db, test 디렉토리에 있는 Makefile은 지금까지 우리가 공부했던 Makefile 과 거의 흡사하다고 가정하자. 그럼 위의 Makefile 을 실행시켜본다.

% make 
cd db ; make
make[1]: Entering directory`/home/raxis/TEST/src'
gcc -O2 -g   -c DBopen.c -o DBopen.o
gcc -O2 -g   -c DBread.c -o DBread.o
gcc -O2 -g   -c DBwrite.c -o DBwrite.o
make[1]: Leaving directory `/home/windows/TEST/src'
cd test ; make
make[1]: Entering directory `/home/raxis/TEST/test'
gcc -O2 -g   -c test.c -o test.o
make[1]: Leaving directory `/home/windows/TEST/test'

위의 가상 실행을 보면 우선 db 로 가서 거기의 Makefile을 수행시키고, 다음에는 test로 가서 Makefile 을 실행시킴을 볼수 있다. 우선은 단순하게 컴파일만 시켰는데, 다르게 한번 생각해보자. db 디렉토리에서의 최종 타겟으로 가령 db.a을 만들어 내고 test 디렉토리에서 이를 링크시킨다고 생각하면 꽤 괜찮은 시나리오가 될것이다. 위에서 [1]이라고 나타난것은 현재의 레벨을 의미한다. 원래 디렉토리의 레벨이 [0]이고, 여기서는 한레벨 더 내려갔으므로 [1]이라고 표시된 것이다

4.7 불필요한 재컴파일 막기.

의존관계 규칙에 의해 하나가 바뀌면 그에 영항받는 모든 화일이 바뀐다고 앞에서 말했다. 그러나 다른 화일들에게 아무 영향을 주지않을 수정이 있었는 데도 재컴파일을 시도한다면 시간낭비가 될수도 있다. 가령 모든 .c 화일에서 include 하는 헤더화일에서 새로운 #define PI 3.14 라고 정의를 했다고 가정하자. 그리고 PI 라는것은 아무곳에서도 사용을 하지 않는다.

이때는 'make -t' 라고 해보자. -t 는 touch를 의미하는 옵션으로써 컴파일을 하지않는 대신 화일의 생성날짜만 가장 최근으로 바꾸어 놓는다. 새로 컴파일 된것처럼 처리를 하는 것이다. touch유틸리티 명렁어에 익숙한 사람이라면 이해할 것이다. touch는 화일의 생성날짜를 현재로 바꾸어 주는 간단한 유틸리티이다.

---------------------------------------
5. make 중요 옵션 정리.
---------------------------------------

make에서 거의 모든것은 Makefile내부에서 모두 지정을 할수 있다. 그 중 일부를 make의 실행시에 옵션으로 통해서 줄수도 있다. 수많은 옵션중에서 기억해둘만한 것 몇가지만 소개하기로 한다.

  -C dir  ... 위에서도 밝혔듯이 Makefile 을 계속 읽지말고 우선은 
              dir 로 이동하라는 것이다. 순환 make 에 사용된다.

  -d      ... Makefile 을 수행하면서 각종 정보를 모조리 출력해준다. 
( --debug )   출력량이 장난이 아님.. 결과를 화일로 저장해서 읽어보면 
              make 의 동작을 대충 이해할수 있다.

  -h      ... 옵션에 관한 도움말을 출력한다. 
( --help )

  -f file ... file 에 해당하는 화일을 Makefile 로써 취급한다.  
( --file )

  -r      ... 내장하고 있는 각종 규칙(Suffix rule 등) 을 없는것으로 
( --no-builtin-rules )  간주한다. 따라서 사용자가 규칙을 새롭게 정의해
                        주어야 한다.

  -t      ... 화일의 생성날짜를 현재시간으로 갱신한다. 
( --touch )

  -v      ... make 의 버전을 출력한다. ( 전 GNU make 3.73 을 씁니다.) 
( --version )

  -p      ... make 에서 내부적으로 세팅되어 있는 값들을 출력한다. 
( --print-data-base )

  -k      ... make 는 에러가 발생하면 도중에 실행을 포기하게 되는데 
( --keep-going ) -k 는 에러가 나더라도 멈추지 말고 계속 진행하라는 뜻

---------------------------------------
6. Makefile 작성의 가이드라인
---------------------------------------

make를 많이 써본 사람은 어느정도 자신만의 Makefile을 작성하는 일정한 스타일 같은것이 있다. 프로그램이 짜는 사람마다 다르듯이 Makefile 도 각각이다. 여기서는 그냥 가장 일반적인 가이드라인을 제시하기로 한다. 다음 강좌에서 Makefile 의 여러 예제를 살펴보면서 다시한번 자세히 설명
할 것이다.

  o  매크로를 잘 사용하면 Makefile이 깔끔해질뿐 아니라, 내용의 수정도
     용이하다. 조금 과장해서 말한다면, 최대한 매크로를 많이 사용하라고
     말하고 싶다. Makefile내에서 두번이상 나오는것들은 매크로로 정의해
     두면 편하다. 자신의 프로그램 특성에 따라서 기존의 매크로를 재정의
     하는것도 좋다.

  o  make 에서 정의되어 있는 규칙들을 최대한 이용한다. 접미사 규칙은
     무조건 이용하기를 권한다. 기존의 규칙들을 자기가 정의하는것도
     좋지만, 억지로 이럴 필요는 없다.

  o  대체로 아래와 같이 Makefile 을 구성한다.
         o  매크로 정의 부분
         o  타겟을 얻기위한 명령어 부분
         o  의존관계 부분

     지금까지 예제로 나온 것들이 모두 위의 순서대로 작성되어 있다.
     반복이 되겠지만 다시한번 예제를 제시해본다.

[예제 17].............................................................

.SUFFIXES = .c .o                     ----+
CFLAGS    = -g                            |
                                          |
OBJS      = main.o \                      |
            read.o \                      |
            write.o                       | 매크로 정의 부분
SRCS      = $(OBJS:.o=.c)                 |
                                          |
TARGET    = test                      ----+

$(TARGET): $(OBJS)                   ----+
        $(CC) -o $@ $(OBJS)               |
dep  :                                    |
        gccmakedpend $(SRCS)              | 
new  :                                    | 명령어 정의 부분
        touch $(SRCS) ; $(MAKE)           |
clean :                                   |
        $(RM) $(OBJS) $(TARGET) core  ----+

                                      ---- 여기부터 의존관계 부분
......................................................................

위의 예제는 최대한 매크로를 많이 이용할려고 했기 때문에 독해(?)하기 어려울 수도 있다.

----다음편 예고------------------------------------------------------
다음편에는 Makefile 의 여러가지 작성 예제를 가지고 설명을 하겠습니다. 아직 make에 익숙하기 않을것이라고 생각하기 때문입니다. 최대한 다양한 예제를 다룸으로써 나중에 많은 도움이 되도록 하죠. make 수행중에 나올 수 있는 여러 예러들도 다음강좌에서 한번 다루어 보죠. 그리고 가능하다면 Imakefile란 것도 한번 건드려 봅시다.



댓글 없음:

댓글 쓰기