diff --git a/src/Microsoft.Windows.CsWin32/AnalyzerReleases.Unshipped.md b/src/Microsoft.Windows.CsWin32/AnalyzerReleases.Unshipped.md
index 25203f87..bcacfa87 100644
--- a/src/Microsoft.Windows.CsWin32/AnalyzerReleases.Unshipped.md
+++ b/src/Microsoft.Windows.CsWin32/AnalyzerReleases.Unshipped.md
@@ -9,4 +9,5 @@ PInvoke001 | Functionality | Warning | SourceGenerator
PInvoke002 | Functionality | Warning | SourceGenerator
PInvoke003 | Functionality | Warning | SourceGenerator
PInvoke004 | Functionality | Warning | SourceGenerator
-PInvoke005 | Functionality | Warning | SourceGenerator
\ No newline at end of file
+PInvoke005 | Functionality | Warning | SourceGenerator
+PInvoke006 | Configuration | Warning | SourceGenerator
\ No newline at end of file
diff --git a/src/Microsoft.Windows.CsWin32/Docs.cs b/src/Microsoft.Windows.CsWin32/Docs.cs
index 3d2fdcb6..e07f51eb 100644
--- a/src/Microsoft.Windows.CsWin32/Docs.cs
+++ b/src/Microsoft.Windows.CsWin32/Docs.cs
@@ -7,11 +7,14 @@ namespace Microsoft.Windows.CsWin32
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
- using System.Reflection;
+ using System.Linq;
using MessagePack;
using Microsoft.Windows.SDK.Win32Docs;
- internal class Docs
+ ///
+ /// An in-memory representation of API documentation.
+ ///
+ public class Docs
{
private static readonly Dictionary DocsByPath = new Dictionary(StringComparer.OrdinalIgnoreCase);
@@ -22,7 +25,12 @@ private Docs(Dictionary apisAndDocs)
this.apisAndDocs = apisAndDocs;
}
- internal static Docs Get(string docsPath)
+ ///
+ /// Loads docs from a file.
+ ///
+ /// The messagepack docs file to read from.
+ /// An instance of that accesses the documentation in the file specified by .
+ public static Docs Get(string docsPath)
{
lock (DocsByPath)
{
@@ -48,6 +56,35 @@ internal static Docs Get(string docsPath)
}
}
+ ///
+ /// Returns a instance that contains all the merged documentation from a list of docs.
+ ///
+ /// The docs to be merged. When API documentation is provided by multiple docs in this list, the first one appearing in this list is taken.
+ /// An instance that contains all the docs provided. When contains exactly one element, that element is returned.
+ public static Docs Merge(IReadOnlyList docs)
+ {
+ if (docs.Count == 1)
+ {
+ // Nothing to merge.
+ return docs[0];
+ }
+
+ Dictionary mergedDocs = new(docs.Sum(d => d.apisAndDocs.Count), StringComparer.OrdinalIgnoreCase);
+ foreach (Docs doc in docs)
+ {
+ foreach (KeyValuePair api in doc.apisAndDocs)
+ {
+ // We want a first one wins policy.
+ if (!mergedDocs.ContainsKey(api.Key))
+ {
+ mergedDocs.Add(api.Key, api.Value);
+ }
+ }
+ }
+
+ return new Docs(mergedDocs);
+ }
+
internal bool TryGetApiDocs(string apiName, [NotNullWhen(true)] out ApiDetails? docs) => this.apisAndDocs.TryGetValue(apiName, out docs);
}
}
diff --git a/src/Microsoft.Windows.CsWin32/Generator.cs b/src/Microsoft.Windows.CsWin32/Generator.cs
index cb13793c..3e3c15d9 100644
--- a/src/Microsoft.Windows.CsWin32/Generator.cs
+++ b/src/Microsoft.Windows.CsWin32/Generator.cs
@@ -286,16 +286,16 @@ public class Generator : IDisposable
/// Initializes a new instance of the class.
///
/// The path to the winmd metadata to generate APIs from.
- /// The path to the API docs file.
+ /// The API docs to include in the generated code.
/// Options that influence the result of generation.
/// The compilation that the generated code will be added to.
/// The parse options that will be used for the generated code.
- public Generator(string metadataLibraryPath, string? apiDocsPath, GeneratorOptions? options = null, CSharpCompilation? compilation = null, CSharpParseOptions? parseOptions = null)
+ public Generator(string metadataLibraryPath, Docs? docs, GeneratorOptions options, CSharpCompilation? compilation = null, CSharpParseOptions? parseOptions = null)
{
this.MetadataIndex = MetadataIndex.Get(metadataLibraryPath, compilation?.Options.Platform);
- this.ApiDocs = apiDocsPath is object ? Docs.Get(apiDocsPath) : null;
+ this.ApiDocs = docs;
- this.options = options ??= new GeneratorOptions();
+ this.options = options;
this.options.Validate();
this.compilation = compilation;
this.parseOptions = parseOptions;
@@ -312,11 +312,6 @@ public Generator(string metadataLibraryPath, string? apiDocsPath, GeneratorOptio
this.generateSupportedOSPlatformAttributesOnInterfaces = (targets & AttributeTargets.Interface) == AttributeTargets.Interface;
}
- if (options.AllowMarshaling)
- {
- this.BannedAPIs.Add("VARIANT", "Use `object` instead of VARIANT when in COM interface mode. VARIANT can only be emitted when emitting COM interfaces as structs.");
- }
-
bool useComInterfaces = options.AllowMarshaling;
this.generalTypeSettings = new TypeSyntaxSettings(
this,
@@ -343,13 +338,16 @@ private enum FriendlyOverloadOf
InterfaceMethod,
}
- internal Dictionary BannedAPIs { get; } = new Dictionary
- {
- { "GetLastError", "Do not generate GetLastError. Call Marshal.GetLastWin32Error() instead. Learn more from https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.getlastwin32error" },
- { "OLD_LARGE_INTEGER", "Use the C# long keyword instead." },
- { "LARGE_INTEGER", "Use the C# long keyword instead." },
- { "ULARGE_INTEGER", "Use the C# ulong keyword instead." },
- };
+ internal static ImmutableDictionary BannedAPIsWithoutMarshaling { get; } = ImmutableDictionary.Empty
+ .Add("GetLastError", "Do not generate GetLastError. Call Marshal.GetLastWin32Error() instead. Learn more from https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.getlastwin32error")
+ .Add("OLD_LARGE_INTEGER", "Use the C# long keyword instead.")
+ .Add("LARGE_INTEGER", "Use the C# long keyword instead.")
+ .Add("ULARGE_INTEGER", "Use the C# ulong keyword instead.");
+
+ internal static ImmutableDictionary BannedAPIsWithMarshaling { get; } = BannedAPIsWithoutMarshaling
+ .Add("VARIANT", "Use `object` instead of VARIANT when in COM interface mode. VARIANT can only be emitted when emitting COM interfaces as structs.");
+
+ internal ImmutableDictionary BannedAPIs => GetBannedAPIs(this.options);
internal MetadataIndex MetadataIndex { get; }
@@ -970,6 +968,8 @@ nsContents.Key is object
return normalizedResults;
}
+ internal static ImmutableDictionary GetBannedAPIs(GeneratorOptions options) => options.AllowMarshaling ? BannedAPIsWithMarshaling : BannedAPIsWithoutMarshaling;
+
[return: NotNullIfNotNull("marshalAs")]
internal static AttributeSyntax? MarshalAs(MarshalAsAttribute? marshalAs)
{
diff --git a/src/Microsoft.Windows.CsWin32/SourceGenerator.cs b/src/Microsoft.Windows.CsWin32/SourceGenerator.cs
index 1d2d0407..9f03ed94 100644
--- a/src/Microsoft.Windows.CsWin32/SourceGenerator.cs
+++ b/src/Microsoft.Windows.CsWin32/SourceGenerator.cs
@@ -90,6 +90,14 @@ public class SourceGenerator : ISourceGenerator
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
+ private static readonly DiagnosticDescriptor DocParsingError = new DiagnosticDescriptor(
+ "PInvoke006",
+ "DocsParseError",
+ "An error occurred while reading docs file: \"{0}\": {1}",
+ "Configuration",
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
///
public void Initialize(GeneratorInitializationContext context)
{
@@ -98,20 +106,12 @@ public void Initialize(GeneratorInitializationContext context)
///
public void Execute(GeneratorExecutionContext context)
{
- if (!(context.Compilation is CSharpCompilation))
+ if (context.Compilation is not CSharpCompilation compilation)
{
return;
}
- if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.MicrosoftWindowsSdkWin32MetadataBasePath", out string? metadataBasePath) ||
- string.IsNullOrWhiteSpace(metadataBasePath))
- {
- return;
- }
-
- context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.MicrosoftWindowsSdkApiDocsPath", out string? apiDocsPath);
-
- GeneratorOptions? options = null;
+ GeneratorOptions options;
AdditionalText? nativeMethodsJsonFile = context.AdditionalFiles
.FirstOrDefault(af => string.Equals(Path.GetFileName(af.Path), NativeMethodsJsonAdditionalFileName, StringComparison.OrdinalIgnoreCase));
if (nativeMethodsJsonFile is object)
@@ -124,6 +124,10 @@ public void Execute(GeneratorExecutionContext context)
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
}
+ else
+ {
+ options = new GeneratorOptions();
+ }
AdditionalText? nativeMethodsTxtFile = context.AdditionalFiles
.FirstOrDefault(af => string.Equals(Path.GetFileName(af.Path), NativeMethodsTxtAdditionalFileName, StringComparison.OrdinalIgnoreCase));
@@ -132,8 +136,6 @@ public void Execute(GeneratorExecutionContext context)
return;
}
- string metadataPath = Path.Combine(metadataBasePath, "Windows.Win32.winmd");
- var compilation = (CSharpCompilation)context.Compilation;
var parseOptions = (CSharpParseOptions)context.ParseOptions;
if (!compilation.Options.AllowUnsafe)
@@ -141,100 +143,142 @@ public void Execute(GeneratorExecutionContext context)
context.ReportDiagnostic(Diagnostic.Create(UnsafeCodeRequired, location: null));
}
- using var generator = new Generator(metadataPath, apiDocsPath, options, compilation, parseOptions);
-
- SourceText? nativeMethodsTxt = nativeMethodsTxtFile.GetText(context.CancellationToken);
- if (nativeMethodsTxt is null)
+ Docs? docs = ParseDocs(context);
+ IReadOnlyList generators = CollectMetadataPaths(context).Select(path => new Generator(path, docs, options, compilation, parseOptions)).ToList();
+ try
{
- return;
- }
-
- foreach (TextLine line in nativeMethodsTxt.Lines)
- {
- context.CancellationToken.ThrowIfCancellationRequested();
- string name = line.ToString();
- if (string.IsNullOrWhiteSpace(name) || name.StartsWith("//", StringComparison.InvariantCulture))
+ SourceText? nativeMethodsTxt = nativeMethodsTxtFile.GetText(context.CancellationToken);
+ if (nativeMethodsTxt is null)
{
- continue;
+ return;
}
- name = name.Trim();
- var location = Location.Create(nativeMethodsTxtFile.Path, line.Span, nativeMethodsTxt.Lines.GetLinePositionSpan(line.Span));
- try
+ foreach (TextLine line in nativeMethodsTxt.Lines)
{
- if (generator.BannedAPIs.TryGetValue(name, out string? reason))
+ context.CancellationToken.ThrowIfCancellationRequested();
+ string name = line.ToString();
+ if (string.IsNullOrWhiteSpace(name) || name.StartsWith("//", StringComparison.InvariantCulture))
{
- context.ReportDiagnostic(Diagnostic.Create(BannedApi, location, reason));
+ continue;
}
- else if (name.EndsWith(".*", StringComparison.Ordinal))
+
+ name = name.Trim();
+ var location = Location.Create(nativeMethodsTxtFile.Path, line.Span, nativeMethodsTxt.Lines.GetLinePositionSpan(line.Span));
+ try
{
- var moduleName = name.Substring(0, name.Length - 2);
- if (!generator.TryGenerateAllExternMethods(moduleName, context.CancellationToken))
+ if (Generator.GetBannedAPIs(options).TryGetValue(name, out string? reason))
{
- context.ReportDiagnostic(Diagnostic.Create(NoMethodsForModule, location, moduleName));
+ context.ReportDiagnostic(Diagnostic.Create(BannedApi, location, reason));
+ continue;
}
- }
- else if (!generator.TryGenerate(name, context.CancellationToken))
- {
- if (generator.TryGetEnumName(name, out string? declaringEnum))
+
+ if (name.EndsWith(".*", StringComparison.Ordinal))
{
- context.ReportDiagnostic(Diagnostic.Create(UseEnumValueDeclaringType, location, declaringEnum));
- if (!generator.TryGenerate(declaringEnum, context.CancellationToken))
+ var moduleName = name.Substring(0, name.Length - 2);
+ bool matchModule = false;
+ foreach (Generator generator in generators)
{
- ReportNoMatch(location, declaringEnum);
+ matchModule |= generator.TryGenerateAllExternMethods(moduleName, context.CancellationToken);
}
+
+ if (!matchModule)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NoMethodsForModule, location, moduleName));
+ }
+
+ continue;
}
- else
+
+ bool matchApi = false;
+ foreach (Generator generator in generators)
+ {
+ if (generator.TryGenerate(name, context.CancellationToken))
+ {
+ matchApi = true;
+ continue;
+ }
+
+ if (generator.TryGetEnumName(name, out string? declaringEnum))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(UseEnumValueDeclaringType, location, declaringEnum));
+ if (generator.TryGenerate(declaringEnum, context.CancellationToken))
+ {
+ matchApi = true;
+ continue;
+ }
+ else
+ {
+ ReportNoMatch(location, declaringEnum);
+ }
+ }
+ }
+
+ if (!matchApi)
{
ReportNoMatch(location, name);
}
}
- }
- catch (GenerationFailedException ex)
- {
- if (Generator.IsPlatformCompatibleException(ex))
+ catch (GenerationFailedException ex)
{
- context.ReportDiagnostic(Diagnostic.Create(CpuArchitectureIncompatibility, location));
+ if (Generator.IsPlatformCompatibleException(ex))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(CpuArchitectureIncompatibility, location));
+ }
+ else
+ {
+ // Build up a complete error message.
+ context.ReportDiagnostic(Diagnostic.Create(InternalError, location, AssembleFullExceptionMessage(ex)));
+ }
}
- else
+ }
+
+ foreach (Generator generator in generators)
+ {
+ var compilationUnits = generator.GetCompilationUnits(context.CancellationToken)
+ .OrderBy(pair => pair.Key, StringComparer.OrdinalIgnoreCase)
+ .ThenBy(pair => pair.Key, StringComparer.Ordinal);
+ foreach (var unit in compilationUnits)
{
- // Build up a complete error message.
- context.ReportDiagnostic(Diagnostic.Create(InternalError, location, AssembleFullExceptionMessage(ex)));
+ context.AddSource(unit.Key, unit.Value.ToFullString());
}
}
- }
- var compilationUnits = generator.GetCompilationUnits(context.CancellationToken)
- .OrderBy(pair => pair.Key, StringComparer.OrdinalIgnoreCase)
- .ThenBy(pair => pair.Key, StringComparer.Ordinal);
- foreach (var unit in compilationUnits)
- {
- context.AddSource(unit.Key, unit.Value.ToFullString());
- }
-
- void ReportNoMatch(Location? location, string failedAttempt)
- {
- var suggestions = generator.GetSuggestions(failedAttempt).Take(4).ToList();
- if (suggestions.Count > 0)
+ void ReportNoMatch(Location? location, string failedAttempt)
{
- var suggestionBuilder = new StringBuilder();
- for (int i = 0; i < suggestions.Count; i++)
+ List suggestions = new();
+ foreach (Generator generator in generators)
+ {
+ suggestions.AddRange(generator.GetSuggestions(failedAttempt).Take(4));
+ }
+
+ if (suggestions.Count > 0)
{
- if (i > 0)
+ var suggestionBuilder = new StringBuilder();
+ for (int i = 0; i < suggestions.Count; i++)
{
- suggestionBuilder.Append(i < suggestions.Count - 1 ? ", " : " or ");
+ if (i > 0)
+ {
+ suggestionBuilder.Append(i < suggestions.Count - 1 ? ", " : " or ");
+ }
+
+ suggestionBuilder.Append('"');
+ suggestionBuilder.Append(suggestions[i]);
+ suggestionBuilder.Append('"');
}
- suggestionBuilder.Append('"');
- suggestionBuilder.Append(suggestions[i]);
- suggestionBuilder.Append('"');
+ context.ReportDiagnostic(Diagnostic.Create(NoMatchingMethodOrTypeWithSuggestions, location, failedAttempt, suggestionBuilder));
+ }
+ else
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NoMatchingMethodOrType, location, failedAttempt));
}
-
- context.ReportDiagnostic(Diagnostic.Create(NoMatchingMethodOrTypeWithSuggestions, location, failedAttempt, suggestionBuilder));
}
- else
+ }
+ finally
+ {
+ foreach (Generator generator in generators)
{
- context.ReportDiagnostic(Diagnostic.Create(NoMatchingMethodOrType, location, failedAttempt));
+ generator.Dispose();
}
}
}
@@ -258,5 +302,46 @@ private static string AssembleFullExceptionMessage(Exception ex)
return sb.ToString();
}
+
+ private static IReadOnlyList CollectMetadataPaths(GeneratorExecutionContext context)
+ {
+ if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWin32InputMetadataPaths", out string? delimitedMetadataBasePaths) ||
+ string.IsNullOrWhiteSpace(delimitedMetadataBasePaths))
+ {
+ return Array.Empty();
+ }
+
+ string[] metadataBasePaths = delimitedMetadataBasePaths.Split(';');
+ return metadataBasePaths;
+ }
+
+ private static Docs? ParseDocs(GeneratorExecutionContext context)
+ {
+ Docs? docs = null;
+ if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWin32InputDocPaths", out string? delimitedApiDocsPaths) &&
+ !string.IsNullOrWhiteSpace(delimitedApiDocsPaths))
+ {
+ string[] apiDocsPaths = delimitedApiDocsPaths!.Split(';');
+ if (apiDocsPaths.Length > 0)
+ {
+ List docsList = new(apiDocsPaths.Length);
+ foreach (string path in apiDocsPaths)
+ {
+ try
+ {
+ docsList.Add(Docs.Get(path));
+ }
+ catch (Exception e)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(DocParsingError, null, path, e.Message));
+ }
+ }
+
+ docs = Docs.Merge(docsList);
+ }
+ }
+
+ return docs;
+ }
}
}
diff --git a/src/Microsoft.Windows.CsWin32/build/Microsoft.Windows.CsWin32.props b/src/Microsoft.Windows.CsWin32/build/Microsoft.Windows.CsWin32.props
index 1a86b066..132fc745 100644
--- a/src/Microsoft.Windows.CsWin32/build/Microsoft.Windows.CsWin32.props
+++ b/src/Microsoft.Windows.CsWin32/build/Microsoft.Windows.CsWin32.props
@@ -7,4 +7,18 @@
+
+
+
+
+
+
+
+
+
+
+ @(ProjectionMetadataWinmd)
+ @(ProjectionDocs)
+
+
diff --git a/src/Win32MetaGeneration/Program.cs b/src/Win32MetaGeneration/Program.cs
index b4918d28..90ea2b88 100644
--- a/src/Win32MetaGeneration/Program.cs
+++ b/src/Win32MetaGeneration/Program.cs
@@ -47,7 +47,7 @@ private static void Main(string[] args)
string apiDocsPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!, "apidocs.msgpack");
using var generator = new Generator(
metadataPath,
- apiDocsPath,
+ Docs.Get(apiDocsPath),
new GeneratorOptions
{
WideCharOnly = true,
diff --git a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs
index 50983408..1d5d9601 100644
--- a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs
+++ b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs
@@ -8,6 +8,7 @@
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
@@ -837,14 +838,6 @@ public void ProjectReferenceBetweenTwoGeneratingProjects(bool internalsVisibleTo
[Fact]
public async Task TestSimpleStructure()
{
- var basePath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkWin32MetadataBasePath")?.Value;
- var docsPath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkApiDocsPath")?.Value;
-
- var globalconfig = $@"is_global = true
-
-build_property.MicrosoftWindowsSdkApiDocsPath = {docsPath}
-build_property.MicrosoftWindowsSdkWin32MetadataBasePath = {basePath}
-";
await new VerifyTest
{
TestState =
@@ -856,7 +849,7 @@ public async Task TestSimpleStructure()
},
AnalyzerConfigFiles =
{
- ("/.globalconfig", globalconfig),
+ ("/.globalconfig", ConstructGlobalConfigString()),
},
GeneratedSources =
{
@@ -906,14 +899,6 @@ public static implicit operator bool(BOOL value)
[Fact]
public async Task TestSimpleEnum()
{
- var basePath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkWin32MetadataBasePath")?.Value;
- var docsPath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkApiDocsPath")?.Value;
-
- var globalconfig = $@"is_global = true
-
-build_property.MicrosoftWindowsSdkApiDocsPath = {docsPath}
-build_property.MicrosoftWindowsSdkWin32MetadataBasePath = {basePath}
-";
await new VerifyTest
{
TestState =
@@ -925,7 +910,7 @@ public async Task TestSimpleEnum()
},
AnalyzerConfigFiles =
{
- ("/.globalconfig", globalconfig),
+ ("/.globalconfig", ConstructGlobalConfigString()),
},
GeneratedSources =
{
@@ -979,12 +964,6 @@ internal enum DISPLAYCONFIG_SCANLINE_ORDERING
[Fact]
public async Task TestSimpleEnumWithoutDocs()
{
- var basePath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkWin32MetadataBasePath")?.Value;
-
- var globalconfig = $@"is_global = true
-
-build_property.MicrosoftWindowsSdkWin32MetadataBasePath = {basePath}
-";
await new VerifyTest
{
TestState =
@@ -996,7 +975,7 @@ public async Task TestSimpleEnumWithoutDocs()
},
AnalyzerConfigFiles =
{
- ("/.globalconfig", globalconfig),
+ ("/.globalconfig", ConstructGlobalConfigString(omitDocs: true)),
},
GeneratedSources =
{
@@ -1040,14 +1019,6 @@ internal enum DISPLAYCONFIG_SCANLINE_ORDERING
[Fact]
public async Task TestFlagsEnum()
{
- var basePath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkWin32MetadataBasePath")?.Value;
- var docsPath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkApiDocsPath")?.Value;
-
- var globalconfig = $@"is_global = true
-
-build_property.MicrosoftWindowsSdkApiDocsPath = {docsPath}
-build_property.MicrosoftWindowsSdkWin32MetadataBasePath = {basePath}
-";
await new VerifyTest
{
TestState =
@@ -1059,7 +1030,7 @@ public async Task TestFlagsEnum()
},
AnalyzerConfigFiles =
{
- ("/.globalconfig", globalconfig),
+ ("/.globalconfig", ConstructGlobalConfigString()),
},
GeneratedSources =
{
@@ -1124,14 +1095,6 @@ internal enum FILE_ACCESS_FLAGS : uint
[Fact]
public async Task TestSimpleDelegate()
{
- var basePath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkWin32MetadataBasePath")?.Value;
- var docsPath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkApiDocsPath")?.Value;
-
- var globalconfig = $@"is_global = true
-
-build_property.MicrosoftWindowsSdkApiDocsPath = {docsPath}
-build_property.MicrosoftWindowsSdkWin32MetadataBasePath = {basePath}
-";
await new VerifyTest
{
TestState =
@@ -1143,7 +1106,7 @@ public async Task TestSimpleDelegate()
},
AnalyzerConfigFiles =
{
- ("/.globalconfig", globalconfig),
+ ("/.globalconfig", ConstructGlobalConfigString()),
},
GeneratedSources =
{
@@ -1298,14 +1261,6 @@ internal readonly partial struct LPARAM
[Fact]
public async Task TestSimpleMethod()
{
- var basePath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkWin32MetadataBasePath")?.Value;
- var docsPath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkApiDocsPath")?.Value;
-
- var globalconfig = $@"is_global = true
-
-build_property.MicrosoftWindowsSdkApiDocsPath = {docsPath}
-build_property.MicrosoftWindowsSdkWin32MetadataBasePath = {basePath}
-";
await new VerifyTest
{
TestState =
@@ -1317,7 +1272,7 @@ public async Task TestSimpleMethod()
},
AnalyzerConfigFiles =
{
- ("/.globalconfig", globalconfig),
+ ("/.globalconfig", ConstructGlobalConfigString()),
},
GeneratedSources =
{
@@ -1450,14 +1405,6 @@ internal static partial class PInvoke
[Fact]
public async Task TestMethodWithOverloads()
{
- var basePath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkWin32MetadataBasePath")?.Value;
- var docsPath = this.GetType().Assembly.GetCustomAttributes().SingleOrDefault(metadata => metadata.Key == "MicrosoftWindowsSdkApiDocsPath")?.Value;
-
- var globalconfig = $@"is_global = true
-
-build_property.MicrosoftWindowsSdkApiDocsPath = {docsPath}
-build_property.MicrosoftWindowsSdkWin32MetadataBasePath = {basePath}
-";
await new VerifyTest
{
TestState =
@@ -1469,7 +1416,7 @@ public async Task TestMethodWithOverloads()
},
AnalyzerConfigFiles =
{
- ("/.globalconfig", globalconfig),
+ ("/.globalconfig", ConstructGlobalConfigString()),
},
GeneratedSources =
{
@@ -2022,6 +1969,25 @@ internal partial struct SECURITY_ATTRIBUTES
}.RunAsync();
}
+ private static string ConstructGlobalConfigString(bool omitDocs = false)
+ {
+ StringBuilder globalConfigBuilder = new();
+ globalConfigBuilder.AppendLine("is_global = true");
+ globalConfigBuilder.AppendLine();
+ globalConfigBuilder.AppendLine($"build_property.CsWin32InputMetadataPaths = {JoinAssemblyMetadata("ProjectionMetadataWinmd")}");
+ if (!omitDocs)
+ {
+ globalConfigBuilder.AppendLine($"build_property.CsWin32InputDocPaths = {JoinAssemblyMetadata("ProjectionDocs")}");
+ }
+
+ return globalConfigBuilder.ToString();
+
+ static string JoinAssemblyMetadata(string name)
+ {
+ return string.Join(";", typeof(GeneratorTests).Assembly.GetCustomAttributes().Where(metadata => metadata.Key == name).Select(metadata => metadata.Value));
+ }
+ }
+
private static ImmutableArray FilterDiagnostics(ImmutableArray diagnostics) => diagnostics.Where(d => d.Severity > DiagnosticSeverity.Hidden).ToImmutableArray();
private static bool IsAttributePresent(AttributeListSyntax al, string attributeName) => al.Attributes.Any(a => a.Name.ToString() == attributeName);
@@ -2174,7 +2140,7 @@ private async Task CreateCompilationAsync(ReferenceAssemblies
return compilation;
}
- private Generator CreateGenerator(GeneratorOptions? options = null, CSharpCompilation? compilation = null) => new Generator(MetadataPath, ApiDocsPath, options ?? DefaultTestGeneratorOptions, compilation ?? this.compilation, this.parseOptions);
+ private Generator CreateGenerator(GeneratorOptions? options = null, CSharpCompilation? compilation = null) => new Generator(MetadataPath, Docs.Get(ApiDocsPath), options ?? DefaultTestGeneratorOptions, compilation ?? this.compilation, this.parseOptions);
private static class MyReferenceAssemblies
{
diff --git a/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj b/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj
index 079ea0f5..fb3008d7 100644
--- a/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj
+++ b/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj
@@ -4,17 +4,24 @@
net5.0;netcoreapp3.1;net472
-
+
-
-
+ <_AssemblyMetadata1 Include="@(ProjectionMetadataWinmd)">
+ %(Identity)
+
+
+
+ <_AssemblyMetadata2 Include="@(ProjectionDocs)">
+ %(Identity)
+
+
-
+
PreserveNewest
-
+
PreserveNewest