본문 바로가기

소프트웨어 빌드 시스템

소프트웨어 빌드 시스템 원리와 활용 요약 (2) - 2. Make 기반 빌드 시스템

2. Make 기반 빌드 시스템
- GNU Make의 예시와 작동 방식을 보여주는 챕터이다.

- 책에서는 c 파일로 예제를 진행하는데, 나는 c++로 수정하여 작성하였다. 코드와 Makefile은 https://github.com/lss0815/software_build_system_review/tree/main/chap2를 참고하면 된다.

2.1 계산기 프로그램 예제

- add.c, calc.c, muilt.c, numbers.h, sub.c의 소스코드가 있을 때, calculator라는 타겟파일을 생성할 것이다.

$ g++ -g -c add.cpp
$ g++ -g -c calc.cpp
$ g++ -g -c mult.cpp
$ g++ -g -c sub.cpp
$ g++ -g -o calculator add.o calc.o mult.o sub.o

- 책에서는 위의 종속성을 다음과 같이 표현하였다. 이 빌드 종속 관계를 GNU Make를 이용하여 작성하는 방법을 챕터에서 설명할 것이다. 

2.2 간단한 Makefile 작성

- 모든 종속성 그래르를 의존성 규칙으로 명시한 Makefile은 다음과 같이 작성할 수 있다.

- 각각을 설명하면, 생성파일: 의존하는 파일리스트, 그리고 다음 줄에 tab 이후에는 UNIX 명령어이다.

# 1st example
calculator: add.o calc.o mult.o sub.o
	g++ -g -o calculator add.o calc.o mult.o sub.o

add.o: add.cpp numbers.h
	g++ -g -c add.cpp

calc.o: calc.cpp numbers.h
	g++ -g -c calc.cpp
    
mult.o: mult.cpp numbers.h
	g++ -g -c mult.cpp
    
sub.o: sub.cpp numbers.h
	g++ -g -c sub.cpp

2.3 Makefile의 간결화

- 오브젝트 파일을 생성하는 코드가 반복되고 있음을 볼 수 있는데, 과연 이렇게 반복되어야할까 싶고 확장성에도 문제가 있을 것으로 보인다.

- 다행히 GNU Make에서는 같은 이름으로 소스파일이 있을 때, 소스코드를 명시하지 않아도 오브젝트 파일을 컴파일하는 기본 내장 규칙이 있다.

#2nd example
calculator: add.o calc.o mult.o sub.o
	g++ -g -o calculator add.o calc.o mult.o sub.o

add.o calc.o mult.o sub.o: numbers.h

-  또 이와 같이, 유지보수에 더 용이한 방법으로 작성할 수 있다.

#3rd example
SRCS = add.cpp calc.cpp mult.cpp sub.cpp
OBJS = $(SRCS:.cpp=.o)
PROG = calculator
CC = g++
CFLAGS = -g

$(PROG) : $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

- 심볼릭 네임들을 이용하여 이와 같이 작성할 수 있다. $()의 경우 선언된 심볼릭 네임을 값으로 출력할 때 사용한다.

- 마지막 8행에 조금 특이한 표현이 있는데, $@은 규칙의 왼쪽에 기재된 $(PROG)을 가르키고, $^은 규칙의 오른쪽인 $(OBJS)를 가르킨다.

2.4 빌드 타겟 추가

- Makefile을 여러 의도로 사용할 수 있게, 타겟을 정의하고 실행할 수 있다. 하나의 파일로 다양한 작업을 할 수 있어 매우 유용하다.

#4th example
SRCS = add.cpp calc.cpp mult.cpp sub.cpp
OBJS = $(SRCS:.cpp=.o)
PROG = calculator
CC = g++
CFLAGS = -g
INSTALL_ROOT = /usr/local

$(PROG) : $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

clean:
	rm -f $(OBJS) $(PROG)

install: $(PROG)
	cp $(PROG) $(INSTALL_ROOT)/bin

uninstall: $(INSTALL_ROOT)/bin/$(PROG)
	rm $(INSTALL_ROOT)/bin/$(PROG)

- 참고로, install과 uninstall 실행 시 root 권한이 필요하고 예제 진행 후 calculator 파일을 지우는 걸 추천한다.

2.5 프레임워크 사용

- 모든 빌드 프로세스를 한 파일에서 관리하는 것은 매우 복잡할 것이다. 개발자가 빌드 파일을 볼 때는 필요한 것만 볼 수 있도록 프레임워크로 만들어 가독성과 확장성을 높일 수 있다.

- Makefile은 다음과 같이 작성하고,

#5th example
SRCS = add.cpp calc.cpp mult.cpp sub.cpp
PROG = calculator
HEADERS = numbers.h

include framework.mk

- framework.mk 파일을 다음과 같이 작성하여 사용할 수 있다.

OBJS = $(SRCS:.cpp=.o)
CC = g++
INSTALL_ROOT = /usr/local

ifdef DEBUG
CFLAGS = -O -g
else
CFLAGS = -O
endif

$(PROG) : $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

$(OBJS) : $(HEADERS)

clean:
	rm -f $(OBJS) $(PROG)

install: $(PROG)
	cp $(PROG) $(INSTALL_ROOT)/bin

uninstall: $(INSTALL_ROOT)/bin/$(PROG)
	rm $(INSTALL_ROOT)/bin/$(PROG)

- 코드를 나눈 것이 대부분의 작업이고 한 가지 추가된 게 있다면, gmake 커맨드 실행 시 옵션을 줄 수 있게 추가했다. 실행 결과는 다음과 같다.

$ gmake
g++    -c -o add.o add.cpp
g++    -c -o calc.o calc.cpp
g++    -c -o mult.o mult.cpp
g++    -c -o sub.o sub.cpp
g++ -O -o calculator add.o calc.o mult.o sub.o
$ gmake clean
rm -f add.o calc.o mult.o sub.o calculator
$ gmake DEBUG=1
g++    -c -o add.o add.cpp
g++    -c -o calc.o calc.cpp
g++    -c -o mult.o mult.cpp
g++    -c -o sub.o sub.cpp
g++ -O -g -o calculator add.o calc.o mult.o sub.o

 

출처: "소프트웨어 빌드 시스템 원리와 활용", 피터스미스, 역 김선일 외 3명