-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
RemoveDir.cs
182 lines (160 loc) · 7.65 KB
/
RemoveDir.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Shared.FileSystem;
using Microsoft.Build.Utilities;
#nullable disable
namespace Microsoft.Build.Tasks
{
/// <summary>
/// Remove the specified directories.
/// </summary>
public class RemoveDir : TaskExtension, IIncrementalTask
{
//-----------------------------------------------------------------------------------
// Property: directory to remove
//-----------------------------------------------------------------------------------
private ITaskItem[] _directories;
[Required]
public ITaskItem[] Directories
{
get
{
ErrorUtilities.VerifyThrowArgumentNull(_directories, nameof(Directories));
return _directories;
}
set => _directories = value;
}
//-----------------------------------------------------------------------------------
// Property: list of directories that were removed from disk
//-----------------------------------------------------------------------------------
[Output]
public ITaskItem[] RemovedDirectories { get; set; }
public bool FailIfNotIncremental { get; set; }
//-----------------------------------------------------------------------------------
// Execute -- this runs the task
//-----------------------------------------------------------------------------------
public override bool Execute()
{
// Our record of the directories that were removed
var removedDirectoriesList = new List<ITaskItem>();
foreach (ITaskItem directory in Directories)
{
if (string.IsNullOrEmpty(directory.ItemSpec))
{
// Skip any empty ItemSpecs, otherwise RemoveDir will wipe the root of the current drive (!).
// https://github.com/dotnet/msbuild/issues/7563
Log.LogWarningWithCodeFromResources("RemoveDir.EmptyPath");
continue;
}
if (FileSystems.Default.DirectoryExists(directory.ItemSpec))
{
if (FailIfNotIncremental)
{
Log.LogErrorFromResources("RemoveDir.Removing", directory.ItemSpec);
continue;
}
// Do not log a fake command line as well, as it's superfluous, and also potentially expensive
Log.LogMessageFromResources(MessageImportance.Normal, "RemoveDir.Removing", directory.ItemSpec);
// Try to remove the directory, this will not log unauthorized access errors since
// we will attempt to remove read only attributes and try again.
bool currentSuccess = RemoveDirectory(directory, false, out bool unauthorizedAccess);
// The first attempt failed, to we will remove readonly attributes and try again..
if (!currentSuccess && unauthorizedAccess)
{
// If the directory delete operation returns an unauthorized access exception
// we need to attempt to remove the readonly attributes and try again.
currentSuccess = RemoveReadOnlyAttributeRecursively(new DirectoryInfo(directory.ItemSpec));
if (currentSuccess)
{
// Retry the remove directory operation, this time we want to log any errors
currentSuccess = RemoveDirectory(directory, true, out unauthorizedAccess);
}
}
// We successfully removed the directory, so add the removed directory to our record
if (currentSuccess)
{
// keep a running list of the directories that were actually removed
// note that we include in this list directories that did not exist
removedDirectoriesList.Add(new TaskItem(directory));
}
}
else
{
Log.LogMessageFromResources(MessageImportance.Normal, "RemoveDir.SkippingNonexistentDirectory", directory.ItemSpec);
// keep a running list of the directories that were actually removed
// note that we include in this list directories that did not exist
removedDirectoriesList.Add(new TaskItem(directory));
}
}
// convert the list of deleted files into an array of ITaskItems
RemovedDirectories = removedDirectoriesList.ToArray();
return !Log.HasLoggedErrors;
}
// Core implementation of directory removal
private bool RemoveDirectory(ITaskItem directory, bool logUnauthorizedError, out bool unauthorizedAccess)
{
bool success = true;
unauthorizedAccess = false;
try
{
// Try to delete the directory
Directory.Delete(directory.ItemSpec, true);
}
catch (UnauthorizedAccessException e)
{
success = false;
// Log the fact that there was a problem only if we have been asked to.
if (logUnauthorizedError)
{
Log.LogErrorWithCodeFromResources("RemoveDir.Error", directory, e.Message);
}
unauthorizedAccess = true;
}
catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))
{
Log.LogErrorWithCodeFromResources("RemoveDir.Error", directory.ItemSpec, e.Message);
success = false;
}
return success;
}
// recursively remove RO attribs from all files
private bool RemoveReadOnlyAttributeRecursively(DirectoryInfo directory)
{
bool success = true;
try
{
// Remove the ReadOnly attribute from the directory if it is present
if ((directory.Attributes & FileAttributes.ReadOnly) != 0)
{
FileAttributes faNew = (directory.Attributes & ~FileAttributes.ReadOnly);
directory.Attributes = faNew;
}
// For each file in the directory remove the readonly attribute if it is present
foreach (FileSystemInfo file in directory.GetFileSystemInfos())
{
if ((file.Attributes & FileAttributes.ReadOnly) != 0)
{
FileAttributes faNew = (file.Attributes & ~FileAttributes.ReadOnly);
file.Attributes = faNew;
}
}
// Recursively call ourselves for sub-directories
foreach (DirectoryInfo folder in directory.GetDirectories())
{
success = RemoveReadOnlyAttributeRecursively(folder);
}
}
catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))
{
Log.LogErrorWithCodeFromResources("RemoveDir.Error", directory, e.Message);
success = false;
}
return success;
}
}
}