Mat*_*att 12 makefile gnu-make
foobar 即使失败也可以创建输出文件,所以在这种情况下我需要删除它.
我可以做这个:
foo: bar baz
foobar $^ -o $@ || (rm -f $@ && exit 1)
Run Code Online (Sandbox Code Playgroud)
但是这不会传播返回的相同退出代码foobar(然后由其输出make).有没有办法在Makefile中捕获错误而不是在shell中?
Chr*_*jer 12
如果DELETE_ON_ERROR不削减它,你要找的是一个有点像tearDown,@After或finally用Java/JUnit的,这是你可以做什么:
.ONESHELL:在单个shell中执行所有shell命令.trapfor EXIT以完成清理.errexit.虽然我们正在努力,但我们也可以pipefail立即开始.例如,假设您想要启动一个docker容器,进行测试,无论如何都停止docker contaner,但获得测试结果.这是怎么做的:
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…
…
Run Code Online (Sandbox Code Playgroud)
export SHELL出口告诉GNU使使用bash作为外壳,其中有较重的足迹比默认sh,但方式更多的功能.export SHELLOPTS套pipefail和errexit为标志bash的外壳.
pipefail确保管道的退出状态不是最后一个命令,而是具有非零退出状态的最后一个命令.所以,false | true将返回1而不是0.errexit确保命令序列的退出状态不是最后一个命令,而是具有非零退出状态的最后一个命令,并且不会执行后续命令.因此,false ; true将返回1,而不是0和true会不会被执行..ONESHELL:告诉GNU使运行在一个单一的外壳中的所有命令.这意味着,你的食谱现在真的是一个shellcript.(需要GNU make 3.82或更高版本.)function tearDown { docker stop test-image }定义了一个名为shell函数tearDown.在此示例中,它将停止docker容器.trap tearDown EXIT是最关键的部分在这个例子中的一切.它告诉shell调用的shell tearDown在退出时运行该函数,即无论命令是成功还是失败.这类似于finallyJava.无法跨多个目标/测试重复使用.它绝对不像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:
…
Run Code Online (Sandbox Code Playgroud)
现在,这将运行所有测试,即使第一个测试失败,在所有测试完成后进行清理,并在任何测试失败时报告错误.
免责声明:这需要.ONESHELLGNU make 的功能,它是在GNU make 3.82中引入的.当前版本的GNU make在编辑时是GNU make 4.2.1,而Mac OS X仍然附带GNU make 3.81.
你.DELETE_ON_ERROR:在这做什么吗?
从食谱中的错误:
通常当配方行失败时,如果它根本改变了目标文件,则该文件已损坏且无法使用 - 或者至少未完全更新.然而文件的时间戳表示它现在是最新的,所以下次make运行时,它不会尝试更新该文件.情况与外壳被信号杀死时的情况相同; 看到中断.因此,通常正确的做法是在开始更改文件后如果配方失败则删除目标文件.如果.DELETE_ON_ERROR作为目标出现,make将执行此操作.这几乎总是你想要做的事情,但这不是历史实践; 因此,为了兼容性,您必须明确请求它.
如果没有,或者你只需要那个目标,那么你想要的shell行是:
foobar $^ -o $@ || (ret=$$?; rm -f $@ && exit $$ret)
Run Code Online (Sandbox Code Playgroud)