我正在调用使用 macrodef
定义的 Ant 任务,但我发现fail
任务的退出状态没有正确传达给 shell。
以下是 SSCCE:
蚂蚁文件:
<project>
<macrodef name="ff">
<sequential>
<fail message="some failure" status="3"/>
</sequential>
</macrodef>
<ff/>
</project>
调用:
$ ant
Buildfile: /[...]/build.xml
BUILD FAILED
[...]build.xml:10: The following error occurred while executing this line:
[...]build.xml:5: some failure
Total time: 0 seconds
构建按预期失败;但退出状态不正确:
$ echo $?
1
即Ant
以 1 而不是 3 的状态退出。仅当fail
任务包装在 macrodef
中时,才会发生这种情况。
可以从有关status
属性的fail
任务文档中获得初步猜测:
这正是使用 macrodef 时使用指定的状态代码退出;假设生成的异常未被捕获,JVM 将以这种状态退出。
发生的情况:fail
任务退出构建时抛出的BuildException
被 macrodef 捕获。最终,宏定义会抛出某种包装器异常,它会吞噬退出代码。
在调试模式下运行 Ant 时,您还可以从异常堆栈跟踪中看到这一点:
使用 macrodef:
some failure
at org.apache.tools.ant.taskdefs.Exit.execute(Exit.java:164)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.taskdefs.Sequential.execute(Sequential.java:68)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.taskdefs.MacroInstance.execute(MacroInstance.java:398)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:390)
at org.apache.tools.ant.Target.performTasks(Target.java:411)
at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1399)
at org.apache.tools.ant.Project.executeTarget(Project.java:1368)
at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
at org.apache.tools.ant.Project.executeTargets(Project.java:1251)
at org.apache.tools.ant.Main.runBuild(Main.java:809)
at org.apache.tools.ant.Main.startAnt(Main.java:217)
at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280)
at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109)
请注意堆栈跟踪中 MacroInstance
类(第 398 行(调用的方法。通过查看此行的 Ant 代码,您可以看到一个捕获 fail
抛出的异常的try-catch
块:
try {
c.perform();
} catch (BuildException ex) {
if (this.macroDef.getBackTrace()) {
throw ProjectHelper.addLocationToBuildException(ex, getLocation());
}
ex.setLocation(getLocation());
throw ex;
}
在 catch
块中,在以下行中引发一个新异常:
throw ProjectHelper.addLocationToBuildException(ex, getLocation());
这会导致忽略原始BuildException
的退出代码。
更进一步,我们可以看到,如果启用了this.macroDef.getBackTrace()
,则会抛出此包装器异常。因此,该问题的解决方案是在 macrodef 中将backtrace
参数设置为 false
:
<macrodef name="ff" backtrace="false">
<sequential>
<fail message="some failure" status="3"/>
</sequential>
</macrodef>