aboutsummaryrefslogtreecommitdiff
path: root/src/wixcop/CommandLine
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2018-09-02 16:12:29 -0500
committerSean Hall <r.sean.hall@gmail.com>2018-09-13 12:05:57 -0500
commit244b46cf7f3252d6dc3884ce184be901d1d173e5 (patch)
treebd6fb4349b926001138d1a3415f93370d64e538f /src/wixcop/CommandLine
parent026d0af96fac5cd2d3d84ade657949ddc7144b99 (diff)
downloadwix-244b46cf7f3252d6dc3884ce184be901d1d173e5.tar.gz
wix-244b46cf7f3252d6dc3884ce184be901d1d173e5.tar.bz2
wix-244b46cf7f3252d6dc3884ce184be901d1d173e5.zip
Migrate WixCop into Tools from wix4.
Diffstat (limited to 'src/wixcop/CommandLine')
-rw-r--r--src/wixcop/CommandLine/ConvertCommand.cs212
-rw-r--r--src/wixcop/CommandLine/HelpCommand.cs25
-rw-r--r--src/wixcop/CommandLine/WixCopCommandLineParser.cs132
3 files changed, 369 insertions, 0 deletions
diff --git a/src/wixcop/CommandLine/ConvertCommand.cs b/src/wixcop/CommandLine/ConvertCommand.cs
new file mode 100644
index 00000000..6af7d4ca
--- /dev/null
+++ b/src/wixcop/CommandLine/ConvertCommand.cs
@@ -0,0 +1,212 @@
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using System.Xml;
6using WixToolset.Extensibility.Data;
7using WixToolset.Extensibility.Services;
8
9namespace WixCop.CommandLine
10{
11 internal class ConvertCommand : ICommandLineCommand
12 {
13 private const string SettingsFileDefault = "wixcop.settings.xml";
14
15 public ConvertCommand(IServiceProvider serviceProvider, bool fixErrors, int indentationAmount, List<string> searchPatterns, bool subDirectories, string settingsFile1, string settingsFile2)
16 {
17 this.ErrorsAsWarnings = new HashSet<string>();
18 this.ExemptFiles = new HashSet<string>();
19 this.FixErrors = fixErrors;
20 this.IndentationAmount = indentationAmount;
21 this.IgnoreErrors = new HashSet<string>();
22 this.SearchPatternResults = new HashSet<string>();
23 this.SearchPatterns = searchPatterns;
24 this.ServiceProvider = serviceProvider;
25 this.SettingsFile1 = settingsFile1;
26 this.SettingsFile2 = settingsFile2;
27 this.SubDirectories = subDirectories;
28 }
29
30 private HashSet<string> ErrorsAsWarnings { get; }
31
32 private HashSet<string> ExemptFiles { get; }
33
34 private bool FixErrors { get; }
35
36 private int IndentationAmount { get; }
37
38 private HashSet<string> IgnoreErrors { get; }
39
40 private HashSet<string> SearchPatternResults { get; }
41
42 private List<string> SearchPatterns { get; }
43
44 private IServiceProvider ServiceProvider { get; }
45
46 private string SettingsFile1 { get; }
47
48 private string SettingsFile2 { get; }
49
50 private bool SubDirectories { get; }
51
52 public int Execute()
53 {
54 // parse the settings if any were specified
55 if (null != this.SettingsFile1 || null != this.SettingsFile2)
56 {
57 this.ParseSettingsFiles(this.SettingsFile1, this.SettingsFile2);
58 }
59 else
60 {
61 if (File.Exists(ConvertCommand.SettingsFileDefault))
62 {
63 this.ParseSettingsFiles(ConvertCommand.SettingsFileDefault, null);
64 }
65 }
66
67 var messaging = this.ServiceProvider.GetService<IMessaging>();
68 var converter = new Converter(messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors);
69
70 var errors = this.InspectSubDirectories(converter, Path.GetFullPath("."));
71
72 foreach (string searchPattern in this.SearchPatterns)
73 {
74 if (!this.SearchPatternResults.Contains(searchPattern))
75 {
76 Console.Error.WriteLine("Could not find file \"{0}\"", searchPattern);
77 errors++;
78 }
79 }
80
81 return errors != 0 ? 2 : 0;
82 }
83
84 /// <summary>
85 /// Get the files that match a search path pattern.
86 /// </summary>
87 /// <param name="baseDir">The base directory at which to begin the search.</param>
88 /// <param name="searchPath">The search path pattern.</param>
89 /// <returns>The files matching the pattern.</returns>
90 private static string[] GetFiles(string baseDir, string searchPath)
91 {
92 // convert alternate directory separators to the standard one
93 var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
94 var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar);
95 string[] files = null;
96
97 try
98 {
99 if (0 > lastSeparator)
100 {
101 files = Directory.GetFiles(baseDir, filePath);
102 }
103 else // found directory separator
104 {
105 var searchPattern = filePath.Substring(lastSeparator + 1);
106
107 files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), searchPattern);
108 }
109 }
110 catch (DirectoryNotFoundException)
111 {
112 // don't let this function throw the DirectoryNotFoundException. (this exception
113 // occurs for non-existant directories and invalid characters in the searchPattern)
114 }
115
116 return files;
117 }
118
119 /// <summary>
120 /// Inspect sub-directories.
121 /// </summary>
122 /// <param name="directory">The directory whose sub-directories will be inspected.</param>
123 /// <returns>The number of errors that were found.</returns>
124 private int InspectSubDirectories(Converter converter, string directory)
125 {
126 var errors = 0;
127
128 foreach (var searchPattern in this.SearchPatterns)
129 {
130 foreach (var sourceFilePath in GetFiles(directory, searchPattern))
131 {
132 var file = new FileInfo(sourceFilePath);
133
134 if (!this.ExemptFiles.Contains(file.Name.ToUpperInvariant()))
135 {
136 this.SearchPatternResults.Add(searchPattern);
137 errors += converter.ConvertFile(file.FullName, this.FixErrors);
138 }
139 }
140 }
141
142 if (this.SubDirectories)
143 {
144 foreach (var childDirectoryPath in Directory.GetDirectories(directory))
145 {
146 errors += this.InspectSubDirectories(converter, childDirectoryPath);
147 }
148 }
149
150 return errors;
151 }
152
153 /// <summary>
154 /// Parse the primary and secondary settings files.
155 /// </summary>
156 /// <param name="localSettingsFile1">The primary settings file.</param>
157 /// <param name="localSettingsFile2">The secondary settings file.</param>
158 private void ParseSettingsFiles(string localSettingsFile1, string localSettingsFile2)
159 {
160 if (null == localSettingsFile1 && null != localSettingsFile2)
161 {
162 throw new ArgumentException("Cannot specify a secondary settings file (set2) without a primary settings file (set1).", "localSettingsFile2");
163 }
164
165 var settingsFile = localSettingsFile1;
166 while (null != settingsFile)
167 {
168 XmlTextReader reader = null;
169 try
170 {
171 reader = new XmlTextReader(settingsFile);
172 var doc = new XmlDocument();
173 doc.Load(reader);
174
175 // get the types of tests that will have their errors displayed as warnings
176 var testsIgnoredElements = doc.SelectNodes("/Settings/IgnoreErrors/Test");
177 foreach (XmlElement test in testsIgnoredElements)
178 {
179 var key = test.GetAttribute("Id");
180 this.IgnoreErrors.Add(key);
181 }
182
183 // get the types of tests that will have their errors displayed as warnings
184 var testsAsWarningsElements = doc.SelectNodes("/Settings/ErrorsAsWarnings/Test");
185 foreach (XmlElement test in testsAsWarningsElements)
186 {
187 var key = test.GetAttribute("Id");
188 this.ErrorsAsWarnings.Add(key);
189 }
190
191 // get the exempt files
192 var localExemptFiles = doc.SelectNodes("/Settings/ExemptFiles/File");
193 foreach (XmlElement file in localExemptFiles)
194 {
195 var key = file.GetAttribute("Name").ToUpperInvariant();
196 this.ExemptFiles.Add(key);
197 }
198 }
199 finally
200 {
201 if (null != reader)
202 {
203 reader.Close();
204 }
205 }
206
207 settingsFile = localSettingsFile2;
208 localSettingsFile2 = null;
209 }
210 }
211 }
212}
diff --git a/src/wixcop/CommandLine/HelpCommand.cs b/src/wixcop/CommandLine/HelpCommand.cs
new file mode 100644
index 00000000..a75dac5c
--- /dev/null
+++ b/src/wixcop/CommandLine/HelpCommand.cs
@@ -0,0 +1,25 @@
1using System;
2using WixToolset.Extensibility.Data;
3
4namespace WixCop.CommandLine
5{
6 internal class HelpCommand : ICommandLineCommand
7 {
8 public int Execute()
9 {
10 Console.WriteLine(" usage: wixcop.exe sourceFile [sourceFile ...]");
11 Console.WriteLine();
12 Console.WriteLine(" -f fix errors automatically for writable files");
13 Console.WriteLine(" -nologo suppress displaying the logo information");
14 Console.WriteLine(" -s search for matching files in current dir and subdirs");
15 Console.WriteLine(" -set1<file> primary settings file");
16 Console.WriteLine(" -set2<file> secondary settings file (overrides primary)");
17 Console.WriteLine(" -indent:<n> indentation multiple (overrides default of 4)");
18 Console.WriteLine(" -? this help information");
19 Console.WriteLine();
20 Console.WriteLine(" sourceFile may use wildcards like *.wxs");
21
22 return 0;
23 }
24 }
25}
diff --git a/src/wixcop/CommandLine/WixCopCommandLineParser.cs b/src/wixcop/CommandLine/WixCopCommandLineParser.cs
new file mode 100644
index 00000000..53012cfd
--- /dev/null
+++ b/src/wixcop/CommandLine/WixCopCommandLineParser.cs
@@ -0,0 +1,132 @@
1using System;
2using System.Collections.Generic;
3using WixCop.Interfaces;
4using WixToolset.Core;
5using WixToolset.Extensibility.Data;
6using WixToolset.Extensibility.Services;
7
8namespace WixCop.CommandLine
9{
10 public sealed class WixCopCommandLineParser : IWixCopCommandLineParser
11 {
12 private bool fixErrors;
13 private int indentationAmount;
14 private readonly List<string> searchPatterns;
15 private readonly IServiceProvider serviceProvider;
16 private string settingsFile1;
17 private string settingsFile2;
18 private bool showHelp;
19 private bool showLogo;
20 private bool subDirectories;
21
22 public WixCopCommandLineParser(IServiceProvider serviceProvider)
23 {
24 this.serviceProvider = serviceProvider;
25
26 this.indentationAmount = 4;
27 this.searchPatterns = new List<string>();
28 this.showLogo = true;
29 }
30
31 public ICommandLineArguments Arguments { get; set; }
32
33 public ICommandLineCommand ParseWixCopCommandLine()
34 {
35 this.Parse();
36
37 if (this.showLogo)
38 {
39 AppCommon.DisplayToolHeader();
40 Console.WriteLine();
41 }
42
43 if (this.showHelp)
44 {
45 return new HelpCommand();
46 }
47
48 return new ConvertCommand(
49 this.serviceProvider,
50 this.fixErrors,
51 this.indentationAmount,
52 this.searchPatterns,
53 this.subDirectories,
54 this.settingsFile1,
55 this.settingsFile2);
56 }
57
58 private void Parse()
59 {
60 this.showHelp = 0 == this.Arguments.Arguments.Length;
61 var parser = this.Arguments.Parse();
62
63 while (!this.showHelp &&
64 String.IsNullOrEmpty(parser.ErrorArgument) &&
65 parser.TryGetNextSwitchOrArgument(out var arg))
66 {
67 if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments.
68 {
69 continue;
70 }
71
72 if (parser.IsSwitch(arg))
73 {
74 if (!this.ParseArgument(parser, arg))
75 {
76 parser.ErrorArgument = arg;
77 }
78 }
79 else
80 {
81 this.searchPatterns.Add(arg);
82 }
83 }
84 }
85
86 private bool ParseArgument(IParseCommandLine parser, string arg)
87 {
88 var parameter = arg.Substring(1);
89
90 switch (parameter.ToLowerInvariant())
91 {
92 case "?":
93 this.showHelp = true;
94 return true;
95 case "f":
96 this.fixErrors = true;
97 return true;
98 case "nologo":
99 this.showLogo = false;
100 return true;
101 case "s":
102 this.subDirectories = true;
103 return true;
104 default: // other parameters
105 if (parameter.StartsWith("set1", StringComparison.Ordinal))
106 {
107 this.settingsFile1 = parameter.Substring(4);
108 }
109 else if (parameter.StartsWith("set2", StringComparison.Ordinal))
110 {
111 this.settingsFile2 = parameter.Substring(4);
112 }
113 else if (parameter.StartsWith("indent:", StringComparison.Ordinal))
114 {
115 try
116 {
117 this.indentationAmount = Convert.ToInt32(parameter.Substring(7));
118 }
119 catch
120 {
121 throw new ArgumentException("Invalid numeric argument.", parameter);
122 }
123 }
124 else
125 {
126 throw new ArgumentException("Invalid argument.", parameter);
127 }
128 return true;
129 }
130 }
131 }
132}