// 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
//
// Gerry Shaw (gerry_shaw@yahoo.com)
using System;
using System.IO;
using System.Globalization;
using NAnt.Core.Attributes;
using NAnt.Core.Types;
using NAnt.Core.Util;
namespace NAnt.Core.Tasks {
///
/// Deletes a file, fileset or directory.
///
///
///
/// Deletes either a single file, all files in a specified directory and
/// its sub-directories, or a set of files specified by one or more filesets.
///
///
/// If the or attribute is
/// set then the fileset contents will be ignored. To delete the files
/// in the fileset ommit the and
/// attributes in the <delete> element.
///
///
/// If the specified file or directory does not exist, no error is
/// reported.
///
///
/// Read-only files cannot be deleted. Use the
/// first to remove the read-only attribute.
///
///
///
/// Delete a single file.
///
///
/// ]]>
///
///
///
///
/// Delete a directory and the contents within. If the directory does not
/// exist, no error is reported.
///
///
///
/// ]]>
///
///
///
///
/// Delete a set of files.
///
///
///
///
///
///
///
///
/// ]]>
///
///
[TaskName("delete")]
public class DeleteTask : Task {
#region Private Instance Fields
private FileInfo _file;
private DirectoryInfo _dir;
private FileSet _fileset = new FileSet();
private bool _includeEmptyDirs = true;
#endregion Private Instance Fields
#region Public Instance Properties
///
/// The file to delete.
///
[TaskAttribute("file")]
public FileInfo File {
get { return _file; }
set { _file = value; }
}
///
/// The directory to delete.
///
[TaskAttribute("dir")]
public DirectoryInfo Directory {
get { return _dir; }
set { _dir = value; }
}
///
/// Remove any empty directories included in the .
/// The default is .
///
[TaskAttribute("includeemptydirs")]
[BooleanValidator()]
public bool IncludeEmptyDirs {
get { return _includeEmptyDirs; }
set { _includeEmptyDirs = value; }
}
///
/// All the files in the file set will be deleted.
///
[BuildElement("fileset")]
public FileSet DeleteFileSet {
get { return _fileset; }
set { _fileset = value; }
}
#endregion Public Instance Properties
#region Override implementation of Task
///
/// Controls whether to show the name of each deleted file or directory.
/// The default is .
///
[TaskAttribute("verbose")]
[BooleanValidator()]
public override bool Verbose {
get { return base.Verbose; }
set { base.Verbose = value; }
}
///
/// Ensures the supplied attributes are valid.
///
/// Xml node used to define this task instance.
protected override void InitializeTask(System.Xml.XmlNode taskNode) {
// limit task to deleting either a file, directory
if (File != null && Directory != null) {
throw new BuildException("Cannot specify both 'file' and 'dir'"
+ " attribute in the same task.", Location);
}
// limit task to deleting either a file/directory or fileset
if ((File != null || Directory != null) && DeleteFileSet.Includes.Count != 0) {
throw new BuildException("Cannot specify both 'file' or 'dir'"
+ " attribute and use in the same task.",
Location);
}
}
protected override void ExecuteTask() {
// ensure base directory is set, even if fileset was not initialized
// from XML
if (DeleteFileSet.BaseDirectory == null) {
DeleteFileSet.BaseDirectory = new DirectoryInfo(Project.BaseDirectory);
}
if (File != null) { // delete a single file
// delete the file in verbose mode
DeleteFile(File.FullName, true);
} else if (Directory != null) { // delete the directory
// explicitly check if directory exist here (while the same
// check is performed in RecursiveDeleteDirectory), to avoid
// output of Info message below
if (!Directory.Exists) {
return;
}
// log message here if we're not in verbose mode, otherwise the
// RecursiveDeleteDirectory method will output verbose message
// for each directory that is removed
if (!Verbose) {
Log(Level.Info, "Deleting directory '{0}'.", Directory.FullName);
}
RecursiveDeleteDirectory(Directory.FullName);
} else { // delete files or directories in fileset
if (DeleteFileSet.FileNames.Count > 0) {
Log(Level.Info, "Deleting {0} files.", DeleteFileSet.FileNames.Count);
foreach (string path in DeleteFileSet.FileNames) {
DeleteFile(path, Verbose);
}
}
if (DeleteFileSet.DirectoryNames.Count > 0 && IncludeEmptyDirs) {
int dirCount = 0;
foreach (string path in DeleteFileSet.DirectoryNames) {
string[] entries = System.IO.Directory.GetFileSystemEntries(path);
if (entries == null || entries.Length == 0) {
try {
DeleteDirectory(path);
dirCount++;
} catch (Exception ex) {
string msg = string.Format(CultureInfo.InvariantCulture,
ResourceUtils.GetString("NA1113"), path);
if (FailOnError) {
throw new BuildException(msg, Location, ex);
}
Log(Level.Warning, msg + " " + ex.Message);
}
}
}
if (dirCount > 0) {
Log(Level.Info, "Deleted {0} directories.", dirCount);
}
}
}
}
#endregion Override implementation of Task
#region Private Instance Methods
private void RecursiveDeleteDirectory(string path) {
try {
// skip the directory if it doesn't exist
if (!System.IO.Directory.Exists(path)) {
return;
}
// first, recursively delete all directories in the directory
string[] dirs = System.IO.Directory.GetDirectories(path);
foreach (string dir in dirs) {
RecursiveDeleteDirectory(dir);
}
// next, delete all files in the directory
string[] files = System.IO.Directory.GetFiles(path);
foreach (string file in files) {
try {
System.IO.File.SetAttributes(file, FileAttributes.Normal);
Log(Level.Verbose, "Deleting file '{0}'.", file);
System.IO.File.Delete(file);
} catch (Exception ex) {
string msg = string.Format(CultureInfo.InvariantCulture,
ResourceUtils.GetString("NA1114"), file);
if (FailOnError) {
throw new BuildException(msg, Location, ex);
}
Log(Level.Verbose, msg + " " + ex.Message);
}
}
DeleteDirectory(path);
} catch (BuildException ex) {
throw ex;
} catch (Exception ex) {
string msg = string.Format(CultureInfo.InvariantCulture,
ResourceUtils.GetString("NA1113"), path);
if (FailOnError) {
throw new BuildException(msg, Location, ex);
}
Log(Level.Warning, msg + " " + ex.Message);
}
}
private void DeleteFile(string path, bool verbose) {
try {
FileInfo deleteInfo = new FileInfo(path);
if (!deleteInfo.Exists) {
return;
}
if (verbose) {
Log(Level.Info, "Deleting file {0}.", path);
}
if (deleteInfo.Attributes != FileAttributes.Normal) {
System.IO.File.SetAttributes(deleteInfo.FullName,
FileAttributes.Normal);
}
System.IO.File.Delete(path);
} catch (Exception ex) {
string msg = string.Format(CultureInfo.InvariantCulture,
ResourceUtils.GetString("NA1114"), path);
if (FailOnError) {
throw new BuildException(msg, Location, ex);
}
Log(Level.Warning, msg + " " + ex.Message);
}
}
private void DeleteDirectory(string path) {
// ensure path is not read-only
System.IO.File.SetAttributes(path, FileAttributes.Normal);
// write output to build log
Log(Level.Verbose, "Deleting directory '{0}'.", path);
// finally, delete the directory
System.IO.Directory.Delete(path);
}
#endregion Private Instance Methods
}
}