Trình Soạn Thảo Không Phải Là Lựa Chọn Duy Nhất Của Lập Trình Viên (Phần 4)
Ở các phần trước, chúng ta đã làm quen với những kiến thức cơ bản về Make và nguyên lý hoạt động của nó. Nếu các bạn cảm thấy hứng thú, việc tham khảo tài liệu hướng dẫn chi tiết đã có thể giúp các bạn thực hiện được nhiều công việc thú vị. Đặc biệt, GNU Make còn cung cấp nhiều tính năng mở rộng mạnh mẽ, khi được vận dụng linh hoạt sẽ tạo ra hiệu quả vượt trội. Để đạt được điều đó, Make cần có khả năng lập trình hóa cao hơn, cho phép xử lý các công việc khác nhau theo cách khác nhau, đồng thời tránh việc lặp đi lặp lại một cách máy móc khi xây dựng các tác vụ giống nhau.
Hôm nay, chúng ta sẽ tìm hiểu về biến số (variables) trong GNU Make. Lưu ý rằng phần này sẽ tập trung vào GNU Make vì có rất nhiều biến thể Make khác nhau, mỗi loại lại có những khác biệt nhỏ về cú pháp dù nguyên lý cơ bản là giống nhau.
Trước tiên, hãy cùng nhìn lại phiên bản Makefile chúng ta có được ở phần trước:
|
|
Quan sát kỹ, chúng ta dễ dàng nhận thấy có nhiều thông tin bị lặp lại. Hãy cùng từng bước tối ưu hóa nó.
1. Tạo quy tắc chung cho việc biên dịch file .obj
Việc chuyển đổi từ file .c
sang .obj
trong ví dụ trên đều tuân theo cùng một phương thức. Trong GNU Make, chúng ta có thể viết một quy tắc mẫu (pattern rule) như sau:
|
|
🔍 Giải thích:
- Dấu
%
đóng vai trò như một ký tự đại diện (wildcard), tương tự như*
trong các lệnh shell.- Quy tắc này có nghĩa: Tất cả các file mục tiêu có đuôi
.obj
sẽ phụ thuộc vào file nguồn cùng tên nhưng có đuôi.c
.$<
là biến tự động chứa tên của file phụ thuộc đầu tiên (trong trường hợp này là file.c
tương ứng).
⚠️ Lưu ý: Đây là tính năng mở rộng của GNU Make, không có trong các phiên bản Make truyền thống. Nếu bạn sử dụng BSD Make, biến
$<
sẽ được viết thành${.IMPSRC}
.
Áp dụng quy tắc này, Makefile của chúng ta được rút gọn như sau:
|
|
2. Tối ưu hóa việc liên kết file .exe
Trong ví dụ hiện tại, chúng ta chỉ tạo một file foobar.exe
. Tuy nhiên, nếu dự án có nhiều file thực thi, việc lặp lại lệnh link
sẽ trở nên bất tiện. Hãy tạo một quy tắc mẫu tương tự cho file .exe
:
|
|
Makefile lúc này trở thành:
|
|
3. Sử dụng biến để quản lý danh sách file
Hai file foo.obj
và bar.obj
xuất hiện ở hai vị trí khác nhau. Đây là lúc chúng ta nên sử dụng biến để quản lý chúng:
|
|
📌 Cú pháp biến trong Make:
=
: Gán giá trị cho biến (tương tự như gán biến trong lập trình).+=
: Thêm giá trị vào biến đã tồn tại.$(BIEN)
: Truy cập giá trị của biến.
4. Xử lý trường hợp có nhiều loại file nguồn
Giả sử dự án của chúng ta không chỉ có file .c
mà còn có file .cpp
hoặc .asm
. Khi đó, quy tắc %.obj : %.c
sẽ không còn phù hợp. Nếu cố gắng định nghĩa hai quy tắc như sau:
|
|
GNU Make sẽ báo lỗi vì một mục tiêu không thể có hai quy tắc xây dựng khác nhau. Để giải quyết, chúng ta sử dụng quy tắc phụ thuộc mở rộng:
|
|
Cú pháp này đảm bảo mỗi file .obj
trong danh sách COBJS
sẽ được xây dựng từ file .c
tương ứng, tránh xung đột với các quy tắc khác.
5. Quản lý các tùy chọn biên dịch
Để linh hoạt bật/tắt các tùy chọn như /Zi
(thông tin gỡ lỗi) hay /O2
(tối ưu hóa), chúng ta có thể định nghĩa biến CFLAGS
:
|
|
6. Tách cấu hình ra file riêng
Để tăng tính tái sử dụng, chúng ta có thể tách các quy tắc chung vào một file độc lập, ví dụ compile.mk
:
|
|
Sau đó, trong Makefile chính, chúng ta sử dụng lệnh include
để nạp file này:
|
|
💡 Tính năng include trong Make:
- Tương tự như
#include
trong C/C++.- Hỗ trợ nạp nhiều file cùng lúc.
- Có thể tự động xây dựng file được include nếu nó chưa tồn tại.
7. Ứng dụng Make cho công việc hàng ngày
Không chỉ giới hạn ở việc biên dịch mã nguồn, Make còn có thể tự động hóa nhiều tác vụ khác. Ví