인디노트

7. Makefile의 실제 예제 본문

개발 플랫폼 및 언어

7. Makefile의 실제 예제

인디개발자 2018. 11. 4. 08:49

https://wiki.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make-7.html


7. Makefile의 실제 예제

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

7.1 프로그램 제작에 쓰일 수 있는 Makefile

여기서는 우선 가장 많이 사용되는 C와 C++에서의 Makefile을 소개하기로 한다. 여러 개의 파일들을 컴파일해서 하나의 실행 파일을 만드는 예제 틀이 바로 예제 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의 독해란 어렵지 않을 것이다. 개략적인 사항만 설명하기로 한다.

.SUFFIXES : .c .o

make 내부에서 정의된 확장자 규칙을 이용하기 위한 것이다. make는 자동적으로 .c와 .o로 끝나는 파일들간에 정의된 규칙이 있는지 찾게 되고 적당한 규칙을 찾아서 수행하게 된다.

CFLAGS = -g $(INC)

CFLAGS 매크로를 재정의 하고 있다. -g 는 디버그 정보를 추가하라는 것이고, $(INC)는 컴파일할때 필요한 include 패스를 적어 두는 곳이다.

all : $(TARGET)

make는 Makefile을 순차적으로 읽어서 가장 처음에 나오는 규칙을 수행하게 된다. 여기서 all 이란 더미타겟(dummy target)이 바로 첫 번째 타겟으로써 작용하게 된다. 관습적으로 all이란 타겟을 정의해 두는 것이 좋다. 결과 파일이 많을 때도 all의 의존 관계(dependency)로써 정의해 두면 꽤 편리하다.

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)를 만들어 보기로 하자. 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 라는 것을 기억하기 바람. (삼단논법 정도만 안다면야...)



반응형
Comments