Câu hỏi Mục đích của .PHONY trong một makefile là gì?


Cái gì .PHONY có nghĩa là trong một Makefile? Tôi đã trải qua điều nàynhưng nó quá phức tạp.

Ai đó có thể giải thích cho tôi một cách đơn giản?


1211
2018-01-27 09:08


gốc




Các câu trả lời:


Theo mặc định, mục tiêu Makefile là "mục tiêu tệp" - chúng được sử dụng để tạo tệp từ các tệp khác. Giả sử mục tiêu của nó là một tập tin, 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 đại diện cho các tệp vật lý trong hệ thống tệp. Ví dụ tốt cho việc này là các mục tiêu chung "sạch" và "tất cả". Rất có thể đây không phải là trường hợp, nhưng bạn có thể có khả năng có một tệp có tên clean trong thư mục chính của bạn. Trong trường hợp này, Make sẽ bị nhầm lẫn 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 có vẻ không cập nhật liên quan đến các phụ thuộc của nó.

Những mục tiêu đặc biệt này được gọi là giả mạo và bạn có thể cho biết rõ ràng Làm cho chúng không được liên kết với tệp, ví dụ:

.PHONY: clean
clean:
  rm -rf *.o

Hiện nay make clean sẽ chạy như mong đợi ngay cả khi bạn có một tệp có tên clean.

Xét về Make, một mục tiêu giả mạo đơn giản là mục tiêu luôn lỗi thời, vì vậy bất cứ khi nào bạn hỏi 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 các mục tiêu thường giả mạo là: all, install, clean, distclean, TAGS, info, check.


1348
2018-01-27 09:11



@eSKay: 'tại sao nó được gọi là' giả mạo '?' - bởi vì nó không phải là mục tiêu thực sự. Tức là, tên mục tiêu 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
@ Lazer: Tôi không biết bạn có phải là người nói tiếng Anh bản ngữ không. Tôi không. từ ngữ giả không có nghĩa là nó nghe như thế nào. en.wiktionary.org/wiki/phony nói: Gian lận; giả mạo; có một sự xuất hiện gây hiểu lầm. - Bahbar
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 tin trong một Makefile được xây dựng nếu nó là một phần của kiểu tôpô bất kể mục tiêu của bạn là gì. Tức 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 là dọn dẹp làm đ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 luôn được chạy khi Makefile cố gắng xây dựng 'cài đặt'. Điều này rất hữu ích cho các bước bạn luôn luôn muốn thực hiện bất kể nếu chúng thành công - nó sẽ bỏ qua dấu thời gian và chỉ ép buộc nó. - synthesizerpatel
@synthesizerpatel Vì vậy, nếu a: b, b: c, c: ph Ở đâu ph Là .PHONY, bạn yêu cầu avà b được cập nhật, nhận xét của bạn ngụ ý rằng ph sẽ được chạy. Trong thực tế, không có a, b, c có thể được cập nhật, thực sự. - Evgeni Sergeev
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ụ. Nhiệm vụ sẽ luôn được thực thi, và Makefile sẽ dễ đọc hơn. - Alkaline


Giả sử bạn có install mục tiêu, vốn rất phổ biến trong makefiles. Nếu bạn làm không phải sử dụng .PHONYvà một tệp có tên install tồn tại trong cùng thư mục với Makefile, sau đó make install sẽ làm không có gì. Điều này là do làm cho diễn giải quy tắc có nghĩa là "thực hiện công thức như vậy và như vậy để tạo tệp có tên installVì tập tin đã ở đó, và các phụ thuộc của nó không thay đổi, không có gì sẽ được thực hiện.

Tuy nhiên nếu bạn thực hiện install mục tiêu PHONY, nó sẽ cho công cụ làm cho rằng mục tiêu là hư cấu, và điều đó làm cho không nên mong đợi nó để tạo ra các tập tin thực tế. Do đó nó sẽ không kiểm tra xem liệu install tệp tồn tại, có 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 mà không tạo ra một tập tin đầ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.


636
2017-08-26 10:54



@PineappleUndertheSea Câu trả lời được chấp nhận đã được cải thiện đáng kể từ mức độ vô giá trị ban đầu của nó, và bây giờ cũng tốt như thế 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. - Mark Amery
Điều này có vẻ là vô nghĩa vì tôi sẽ không bao giờ có các tập tin có tên 'install' hoặc tương tự trong codebase của tôi. Hầu hết các tệp sẽ có đuôi 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 chữ cái, như 'README'. Sau đó, một lần nữa, nếu bạn có một kịch bản bash tên 'install' thay vì 'install.sh', bạn sẽ có một thời gian xấu. - Jason Tu
@ JasonTu Điều này không nhất thiết phải đúng. Các quy ước kịch bản lệnh Bash yêu cầu bạn bỏ qua .sh hoặc là .bash 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 mà bạn đưa vào (source mylib.sh). Trong thực tế, tôi đã nhận được câu hỏi SO này bởi vì tôi đã có một kịch bản trong cùng thư mục với Makefile của tôi được gọi là install - Kyle
@ Kyle Vâng, tôi không chắc chắn về ý nghĩa quá khứ của mình. Những ngày này tôi sử dụng .PHONY mọi lúc ... - Jason Tu
@JasonTu Các giải pháp ở đây là đơn giản: xây dựng một cỗ máy thời gian và "thay thế" quá khứ của bạn. Tôi khuyên bạn nên dùng xẻng cùng với bạn để không ai nhận ra bạn là .PHONY phiên bản. - Mateen Ulhaq


