Skip to content

Commit

Permalink
Add new Mosa.Utility.CreateCoreLib tool (#1234)
Browse files Browse the repository at this point in the history
* Add new Mosa.Tool.CreateCoreLib tool

Signed-off-by: AnErrupTion <[email protected]>

* Rename to Mosa.Utility.CreateCoreLib

Signed-off-by: AnErrupTion <[email protected]>

---------

Signed-off-by: AnErrupTion <[email protected]>
  • Loading branch information
AnErrupTion authored May 18, 2024
1 parent b7d9f02 commit 5ac3b6c
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Source/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<PropertyGroup>
<OutputPath>../../bin</OutputPath>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>11.0</LangVersion>
<LangVersion>12.0</LangVersion>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<!-- <WarningsAsErrors>nullable</WarningsAsErrors>-->
Expand Down
27 changes: 24 additions & 3 deletions Source/Mosa.Linux.sln
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BareMetal.Demos", "BareMeta
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{A4028807-1B21-4D14-9CE3-5FD1AAD9EDD7}"
ProjectSection(SolutionItems) = preProject
Docs\a-dive-into-baremetal.rst = Docs\a-dive-into-baremetal.rst
Docs\authors.rst = Docs\authors.rst
Docs\build-status.rst = Docs\build-status.rst
Docs\command-line-arguments.rst = Docs\command-line-arguments.rst
Expand All @@ -163,6 +164,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentat
Docs\introduction.rst = Docs\introduction.rst
Docs\license.rst = Docs\license.rst
Docs\mosa-runtime-tables.dot = Docs\mosa-runtime-tables.dot
Docs\optimization-options.rst = Docs\optimization-options.rst
Docs\project-structure.rst = Docs\project-structure.rst
Docs\runtime-tables.rst = Docs\runtime-tables.rst
Docs\settings-options.rst = Docs\settings-options.rst
Docs\tool-compiler.rst = Docs\tool-compiler.rst
Expand All @@ -172,9 +175,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentat
Docs\tool-launcher.rst = Docs\tool-launcher.rst
Docs\unit-tests.rst = Docs\unit-tests.rst
Docs\usb-flash-drive-installation.rst = Docs\usb-flash-drive-installation.rst
Docs\project-structure.rst = Docs\project-structure.rst
Docs\optimization-options.rst = Docs\optimization-options.rst
Docs\a-dive-into-baremetal.rst = Docs\a-dive-into-baremetal.rst
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mosa.BareMetal.HelloWorld.x64", "Mosa.BareMetal.HelloWorld.x64\Mosa.BareMetal.HelloWorld.x64.csproj", "{5F4136A8-EB83-41BE-9D81-B16E723AAE29}"
Expand Down Expand Up @@ -239,6 +239,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mosa.Workspace.GDB.Debug",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mosa.Tool.Explorer.Avalonia", "Mosa.Tool.Explorer.Avalonia\Mosa.Tool.Explorer.Avalonia.csproj", "{FBC3B0CD-D775-41C6-A735-F59118838397}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mosa.Utility.CreateCoreLib", "Mosa.Utility.CreateCoreLib\Mosa.Utility.CreateCoreLib.csproj", "{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1405,6 +1407,24 @@ Global
{FBC3B0CD-D775-41C6-A735-F59118838397}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{FBC3B0CD-D775-41C6-A735-F59118838397}.Release|x86.ActiveCfg = Release|Any CPU
{FBC3B0CD-D775-41C6-A735-F59118838397}.Release|x86.Build.0 = Release|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Debug|x86.ActiveCfg = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Debug|x86.Build.0 = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Description|Any CPU.ActiveCfg = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Description|Any CPU.Build.0 = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Description|Mixed Platforms.ActiveCfg = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Description|Mixed Platforms.Build.0 = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Description|x86.ActiveCfg = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Description|x86.Build.0 = Debug|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Release|Any CPU.Build.0 = Release|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Release|x86.ActiveCfg = Release|Any CPU
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1479,6 +1499,7 @@ Global
{00512754-29C4-4AA3-8619-8C04120D7B55} = {D1C4B715-9764-4430-B3D3-676B0EBCE75A}
{A4028807-1B21-4D14-9CE3-5FD1AAD9EDD7} = {F0EFF742-92D5-4219-939A-8F6F8DAB24E5}
{FBC3B0CD-D775-41C6-A735-F59118838397} = {D032B24A-CE3A-4881-BACE-CC4FE0AFD69D}
{3347E41D-AD8B-4891-9A9B-5EE36BACA6F1} = {90065B0F-1BFE-40D8-AED5-11096B2535B0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C22A5C94-6B05-4B1B-845A-A2EA7615E093}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="ICSharpCode.Decompiler" Version="8.2.0.7535" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
</ItemGroup>

</Project>
12 changes: 12 additions & 0 deletions Source/Mosa.Utility.CreateCoreLib/Options.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using CommandLine;

namespace Mosa.Utility.CreateCoreLib;

public class Options
{
[Option('o', "output", Required = true, HelpText = "Sets the output directory.")]
public string? OutputDirectory { get; set; }

[Option('c', "copy-files", Required = false, HelpText = "If enabled, only copies and patches the source files and stops.")]
public bool CopyFiles { get; set; }
}
71 changes: 71 additions & 0 deletions Source/Mosa.Utility.CreateCoreLib/Patcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System.Diagnostics;

namespace Mosa.Utility.CreateCoreLib;

/// <summary>
/// A custom patch system. It isn't designed to be particularly fast, but is rather designed with the simplicity of a singular patch in mind.
/// </summary>
public static class Patcher
{
/*
* Modes:
* rs - [r]emove line if it [s]tarts with str1
* R - [R]eplace all occurences of str1 with str2
*/
public record PatchRecord(string File, string Mode, string Str1, string? Str2);

// First round of patches before compilation (i.e. with files directly from the repository), they use paths relative to the current directory.
// The reference API source files weren't designed to be compiled as-is, so they have to be patched very lightly.
public static readonly PatchRecord[] FirstPatches =
[
new PatchRecord("runtime/src/libraries/System.DirectoryServices/ref/System.DirectoryServices.manual.cs", "rs", "[assembly: TypeForwardedTo(", null),
new PatchRecord("runtime/src/libraries/System.Net.Http.Json/ref/System.Net.Http.Json.cs", "R", "protected override bool TryComputeLength", "protected internal override bool TryComputeLength"),
new PatchRecord("runtime/src/libraries/System.Net.Http.WinHttpHandler/ref/System.Net.Http.WinHttpHandler.cs", "R", "protected override System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage> SendAsync", "protected internal override System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage> SendAsync"),
new PatchRecord("runtime/src/libraries/System.Data.Common/ref/System.Data.Common.cs", "R", "protected override System.Xml.XPath.XPathNavigator? CreateNavigator", "protected internal override System.Xml.XPath.XPathNavigator? CreateNavigator"),
new PatchRecord("runtime/src/libraries/System.Configuration.ConfigurationManager/ref/System.Configuration.ConfigurationManager.cs", "R", "[System.ObsoleteAttribute(System.Obsoletions.BinaryFormatterMessage + @\"", "[System.ObsoleteAttribute(\"BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information")
];

// Second round of patches after decompilation, they use paths relative to the output directory.
// The code produced by ICSharpCode's decompiler isn't perfected, and isn't designed to be compiled directly either.
// While it requires a bit more patching than before, it still isn't a dramatic amount of patches.
public static readonly PatchRecord[] SecondPatches =
[
new PatchRecord("System.Collections.Generic/PriorityQueue.cs", "R", "IEnumerator<(TElement, TPriority)>", "IEnumerator<(TElement Element, TPriority Priority)>"),
new PatchRecord("System.Collections.Generic/PriorityQueue.cs", "R", "IEnumerable<(TElement, TPriority)>", "IEnumerable<(TElement Element, TPriority Priority)>"),
new PatchRecord("System/Nullable.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System/ReadOnlySpan.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Runtime.InteropServices/Marshal.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Runtime.InteropServices/MemoryMarshal.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Runtime.CompilerServices/Unsafe.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Numerics/Vector.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Runtime.Intrinsics/Vector64.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Runtime.Intrinsics/Vector128.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Runtime.Intrinsics/Vector256.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Runtime.Intrinsics/Vector512.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Threading/Volatile.cs", "R", "[RequiresLocation]", string.Empty),
new PatchRecord("System.Threading/Interlocked.cs", "R", "[RequiresLocation]", string.Empty)
];

public static string Patch(PatchRecord patch, string path = "")
{
var file = !string.IsNullOrEmpty(path) ? Path.Combine(path, patch.File) : patch.File;

switch (patch.Mode)
{
case "rs":
{
var code = File.ReadAllLines(file).ToList();
var temp = code[..];

foreach (var line in temp)
if (line.StartsWith(patch.Str1))
code.Remove(line);

return string.Join('\n', code);
}
case "R": return File.ReadAllText(file).Replace(patch.Str1, patch.Str2);
}

throw new UnreachableException();
}
}
124 changes: 124 additions & 0 deletions Source/Mosa.Utility.CreateCoreLib/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using System.Diagnostics;
using CommandLine;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Mosa.Utility.CreateCoreLib;

var options = Parser.Default.ParseArguments<Options>(args).Value;
if (options == null) return;

var outputDirectory = options.OutputDirectory;
var copyFiles = options.CopyFiles;

if (string.IsNullOrEmpty(outputDirectory))
{
Console.WriteLine("Invalid output directory. Usage: Mosa.Utility.CreateCoreLib --output/-o <directory>");
return;
}

Directory.CreateDirectory(outputDirectory);

if (!Directory.Exists("runtime"))
{
Console.WriteLine("Cloning .NET runtime GitHub repository...");
Process.Start("git", "clone https://github.com/dotnet/runtime -b release/8.0 --depth=1")?.WaitForExit();
}

Console.WriteLine(copyFiles ? "Copying files..." : "Parsing input files...");

var syntaxTrees = new List<SyntaxTree>();

foreach (var folder in Directory.EnumerateDirectories(Path.Combine("runtime", "src", "libraries")))
{
// We don't need those namespaces so we exclude them.
if (folder.Contains("Microsoft.Bcl.") || folder.Contains("Microsoft.Extensions.")) continue;

var refDirectory = Path.Combine(folder, "ref");
if (!Directory.Exists(refDirectory))
continue;

foreach (var file in Directory.GetFiles(refDirectory, "*.cs"))
{
// All of these files contain "type forwards", which basically forward certain types to other assemblies.
// Since we have a monolithic assembly, we can safely ignore those (though we'll still have to patch a few).
// They also cause compilation errors because they're not designed to be compiled, so removing them is a must.
if (file.EndsWith(".Forwards.cs") || file.EndsWith(".netframework.cs") || file.EndsWith(".TypeForwards.cs")
|| file.Contains(".Typeforwards."))
continue;

var fileName = Path.GetFileName(file);
string? text = null;

var patch = Patcher.FirstPatches.FirstOrDefault(x => file.EndsWith(x.File));
if (patch != default)
{
Console.WriteLine($"Patching {fileName}...");
text = Patcher.Patch(patch);
}

if (copyFiles)
{
var outputPath = Path.Combine(outputDirectory, fileName);

if (text != null)
File.WriteAllText(outputPath, text);
else
File.Copy(file, outputPath, true);

continue;
}

text ??= File.ReadAllText(file);
syntaxTrees.Add(CSharpSyntaxTree.ParseText(text, new CSharpParseOptions(preprocessorSymbols: ["NETCOREAPP"])));
}
}

if (copyFiles) return;

var compilation = CSharpCompilation.Create("System.Runtime", syntaxTrees, null,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true));
var outputFile = compilation.AssemblyName + ".dll";

