Почему GNU заставляет удалить файл

У меня есть немного хакерский make-файл для запуска тестов:

### Run the tests

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

test: $(tests)

$(tests): %: %.c
    gcc -o $@ $(testflags) $<
    $@

Это работает, но заставляет Make делать то, чего я никогда раньше не видел. Мой тест в настоящее время не работает и вызывает ошибку шины. Make дает следующий результат:

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

Мне интересна последняя строчка. Я никогда раньше не видел, чтобы Make делал это. Почему Make удаляет скомпилированный тест?

Примечание: я довольно сильно отредактировал этот пример, чтобы упростить его. Я мог допустить некоторые ошибки.


person Community    schedule 26.02.2009    source источник


Ответы (3)


Потому что цель могла быть построена неправильно. В следующий раз, когда вы make проект, он попытается перестроить цель. Если бы файл не был удален, make не смог бы узнать, что что-то пошло не так. make не может знать, что ошибка произошла из-за теста, а не из-за процесса, который создает цель.


Желательно ли такое поведение в вашем случае, зависит от характера тестов. Если вы планируете исправить тест так, чтобы он не вызывал Bus error, удаление цели не представляет большого труда. Если вы хотите использовать цель для отладки позже, вам нужно будет внести изменения в свой процесс make.

Один из способов не удалять цели - использовать цель .PRECIOUS.


Другой может быть:

$(tests): %: %.c
    gcc -o $@ $(testflags) $<
    -$@

Не тестировалось, но в документации указано, что цель не будет удалена. :

Когда происходит ошибка, которую программе make не было приказано игнорировать, это означает, что текущая цель не может быть правильно преобразована, как и любая другая, которая зависит от нее прямо или косвенно. Никакие дальнейшие команды для этих целей выполняться не будут, поскольку их предварительные условия не были выполнены.

а также:

Обычно, когда команда терпит неудачу, если она вообще изменила целевой файл, файл поврежден и не может быть использован - или, по крайней мере, он не обновляется полностью. Однако отметка времени файла говорит о том, что теперь он обновлен, поэтому при следующем запуске make не будет пытаться обновить этот файл. Ситуация такая же, как когда команда прерывается сигналом; см. Прерывания. Поэтому, как правило, правильнее всего удалить целевой файл, если команда не работает после начала изменения файла. make сделает это, если .DELETE_ON_ERROR отображается как цель. Это почти всегда то, что вы хотите сделать, но это не историческая практика; поэтому для совместимости вы должны явно запросить это.

person Jon Ericson    schedule 26.02.2009

Один из способов избежать такого поведения - разделить выполнение сборки и теста на два этапа:

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

test: $(tests) runtests

$(tests): %: %.c
    gcc -o $@ $(testflags) $<

runtests: %.out: %
    $< | tee $@

(Вероятно, в моем синтаксисе make есть ошибки, любой может их исправить.) Общая идея состоит в том, чтобы при тестовом прогоне генерировался выходной файл, что упрощает make запуск каждого теста по отдельности.

person Greg Hewgill    schedule 26.02.2009
comment
+1 за то, что это единственный ответ, указывающий на то, что поведение было вызвано ошибочным make-файлом, который объединил создание тестовой программы и ее запуск в одно правило. - person R.. GitHub STOP HELPING ICE; 18.10.2010
comment
ИМО, это лучшее решение: чище и больше похоже. Хотя, с деталями можно было бы обработать более аккуратно. Сначала автор make-файла должен решить: хотите ли вы всегда запускать тесты или запускать тесты только в том случае, если тестовый файл был перестроен (последнее - то, что делал исходный пример). - person MadScientist; 16.08.2013

Это поведение make по умолчанию. Когда команда возвращает код ошибки (например, ненулевой возврат), цель make удаляется. Директивы makefile .PRECIOUS и .IGNORE могут изменить это поведение.

person an0nym0usc0ward    schedule 26.02.2009