<aside> 💡 [토막글] Make와 Sanitizer 더 잘 쓰기에서 이어지는 내용입니다.

</aside>

기존 Makefile

우선 전형적인 Makefile 예시입니다.

# 예시 이름
NAME := libft.a

# 소스코드 목록 - 제출용이 아닌 경우에만 사용
SRCS ?= $(shell find . -name "*.c" | xargs)

# 기본 변수
CC := clang
CFLAGS := -Wall -Wextra -Werror

# Norm 필수 규칙 (all, clean, fclean, re)
all: $(NAME)
clean:
	find . \\( -name "*.o" -o -name "*.d" \\) -delete
fclean: clean
	rm -f $(NAME)
re:
	$(MAKE) fclean
	$(MAKE) all
.PHONY: all clean fclean re

# **************************************************************************** #
#
#
#    여기서부터 중요!!!
#
#    플래그로 바이너리가 달라지는 경우 파일명에 $(SUFFIX)를 붙입니다.
#
#    make $(NAME)으로 $(NAME)을 만들려고 할 경우, 타겟 파일을 단순히 복사합니다.
#    타겟 파일은 $(NAME)과 이름이 겹칠 수 있어 이름에 .target을 붙였습니다.
#
# **************************************************************************** #

# 여기에 코드 추가

SUFFIX := $(if $(SANITIZER),.$(SANITIZER))

%.a:
	$(AR) rcs $@ $^
%$(SUFFIX).o: %.c
# SANITIZER가 있을 때만 -fsanitizer 플래그 사용
	$(CC) $(CFLAGS) -c $< -o $@ -MMD $(if $(SANITIZER),-fsanitize=$(SANITIZER),)

$(NAME:.a=.target$(SUFFIX).a): $(patsubst ./%.c,%$(SUFFIX).o,$(SRCS))
.PHONY: $(NAME)
$(NAME): $(NAME:.a=.target$(SUFFIX).a)
	cp $< $@

-include $(SRCS:.c=$(SUFFIX).d)

여기서 SANITIZER 변수를 make에 전달해야만 해당 sanitizer를 사용하게 됩니다.

플래그를 저장하자

위 예시 코드의 여기에 코드 추가 부분에 아래 코드를 추가해 주세요

ifeq ($(SANITIZER),)
ifeq ($(SAVE),1)
# SANITIZER가 주어지지 않았는데 저장한다면 (SAVE 변수가 1이라면) 이전 설정 삭제
TMP := $(shell rm -f flag_sanitizer.mk)
# 바로 쉘을 실행할 수 없기 때문에 `아무_변수명 := $(shell 실행할_것)` 트릭을 사용합니다.
else
# SANITIZER가 주어지지 않으면 (+ 저장하지 않는다면) 기본값으로 이전 설정을 사용합니다.
-include flag_sanitizer.mk
endif
else
# SANITIZER가 주어진 경우!
ifeq ($(SAVE),1)
# 설정을 저장한다면 *-include*로 사용할 수 있게 *flag_sanitizer.mk*에 저장합니다.
TMP := $(shell printf "TMP := \\$$(shell printf 'No SANITIZER given. using previous one. (run \\`make reset\\` to reset)\\\\\\\\n' 1>&2)\\\\nSANITIZER := $(SANITIZER)\\n" > flag_sanitizer.mk)
# 이전 설정을 사용한다는 경고 메시지를 출력하고 SANITIZER 변수를 설정하는 파일을 생성
endif
endif

# 이전 설정을 저장한 flag_sanitizer.mk를 삭제하는 reset 스크립트를 추가합니다.
.PHONY: reset
reset:
	rm -f flag_sanitizer.mk
# 그리고 clean 스크립트를 실행하면 reset 스크립트도 실행되도록 dependency에 추가합니다.
clean: reset

SAVE=1 없이도 항상 유지하려면 ifeq ($(SAVE),1)와 그 endif를 제거하면 됩니다.

추가로, 제출 파일에 제한이 없다면 이렇게 추가한 코드를 별도의 파일로 분리하는 게 좋겠죠.

결론

Sanitizer를 잘 쓰자는 것이 아닙니다. 물론 Sanitizer 잘 쓰면 좋습니다.

Sanitizer만이 아니라 뭐든지, 시간을 많이 잡아먹는 게 있다면 자동화로 시간을 아끼세요.

그렇게 조금씩 아낀 시간이 모여 뭔가 할 수 있는 시간이 만들어질 거에요 🙂

<aside> ⚠️ re: fclean all인 경우 make re에서 오동작합니다. re: fclean all을 쓰지 마세요!

</aside>