2009-02-26 17 views
11

Tôi đã có một makefile nhẹ hackish cho chạy thử nghiệm:Tại sao GNU làm xóa một tập tin

### Run the tests 

tests := tests/test1 tests/test2 ... 

test: $(tests) 

$(tests): %: %.c 
    gcc -o [email protected] $(testflags) $< 
    [email protected] 

Nó hoạt động, nhưng nó làm Hãy làm điều gì đó tôi chưa bao giờ nhìn thấy nó làm trước. Thử nghiệm của tôi hiện bị hỏng và gây ra lỗi xe buýt. Cung cấp cho kết quả sau:

gcc -o tests/test1 [flags blah blah] tests/test1.c 
tests/test1 
make: *** [tests/test1] Bus error 
make: *** Deleting file `tests/test1' 

Tôi tò mò về dòng cuối cùng. Tôi chưa bao giờ thấy Make làm điều đó trước đây. Tại sao xóa bài kiểm tra đã biên dịch?

Lưu ý: Tôi đã chỉnh sửa ví dụ này khá nhiều để làm cho nó đơn giản hơn. Tôi có thể đã giới thiệu một số sai lầm.

Trả lời

14

Vì mục tiêu có thể chưa được xây dựng chính xác. Lần sau khi bạn make dự án, nó sẽ cố gắng xây dựng lại mục tiêu. Nếu tập tin chưa bị xóa, make sẽ không có cách nào để biết có điều gì đó không ổn. make không thể biết rằng sự thất bại đến từ một thử nghiệm chứ không phải là quá trình xây dựng mục tiêu.


Hành vi này có được mong muốn trong trường hợp của bạn hay không tùy thuộc vào bản chất của các thử nghiệm. Nếu bạn có kế hoạch sửa chữa thử nghiệm để nó không gây ra một Bus error, việc loại bỏ mục tiêu không phải là vấn đề lớn. Nếu bạn muốn sử dụng mục tiêu để gỡ lỗi sau này, bạn sẽ cần phải thực hiện thay đổi cho quá trình thực hiện của mình.

Một cách để không xóa mục tiêu là sử dụng mục tiêu .PRECIOUS.


khác có thể là:

$(tests): %: %.c 
    gcc -o [email protected] $(testflags) $< 
    [email protected] 

Không thử nghiệm, nhưng documentation chỉ ra mục tiêu sẽ không được gỡ bỏ:

Khi một lỗi xảy ra làm mà không được nói phớt lờ , nó ngụ ý rằng mục tiêu hiện tại không thể được làm lại một cách chính xác, và không ai có thể phụ thuộc vào nó một cách trực tiếp hoặc gián tiếp. Không có lệnh nào khác sẽ được thực hiện cho các mục tiêu này, vì điều kiện tiên quyết của chúng chưa đạt được.

và:

Thông thường khi một lệnh thất bại, nếu nó đã làm thay đổi tập tin mục tiêu ở tất cả, các tập tin bị hỏng và không thể được sử dụng-hay ít nhất nó không được cập nhật hoàn toàn. Tuy nhiên, tem thời gian của tập tin nói rằng nó đã được cập nhật, do đó, lần sau thực hiện chạy, nó sẽ không cố gắng cập nhật tập tin đó. Tình huống cũng giống như khi lệnh bị giết bởi một tín hiệu; xem Ngắt. Vì vậy, thông thường, điều đúng đắn cần làm là xóa tệp đích nếu lệnh không thành công sau khi bắt đầu thay đổi tệp. thực hiện sẽ làm điều này nếu .DELETE_ON_ERROR xuất hiện dưới dạng mục tiêu. Điều này hầu như luôn là điều bạn muốn làm, nhưng nó không phải là thực hành lịch sử; do đó, để tương thích, bạn phải yêu cầu một cách rõ ràng.

11

Một cách để tránh hành vi này là để phân chia các xây dựng và thực hiện thử nghiệm thành hai bước:

tests := tests/test1 tests/test2 ... 

test: $(tests) runtests 

$(tests): %: %.c 
    gcc -o [email protected] $(testflags) $< 

runtests: %.out: % 
    $< | tee [email protected] 

(Có lẽ sai sót trong làm cho cú pháp của tôi, ai cũng cảm thấy tự do để sửa chữa nó.) Ý tưởng chung là để chạy thử nghiệm tạo ra một tệp đầu ra, giúp dễ dàng hơn cho make chạy từng thử nghiệm riêng lẻ.

+1

+1 là câu trả lời duy nhất để chỉ ra rằng hành vi đã được gây ra bởi một tệp makefile lỗi kết hợp tạo chương trình thử nghiệm và chạy nó thành một quy tắc duy nhất. –

+0

IMO đây là giải pháp tốt hơn: sạch hơn, và nhiều hơn nữa "làm cho giống như". Mặc dù, các chi tiết có thể được xử lý sạch hơn. Đầu tiên tác giả makefile phải quyết định: bạn có muốn _always_ chạy các kiểm tra hay chỉ chạy các kiểm tra nếu tệp thử nghiệm được xây dựng lại (sau này là ví dụ ban đầu đã làm). – MadScientist

6

Đây là hành vi mặc định của thực hiện. Khi lệnh trả về mã lỗi (ví dụ: trả về số không 0) thì đích thực hiện sẽ bị xóa. Các chỉ thị makefile .PRECIOUS và .IGNORE có thể thay đổi hành vi này.

Các vấn đề liên quan