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