Makefile: Chúng là gì và bạn có thể làm gì với chúng?

Tác giả Starlink, T.M.Hai 07, 2024, 01:42:54 CHIỀU

« Chủ đề trước - Chủ đề tiếp »

0 Thành viên và 1 Khách đang xem chủ đề.

Đừng giả vờ, hãy thực hiện!

  • Makefile được sử dụng bởi Make, chương trình tự động hóa các quy trình xây dựng thông qua makefile để biên dịch mã hiệu quả.
  • Makefile bao gồm các quy tắc có mục tiêu, sự phụ thuộc và hành động.
  • Makefile yêu cầu thụt lề bằng tab chứ không phải khoảng trắng, vì vậy hãy chú ý đến khoảng trắng khi làm việc với makefile.


Nếu bạn đã cài đặt bất kỳ phần mềm nào từ mã nguồn, có lẽ bạn đã sử dụng Make. Nhưng chương trình này là gì và nó hoạt động như thế nào? Tìm hiểu tất cả về makefile và cách chúng có thể cách mạng hóa quy trình xây dựng của bạn.

1. Biên dịch là gì?

Makefile và biên dịch có mối liên hệ chặt chẽ với nhau. Mặc dù chúng không chỉ dành cho biên dịch, chương trình Make bắt nguồn từ những sự bực bội mà biên dịch có thể gây ra.

Biên dịch lấy các tệp nguồn và chuyển đổi chúng thành một dạng khác. Ví dụ điển hình là tệp.c được biên dịch thành tệp đối tượng.o. Các chương trình C cũng trải qua quá trình liên kết chuyển đổi các tệp.o thành tệp thực thi cuối cùng. Trên thực tế, có bốn giai đoạn riêng biệt liên quan đến việc xây dựng một dự án C:


Biên dịch đã trở nên phổ biến vào đầu những năm 1970 với sự phát triển của ngôn ngữ C, nhưng các ngôn ngữ mới hơn như Typescript, Rust và Sass cũng sử dụng nó. Giải pháp thay thế là thông dịch, sử dụng một chương trình trung gian để chạy mã thay vì tạo ra một tệp thực thi độc lập. Các ngôn ngữ được thông dịch bao gồm JavaScript, Python và Ruby.

Các chương trình biên dịch thường chạy nhanh hơn—đôi khi nhanh hơn nhiều—so với các chương trình được thông dịch. Biên dịch cũng kiểm tra mã của bạn để tìm một số loại lỗi nhất định mà thông dịch chỉ có thể kiểm tra khi chạy, do đó, dễ dàng đảm bảo rằng các chương trình được biên dịch là chính xác. Nhưng biên dịch có thể là một quá trình chậm, đặc biệt là đối với các dự án có hàng trăm hoặc hàng nghìn tệp nguồn.

2. Makefile có chức năng gì?

Makefile được sử dụng bởi Make, một chương trình giúp bạn tự động hóa quy trình xây dựng, bao gồm các tác vụ như biên dịch.

Khi bạn chạy lệnh make, nó sẽ tìm kiếm một tệp có tên là Makefile hoặc makefile theo mặc định. Tệp này sẽ chứa hướng dẫn về cách biên dịch—hoặc xây dựng—mỗi tệp trong chương trình của bạn.

Makefile được xây dựng xung quanh khái niệm "phụ thuộc" (hoặc "điều kiện tiên quyết") mà Make kiểm tra bằng cách sử dụng dấu thời gian tệp. Điều này có nghĩa là Make có thể tăng tốc độ biên dịch đáng kể: thay vì biên dịch lại từng tệp nguồn, Make chỉ xây dựng lại phần tối thiểu. Nói chung, nếu tệp nguồn đã thay đổi, Make sẽ biên dịch lại tệp đó, nếu không, Make sẽ để nguyên tệp đó.

3. Makefile trông như thế nào?

Điều đầu tiên bạn phải biết về makefile là khoảng trắng rất quan trọng. Điều này được coi là một quyết định thiết kế tồi, nhưng chúng ta phải chấp nhận, vì vậy hãy cẩn thận!

Một makefile chứa một loạt các quy tắc mô tả cách xây dựng từng thành phần. Mẫu chung của một quy tắc trông như thế này:

Mã nguồn [Chọn]
target: dependencies
    actions

Sau đây là tệp makefile đơn giản nhất, biên dịch một tệp nguồn duy nhất thành một tệp thực thi:

Mã nguồn [Chọn]
program: program.c
    gcc -o program program.c

Dòng thứ hai của makefile này được thụt lề, và thụt lề phải là một ký tự tab đơn. Cho dù bạn đang sao chép-dán hay nhập thủ công một makefile, hãy rất cẩn thận để giữ một ký tự tab đơn ở đầu các dòng thụt lề. Ví dụ, nếu bạn sử dụng bốn khoảng trắng thay vì một tab, bạn sẽ thấy lỗi như "Makefile:2: *** missing separator. Stop."

