Search
Duplicate
🐏

Makefile, make 기초

간단소개
Makefile에 대해 알아야 할 모든 것
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
Makefile
C
Scrap
태그
makefile
9 more properties

make

make의존성 관리증분 빌드 기능을 갖춘 빌드 도구입니다.

의존성 관리

빌드 과정에서 의존성에 따른 빌드 순서는 무척 중요합니다.
a.ca.o를 만들고, a.oa.out을 만드는 상황을 가정하겠습니다.
a.out을 빌드하기 위해서는 a.o가 필요하고, a.o를 빌드하기 위해서는 a.c가 필요합니다.
여기서 a.outa.o에, a.oa.c에 의존성이 있다고 합니다.
이 의존 관계를 의존성 그래프로 나타내면 이렇게 됩니다.
이해를 돕기 위한 예시 이미지. (a.c)→(a.o)→(a.out)
최종적으로 만들고 싶은 파일은 a.out입니다.
a.out을 만들기 위해서는 단순히…
a.c를 컴파일해서 a.o를 만들고 나서,
a.o를 링킹해서 a.out을 만들면 됩니다.
간단하죠? 그럼 이번에는 어떨까요?
조금 복잡한 의존성 그래프 예시
의존성 그래프가 조금 복잡해졌습니다.
여기서 test를 수행하려면 어떻게 해야 할까요?
…여러가지 방법이 있겠지만, 순서를 잘 지켜야 할 것입니다.
make는 규칙을 정의하면 그 규칙에서 적절한 작업 순서를 찾아서, 그 순서대로 작업을 수행합니다.
이것을 의존성 관리라고 합니다.

증분 빌드

예시를 들기 위해서 아까의 그림을 다시 보겠습니다.
이해를 돕기 위한 예시 이미지. (a.c)→(a.o)→(a.out)
a.c가 바뀌면 a.out에 그 변경사항을 반영하기 위해서 뭘 해야 할까요?
간단합니다. 똑같이 a.c를 컴파일해서 a.o를 만들고, a.o를 링킹해서 a.out을 만들면 됩니다.
이번에는 복잡한 의존성 그래프를 보겠습니다.
조금 복잡한 의존성 그래프 예시
여기서 ft_a.ctestee_sub2.c가 바뀌면 그 변경사항을 test에 반영하려면 어떻게 해야 할까요?
의존성 그래프에서 변경사항이 있을 때 다시 만들어야 할 것들을 추적하는 과정
의존성 그래프에서 변경사항을 추적해서 변경이 필요한 것들만 다시 만들면 됩니다.
이 과정은 사람이 하기에는 머리가 아픈 일입니다.
make는 규칙을 잘 정의하면 무언가가 변경되었을 때, 변경이 필요한 것들만 다시 만들어줍니다.
이것을 증분 빌드라고 합니다.

make의 버전

make의 규격은 POSIX에서 정의하고 있습니다.
하지만 대부분의 make 구현으로는 GNU Make를 사용합니다.
이 글에서는 GNU Make를 사용하는 것으로 가정하고 GNU Make를 기준으로 설명하겠습니다.
POSIX에서 정의한 make와는 조금 다른 부분이 있습니다.

Makefile

앞선 make 소개에서 규칙을 정의하면 변경이 필요한 것만 만들어 준다고 했었는데요,
그 규칙을 Makefile이라는 파일에 정의하게 됩니다.
Makefile만의 문법이 따로 있어서 그 문법에 맞게 규칙을 정의하게 되는데요,
그 규칙은 의존 관계와 그 파일을 만드는 방법으로 나뉩니다.

규칙

간단한 의존성 그래프 예시를 다시 보겠습니다.
이해를 돕기 위한 예시 이미지. (a.c)→(a.o)→(a.out)
여기서 a.c는 사람이 직접 만들 파일입니다. a.c를 만드는 규칙은 필요하지 않습니다.
이 경우에는 아래의 두 가지 규칙이 필요합니다.
1.
a.c를 컴파일해서 a.o를 만드는 규칙
의존성: a.c
만드는 방법: cc -c a.c
2.
a.o를 링킹해서 a.out을 만드는 규칙
의존성: a.o
만드는 방법: cc a.o -o a.out
이를 Makefile 문법으로 나타내면 이렇게 됩니다.
a.o: a.c cc -c a.c a.out: a.o cc a.o -o a.out
Makefile
복사
아주 간단한 Makefile 예시
우선 (만들 파일1) (만들 파일2) ...: (의존성1) (의존성2) ...처럼 의존 관계를 정의하고,
다음 줄부터 만드는 방법을 한 줄씩 탭으로 들여쓰기 해서 차례대로 쓰면 됩니다.

변수

같은 이름을 여러번 쓰는 것은 좋지 않습니다.
예를 들어 특정 파일의 이름을 여러 번 쓴다면, 파일 이름이 바뀔 때마다 그 파일 이름을 쓴 모든 곳을 수정해야 하고, 그 중 한 곳이라도 수정하는 것을 잊으면 제대로 동작하지 않을 수 있습니다.
그럴 때에는 변수를 쓸 수 있습니다.
변수는 변수명 = 값으로 정의하고, $(변수명)으로 사용할 수 있습니다.
# 변수 정의 CC = cc A_C = a.c A_O = a.o TARGET = a.out # 변수 사용 $(A_O): $(A_C) $(CC) -c $(A_C) $(TARGET): $(A_O) $(CC) $(A_O) -o $(TARGET)
Makefile
복사
변수를 사용한, 아주 간단한 Makefile 예시
이렇게 변수를 정의해두면 파일명이나 컴파일러가 바뀌어도 그 파일명이나 컴파일러가 쓰인 모든 곳을 바꿀 필요 없이 변수를 정의한 곳만 바꾸면 됩니다.

자동 변수, 패턴

위처럼 모든 파일마다 규칙을 만들어야 한다면 Makefile을 쓰더라도 Makefile이 매우 길어질 것입니다.
다행히 (gnu make에는) 규칙에 패턴을 지정할 수 있습니다.
하지만 패턴에 대해 알아보기 전에 자동 변수를 먼저 간단히 알아보겠습니다.
자동 변수는 규칙 안에서 사용할 수 있는, 그때그때 자동으로 만들어지는 변수입니다.
$@는 만들려는 파일 이름, $<는 의존성 중 첫번째, $^는 모든 의존성 등… 이 외에도 여러가지가 있습니다.
a.o: a.c cc -c $< a.out: a.o cc $^ -o $@
Makefile
복사
자동 변수를 사용한, 아주 간단한 Makefile 예시
이런 자동 변수와 패턴을 결합하면 여러 파일에 대한 규칙을, 규칙 하나로 커버할 수 있게 됩니다.
EXECUTABLE_TARGETS = a.out %.o: %.c cc -c $< $(EXECUTABLE_TARGETS): cc $^ -o $@ a.out: a.o
Makefile
복사
자동 변수와 패턴을 사용한, 아주 간단한 Makefile 예시
규칙에 %가 있으면, 이 %에는 무엇이든 들어갈 수 있습니다.
그리고 만들려는 파일의 %에 들어간 부분이 의존성의 %에도 들어가게 됩니다.

내장 변수, 내장 규칙

GNU Make에는 많은 변수와 규칙이 내장되어 있습니다.
make -p 명령어로 내장된 변수와 내장된 규칙을 확인할 수 있는데, 간단히 그 중 몇 개만 훑어보겠습니다.

내장 변수

CC - C 컴파일러. 기본값은 cc
CFLAGS - C 컴파일러 플래그
CXX - C++ 컴파일러. 기본값은 c++
CXXFLAGS - C++ 컴파일러 플래그
LDFLAGS - 링커 플래그
CPPFLAGS - C 전처리기(preprocessor) 플래그 - C와 C++에 모두 사용
이 외에도 정말 많은 내장 변수가 있지만, 자주 쓰이는 것만 정리해봤습니다.

내장 규칙

%.o: %.c $(CC) $(TARGET_ARCH) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< %.o: %.cpp $(CXX) $(TARGET_ARCH) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $< %: %.o $(CC) $(TARGET_ARCH) $(LOADLIBES) $(LDLIBS) $(LDFLAGS) -o $@ $^
Makefile
복사
이런 내장 규칙을 활용해서 Makefile의 길이를 쉽게 줄일 수 있습니다.
심지어 c 파일 이름이 (실행 파일 이름).c인 경우 Make 없이도 make (실행 파일 이름)이 됩니다!

.PHONY 규칙

무언가를 만들려는 건 아니지만 자주 쓰는 명령어를 make에 alias처럼 등록해서 쓰고 싶을 수 있습니다.
clean: rm -f *.o
Makefile
복사
실제로 이런 식으로 등록해서 쓸 수 있습니다.
하지만 이렇게 쓰는 경우에는 clean이라는 파일이 있으면 make clean이 아무것도 실행하지 않을 것입니다.
그런 문제를 해결하기 위해 clean을 가짜를 의미하는 .PHONY 타겟으로 지정할 수 있습니다.
.PHONY: clean clean: rm -f *.o
Makefile
복사
이렇게 clean.PHONY 타겟으로 지정하면 clean이라는 파일이 있어도 make clean이 의도대로 동작합니다.

마무리

make의존성 관리증분 빌드 기능을 갖춘, 무언가를 만드는 데 쓸 수 있는 빌드 도구입니다.
Makefile에 무언가를 만드는 방법을 정의할 수 있습니다.
이것만 알면 Makefile로 무엇이든 할 수 있습니다!
같이 보면 좋은 글