==== //depot/home/tarasov/nant/src/NAnt.Core/Target.cs#2 (text) ====


@@ -31,6 +31,7 @@
     public sealed class Target : Element, ICloneable {
         string _name = null;
         string _desc = null;
+        string _onFail = null;
         bool _hasExecuted = false;
         bool _ifDefined = true;
         bool _unlessDefined = false;
@@ -97,6 +98,17 @@
             }
         }
 
+        /// <summary> The name of the target which should be called in case of failure.</summary>
+        [TaskAttribute("onfail")]
+        public string OnFailTarget {
+            get {
+                return _onFail;
+            }
+            set {
+                _onFail = value;
+            }
+        }
+
         /// <summary>Indicates if the target has been executed.</summary>
         /// <remarks>
         ///   <para>Targets that have been executed will not execute a second time.</para>
@@ -146,6 +158,27 @@
                            }
 
                     }
+                } catch (Exception e) {
+                    try {
+                        if (OnFailTarget != null) {
+                            Project.Log(Level.Warning, string.Format(CultureInfo.InvariantCulture,
+                                " Target failed, calling onfail target '{0}'", OnFailTarget));
+                            
+                            Target t = Project.Targets.Find(OnFailTarget);
+                            if (t == null) {
+                                // if we can't find it, then neither should Project.Execute.
+                                // Let them do the error handling and exception generation.
+                                Project.Execute(OnFailTarget);
+                            }
+
+                            //Execute a copy.
+                            t.Copy().Execute();
+                        }
+                    } catch (Exception onFailException) {
+                        e = new MultipleException(e, onFailException);
+                    }
+
+                    throw e;
                 } finally {
                     Project.OnTargetFinished(this, new BuildEventArgs(this));
                 }


==== //depot/home/tarasov/nant/src/NAnt.Core/Task.cs#2 (text) ====


@@ -46,6 +46,7 @@
         bool _verbose = false;
         bool _ifDefined = true;
         bool _unlessDefined = false;
+        string _onFailTarget = null;
 
         #endregion Private Instance Fields
 
@@ -92,6 +93,16 @@
         }
 
         /// <summary>
+        /// The name of the target to call in case of this task failure.
+        /// </summary>
+        [TaskAttribute("onfail", Required=false)]
+        public string OnFailTarget {
+            get { return _onFailTarget; }
+            set { _onFailTarget = value; }
+        }
+
+
+        /// <summary>
         /// The name of the task.
         /// </summary>
         public override string Name {
@@ -138,8 +149,27 @@
                         "{0} Generated Exception", 
                         Name), e);
 
+                    try {
+                        if (OnFailTarget != null) {
+                            Log(Level.Warning, LogPrefix + string.Format(CultureInfo.InvariantCulture,
+                                " Task failed, calling onfail target '{0}'", OnFailTarget));
+                            
+                            Target t = Project.Targets.Find(OnFailTarget);
+                            if (t == null) {
+                                // if we can't find it, then neither should Project.Execute.
+                                // Let them do the error handling and exception generation.
+                                Project.Execute(OnFailTarget);
+                            }
+
+                            //Execute a copy.
+                            t.Copy().Execute();
+                        }
+                    } catch (Exception onFailException) {
+                        e = new MultipleException(e, onFailException);
+                    }
+
                     if (FailOnError) {
-                        throw;
+                        throw e;
                     } else {
                         if (this.Verbose) {
                             Log(Level.Error, LogPrefix + e.ToString());
@@ -234,4 +264,4 @@
 
         #endregion Protected Instance Methods
     }
-}
+}


==== //depot/home/tarasov/nant/tests/NAnt.Core/TargetTest.cs#2 (text) ====


@@ -35,8 +35,9 @@
             <project default='Target1'>
                 <property name='a' value='{0}'/>
                 <property name='b' value='{1}'/>
+                <property name='c' value='{2}'/>
 
-                <target name='Target1' depends='Target2 Target3'>
+                <target name='Target1' depends='Target2 Target3 Target4'>
                     <echo message='Target1 executed'/>
                 </target>
                 <target name='Target2' if='${{a}}'>
@@ -45,12 +46,21 @@
                 <target name='Target3' unless='${{b}}' depends='Target1'> <!-- check for infinite loop -->
                     <echo message='Target3 executed'/>
                 </target>
