Bỏ qua điều hướng, tới nội dung chính
Học C
Bài 37.328 phút đọc

Theo dõi header tự động

Sau bài này bạn sẽ làm được

  • Giải thích vì sao sửa header không kích hoạt dựng lại
  • Dùng -MMD -MP để sinh và nạp phụ thuộc
  • Đọc một file .d GCC sinh ra
  • Dùng make -j để dựng song song

Makefile ở bài trước có một lỗ hổng nguy hiểm: sửa một tệp header không kích hoạt dịch lại. Hậu quả là bạn chạy chương trình với bố cục struct cũ và mới lẫn lộn, một trong những lỗi khó hiểu nhất. Hai cờ chữ chữa nó.

#Bug khó hiểu nhất

Makefile thiếu theo dõi header
$(BUILD)/%.o: $(SRC_DIR)/%.c
	$(CC) $(CFLAGS) -c $< -o $@

# Quy tac chi noi: build/x.o phu thuoc src/x.c.
# No KHONG noi gi ve cac .h ma x.c include.

# Nen khi ban sua include/toan.h:
#   - src/main.c KHONG doi -> Make khong dich lai main.o
#   - nhung main.o dua tren toan.h CU
terminal
# Makefile khong co -MMD, sua header
touch include/toan.h && make
mingw32-make: 'app_sai.exe' is up to date.

#-MMD -MP

Thêm hai cờ và một dòng
# 1. Them -MMD -MP vao CFLAGS
CFLAGS := -std=c17 -Wall -Wextra -Iinclude -MMD -MP

# 2. Tinh danh sach file .d
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(BUILD)/%.o)
DEPS := $(OBJS:.o=.d)

# 3. Nap cac file .d o cuoi Makefile
-include $(DEPS)
CờLàm gì
-MMDKhi dịch, sinh thêm tệp .d liệt kê các header mà tệp .c include (bỏ header hệ thống)
-MPThêm một mục tiêu giả rỗng cho mỗi header, tránh lỗi khi xoá header
-includeNạp các tệp .d; dấu - để không lỗi khi tệp .d chưa tồn tại

#Đọc tệp .d

terminal
make && cat build/main.d
build/main.o: src/main.c include/toan.h

include/toan.h:

#Chứng minh

terminal
# Voi -MMD -MP: sua header -> dich lai cac file phu thuoc
touch include/toan.h && make
gcc -std=c17 -Wall -Wextra -Iinclude -MMD -MP -c src/main.c -o build/main.o
gcc -std=c17 -Wall -Wextra -Iinclude -MMD -MP -c src/toan.c -o build/toan.o
gcc build/main.o build/toan.o -o build/app.exe
==> Da build: build/app.exe
Không theo dõi header
CFLAGS := -std=c17 -Iinclude $(BUILD)/%.o: $(SRC_DIR)/%.c $(CC) $(CFLAGS) -c $< -o $@ # Sua header -> KHONG dich lai -> bug bo cuc struct. # "Chua" bang make clean moi lan -> dich lai het, cham.
Có -MMD -MP
CFLAGS := -std=c17 -Iinclude -MMD -MP $(BUILD)/%.o: $(SRC_DIR)/%.c $(CC) $(CFLAGS) -c $< -o $@ -include $(DEPS) # Sua header -> dich lai DUNG cac file phu thuoc no. # Khong bao gio can make clean.

#Dựng song song

terminal
# -j: dung nhieu luong, dich cac file doc lap cung luc
make -j8
# dung het so CPU (Linux/macOS)
make -j$(nproc)

Tự làm thử

  1. Thêm -MMD -MP vào Makefile của bạn và mở một tệp .d được sinh ra.
  2. Sửa một header và xác nhận đúng các tệp phụ thuộc được dịch lại.
  3. Bỏ -MP, xoá một header, và quan sát lỗi No rule to make target.
  4. So -MD và -MMD: đếm số dòng trong tệp .d mỗi cách sinh ra.
  5. Dựng một dự án nhiều tệp với -j8 và đo chênh lệch thời gian.
  6. Cố tình bỏ một phụ thuộc và xem make -j có lộ ra lỗi thứ tự không.

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

  • Thiếu theo dõi header là bug khó hiểu nhất của Make: sửa header không kích hoạt dịch lại.
  • -MMD sinh tệp .d liệt kê header của bạn, -MP thêm mục tiêu giả để tránh lỗi khi xoá header.
  • Tệp .d chính là một quy tắc Make; -include nạp chúng vào đồ thị phụ thuộc.
  • Ba dòng -MMD -MP, DEPS, -include nên có trong mọi dự án C.
  • make -j dựng song song và là phép thử tính đúng của Makefile; dựng tuần tự khi cần đọc lỗi.