From f58566c053f3bce62254591a51d8021ed38b989a Mon Sep 17 00:00:00 2001 From: Kai Jellinghaus Date: Wed, 10 Aug 2022 17:01:37 +0200 Subject: [PATCH 1/4] Add SilkTouch dotnet tool --- .config/dotnet-tools.json | 12 + Directory.Packages.props | 2 +- Silk.NET.sln | 45 +- .../Silk.NET.SilkTouch.DotnetTool/Program.cs | 523 ++++++++++++++++++ .../Silk.NET.SilkTouch.DotnetTool.csproj} | 8 +- .../Silk.NET.SilkTouch.Roslyn.csproj | 12 - .../ClangScraper.cs | 27 +- .../Silk.NET.SilkTouch.Scraper/XmlVisitor.cs | 6 + .../Silk.NET.SilkTouch.Symbols.csproj | 1 + .../Silk.NET.SilkTouch.Symbols/TypeId.cs | 25 + src/generators/SilkTouch/ExitCodes.cs | 14 - src/generators/SilkTouch/LogMode.cs | 32 -- src/generators/SilkTouch/Program.cs | 7 - 13 files changed, 611 insertions(+), 103 deletions(-) create mode 100644 .config/dotnet-tools.json create mode 100644 src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs rename src/generators/{SilkTouch/SilkTouch.csproj => Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj} (72%) delete mode 100644 src/generators/Silk.NET.SilkTouch.Roslyn/Silk.NET.SilkTouch.Roslyn.csproj delete mode 100644 src/generators/SilkTouch/ExitCodes.cs delete mode 100644 src/generators/SilkTouch/LogMode.cs delete mode 100644 src/generators/SilkTouch/Program.cs diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000000..76a3dd65c9 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "silk.net.silktouch.dotnettool": { + "version": "3.0.0-preview", + "commands": [ + "silktouch" + ] + } + } +} \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index ef75ce2282..6abca158b0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,7 +17,7 @@ - + diff --git a/Silk.NET.sln b/Silk.NET.sln index c7bff49285..b64a83616c 100644 --- a/Silk.NET.sln +++ b/Silk.NET.sln @@ -44,10 +44,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.Maths.Benchmarks", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.Scraper", "src\generators\Silk.NET.SilkTouch.Scraper\Silk.NET.SilkTouch.Scraper.csproj", "{EA623F04-DADA-4714-B2C5-44C82E211492}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilkTouch", "src\generators\SilkTouch\SilkTouch.csproj", "{7DF67686-3F46-4569-935F-4A9E9903B575}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.Roslyn", "src\generators\Silk.NET.SilkTouch.Roslyn\Silk.NET.SilkTouch.Roslyn.csproj", "{11841F56-7603-40D9-BC06-10EBBE17D905}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.Emitter", "src\generators\Silk.NET.SilkTouch.Emitter\Silk.NET.SilkTouch.Emitter.csproj", "{50F26B27-32B6-4D66-ADD5-CC9C38373B19}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.Core", "src\libraries\Silk.NET.Core\Silk.NET.Core.csproj", "{69CF4437-59F7-4304-9AE1-4B58E1A93367}" @@ -78,6 +74,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.TypeReso EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.TypeResolution.Tests", "tests\Silk.NET.SilkTouch.TypeResolution.Tests\Silk.NET.SilkTouch.TypeResolution.Tests.csproj", "{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.DotnetTool", "src\generators\Silk.NET.SilkTouch.DotnetTool\Silk.NET.SilkTouch.DotnetTool.csproj", "{50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -124,30 +122,6 @@ Global {EA623F04-DADA-4714-B2C5-44C82E211492}.Release|x64.Build.0 = Release|Any CPU {EA623F04-DADA-4714-B2C5-44C82E211492}.Release|x86.ActiveCfg = Release|Any CPU {EA623F04-DADA-4714-B2C5-44C82E211492}.Release|x86.Build.0 = Release|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Debug|x64.ActiveCfg = Debug|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Debug|x64.Build.0 = Debug|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Debug|x86.ActiveCfg = Debug|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Debug|x86.Build.0 = Debug|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Release|Any CPU.Build.0 = Release|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Release|x64.ActiveCfg = Release|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Release|x64.Build.0 = Release|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Release|x86.ActiveCfg = Release|Any CPU - {7DF67686-3F46-4569-935F-4A9E9903B575}.Release|x86.Build.0 = Release|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Debug|Any CPU.Build.0 = Debug|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Debug|x64.ActiveCfg = Debug|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Debug|x64.Build.0 = Debug|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Debug|x86.ActiveCfg = Debug|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Debug|x86.Build.0 = Debug|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Release|Any CPU.ActiveCfg = Release|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Release|Any CPU.Build.0 = Release|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Release|x64.ActiveCfg = Release|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Release|x64.Build.0 = Release|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Release|x86.ActiveCfg = Release|Any CPU - {11841F56-7603-40D9-BC06-10EBBE17D905}.Release|x86.Build.0 = Release|Any CPU {50F26B27-32B6-4D66-ADD5-CC9C38373B19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {50F26B27-32B6-4D66-ADD5-CC9C38373B19}.Debug|Any CPU.Build.0 = Debug|Any CPU {50F26B27-32B6-4D66-ADD5-CC9C38373B19}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -316,6 +290,18 @@ Global {89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Release|x64.Build.0 = Release|Any CPU {89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Release|x86.ActiveCfg = Release|Any CPU {89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Release|x86.Build.0 = Release|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Debug|x64.ActiveCfg = Debug|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Debug|x64.Build.0 = Debug|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Debug|x86.ActiveCfg = Debug|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Debug|x86.Build.0 = Debug|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Release|Any CPU.Build.0 = Release|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Release|x64.ActiveCfg = Release|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Release|x64.Build.0 = Release|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Release|x86.ActiveCfg = Release|Any CPU + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -330,8 +316,6 @@ Global {8238D9F3-E158-4633-8017-B29AA3AD61F7} = {EA3CA547-452A-4D9E-BDB3-3BC5D7F15ACA} {CB8B28DE-456A-4B8E-85A6-2C50CEE08CA2} = {FD15E196-1C63-47D6-8AD5-64F015120B4B} {EA623F04-DADA-4714-B2C5-44C82E211492} = {8238D9F3-E158-4633-8017-B29AA3AD61F7} - {7DF67686-3F46-4569-935F-4A9E9903B575} = {8238D9F3-E158-4633-8017-B29AA3AD61F7} - {11841F56-7603-40D9-BC06-10EBBE17D905} = {8238D9F3-E158-4633-8017-B29AA3AD61F7} {50F26B27-32B6-4D66-ADD5-CC9C38373B19} = {8238D9F3-E158-4633-8017-B29AA3AD61F7} {69CF4437-59F7-4304-9AE1-4B58E1A93367} = {C9718C94-2F21-4E8D-B55D-8F0B1A131346} {AF6E05E0-9C51-4D52-AC7E-056714CAC5F4} = {9020C7C6-C366-4BD3-8C8A-F81394EC7174} @@ -346,6 +330,7 @@ Global {381D1039-3259-488F-BB25-D90EE63A3E82} = {94D5D1E1-B998-4CB1-9D04-DA138A2B0F3C} {7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2} = {8238D9F3-E158-4633-8017-B29AA3AD61F7} {89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE} = {94D5D1E1-B998-4CB1-9D04-DA138A2B0F3C} + {50F2BCCC-BDC8-4694-B55E-583A6A77E1CF} = {8238D9F3-E158-4633-8017-B29AA3AD61F7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F5273D7F-3334-48DF-94E3-41AE6816CD4D} diff --git a/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs b/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs new file mode 100644 index 0000000000..aee80b2d6b --- /dev/null +++ b/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs @@ -0,0 +1,523 @@ +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using System.Xml; +using ClangSharp; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Silk.NET.SilkTouch.Emitter; +using Silk.NET.SilkTouch.Scraper; +using Silk.NET.SilkTouch.Symbols; +using Silk.NET.SilkTouch.TypeResolution; +using Symbol=Silk.NET.SilkTouch.Symbols.Symbol; +using Type=System.Type; + +namespace Silk.NET.SilkTouch.DotnetTool +{ + internal class Program + { + static async Task Main(string[] args) + { + var logLevel = new Option(new[] { "log-level", "l" }, () => LogLevel.Information); + var useStandardIncludes = new Option(new[] { "standard-includes", "si" }, () => true); + var extraIncludedDirectories = new Option + (new[] { "extra-included-directories", "exd" }, Array.Empty) + { + AllowMultipleArgumentsPerToken = true + }; + var includedNames = new Option + (new[] { "included-names" }, Array.Empty) + { + AllowMultipleArgumentsPerToken = true + }; + var excludedNames = new Option + (new[] { "excluded-names" }, Array.Empty) + { + AllowMultipleArgumentsPerToken = true + }; + var definedMacros = new Option + (new[] { "defined-macros" }, Array.Empty) + { + AllowMultipleArgumentsPerToken = true + }; + var headerFile = new Option(new[] { "header-file" }) + { + IsRequired = true + }; + + var usedSymbolVisitors = new Option + ( + new[] { "used-symbol-visitors", "vs" }, + () => AvailableSymbolVisitors.AllTypeResolvers, + "The symbol visitors called to process the raw symbols into those used to emit C#." + + " Generally includes most type resolvers and overloaders as appropriate." + ); + + var xmlFile = new Option(new[] { "xml-file", "xf" }, "The File to read/write XML from/to"); + var rawSymbolsFile = new Option + (new[] { "raw-symbols-file", "rsf" }, "The File to read/write Raw Symbols from/to"); + var processedSymbolsFile = new Option + (new[] { "processed-symbols-file", "psf" }, "The File to read/write Processed Symbols from/to"); + var csharpDir = new Option + (new[] { "csharp-dir", "csd" }, "The Directory to read/write C# files from/to"); + + var generateBindings = new Command("bindings") + { + xmlFile, + rawSymbolsFile, + processedSymbolsFile, + csharpDir, + extraIncludedDirectories, + includedNames, + excludedNames, + definedMacros, + headerFile, + usedSymbolVisitors + }; + generateBindings.SetHandler + ( + + (async (context) => + { + var serviceProvider = CreateServiceProvider(context.ParseResult.GetValueForOption(logLevel)); + XmlDocument xmlDoc = GetXml + ( + serviceProvider, + context.ParseResult.GetValueForOption(useStandardIncludes), + context.ParseResult.GetValueForOption(extraIncludedDirectories)!, + context.ParseResult.GetValueForOption(headerFile)!.FullName, + context.ParseResult.GetValueForOption(includedNames)!, + context.ParseResult.GetValueForOption(excludedNames)!, + context.ParseResult.GetValueForOption(definedMacros)! + ); + var xmlFileRes = context.ParseResult.GetValueForOption(xmlFile); + if (xmlFileRes is not null) + { + if (xmlFileRes.Exists) xmlFileRes.Delete(); + await using var stream = xmlFileRes.OpenWrite(); + await using var writer = new XmlTextWriter(stream, Encoding.UTF8); + xmlDoc.WriteTo(writer); + } + + var (rawSymbols, typeStore) = GetSymbols(serviceProvider, xmlDoc); + var rawSymbolsFileRes = context.ParseResult.GetValueForOption(rawSymbolsFile); + if (rawSymbolsFileRes is not null) + { + if (rawSymbolsFileRes.Exists) rawSymbolsFileRes.Delete(); + await using var stream = rawSymbolsFileRes.OpenWrite(); + await JsonSerializer.SerializeAsync(stream, rawSymbols, GetJsonOptions()); + } + + var processedSymbols = ProcessSymbols + ( + serviceProvider, + rawSymbols, + typeStore, + context.ParseResult.GetValueForOption(usedSymbolVisitors) + ); + var processedSymbolsFileRes = context.ParseResult.GetValueForOption(processedSymbolsFile); + if (processedSymbolsFileRes is not null) + { + if (processedSymbolsFileRes.Exists) processedSymbolsFileRes.Delete(); + await using var stream = processedSymbolsFileRes.OpenWrite(); + await JsonSerializer.SerializeAsync(stream, processedSymbols, GetJsonOptions()); + } + + var csharp = GetCSharp(serviceProvider, processedSymbols, typeStore).Select(x => x.ToFullString()) + .Select((x, i) => (x, i.ToString())); + var csharpDirRes = context.ParseResult.GetValueForOption(csharpDir); + if (csharpDirRes is not null) + { + foreach (var (contents, fileName) in csharp) + { + var path = Path.Combine(csharpDirRes.FullName, fileName); + await File.WriteAllTextAsync(path, contents); + } + } + }) + ); + + var generateCSharp = new Command("csharp") + { + processedSymbolsFile, csharpDir, logLevel + }; + generateCSharp.SetHandler + ( + (async static (processedSymbolsFile, csharpDir, logLevel) => + { + var serviceProvider = CreateServiceProvider(logLevel); + Symbol[] symbols; + TypeStore typeStore; + if (processedSymbolsFile is null) + { + symbols = Array.Empty(); + typeStore = new TypeStore(); + } + else + { + var logger = serviceProvider.GetRequiredService>(); + logger.LogInformation("Loading Symbols from File..."); + await using var stream = processedSymbolsFile.OpenRead(); + symbols = await JsonSerializer.DeserializeAsync(stream, GetJsonOptions()) ?? Array.Empty(); + logger.LogInformation("Loaded {count} Symbols from Disk. Restoring Type Store...", symbols.Length); + typeStore = RestoreTypeStore(symbols); + logger.LogInformation("Type Store Restored"); + } + + var csharp = GetCSharp(serviceProvider, symbols, typeStore).Select(x => x.ToFullString()) + .Select((x, i) => (x, i.ToString())); + if (csharpDir is not null) + { + foreach (var (contents, fileName) in csharp) + { + var path = Path.Combine(csharpDir.FullName, fileName); + await File.WriteAllTextAsync(path, contents); + } + } + }), + processedSymbolsFile, + csharpDir, + logLevel + ); + + var generateRawSymbols = new Command("raw-symbols") + { + xmlFile, + rawSymbolsFile, + logLevel + }; + generateRawSymbols.SetHandler( + (Func) + (async static (xmlFile, rawSymbolsFile, logLevel) => + { + var serviceProvider = CreateServiceProvider(logLevel); + Symbol[] symbols; + TypeStore typeStore; + if (xmlFile is null) + { + symbols = Array.Empty(); + typeStore = new TypeStore(); + } + else + { + var logger = serviceProvider.GetRequiredService>(); + var xmlDoc = new XmlDocument(); + await using var stream = xmlFile.OpenRead(); + xmlDoc.Load(stream); + (symbols, typeStore) = GetSymbols(serviceProvider, xmlDoc); + logger.LogTrace("{count} Symbols Scraped from XML", symbols.Length); + } + + if (rawSymbolsFile is not null) + { + if (rawSymbolsFile.Exists) rawSymbolsFile.Delete(); + await using var stream = rawSymbolsFile.OpenWrite(); + await JsonSerializer.SerializeAsync(stream, symbols, GetJsonOptions()); + } + }), xmlFile, rawSymbolsFile, logLevel); + + var generateXML = new Command("xml") + { + xmlFile, + logLevel, + useStandardIncludes, + extraIncludedDirectories, + includedNames, + excludedNames, + definedMacros, + headerFile + }; + generateXML.SetHandler + ( + (async static (xmlFile, logLevel, useStandardIncludes, extraIncludedDirectories, + includedNames, excludedNames, definedMacros, headerFile) => + { + var serviceProvider = CreateServiceProvider(logLevel); + XmlDocument xmlDoc = GetXml + ( + serviceProvider, + useStandardIncludes, + extraIncludedDirectories, + headerFile.FullName, + includedNames, + excludedNames, + definedMacros + ); + if (xmlFile is not null) + { + if (xmlFile.Exists) xmlFile.Delete(); + await using var stream = xmlFile.OpenWrite(); + await using var writer = new XmlTextWriter(stream, Encoding.UTF8); + xmlDoc.WriteTo(writer); + } + }), + xmlFile, + logLevel, + useStandardIncludes, + extraIncludedDirectories, + includedNames, + excludedNames, + definedMacros, + headerFile + ); + + var processSymbols = new Command("process-symbols") + { + logLevel, + rawSymbolsFile, + processedSymbolsFile, + usedSymbolVisitors + }; + processSymbols.SetHandler( + async static (logLevel, rawSymbolsFile, processedSymbolsFile, usedSymbolVisitors) => + { + var serviceProvider = CreateServiceProvider(logLevel); + + Symbol[] rawSymbols; + TypeStore typeStore; + if (rawSymbolsFile is null) + { + rawSymbols = Array.Empty(); + typeStore = new TypeStore(); + } + else + { + var logger = serviceProvider.GetRequiredService>(); + logger.LogInformation("Loading Symbols from File..."); + await using var stream = rawSymbolsFile.OpenRead(); + rawSymbols = await JsonSerializer.DeserializeAsync(stream, GetJsonOptions()) ?? Array.Empty(); + logger.LogInformation("Loaded {count} Symbols from Disk. Restoring Type Store...", rawSymbols.Length); + typeStore = RestoreTypeStore(rawSymbols); + logger.LogInformation("Type Store Restored"); + } + + var processedSymbols = ProcessSymbols(serviceProvider, rawSymbols, typeStore, usedSymbolVisitors); + if (processedSymbolsFile is not null) + { + if (processedSymbolsFile.Exists) processedSymbolsFile.Delete(); + await using var stream = processedSymbolsFile.OpenWrite(); + await JsonSerializer.SerializeAsync(stream, processedSymbols, GetJsonOptions()); + } + + }, logLevel, rawSymbolsFile, processedSymbolsFile, usedSymbolVisitors); + + var generate = new Command("generate") + { + generateBindings, + generateCSharp, + generateRawSymbols, + generateXML, + processSymbols + }; + + var rootCommand = new RootCommand + ("SilkTouch dotnet tool. Use to generate the silkiest bindings you've ever seen!") + { + generate + }; + + return await rootCommand.InvokeAsync(args); + } + + private static IEnumerable ProcessSymbols + (IServiceProvider serviceProvider, IEnumerable symbols, TypeStore typeStore, AvailableSymbolVisitors usedSymbolVisitors) + { + var visitors = new List(); + + if ((usedSymbolVisitors & AvailableSymbolVisitors.PointerTypeResolver) != 0) + { + visitors.Add(ActivatorUtilities.CreateInstance(serviceProvider, typeStore)); + } + + if ((usedSymbolVisitors & AvailableSymbolVisitors.InternalTypeResolver) != 0) + { + var typeScopeSymbolVisitor = ActivatorUtilities.CreateInstance + (serviceProvider, typeStore); + visitors.Add(typeScopeSymbolVisitor); + visitors.Add + ( + ActivatorUtilities.CreateInstance + (serviceProvider, typeScopeSymbolVisitor.RootScope, typeStore) + ); + } + + if ((usedSymbolVisitors & AvailableSymbolVisitors.PrimitiveTypeResolver) != 0) + { + visitors.Add(ActivatorUtilities.CreateInstance(serviceProvider, typeStore)); + } + + + foreach (var visitor in visitors) + { + symbols = symbols.Select(visitor.Visit); + } + + return symbols; + } + + private static XmlDocument GetXml + ( + IServiceProvider serviceProvider, + bool useStandardIncludes, + string[] extraIncludedDirectories, + string headerFile, + string[] includedNames, + string[] excludedNames, + string[] definedMacros + ) + { + var scraper = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); + var includedDirectories = new List(); + if (useStandardIncludes) + { + includedDirectories.AddRange(scraper.ResolveStandardIncludes()); + } + includedDirectories.AddRange(extraIncludedDirectories); + + var xml = scraper.GenerateXML(headerFile, includedNames, excludedNames, includedDirectories.ToArray(), definedMacros); + + return xml ?? new XmlDocument(); + } + + private static (Symbol[], TypeStore) GetSymbols(IServiceProvider serviceProvider, XmlDocument xml) + { + var scraper = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); + var typeStore = new TypeStore(); + var symbols = scraper.ScrapeXML(xml, typeStore).ToArray(); + + return (symbols, typeStore); + } + + private static TypeStore RestoreTypeStore(IEnumerable symbols) + { + var typeStore = new TypeStore(); + var visitor = new TypeStoreRestoreVisitor(typeStore); + + foreach (var symbol in symbols) + { + visitor.Visit(symbol); // discard, we don't care + } + + return typeStore; + } + + private static IEnumerable GetCSharp(IServiceProvider serviceProvider, IEnumerable symbols, TypeStore typeStore) + { + var emitter = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); + + return symbols.Select(x => emitter.Transform(x, typeStore)); + } + + + private sealed class TypeStoreRestoreVisitor : SymbolVisitor + { + public TypeStoreRestoreVisitor(TypeStore typeStore) : base(typeStore) + { } + + protected override TypeReference VisitTypeReference(TypeReference typeReference) + { + if (typeReference is UnresolvedTypeReference) return typeReference; + return base.VisitTypeReference(typeReference); + } + } + + + private static IServiceProvider CreateServiceProvider(LogLevel minimum) + { + + var configuration = new ConfigurationBuilder() + .AddEnvironmentVariables(source => source.Prefix = "SILK_DOTNET_") + .Build(); + var serviceProvider = new ServiceCollection() + .AddLogging(builder => + { + builder.AddConsole(); + builder.SetMinimumLevel(minimum); + } + ) + .Configure(configuration.GetSection("Scraper")) + .AddSingleton() + .AddSingleton() + .BuildServiceProvider(); + return serviceProvider; + } + + private static JsonSerializerOptions GetJsonOptions() + { + var opts = new JsonSerializerOptions(); + opts.Converters.Add(new SymbolJsonConverterFactory()); + return opts; + } + + private sealed class SymbolJsonConverterFactory : JsonConverterFactory + { + + public override bool CanConvert + (Type typeToConvert) => typeToConvert.IsAbstract && typeToConvert.IsAssignableTo(typeof(Symbol)); + public override JsonConverter? CreateConverter + (Type typeToConvert, JsonSerializerOptions options) => (JsonConverter?) Activator.CreateInstance + (typeof(SymbolJsonConverter<>).MakeGenericType(typeToConvert)); + } + + private sealed class SymbolJsonConverter + : JsonConverter + where T : Symbol + { + public override bool CanConvert + // If a leaf type (non-abstract) is given, serialize as that + (Type typeToConvert) => typeToConvert.IsAbstract && typeToConvert.IsAssignableTo(typeof(T)); + + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var document = JsonDocument.ParseValue(ref reader); + if (!document.RootElement.TryGetProperty("type", out var typeProp) || typeProp.GetString() is not {} type) + throw new InvalidOperationException("Cannot deserialize symbol without \"type\""); + + var symbolType = typeof(T).Assembly.GetTypes().First(x => x.Name == type); + if (symbolType is null) + throw new InvalidOperationException($"Could not find type {type}"); + + return document.Deserialize(symbolType, options) as T; + } + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + var type = value.GetType(); + writer.WritePropertyName("type"); + writer.WriteStringValue(type.Name); + + var element = JsonSerializer.SerializeToElement(value, type, options); + foreach (var prop in element.EnumerateObject()) + { + writer.WritePropertyName(prop.Name); + prop.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + } + } + + [Flags] + private enum AvailableSymbolVisitors + { + None = 0, + PointerTypeResolver = 1 << 1, + InternalTypeResolver = 1 << 2, + PrimitiveTypeResolver = 1 << 3, + AllTypeResolvers = PointerTypeResolver | InternalTypeResolver | PrimitiveTypeResolver, + } + } +} diff --git a/src/generators/SilkTouch/SilkTouch.csproj b/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj similarity index 72% rename from src/generators/SilkTouch/SilkTouch.csproj rename to src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj index 59f591eb17..2a9654e85b 100644 --- a/src/generators/SilkTouch/SilkTouch.csproj +++ b/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj @@ -5,20 +5,20 @@ net6.0 true silktouch + DotnetTool + true + $(NETCoreSdkRuntimeIdentifier) - + - - - diff --git a/src/generators/Silk.NET.SilkTouch.Roslyn/Silk.NET.SilkTouch.Roslyn.csproj b/src/generators/Silk.NET.SilkTouch.Roslyn/Silk.NET.SilkTouch.Roslyn.csproj deleted file mode 100644 index 621aa85eb3..0000000000 --- a/src/generators/Silk.NET.SilkTouch.Roslyn/Silk.NET.SilkTouch.Roslyn.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - netstandard2.0 - - - - - - - - diff --git a/src/generators/Silk.NET.SilkTouch.Scraper/ClangScraper.cs b/src/generators/Silk.NET.SilkTouch.Scraper/ClangScraper.cs index 1d6d69e1f3..72c172c59e 100644 --- a/src/generators/Silk.NET.SilkTouch.Scraper/ClangScraper.cs +++ b/src/generators/Silk.NET.SilkTouch.Scraper/ClangScraper.cs @@ -260,8 +260,29 @@ out var handle try { - - if (result != CXErrorCode.CXError_Success) + if (result == CXErrorCode.CXError_Failure) + { + if (handle.NumDiagnostics > 0) + { + for (uint i = 0; i < handle.NumDiagnostics; i++) + { + var x = handle.GetDiagnostic(i); + + logger.Log(x.Severity switch + { + CXDiagnosticSeverity.CXDiagnostic_Ignored => LogLevel.Trace, + CXDiagnosticSeverity.CXDiagnostic_Note => LogLevel.Debug, + CXDiagnosticSeverity.CXDiagnostic_Warning => LogLevel.Warning, + CXDiagnosticSeverity.CXDiagnostic_Error => LogLevel.Error, + CXDiagnosticSeverity.CXDiagnostic_Fatal => LogLevel.Critical, + _ => throw new ArgumentOutOfRangeException() + }, "{category} {message}", x.CategoryText, x.Format(0)); + } + } + + throw new Exception($"Could not parse translational unit. {Enum.GetName(result)}"); + } + else if (result != CXErrorCode.CXError_Success) { if (handle.NumDiagnostics > 0) { @@ -274,7 +295,7 @@ out var handle } } } - + throw new Exception($"Could not parse translational unit. {Enum.GetName(result)}"); } diff --git a/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs b/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs index c6641f8e45..1d4da4762c 100644 --- a/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs +++ b/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs @@ -26,6 +26,7 @@ public XmlVisitor(ILogger logger, TypeStore typeStore) public IEnumerable Visit(XmlNode node) { + _logger.LogTrace("Visiting XML Node of kind {name}", node.Name); switch (node) { case XmlElement { Name: "bindings" } bindings: @@ -38,7 +39,12 @@ public IEnumerable Visit(XmlNode node) return VisitField(field); default: { + _logger.LogWarning("Skipping unknown XML Node of kind {name}", node.Name); + #if DEBUG throw new NotImplementedException(); + #else + return Array.Empty(); + #endif } } } diff --git a/src/generators/Silk.NET.SilkTouch.Symbols/Silk.NET.SilkTouch.Symbols.csproj b/src/generators/Silk.NET.SilkTouch.Symbols/Silk.NET.SilkTouch.Symbols.csproj index 6cb3c8870a..02fd2f7770 100644 --- a/src/generators/Silk.NET.SilkTouch.Symbols/Silk.NET.SilkTouch.Symbols.csproj +++ b/src/generators/Silk.NET.SilkTouch.Symbols/Silk.NET.SilkTouch.Symbols.csproj @@ -8,5 +8,6 @@ + diff --git a/src/generators/Silk.NET.SilkTouch.Symbols/TypeId.cs b/src/generators/Silk.NET.SilkTouch.Symbols/TypeId.cs index d8fd01e44f..b7e9b960c2 100644 --- a/src/generators/Silk.NET.SilkTouch.Symbols/TypeId.cs +++ b/src/generators/Silk.NET.SilkTouch.Symbols/TypeId.cs @@ -1,11 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text.Json; +using System.Text.Json.Serialization; + namespace Silk.NET.SilkTouch.Symbols; /// /// The Id of a /// +[JsonConverter(typeof(TypeId.JsonConverter))] public readonly struct TypeId : IEquatable { private readonly Guid _guid; @@ -34,4 +38,25 @@ private TypeId(Guid guid) /// public static bool operator !=(TypeId left, TypeId right) => !left.Equals(right); + + /// + public override string ToString() + { + return _guid.ToString(); + } + + private sealed class JsonConverter : JsonConverter + { + public override TypeId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (!reader.TryGetGuid(out var guid)) + throw new InvalidOperationException("Could not parse TypeId"); + + return new TypeId(guid); + } + public override void Write(Utf8JsonWriter writer, TypeId value, JsonSerializerOptions options) + { + writer.WriteStringValue(value._guid); + } + } } diff --git a/src/generators/SilkTouch/ExitCodes.cs b/src/generators/SilkTouch/ExitCodes.cs deleted file mode 100644 index c4bfca7601..0000000000 --- a/src/generators/SilkTouch/ExitCodes.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace SilkTouch -{ - internal enum ExitCodes - { - Ok, - SubagentBadArgs, - SubagentFailedToParse, - SubagentFailedToStart, - FailedToGetMSBuild - } -} diff --git a/src/generators/SilkTouch/LogMode.cs b/src/generators/SilkTouch/LogMode.cs deleted file mode 100644 index 010ed44754..0000000000 --- a/src/generators/SilkTouch/LogMode.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace SilkTouch -{ - /// - /// The level of logging to use during generation. - /// - public enum LogMode - { - /// - /// Standard logging. Contains info throughout the generation process and warnings produced. - /// - Standard, - - /// - /// Silent logging. Only logs if there's an error and suppresses the "logo". - /// - Silent, - - /// - /// Verbose logging. Contains trace logs including suppressed diagnostics. Spammier. - /// - Verbose, - - /// - /// Very verbose logging. Contains every log message ever possibly conceived. - /// Spammiest, far too spammy for most uses. - /// - VVerbose - } -} diff --git a/src/generators/SilkTouch/Program.cs b/src/generators/SilkTouch/Program.cs deleted file mode 100644 index b88784b399..0000000000 --- a/src/generators/SilkTouch/Program.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace SilkTouch -{ - internal class Program - { - static void Main(string[] args) { } - } -} From c4743cefdb697a115ebdd3e5e304eb892669eacd Mon Sep 17 00:00:00 2001 From: Kai Jellinghaus Date: Wed, 10 Aug 2022 17:09:57 +0200 Subject: [PATCH 2/4] Update Deps --- src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs | 1 + .../Silk.NET.SilkTouch.DotnetTool.csproj | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs b/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs index aee80b2d6b..85cdde8993 100644 --- a/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs +++ b/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs @@ -75,6 +75,7 @@ static async Task Main(string[] args) var generateBindings = new Command("bindings") { + logLevel, xmlFile, rawSymbolsFile, processedSymbolsFile, diff --git a/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj b/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj index 2a9654e85b..38845f6e12 100644 --- a/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj +++ b/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj @@ -21,6 +21,8 @@ + + From 767c41f7f7433b57f5e94fc0b2746308b34dfb16 Mon Sep 17 00:00:00 2001 From: Kai Jellinghaus Date: Wed, 10 Aug 2022 17:10:59 +0200 Subject: [PATCH 3/4] Remove Console out --- .../Subagent/VisualStudioResolver.cs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/generators/Silk.NET.SilkTouch.Scraper/Subagent/VisualStudioResolver.cs b/src/generators/Silk.NET.SilkTouch.Scraper/Subagent/VisualStudioResolver.cs index 55c5d6fba9..d70027180c 100644 --- a/src/generators/Silk.NET.SilkTouch.Scraper/Subagent/VisualStudioResolver.cs +++ b/src/generators/Silk.NET.SilkTouch.Scraper/Subagent/VisualStudioResolver.cs @@ -96,10 +96,8 @@ public static bool TryGetVisualStudioInfo([NotNullWhen(true)] out VisualStudioIn Debug.WriteLine(visualStudios.Length + " Visual Studio installation(s)"); // cycle through candidate installations, try and find everything we're looking for. - var hasVs = false; foreach (var visualStudio in visualStudios) { - hasVs = true; Debug.WriteLine ( $"Testing \"{visualStudio.Name}\" v{visualStudio.Version.ToString(3)} at " + @@ -158,21 +156,8 @@ public static bool TryGetVisualStudioInfo([NotNullWhen(true)] out VisualStudioIn return true; } - if (!hasVs) - { - Console.WriteLine("No instance of Visual Studio found whatsoever."); - } - - // if any of it's still null, we couldn't find a candidate. - Console.WriteLine - ( - "Couldn't find a viable Visual Studio installation - ensure you have the Windows 10 SDK and C++ " + - "tools installed. SilkTouch Scraper may not function correctly without Visual Studio or Visual " + - "Studio Build Tools with these workloads." - ); - info = null; _vsInfoKnownError = true; return false; } -} \ No newline at end of file +} From 20e560518105512797c3a13c249a4840e2e7210c Mon Sep 17 00:00:00 2001 From: Kai Jellinghaus Date: Fri, 12 Aug 2022 10:57:27 +0200 Subject: [PATCH 4/4] Finishing touches --- src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs b/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs index 85cdde8993..22750c22b7 100644 --- a/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs +++ b/src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs @@ -71,7 +71,7 @@ static async Task Main(string[] args) var processedSymbolsFile = new Option (new[] { "processed-symbols-file", "psf" }, "The File to read/write Processed Symbols from/to"); var csharpDir = new Option - (new[] { "csharp-dir", "csd" }, "The Directory to read/write C# files from/to"); + (new[] { "csharp-directory", "csharp-dir", "csd" }, "The Directory to read/write C# files from/to"); var generateBindings = new Command("bindings") { @@ -137,7 +137,7 @@ static async Task Main(string[] args) } var csharp = GetCSharp(serviceProvider, processedSymbols, typeStore).Select(x => x.ToFullString()) - .Select((x, i) => (x, i.ToString())); + .Select((x, i) => (x, i.ToString() + ".cs")); var csharpDirRes = context.ParseResult.GetValueForOption(csharpDir); if (csharpDirRes is not null) { @@ -178,7 +178,7 @@ static async Task Main(string[] args) } var csharp = GetCSharp(serviceProvider, symbols, typeStore).Select(x => x.ToFullString()) - .Select((x, i) => (x, i.ToString())); + .Select((x, i) => (x, i.ToString() + ".cs")); if (csharpDir is not null) { foreach (var (contents, fileName) in csharp)