CHÚ THÍCH: Công cụ tạo ra đọc tệp makefile và kiểm tra các 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 thư mục 'kiểm tra' các tệp 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ả định 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, sửa đổi (hoặc sáng tạo) tem thời gian của 'hello' sẽ mới hơn so với 'hello.c'. Vì vậy, khi chúng ta sẽ gọi 'make hello' từ dòng lệnh, nó sẽ in như sau:

make: `hello' is up to date.

Bây giờ truy cập vào tập tin 'hello.c' và đặt một số khoảng trắng vào nó, nó không ảnh hưởng đến cú pháp mã hoặc logic và sau đó lưu và thoát. Bây giờ sửa đổi tem thời gian của hello.c là mới hơn so với 'hello'. Bây giờ nếu bạn gọi 'make hello', nó sẽ thực thi các lệnh như sau:

cc hello.c -o hello

Và tệp 'hello' (tệp văn bản) sẽ được ghi đè bằng một tệp nhị phân mới 'hello' (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 nếu có bất kỳ tệp nào có trong pwd có tên 'hello' và thực hiện lệnh mỗi lần.

Bây giờ giả sử nếu không có sự phụ thuộc của mục tiêu là có trong makefile:

hello:
    cc hello.c -o hello

và 'hello' đã có trong bài kiểm tra 'pwd', sau đó 'make hello' sẽ luôn hiển thị dưới dạng:

make: `hello' is up to date.

64
2017-09-30 14:51



Đ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 gây ra makenói chung là có ý nghĩa, đó là tất cả về các tập tin! Cảm ơn bạn vì câu trả lời này. - Kzqai


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

63
2017-08-25 22:39





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


34
2018-01-27 16:48





Giải thích tốt nhất là GNU tự tạo thủ công: 4.6 Phần mục tiêu giả mạo.

.PHONY là một trong những Tên mục tiêu được tạo sẵn đặc biệt. 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 những tài liệu tham khảo này.

Khi đó là thời gian để xem xét một mục tiêu .PHONY, thực hiện sẽ chạy công thức của nó   vô điều kiện, bất kể tệp có tên đó tồn tại hay không    thời gian sửa đổi cuối cùng của nó là gì.

Bạn cũng có thể quan tâm Mục tiêu tiêu chuẩn nhu la all và clean.


21
2018-04-03 23:49





Ngoài ra còn có một điều quan trọng phức tạp của ".PHONY" - khi một mục tiêu vật lý phụ thuộc vào mục tiêu giả tạo phụ thuộc vào một mục tiêu vật lý khác:

TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2

Bạn chỉ cần mong đợi rằng nếu bạn đã cập nhật TARGET2, thì TARGET1 nên được coi là cũ so với TARGET1, vì vậy TARGET1 phải được tạo 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 phải cũ với TARGET1 - trong trường hợp này bạn nên mong đợi rằng TARGET1 không nên xây dựng lại.

Điều này đáng ngạc nhiên không hiệu quả vì: mục tiêu giả đã được chạy anyway (như mục tiêu giả thường làm), có nghĩa là mục tiêu giả được xem là cập nhật. Và vì lý do đó TARGET1 được coi là cũ so vớ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:

  • trước 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 gián tiếp thông qua một mục tiêu giả tạo - nhưng nó luôn luôn được xây dựng lại do sự phụ thuộc này. Nếu bạn thay đổi sự phụ thuộc trong fileall từ filefwd đến file, hiện nay fileall không được xây dựng lại mỗi lần, nhưng chỉ khi bất kỳ mục tiêu phụ thuộc nào là cũ để chống lại nó như là một tập tin.


8
2018-02-11 10:11





Tôi thường sử dụng chúng để nói cho mục tiêu mặc định không cháy.

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

Không có PHONY, make superclean sẽ bắn clean, andsomethingelsecatcher superclean; nhưng với PHONY, make superclean sẽ không bắn catcher superclean.

Chúng ta không phải lo lắng về việc nói 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 các tập tin sạch sẽ, nó có lệnh để bắn để làm cho nó sẽ nghĩ rằng đó là một mục tiêu cuối cùng.

Tuy nhiên, superclean mục tiêu thực sự là giả mạo, do đó, hãy cố gắng để ngăn xếp nó lên với bất cứ điều gì khác cung cấp deps cho superclean mục tiêu - điều này bao gồm superclean mục tiêu và % Mục tiêu.

Lưu ý rằng chúng tôi không nói gì cả về andsomethingelse hoặc là blah, do đó, họ rõ ràng đi đến catcher.

Đầ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

0
2017-07-08 13:22