C++로 규모가 있는 프로젝트를 개발하다 보면 여러 개의 소스 파일과 헤더 파일을 각각 컴파일하고, 이를 하나의 실행 파일로 묶는 과정이 필요합니다. 이번 글에서는 이러한 빌드 과정을 자동화해 주는 Makefile의 개념과 실제 활용 방법을 예제 코드와 함께 살펴보겠습니다.
Makefile이란 무엇인가?
Makefile은 make 빌드 도구가 수행할 작업을 정의하는 설정 파일입니다. 일반적으로 C++ 프로젝트는 클래스나 기능 단위로 .cpp 파일과 .h 파일을 분리해서 작성한 뒤, Makefile이 이 파일들을 컴파일하고 서로 링크하여 하나의 실행 프로그램으로 만들도록 구성합니다.
Makefile을 사용하면 변경된 파일만 다시 컴파일하는 증분 빌드가 가능해지고, 복잡한 컴파일 명령어를 매번 입력할 필요 없이 make 명령 한 번으로 전체 프로젝트를 빌드할 수 있습니다.
예제 프로젝트 구성
다음은 네 개의 소스 파일과 하나의 헤더 파일로 구성된 간단한 C++ 프로젝트입니다.
main.cpp
#include <bits/stdc++.h>
#include "function.h"
using namespace std;
// 메인 실행 프로그램
int main() {
int num1 = 1;
int num2 = 2;
cout << multiply(num1, num2) << endl;
int num3 = 5;
cout << factorial(num3) << endl;
print();
}
print.cpp
#include <bits/stdc++.h>
#include "function.h"
using namespace std;
void print() {
cout << "makefile" << endl;
}
factorial.cpp
#include <bits/stdc++.h>
#include "function.h"
using namespace std;
// 팩토리얼 계산 프로그램
int factorial(int n) {
if (n == 1)
return 1;
return n * factorial(n - 1);
}
multiply.cpp
#include <bits/stdc++.h>
#include "function.h"
using namespace std;
int multiply(int a, int b) {
return a * b;
}
functions.h
#ifndef FUNCTIONS_H #define FUNCTIONS_H void print(); int factorial(int); int multiply(int, int); #endif
헤더 파일 functions.h에는 각 소스 파일에서 구현한 함수들의 선언이 들어 있으며, #ifndef, #define, #endif 전처리 지시문(인클루드 가드)을 통해 중복 포함을 방지합니다.
Makefile 작성하기
위 파일들을 한 번에 빌드하려면 프로젝트 루트 디렉터리에 다음과 같은 Makefile을 작성합니다.
all: main.o print.o factorial.o multiply.o g++ main.o print.o factorial.o multiply.o -o program main.o: main.cpp functions.h g++ -c main.cpp print.o: print.cpp functions.h g++ -c print.cpp factorial.o: factorial.cpp functions.h g++ -c factorial.cpp multiply.o: multiply.cpp functions.h g++ -c multiply.cpp clean: rm -f *.o program
여기서 주의할 점은 명령줄 앞의 공백이 반드시 탭(Tab) 문자여야 한다는 것입니다. 스페이스를 사용하면 make가 오류를 발생시킵니다.
- 타겟(target): 생성할 결과물(예: main.o, program)
- 의존성(dependency): 타겟을 만들기 위해 필요한 파일
- 명령(recipe): 타겟을 생성하기 위해 실행할 셸 명령
이제 터미널에서 make를 실행하면 모든 객체 파일이 컴파일되고 링크되어 program이라는 실행 파일이 생성됩니다. 빌드 산출물을 삭제할 때는 make clean을 사용하면 됩니다.
실행 결과
2 120 makefile
프로그램을 실행하면 곱셈 결과(1 × 2 = 2), 팩토리얼 결과(5! = 120), 그리고 print 함수의 출력 순서로 화면에 나타납니다.
마무리
Makefile은 파일 간 의존 관계를 자동으로 파악하여 수정된 부분만 다시 컴파일해 주기 때문에, 빌드 시간을 크게 단축하고 반복 작업을 줄여줍니다. 소규모 프로젝트에서는 간단한 Makefile로 충분하지만, 프로젝트가 커지면 변수(CXX, CXXFLAGS)와 패턴 규칙을 활용해 더 유연하게 확장할 수 있습니다. C++ 개발자라면 Makefile 작성법을 익혀두면 빌드 환경을 체계적으로 관리하는 데 큰 도움이 됩니다.