Mục đích của .PHONY trong makefile là gì?


1739

Điều đó .PHONYcó nghĩa gì trong Makefile? Tôi đã trải qua điều này , nhưng nó quá phức tạp.

Ai đó có thể giải thích cho tôi bằng những thuật ngữ đơn giản?

Câu trả lời:


1947

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 cleantrong 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, cleanmụ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 cleansẽ 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 makemục tiêu mà thường giả mạo là: all, install, clean, distclean, TAGS, info, check.


49
@eSKay: 'tại sao lại gọi là' giả mạo '?' - bởi vì đó không phải là một mục tiêu thực sự. Đó là, tên đích không phải là một tệp được tạo bởi các lệnh của mục tiêu đó.
Bernard

96
@Lazer: Tôi không biết nếu bạn là người nói tiếng Anh bản ngữ. Tôi không. từ phony không có nghĩa là nó nghe như thế nào. vi.wiktionary.org/wiki/phony nói: Gian lận; giả mạo; có vẻ ngoài sai lệch.
Bahbar

57
Câu trả lời này không hoàn toàn chính xác - mặc dù nó có thể được giải quyết trong hướng dẫn được liên kết. .PHONY buộc một nhãn / tệp trong Makefile được xây dựng nếu đó là một phần của cấu trúc liên kết - bất kể mục tiêu của bạn là gì. Điều đó có nghĩa là, nếu bạn có nhãn 'dọn dẹp:' được đặt giả mạo và nhãn cài đặt của bạn được xác định với dọn dẹp là điều kiện tiên quyết - tức là 'cài đặt: dọn dẹp', dọn dẹp sẽ luôn được chạy khi Makefile cố gắng xây dựng 'Tải về'. Điều này hữu ích cho các bước bạn luôn muốn thực hiện bất kể họ có thành công hay không - nó sẽ bỏ qua dấu thời gian và chỉ buộc nó.
tổng

9
Lưu ý rằng bạn không cần sử dụng .PHONY miễn là bạn không có tệp có cùng tên với tác vụ. Tác vụ sẽ luôn được thực thi bằng mọi cách và Makefile sẽ dễ đọc hơn.
Bernard

15
"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" - lời giải thích tuyệt vời!
Danijel

730

Giả sử bạn có installmụ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 .PHONYvà một tệp có tên installtồn tại trong cùng thư mục với Makefile, thì make installsẽ 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 installmụ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 installtệ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.


6
@PinnutUndertheSea Câu trả lời được chấp nhận đã được cải thiện đáng kể so với mức độ vô giá trị ban đầu của nó, và bây giờ cũng tốt như câu trả lời này. Tôi đã phải xem qua lịch sử sửa đổi của nó để hiểu bình luận của bạn.
Đánh dấu Amery

2
Điều này có vẻ vô nghĩa vì tôi sẽ không bao giờ có các tệp có tên 'install' hoặc tương tự trong cơ sở mã của tôi. Hầu hết các tệp sẽ có phần mở rộng tệp và các tệp không có phần mở rộng tệp thường có trong tất cả các giới hạn, như 'README'. Sau đó, một lần nữa, nếu bạn có một tập lệnh bash có tên 'install' thay vì 'install.sh', bạn sẽ có một khoảng thời gian tồi tệ.
hạt nhân

6
@JasonTu Điều này không hẳn đúng. Các quy ước về kịch bản Bash yêu cầu bạn bỏ qua .shhoặc .bashphầ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
Kyle

10
@Kyle Vâng, tôi không chắc quá khứ của tôi có ý nghĩa gì. Những ngày này tôi sử dụng .PHONYtất cả thời gian ...
nucletide

2
@JasonTu Giải pháp ở đây rất đơn giản: xây dựng cỗ máy thời gian và "thay thế" con người trong quá khứ của bạn. Tôi khuyên bạn nên mang theo một cái xẻng cùng với bạn để không ai nhận ra bạn là .PHONYphiên bản.
Mateen Ulhaq

120

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.

Thí dụ

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.

3
Điều này không chỉ làm cho các lệnh mà tôi chạy có ý nghĩa, điều này cuối cùng cũng có nghĩa makelà 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.
Kzqai

80
.PHONY: install
  • có nghĩa là từ "cài đặt" không đại diện cho tên tệp trong Makefile này;
  • có nghĩa là Makefile không liên quan gì đến một tệp có tên "install" trong cùng thư mục.

45

Nó là một mục tiêu xây dựng không phải là một tên tệp.


33

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 .

.PHONYlà 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.


11

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:

  • đầu tiên hãy 'chuẩn bị' để chuẩn bị "tệp nguồn"
  • chơi xung quanh với điều đó bằng cách chạm vào các tệp cụ thể để xem chúng được cập nhật

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 filealltừ filefwdsang file, bây giờ fileallsẽ 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.


5

Mục tiêu đặc biệt .PHONY:cho phép khai báo các mục tiêu giả mạo, do đó makesẽ 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.


3

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 supercleansẽ sa thải clean, andsomethingelsecatcher superclean; nhưng với PHONY, make supercleansẽ không bắn catcher superclean.

Chúng tôi không phải lo lắng về việc nói rằng cleanmụ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, supercleanmụ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 supercleanmục tiêu - điều này bao gồm các supercleanmụ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ề andsomethingelsehoặ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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.