// NAnt - A .NET build tool
// Copyright (C) 2001-2003 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
//
// Ian MacLean (ian@maclean.ms)
// Gerry Shaw (gerry_shaw@yahoo.com)
// Scott Hernandez (ScottHernandez@_yeah_not_really_@hotmail.com)
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Xml;
using NAnt.Core.Attributes;
using NAnt.Core.Util;
namespace NAnt.Core.Tasks {
///
/// Includes an external build file.
///
///
///
/// This task is used to break your build file into smaller chunks. You
/// can load a partial build file and have it included into the build file.
///
///
/// Any global (project level) tasks in the included build file are executed
/// when this task is executed. Tasks in target elements are only executed
/// if that target is executed.
///
///
/// The project element attributes are ignored.
///
///
/// This task can only be in the global (project level) section of the
/// build file.
///
///
/// This task can only include files from the file system.
///
///
///
///
/// Include a task that fetches the project version from the
/// GetProjectVersion.include build file.
///
///
///
/// ]]>
///
///
[TaskName("include")]
public class IncludeTask : Task {
#region Private Instance Fields
private string _buildFileName;
#endregion Private Instance Fields
#region Private Static Fields
///
/// Used to check for recursived includes.
///
private static Stack _includedFileNames = new Stack();
private static string _currentBasedir = "";
private static int _nestinglevel = 0;
#endregion Private Static Fields
#region Public Instance Properties
///
/// Build file to include.
///
[TaskAttribute("buildfile", Required=true)]
[StringValidator(AllowEmpty=false)]
public string BuildFileName {
get { return _buildFileName; }
set { _buildFileName = value; }
}
#endregion Public Instance Properties
#region Override implementation of Task
///
/// Verifies parameters.
///
/// Xml taskNode used to define this task instance.
protected override void InitializeTask(XmlNode taskNode) {
// Task can only be included as a global task.
// This might not be a firm requirement but you could get some real
// funky errors if you start including targets wily-nily.
if (Parent != null && !(Parent is Project)) {
throw new BuildException(ResourceUtils.GetString("NA1180"), Location);
}
if (StringUtils.IsNullOrEmpty(_currentBasedir) || _nestinglevel == 0) {
_currentBasedir = Project.BaseDirectory;
}
string buildFileName = null;
try {
// check if build file is valid file name
buildFileName = Path.GetFullPath(Path.Combine(_currentBasedir, BuildFileName));
} catch (Exception ex) {
throw new BuildException(string.Format(CultureInfo.InvariantCulture,
ResourceUtils.GetString("NA1128"), BuildFileName),
Location, ex);
}
// check for recursive includes
foreach (string currentFileName in _includedFileNames) {
if (currentFileName == buildFileName) {
throw new BuildException(ResourceUtils.GetString("NA1179"), Location);
}
}
}
protected override void ExecuteTask() {
string includedFileName = Path.GetFullPath(Path.Combine(_currentBasedir,
BuildFileName));
// check if build file exists
if (!File.Exists(includedFileName)) {
throw new BuildException(string.Format(CultureInfo.InvariantCulture,
ResourceUtils.GetString("NA1127"), includedFileName), Location);
}
// check if file has already been mapped
if (Project.LocationMap.FileIsMapped(includedFileName)) {
Log(Level.Verbose, ResourceUtils.GetString("String_DuplicateInclude"), includedFileName);
return;
}
// push ourselves onto the stack (prevents recursive includes)
_includedFileNames.Push(includedFileName);
// increment the nesting level
_nestinglevel ++;
Log(Level.Verbose, "Including file {0}.", includedFileName);
// store original base directory
string oldBaseDir = _currentBasedir;
// set basedir to be used by the nested calls (if any)
_currentBasedir = Path.GetDirectoryName(includedFileName);
try {
XmlDocument doc = new XmlDocument();
doc.Load(includedFileName);
Project.InitializeProjectDocument(doc);
} catch (BuildException) {
// rethrow exception
throw;
} catch (Exception ex) {
throw new BuildException(string.Format(CultureInfo.InvariantCulture,
ResourceUtils.GetString("NA1128"), includedFileName),
Location, ex);
} finally {
// pop off the stack
_includedFileNames.Pop();
// decrease the nesting level
_nestinglevel--;
// restore original base directory
_currentBasedir = oldBaseDir;
}
}
#endregion Override implementation of Task
}
}