Bug 24877 - exec task doesn't handle error conditions well
Summary: exec task doesn't handle error conditions well
Status: NEW
Alias: None
Product: Ant
Classification: Unclassified
Component: Core tasks (show other bugs)
Version: 1.6.0
Hardware: All Linux
: P3 enhancement (vote)
Target Milestone: ---
Assignee: Ant Notifications List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2003-11-21 04:04 UTC by Eric Safern
Modified: 2012-04-18 07:00 UTC (History)
1 user (show)



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Safern 2003-11-21 04:04:16 UTC
Using the exec core task, if I set the attribute failonerror to true, ant will 
exit if the executed sub-program returns an error code.  That part is fine.

The error message displayed does not include anything the sub-program displayed 
on the error stream.  

I get: "/home/esafern/depot/ContentDev/BuildTools/build.xml:868: exec returned: 
2" - not the most useful of error messages.
 
This, I could argue, is a bug right here - it is an error stream, after all. 

But, OK, I should be able to override this behavior as follows:

<exec executable="perl" failonerror="no" errorproperty="errorText">
 <arg line="prog.pl" />
</exec>

<antcall target="failOnError" />

<!-- failOnError: exit if the errorText property is set -->
<target name="failOnError" if="errorText">
 <fail message="Error: an executed program has returned the following 
error: '${errorText}'.  Please contact your site
 administrator for further help." />
</target> <!-- failOnError -->

However, this doesn't work - the failOnError target is always called.

Why?

Because the exec task sets the errorText property, even if the subprogram 
produces no output on the error stream!

I believe it shouldn't do that.  

The same logic applies to the outputproperty (which I haven't tested) - then I 
can take action depending on whether or not the subprogram produced output.  

I realize I could build a condition around <not> <equals arg1=${errorText} 
arg2=""></not> - but this seems overly complex.
Comment 1 Steve Loughran 2003-11-21 08:14:14 UTC
What about using the resultproperty to cache the return code, then probing that
for being non-zero? It would be a workaround.

Out of curiosity, what it the contents of the errorText property after the exec?
an empty string? We really ought to consider leaving it unset in such a case.
Comment 2 Conor MacNeill 2003-11-21 10:33:35 UTC
I'm not sure it is better to leave this unset if there is no output. It is
possible but does it make sense? Should it apply to output properties as well.
What about people who output the properties after a run

<echo message="errors: ${erroroutput}"/>

The results for that would be the string ${erroroutput} if it is not set. Maybe
not that desirable.
Comment 3 peter reilly 2003-11-21 11:33:25 UTC
I argree with Conor. Eric is trying to overload the meaning of
the errorproperty.

There is a resultProperty in exec which can be used to see if the
exec returned a non zero value. (This also always gets set)

One can also use ant-contrib's trycatch.
Comment 4 peter reilly 2003-11-21 11:50:55 UTC
(fun with beanshelldef : see
http://marc.theaimsgroup.com/?l=ant-dev&m=106577547220771&w=2
Comment 5 peter reilly 2003-11-21 11:53:02 UTC
Opps I did a send by accidient:
One could extend ExecTask with an onerror nested element:

  <target name="init">
    <bsh:beanshelldef name="exec2" classname="Exec2">
      import org.apache.tools.ant.taskdefs.condition.Condition;
      import org.apache.tools.ant.taskdefs.ExecTask;
      import org.apache.tools.ant.taskdefs.Sequential;
      public class Exec2 extends ExecTask implements Condition {
          private boolean runOk = false;
          private Sequential onError;
          protected void maybeSetResultPropertyValue(int result) {
             super.maybeSetResultPropertyValue(result);
             runOk = (result == 0);
         }
         public void addOnError(Sequential onError) {
             this.onError = onError;
         }
         public boolean eval() {
             execute();
             return runOk;
         }
         public void execute() {
             super.execute();
             if (!runOk &amp;&amp; onError != null) {
                onError.perform();
             }
         }
      }
    </bsh:beanshelldef>
  </target>

and use it like so:

  <target name="onerror" depends="init">
    <exec2 executable="sh"
           errorproperty="sh.error.output"
           failonerror="false">
      <arg value="error.sh"/>
      <onError>
        <echo>exec failed with error output: ${sh.error.output}</echo>
      </onError>
    </exec2>
  </target>
Comment 6 Eric Safern 2003-11-21 18:00:26 UTC
Well, I like the OnError idea.

Checking the return value works, but a) it's an ugly block of code needed after 
each and every exec and b) The ant documentation goes out of it's way to say 
that the definition of 'return code specifying failure' is OS-specific. This 
makes my code OS dependent, by making explicit that a non-zero return specifies 
failure.

In answer to Steve, yes - the errorText property is an empty string but set.

Finally, no one picked up on my first suggestion (perhaps not very clear).  If 
you set failonerror="yes", the errror message returned should quote the error 
message returned by the sub-program, and return the error code - rather than 
just telling me the error code as it does now.
Comment 7 Eric Safern 2003-11-21 21:54:39 UTC
OK, here's an update: the suggestion to set a property based on the return 
value doesn't work when you're execing more than one sub-program.

Why?

Because of the way Ant properties work - they're immutable.  So after the first 
exec call, execError and errorText are both set and can't be changed! I suppose 
I could bury each exec call in an ant task that I <antcall> - but where does 
the ugliness end??

Comment 8 peter reilly 2003-11-24 17:43:39 UTC
I do not think that this is a bug.
If the command generates output on the error stream, ant
will display this output.
The user/build script author may suppress this output
by using the "outputproperty" or the "errorproperty" attributes.
(if "outputproperty" is sprecified by itself, it will have contain
error and std output).

Displaying the error output despite the user specifing otherwise would
in my opinion be an error.
Comment 9 Eric Safern 2003-11-24 18:21:38 UTC
You could say the same thing about the resultproperty, which *is* displayed 
when the failonerror is enabled (despite the user's setting or non-setting of 
this property).  That's why the manual says "Only of interest if 
failonerror=false." If we did the same thing with errorproperty, we could add 
the same line to the manual by errorproperty as well.

The bottom line is that I need some reasonable way to display the error message 
when the execed subprogram fails!  I have yet to hear any way to do this, short 
of having a dedicated property and a dedicated target for *each* call to exec:


<exec executable="perl" failonerror="no" errorproperty="eP1" 
resultproperty="rP1">
  <arg line="Prog1.pl" />
</exec>

<condition property="execFailed1">
  <not>
    <equals arg1="${rp1}" arg2="0" />
  </not>
</condition>

<antcall target="failOnError1" />

<target name="failOnError1" if="execFailed1">
  <fail message="Error: an executed program has returned the following 
error: '${eP1}'.  Please contact your site
 administrator for further help." />
</target> <!-- failOnError1 -->


<exec executable="perl" failonerror="no" errorproperty="eP2" 
resultproperty="rP2">
  <arg line="Prog2.pl" />
</exec>

<condition property="execFailed2">
  <not>
    <equals arg1="${rp2}" arg2="0" />
  </not>
</condition>

<antcall target="failOnError2" />

<target name="failOnError2" if="execFailed2">
  <fail message="Error: an executed program has returned the following 
error: '${eP2}'.  Please contact your site administrator for further help." />
</target> <!-- failOnError2 -->

repeated again and again.  That's 12 lines of noise to 3 lines of useful code.

And talk about subtle bugs - what do you think will happen if the programmer 
screws up his cut-and-paste and leaves a '3' when he needed a '4?'  Good luck 
catching that in the testing process!!!