如果 DELETE_ON_ERROR
无法满足要求,而您正在寻找类似于Java/JUnit中的tearDown
或finally
的东西,那么您可以这样做:
- 使用
.ONESHELL:
以在单个Shell中执行所有Shell命令。
- 为
EXIT
安装trap
以完成清理工作。
- 通过设置
errexit
确保Shell在任何错误情况下退出。 顺便说一句,我们也可以立即设置pipefail
。
例如,假设您想启动一个Docker容器,进行测试,无论如何都停止Docker容器,但获取测试结果。 就是这样做的:
export SHELL:=/bin/bash
export SHELLOPTS:=$(if $(SHELLOPTS),$(SHELLOPTS):)pipefail:errexit
.ONESHELL:
.PHONY: test
test:
function tearDown {
docker stop test-image
}
trap tearDown EXIT
docker run --name test-image …
testStep1…
testStep2…
testStep3…
…
工作原理
export SHELL
告诉 GNU make 使用 bash
作为 shell,它比默认的 sh
占用更多内存,但功能更强大。
export SHELLOPTS
设置 bash
shell 的 pipefail
和 errexit
标志。
pipefail
确保管道的退出状态码不是最后一个命令的退出状态码,而是最后一个具有非零退出状态码的命令。因此,false | true
将返回 1
而不是 0
。
errexit
确保命令序列的退出状态码不是最后一个命令的退出状态码,而是最后一个具有非零退出状态码的命令,并且不会执行后续命令。因此,false ; true
将返回 1
而不是 0
,并且将不执行 true
命令。
.ONESHELL:
告诉 GNU make 在单个 shell 中运行所有命令。这意味着,您的配方现在真正是一个 shell 脚本。(需要 GNU make 3.82 或更高版本。)
function tearDown { docker stop test-image }
定义一个名为 tearDown
的 shell 函数。在此示例中,它将停止 docker 容器。
trap tearDown EXIT
是此示例中的最关键部分。它告诉为配方调用的 shell 在退出时运行 tearDown
函数,也就是说,无论命令成功还是失败。
限制
这类似于 Java 中的 finally
。无法在多个目标/测试之间重复使用。它绝对不像 JUnit 中的 @AfterClass
/ @AfterAll
或 tearDown()
/ @After
/ @AfterEach
。
更好的使用方法
但是,如果需要,您可以这样做。例如,您想在同一个 docker 容器上运行多个测试,并在任何情况下都拆除它。那就类似于 JUnit 中的 @AfterClass
/ @AfterAll
。然后,可以这样做:
export SHELL:=/bin/bash
export SHELLOPTS:=$(if $(SHELLOPTS),$(SHELLOPTS):)pipefail:errexit
.ONESHELL:
.PHONY: start
start:
docker run --name test-image …
.PHONY: stop
stop:
docker stop test-image
.PHONY: test
test: start
function tearDown {
$(MAKE) stop
}
trap tearDown EXIT
$(MAKE) -k testImpl
.PHONY: testImpl
testImpl: testCase1 testCase2 testCase3
.PHONY: testCase1
testCase1:
…
.PHONY: testCase2
testCase2:
…
.PHONY: testCase3
testCase3:
…
即使第一个测试失败,这将运行所有测试,完成所有测试后进行清理,并在任何测试失败时报告错误。
免责声明:此功能需要GNU make的.ONESHELL特性,该特性引入于GNU make 3.82。截至本编辑时,GNU make的当前版本为4.2.1,而Mac OS X仍带有GNU make 3.81。
SHELLOPTS
的那一行至关重要的原因。.ONESHELL
不是默认设置的原因是,对于 shell 而言,errexit
不是默认设置,并且没有标准的方法来设置它,这取决于 shell。 - Christian Hujerset -e
,配方的行为也可能不同,例如false; true
,其中true
将不再被执行。绝对不反对您的解决方案,但应该意识到.ONESHELL
的影响。 - schieferstapel