Makefile 在生成文件中出现错误后,如何清理?
Makefile 在生成文件中出现错误后,如何清理?,makefile,gnu-make,Makefile,Gnu Make,foobar即使失败也可能创建输出文件,因此在这种情况下我需要删除它 我可以这样做: foo: bar baz foobar $^ -o $@ || (rm -f $@ && exit 1) 但这不会传播由foobar返回的相同退出代码(然后由make输出)。有没有办法在Makefile而不是shell中捕获错误?是否执行。删除错误:在此处执行所需操作 发件人: 通常,当配方行失败时,如果它根本更改了目标文件,则该文件已损坏且无法使用,或者至少没有完全更新。但是
foobar
即使失败也可能创建输出文件,因此在这种情况下我需要删除它
我可以这样做:
foo: bar baz
foobar $^ -o $@ || (rm -f $@ && exit 1)
但这不会传播由
foobar
返回的相同退出代码(然后由make
输出)。有没有办法在Makefile而不是shell中捕获错误?是否执行。删除错误:
在此处执行所需操作
发件人:
通常,当配方行失败时,如果它根本更改了目标文件,则该文件已损坏且无法使用,或者至少没有完全更新。但是文件的时间戳显示它现在是最新的,所以下次运行时,它不会尝试更新该文件。情况与炮弹被信号击毙时一样;参见中断。因此,通常正确的做法是,如果配方在开始更改文件后失败,则删除目标文件。make将在以下情况下执行此操作。删除\u ON\u错误显示为目标。这几乎总是你想要做的,但它不是历史的实践;因此,为了兼容性,您必须显式地请求它
如果不是,或者如果您只需要一个目标,那么您想要的shell行是:
foobar $^ -o $@ || (ret=$$?; rm -f $@ && exit $$ret)
如果Java/JUnit中的
DELETE\ON\u ERROR
无法将其删除,并且您看到的有点像tearDown
、@After
、或finally
,您可以这样做:
.ONESHELL:
在单个shell中执行所有shell命令退出安装陷阱
,以完成清理
errexit
,确保shell在出现任何错误时退出。在进行此操作时,我们还可以立即设置pipefail
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告诉gnumake使用export SHELL
作为SHELL,它比默认的bash
占用的空间更大,但功能更多sh
为export SHELLOPTS
shell设置bash
和pipefail
标志。errexit
确保管道的退出状态不是最后一个命令,而是最后一个非零退出状态的命令。因此,pipefail
将返回false | true
,而不是1
0
确保命令序列的退出状态不是最后一个命令,而是最后一个非零退出状态的命令,并且后续命令不会执行。所以,errexit
将返回false;true
而不是1
,并且0
将不会执行true
告诉gnumake在一个shell中运行所有命令。这意味着,你的食谱现在真的是一个脚本。(需要GNU make 3.82或更高版本。).ONESHELL:
定义了一个名为函数tearDown{docker stop test image}
的shell函数。在本例中,它将停止docker容器tearDown
陷阱拆卸出口是本例中最关键的部分。它告诉为配方调用的shell在退出时运行
函数,也就是说,无论命令是成功还是失败tearDown
finally
。不可能跨多个目标/测试重用。它绝对不像JUnit中的AfterClass/@AfterAll
或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的当前版本是GNU make 4.2.1,Mac OS X仍然附带GNU make 3.81。我想在这种情况下,将其用于所有目标不会有什么坏处。但是,作为将来的参考,是否有一种方法可以在不污染shell命令的情况下执行此操作?很高兴看到make
而不是长时间的shell hacks重复了简单而清晰的代码。除非有办法抑制shell命令部分的回音,我对此表示怀疑。这个选项要么全有,要么全无。我不知道有什么方法可以做到这一点,仅仅是为了特定的规则。这种贝壳游戏是我最好的。也就是说,即使面对“丑陋”的shell命令,您也可以使用automake样式来控制make输出的外观。@matt您可以将shell代码移动到脚本中,使其更漂亮。您还可以在它前面加上类似于@echo foobar$^-o$@;的前缀
但这可能会让查看make输出的人感到困惑,他们想知道文件是如何被删除的。@RossRidge第二个建议是我的沉默规则建议,仅供记录。我的automake样式沉默规则功能现在有一个github项目:。警告一句:。ONESHELL可能会使你的食谱表现不同。请参阅。@schieferstapel的最后一段的确,这就是为什么关于SHELLOPTS
的那一行非常重要的原因。.ONESHELL
不是默认值是有原因的,原因是errexit
不是shell的默认值,并且没有标准的设置方法,这取决于shell。感谢您的详细说明。但是,即使使用set-e
,食谱也可能表现出