Bash 如果目标失败,Makefile应该退出

Bash 如果目标失败,Makefile应该退出,bash,makefile,grep,Bash,Makefile,Grep,我有一个以下格式的makefile- all: target_1 target_2 target_3 target_1: TASK_A1 | tee ta.log TASK_A2 | tee -a ta.log target_2: TASK_B | tee tb.log target_3: TASK_C | tee tc.log <some lines of output> Errors: 0, Warnings: 12 <some mo

我有一个以下格式的makefile-

all: target_1 target_2 target_3

target_1:
    TASK_A1 | tee ta.log
    TASK_A2 | tee -a ta.log

target_2:
    TASK_B | tee tb.log

target_3:
    TASK_C | tee tc.log
<some lines of output>
Errors: 0, Warnings: 12
<some more lines of output>
Errors: 5, Warnings: 10
任务_A生成以下格式的日志输出-

all: target_1 target_2 target_3

target_1:
    TASK_A1 | tee ta.log
    TASK_A2 | tee -a ta.log

target_2:
    TASK_B | tee tb.log

target_3:
    TASK_C | tee tc.log
<some lines of output>
Errors: 0, Warnings: 12
<some more lines of output>
Errors: 5, Warnings: 10

错误:0,警告:12
错误:5,警告:10
当我完成全部生成时,理想情况下,如果目标_1失败,但它继续以目标_2为目标,makefile应该退出

我能想到的一个可能的解决方案是从每个目标的日志文件中grep“Errors:0”,然后检查grep的返回值(如果grep返回非零值,则退出)。我不认为这是非常有效的解决方案,因为我需要为每个目标执行上述步骤


是否有更有效、更智能的方法来解决此问题?

不知道更多的单个任务,除了处理“错误:0”的输出之外,您无法做更多的事情。但是,您可以像这样应用干燥原理:

runtask = $(1) | tee -a $(2) | awk '{ print; if (/Errors: 0/) y=1; } END { if (y) { exit 0 } else { exit 1 } }'

all: target_1 target_2 target_3

target_1:
        @$(call runtask, TASK_A1, ta.log)
        @$(call runtask, TASK_A2, ta.log)

target_2:
        @$(call runtask, TASK_B, tb.log)

target_3:
        @$(call runtask, TASK_C, tc.log)

至少这样,如果需要修改搜索模式,您不必遍历每一行。

因此,首先,如果
target_2
依赖于
target_1
,您应该在makefile中明确指定:

target_2: target_1
否则,如果有人使用
-j
构建,则无论
target\u 1
是否成功,
target\u 2
都可能会运行

接下来,如果其中一个目标失败,您希望使其失败。如果配方行返回值为false(非零),Make将终止。我假设,
TASK_A1
和friends如果产生错误,则返回false(否则本文的其余部分就没有意义了)

不幸的是,您有一个使事情复杂化的管道——即,配方的返回值将是
tee
的返回值,而不是
TASK\u A1
。见下文:

bash> false
bash> echo $?
1
bash> false | tee blah
bash> echo $?
0
在本例中,
teeblah
返回true(
0
),因此
$?
(返回代码)为0。幸运的是,您可以使用
PIPESTATUS
从管道的第一部分获取返回代码

bash> false | tee blah; [ ${PIPESTATUS[0]} -eq 0 ]
bash> echo $?
1
bash> true | tee blah; [ ${PIPESTATUS[0]} -eq 0 ]
bash> echo $?
0
所以。。。总之,您可以执行以下操作:

all: target_3

target_1: 
    TASK_A1 | tee ta.log; [ $${PIPESTATUS[0]} -eq 0 ]
    TASK_A2 | tee -a ta.log; [ $${PIPESTATUS[0]} -eq 0 ]

target_2: target1
    TASK_B | tee tb.log; [ $${PIPESTATUS[0]} -eq 0 ]

target_3: target2
    TASK_C | tee tc.log; [ $${PIPESTATUS[0]} -eq 0 ]
一旦make命中一个失败的任务,它就会失败。 注意,在这个解决方案中,如果
TASK_A1
失败,那么
TASK_A2
将不会运行。我不确定这是否是你想要的。如果您希望两者都运行,则可以将
target1
更改为:

target1:
   ( TASK_A1; TASK_A2 ) | tee ta.log; [ $${PIPESTATUS[0]} -eq 0 ]

idk关于其余部分,但在awk
{print;if(/Errors:0/)y=1;}END{if(y){exit 0}否则{exit 1}
相当于
{print}/Errors:0/{y=1}END{exit!y}
非常感谢。您的解决方案非常有效,比我处理问题的方式更好、更干净。