Эквивалент pipefail в GNU make?

Скажем, у меня есть следующие файлы:

buggy_program:

#!/bin/sh
echo "wops, some bug made me exit with failure"
exit 1

Makefile:

file.gz:
    buggy_program | gzip -9 -c >[email protected]

Теперь, если я наберу make, GNU make с радостью построит file.gz, хотя buggy_program выйдет с ненулевым статусом.

В bash я мог бы сделать set -o pipefail, чтобы сделать выход конвейера с ошибкой, если хотя бы одна программа в конвейере завершила сбой. Есть ли аналогичный метод в GNU make? Или какое-то обходное решение, которое не связано с временными файлами? (Причина gzipping здесь заключается в том, чтобы избежать огромного временного файла.)

Ответ 1

Здесь возможно решение, не требующее bash. Представьте, что у вас есть две программы thisworks и thisfails, которые не работают или работают нормально, соответственно. Тогда вы оставите только work.gz, удалив fail.gz, т.е. создать gzipped make target, если и только если программа выполнена правильно:

all: fail.gz work.gz

work.gz:
    ( thisworks && touch [email protected] ) | gzip -c -9 >[email protected]
    rm [email protected] || rm [email protected]

fail.gz:
    ( thisfails && touch [email protected] ) | gzip -c -9 >[email protected]
    rm [email protected] || rm [email protected]

Пояснение:

В первой строке правила work.gz thisworks будет успешно завершен, и будет создан файл work.gz.ok, и все stdout перейдут через gzip в work.gz. Затем во второй строке, поскольку work.gz.ok существует, первая команда rm также успешно завершается - и поскольку || короткое замыкание, второй rm не запускается и поэтому work.gz не удаляется.

OTOH, в первой строке правила fail.gz thisfails выйдет с ошибкой, а fail.gz.ok не будет создан. Все stdout все еще проходит через gzip в fail.gz. Затем во второй строке, поскольку fail.gz.ok не существует, первая команда rm завершается с ошибкой, поэтому || пытается выполнить вторую команду rm, которая удаляет файл fail.gz.


Чтобы легко проверить, что это работает так, как нужно, просто замените thisworks и thisfails командами true и false, соответственно, поместите их в Makefile и введите make.

(Благодаря добрым людям в #autotools, которые помогают мне в этом.)

Ответ 2

Попробуйте это

SHELL=/bin/bash -o pipefail

file.gz:
    buggy_program | gzip -9 -c >[email protected]

Ответ 3

Вы можете сделать:

SHELL=/bin/bash

.DELETE_ON_ERROR:
file.gz:
    set -o pipefail; buggy_program | gzip -9 -c >[email protected]

но этот работает только с bash.