From 7887cf7348014b38b3f9c19b8ed38bcde9440bd5 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Mon, 21 Jan 2019 16:00:05 +0100 Subject: [PATCH] Generate Specs with C# --- src/SpecFileGen/Program.cs | 350 +++++++++++++++++++++++++++++ src/SpecFileGen/SpecFileGen.csproj | 8 + src/markdig.sln | 144 ++++++------ 3 files changed, 433 insertions(+), 69 deletions(-) create mode 100644 src/SpecFileGen/Program.cs create mode 100644 src/SpecFileGen/SpecFileGen.csproj diff --git a/src/SpecFileGen/Program.cs b/src/SpecFileGen/Program.cs new file mode 100644 index 00000000..6249e9eb --- /dev/null +++ b/src/SpecFileGen/Program.cs @@ -0,0 +1,350 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace SpecFileGen +{ + class Program + { + const string SpecificationsDirectory = "../../../../Markdig.Tests/Specs/"; + + class Spec + { + public readonly string Name; + public readonly string Path; + public readonly string OutputPath; + public readonly string Extensions; + public int TestCount; + + public Spec(string name, string fileName, string extensions) + { + Name = name; + Path = SpecificationsDirectory + fileName; + OutputPath = Path.Substring(0, Path.Length - 2) + "cs"; + Extensions = extensions; + } + } + static readonly Spec[] Specs = new[] + { + new Spec("CommonMark v. 0.28", "CommonMark.md", ""), + new Spec("Pipe Tables", "PipeTableSpecs.md", "pipetables|advanced"), + new Spec("Footnotes", "FootnotesSpecs.md", "footnotes|advanced"), + new Spec("Generic Attributes", "GenericAttributesSpecs.md", "attributes|advanced"), + new Spec("Emphasis Extra", "EmphasisExtraSpecs.md", "emphasisextras|advanced"), + new Spec("Hardline Breaks", "HardlineBreakSpecs.md", "hardlinebreak|advanced+hardlinebreak"), + new Spec("Grid Tables", "GridTableSpecs.md", "gridtables|advanced"), + new Spec("Custom Containers", "CustomContainerSpecs.md", "customcontainers+attributes|advanced"), + new Spec("Definition Lists", "DefinitionListSpecs.md", "definitionlists+attributes|advanced"), + new Spec("Emoji", "EmojiSpecs.md", "emojis|advanced+emojis"), + new Spec("Abbreviations", "AbbreviationSpecs.md", "abbreviations|advanced"), + new Spec("List Extras", "ListExtraSpecs.md", "listextras|advanced"), + new Spec("Math", "MathSpecs.md", "mathematics|advanced"), + new Spec("Bootstrap", "BootstrapSpecs.md", "bootstrap+pipetables+figures+attributes"), + new Spec("Media", "MediaSpecs.md", "medialinks|advanced+medialinks"), + new Spec("Smarty Pants", "SmartyPantsSpecs.md", "pipetables+smartypants|advanced+smartypants"), + new Spec("Auto Identifiers", "AutoIdentifierSpecs.md", "autoidentifiers|advanced"), + new Spec("Task Lists", "TaskListSpecs.md", "tasklists|advanced"), + new Spec("Diagrams", "DiagramsSpecs.md", "diagrams|advanced"), + new Spec("No Html", "NoHtmlSpecs.md", "nohtml"), + new Spec("Yaml", "YamlSpecs.md", "yaml"), + new Spec("Auto Links", "AutoLinks.md", "autolinks|advanced"), + new Spec("Jira Links", "JiraLinks.md", "jiralinks"), + new Spec("Globalization", "GlobalizationSpecs.md", "globalization+advanced+emojis"), + new Spec("Figures, Footers and Cites", "FigureFooterAndCiteSpecs.md", "figures+footers+citations|advanced"), + }; + + static void Main(string[] args) + { + Console.WriteLine("Generating {0} specs ...", Specs.Length); + + bool anyChanged = false; + List missingSpecs = new List(); + int totalTests = 0; + + foreach (var spec in Specs) + { + if (!File.Exists(spec.Path)) + { + missingSpecs.Add(spec); + continue; + } + + string source = ParseSpecification(spec, File.ReadAllText(spec.Path)); + totalTests += spec.TestCount; + + if (File.Exists(spec.OutputPath)) // If the source hasn't changed, don't bump the generated tag + { + string previousSource = File.ReadAllText(spec.OutputPath); + int start = previousSource.IndexOf('\n', StringComparison.Ordinal) + 1; + int previousLength = previousSource.Length - start; + if (start != 0 && previousLength == source.Length) + { + if (previousSource.IndexOf(source, start, previousLength, StringComparison.Ordinal) == start) + { + // The source did not change + continue; + } + } + } + + string generated = "// Generated: " + DateTime.UtcNow.ToString() + '\n'; + File.WriteAllText(spec.OutputPath, generated + source); + anyChanged = true; + } + + if (missingSpecs.Count != 0) + { + foreach (var spec in missingSpecs) + { + EmitError("Could not find the specification file at " + spec.Path); + } + Environment.Exit(1); + } + + if (anyChanged && Environment.GetEnvironmentVariable("CI") != null) + { + EmitError("Run SpecFileGen when changing specification files"); + Environment.Exit(1); + } + + Console.WriteLine("There are {0} tests in total", totalTests); + } + static void EmitError(string error) + { + var prevColor = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(); + Console.WriteLine(new string('-', error.Length)); + Console.WriteLine(error); + Console.WriteLine(new string('-', error.Length)); + Console.WriteLine(); + Console.ForegroundColor = prevColor; + } + + static readonly StringBuilder StringBuilder = new StringBuilder(1 << 20); // 1 MB + static void Write(string text) + { + StringBuilder.Append(text); + } + static void Line(string text = null) + { + if (text != null) StringBuilder.Append(text); + StringBuilder.Append('\n'); + } + static void Indent(int count = 1) + { + StringBuilder.Append(new string(' ', 4 * count)); + } + + static string ParseSpecification(Spec spec, string specSource) + { + Line(); + Write("// "); Line(new string('-', 32)); + Write("// "); Write(new string(' ', 16 - spec.Name.Length / 2)); Line(spec.Name); + Write("// "); Line(new string('-', 32)); + Line(); + Line("using System;"); + Line("using NUnit.Framework;"); + Line(); + Write("namespace Markdig.Tests.Specs."); Line(CompressedName(spec.Name).Replace('.', '_')); + Line("{"); + + var lines = specSource.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); + + bool nameChanged = true; + string name = ""; + string compressedName = ""; + int number = 0; + int commentOffset = 0, commentEnd = 0, markdownOffset = 0, markdownEnd = 0, htmlOffset = 0, htmlEnd = 0; + bool first = true; + LinkedList<(string Heading, string Compressed, int Level)> headings = new LinkedList<(string, string, int)>(); + StringBuilder nameBuilder = new StringBuilder(64); + + int i = 0; + while (i < lines.Length) + { + commentOffset = commentEnd = i; + while (!lines[i].Equals("```````````````````````````````` example", StringComparison.Ordinal)) + { + string line = lines[i]; + if (line.Length > 2 && line[0] == '#') + { + int level = line.IndexOf(' ', StringComparison.Ordinal); + while (headings.Count != 0) + { + if (headings.Last.Value.Level < level) break; + headings.RemoveLast(); + } + string heading = line.Substring(level + 1); + headings.AddLast((heading, CompressedName(heading), level)); + + foreach (var (Heading, _, _) in headings) + nameBuilder.Append(Heading + " / "); + nameBuilder.Length -= 3; + name = nameBuilder.ToString(); + nameBuilder.Length = 0; + + foreach (var (_, Compressed, _) in headings) + nameBuilder.Append(Compressed); + compressedName = nameBuilder.ToString(); + nameBuilder.Length = 0; + + nameChanged = true; + } + i++; + + if (!IsEmpty(line)) + commentEnd = i; + + if (i == lines.Length) + { + if (commentOffset != commentEnd) + { + while (commentOffset < commentEnd && IsEmpty(lines[commentOffset])) commentOffset++; + for (i = commentOffset; i < commentEnd; i++) + { + Indent(2); Write("// "); Line(lines[i]); + } + } + goto End; + } + }; + + markdownOffset = ++i; + while (!(lines[i].Length == 1 && lines[i][0] == '.')) i++; + markdownEnd = i++; + + htmlOffset = i; + while (!lines[i].Equals("````````````````````````````````", StringComparison.Ordinal)) i++; + htmlEnd = i++; + + if (nameChanged) + { + if (!first) + { + Indent(); Line("}"); + Line(); + } + Indent(); Line("[TestFixture]"); + Indent(); Line("public class Test" + compressedName); + Indent(); Line("{"); + first = false; + nameChanged = false; + } + else Line(); + + WriteTest(name, compressedName, ++number, spec.Extensions, lines, + commentOffset, commentEnd, + markdownOffset, markdownEnd, + htmlOffset, htmlEnd); + } + + End: + if (!first) + { + Indent(); Line("}"); + } + Line("}"); + + string source = StringBuilder.ToString(); + StringBuilder.Length = 0; + + spec.TestCount = number; + return source; + } + + static void WriteTest(string name, string compressedName, int number, string extensions, string[] lines, int commentOffset, int commentEnd, int markdownOffset, int markdownEnd, int htmlOffset, int htmlEnd) + { + if (commentOffset != commentEnd) + { + while (commentOffset < commentEnd && IsEmpty(lines[commentOffset])) commentOffset++; + for (int i = commentOffset; i < commentEnd; i++) + { + Indent(2); Write("// "); Line(lines[i]); + } + } + + Indent(2); Line("[Test]"); + Indent(2); Line("public void " + compressedName + "_Example" + number.ToString().PadLeft(3, '0') + "()"); + Indent(2); Line("{"); + Indent(3); Line("// Example " + number); + Indent(3); Line("// Section: " + name); + + Indent(3); Line("//"); + Indent(3); Line("// The following Markdown:"); + for (int i = markdownOffset; i < markdownEnd; i++) + { + Indent(3); Write("// "); Indent(); Line(lines[i]); + } + + Indent(3); Line("//"); + Indent(3); Line("// Should be rendered as:"); + for (int i = htmlOffset; i < htmlEnd; i++) + { + Indent(3); Write("// "); Indent(); Line(lines[i]); + } + if (htmlOffset >= htmlEnd) + { + Indent(3); Write("//"); + } + + Line(); + Indent(3); Line($"Console.WriteLine(\"Example {number}\\nSection {name}\\n\");"); + + Indent(3); + Write("TestParser.TestSpec(\""); + for (int i = markdownOffset; i < markdownEnd; i++) + { + Write(Escape(lines[i])); + if (i != markdownEnd - 1) Write("\\n"); + } + Write("\", \""); + for (int i = htmlOffset; i < htmlEnd; i++) + { + Write(Escape(lines[i])); + if (i != htmlEnd - 1) Write("\\n"); + } + Write("\", \""); + Write(extensions); + Line("\");"); + + Indent(2); Line("}"); + } + static string Escape(string input) + { + return input + .Replace("→", "\t") + .Replace("\\", "\\\\") + .Replace("\"", "\\\"") + .Replace("\0", "\\0") + .Replace("\a", "\\a") + .Replace("\b", "\\b") + .Replace("\f", "\\f") + .Replace("\n", "\\n") + .Replace("\r", "\\r") + .Replace("\t", "\\t") + .Replace("\v", "\\v") + ; + } + static string CompressedName(string name) + { + string compressedName = ""; + foreach (var part in name.Replace(',', ' ').Split(' ', StringSplitOptions.RemoveEmptyEntries)) + { + compressedName += char.IsLower(part[0]) + ? char.ToUpper(part[0]) + part.Substring(1) + : part; + } + return compressedName; + } + static bool IsEmpty(string str) + { + for (int i = 0; i < str.Length; i++) + { + if (str[i] != ' ') return false; + } + return true; + } + } +} diff --git a/src/SpecFileGen/SpecFileGen.csproj b/src/SpecFileGen/SpecFileGen.csproj new file mode 100644 index 00000000..01d51132 --- /dev/null +++ b/src/SpecFileGen/SpecFileGen.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp2.2 + + + diff --git a/src/markdig.sln b/src/markdig.sln index 81b5a8fd..9feff73a 100644 --- a/src/markdig.sln +++ b/src/markdig.sln @@ -1,69 +1,75 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27004.2005 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{061866E2-005C-4D13-A338-EA464BBEC60F}" - ProjectSection(SolutionItems) = preProject - ..\appveyor.yml = ..\appveyor.yml - ..\changelog.md = ..\changelog.md - ..\license.txt = ..\license.txt - ..\readme.md = ..\readme.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig", "Markdig\Markdig.csproj", "{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Tests", "Markdig.Tests\Markdig.Tests.csproj", "{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}" - ProjectSection(ProjectDependencies) = postProject - {8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Benchmarks", "Markdig.Benchmarks\Markdig.Benchmarks.csproj", "{6A19F040-BC7C-4283-873A-177B5324F1ED}" - ProjectSection(ProjectDependencies) = postProject - {8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig.WebApp", "Markdig.WebApp\Markdig.WebApp.csproj", "{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnicodeNormDApp", "UnicodeNormDApp\UnicodeNormDApp.csproj", "{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mdtoc", "mdtoc\mdtoc.csproj", "{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.Build.0 = Release|Any CPU - {A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.Build.0 = Release|Any CPU - {6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.Build.0 = Release|Any CPU - {3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.Build.0 = Release|Any CPU - {33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.Build.0 = Debug|Any CPU - {33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.ActiveCfg = Release|Any CPU - {33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.Build.0 = Release|Any CPU - {E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {D068F7B6-6ACC-456C-A2E1-10EA746D956D} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28407.52 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{061866E2-005C-4D13-A338-EA464BBEC60F}" + ProjectSection(SolutionItems) = preProject + ..\appveyor.yml = ..\appveyor.yml + ..\changelog.md = ..\changelog.md + ..\license.txt = ..\license.txt + ..\readme.md = ..\readme.md + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig", "Markdig\Markdig.csproj", "{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Tests", "Markdig.Tests\Markdig.Tests.csproj", "{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}" + ProjectSection(ProjectDependencies) = postProject + {8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Benchmarks", "Markdig.Benchmarks\Markdig.Benchmarks.csproj", "{6A19F040-BC7C-4283-873A-177B5324F1ED}" + ProjectSection(ProjectDependencies) = postProject + {8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig.WebApp", "Markdig.WebApp\Markdig.WebApp.csproj", "{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnicodeNormDApp", "UnicodeNormDApp\UnicodeNormDApp.csproj", "{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mdtoc", "mdtoc\mdtoc.csproj", "{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpecFileGen", "SpecFileGen\SpecFileGen.csproj", "{DB6E2ED5-7884-4E97-84AF-35E2480CF685}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.Build.0 = Release|Any CPU + {A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.Build.0 = Release|Any CPU + {6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.Build.0 = Release|Any CPU + {3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.Build.0 = Release|Any CPU + {33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.Build.0 = Release|Any CPU + {E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.Build.0 = Release|Any CPU + {DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D068F7B6-6ACC-456C-A2E1-10EA746D956D} + EndGlobalSection +EndGlobal