Console.WriteLine("Compiling source files...");

using (var stream = File.OpenWrite("System.Runtime.dll"))
{
var result = compilation.Emit(stream);
if (!result.Success)
{
var failures = result.Diagnostics
.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);

foreach (var diagnostic in failures)
Console.WriteLine($"{diagnostic.Id}: {diagnostic.GetMessage()}");

return;
}
}

Console.WriteLine("Decompiling assembly...");

var module = new PEFile(outputFile);
var resolver = new UniversalAssemblyResolver(outputFile, true, module.Metadata.DetectTargetFrameworkId());
var decompiler = new WholeProjectDecompiler(resolver);

decompiler.DecompileProject(module, outputDirectory);

// We don't need the project file because we should already have one. Even if it's missing, it's trivial to create.
Console.WriteLine("Removing project file...");
File.Delete(Path.Combine(outputDirectory, Path.ChangeExtension(outputFile, "csproj")));

// The assembly will generate its own assembly information at compile time, so we don't need to pre-define it.
Console.WriteLine("Removing Properties folder...");
Directory.Delete(Path.Combine(outputDirectory, "Properties"), true);

foreach (var patch in Patcher.SecondPatches)
{
var file = Path.Combine(outputDirectory, patch.File);
Console.WriteLine($"Patching {patch.File}...");

var code = Patcher.Patch(patch, outputDirectory);
File.WriteAllText(file, code);
}
26 changes: 24 additions & 2 deletions Source/Mosa.sln
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BareMetal.Demos", "BareMeta
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{A4028807-1B21-4D14-9CE3-5FD1AAD9EDD7}"
ProjectSection(SolutionItems) = preProject
Docs\a-dive-into-baremetal.rst = Docs\a-dive-into-baremetal.rst
Docs\authors.rst = Docs\authors.rst
Docs\build-status.rst = Docs\build-status.rst
Docs\command-line-arguments.rst = Docs\command-line-arguments.rst
Expand All @@ -177,16 +178,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentat
Docs\compiler-transformations.rst = Docs\compiler-transformations.rst
Docs\conf.py = Docs\conf.py
Docs\contents.txt = Docs\contents.txt
Docs\create-operating-system.rst = Docs\create-operating-system.rst
Docs\demos.rst = Docs\demos.rst
Docs\faq.rst = Docs\faq.rst
Docs\get-involved.rst = Docs\get-involved.rst
Docs\getting-started.rst = Docs\getting-started.rst
Docs\index.rst = Docs\index.rst
Docs\introduction.rst = Docs\introduction.rst
Docs\license.rst = Docs\license.rst
Docs\mosa-project-structure.rst = Docs\mosa-project-structure.rst
Docs\mosa-runtime-tables.dot = Docs\mosa-runtime-tables.dot
Docs\optimization-options.rst = Docs\optimization-options.rst
Docs\project-structure.rst = Docs\project-structure.rst
Docs\runtime-tables.rst = Docs\runtime-tables.rst
Docs\settings-options.rst = Docs\settings-options.rst
Docs\tool-compiler.rst = Docs\tool-compiler.rst
Expand Down Expand Up @@ -255,6 +256,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Template", "Template", "{A1
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mosa.Tool.Explorer.Avalonia", "Mosa.Tool.Explorer.Avalonia\Mosa.Tool.Explorer.Avalonia.csproj", "{763FDCF2-2501-4756-A7A1-62813D610B00}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mosa.Utility.CreateCoreLib", "Mosa.Utility.CreateCoreLib\Mosa.Utility.CreateCoreLib.csproj", "{B940F546-628B-45C0-9FA5-4479F166033F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1457,6 +1460,24 @@ Global
{763FDCF2-2501-4756-A7A1-62813D610B00}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{763FDCF2-2501-4756-A7A1-62813D610B00}.Release|x86.ActiveCfg = Release|Any CPU
{763FDCF2-2501-4756-A7A1-62813D610B00}.Release|x86.Build.0 = Release|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Debug|x86.ActiveCfg = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Debug|x86.Build.0 = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Description|Any CPU.ActiveCfg = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Description|Any CPU.Build.0 = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Description|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Description|Mixed Platforms.Build.0 = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Description|x86.ActiveCfg = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Description|x86.Build.0 = Debug|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Release|Any CPU.Build.0 = Release|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Release|x86.ActiveCfg = Release|Any CPU
{B940F546-628B-45C0-9FA5-4479F166033F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1534,6 +1555,7 @@ Global
{26667157-E909-4F7A-9130-F8B5C15A1E4E} = {3A538FDC-0226-4971-A3C0-31570CDA340D}
{A1660856-2153-477F-BAAC-39E8BEB75971} = {F0EFF742-92D5-4219-939A-8F6F8DAB24E5}
{763FDCF2-2501-4756-A7A1-62813D610B00} = {D032B24A-CE3A-4881-BACE-CC4FE0AFD69D}
{B940F546-628B-45C0-9FA5-4479F166033F} = {90065B0F-1BFE-40D8-AED5-11096B2535B0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C22A5C94-6B05-4B1B-845A-A2EA7615E093}
Expand Down

0 comments on commit 5ac3b6c

Please sign in to comment.