diff options
| author | Rob Mensching <rob@firegiant.com> | 2020-07-18 14:57:08 -0700 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2020-07-18 15:06:43 -0700 |
| commit | e04caab11fb8f2cac4d575ef1e352221bd421586 (patch) | |
| tree | 7c9752f17fab2793cabb07e73a42b52d573e1249 /src | |
| parent | 92bb1d2d74e46714459c2d0fc23f185329745718 (diff) | |
| download | wix-e04caab11fb8f2cac4d575ef1e352221bd421586.tar.gz wix-e04caab11fb8f2cac4d575ef1e352221bd421586.tar.bz2 wix-e04caab11fb8f2cac4d575ef1e352221bd421586.zip | |
Separate "format" from "convert"
Closes wixtoolset/issues#6215
Diffstat (limited to 'src')
| -rw-r--r-- | src/WixToolset.Converters/ConvertCommand.cs | 269 | ||||
| -rw-r--r-- | src/WixToolset.Converters/ConverterExtensionCommandLine.cs | 11 | ||||
| -rw-r--r-- | src/WixToolset.Converters/FixupCommandBase.cs | 267 | ||||
| -rw-r--r-- | src/WixToolset.Converters/FormatCommand.cs | 60 | ||||
| -rw-r--r-- | src/WixToolset.Converters/WixConverter.cs | 179 | ||||
| -rw-r--r-- | src/test/WixToolsetTest.Converters/ConverterFixture.cs | 104 | ||||
| -rw-r--r-- | src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs | 10 | ||||
| -rw-r--r-- | src/test/WixToolsetTest.Converters/FormatFixture.cs | 117 |
8 files changed, 589 insertions, 428 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 @@ | |||
| 3 | namespace WixToolset.Converters | 3 | namespace 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 | } |
diff --git a/src/WixToolset.Converters/ConverterExtensionCommandLine.cs b/src/WixToolset.Converters/ConverterExtensionCommandLine.cs index ed4b613e..d78b89cc 100644 --- a/src/WixToolset.Converters/ConverterExtensionCommandLine.cs +++ b/src/WixToolset.Converters/ConverterExtensionCommandLine.cs | |||
| @@ -21,8 +21,11 @@ namespace WixToolset.Converters | |||
| 21 | 21 | ||
| 22 | private IWixToolsetServiceProvider ServiceProvider { get; } | 22 | private IWixToolsetServiceProvider ServiceProvider { get; } |
| 23 | 23 | ||
| 24 | // TODO: Do something with CommandLineSwitches | 24 | public override IEnumerable<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[] |
| 25 | public override IEnumerable<ExtensionCommandLineSwitch> CommandLineSwitches => base.CommandLineSwitches; | 25 | { |
| 26 | new ExtensionCommandLineSwitch { Switch = "convert", Description = "Convert v3 source code to v4 source code." }, | ||
| 27 | new ExtensionCommandLineSwitch { Switch = "format", Description = "Ensures consistent formatting of source code." }, | ||
| 28 | }; | ||
| 26 | 29 | ||
| 27 | public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) | 30 | public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) |
| 28 | { | 31 | { |
| @@ -32,6 +35,10 @@ namespace WixToolset.Converters | |||
| 32 | { | 35 | { |
| 33 | command = new ConvertCommand(this.ServiceProvider); | 36 | command = new ConvertCommand(this.ServiceProvider); |
| 34 | } | 37 | } |
| 38 | else if ("format".Equals(argument, StringComparison.OrdinalIgnoreCase)) | ||
| 39 | { | ||
| 40 | command = new FormatCommand(this.ServiceProvider); | ||
| 41 | } | ||
| 35 | 42 | ||
| 36 | return command != null; | 43 | return command != null; |
| 37 | } | 44 | } |
diff --git a/src/WixToolset.Converters/FixupCommandBase.cs b/src/WixToolset.Converters/FixupCommandBase.cs new file mode 100644 index 00000000..0f58fbdb --- /dev/null +++ b/src/WixToolset.Converters/FixupCommandBase.cs | |||
| @@ -0,0 +1,267 @@ | |||
| 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 | |||
| 3 | namespace WixToolset.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | using System.Threading; | ||
| 9 | using System.Threading.Tasks; | ||
| 10 | using System.Xml; | ||
| 11 | using WixToolset.Extensibility.Data; | ||
| 12 | using WixToolset.Extensibility.Services; | ||
| 13 | |||
| 14 | internal abstract class FixupCommandBase : ICommandLineCommand | ||
| 15 | { | ||
| 16 | protected FixupCommandBase() | ||
| 17 | { | ||
| 18 | this.IndentationAmount = 4; // default indentation amount | ||
| 19 | this.ErrorsAsWarnings = new HashSet<string>(); | ||
| 20 | this.ExemptFiles = new HashSet<string>(); | ||
| 21 | this.IgnoreErrors = new HashSet<string>(); | ||
| 22 | this.SearchPatternResults = new HashSet<string>(); | ||
| 23 | this.SearchPatterns = new List<string>(); | ||
| 24 | } | ||
| 25 | |||
| 26 | public bool ShowLogo { get; private set; } | ||
| 27 | |||
| 28 | public bool StopParsing { get; private set; } | ||
| 29 | |||
| 30 | protected bool ShowHelp { get; set; } | ||
| 31 | |||
| 32 | protected bool DryRun { get; set; } | ||
| 33 | |||
| 34 | protected HashSet<string> ErrorsAsWarnings { get; } | ||
| 35 | |||
| 36 | protected HashSet<string> IgnoreErrors { get; } | ||
| 37 | |||
| 38 | protected HashSet<string> ExemptFiles { get; } | ||
| 39 | |||
| 40 | protected int IndentationAmount { get; set; } | ||
| 41 | |||
| 42 | protected bool Recurse { get; set; } | ||
| 43 | |||
| 44 | private HashSet<string> SearchPatternResults { get; } | ||
| 45 | |||
| 46 | private List<string> SearchPatterns { get; } | ||
| 47 | |||
| 48 | private string SettingsFile1 { get; set; } | ||
| 49 | |||
| 50 | private string SettingsFile2 { get; set; } | ||
| 51 | |||
| 52 | public bool TryParseArgument(ICommandLineParser parser, string argument) | ||
| 53 | { | ||
| 54 | if (!parser.IsSwitch(argument)) | ||
| 55 | { | ||
| 56 | this.SearchPatterns.Add(argument); | ||
| 57 | return true; | ||
| 58 | } | ||
| 59 | |||
| 60 | var parameter = argument.Substring(1); | ||
| 61 | switch (parameter.ToLowerInvariant()) | ||
| 62 | { | ||
| 63 | case "?": | ||
| 64 | case "h": | ||
| 65 | case "-help": | ||
| 66 | this.ShowHelp = true; | ||
| 67 | this.ShowLogo = true; | ||
| 68 | this.StopParsing = true; | ||
| 69 | return true; | ||
| 70 | |||
| 71 | case "n": | ||
| 72 | case "--dry-run": | ||
| 73 | this.DryRun = true; | ||
| 74 | return true; | ||
| 75 | |||
| 76 | case "nologo": | ||
| 77 | case "-nologo": | ||
| 78 | this.ShowLogo = false; | ||
| 79 | return true; | ||
| 80 | |||
| 81 | case "s": | ||
| 82 | case "r": | ||
| 83 | case "-recurse": | ||
| 84 | this.Recurse = 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 abstract Task<int> ExecuteAsync(CancellationToken cancellationToken); | ||
| 116 | |||
| 117 | protected void ParseSettings(string defaultSettingsFile) | ||
| 118 | { | ||
| 119 | // parse the settings if any were specified | ||
| 120 | if (null != this.SettingsFile1 || null != this.SettingsFile2) | ||
| 121 | { | ||
| 122 | this.ParseSettingsFiles(this.SettingsFile1, this.SettingsFile2); | ||
| 123 | } | ||
| 124 | else | ||
| 125 | { | ||
| 126 | if (File.Exists(defaultSettingsFile)) | ||
| 127 | { | ||
| 128 | this.ParseSettingsFiles(defaultSettingsFile, null); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | protected int Inspect(Func<string, bool, int> inspector, CancellationToken cancellationToken) | ||
| 134 | { | ||
| 135 | var errors = this.InspectSubDirectories(inspector, Path.GetFullPath("."), cancellationToken); | ||
| 136 | |||
| 137 | foreach (var searchPattern in this.SearchPatterns) | ||
| 138 | { | ||
| 139 | if (!this.SearchPatternResults.Contains(searchPattern)) | ||
| 140 | { | ||
| 141 | Console.Error.WriteLine("Could not find file \"{0}\"", searchPattern); | ||
| 142 | errors++; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | return errors; | ||
| 147 | } | ||
| 148 | |||
| 149 | /// <summary> | ||
| 150 | /// Inspect sub-directories. | ||
| 151 | /// </summary> | ||
| 152 | /// <param name="directory">The directory whose sub-directories will be inspected.</param> | ||
| 153 | /// <returns>The number of errors that were found.</returns> | ||
| 154 | private int InspectSubDirectories(Func<string, bool, int> inspector, string directory, CancellationToken cancellationToken) | ||
| 155 | { | ||
| 156 | var errors = 0; | ||
| 157 | |||
| 158 | foreach (var searchPattern in this.SearchPatterns) | ||
| 159 | { | ||
| 160 | foreach (var sourceFilePath in GetFiles(directory, searchPattern)) | ||
| 161 | { | ||
| 162 | cancellationToken.ThrowIfCancellationRequested(); | ||
| 163 | |||
| 164 | var file = new FileInfo(sourceFilePath); | ||
| 165 | |||
| 166 | if (!this.ExemptFiles.Contains(file.Name.ToUpperInvariant())) | ||
| 167 | { | ||
| 168 | this.SearchPatternResults.Add(searchPattern); | ||
| 169 | errors += inspector(file.FullName, !this.DryRun); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | if (this.Recurse) | ||
| 175 | { | ||
| 176 | foreach (var childDirectoryPath in Directory.GetDirectories(directory)) | ||
| 177 | { | ||
| 178 | errors += this.InspectSubDirectories(inspector, childDirectoryPath, cancellationToken); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | return errors; | ||
| 183 | } | ||
| 184 | |||
| 185 | /// <summary> | ||
| 186 | /// Parse the primary and secondary settings files. | ||
| 187 | /// </summary> | ||
| 188 | /// <param name="localSettingsFile1">The primary settings file.</param> | ||
| 189 | /// <param name="localSettingsFile2">The secondary settings file.</param> | ||
| 190 | private void ParseSettingsFiles(string localSettingsFile1, string localSettingsFile2) | ||
| 191 | { | ||
| 192 | if (null == localSettingsFile1 && null != localSettingsFile2) | ||
| 193 | { | ||
| 194 | throw new ArgumentException("Cannot specify a secondary settings file (set2) without a primary settings file (set1).", nameof(localSettingsFile2)); | ||
| 195 | } | ||
| 196 | |||
| 197 | var settingsFile = localSettingsFile1; | ||
| 198 | while (null != settingsFile) | ||
| 199 | { | ||
| 200 | var doc = new XmlDocument(); | ||
| 201 | doc.Load(settingsFile); | ||
| 202 | |||
| 203 | // get the types of tests that will have their errors displayed as warnings | ||
| 204 | var testsIgnoredElements = doc.SelectNodes("/Settings/IgnoreErrors/Test"); | ||
| 205 | foreach (XmlElement test in testsIgnoredElements) | ||
| 206 | { | ||
| 207 | var key = test.GetAttribute("Id"); | ||
| 208 | this.IgnoreErrors.Add(key); | ||
| 209 | } | ||
| 210 | |||
| 211 | // get the types of tests that will have their errors displayed as warnings | ||
| 212 | var testsAsWarningsElements = doc.SelectNodes("/Settings/ErrorsAsWarnings/Test"); | ||
| 213 | foreach (XmlElement test in testsAsWarningsElements) | ||
| 214 | { | ||
| 215 | var key = test.GetAttribute("Id"); | ||
| 216 | this.ErrorsAsWarnings.Add(key); | ||
| 217 | } | ||
| 218 | |||
| 219 | // get the exempt files | ||
| 220 | var localExemptFiles = doc.SelectNodes("/Settings/ExemptFiles/File"); | ||
| 221 | foreach (XmlElement file in localExemptFiles) | ||
| 222 | { | ||
| 223 | var key = file.GetAttribute("Name").ToUpperInvariant(); | ||
| 224 | this.ExemptFiles.Add(key); | ||
| 225 | } | ||
| 226 | |||
| 227 | settingsFile = localSettingsFile2; | ||
| 228 | localSettingsFile2 = null; | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | /// <summary> | ||
| 233 | /// Get the files that match a search path pattern. | ||
| 234 | /// </summary> | ||
| 235 | /// <param name="baseDir">The base directory at which to begin the search.</param> | ||
| 236 | /// <param name="searchPath">The search path pattern.</param> | ||
| 237 | /// <returns>The files matching the pattern.</returns> | ||
| 238 | private static string[] GetFiles(string baseDir, string searchPath) | ||
| 239 | { | ||
| 240 | // convert alternate directory separators to the standard one | ||
| 241 | var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||
| 242 | var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); | ||
| 243 | string[] files = null; | ||
| 244 | |||
| 245 | try | ||
| 246 | { | ||
| 247 | if (0 > lastSeparator) | ||
| 248 | { | ||
| 249 | files = Directory.GetFiles(baseDir, filePath); | ||
| 250 | } | ||
| 251 | else // found directory separator | ||
| 252 | { | ||
| 253 | var searchPattern = filePath.Substring(lastSeparator + 1); | ||
| 254 | |||
| 255 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), searchPattern); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | catch (DirectoryNotFoundException) | ||
| 259 | { | ||
| 260 | // don't let this function throw the DirectoryNotFoundException. (this exception | ||
| 261 | // occurs for non-existant directories and invalid characters in the searchPattern) | ||
| 262 | } | ||
| 263 | |||
| 264 | return files; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | } | ||
diff --git a/src/WixToolset.Converters/FormatCommand.cs b/src/WixToolset.Converters/FormatCommand.cs new file mode 100644 index 00000000..e9965df3 --- /dev/null +++ b/src/WixToolset.Converters/FormatCommand.cs | |||
| @@ -0,0 +1,60 @@ | |||
| 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 | |||
| 3 | namespace WixToolset.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Threading; | ||
| 7 | using System.Threading.Tasks; | ||
| 8 | using WixToolset.Extensibility.Services; | ||
| 9 | |||
| 10 | internal class FormatCommand : FixupCommandBase | ||
| 11 | { | ||
| 12 | private const string SettingsFileDefault = "wix.format.settings.xml"; | ||
| 13 | |||
| 14 | public FormatCommand(IWixToolsetServiceProvider serviceProvider) | ||
| 15 | { | ||
| 16 | this.Messaging = serviceProvider.GetService<IMessaging>(); | ||
| 17 | } | ||
| 18 | |||
| 19 | private IMessaging Messaging { get; } | ||
| 20 | |||
| 21 | public override Task<int> ExecuteAsync(CancellationToken cancellationToken) | ||
| 22 | { | ||
| 23 | if (this.ShowHelp) | ||
| 24 | { | ||
| 25 | DisplayHelp(); | ||
| 26 | return Task.FromResult(-1); | ||
| 27 | } | ||
| 28 | |||
| 29 | var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); | ||
| 30 | |||
| 31 | this.ParseSettings(SettingsFileDefault); | ||
| 32 | |||
| 33 | var errors = base.Inspect(Inspector, cancellationToken); | ||
| 34 | |||
| 35 | return Task.FromResult(errors); | ||
| 36 | |||
| 37 | int Inspector(string file, bool fix) | ||
| 38 | { | ||
| 39 | return converter.FormatFile(file, fix); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | private static void DisplayHelp() | ||
| 44 | { | ||
| 45 | Console.WriteLine(); | ||
| 46 | Console.WriteLine("Usage: wix format [options] sourceFile [sourceFile ...]"); | ||
| 47 | Console.WriteLine(); | ||
| 48 | Console.WriteLine("Options:"); | ||
| 49 | Console.WriteLine(" -h|--help Show command line help."); | ||
| 50 | Console.WriteLine(" --nologo Suppress displaying the logo information."); | ||
| 51 | Console.WriteLine(" -n|--dry-run Only display errors, do not update files."); | ||
| 52 | Console.WriteLine(" -r|--recurse Search for matching files in current dir and subdirs."); | ||
| 53 | Console.WriteLine(" -set1<file> Primary settings file."); | ||
| 54 | Console.WriteLine(" -set2<file> Secondary settings file (overrides primary)."); | ||
| 55 | Console.WriteLine(" -indent:<n> Indentation multiple (overrides default of 4)."); | ||
| 56 | Console.WriteLine(); | ||
| 57 | Console.WriteLine(" sourceFile may use wildcards like *.wxs"); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
diff --git a/src/WixToolset.Converters/WixConverter.cs b/src/WixToolset.Converters/WixConverter.cs index a05c7f58..bfeed03e 100644 --- a/src/WixToolset.Converters/WixConverter.cs +++ b/src/WixToolset.Converters/WixConverter.cs | |||
| @@ -19,6 +19,12 @@ namespace WixToolset.Converters | |||
| 19 | /// </summary> | 19 | /// </summary> |
| 20 | public class WixConverter | 20 | public class WixConverter |
| 21 | { | 21 | { |
| 22 | private enum ConvertOperation | ||
| 23 | { | ||
| 24 | Convert, | ||
| 25 | Format, | ||
| 26 | } | ||
| 27 | |||
| 22 | private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); | 28 | private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); |
| 23 | private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters | 29 | private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters |
| 24 | 30 | ||
| @@ -107,18 +113,6 @@ namespace WixToolset.Converters | |||
| 107 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, | 113 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, |
| 108 | }; | 114 | }; |
| 109 | 115 | ||
| 110 | private readonly static SortedSet<string> Wix3Namespaces = new SortedSet<string> | ||
| 111 | { | ||
| 112 | "http://schemas.microsoft.com/wix/2006/wi", | ||
| 113 | "http://schemas.microsoft.com/wix/2006/localization", | ||
| 114 | }; | ||
| 115 | |||
| 116 | private readonly static SortedSet<string> Wix4Namespaces = new SortedSet<string> | ||
| 117 | { | ||
| 118 | "http://wixtoolset.org/schemas/v4/wxs", | ||
| 119 | "http://wixtoolset.org/schemas/v4/wxl", | ||
| 120 | }; | ||
| 121 | |||
| 122 | private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping; | 116 | private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping; |
| 123 | 117 | ||
| 124 | /// <summary> | 118 | /// <summary> |
| @@ -193,6 +187,8 @@ namespace WixToolset.Converters | |||
| 193 | 187 | ||
| 194 | private int IndentationAmount { get; set; } | 188 | private int IndentationAmount { get; set; } |
| 195 | 189 | ||
| 190 | private ConvertOperation Operation { get; set; } | ||
| 191 | |||
| 196 | private string SourceFile { get; set; } | 192 | private string SourceFile { get; set; } |
| 197 | 193 | ||
| 198 | private int SourceVersion { get; set; } | 194 | private int SourceVersion { get; set; } |
| @@ -205,22 +201,11 @@ namespace WixToolset.Converters | |||
| 205 | /// <returns>The number of errors found.</returns> | 201 | /// <returns>The number of errors found.</returns> |
| 206 | public int ConvertFile(string sourceFile, bool saveConvertedFile) | 202 | public int ConvertFile(string sourceFile, bool saveConvertedFile) |
| 207 | { | 203 | { |
| 208 | XDocument document; | 204 | var document = this.OpenSourceFile(sourceFile); |
| 209 | |||
| 210 | // Set the instance info. | ||
| 211 | this.Errors = 0; | ||
| 212 | this.SourceFile = sourceFile; | ||
| 213 | this.SourceVersion = 0; | ||
| 214 | 205 | ||
| 215 | try | 206 | if (document is null) |
| 216 | { | 207 | { |
| 217 | document = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | 208 | return 1; |
| 218 | } | ||
| 219 | catch (XmlException e) | ||
| 220 | { | ||
| 221 | this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); | ||
| 222 | |||
| 223 | return this.Errors; | ||
| 224 | } | 209 | } |
| 225 | 210 | ||
| 226 | this.ConvertDocument(document); | 211 | this.ConvertDocument(document); |
| @@ -228,17 +213,7 @@ namespace WixToolset.Converters | |||
| 228 | // Fix errors if requested and necessary. | 213 | // Fix errors if requested and necessary. |
| 229 | if (saveConvertedFile && 0 < this.Errors) | 214 | if (saveConvertedFile && 0 < this.Errors) |
| 230 | { | 215 | { |
| 231 | try | 216 | this.SaveDocument(document); |
| 232 | { | ||
| 233 | using (var writer = XmlWriter.Create(this.SourceFile, new XmlWriterSettings { OmitXmlDeclaration = true })) | ||
| 234 | { | ||
| 235 | document.Save(writer); | ||
| 236 | } | ||
| 237 | } | ||
| 238 | catch (UnauthorizedAccessException) | ||
| 239 | { | ||
| 240 | this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); | ||
| 241 | } | ||
| 242 | } | 217 | } |
| 243 | 218 | ||
| 244 | return this.Errors; | 219 | return this.Errors; |
| @@ -251,13 +226,68 @@ namespace WixToolset.Converters | |||
| 251 | /// <returns>The number of errors found.</returns> | 226 | /// <returns>The number of errors found.</returns> |
| 252 | public int ConvertDocument(XDocument document) | 227 | public int ConvertDocument(XDocument document) |
| 253 | { | 228 | { |
| 229 | // Reset the instance info. | ||
| 254 | this.Errors = 0; | 230 | this.Errors = 0; |
| 255 | this.SourceVersion = 0; | 231 | this.SourceVersion = 0; |
| 232 | this.Operation = ConvertOperation.Convert; | ||
| 233 | |||
| 234 | // Remove the declaration. | ||
| 235 | if (null != document.Declaration) | ||
| 236 | { | ||
| 237 | if (this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) | ||
| 238 | { | ||
| 239 | document.Declaration = null; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | TrimLeadingText(document); | ||
| 244 | |||
| 245 | // Start converting the nodes at the top. | ||
| 246 | this.ConvertNodes(document.Nodes(), 0); | ||
| 247 | |||
| 248 | return this.Errors; | ||
| 249 | } | ||
| 256 | 250 | ||
| 257 | var declaration = document.Declaration; | 251 | /// <summary> |
| 252 | /// Format a file. | ||
| 253 | /// </summary> | ||
| 254 | /// <param name="sourceFile">The file to format.</param> | ||
| 255 | /// <param name="saveConvertedFile">Option to save the format errors that are found.</param> | ||
| 256 | /// <returns>The number of errors found.</returns> | ||
| 257 | public int FormatFile(string sourceFile, bool saveConvertedFile) | ||
| 258 | { | ||
| 259 | var document = this.OpenSourceFile(sourceFile); | ||
| 260 | |||
| 261 | if (document is null) | ||
| 262 | { | ||
| 263 | return 1; | ||
| 264 | } | ||
| 265 | |||
| 266 | this.FormatDocument(document); | ||
| 267 | |||
| 268 | // Fix errors if requested and necessary. | ||
| 269 | if (saveConvertedFile && 0 < this.Errors) | ||
| 270 | { | ||
| 271 | this.SaveDocument(document); | ||
| 272 | } | ||
| 273 | |||
| 274 | return this.Errors; | ||
| 275 | } | ||
| 276 | |||
| 277 | /// <summary> | ||
| 278 | /// Format a document. | ||
| 279 | /// </summary> | ||
| 280 | /// <param name="document">The document to format.</param> | ||
| 281 | /// <returns>The number of errors found.</returns> | ||
| 282 | public int FormatDocument(XDocument document) | ||
| 283 | { | ||
| 284 | // Reset the instance info. | ||
| 285 | this.Errors = 0; | ||
| 286 | this.SourceVersion = 0; | ||
| 287 | this.Operation = ConvertOperation.Format; | ||
| 258 | 288 | ||
| 259 | // Remove the declaration. | 289 | // Remove the declaration. |
| 260 | if (null != declaration) | 290 | if (null != document.Declaration) |
| 261 | { | 291 | { |
| 262 | if (this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) | 292 | if (this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) |
| 263 | { | 293 | { |
| @@ -273,6 +303,37 @@ namespace WixToolset.Converters | |||
| 273 | return this.Errors; | 303 | return this.Errors; |
| 274 | } | 304 | } |
| 275 | 305 | ||
| 306 | private XDocument OpenSourceFile(string sourceFile) | ||
| 307 | { | ||
| 308 | this.SourceFile = sourceFile; | ||
| 309 | |||
| 310 | try | ||
| 311 | { | ||
| 312 | return XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 313 | } | ||
| 314 | catch (XmlException e) | ||
| 315 | { | ||
| 316 | this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); | ||
| 317 | } | ||
| 318 | |||
| 319 | return null; | ||
| 320 | } | ||
| 321 | |||
| 322 | private void SaveDocument(XDocument document) | ||
| 323 | { | ||
| 324 | try | ||
| 325 | { | ||
| 326 | using (var writer = XmlWriter.Create(this.SourceFile, new XmlWriterSettings { OmitXmlDeclaration = true })) | ||
| 327 | { | ||
| 328 | document.Save(writer); | ||
| 329 | } | ||
| 330 | } | ||
| 331 | catch (UnauthorizedAccessException) | ||
| 332 | { | ||
| 333 | this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 276 | private void ConvertNodes(IEnumerable<XNode> nodes, int level) | 337 | private void ConvertNodes(IEnumerable<XNode> nodes, int level) |
| 277 | { | 338 | { |
| 278 | // Note we operate on a copy of the node list since we may | 339 | // Note we operate on a copy of the node list since we may |
| @@ -901,7 +962,10 @@ namespace WixToolset.Converters | |||
| 901 | /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> | 962 | /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> |
| 902 | private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) | 963 | private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) |
| 903 | { | 964 | { |
| 904 | if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error | 965 | // Ignore the error if explicitly ignored or outside the range of the current operation. |
| 966 | if (this.IgnoreErrors.Contains(converterTestType) || | ||
| 967 | (this.Operation == ConvertOperation.Convert && converterTestType < ConverterTestType.DeclarationPresent) || | ||
| 968 | (this.Operation == ConvertOperation.Format && converterTestType > ConverterTestType.DeclarationPresent)) | ||
| 905 | { | 969 | { |
| 906 | return false; | 970 | return false; |
| 907 | } | 971 | } |
| @@ -909,7 +973,7 @@ namespace WixToolset.Converters | |||
| 909 | // Increase the error count. | 973 | // Increase the error count. |
| 910 | this.Errors++; | 974 | this.Errors++; |
| 911 | 975 | ||
| 912 | var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); | 976 | var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wix.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); |
| 913 | var warning = this.ErrorsAsWarnings.Contains(converterTestType); | 977 | var warning = this.ErrorsAsWarnings.Contains(converterTestType); |
| 914 | var display = String.Format(CultureInfo.CurrentCulture, message, args); | 978 | var display = String.Format(CultureInfo.CurrentCulture, message, args); |
| 915 | 979 | ||
| @@ -1050,39 +1114,19 @@ namespace WixToolset.Converters | |||
| 1050 | UnauthorizedAccessException, | 1114 | UnauthorizedAccessException, |
| 1051 | 1115 | ||
| 1052 | /// <summary> | 1116 | /// <summary> |
| 1053 | /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'. | ||
| 1054 | /// </summary> | ||
| 1055 | DeclarationEncodingWrong, | ||
| 1056 | |||
| 1057 | /// <summary> | ||
| 1058 | /// Displayed when the XML declaration is missing from the source file. | ||
| 1059 | /// </summary> | ||
| 1060 | DeclarationMissing, | ||
| 1061 | |||
| 1062 | /// <summary> | ||
| 1063 | /// Displayed when the whitespace preceding a CDATA node is wrong. | ||
| 1064 | /// </summary> | ||
| 1065 | WhitespacePrecedingCDATAWrong, | ||
| 1066 | |||
| 1067 | /// <summary> | ||
| 1068 | /// Displayed when the whitespace preceding a node is wrong. | 1117 | /// Displayed when the whitespace preceding a node is wrong. |
| 1069 | /// </summary> | 1118 | /// </summary> |
| 1070 | WhitespacePrecedingNodeWrong, | 1119 | WhitespacePrecedingNodeWrong, |
| 1071 | 1120 | ||
| 1072 | /// <summary> | 1121 | /// <summary> |
| 1073 | /// Displayed when an element is not empty as it should be. | 1122 | /// Displayed when the whitespace preceding an end element is wrong. |
| 1074 | /// </summary> | ||
| 1075 | NotEmptyElement, | ||
| 1076 | |||
| 1077 | /// <summary> | ||
| 1078 | /// Displayed when the whitespace following a CDATA node is wrong. | ||
| 1079 | /// </summary> | 1123 | /// </summary> |
| 1080 | WhitespaceFollowingCDATAWrong, | 1124 | WhitespacePrecedingEndElementWrong, |
| 1081 | 1125 | ||
| 1082 | /// <summary> | 1126 | /// <summary> |
| 1083 | /// Displayed when the whitespace preceding an end element is wrong. | 1127 | /// Displayed when the XML declaration is present in the source file. |
| 1084 | /// </summary> | 1128 | /// </summary> |
| 1085 | WhitespacePrecedingEndElementWrong, | 1129 | DeclarationPresent, |
| 1086 | 1130 | ||
| 1087 | /// <summary> | 1131 | /// <summary> |
| 1088 | /// Displayed when the xmlns attribute is missing from the document element. | 1132 | /// Displayed when the xmlns attribute is missing from the document element. |
| @@ -1155,11 +1199,6 @@ namespace WixToolset.Converters | |||
| 1155 | AutoGuidUnnecessary, | 1199 | AutoGuidUnnecessary, |
| 1156 | 1200 | ||
| 1157 | /// <summary> | 1201 | /// <summary> |
| 1158 | /// Displayed when the XML declaration is present in the source file. | ||
| 1159 | /// </summary> | ||
| 1160 | DeclarationPresent, | ||
| 1161 | |||
| 1162 | /// <summary> | ||
| 1163 | /// The Feature Absent attribute renamed to AllowAbsent. | 1202 | /// The Feature Absent attribute renamed to AllowAbsent. |
| 1164 | /// </summary> | 1203 | /// </summary> |
| 1165 | FeatureAbsentAttributeReplaced, | 1204 | FeatureAbsentAttributeReplaced, |
diff --git a/src/test/WixToolsetTest.Converters/ConverterFixture.cs b/src/test/WixToolsetTest.Converters/ConverterFixture.cs index 6e2ad2c5..cf89ba7e 100644 --- a/src/test/WixToolsetTest.Converters/ConverterFixture.cs +++ b/src/test/WixToolsetTest.Converters/ConverterFixture.cs | |||
| @@ -40,110 +40,6 @@ namespace WixToolsetTest.Converters | |||
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | [Fact] | 42 | [Fact] |
| 43 | public void CanFixWhitespace() | ||
| 44 | { | ||
| 45 | var parse = String.Join(Environment.NewLine, | ||
| 46 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 47 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 48 | " <Fragment>", | ||
| 49 | " <Property Id='Prop'", | ||
| 50 | " Value='Val'>", | ||
| 51 | " </Property>", | ||
| 52 | " </Fragment>", | ||
| 53 | "</Wix>"); | ||
| 54 | |||
| 55 | var expected = String.Join(Environment.NewLine, | ||
| 56 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 57 | " <Fragment>", | ||
| 58 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 59 | " </Fragment>", | ||
| 60 | "</Wix>"); | ||
| 61 | |||
| 62 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 63 | |||
| 64 | var messaging = new MockMessaging(); | ||
| 65 | var converter = new WixConverter(messaging, 4, null, null); | ||
| 66 | |||
| 67 | var errors = converter.ConvertDocument(document); | ||
| 68 | |||
| 69 | var actual = UnformattedDocumentString(document); | ||
| 70 | |||
| 71 | Assert.Equal(expected, actual); | ||
| 72 | Assert.Equal(5, errors); | ||
| 73 | } | ||
| 74 | |||
| 75 | [Fact] | ||
| 76 | public void CanPreserveNewLines() | ||
| 77 | { | ||
| 78 | var parse = String.Join(Environment.NewLine, | ||
| 79 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 80 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 81 | " <Fragment>", | ||
| 82 | "", | ||
| 83 | " <Property Id='Prop' Value='Val' />", | ||
| 84 | "", | ||
| 85 | " </Fragment>", | ||
| 86 | "</Wix>"); | ||
| 87 | |||
| 88 | var expected = String.Join(Environment.NewLine, | ||
| 89 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 90 | " <Fragment>", | ||
| 91 | "", | ||
| 92 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 93 | "", | ||
| 94 | " </Fragment>", | ||
| 95 | "</Wix>"); | ||
| 96 | |||
| 97 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 98 | |||
| 99 | var messaging = new MockMessaging(); | ||
| 100 | var converter = new WixConverter(messaging, 4, null, null); | ||
| 101 | |||
| 102 | var conversions = converter.ConvertDocument(document); | ||
| 103 | |||
| 104 | var actual = UnformattedDocumentString(document); | ||
| 105 | |||
| 106 | Assert.Equal(expected, actual); | ||
| 107 | Assert.Equal(4, conversions); | ||
| 108 | } | ||
| 109 | |||
| 110 | [Fact] | ||
| 111 | public void CanConvertWithNewLineAtEndOfFile() | ||
| 112 | { | ||
| 113 | var parse = String.Join(Environment.NewLine, | ||
| 114 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 115 | " <Fragment>", | ||
| 116 | "", | ||
| 117 | " <Property Id='Prop' Value='Val' />", | ||
| 118 | "", | ||
| 119 | " </Fragment>", | ||
| 120 | "</Wix>", | ||
| 121 | ""); | ||
| 122 | |||
| 123 | var expected = String.Join(Environment.NewLine, | ||
| 124 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 125 | " <Fragment>", | ||
| 126 | "", | ||
| 127 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 128 | "", | ||
| 129 | " </Fragment>", | ||
| 130 | "</Wix>", | ||
| 131 | ""); | ||
| 132 | |||
| 133 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 134 | |||
| 135 | var messaging = new MockMessaging(); | ||
| 136 | var converter = new WixConverter(messaging, 4, null, null); | ||
| 137 | |||
| 138 | var conversions = converter.ConvertDocument(document); | ||
| 139 | |||
| 140 | var actual = UnformattedDocumentString(document); | ||
| 141 | |||
| 142 | Assert.Equal(expected, actual); | ||
| 143 | Assert.Equal(3, conversions); | ||
| 144 | } | ||
| 145 | |||
| 146 | [Fact] | ||
| 147 | public void CanConvertMainNamespace() | 43 | public void CanConvertMainNamespace() |
| 148 | { | 44 | { |
| 149 | var parse = String.Join(Environment.NewLine, | 45 | var parse = String.Join(Environment.NewLine, |
diff --git a/src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs b/src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs index 5eaeb985..79cc3f69 100644 --- a/src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs +++ b/src/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs | |||
| @@ -84,7 +84,7 @@ namespace WixToolsetTest.Converters | |||
| 84 | var settingsFile = Path.Combine(folder, "wixcop.settings.xml"); | 84 | var settingsFile = Path.Combine(folder, "wixcop.settings.xml"); |
| 85 | 85 | ||
| 86 | var result = RunConversion(targetFile, settingsFile: settingsFile); | 86 | var result = RunConversion(targetFile, settingsFile: settingsFile); |
| 87 | Assert.Equal(2, result.ExitCode); | 87 | Assert.Equal(7, result.ExitCode); |
| 88 | 88 | ||
| 89 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | 89 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); |
| 90 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); | 90 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); |
| @@ -108,7 +108,7 @@ namespace WixToolsetTest.Converters | |||
| 108 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); | 108 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); |
| 109 | 109 | ||
| 110 | var result = RunConversion(targetFile); | 110 | var result = RunConversion(targetFile); |
| 111 | Assert.Equal(2, result.ExitCode); | 111 | Assert.Equal(10, result.ExitCode); |
| 112 | 112 | ||
| 113 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | 113 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); |
| 114 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); | 114 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); |
| @@ -133,7 +133,7 @@ namespace WixToolsetTest.Converters | |||
| 133 | 133 | ||
| 134 | var result = RunConversion(targetFile); | 134 | var result = RunConversion(targetFile); |
| 135 | 135 | ||
| 136 | Assert.Equal(2, result.ExitCode); | 136 | Assert.Equal(10, result.ExitCode); |
| 137 | Assert.Single(result.Messages.Where(message => message.ToString().EndsWith("(QtExecCmdTimeoutAmbiguous)"))); | 137 | Assert.Single(result.Messages.Where(message => message.ToString().EndsWith("(QtExecCmdTimeoutAmbiguous)"))); |
| 138 | 138 | ||
| 139 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | 139 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); |
| @@ -142,7 +142,7 @@ namespace WixToolsetTest.Converters | |||
| 142 | 142 | ||
| 143 | // still fails because QtExecCmdTimeoutAmbiguous is unfixable | 143 | // still fails because QtExecCmdTimeoutAmbiguous is unfixable |
| 144 | var result2 = RunConversion(targetFile); | 144 | var result2 = RunConversion(targetFile); |
| 145 | Assert.Equal(2, result2.ExitCode); | 145 | Assert.Equal(1, result2.ExitCode); |
| 146 | } | 146 | } |
| 147 | } | 147 | } |
| 148 | 148 | ||
| @@ -153,7 +153,7 @@ namespace WixToolsetTest.Converters | |||
| 153 | var exitCode = WixRunner.Execute(new[] | 153 | var exitCode = WixRunner.Execute(new[] |
| 154 | { | 154 | { |
| 155 | "convert", | 155 | "convert", |
| 156 | fixErrors ? "-f" : null, | 156 | fixErrors ? null : "--dry-run", |
| 157 | String.IsNullOrEmpty(settingsFile) ? null : "-set1" + settingsFile, | 157 | String.IsNullOrEmpty(settingsFile) ? null : "-set1" + settingsFile, |
| 158 | targetFile | 158 | targetFile |
| 159 | }, serviceProvider, out var messages); | 159 | }, serviceProvider, out var messages); |
diff --git a/src/test/WixToolsetTest.Converters/FormatFixture.cs b/src/test/WixToolsetTest.Converters/FormatFixture.cs new file mode 100644 index 00000000..739fba66 --- /dev/null +++ b/src/test/WixToolsetTest.Converters/FormatFixture.cs | |||
| @@ -0,0 +1,117 @@ | |||
| 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 | |||
| 3 | namespace WixToolsetTest.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixToolset.Converters; | ||
| 8 | using WixToolsetTest.Converters.Mocks; | ||
| 9 | using Xunit; | ||
| 10 | |||
| 11 | public class FormatFixture : BaseConverterFixture | ||
| 12 | { | ||
| 13 | [Fact] | ||
| 14 | public void CanFixWhitespace() | ||
| 15 | { | ||
| 16 | var parse = String.Join(Environment.NewLine, | ||
| 17 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 18 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 19 | " <Fragment>", | ||
| 20 | " <Property Id='Prop'", | ||
| 21 | " Value='Val'>", | ||
| 22 | " </Property>", | ||
| 23 | " </Fragment>", | ||
| 24 | "</Wix>"); | ||
| 25 | |||
| 26 | var expected = String.Join(Environment.NewLine, | ||
| 27 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 28 | " <Fragment>", | ||
| 29 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 30 | " </Fragment>", | ||
| 31 | "</Wix>"); | ||
| 32 | |||
| 33 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 34 | |||
| 35 | var messaging = new MockMessaging(); | ||
| 36 | var converter = new WixConverter(messaging, 4, null, null); | ||
| 37 | |||
| 38 | var errors = converter.FormatDocument(document); | ||
| 39 | |||
| 40 | var actual = UnformattedDocumentString(document); | ||
| 41 | |||
| 42 | Assert.Equal(expected, actual); | ||
| 43 | Assert.Equal(5, errors); | ||
| 44 | } | ||
| 45 | |||
| 46 | [Fact] | ||
| 47 | public void CanPreserveNewLines() | ||
| 48 | { | ||
| 49 | var parse = String.Join(Environment.NewLine, | ||
| 50 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 51 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 52 | " <Fragment>", | ||
| 53 | "", | ||
| 54 | " <Property Id='Prop' Value='Val' />", | ||
| 55 | "", | ||
| 56 | " </Fragment>", | ||
| 57 | "</Wix>"); | ||
| 58 | |||
| 59 | var expected = String.Join(Environment.NewLine, | ||
| 60 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 61 | " <Fragment>", | ||
| 62 | "", | ||
| 63 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 64 | "", | ||
| 65 | " </Fragment>", | ||
| 66 | "</Wix>"); | ||
| 67 | |||
| 68 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 69 | |||
| 70 | var messaging = new MockMessaging(); | ||
| 71 | var converter = new WixConverter(messaging, 4, null, null); | ||
| 72 | |||
| 73 | var conversions = converter.FormatDocument(document); | ||
| 74 | |||
| 75 | var actual = UnformattedDocumentString(document); | ||
| 76 | |||
| 77 | Assert.Equal(expected, actual); | ||
| 78 | Assert.Equal(4, conversions); | ||
| 79 | } | ||
| 80 | |||
| 81 | [Fact] | ||
| 82 | public void CanFormatWithNewLineAtEndOfFile() | ||
| 83 | { | ||
| 84 | var parse = String.Join(Environment.NewLine, | ||
| 85 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 86 | " <Fragment>", | ||
| 87 | "", | ||
| 88 | " <Property Id='Prop' Value='Val' />", | ||
| 89 | "", | ||
| 90 | " </Fragment>", | ||
| 91 | "</Wix>", | ||
| 92 | ""); | ||
| 93 | |||
| 94 | var expected = String.Join(Environment.NewLine, | ||
| 95 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 96 | " <Fragment>", | ||
| 97 | "", | ||
| 98 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 99 | "", | ||
| 100 | " </Fragment>", | ||
| 101 | "</Wix>", | ||
| 102 | ""); | ||
| 103 | |||
| 104 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 105 | |||
| 106 | var messaging = new MockMessaging(); | ||
| 107 | var converter = new WixConverter(messaging, 4, null, null); | ||
| 108 | |||
| 109 | var conversions = converter.FormatDocument(document); | ||
| 110 | |||
| 111 | var actual = UnformattedDocumentString(document); | ||
| 112 | |||
| 113 | Assert.Equal(expected, actual); | ||
| 114 | Assert.Equal(3, conversions); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
