// 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 } }