From 244b46cf7f3252d6dc3884ce184be901d1d173e5 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 2 Sep 2018 16:12:29 -0500 Subject: Migrate WixCop into Tools from wix4. --- src/wixcop/CommandLine/ConvertCommand.cs | 212 ++++++++++++++++++++++ src/wixcop/CommandLine/HelpCommand.cs | 25 +++ src/wixcop/CommandLine/WixCopCommandLineParser.cs | 132 ++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 src/wixcop/CommandLine/ConvertCommand.cs create mode 100644 src/wixcop/CommandLine/HelpCommand.cs create mode 100644 src/wixcop/CommandLine/WixCopCommandLineParser.cs (limited to 'src/wixcop/CommandLine') 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 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using WixToolset.Extensibility.Data; +using WixToolset.Extensibility.Services; + +namespace WixCop.CommandLine +{ + internal class ConvertCommand : ICommandLineCommand + { + private const string SettingsFileDefault = "wixcop.settings.xml"; + + public ConvertCommand(IServiceProvider serviceProvider, bool fixErrors, int indentationAmount, List searchPatterns, bool subDirectories, string settingsFile1, string settingsFile2) + { + this.ErrorsAsWarnings = new HashSet(); + this.ExemptFiles = new HashSet(); + this.FixErrors = fixErrors; + this.IndentationAmount = indentationAmount; + this.IgnoreErrors = new HashSet(); + this.SearchPatternResults = new HashSet(); + this.SearchPatterns = searchPatterns; + this.ServiceProvider = serviceProvider; + this.SettingsFile1 = settingsFile1; + this.SettingsFile2 = settingsFile2; + this.SubDirectories = subDirectories; + } + + private HashSet ErrorsAsWarnings { get; } + + private HashSet ExemptFiles { get; } + + private bool FixErrors { get; } + + private int IndentationAmount { get; } + + private HashSet IgnoreErrors { get; } + + private HashSet SearchPatternResults { get; } + + private List SearchPatterns { get; } + + private IServiceProvider ServiceProvider { get; } + + private string SettingsFile1 { get; } + + private string SettingsFile2 { get; } + + private bool SubDirectories { get; } + + public int Execute() + { + // parse the settings if any were specified + if (null != this.SettingsFile1 || null != this.SettingsFile2) + { + this.ParseSettingsFiles(this.SettingsFile1, this.SettingsFile2); + } + else + { + if (File.Exists(ConvertCommand.SettingsFileDefault)) + { + this.ParseSettingsFiles(ConvertCommand.SettingsFileDefault, null); + } + } + + var messaging = this.ServiceProvider.GetService(); + var converter = new Converter(messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); + + var errors = this.InspectSubDirectories(converter, Path.GetFullPath(".")); + + foreach (string searchPattern in this.SearchPatterns) + { + if (!this.SearchPatternResults.Contains(searchPattern)) + { + Console.Error.WriteLine("Could not find file \"{0}\"", searchPattern); + errors++; + } + } + + return errors != 0 ? 2 : 0; + } + + /// + /// Get the files that match a search path pattern. + /// + /// The base directory at which to begin the search. + /// The search path pattern. + /// The files matching the pattern. + private static string[] GetFiles(string baseDir, string searchPath) + { + // convert alternate directory separators to the standard one + var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); + string[] files = null; + + try + { + if (0 > lastSeparator) + { + files = Directory.GetFiles(baseDir, filePath); + } + else // found directory separator + { + var searchPattern = filePath.Substring(lastSeparator + 1); + + files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), searchPattern); + } + } + catch (DirectoryNotFoundException) + { + // don't let this function throw the DirectoryNotFoundException. (this exception + // occurs for non-existant directories and invalid characters in the searchPattern) + } + + return files; + } + + /// + /// Inspect sub-directories. + /// + /// The directory whose sub-directories will be inspected. + /// The number of errors that were found. + private int InspectSubDirectories(Converter converter, string directory) + { + var errors = 0; + + foreach (var searchPattern in this.SearchPatterns) + { + foreach (var sourceFilePath in GetFiles(directory, searchPattern)) + { + var file = new FileInfo(sourceFilePath); + + if (!this.ExemptFiles.Contains(file.Name.ToUpperInvariant())) + { + this.SearchPatternResults.Add(searchPattern); + errors += converter.ConvertFile(file.FullName, this.FixErrors); + } + } + } + + if (this.SubDirectories) + { + foreach (var childDirectoryPath in Directory.GetDirectories(directory)) + { + errors += this.InspectSubDirectories(converter, childDirectoryPath); + } + } + + return errors; + } + + /// + /// Parse the primary and secondary settings files. + /// + /// The primary settings file. + /// The secondary settings file. + private void ParseSettingsFiles(string localSettingsFile1, string localSettingsFile2) + { + if (null == localSettingsFile1 && null != localSettingsFile2) + { + throw new ArgumentException("Cannot specify a secondary settings file (set2) without a primary settings file (set1).", "localSettingsFile2"); + } + + var settingsFile = localSettingsFile1; + while (null != settingsFile) + { + XmlTextReader reader = null; + try + { + reader = new XmlTextReader(settingsFile); + var doc = new XmlDocument(); + doc.Load(reader); + + // get the types of tests that will have their errors displayed as warnings + var testsIgnoredElements = doc.SelectNodes("/Settings/IgnoreErrors/Test"); + foreach (XmlElement test in testsIgnoredElements) + { + var key = test.GetAttribute("Id"); + this.IgnoreErrors.Add(key); + } + + // get the types of tests that will have their errors displayed as warnings + var testsAsWarningsElements = doc.SelectNodes("/Settings/ErrorsAsWarnings/Test"); + foreach (XmlElement test in testsAsWarningsElements) + { + var key = test.GetAttribute("Id"); + this.ErrorsAsWarnings.Add(key); + } + + // get the exempt files + var localExemptFiles = doc.SelectNodes("/Settings/ExemptFiles/File"); + foreach (XmlElement file in localExemptFiles) + { + var key = file.GetAttribute("Name").ToUpperInvariant(); + this.ExemptFiles.Add(key); + } + } + finally + { + if (null != reader) + { + reader.Close(); + } + } + + settingsFile = localSettingsFile2; + localSettingsFile2 = null; + } + } + } +} 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 @@ +using System; +using WixToolset.Extensibility.Data; + +namespace WixCop.CommandLine +{ + internal class HelpCommand : ICommandLineCommand + { + public int Execute() + { + Console.WriteLine(" usage: wixcop.exe sourceFile [sourceFile ...]"); + Console.WriteLine(); + Console.WriteLine(" -f fix errors automatically for writable files"); + Console.WriteLine(" -nologo suppress displaying the logo information"); + Console.WriteLine(" -s search for matching files in current dir and subdirs"); + Console.WriteLine(" -set1 primary settings file"); + Console.WriteLine(" -set2 secondary settings file (overrides primary)"); + Console.WriteLine(" -indent: indentation multiple (overrides default of 4)"); + Console.WriteLine(" -? this help information"); + Console.WriteLine(); + Console.WriteLine(" sourceFile may use wildcards like *.wxs"); + + return 0; + } + } +} 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 @@ +using System; +using System.Collections.Generic; +using WixCop.Interfaces; +using WixToolset.Core; +using WixToolset.Extensibility.Data; +using WixToolset.Extensibility.Services; + +namespace WixCop.CommandLine +{ + public sealed class WixCopCommandLineParser : IWixCopCommandLineParser + { + private bool fixErrors; + private int indentationAmount; + private readonly List searchPatterns; + private readonly IServiceProvider serviceProvider; + private string settingsFile1; + private string settingsFile2; + private bool showHelp; + private bool showLogo; + private bool subDirectories; + + public WixCopCommandLineParser(IServiceProvider serviceProvider) + { + this.serviceProvider = serviceProvider; + + this.indentationAmount = 4; + this.searchPatterns = new List(); + this.showLogo = true; + } + + public ICommandLineArguments Arguments { get; set; } + + public ICommandLineCommand ParseWixCopCommandLine() + { + this.Parse(); + + if (this.showLogo) + { + AppCommon.DisplayToolHeader(); + Console.WriteLine(); + } + + if (this.showHelp) + { + return new HelpCommand(); + } + + return new ConvertCommand( + this.serviceProvider, + this.fixErrors, + this.indentationAmount, + this.searchPatterns, + this.subDirectories, + this.settingsFile1, + this.settingsFile2); + } + + private void Parse() + { + this.showHelp = 0 == this.Arguments.Arguments.Length; + var parser = this.Arguments.Parse(); + + while (!this.showHelp && + String.IsNullOrEmpty(parser.ErrorArgument) && + parser.TryGetNextSwitchOrArgument(out var arg)) + { + if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments. + { + continue; + } + + if (parser.IsSwitch(arg)) + { + if (!this.ParseArgument(parser, arg)) + { + parser.ErrorArgument = arg; + } + } + else + { + this.searchPatterns.Add(arg); + } + } + } + + private bool ParseArgument(IParseCommandLine parser, string arg) + { + var parameter = arg.Substring(1); + + switch (parameter.ToLowerInvariant()) + { + case "?": + this.showHelp = true; + return true; + case "f": + this.fixErrors = true; + return true; + case "nologo": + this.showLogo = false; + return true; + case "s": + this.subDirectories = true; + return true; + default: // other parameters + if (parameter.StartsWith("set1", StringComparison.Ordinal)) + { + this.settingsFile1 = parameter.Substring(4); + } + else if (parameter.StartsWith("set2", StringComparison.Ordinal)) + { + this.settingsFile2 = parameter.Substring(4); + } + else if (parameter.StartsWith("indent:", StringComparison.Ordinal)) + { + try + { + this.indentationAmount = Convert.ToInt32(parameter.Substring(7)); + } + catch + { + throw new ArgumentException("Invalid numeric argument.", parameter); + } + } + else + { + throw new ArgumentException("Invalid argument.", parameter); + } + return true; + } + } + } +} -- cgit v1.2.3-55-g6feb