From 32c33f0a39cbca251135cfb4cb985e48f8306f57 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Wed, 21 Dec 2022 13:11:53 -0500 Subject: [PATCH] Add SchemaValidator to validate WiX authoring. SchemaValidator is a quick-n-dirty tool to let you validate a set of WiX source files against the WiX schemas. This is mostly useful to highlight incomplete/inaccurate schemas. --- Web.sln | 9 +- tools/SchemaValidator/CommandLine.cs | 97 ++++++++++++++++++++ tools/SchemaValidator/Program.cs | 75 +++++++++++++++ tools/SchemaValidator/SchemaValidator.csproj | 10 ++ 4 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 tools/SchemaValidator/CommandLine.cs create mode 100644 tools/SchemaValidator/Program.cs create mode 100644 tools/SchemaValidator/SchemaValidator.csproj diff --git a/Web.sln b/Web.sln index 2a95b3cf..3b87c5d9 100644 --- a/Web.sln +++ b/Web.sln @@ -1,7 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.4.33122.133 VisualStudioVersion = 17.4.33103.184 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "src\Web\Web.csproj", "{C728C975-EEE4-4A54-ACD2-95E8BCA2F5CE}" @@ -12,10 +11,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XsdToMarkdownTests", "src\t EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{1D3076DE-5378-4273-AB3B-C14B6F4C46BC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FeedGenerator", "src\FeedGenerator\FeedGenerator.csproj", "{07F578E6-AE83-4615-821D-59B4F8D92B25}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FeedGenerator", "src\FeedGenerator\FeedGenerator.csproj", "{07F578E6-AE83-4615-821D-59B4F8D92B25}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XmlDocToMarkdown", "src\XmlDocToMarkdown\XmlDocToMarkdown.csproj", "{AD1815B0-1F4F-4CEE-961D-0AAFE21227BF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SchemaValidator", "tools\SchemaValidator\SchemaValidator.csproj", "{20D9C939-933C-4F1F-8EF7-298CA16F136F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,6 +43,10 @@ Global {AD1815B0-1F4F-4CEE-961D-0AAFE21227BF}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD1815B0-1F4F-4CEE-961D-0AAFE21227BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {AD1815B0-1F4F-4CEE-961D-0AAFE21227BF}.Release|Any CPU.Build.0 = Release|Any CPU + {20D9C939-933C-4F1F-8EF7-298CA16F136F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20D9C939-933C-4F1F-8EF7-298CA16F136F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20D9C939-933C-4F1F-8EF7-298CA16F136F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20D9C939-933C-4F1F-8EF7-298CA16F136F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/SchemaValidator/CommandLine.cs b/tools/SchemaValidator/CommandLine.cs new file mode 100644 index 00000000..38199e50 --- /dev/null +++ b/tools/SchemaValidator/CommandLine.cs @@ -0,0 +1,97 @@ +// 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. + +namespace WixBuildTools.SchemaValidator; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +/// +/// Command-line parsing. +/// +public class CommandLine +{ + private CommandLine() + { + } + + /// + /// List of schema files to process. + /// + public List Schemas { get; private set; } = new List(); + + /// + /// List of source files to process. + /// + public List Sources { get; private set; } = new List(); + + /// + /// Show the help text. + /// + public static void ShowHelp() + { + Console.WriteLine("SchemaValidator.exe [-?] path/to/*.xsd path/to/*.wx?"); + } + + /// + /// Parses the command-line. + /// + /// Arguments from command-line. + /// Command line object created from command-line arguments + /// True if command-line is parsed, false if a failure was occurred. + public static bool TryParseArguments(string[] args, out CommandLine commandLine) + { + var success = true; + + commandLine = new CommandLine(); + + for (var i = 0; i < args.Length; ++i) + { + if ('-' == args[i][0] || '/' == args[i][0]) + { + var arg = args[i].Substring(1).ToLowerInvariant(); + if ("?" == arg || "help" == arg) + { + return false; + } + } + else + { + var paths = args[i].Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var path in paths) + { + var sourcePath = Path.GetFullPath(path); + var sourceDir = Path.GetDirectoryName(sourcePath); + var wildcard = sourcePath.Substring(sourceDir!.Length + 1); + var files = Directory.EnumerateFiles(sourceDir, wildcard, SearchOption.AllDirectories); + if (files.Any()) + { + commandLine.Schemas.AddRange(files.Where(f => Path.GetExtension(f).Equals(".xsd", StringComparison.OrdinalIgnoreCase))); + commandLine.Sources.AddRange(files.Where(f => Path.GetExtension(f).StartsWith(".wx", StringComparison.OrdinalIgnoreCase))); + } + else + { + Console.Error.WriteLine($"Source file '{sourcePath}' could not be found."); + success = false; + } + } + } + } + + if (0 == commandLine.Schemas.Count) + { + Console.Error.WriteLine("No schema files specified. Specify at least one file."); + success = false; + } + + if (0 == commandLine.Sources.Count) + { + Console.Error.WriteLine("No source files specified. Specify at least one file."); + success = false; + } + + return success; + } +} diff --git a/tools/SchemaValidator/Program.cs b/tools/SchemaValidator/Program.cs new file mode 100644 index 00000000..24060dc6 --- /dev/null +++ b/tools/SchemaValidator/Program.cs @@ -0,0 +1,75 @@ +// 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. + +namespace WixBuildTools.SchemaValidator; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Schema; + +public sealed class Program +{ + public static int Main(string[] args) + { + if (!CommandLine.TryParseArguments(args, out var commandLine)) + { + CommandLine.ShowHelp(); + return 1; + } + + Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Start run. Validating {commandLine.Sources.Count} source files against {commandLine.Schemas.Count} schemas."); + + var settings = GetReaderSettings(commandLine); + + var errors = 0; + + foreach (var source in commandLine.Sources) + { + try + { + Validate(source, settings); + } + catch (XmlSchemaException xse) + { + ++errors; + Console.WriteLine($"Encountered validation error in {source}({xse.LineNumber},{xse.LinePosition}): {xse.Message}"); + } + } + + Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Run complete. Encountered {errors} validation errors from {commandLine.Sources.Count} source files against {commandLine.Schemas.Count} schemas."); + + return 0; + } + + private static XmlReaderSettings GetReaderSettings(CommandLine commandLine) + { + var settings = new XmlReaderSettings + { + ValidationType = ValidationType.Schema, + ValidationFlags = + XmlSchemaValidationFlags.ReportValidationWarnings | + XmlSchemaValidationFlags.ProcessIdentityConstraints | + XmlSchemaValidationFlags.ProcessInlineSchema | + XmlSchemaValidationFlags.ProcessSchemaLocation + }; + + foreach (var schema in commandLine.Schemas) + { + settings.Schemas.Add(null, schema); + } + + return settings; + } + + static void Validate(string xmlFile, XmlReaderSettings settings) + { + var document = new XmlDocument(); + var reader = XmlReader.Create(xmlFile, settings); + document.Load(reader); + document.Validate(null); + } +} diff --git a/tools/SchemaValidator/SchemaValidator.csproj b/tools/SchemaValidator/SchemaValidator.csproj new file mode 100644 index 00000000..74abf5c9 --- /dev/null +++ b/tools/SchemaValidator/SchemaValidator.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + +