Makefile - 5. Makefile 최적화 및 실전 프로젝트 적용 (5-1. 효율적인 Makefile 작성법 (Writing Efficient Makefiles))
2025. 3. 11. 19:13ㆍ개발/개발도구와 환경
📌 5-1. 효율적인 Makefile 작성법 (Writing Efficient Makefiles)
Makefile을 효율적으로 작성하면 코드 중복을 줄이고 유지보수를 쉽게 할 수 있습니다.
이를 위해 공통 규칙 정리 (%.o: %.c 활용) 및 중복 제거 (define 사용) 기법을 적용합니다.
1. 공통 규칙 정리 (%.o: %.c 활용)
📌 1-1. %.o: %.c를 활용한 패턴 규칙 (Pattern Rules)
Makefile에서 .c 파일을 컴파일하여 .o 파일로 변환하는 과정은 반복적인 작업입니다.
이를 효율적으로 처리하기 위해 패턴 규칙(%.o: %.c)을 사용합니다.
📌 1-2. 기본적인 %.o: %.c 규칙
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
자동 변수 | 설명 |
$< | 첫 번째 의존 파일 (.c 파일) |
$@ | 현재 목표 파일 (.o 파일) |
📌 1-3. %.o: %.c 활용한 Makefile 최적화
🔹 예제 1: 기본적인 %.o: %.c 활용
CC = gcc
CFLAGS = -Wall -g
SRC = main.c utils.c io.c
OBJS = $(SRC:.c=.o)
TARGET = myprogram
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(TARGET)
✅ 이점
- 모든 .c 파일을 자동으로 .o 파일로 변환.
- 새로운 .c 파일이 추가되면 Makefile을 수정할 필요 없음.
- 반복적인 규칙을 제거하여 가독성을 높임.
📌 1-4. 다중 디렉토리에서 %.o: %.c 활용
🔹 예제 2: 서브 디렉토리에서 %.o: %.c 적용
CC = gcc
CFLAGS = -Wall -g
SRCDIR = src
OBJDIR = build
SRC = $(wildcard $(SRCDIR)/*.c)
OBJS = $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(SRC))
TARGET = myprogram
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJDIR)/*.o $(TARGET)
✅ 이점
- src/ 디렉토리의 .c 파일을 자동으로 찾아 build/ 디렉토리에 .o 파일로 변환.
- 디렉토리 구조를 유지하면서 빌드 관리가 편리해짐.
2. 중복 제거 (define 사용)
📌 2-1. define을 활용한 코드 중복 제거
Makefile에서 define을 사용하면 여러 줄의 명령어를 변수처럼 저장하여 재사용할 수 있음.
define compile_template
$(CC) $(CFLAGS) -c $< -o $@
endef
➡ 위 define 블록을 활용하면 여러 줄의 코드를 한 줄처럼 사용할 수 있음.
📌 2-2. define을 활용한 최적화된 Makefile
🔹 예제 3: define을 사용한 코드 중복 제거
CC = gcc
CFLAGS = -Wall -g
SRC = main.c utils.c io.c
OBJS = $(SRC:.c=.o)
TARGET = myprogram
define compile_template
$(CC) $(CFLAGS) -c $< -o $@
endef
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
%.o: %.c
$(compile_template)
clean:
rm -f $(OBJS) $(TARGET)
✅ 이점
- define compile_template를 사용하여 컴파일 명령을 재사용 가능.
- .c → .o 변환 과정에서 불필요한 코드 반복 제거.
- 가독성이 향상되며 유지보수 용이.
📌 2-3. define을 활용한 복잡한 작업 자동화
🔹 예제 4: define을 사용하여 디버그 빌드 지원
CC = gcc
CFLAGS = -Wall -g
SRC = main.c utils.c io.c
OBJS = $(SRC:.c=.o)
TARGET = myprogram
define debug_template
$(CC) $(CFLAGS) -DDEBUG -c $< -o $@
endef
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
debug: CFLAGS += -DDEBUG
debug: $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $^
%.o: %.c
$(debug_template)
clean:
rm -f $(OBJS) $(TARGET)
✅ 이점
- make debug를 실행하면 디버그 빌드(-DDEBUG 플래그 포함) 수행.
- define을 사용하여 디버그 빌드와 일반 빌드를 쉽게 전환 가능.
3. define과 $(eval)을 활용한 동적 Makefile 생성
📌 3-1. $(eval)을 활용한 동적 빌드 시스템
Makefile에서 $(eval)을 사용하면 실행 중에 새로운 규칙을 생성 가능.
🔹 예제 5: $(eval)을 사용하여 자동 규칙 생성
CC = gcc
CFLAGS = -Wall -g
SRC = main.c utils.c io.c
OBJS = $(SRC:.c=.o)
TARGET = myprogram
define compile_rule
$(1): $(1:.o=.c)
$(CC) $(CFLAGS) -c $$< -o $$@
endef
$(foreach obj,$(OBJS),$(eval $(call compile_rule,$(obj))))
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
clean:
rm -f $(OBJS) $(TARGET)
✅ 이점
- $(foreach obj, $(OBJS), $(eval $(call compile_rule, $(obj))))을 사용하여 각 .o 파일에 대한 규칙을 자동 생성.
- 새로운 .c 파일을 추가해도 Makefile을 수정할 필요 없음.
📌 5-1. 효율적인 Makefile 작성법 요약 정리
기법 | 설명 |
%.o: %.c 패턴 규칙 | .c 파일을 .o 파일로 변환하는 공통 규칙 |
디렉토리 관리 (patsubst) | src/ 디렉토리에서 .c를 .o로 자동 변환 |
define 활용 | 중복된 명령어를 변수처럼 저장하여 재사용 |
$(eval) 활용 | 실행 중에 새로운 규칙을 동적으로 생성 |