// 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 (gerry_shaw@yahoo.com)
using NAnt.Core.Attributes;
using NAnt.Core.Util;
namespace NAnt.Core.Tasks {
///
/// Calls a NAnt target in the current project.
///
///
///
/// When the is used to execute a target, both that
/// target and all its dependent targets will be re-executed.
///
///
/// To avoid dependent targets from being executed more than once, two
/// options are available:
///
///
/// -
///
/// Add an "unless" attribute with value "${target::has-executed('<target name>')}"
/// to the dependent targets.
///
///
/// -
///
/// Set the attribute on the
/// to (recommended).
///
///
///
///
///
///
/// Call the target "build".
///
///
///
/// ]]>
///
///
///
///
/// This shows how a project could 'compile' a debug and release build
/// using a common compile target.
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
/// ]]>
///
///
/// The parameter of the
/// defaults to ,
/// causing the "init" target to be executed for both
/// the "debug" and "release" build.
///
///
/// This results in the following build log:
///
///
/// build:
///
/// init:
///
/// [echo] initializing
///
/// compile:
///
/// [echo] compiling with debug = false
///
/// init:
///
/// [echo] initializing
///
/// compile:
///
/// [echo] compiling with debug = true
///
/// BUILD SUCCEEDED
///
///
/// If the "init" should only be executed once, set the
/// attribute of the
/// to .
///
///
/// The build log would then look like this:
///
///
/// build:
///
/// init:
///
/// [echo] initializing
///
/// compile:
///
/// [echo] compiling with debug = false
///
/// compile:
///
/// [echo] compiling with debug = true
///
/// BUILD SUCCEEDED
///
///
[TaskName("call")]
public class CallTask : Task {
#region Private Instance Fields
private string _target;
private bool _force;
private bool _cascade = true;
#endregion Private Instance Fields
#region Public Instance Properties
///
/// NAnt target to call.
///
[TaskAttribute("target", Required=true)]
[StringValidator(AllowEmpty=false)]
public string TargetName {
get { return _target; }
set { _target = StringUtils.ConvertEmptyToNull(value); }
}
// TO-DO : remove this property after NAnt 0.8.6 or so.
///
/// Force an execute even if the target has already been executed. The
/// default is .
///
[TaskAttribute("force")]
[System.Obsolete("Use the \"cascase\" attribute to control whether dependencies should be re-executed.", false)]
public bool ForceExecute {
get { return _force; }
set { _force = value; }
}
///
/// Execute the specified targets dependencies -- even if they have been
/// previously executed. The default is .
///
[TaskAttribute("cascade")]
public bool CascadeDependencies {
get { return _cascade; }
set { _cascade = value; }
}
#endregion Public Instance Properties
#region Override implementation of Task
///
/// Executes the specified target.
///
protected override void ExecuteTask() {
Target owningTarget = Parent as Target;
if (owningTarget != null) {
// topologically sorted list of targets that will be executed
TargetCollection targets = Project.TopologicalTargetSort(TargetName, Project.Targets);
// check if owning target is part of list of targets that will
// be executed again
if (targets.Find(owningTarget.Name) != null) {
// check if owning target is actually a dependency of the
// target that should be executed
if (targets.IndexOf(targets.Find(owningTarget.Name)) < targets.IndexOf(targets.Find(TargetName))) {
throw new BuildException("Circular dependency: " + targets.ToString(" <- ") + " <- " + owningTarget.Name);
}
}
}
Project.Execute(TargetName, CascadeDependencies);
}
///
/// Makes sure the is not calling its own
/// parent.
///
/// The task XML node.
protected override void InitializeTask(System.Xml.XmlNode taskNode) {
Target target = Project.Targets.Find(TargetName);
if (target != null) {
Target owningTarget = Parent as Target;
if (target == owningTarget) {
throw new BuildException("Call task cannot call its own parent.",
Location);
}
}
}
#endregion Override implementation of Task
}
}