aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Converters/ConvertCommand.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Converters/ConvertCommand.cs')
-rw-r--r--src/WixToolset.Converters/ConvertCommand.cs269
1 files changed, 22 insertions, 247 deletions
diff --git a/src/WixToolset.Converters/ConvertCommand.cs b/src/WixToolset.Converters/ConvertCommand.cs
index 51e7b997..50ca7249 100644
--- a/src/WixToolset.Converters/ConvertCommand.cs
+++ b/src/WixToolset.Converters/ConvertCommand.cs
@@ -3,283 +3,58 @@
3namespace WixToolset.Converters 3namespace WixToolset.Converters
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Threading; 6 using System.Threading;
9 using System.Threading.Tasks; 7 using System.Threading.Tasks;
10 using System.Xml;
11 using WixToolset.Extensibility.Data;
12 using WixToolset.Extensibility.Services; 8 using WixToolset.Extensibility.Services;
13 9
14 internal class ConvertCommand : ICommandLineCommand 10 internal class ConvertCommand : FixupCommandBase
15 { 11 {
16 private const string SettingsFileDefault = "wixcop.settings.xml"; 12 private const string SettingsFileDefault = "wix.convert.settings.xml";
17 13
18 public ConvertCommand(IWixToolsetServiceProvider serviceProvider) 14 public ConvertCommand(IWixToolsetServiceProvider serviceProvider)
19 { 15 {
20 this.Messaging = serviceProvider.GetService<IMessaging>(); 16 this.Messaging = serviceProvider.GetService<IMessaging>();
21
22 this.IndentationAmount = 4; // default indentation amount
23 this.ErrorsAsWarnings = new HashSet<string>();
24 this.ExemptFiles = new HashSet<string>();
25 this.IgnoreErrors = new HashSet<string>();
26 this.SearchPatternResults = new HashSet<string>();
27 this.SearchPatterns = new List<string>();
28 } 17 }
29 18
30 private IMessaging Messaging { get; } 19 private IMessaging Messaging { get; }
31 20
32 public bool ShowLogo { get; private set; } 21 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
33
34 public bool StopParsing { get; private set; }
35
36 private bool ShowHelp { get; set; }
37
38 private HashSet<string> ErrorsAsWarnings { get; }
39
40 private HashSet<string> ExemptFiles { get; }
41
42 private bool FixErrors { get; set; }
43
44 private int IndentationAmount { get; set; }
45
46 private HashSet<string> IgnoreErrors { get; }
47
48 private HashSet<string> SearchPatternResults { get; }
49
50 private List<string> SearchPatterns { get; }
51
52 private string SettingsFile1 { get; set; }
53
54 private string SettingsFile2 { get; set; }
55
56 private bool SubDirectories { get; set; }
57
58 public bool TryParseArgument(ICommandLineParser parser, string argument)
59 {
60 if (!parser.IsSwitch(argument))
61 {
62 this.SearchPatterns.Add(argument);
63 return true;
64 }
65
66 var parameter = argument.Substring(1);
67 switch (parameter.ToLowerInvariant())
68 {
69 case "?":
70 this.ShowHelp = true;
71 this.ShowLogo = true;
72 this.StopParsing = true;
73 return true;
74
75 case "f":
76 this.FixErrors = true;
77 return true;
78
79 case "nologo":
80 this.ShowLogo = false;
81 return true;
82
83 case "s":
84 this.SubDirectories = true;
85 return true;
86
87 default: // other parameters
88 if (parameter.StartsWith("set1", StringComparison.Ordinal))
89 {
90 this.SettingsFile1 = parameter.Substring(4);
91 return true;
92 }
93 else if (parameter.StartsWith("set2", StringComparison.Ordinal))
94 {
95 this.SettingsFile2 = parameter.Substring(4);
96 return true;
97 }
98 else if (parameter.StartsWith("indent:", StringComparison.Ordinal))
99 {
100 try
101 {
102 this.IndentationAmount = Convert.ToInt32(parameter.Substring(7));
103 }
104 catch
105 {
106 parser.ErrorArgument = parameter; // $"Invalid numeric argument: {parameter}";
107 }
108 return true;
109 }
110
111 return false;
112 }
113 }
114
115 public Task<int> ExecuteAsync(CancellationToken cancellationToken)
116 { 22 {
117 if (this.ShowHelp) 23 if (this.ShowHelp)
118 { 24 {
119 DisplayHelp(); 25 DisplayHelp();
120 return Task.FromResult(1); 26 return Task.FromResult(-1);
121 }
122
123 // parse the settings if any were specified
124 if (null != this.SettingsFile1 || null != this.SettingsFile2)
125 {
126 this.ParseSettingsFiles(this.SettingsFile1, this.SettingsFile2);
127 }
128 else
129 {
130 if (File.Exists(ConvertCommand.SettingsFileDefault))
131 {
132 this.ParseSettingsFiles(ConvertCommand.SettingsFileDefault, null);
133 }
134 } 27 }
135 28
136 var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); 29 var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors);
137 30
138 var errors = this.InspectSubDirectories(converter, Path.GetFullPath("."), cancellationToken); 31 this.ParseSettings(SettingsFileDefault);
32
33 var errors = base.Inspect(Inspector, cancellationToken);
139 34
140 foreach (var searchPattern in this.SearchPatterns) 35 return Task.FromResult(errors);
36
37 int Inspector(string file, bool fix)
141 { 38 {
142 if (!this.SearchPatternResults.Contains(searchPattern)) 39 return converter.ConvertFile(file, fix);
143 {
144 Console.Error.WriteLine("Could not find file \"{0}\"", searchPattern);
145 errors++;
146 }
147 } 40 }
148
149 return Task.FromResult(errors != 0 ? 2 : 0);
150 } 41 }
151 42
152 private static void DisplayHelp() 43 private static void DisplayHelp()
153 { 44 {
154 Console.WriteLine(" usage: wix.exe convert sourceFile [sourceFile ...]");
155 Console.WriteLine(); 45 Console.WriteLine();
156 Console.WriteLine(" -f fix errors automatically for writable files"); 46 Console.WriteLine("Usage: wix convert [options] sourceFile [sourceFile ...]");
157 Console.WriteLine(" -nologo suppress displaying the logo information");
158 Console.WriteLine(" -s search for matching files in current dir and subdirs");
159 Console.WriteLine(" -set1<file> primary settings file");
160 Console.WriteLine(" -set2<file> secondary settings file (overrides primary)");
161 Console.WriteLine(" -indent:<n> indentation multiple (overrides default of 4)");
162 Console.WriteLine(" -? this help information");
163 Console.WriteLine(); 47 Console.WriteLine();
164 Console.WriteLine(" sourceFile may use wildcards like *.wxs"); 48 Console.WriteLine("Options:");
165 } 49 Console.WriteLine(" -h|--help Show command line help.");
166 50 Console.WriteLine(" --nologo Suppress displaying the logo information.");
167 /// <summary> 51 Console.WriteLine(" -n|--dry-run Only display errors, do not update files.");
168 /// Get the files that match a search path pattern. 52 Console.WriteLine(" -r|--recurse Search for matching files in current dir and subdirs.");
169 /// </summary> 53 Console.WriteLine(" -set1<file> Primary settings file.");
170 /// <param name="baseDir">The base directory at which to begin the search.</param> 54 Console.WriteLine(" -set2<file> Secondary settings file (overrides primary).");
171 /// <param name="searchPath">The search path pattern.</param> 55 Console.WriteLine(" -indent:<n> Indentation multiple (overrides default of 4).");
172 /// <returns>The files matching the pattern.</returns> 56 Console.WriteLine();
173 private static string[] GetFiles(string baseDir, string searchPath) 57 Console.WriteLine(" sourceFile may use wildcards like *.wxs");
174 {
175 // convert alternate directory separators to the standard one
176 var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
177 var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar);
178 string[] files = null;
179
180 try
181 {
182 if (0 > lastSeparator)
183 {
184 files = Directory.GetFiles(baseDir, filePath);
185 }
186 else // found directory separator
187 {
188 var searchPattern = filePath.Substring(lastSeparator + 1);
189
190 files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), searchPattern);
191 }
192 }
193 catch (DirectoryNotFoundException)
194 {
195 // don't let this function throw the DirectoryNotFoundException. (this exception
196 // occurs for non-existant directories and invalid characters in the searchPattern)
197 }
198
199 return files;
200 }
201
202 /// <summary>
203 /// Inspect sub-directories.
204 /// </summary>
205 /// <param name="directory">The directory whose sub-directories will be inspected.</param>
206 /// <returns>The number of errors that were found.</returns>
207 private int InspectSubDirectories(WixConverter converter, string directory, CancellationToken cancellationToken)
208 {
209 var errors = 0;
210
211 foreach (var searchPattern in this.SearchPatterns)
212 {
213 foreach (var sourceFilePath in GetFiles(directory, searchPattern))
214 {
215 cancellationToken.ThrowIfCancellationRequested();
216
217 var file = new FileInfo(sourceFilePath);
218
219 if (!this.ExemptFiles.Contains(file.Name.ToUpperInvariant()))
220 {
221 this.SearchPatternResults.Add(searchPattern);
222 errors += converter.ConvertFile(file.FullName, this.FixErrors);
223 }
224 }
225 }
226
227 if (this.SubDirectories)
228 {
229 foreach (var childDirectoryPath in Directory.GetDirectories(directory))
230 {
231 errors += this.InspectSubDirectories(converter, childDirectoryPath, cancellationToken);
232 }
233 }
234
235 return errors;
236 }
237
238 /// <summary>
239 /// Parse the primary and secondary settings files.
240 /// </summary>
241 /// <param name="localSettingsFile1">The primary settings file.</param>
242 /// <param name="localSettingsFile2">The secondary settings file.</param>
243 private void ParseSettingsFiles(string localSettingsFile1, string localSettingsFile2)
244 {
245 if (null == localSettingsFile1 && null != localSettingsFile2)
246 {
247 throw new ArgumentException("Cannot specify a secondary settings file (set2) without a primary settings file (set1).", nameof(localSettingsFile2));
248 }
249
250 var settingsFile = localSettingsFile1;
251 while (null != settingsFile)
252 {
253 var doc = new XmlDocument();
254 doc.Load(settingsFile);
255
256 // get the types of tests that will have their errors displayed as warnings
257 var testsIgnoredElements = doc.SelectNodes("/Settings/IgnoreErrors/Test");
258 foreach (XmlElement test in testsIgnoredElements)
259 {
260 var key = test.GetAttribute("Id");
261 this.IgnoreErrors.Add(key);
262 }
263
264 // get the types of tests that will have their errors displayed as warnings
265 var testsAsWarningsElements = doc.SelectNodes("/Settings/ErrorsAsWarnings/Test");
266 foreach (XmlElement test in testsAsWarningsElements)
267 {
268 var key = test.GetAttribute("Id");
269 this.ErrorsAsWarnings.Add(key);
270 }
271
272 // get the exempt files
273 var localExemptFiles = doc.SelectNodes("/Settings/ExemptFiles/File");
274 foreach (XmlElement file in localExemptFiles)
275 {
276 var key = file.GetAttribute("Name").ToUpperInvariant();
277 this.ExemptFiles.Add(key);
278 }
279
280 settingsFile = localSettingsFile2;
281 localSettingsFile2 = null;
282 }
283 } 58 }
284 } 59 }
285} 60}