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]

Attachment: 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());
        }
    }
}


Reply via email to