Makefile là một file văn bản dùng cho công cụ make trong Linux/Unix. Nó giúp tự động hóa việc biên dịch, liên kết và quản lý dự án C/C++. Thay vì gõ lệnh g++ btest.cpp -o btest thủ công mỗi lần, bạn chỉ cần chạy make và Makefile sẽ lo phần còn lại.
Ưu điểm:
- Tự động hóa build: không cần gõ lệnh dài.
- Quản lý nhiều file: dễ dàng build dự án lớn với nhiều
.cpp - Tái sử dụng: chỉ cần chỉnh vài dòng khi thêm file mới.
📄 Cấu trúc cơ bản của Makefile
target: dependencies
command
- Target: tên file cần tạo (ví dụ file thực thi b
test).
- Dependencies: các file nguồn hoặc file phụ thuộc (ví dụ b
test.cpp). Nếu dependencies thay đổi, target sẽ được build lại.
- Commands: các lệnh shell để tạo target từ dependencies. Lưu ý: phải bắt đầu bằng tab chứ không phải dấu cách.
🔧 Các biến mặc định
Trong Makefile, các biến như CXX, CXXFLAGS, và LDFLAGS là cách chuẩn để định nghĩa công cụ biên dịch và các tùy chọn đi kèm. Chúng giúp Makefile dễ đọc, dễ bảo trì và tái sử dụng.
CXX: Chỉ định trình biên dịch C++ vd: g++CXXFLAGS: Các cờ (flags) cho trình biên dịch C++. Dùng để bật cảnh báo, chọn chuẩn ngôn ngữ, tối ưu hóa.-gbật chế độ debug. Nó sẽ nhúng thông tin gỡ lỗi (debug symbols) vào file thực thi, để bạn có thể dùng công cụ như gdb hoặc lldb để debug chương trình.-Wallbật cảnh báo-std=c++17dùng chuẩn C++17
LDFLAGS: Các cờ cho linker (trình liên kết). Dùng để thêm thư viện ngoài khi build.
📝 Các kiểu viết Makefile
Có 2 kiểu viết Makefile
# ===================================================
# Kiểu 1: Makefile kiểu viết trực tiếp
# - Ưu điểm: đơn giản, dễ hiểu, phù hợp cho dự án nhỏ với 1–2 file.
# - Nhược điểm: khó mở rộng, nếu đổi tên file hoặc thêm nhiều file thì phải sửa nhiều chỗ.
# Các lệnh có thể gọi:
# make, make test, make clean
# ===================================================
# Biến định nghĩa
CXX = g++
CXXFLAGS = -Wall -std=c++17 -g
LDFLAGS = -lboost_system
# Target chính
btest: btest.cpp
$(CXX) $(CXXFLAGS) -o btest btest.cpp $(LDFLAGS)
# Target phụ để dọn dẹp
clean:
rm -f btest# ===================================================
# Kiểu 2: Dùng biến TARGET và SRC
# - Ưu điểm: dễ mở rộng, chỉ cần đổi giá trị biến TARGET hoặc thêm file vào SRC.
# - Nhược điểm: hơi dài hơn, nhưng vẫn rất phổ biến trong dự án thực tế.
# Các lệnh có thể gọi:
# make, make all, make test, make clean
# ===================================================
# Biến định nghĩa
CXX = g++
CXXFLAGS = -Wall -std=c++17 -g
LDFLAGS = -lboost_system
TARGET = btest
SRC = btest.cpp
# Dùng biến all gọi $TARGET
all: $(TARGET)
$(TARGET): $(SRC)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SRC) $(LDFLAGS)
clean:
rm -f $(TARGET)Thay vì dùng command dài “g++ -Wall -std=c++17 -g btest.cpp -o btest”, chúng ta có thểm dùng command ngắn hơn
make
- Nếu có target
all, nó sẽ chạyall. - Nếu không có, nó sẽ build target đầu tiên trong file (ví dụ b
test).
make btest
- Build chương trình b
testtừ btest.cpp. - Dùng cả trong kiểu 1 và kiểu 2.
make clean
- Xóa file thực thi b
test. - Giúp bạn build lại từ đầu.
🔤 Đổi tên Makefile
Khi bạn gõ lệnh make mà không chỉ định file, công cụ make sẽ tự động tìm file tên Makefile hoặc makefile trong thư mục hiện tại.
Nếu bạn đổi tên thành btest.mk, thì make sẽ không tự động nhận ra.
Nếu file tên là btest.mk, bạn phải chỉ định rõ bằng option -f vd: “make -f btest.mk”
Ví dụ cấu trúc dự án với nhiều file .mk
project/
├── Makefile
├── build.mk
├── test.mk
├── clean.mk
└── src/
├── main.cpp
├── utils.cpp
└── math.cpp
Makefile
include build.mk
include test.mk
include clean.mk
CXX = g++
CXXFLAGS = -Wall -std=c++17 -g
LDFLAGS = -lpthread
TARGET = app
SRC = src/main.cpp src/utils.cpp src/math.cpp
all: $(TARGET)
$(TARGET): $(SRC)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SRC) $(LDFLAGS)test: $(TARGET)
./$(TARGET) --run-testsclean:
rm -f $(TARGET)Cách sử dụng
makehoặcmake all→ build chương trình.make test→ chạy test.make clean→ dọn dẹp file build.
👉 Ưu điểm của cách chia nhỏ này:
- Dễ bảo trì: mỗi file
.mkchỉ lo một nhiệm vụ. - Dễ mở rộng: thêm
deploy.mk,doc.mk… mà không phải sửa Makefile chính nhiều. - Linh hoạt: có thể tái sử dụng file
.mkở nhiều dự án khác nhau.