728x90

요즘은 IDE가 잘되어있어서 빌드 시스템의 필요성을 모르는 사람이 꽤 많다.

심지어 C를 배웠던 4학년 학부생에게 비주얼스튜디오 없이 리눅스 환경에서 해봐라고해도 못하는 경우가 허다하다.

사실 평생 살면서 몰라도 될 수도 있다. 하지만 모르는걸 추천하지 않는다.

왜냐하면 이렇게 평생 사용한다면 C나 C++에 대해서 반쪽밖에 모르고 사용하는 것과 같다.

빌드 시스템을 안쓰고 IDE에 의존하다보니 C가 어떠한 형식으로 빌드가 되는지,

또한 반복수행을 어떻게 해야하는지를 르고 사용하게 된다.

따라서 우리는 수 많은 C혹은 C++파일들을 어떻게 빌드하는지에 대한 빌드시스템에 대해서 알아보도록 할것이다.


C는 역사적으로도 오래된 언어이므로 빌드 시스템이 꽤 많이 있다.

그러나 인지도가 있는것을 골라라고 한다면 단연 make와 cmake이다.

사실 make는 사용하기 조금 귀찮기 때문에 최근에는 cmake로 통일되는 추세이다.

그래도 make를 알아두는것은 중요하다고 생각한다.

왜냐하면 cmake역시 근간은 make이기 때문이다. 몰라도 사용하는데 큰 지장은 없지만

알아두면 이해하는데 편하다는건 사실이다.

이제 make에 대해서 알아보도록 하자.


아래는 make빌드 시스템을 사용한 예제이다.


파일은 총 4개이다. 내용은 아래와 같다.

//main.c
#include "calc.h"
#include <stdio.h>

int main() {
int a = 5;
int b = 3;
printf("%d + %d = %d", a, b, a + b);
return 0;
}
//calc.c
#include "calc.h"

int sum(int a, int b) { return a + b; }
//calc.h
int calc(int a, int b);

#Makefile
CC = gcc
OBJS = calc.o main.o
TARGET = Main

all : $(TARGET)

$(TARGET) : $(OBJS)
$(CC) -o $@ $(OBJS)

clean :
rm -rf $(OBJS) $(TARGET)

make빌드내의 파일은 총4가지가 있다. 2개의 소스파일과 1개의 헤더파일, 그리고 Makefile이존재한다.

make build에서 특별한 일이 없다면 진입점인 Makefile의 이름은 Make여야한다.

바꿀수는 있지만 바꾸는걸 추천하지는 않는다. 오랜 전통이며 사실 바꿀의미도 없다.

이제 여러분들에게 각각부분을 나눠서 의미를 이야기해주겠다.


주석

#Makefile

Makefile의 주석은 리눅스 계열답게 #을 사용한다.

보통 윈도우 계열은 ;를 사용하는 것과는 대조적이다.



변수(Variable)

CC = gcc
OBJS = calc.o main.o
TARGET = Main

우리가 생각하는 그 변수 맞다. 관습적으로 위의 3개는 무조건 쓴다. 또한 관습적으로 대문자로 쓴다.

물론 다른 변수를 만드는것도 가능하다. 위의 변수의 의미는 아래와 같다.


CC - C컴파일러의 준말로 C++도 그냥 CC라고 부른다. 우리가 쓸 컴파일러는 gcc이다.

OBJS - 우리가 빌드하는데 쓸 목적파일들을 정의한다.

TARGET - 우리가 최종적으로 사용할 빌드파일을 의미한다. 타겟의 의미는 후술.


변수를 호출할때는 달러사인($)를 붙힌후 괄호로 묶는다.


규칙(Rule)

all : $(TARGET)

$(TARGET) : $(OBJS)
$(CC) -o $@ $(OBJS)

clean :
rm -rf $(OBJS) $(TARGET)

빌드가 시작되는 전체 내용을 룰이라고 한다. 룰은 타겟의 집합이며 위의 경우 3개의 타겟으로 나눠져 있다.

타겟은 다시 타겟,선행조건,레시피 3가지로 나뉘어진다.

가령 위의 경우 all,$(TARGET),clean은 타겟이며 타겟명뒤의 : 뒤의 것이 바로 선행조건이다.

위의 경우 all타겟의 경우 $(TARGET)이 존재해야한다는 것을 알 수 있다.

해당 변수의 이름은 Main이므로 Main이라는 파일이 존재해야 all타겟을 실행할 수 있다는걸 알 수 있다.

물론 clean처럼 선행조건이 없는 경우도 존재한다. 이는 의존하는게 없다는 것을 의미한다.

안의 내용은 레시피라고 부르며 우리가 실제로 할 작업을 의미한다.


위의 예시를 흐름을 타고가서 설명해보자.

우리는 이 룰에서 all이라는 타겟은 특수한 타겟이다.

별 설명이 없다면 Make타겟을 실행해야한다.

또한 Make타겟은 $(OBJS)가 필요하다.

여기서 우리는 $(OBJS)타겟을 실행해야한다.

그런데 없다... 사실 없어도 상관없다. 이는 make예외인데 타겟이름이 목적파일로만 되어있다면

자동으로 그 타겟을 생성한다. 지금 $(OBJS)타겟의 이름은 calc.o main.o이며

이 타겟이 자동으로 만들어져서 실행되게 된다.

$(TARGET)의 레시피는 gcc -o Main calc.o main.o로 치환된다.

한가지 처음 보는게 있다면 @이다. @의 의미는 해당 타겟의 이름을 의미한다.

여기서 $(TARGET)을 가르키며 $(TARGET)은 Main이므로 자동적으로 이는 Main을 의미한다.

실제로 빌드는 여기까지 진행된다.


참고로 소스파일의 경우에는 해당 소스파일의 이름을 명시해줘야하지만 헤더는 그럴필요가 없이 디렉터리만 맞으면된다.

이는 당연한 이야기이겠지만.


clean타겟은 호출해주는 데가 없으므로 호출되지 않으며 명시적으로 호출해줘야한다.

외부에 make clean이라고 명시적으로 호출해줘야하는 것이다.

clean의 역활은 우리가 만든 파일을 다시 몽땅 지우는 것이다.

이제 테스트 해보자.



make를 호출하면 해당 타겟들이 차례로 실행되는걸 확인할 수 있다.



해당 파일들이 모두 만들어 진다.



빌드된 앱이 정상작동 된다.



clean은 명시적으로 호출해줘야한다. 잘 작동한다.




+ Recent posts