Trong ví dụ này, mục tiêu là tên của chương trình thực thi cuối cùng: program. Mục tiêu không nhất thiết phải khớp với tên tệp, nhưng thường thì phải khớp.

Chỉ có một phụ thuộc của mục tiêu này—program.c—mà Make sẽ sử dụng để quyết định phải làm gì. Nếu tệp "program" mới hơn "program.c", Make sẽ không làm gì cả.

4. Viết Makefile đầu tiên của bạn

Không có gì bằng trải nghiệm thực tế và may mắn thay, Make khá dễ sử dụng ở dạng đơn giản nhất.

4.1. Cài đặt Make

Trên bản phân phối Linux sử dụng apt, như Ubuntu, Mint hoặc Debian, bạn có thể cài đặt Make như sau:

Mã nguồn [Chọn]
sudo apt install make
Trên macOS, bạn có thể cài đặt Make thông qua Xcode bằng cách chạy lệnh này:

Mã nguồn [Chọn]
xcode-select --install
Ngoài ra, nếu bạn đã cài đặt homebrew, hãy chạy:

Mã nguồn [Chọn]
brew install make
Nếu bạn thấy lỗi như "make: command not found" khi bạn cố chạy nó, chương trình make không được cài đặt trong PATH của bạn. Bạn có thể khắc phục sự cố này bằng cách kiểm tra xem nó đã được cài đặt đúng chưa.

4.2. Viết một chương trình kiểm tra đơn giản

Bạn có thể tự mình xem Make hoạt động bằng cách kiểm tra makefile ở trên bằng một chương trình c đơn giản. Bạn chỉ cần cài đặt gcc và make để thử nghiệm.

Tạo một tệp có tên "program.c" và điền nội dung sau vào đó:

Mã nguồn [Chọn]
#include <stdio.h>

int main()
{
    printf("Hello, world.\n");
}

Tại thời điểm này, hãy kiểm tra xem chương trình của bạn có chính xác không (và bạn đã cài đặt gcc) bằng cách chạy lệnh sau:

Mã nguồn [Chọn]
gcc -o program program.c
Xác nhận gcc xây dựng chương trình thành công, sau đó xóa chương trình:

Mã nguồn [Chọn]
rm program
4.3. Viết Makefile cơ bản

Bây giờ hãy tạo tệp makefile mẫu (có thể là "Makefile" hoặc "makefile") với nội dung sau:

Mã nguồn [Chọn]
program: program.c
    gcc -o program program.c

4.4. Chạy Make

Trong terminal của bạn, hãy đảm bảo thư mục làm việc của bạn chứa các tệp Makefile và program.c, sau đó chạy Make:

Mã nguồn [Chọn]
make

Sau khi xác nhận Make đã biên dịch và xây dựng chương trình của bạn, hãy kiểm tra hành vi của nó bằng cách chạy lại:

Mã nguồn [Chọn]
make

Make cho bạn biết mọi thứ đều được cập nhật, vì vậy nó không thực sự làm bất cứ điều gì ngay bây giờ. Đây là một trong những lợi ích lớn nhất của Make: nó sẽ chỉ dành thời gian để xây dựng các thành phần cần được xây dựng lại. Hãy thử cập nhật tệp nguồn của bạn và bạn sẽ thấy sự khác biệt:

Mã nguồn [Chọn]
touch program.c
make


Lệnh touch ở đây chỉ cập nhật thời gian đã sửa đổi của tệp thành thời gian hiện tại. Đây chỉ là một cách nhanh chóng để "đánh lừa" Make nghĩ rằng tệp đã thay đổi.

5. Còn có thể làm gì nữa?

Make chỉ thực sự bị giới hạn bởi bản chất dựa trên tệp của nó. Bất kỳ tệp nào cũng có thể là một phần của quy trình xây dựng của bạn và hoạt động như một mục tiêu hoặc sự phụ thuộc.

Điều này có nghĩa là Make phù hợp với nhiều dự án khác nhau. Bất kỳ thứ gì có các bước xây dựng có thể dự đoán và lặp lại đều phù hợp.

Ví dụ, hãy lấy một trình tạo trang web tĩnh tạo ra các trang web từ các nguồn khác, thường bao gồm các tệp văn bản Markdown. Makefile này sẽ xây dựng lại một trang web cụ thể:

Mã nguồn [Chọn]
index.html: index.md
    pandoc index.md index.html

Chúng ta chỉ mới đề cập đến bề nổi những gì Make có thể làm ở đây. Trên thực tế, makefile chứa các mẫu để xác định các quy tắc cho nhiều tệp khác nhau, tạo ra các chuỗi phụ thuộc phức tạp.

Lần tới khi bạn xây dựng phần mềm, hãy xem có tệp makefile nào không và thử khám phá nội dung của nó. Hy vọng là bạn có thể hiểu một số điều cơ bản và nhận ra một số điều đang diễn ra bên trong. Với hướng dẫn GNU make trong tay, bạn có thể bắt đầu sử dụng Make cho các dự án của riêng mình để tránh thời gian chết không cần thiết.