Câu trả lời:
Theo mặc định, các mục tiêu Makefile là "mục tiêu tệp" - chúng được sử dụng để xây dựng các tệp từ các tệp khác. Giả sử mục tiêu của nó là một tệp và điều này làm cho việc viết Makefiles tương đối dễ dàng:
foo: bar
create_one_from_the_other foo bar
Tuy nhiên, đôi khi bạn muốn Makefile của bạn chạy các lệnh không thể hiện các tệp vật lý trong hệ thống tệp. Ví dụ điển hình cho điều này là các mục tiêu chung "sạch" và "tất cả". Có thể đây không phải là trường hợp, nhưng bạn có thể có một tệp có tên clean
trong thư mục chính của bạn. Trong trường hợp như vậy, Make sẽ bị nhầm lẫn bởi vì theo mặc định, clean
mục tiêu sẽ được liên kết với tệp này và Make sẽ chỉ chạy nó khi tệp dường như không được cập nhật liên quan đến các phụ thuộc của nó.
Các mục tiêu đặc biệt này được gọi là giả mạo và bạn có thể nói rõ ràng Làm cho chúng không được liên kết với các tệp, ví dụ:
.PHONY: clean
clean:
rm -rf *.o
Bây giờ make clean
sẽ chạy như mong đợi ngay cả khi bạn có một tệp có tên clean
.
Về mặt Make, một mục tiêu giả mạo chỉ đơn giản là một mục tiêu luôn lỗi thời, vì vậy bất cứ khi nào bạn yêu cầu make <phony_target>
, nó sẽ chạy, độc lập với trạng thái của hệ thống tệp. Một số chung make
mục tiêu mà thường giả mạo là: all
, install
, clean
, distclean
, TAGS
, info
, check
.
Giả sử bạn có install
mục tiêu, một mục tiêu rất phổ biến trong các tệp tạo tệp. Nếu bạn không sử dụng .PHONY
và một tệp có tên install
tồn tại trong cùng thư mục với Makefile, thì make install
sẽ không làm gì cả . Điều này là do Make diễn giải quy tắc có nghĩa là "thực thi công thức tương tự và như vậy để tạo tệp có tên install
". Vì tập tin đã có sẵn và các phần phụ thuộc của nó không thay đổi, sẽ không có gì được thực hiện.
Tuy nhiên, nếu bạn tạo install
mục tiêu PHONY, nó sẽ cho công cụ make biết rằng mục tiêu là hư cấu và điều đó không nên hy vọng nó sẽ tạo ra tệp thực tế. Do đó, nó sẽ không kiểm tra xem install
tệp có tồn tại hay không, nghĩa là: a) hành vi của nó sẽ không bị thay đổi nếu tệp tồn tại và b) thêm stat()
sẽ không được gọi.
Nói chung, tất cả các mục tiêu trong Makefile của bạn không tạo tệp đầu ra có cùng tên với tên đích sẽ là PHONY. Điều này thường bao gồm all
, install
, clean
, distclean
, và vân vân.
.sh
hoặc .bash
phần mở rộng cho "chương trình" chạy giống như chúng có chức năng chính và dự trữ thêm phần mở rộng cho các thư viện bạn bao gồm ( source mylib.sh
). Trên thực tế, tôi đã nhận được câu hỏi SO này bởi vì tôi có một tập lệnh trong cùng thư mục với Makefile của tôi được gọiinstall
.PHONY
tất cả thời gian ...
.PHONY
phiên bản.
LƯU Ý : Công cụ make đọc tệp makefile và kiểm tra dấu thời gian sửa đổi của các tệp ở cả hai bên của biểu tượng ':' trong một quy tắc.
Trong một thư mục 'kiểm tra' các tập tin sau đây có mặt:
prerit@vvdn105:~/test$ ls
hello hello.c makefile
Trong makefile, một quy tắc được định nghĩa như sau:
hello:hello.c
cc hello.c -o hello
Bây giờ giả sử rằng tệp 'hello' là một tệp văn bản chứa một số dữ liệu, được tạo sau tệp 'hello.c'. Vì vậy, dấu thời gian sửa đổi (hoặc sáng tạo) của 'hello' sẽ mới hơn so với 'hello.c'. Vì vậy, khi chúng tôi sẽ gọi 'make hello' từ dòng lệnh, nó sẽ in dưới dạng:
make: `hello' is up to date.
Bây giờ truy cập tệp 'hello.c' và đặt một số khoảng trắng vào đó, điều này không ảnh hưởng đến cú pháp mã hoặc logic sau đó lưu và thoát. Bây giờ dấu thời gian sửa đổi của hello.c mới hơn so với 'xin chào'. Bây giờ nếu bạn gọi 'make hello', nó sẽ thực thi các lệnh như:
cc hello.c -o hello
Và tệp 'hello' (tệp văn bản) sẽ được ghi đè bằng tệp nhị phân mới 'xin chào' (kết quả của lệnh biên dịch ở trên).
Nếu chúng ta sử dụng .PHONY trong makefile như sau:
.PHONY:hello
hello:hello.c
cc hello.c -o hello
và sau đó gọi 'make hello', nó sẽ bỏ qua bất kỳ tệp nào có trong pwd 'test' và thực hiện lệnh mỗi lần.
Bây giờ, giả sử, mục tiêu 'xin chào' không có phụ thuộc được khai báo:
hello:
cc hello.c -o hello
và tập tin 'hello' đã có trong pwd 'test', sau đó 'make hello' sẽ luôn hiển thị dưới dạng:
make: `hello' is up to date.
make
là toàn bộ ý nghĩa, đó là tất cả về các tệp! Cảm ơn bạn cho câu trả lời này.
.PHONY: install
Giải thích tốt nhất là GNU tự làm thủ công: phần 4.6 Mục tiêu giả mạo .
.PHONY
là một trong những Tên mục tiêu tích hợp đặc biệt của Make . Có những mục tiêu khác mà bạn có thể quan tâm, vì vậy nó đáng để lướt qua các tài liệu tham khảo này.
Khi đến lúc phải xem xét một mục tiêu .PHONY, make sẽ chạy công thức của nó một cách vô điều kiện, bất kể tập tin có tên đó tồn tại hay thời gian sửa đổi cuối cùng của nó là gì.
Bạn cũng có thể quan tâm đến các Mục tiêu tiêu chuẩn như all
, và clean
.
Ngoài ra còn có một điều trị khó khăn quan trọng là ".PHONY" - khi mục tiêu vật lý phụ thuộc vào mục tiêu giả mạo phụ thuộc vào mục tiêu vật lý khác:
MỤC TIÊU1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> MỤC TIÊU2
Bạn chỉ đơn giản mong đợi rằng nếu bạn đã cập nhật TARGET2, thì TARGET1 sẽ được coi là cũ so với TARGET1, vì vậy TARGET1 nên được xây dựng lại. Và nó thực sự hoạt động theo cách này .
Phần khó khăn là khi TARGET2 không hoạt động so với TARGET1 - trong trường hợp đó bạn nên mong đợi rằng TARGET1 không nên được xây dựng lại.
Điều này đáng ngạc nhiên không hoạt động bởi vì: dù sao thì mục tiêu giả mạo vẫn được chạy (như các mục tiêu giả mạo thường làm) , điều đó có nghĩa là mục tiêu giả mạo đã được xem xét cập nhật . Và do đó, TARGET1 được coi là cũ chống lại mục tiêu giả mạo .
Xem xét:
all: fileall
fileall: file2 filefwd
echo file2 file1 >fileall
file2: file2.src
echo file2.src >file2
file1: file1.src
echo file1.src >file1
echo file1.src >>file1
.PHONY: filefwd
.PHONY: filefwd2
filefwd: filefwd2
filefwd2: file1
@echo "Produced target file1"
prepare:
echo "Some text 1" >> file1.src
echo "Some text 2" >> file2.src
Bạn có thể chơi xung quanh với điều này:
Bạn có thể thấy rằng fileall phụ thuộc vào file1 một cách gián tiếp thông qua một mục tiêu giả mạo - nhưng nó luôn được xây dựng lại do sự phụ thuộc này. Nếu bạn thay đổi phụ thuộc fileall
từ filefwd
sang file
, bây giờ fileall
sẽ không được xây dựng lại mỗi lần, mà chỉ khi bất kỳ mục tiêu phụ thuộc nào cũ chống lại nó dưới dạng tệp.
Mục tiêu đặc biệt .PHONY:
cho phép khai báo các mục tiêu giả mạo, do đó make
sẽ không kiểm tra chúng như tên tệp thực tế: nó sẽ hoạt động mọi lúc ngay cả khi các tệp đó vẫn tồn tại.
Bạn có thể đặt một vài .PHONY:
trong Makefile
:
.PHONY: all
all : prog1 prog2
...
.PHONY: clean distclean
clean :
...
distclean :
...
Có một cách khác để khai báo các mục tiêu giả mạo: chỉ cần đặt '::'
all :: prog1 prog2
...
clean ::
...
distclean ::
...
'::' có một ý nghĩa đặc biệt: các mục tiêu là giả mạo cộng với chúng có thể xuất hiện nhiều lần:
clean ::
rm file1
...
clean ::
rm file2
Các khối lệnh sẽ được gọi lần lượt.
Tôi thường sử dụng chúng để nói mục tiêu mặc định không bắn.
superclean: clean andsomethingelse
blah: superclean
clean:
@echo clean
%:
@echo catcher $@
.PHONY: superclean
Nếu không có giả mạo, make superclean
sẽ sa thải clean
, andsomethingelse
và catcher superclean
; nhưng với PHONY, make superclean
sẽ không bắn catcher superclean
.
Chúng tôi không phải lo lắng về việc nói rằng clean
mục tiêu là PHONY, bởi vì nó không hoàn toàn giả mạo. Mặc dù nó không bao giờ tạo ra tệp sạch, nhưng nó có các lệnh để kích hoạt, vì vậy hãy nghĩ rằng đó là mục tiêu cuối cùng.
Tuy nhiên, superclean
mục tiêu thực sự là giả mạo, vì vậy make sẽ cố gắng xếp nó lên với bất kỳ thứ gì khác cung cấp deps cho superclean
mục tiêu - điều này bao gồm các superclean
mục tiêu khác và %
mục tiêu.
Lưu ý rằng chúng tôi không nói bất cứ điều gì về andsomethingelse
hoặc blah
, vì vậy họ rõ ràng đi đến người bắt.
Đầu ra trông giống như thế này:
$ make clean
clean
$ make superclean
clean
catcher andsomethingelse
$ make blah
clean
catcher andsomethingelse
catcher blah