Hello nant-developers, I want to discuss some ideas I have on failures which occur during the build process. I've been desperately trying to solve the problems which I'm going to address in this letter using the standard features of NAnt and I've found that the ways by which I can solve these problems are very far from being nice and reasonable.
Consider such a problem (it is a simplified version of a problem I had): I have a build file for quite a big project, which consists of several subprojects. For simplicity, lets consider that these subprojects are independent and built in some fixed order. To be more general, I have a bunch of targets, which i call using the <call> task: <target name="A" /> <target name="B" /> <target name="C" /> ... <target name="build"> <call target="A" /> <call target="B" /> <call target="C" /> ... <call target="send.mail.on.successful.build" /> </target> The problem is, that I only get mail on successful build. And the bigger problem is, that when some of the A,B,C targets fails and I don't get any mail, I don't know, which target has the problem (of course, unless I do take a look at the log). To be more specific, I also want that different mail messages were sent to the different mail addresses in case of different targets failures (sorry for a bit awkward explanation). Of course, in <call> I can add failonerror="false", and then after each call check some property which is set in the end of the previously called target, but it is a really ugly hack! I'm still not sure, if it was the only way of doing failure notification, but it was the only one I could guess. I've had some time thinking this over, and came to a conclusion that some kind of "exception handling" similar to the C# kind is needed. I've inspected the .Core source code (thanks for the great code, it's very easy to read it!), and decided to make a small patch. My idea of solution of the problem was the following: we add the onfail attribute to <task> (I mean the base class here) and <target>, and, in case of failure, the target which has the name of the value of the onfail attribute is called. Surely enough, I don't want only to send the mail in case of unsuccessful build, I had some other things to do :-) There are some tricky parts in such a solution: - how do we handle the failure of the task, which was called "onfail" - getting the failure info in the "onfail" task (for example, to send part of the build log to the "subscribed" developers) - probably, some other I don't remember now... The first one is "solved", but I don't like the way I did it: I've created host for multiple exceptions, so the data of all can be retrived. I wanted to bind the exceptions by adding the exception from failed task/target as a constructor parameter for the second exception, but it is impossible, because target (in the meaning of class) does not know, that it was called "by reason of the exception". I have some thoughts on the second one (it is not solved in any way), but it can demand quite a serious design ideas/changes. Again, I don't have the stories, which describe how I want to see such a feature. Still I'm sure it can be very useful and needs lots of opinions to base on. I've implemented the tests first, the do pass and are included into the patch. The patch is in the attach, but it is in quite a strange format (I've ripped the parts of the changes history from Perforce). Sorry for this inconvinience. Also the new file (with the strange exception implementation) is in the attach. I'm ready to discuss any ideas of what was done wrong and how it should be done. I'm ready to take part in the development of this feature if it will be redesigned. -- Best regards, Ivan mailto:[EMAIL PROTECTED]
onFail.diff
Description: Binary data
// NAnt - A .NET build tool // Copyright (C) 2001-2002 Gerry Shaw // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // Gerry Shaw ([EMAIL PROTECTED]) // Mike Krueger ([EMAIL PROTECTED]) // Ian MacLean ([EMAIL PROTECTED]) // William E. Caputo ([EMAIL PROTECTED] | [EMAIL PROTECTED]) // Gert Driesen ([EMAIL PROTECTED]) // // Ivan Y. Tarasov ([EMAIL PROTECTED]) using System; using System.Globalization; using System.Runtime.Serialization; using System.Security.Permissions; namespace NAnt.Core { [Serializable] public class MultipleException : ApplicationException { private Exception[] _exceptions = null; public Exception[] Exceptions { get { return _exceptions; } } public Exception this[int index] { get { return Exceptions[index]; } } public MultipleException() : base() { } public MultipleException(string message) : base(message) { } public MultipleException(string message, Exception e) : base(message, e) { } protected MultipleException(SerializationInfo info, StreamingContext context) : base(info, context) { } public MultipleException(params Exception[] exceptions) : base("") { _exceptions = exceptions; } [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)] public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); if (Exceptions != null) { foreach (Exception e in Exceptions) { e.GetObjectData(info, context); } } } public override string Message { get { string result = base.Message; if (Exceptions != null) { foreach (Exception e in Exceptions) { result += e.ToString(); } } return result; } } public override string ToString() { return string.Format(CultureInfo.InvariantCulture,"{0}:{1}{2}", Message, Environment.NewLine, base.ToString()); } } }