+                <target name='Target4' if='${{c}}'>
+                    <call target='failingTarget' failonerror='false'/>
+                </target>
+                <target name='failingTarget' onfail='onFailTarget'>
+                    <fail/>
+                </target>
+                <target name='onFailTarget'>
+                    <echo message='onFailTarget executed'/>
+                </target>
             </project>";
 
 
        [Test]
         public void Test_Normal() {
-            string result = RunBuild(FormatBuildFile("false", "true"));
+            string result = RunBuild(FormatBuildFile("false", "true", "false"));
             Assertion.Assert("Target1 should have executed.\n" + result, result.IndexOf("Target1 executed") != -1);
             Assertion.Assert("Target2 should not have executed.\n" + result, result.IndexOf("Target2 executed") == -1);
             Assertion.Assert("Target3 should not have executed.\n" + result, result.IndexOf("Target3 executed") == -1);
@@ -58,7 +68,7 @@
 
        [Test]
         public void Test_If() {
-            string result = RunBuild(FormatBuildFile("true", "true"));
+            string result = RunBuild(FormatBuildFile("true", "true", "false"));
             Assertion.Assert("Target1 should have executed.\n" + result, result.IndexOf("Target1 executed") != -1);
             Assertion.Assert("Target2 should have executed.\n" + result, result.IndexOf("Target2 executed") != -1);
             Assertion.Assert("Target3 should not have executed.\n" + result, result.IndexOf("Target3 executed") == -1);
@@ -66,7 +76,7 @@
 
        [Test]
         public void Test_Unless() {
-            string result = RunBuild(FormatBuildFile("false", "false"));
+            string result = RunBuild(FormatBuildFile("false", "false", "false"));
             Assertion.Assert("Target1 should have executed.\n" + result, result.IndexOf("Target1 executed") != -1);
             Assertion.Assert("Target2 should not have executed.\n" + result, result.IndexOf("Target2 executed") == -1);
             Assertion.Assert("Target3 should have executed.\n" + result, result.IndexOf("Target3 executed") != -1);
@@ -74,14 +84,23 @@
 
        [Test]
         public void Test_Depends() {
-            string result = RunBuild(FormatBuildFile("true", "false"));
+            string result = RunBuild(FormatBuildFile("true", "false", "false"));
             Assertion.Assert("Target1 should have executed.\n" + result, result.IndexOf("Target1 executed") != -1);
             Assertion.Assert("Target2 should have executed.\n" + result, result.IndexOf("Target2 executed") != -1);
             Assertion.Assert("Target3 should have executed.\n" + result, result.IndexOf("Target3 executed") != -1);
         }
 
-        private string FormatBuildFile(string a, string b) {
-            return String.Format(CultureInfo.InvariantCulture, _format, a, b);
+        [Test]
+        public void Test_OnFail() {
+            string result = RunBuild(FormatBuildFile("false", "true", "true"));
+            Assertion.Assert("Target1 should have executed.\n" + result, result.IndexOf("Target1 executed") != -1);
+            Assertion.Assert("Target2 should not have executed.\n" + result, result.IndexOf("Target2 executed") == -1);
+            Assertion.Assert("Target3 should not have executed.\n" + result, result.IndexOf("Target3 executed") == -1);

+            Assertion.Assert("onFailTarget should have executed.\n" + result, result.IndexOf("onFailTarget executed") != -1);
+        }
+
+        private string FormatBuildFile(string a, string b, string c) {
+            return String.Format(CultureInfo.InvariantCulture, _format, a, b, c);
         }
     }
 }


==== //depot/home/tarasov/nant/tests/NAnt.Core/TaskTest.cs#2 (text) ====


@@ -36,6 +36,7 @@
     [TaskName("test")]
     class TestTask : Task {
         bool _fail = false;
+        string _name = null;
 
         [TaskAttribute("fail", Required=false)]
         [BooleanValidator()]
@@ -44,13 +45,27 @@
             set { _fail = value; }
         }
 
+        [TaskAttribute("customname", Required=false)]
+        public string CustomName {
+            get { return _name; }
+            set { _name = value; }
+        }
+
         protected override void ExecuteTask() {
-            Log(Level.Info, LogPrefix + "TestTask executed");
+            Log(Level.Info, LogPrefix + GetTaskName() + " executed");
             Log(Level.Verbose, LogPrefix + "Verbose message");
             if (Fail) {
-                throw new BuildException("TestTask failed");
+                throw new BuildException(GetTaskName() + " failed");
             }
         }
+        
+        private string GetTaskName()
+        {
+            string result = "TestTask";
+            if (CustomName != null)
+                result += "[" + CustomName + "]";
+            return result;
+        }
     }
 
    [TestFixture]
@@ -62,6 +77,9 @@
                 <target name='test'>
                     <test {1}/>
                 </target>
+                <target name='onFailTarget'>
+                    <test customname='onFail' />
+                </target>
             </project>";
 
        [SetUp]
@@ -117,6 +135,19 @@
             Assertion.Assert("Task should have executed.\n" + result, result.IndexOf("TestTask executed") != -1);
         }
 
+        [Test]
+        public void Test_OnFail() {
+            string result = RunBuild(FormatBuildFile("name='FailingTask' fail='true' onfail='onFailTarget' failonerror='false'"));
+            Assertion.Assert("'onFailTarget' should have executed.\n" + result, result.IndexOf("TestTask[onFail] executed") != -1);
+
+            try {
+                result = RunBuild(FormatBuildFile("customname='FailingTask' fail='true' onfail='onFailTarget'"));
+                Assertion.Fail("Build should have failed.\n" + result);
+            } catch (TestBuildException) {
+                Assertion.Assert("'onFailTarget' should have executed.\n" + result, result.IndexOf("TestTask[onFail] executed") != -1);
+            }
+        }
+
 /*
         public void Test_UnknownAttribute() {
             try {


