diff --git a/Directory.Packages.props b/Directory.Packages.props index 457763170e..f84f3a8e92 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,6 +4,7 @@ For more information what this is see https://github.com/NuGet/Home/wiki/Centrally-managing-NuGet-package-versions --> + 6.0.0 4.2.0 @@ -12,7 +13,7 @@ - + @@ -25,10 +26,10 @@ - - - - + + + + @@ -46,5 +47,8 @@ + + + diff --git a/src/generators/Silk.NET.SilkTouch.Scraper/ClangScraper.cs b/src/generators/Silk.NET.SilkTouch.Scraper/ClangScraper.cs index 2cc06cdd70..0c6c918285 100644 --- a/src/generators/Silk.NET.SilkTouch.Scraper/ClangScraper.cs +++ b/src/generators/Silk.NET.SilkTouch.Scraper/ClangScraper.cs @@ -9,6 +9,7 @@ using System.Xml; using ClangSharp; using ClangSharp.Interop; +using Microsoft.Extensions.Logging; using Silk.NET.SilkTouch.Symbols; namespace Silk.NET.SilkTouch.Scraper; @@ -18,6 +19,7 @@ namespace Silk.NET.SilkTouch.Scraper; /// public sealed class ClangScraper { + private readonly ILoggerFactory _loggerFactory; /// /// Placeholder used in place of library paths /// @@ -28,6 +30,15 @@ public sealed class ClangScraper /// public static readonly string LibraryNamespacePlaceholder = "LIBRARY_NAMESPACE"; + /// + /// Creates a ClangScraper given it's dependencies + /// + /// A logger factory to create loggers from + public ClangScraper(ILoggerFactory loggerFactory) + { + _loggerFactory = loggerFactory; + } + /// /// Scrapes the given XML document for symbols /// @@ -42,7 +53,7 @@ public IEnumerable ScrapeXML(XmlDocument document) return Enumerable.Empty(); } - var visitor = new XmlVisitor(); + var visitor = new XmlVisitor(_loggerFactory.CreateLogger()); return visitor.Visit(bindings); } @@ -58,6 +69,7 @@ public IEnumerable ScrapeXML(XmlDocument document) /// Will be thrown when errors during parsing are encountered public XmlDocument? GenerateXML(string headerFile, string[] includedNames, string[] excludedNames, string[] includeDirectories, string[] definedMacros) { + var logger = _loggerFactory.CreateLogger("ClangScraper.ScrapeXML"); var opts = PInvokeGeneratorConfigurationOptions.None; opts |= PInvokeGeneratorConfigurationOptions.NoDefaultRemappings; @@ -105,11 +117,11 @@ Stream OutputStreamFactory(string fileName) try { using (var pinvokeGenerator = new PInvokeGenerator(config, OutputStreamFactory)) - GenerateBindings(pinvokeGenerator, headerFile, commandLineArgs.ToArray(), translationFlags); + GenerateBindings(pinvokeGenerator, headerFile, commandLineArgs.ToArray(), translationFlags, logger); foreach (var (name, stream) in files) { - Console.WriteLine(name); + logger.LogTrace("Outputting File \"{name}\"", name); var doc = new XmlDocument(); stream.Position = 0; doc.Load(stream); @@ -132,7 +144,8 @@ private static void GenerateBindings PInvokeGenerator pinvokeGenerator, string headerFile, string[] commandLineArgs, - CXTranslationUnit_Flags translationFlags + CXTranslationUnit_Flags translationFlags, + ILogger logger ) { var result = CXTranslationUnit.TryParse @@ -168,10 +181,20 @@ out var handle foreach (var diagnostic in pinvokeGenerator.Diagnostics) { - if (diagnostic.Level > DiagnosticLevel.Warning) - { - Console.WriteLine(diagnostic.Message); - } + logger.Log + ( + diagnostic.Level switch + { + DiagnosticLevel.Info => LogLevel.Debug, + DiagnosticLevel.Warning => LogLevel.Information, + DiagnosticLevel.Error => LogLevel.Warning, + _ => LogLevel.Debug + }, + "Clang Diagnostic: {level} at: {location} \"{message}\"", + diagnostic.Level, + diagnostic.Level, + diagnostic.Message + ); } } finally diff --git a/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs b/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs index 20d9f407ee..8f7d395c52 100644 --- a/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs +++ b/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs @@ -4,15 +4,23 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Metadata; using System.Xml; +using Microsoft.Extensions.Logging; using Silk.NET.SilkTouch.Symbols; +using TypeReference=Silk.NET.SilkTouch.Symbols.TypeReference; namespace Silk.NET.SilkTouch.Scraper; internal sealed class XmlVisitor { + private readonly ILogger _logger; + public XmlVisitor(ILogger logger) { + _logger = logger; + } + public IEnumerable Visit(XmlNode node) { switch (node) @@ -40,22 +48,77 @@ private IEnumerable VisitField(XmlElement field) throw new InvalidOperationException("Field requires a name"); } - // TODO: Appropriately Visit Type. - var type = new ExternalTypeReference - ( - null, - new IdentifierSymbol - ( - field.ChildNodes.Cast().SingleOrDefault(x => x.Name == "type")?.InnerText ?? - throw new InvalidOperationException("Could not decode Field Type") - ) - ); + var typeNode = field.ChildNodes.Cast().SingleOrDefault(x => x.Name == "type") as XmlElement; + if (typeNode is null) + throw new InvalidOperationException("Field type cannot be null"); + + TypeReference? finalType = null; + foreach (var type in VisitType(typeNode)) + { + if (type is null) + { + throw new InvalidOperationException + ( + $"{nameof(XmlVisitor)}.{nameof(VisitType)} has returned null" + ); + } + + if (type is not TypeReference tr) + { + throw new InvalidOperationException + ( + $"{nameof(XmlVisitor)}.{nameof(VisitType)} has returned something other then a type reference" + ); + } + + if (finalType is not null) + { + throw new InvalidOperationException + ($"{nameof(XmlVisitor)}.{nameof(VisitType)} has returned more then one result"); + } + + finalType = tr; + } + + if (finalType is null) + { + throw new InvalidOperationException + ($"No type could be resolved from {typeNode.OuterXml}"); + } return new[] { - new FieldSymbol(type, new IdentifierSymbol(name)) + new FieldSymbol(finalType, new IdentifierSymbol(name)) }; } + + // TODO: Configurable Type maps + private static readonly Dictionary _typeMap = new() + { + ["int"] = new ExternalTypeReference(null, new IdentifierSymbol("int")) + }; + + private bool TryResolveTypeRef(string text, [NotNullWhen(true)] out TypeReference? reference) + { + if (_typeMap.TryGetValue(text, out reference)) + { + return true; + } + else + { + _logger.LogDebug("Failed to resolve type reference from \"{text}\"", text); + return false; + } + } + + // NOTE: This does not visit types as in class/struct, but visits *references* to types. Like from methods or fields. + private IEnumerable VisitType(XmlElement type) + { + return + TryResolveTypeRef(type.InnerText, out var r) + ? new[] { r } + : Array.Empty(); + } private IEnumerable VisitStruct(XmlElement @struct) { diff --git a/tests/Silk.NET.SilkTouch.IntegrationTests/Silk.NET.SilkTouch.IntegrationTests.csproj b/tests/Silk.NET.SilkTouch.IntegrationTests/Silk.NET.SilkTouch.IntegrationTests.csproj index a6d8c8d43e..d9bd64a93c 100644 --- a/tests/Silk.NET.SilkTouch.IntegrationTests/Silk.NET.SilkTouch.IntegrationTests.csproj +++ b/tests/Silk.NET.SilkTouch.IntegrationTests/Silk.NET.SilkTouch.IntegrationTests.csproj @@ -20,6 +20,7 @@ + diff --git a/tests/Silk.NET.SilkTouch.IntegrationTests/StructIntegrationTests.cs b/tests/Silk.NET.SilkTouch.IntegrationTests/StructIntegrationTests.cs index ce9cfcb13d..5472d9f525 100644 --- a/tests/Silk.NET.SilkTouch.IntegrationTests/StructIntegrationTests.cs +++ b/tests/Silk.NET.SilkTouch.IntegrationTests/StructIntegrationTests.cs @@ -152,6 +152,23 @@ struct S int i; double d; }; +};"); + return Verifier.Verify(result); + } + + [Fact, + Trait("Category", "Integration"), + Trait("Source Language", "C++"), + Trait("Target Language", "C#"), + Trait("Feature", "Structs"), + Trait("Feature", "Fields")] + public Task Test8() + { + var result = TestHelper.GetCSharpOutputFromCpp(@" +struct a { + struct x { struct a *p; /* ... */ }; + + struct x *p; };"); return Verifier.Verify(result); } diff --git a/tests/Silk.NET.SilkTouch.IntegrationTests/TestHelper.cs b/tests/Silk.NET.SilkTouch.IntegrationTests/TestHelper.cs index 30907b7e2d..11faf8bb01 100644 --- a/tests/Silk.NET.SilkTouch.IntegrationTests/TestHelper.cs +++ b/tests/Silk.NET.SilkTouch.IntegrationTests/TestHelper.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Diagnostics; +using System.Data; using System.IO; using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Silk.NET.SilkTouch.Emitter; using Silk.NET.SilkTouch.Scraper; using Xunit; @@ -16,10 +18,13 @@ public static class TestHelper public static string GetCSharpOutputFromCpp(string cpp) { var tempFile = Path.GetTempFileName(); + var serviceProvider = new ServiceCollection() + .AddLogging(builder => builder.AddConsole()) + .BuildServiceProvider(); File.WriteAllText(tempFile, "/* THIS IS A GENERATED FILE, PIPED TO CLANG FOR TESTING BY SILK.NET */" + cpp); - var scraper = new ClangScraper(); + var scraper = new ClangScraper(serviceProvider.GetRequiredService()); var xml = scraper.GenerateXML (tempFile, Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); diff --git a/tests/Silk.NET.SilkTouch.Scraper.Tests/BasicXMLTests.cs b/tests/Silk.NET.SilkTouch.Scraper.Tests/BasicXMLTests.cs index ee46bb7ace..ebb7f8ec67 100644 --- a/tests/Silk.NET.SilkTouch.Scraper.Tests/BasicXMLTests.cs +++ b/tests/Silk.NET.SilkTouch.Scraper.Tests/BasicXMLTests.cs @@ -44,7 +44,7 @@ typedef struct { int32_t f2; } Test;"); - var scraper = new ClangScraper(); + var scraper = Helpers.CreateScraper(); var xml = scraper.GenerateXML (tempFile, Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); diff --git a/tests/Silk.NET.SilkTouch.Scraper.Tests/FieldScrapingTests.cs b/tests/Silk.NET.SilkTouch.Scraper.Tests/FieldScrapingTests.cs index 29d3129782..11e04deb59 100644 --- a/tests/Silk.NET.SilkTouch.Scraper.Tests/FieldScrapingTests.cs +++ b/tests/Silk.NET.SilkTouch.Scraper.Tests/FieldScrapingTests.cs @@ -17,7 +17,7 @@ public void FieldSymbol() var doc = new XmlDocument(); doc.LoadXml(@"int"); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var field = Assert.IsType(symbol); @@ -31,7 +31,7 @@ public void CorrectIdentifier() var doc = new XmlDocument(); doc.LoadXml(@"int"); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var field = Assert.IsType(symbol); @@ -46,7 +46,7 @@ public void CorrectType() var doc = new XmlDocument(); doc.LoadXml(@"int"); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var field = Assert.IsType(symbol); diff --git a/tests/Silk.NET.SilkTouch.Scraper.Tests/Helpers.cs b/tests/Silk.NET.SilkTouch.Scraper.Tests/Helpers.cs new file mode 100644 index 0000000000..4337aff747 --- /dev/null +++ b/tests/Silk.NET.SilkTouch.Scraper.Tests/Helpers.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Xml; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Silk.NET.SilkTouch.Symbols; + +namespace Silk.NET.SilkTouch.Scraper.Tests; + +public static class Helpers +{ + private static IServiceProvider CreateServiceProvider() + { + return new ServiceCollection() + .AddLogging(builder => + { + builder.AddConsole(); + builder.SetMinimumLevel(LogLevel.Trace); + } + ) + .BuildServiceProvider(); + } + + public static ClangScraper CreateScraper() + { + var serviceProvider = CreateServiceProvider(); + + return new ClangScraper(serviceProvider.GetRequiredService()); + } +} diff --git a/tests/Silk.NET.SilkTouch.Scraper.Tests/IdentityScrapingTests.cs b/tests/Silk.NET.SilkTouch.Scraper.Tests/IdentityScrapingTests.cs index 8e4307c8f7..f4f1a92789 100644 --- a/tests/Silk.NET.SilkTouch.Scraper.Tests/IdentityScrapingTests.cs +++ b/tests/Silk.NET.SilkTouch.Scraper.Tests/IdentityScrapingTests.cs @@ -15,7 +15,7 @@ public void GeneratesNoSymbols() { var doc = new XmlDocument(); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); Assert.Empty(symbols); } @@ -29,7 +29,7 @@ public void BindingsGeneratesNoSymbols() "); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); Assert.Empty(symbols); } diff --git a/tests/Silk.NET.SilkTouch.Scraper.Tests/NamespaceScrapingTests.cs b/tests/Silk.NET.SilkTouch.Scraper.Tests/NamespaceScrapingTests.cs index 8272af6382..c27deb787c 100644 --- a/tests/Silk.NET.SilkTouch.Scraper.Tests/NamespaceScrapingTests.cs +++ b/tests/Silk.NET.SilkTouch.Scraper.Tests/NamespaceScrapingTests.cs @@ -21,7 +21,7 @@ public void NamespaceSymbol() "); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var @namespace = Assert.IsType(symbol); @@ -42,7 +42,7 @@ public void NamespaceMember() "); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var @namespace = Assert.IsType(symbol); @@ -68,7 +68,7 @@ public void MultipleNamespaceMembers() "); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var @namespace = Assert.IsType(symbol); diff --git a/tests/Silk.NET.SilkTouch.Scraper.Tests/Silk.NET.SilkTouch.Scraper.Tests.csproj b/tests/Silk.NET.SilkTouch.Scraper.Tests/Silk.NET.SilkTouch.Scraper.Tests.csproj index 2dc0e93a11..3ea7a2fb59 100644 --- a/tests/Silk.NET.SilkTouch.Scraper.Tests/Silk.NET.SilkTouch.Scraper.Tests.csproj +++ b/tests/Silk.NET.SilkTouch.Scraper.Tests/Silk.NET.SilkTouch.Scraper.Tests.csproj @@ -19,6 +19,7 @@ + diff --git a/tests/Silk.NET.SilkTouch.Scraper.Tests/StructMemberScrapingTests.cs b/tests/Silk.NET.SilkTouch.Scraper.Tests/StructMemberScrapingTests.cs index 0179dd2840..44cb47c6a6 100644 --- a/tests/Silk.NET.SilkTouch.Scraper.Tests/StructMemberScrapingTests.cs +++ b/tests/Silk.NET.SilkTouch.Scraper.Tests/StructMemberScrapingTests.cs @@ -18,7 +18,7 @@ public void SingleMember() var doc = new XmlDocument(); doc.LoadXml(@"int"); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var @struct = Assert.IsType(symbol); @@ -43,7 +43,7 @@ public void MultipleMembers() @"intint" ); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var @struct = Assert.IsType(symbol); diff --git a/tests/Silk.NET.SilkTouch.Scraper.Tests/StructScrapingTests.cs b/tests/Silk.NET.SilkTouch.Scraper.Tests/StructScrapingTests.cs index 94dcaef881..b29d174d7a 100644 --- a/tests/Silk.NET.SilkTouch.Scraper.Tests/StructScrapingTests.cs +++ b/tests/Silk.NET.SilkTouch.Scraper.Tests/StructScrapingTests.cs @@ -17,7 +17,7 @@ public void StructSymbol() var doc = new XmlDocument(); doc.LoadXml(@""); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var @struct = Assert.IsType(symbol); @@ -31,7 +31,7 @@ public void CorrectIdentifier() var doc = new XmlDocument(); doc.LoadXml(@""); - var symbols = new ClangScraper().ScrapeXML(doc); + var symbols = Helpers.CreateScraper().ScrapeXML(doc); var symbol = Assert.Single(symbols); var @struct = Assert.IsType(symbol);