-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStringReplaceTask.cs
158 lines (142 loc) · 6.03 KB
/
StringReplaceTask.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
// (c) Copyright Microsoft Corporation.
// This source is subject to [###LICENSE_NAME###].
// Please see [###LICENSE_LINK###] for details.
// All other rights reserved.
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Text.RegularExpressions;
namespace Build.Tasks
{
/// <summary>
/// Replace strings inside of a set of files, updating their contents. The
/// task operates outside the realm of source control and should typically
/// be used on a copy of sources, instead of an enlistment's source, to
/// prevent overwriting and checking in the modified files.
/// </summary>
public partial class StringReplaceTask : Task
{
/// <summary>
/// Gets or sets the files to search within.
/// </summary>
[Required]
public ITaskItem[] SourceFiles { get; set; }
/// <summary>
/// Gets or sets the token text that is searched for using an exact
/// ordinal match.
/// </summary>
[Required]
public string TokenText { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the token is considered
/// to be a regular expression.
/// </summary>
/// <value><c>True</c> if token is a regular expression; otherwise, <c>false</c>.</value>
public bool TokenIsRegularExpression { get; set; }
/// <summary>
/// Gets or sets the replacement text.
/// </summary>
public string ReplacementText { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to write files even if
/// they are marked as read only files.
/// </summary>
public bool OverwriteReadOnlyFiles { get; set; }
/// <summary>
/// Initializes a new instance of the StringReplaceTask class.
/// </summary>
public StringReplaceTask()
{
}
/// <summary>
/// Retrieve the encoding for a file by reading its contents and
/// returning the final Encoding that the stream reader has.
/// </summary>
/// <param name="sourcePath">The source file.</param>
/// <returns>Returns the encoding.</returns>
internal static Encoding GetFileEncoding(string sourcePath)
{
Encoding encoding = Encoding.Default;
using (StreamReader reader = new StreamReader(File.Open(sourcePath, FileMode.Open, FileAccess.Read)))
{
reader.ReadLine();
encoding = reader.CurrentEncoding;
}
return encoding;
}
/// <summary>
/// Replace the token string inside of a set of files with a new value.
/// </summary>
/// <returns>A value indicating whether the task succeeded.</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Any exception is an error")]
public override bool Execute()
{
// There's nothing to do if we have no source files
if (SourceFiles == null || SourceFiles.Length == 0)
{
return true;
}
// Process the files
bool succeeded = true;
int updatedFiles = 0;
for (int i = 0; i < SourceFiles.Length; i++)
{
try
{
string sourcePath = SourceFiles[i].ItemSpec;
FileInfo sourceInfo = new FileInfo(sourcePath);
// Make sure they didn't pass a directory as an item
if (Directory.Exists(sourcePath))
{
Log.LogError("Cannot move item {0} because it is a directory!", sourcePath);
succeeded = false;
continue;
}
// Make sure the source exists
if (!sourceInfo.Exists)
{
Log.LogError("Cannot process file {0} that does not exist!", sourcePath);
succeeded = false;
continue;
}
Encoding encoding = GetFileEncoding(sourcePath);
string content = File.ReadAllText(sourcePath);
string newContent = string.Empty;
if (TokenIsRegularExpression)
{
Regex r = new Regex(TokenText);
newContent = r.Replace(content, ReplacementText);
}
else
{
newContent = content.Replace(TokenText, ReplacementText);
}
// Check for a change
if (string.CompareOrdinal(content, newContent) != 0)
{
// Make it possible to overwrite read-only files
if (OverwriteReadOnlyFiles && sourceInfo.IsReadOnly)
{
Log.LogMessage(MessageImportance.Low, "Removing read-only attribute from file {0}.", sourcePath);
sourceInfo.Attributes = FileAttributes.Normal;
}
// Replace and rewrite the file
// Log.LogMessage(MessageImportance.Normal, "Replaced token \"{0}\" with \"{1}\" in \"{2}.", TokenText, ReplacementText, sourcePath);
File.WriteAllText(sourcePath, newContent, encoding);
updatedFiles++;
}
}
catch (Exception ex)
{
Log.LogErrorFromException(ex);
succeeded = false;
}
}
Log.LogMessage(MessageImportance.High, "Replaced all \"{0}\" tokens with \"{1}\" in {2} files.", TokenText, ReplacementText, updatedFiles);
return succeeded;
}
}
}