Hi, just joined this list because I'm planning on transitioning our current build process (which is a big mess of shell scripts under cygwin) over to NAnt. Thus far I like it a lot, however there was one critical thing which NAnt didn't do, and which I couldn't find anything indicating how I might do it - which is to continue the build upon project failure (after running failure actions such as emailing), rather than just dying on the spot.
As such I fixed it myself (score +10 for Open Source)... If anyone else has already done this or knows of a more 'normal' means, can you please let me know, otherwise here's some patches for 0.85-rc2. With these if you set the nant.resumeonfailure property to anything (true is nice), then Nant will report a project fail, run the nant.onfailure action, but then pick up where it left off. It will not rebuild projects which have previously failed, and if any projects have a dependancy which has previously failed then they'll be skipped too. I'm kind of expecting someone to come back and say 'no we don't want that, go away', but if by some small chance someone wants to include it, if I have to jump through any hoops before submitting patches (signing agreements/etc) then could you point me in the right direction? Thanks for the great product and for your time, Orion.
--- Project.cs 2005-01-24 03:42:28.000000000 +-1300 +++ Project.cs 2005-02-21 21:22:48.000000000 +-1300 @@ -105,12 +105,13 @@ internal const string NAntPropertyProjectName = "nant.project.name"; internal const string NAntPropertyProjectBuildFile = "nant.project.buildfile"; internal const string NAntPropertyProjectBaseDir = "nant.project.basedir"; internal const string NAntPropertyProjectDefault = "nant.project.default"; internal const string NAntPropertyOnSuccess = "nant.onsuccess"; internal const string NAntPropertyOnFailure = "nant.onfailure"; + internal const string NAntPropertyResumeOnFailure = "nant.resumeonfailure"; #endregion Internal Static Fields #region Public Instance Events public event BuildEventHandler BuildStarted; @@ -875,20 +876,35 @@ Target callingTarget = _currentTarget; do { // determine target that should be executed currentTarget = (Target) sortedTargets[currentIndex++]; + if(currentTarget.ExecuteFailed) + { + Log(Level.Info,"Skipping Project {0} because Dependancy {1} failed", + targetName,currentTarget.Name); + break; + } + // store target that will be executed _currentTarget = currentTarget; // only execute targets that have not been executed already, if // we are not forcing. if (forceDependencies || !currentTarget.Executed) { currentTarget.Execute(); } + + if(currentTarget.ExecuteFailed) + { + Log(Level.Info,"Skipping Project {0} because Dependancy {1} failed", + targetName,currentTarget.Name); + break; + } + } while (!currentTarget.Name.Equals(targetName)); // restore calling target, as a <call> task might have caused the // current target to be executed and when finished executing this // target, the target that contained the <call> task should be // considered the current target again
--- Target.cs 2004-10-31 05:15:36.000000000 +-1300 +++ Target.cs 2005-02-23 09:43:13.000000000 +-1300 @@ -36,12 +36,13 @@ private string _name = null; private string _description = null; private string _ifCondition = null; private string _unlessCondition = null; private StringCollection _dependencies = new StringCollection(); private bool _executed = false; + private bool _executefailed = false; #endregion Private Instance Fields #region Public Instance Constructors /// <summary> @@ -57,14 +58,22 @@ /// <summary> /// This indicates whether the target has already executed. /// </summary> public bool Executed { get { return _executed; - } - } + } + } + + public bool ExecuteFailed + { + get + { + return _executefailed; + } + } /// <summary> /// The name of the target. /// </summary> /// <remarks> /// <para> @@ -260,16 +269,29 @@ } else { throw new BuildException(string.Format(CultureInfo.InvariantCulture, "Invalid element <{0}>. Unknown task or datatype.", childNode.Name), Project.LocationMap.GetLocation(childNode)); } } - } finally { + } catch(BuildException ex) { + if(Project.Properties[Project.NAntPropertyResumeOnFailure] == null) + { + this._executefailed = true; + throw ex; + } + else + { + Log(Level.Warning,"TARGET FAILED: {0}\n{1}",_name,ex.RawMessage); + this._executefailed = true; + Project.Execute(Project.Properties[Project.NAntPropertyOnFailure]); + //keep going + } + } finally { _executed = true; Project.OnTargetFinished(this, new BuildEventArgs(this)); } } } #endregion Public Instance Methods } }