Bài 37.430 phút đọc
Project: Makefile đa cấu hình
Sau bài này bạn sẽ làm được
- Viết Makefile có debug, release, asan
- Tổ chức src, build, include đúng cách
- Thêm mục tiêu clean, run, test, help
- Dùng CFLAGS theo mục tiêu cụ thể
Bài này gom mọi thứ của chương thành một Makefile dùng được cho mọi dự án nhỏ và vừa: ba cấu hình build, theo dõi header tự động, và các mục tiêu tiện ích. Chép nó, đổi tên chương trình, và bạn có một hệ thống dựng đầy đủ.
#Cấu trúc thư mục
project/
src/ ma nguon .c
include/ header .h cong khai
build/ file sinh ra (.o, .d, binary), them vao .gitignore
tests/ ma kiem thu
Makefile
README.md#Makefile hoàn chỉnh
Makefile
# ===== Cau hinh =====
CC := gcc
CSTD := -std=c17
WARN := -Wall -Wextra -Wpedantic -Wshadow -Wstrict-prototypes
INCLUDES := -Iinclude
CFLAGS := $(CSTD) $(WARN) $(INCLUDES) -MMD -MP
LDFLAGS :=
LDLIBS := -lm
SRC_DIR := src
BUILD_DIR := build
TARGET := $(BUILD_DIR)/app
SRCS := $(wildcard $(SRC_DIR)/*.c)
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:.o=.d)
# ===== Muc tieu mac dinh =====
.PHONY: all
all: debug
# ===== Ba cau hinh build =====
.PHONY: debug release asan
debug: CFLAGS += -g3 -O0 -DDEBUG
debug: $(TARGET)
release: CFLAGS += -O2 -DNDEBUG -flto -D_FORTIFY_SOURCE=2 -fstack-protector-strong
release: LDFLAGS += -flto
release: $(TARGET)
asan: CFLAGS += -g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer
asan: LDFLAGS += -fsanitize=address,undefined
asan: $(TARGET)
# ===== Quy tac build =====
$(TARGET): $(OBJS)
@mkdir -p $(dir $@)
$(CC) $(OBJS) $(LDFLAGS) $(LDLIBS) -o $@
@echo "==> Da build: $@"
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
# ===== Phu thuoc header tu dong =====
-include $(DEPS)
# ===== Tien ich =====
.PHONY: clean run test help
clean:
rm -rf $(BUILD_DIR)
run: $(TARGET)
./$(TARGET)
test: $(TARGET)
@$(MAKE) -C tests
help:
@echo "make debug - build ban debug (mac dinh)"
@echo "make release - build ban toi uu, gia co bao mat"
@echo "make asan - build voi sanitizer"
@echo "make clean - xoa file build"
@echo "make run - chay chuong trinh"
@echo "make test - chay test"#CFLAGS theo mục tiêu
Biến theo mục tiêu
Cú pháp
debug: CFLAGS += ... đặt biến chỉ cho mục tiêu đó và mọi phụ thuộc của nó. Nhờ vậy ba cấu hình dùng chung một quy tắc dịch nhưng với bộ cờ khác nhau.Cơ chế: cùng quy tắc, khác cờ
debug: CFLAGS += -g3 -O0 -DDEBUG
debug: $(TARGET)
release: CFLAGS += -O2 -DNDEBUG -flto ...
release: $(TARGET)
/* Ca "debug" va "release" deu phu thuoc $(TARGET), va $(TARGET)
dung MOT quy tac dich. Nhung dong "debug: CFLAGS += ..." lam
CFLAGS co gia tri KHAC khi ban dung qua muc tieu "debug".
make debug -> CFLAGS = ...co ban... -g3 -O0 -DDEBUG
make release -> CFLAGS = ...co ban... -O2 -DNDEBUG -flto ...
Do la ly do khong can viet ba quy tac dich rieng cho ba cau
hinh. Mot quy tac, ba bo co, chon bang ten muc tieu. */#Chạy thử ba cấu hình
terminal
make debug
gcc -std=c17 -Wall -Wextra -Wpedantic -Wshadow -Wstrict-prototypes -Iinclude -MMD -MP -g3 -O0 -DDEBUG -c src/main.c -o build/main.o gcc -std=c17 ... -g3 -O0 -DDEBUG -c src/toan.c -o build/toan.o gcc build/main.o build/toan.o -lm -o build/app ==> Da build: build/app
make run
./build/app 2 + 3 = 5 2 * 3 = 6
make clean && make release
gcc -std=c17 ... -O2 -DNDEBUG -flto -D_FORTIFY_SOURCE=2 -fstack-protector-strong -c src/main.c -o build/main.o gcc -std=c17 ... -O2 -DNDEBUG ... -c src/toan.c -o build/toan.o gcc build/main.o build/toan.o -flto -lm -o build/app ==> Da build: build/app
#Mục tiêu tiện ích
| Mục tiêu | Làm gì |
|---|---|
| make | Dựng bản debug (mặc định) |
| make release | Dựng bản tối ưu, gia cố |
| make asan | Dựng bản sanitizer |
| make run | Dựng rồi chạy chương trình |
| make test | Chạy Makefile trong tests/ |
| make clean | Xoá thư mục build |
| make help | In danh sách mục tiêu |
terminal
make help
make debug - build ban debug (mac dinh) make release - build ban toi uu, gia co bao mat make asan - build voi sanitizer make clean - xoa file build make run - chay chuong trinh make test - chay test
Tự làm thử
- Chép Makefile này vào một dự án và dựng cả ba cấu hình.
- Dựng
debugrồireleasemà quênclean, và giải thích vì sao binary bị trộn. - Tách thư mục build theo cấu hình để không cần
cleankhi đổi. - Thêm mục tiêu
formatgọiclang-format. - Viết một Makefile con trong
tests/và gọi nó bằngmake test. - Thêm
.gitignorebỏ qua thư mụcbuild/.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- Cấu trúc chuẩn:
src/,include/,build/,tests/, với nguồn và sản phẩm tách bạch. - Ba cấu hình debug, release, asan dùng chung một quy tắc dịch qua biến theo mục tiêu.
- Đổi cấu hình phải
cleantrước, hoặc tách thư mục build theo cấu hình. - Các mục tiêu tiện ích:
run,test,clean,help, và$(MAKE) -Cđể gọi Makefile con. - Các mục tiêu chất lượng của Phần 20 ghép vào thành một
make kiem-trachạy toàn bộ quy trình.