Makefile

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 btest).
  • Dependencies: các file nguồn hoặc file phụ thuộc (ví dụ btest.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.
    • -g bậ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.
    • -Wall bật cảnh báo
    • -std=c++17 dù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ạy all.
  • Nếu không có, nó sẽ build target đầu tiên trong file (ví dụ btest).

make btest

  • Build chương trình btest từ btest.cpp.
  • Dùng cả trong kiểu 1 và kiểu 2.

make clean

  • Xóa file thực thi btest.
  • 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
build.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.mk
test: $(TARGET)
  ./$(TARGET) --run-tests
clean.mk
clean:
  rm -f $(TARGET)

Cách sử dụng

  • make hoặc make 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 .mk chỉ 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.

Leave a Comment