Bỏ qua điều hướng, tới nội dung chính
Học C
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êuLàm gì
makeDựng bản debug (mặc định)
make releaseDựng bản tối ưu, gia cố
make asanDựng bản sanitizer
make runDựng rồi chạy chương trình
make testChạy Makefile trong tests/
make cleanXoá thư mục build
make helpIn 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ử

  1. Chép Makefile này vào một dự án và dựng cả ba cấu hình.
  2. Dựng debug rồi release mà quên clean, và giải thích vì sao binary bị trộn.
  3. Tách thư mục build theo cấu hình để không cần clean khi đổi.
  4. Thêm mục tiêu format gọi clang-format.
  5. Viết một Makefile con trong tests/ và gọi nó bằng make test.
  6. Thêm .gitignore bỏ qua thư mục build/.

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 clean trướ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-tra chạy toàn bộ quy trình.