diff --git a/docs/winrt-interop-dll-spec.md b/docs/winrt-interop-dll-spec.md
index ce1194699..2972aab29 100644
--- a/docs/winrt-interop-dll-spec.md
+++ b/docs/winrt-interop-dll-spec.md
@@ -25,6 +25,7 @@ The **mangled namespace** for a given type is defined as follows:
1. **No namespace**: if the input type has no namespace, the generated namespace defaults to `ABI`.
2. **Existing namespace**: if the input type has a namespace, the generated namespace prepends `ABI.` to the original namespace.
+3. **Nested types**: if the input type is nested, the namespace is derived from the outermost declaring type (i.e. the top-level enclosing type), using the same rules above.
The **mangled type name** for a given type is defined as follows:
@@ -45,11 +46,11 @@ These are the well-known assemblies and their compact identifiers:
- `System.Runtime`: `#corlib`
- `Microsoft.Windows.SDK.NET` or `Microsoft.Windows.UI.Xaml`: `#Windows`
- `WinRT.Runtime`: `#CsWinRT`
-- `Microsoft.UI.Xaml.Projection`: `#WinUI2`
-- `Microsoft.Graphics.Canvas.Interop`: `#Win2D`
Compact identifiers are prefixed with `#` to distinguish them from user-defined assembly names.
+For types not belonging to any well-known assembly, the implementation also checks for a `[WindowsRuntimeMetadata]` attribute on the resolved type definition. If the attribute is present, the Windows Runtime metadata name from the attribute is used as the assembly identifier instead of the actual assembly name. This allows types carrying WinRT metadata to be identified by their canonical Windows Runtime name rather than the .NET assembly they happen to live in. If the attribute is not present, the raw assembly name is used as-is.
+
### Examples
**Primitive type**
@@ -134,6 +135,8 @@ primitiveType : 'bool'
| 'ulong'
| 'float'
| 'double'
+ | 'nint'
+ | 'nuint'
| 'string'
| 'object';
@@ -154,8 +157,6 @@ arrayType : '<' mangledTypeName '>' 'Array';
assemblyName : '#corlib'
| '#Windows'
| '#CsWinRT'
- | '#WinUI2'
- | '#Win2D'
| identifier;
// After substitutions, identifiers may contain '-' (from namespaces), '+' (nested type separators,
diff --git a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs
new file mode 100644
index 000000000..0eb151d99
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs
@@ -0,0 +1,391 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Immutable;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using WindowsRuntime.SourceGenerator.Models;
+
+namespace WindowsRuntime.SourceGenerator;
+
+///
+public partial class AuthoringExportTypesGenerator
+{
+ ///
+ /// Generation methods for .
+ ///
+ private static class Execute
+ {
+ ///
+ /// Gets the options for the generator.
+ ///
+ /// The input options provider.
+ /// The cancellation token for the operation.
+ /// The resulting options.
+ public static AuthoringExportTypesOptions GetOptions(AnalyzerConfigOptionsProvider provider, CancellationToken token)
+ {
+ token.ThrowIfCancellationRequested();
+
+ return new(
+ PublishAot: provider.GlobalOptions.GetPublishAot(),
+ IsComponent: provider.GlobalOptions.GetCsWinRTComponent(),
+ MergeReferencedActivationFactories: provider.GlobalOptions.GetCsWinRTMergeReferencedActivationFactories());
+ }
+
+ ///
+ /// Gets the info for generating native exports.
+ ///
+ /// The input data.
+ /// The cancellation token for the operation.
+ /// The resulting info.
+ public static AuthoringNativeExportsInfo GetNativeExportsInfo((Compilation compilation, AuthoringExportTypesOptions Options) data, CancellationToken token)
+ {
+ token.ThrowIfCancellationRequested();
+
+ return new(
+ AssemblyName: data.compilation.AssemblyName ?? "",
+ Options: data.Options);
+ }
+
+ ///
+ /// Gets all portable executable references from a compilation.
+ ///
+ /// The input data.
+ /// The cancellation token for the operation.
+ /// All portable executable references for the input compilation.
+ public static AuthoringManagedExportsInfo GetManagedExportsInfo((Compilation Compilation, AuthoringExportTypesOptions Options) data, CancellationToken token)
+ {
+ token.ThrowIfCancellationRequested();
+
+ // Skip going through references if merging is not enabled
+ if (!data.Options.MergeReferencedActivationFactories)
+ {
+ return new(
+ AssemblyName: data.Compilation.AssemblyName ?? "",
+ MergedManagedExportsTypeNames: [],
+ Options: data.Options);
+ }
+
+ ImmutableArray.Builder builder = ImmutableArray.CreateBuilder();
+
+ // Go through all references to find transitively-references Windows Runtime components
+ foreach (MetadataReference metadataReference in data.Compilation.References)
+ {
+ token.ThrowIfCancellationRequested();
+
+ if (data.Compilation.GetAssemblyOrModuleSymbol(metadataReference) is not IAssemblySymbol assemblySymbol)
+ {
+ continue;
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ // Add the type name if the assembly is a Windows Runtime component
+ if (Helpers.TryGetDependentAssemblyExportsTypeName(
+ assemblySymbol: assemblySymbol,
+ compilation: data.Compilation,
+ token: token,
+ name: out string? name))
+ {
+ builder.Add(name);
+ }
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ return new(
+ AssemblyName: data.Compilation.AssemblyName ?? "",
+ MergedManagedExportsTypeNames: builder.ToImmutable(),
+ Options: data.Options);
+ }
+
+ ///
+ /// Emits the managed exports for authored components.
+ ///
+ /// The instance to use.
+ /// The input info.
+ public static void EmitManagedExports(SourceProductionContext context, AuthoringManagedExportsInfo info)
+ {
+ if (!info.Options.ShouldEmitManagedExports())
+ {
+ return;
+ }
+
+ IndentedTextWriter writer = new(literalLength: 0, formattedCount: 0);
+
+ // Emit the '[WindowsRuntimeComponentAssemblyExportsType]' attribute so other tooling (including this same generator)
+ // can reliably find the generated export types from other assemblies, which is needed when merging activation factories.
+ writer.WriteLine($$"""
+ //
+ #pragma warning disable
+
+ [assembly: global::WindowsRuntime.InteropServices.WindowsRuntimeComponentAssemblyExportsType(typeof(global::ABI.{{info.AssemblyName.EscapeIdentifierName()}}.ManagedExports))]
+
+ namespace ABI.{{info.AssemblyName.EscapeIdentifierName()}};
+
+ using global::System;
+ using global::System.CodeDom.Compiler;
+ using global::System.ComponentModel;
+ using global::System.Diagnostics;
+ using global::System.Diagnostics.CodeAnalysis;
+ using global::System.Runtime.CompilerServices;
+
+ ///
+ /// Contains the managed exports for activating types from the current project in an authoring scenario.
+ ///
+ """);
+
+ // Emit the standard generated attributes, and also mark the type as hidden, since it's generated as a public type
+ writer.WriteGeneratedAttributes(nameof(AuthoringExportTypesGenerator), useFullyQualifiedTypeNames: false);
+ writer.WriteLine($$"""
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static unsafe class ManagedExports
+ """);
+
+ // Indent via a block, as we'll also need to emit custom logic for the activation factory
+ using (writer.WriteBlock())
+ {
+ // The 'GetActivationFactory' method is the one that actually contains all the high-level logic for
+ // activation scenarios. If we have any native exports (see below), those would call this method too.
+ writer.WriteLine($$"""
+ ///
+ /// Retrieves the activation factory from a DLL that contains activatable Windows Runtime classes.
+ ///
+ /// The class identifier that is associated with an activatable runtime class.
+ /// The resulting pointer to the activation factory that corresponds with the class specified by .
+ public static void* GetActivationFactory(ReadOnlySpan activatableClassId)
+ """);
+
+ using (writer.WriteBlock())
+ {
+ // Emit the unsafe accessor pointing to 'WinRT.Authoring.dll', where the actual activation code is located
+ if (info.Options.IsComponent)
+ {
+ writer.WriteLine($"""
+ [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, MethodName = "GetActivationFactory")]
+ static extern void* AuthoringGetActivationFactory(
+ [UnsafeAccessorType("ABI.{info.AssemblyName.EscapeIdentifierName()}.ManagedExports, WinRT.Authoring")] object? _,
+ ReadOnlySpan activatableClassId);
+ """);
+ }
+
+ // Emit the specialized code to redirect the activation
+ if (info.Options.IsComponent && !info.Options.MergeReferencedActivationFactories)
+ {
+ writer.WriteLine("return AuthoringGetActivationFactory(null, activatableClassId);");
+ }
+ else if (info.Options.IsComponent && info.Options.MergeReferencedActivationFactories)
+ {
+ writer.WriteLine("""
+ void* activationFactory = AuthoringGetActivationFactory(null, activatableClassId);
+
+ return activationFactory ?? ReferencedManagedExports.GetActivationFactory(activatableClassId);
+ """);
+ }
+ else if (info.Options.MergeReferencedActivationFactories)
+ {
+ writer.WriteLine("return ReferencedManagedExports.GetActivationFactory(activatableClassId);");
+ }
+ }
+
+ // Emit the reflection friendly overload as well
+ writer.WriteLine();
+ writer.WriteLine($$"""
+ ///
+ /// This overload uses reflection friendly types and is only meant to be used from managed runtime hosts.
+ public static nint GetActivationFactory(string activatableClassId)
+ {
+ return (nint)GetActivationFactory(activatableClassId.AsSpan());
+ }
+ """);
+ }
+
+ // Emit a helper type with the logic for merging activaton factories, if needed
+ if (info.Options.MergeReferencedActivationFactories)
+ {
+ writer.WriteLine();
+ writer.WriteLine("""
+ ///
+ /// Contains the logic for activating types from transitively referenced Windows Runtime components.
+ ///
+ """);
+ writer.WriteGeneratedAttributes(nameof(AuthoringExportTypesGenerator), useFullyQualifiedTypeNames: false);
+ writer.WriteLine("file static unsafe class ReferencedManagedExports");
+
+ using (writer.WriteBlock())
+ {
+ writer.WriteLine("""
+ ///
+ /// Retrieves the activation factory from all dependent Windows Runtime components.
+ ///
+ /// The class identifier that is associated with an activatable runtime class.
+ /// The resulting pointer to the activation factory that corresponds with the class specified by .
+ public static void* GetActivationFactory(ReadOnlySpan activatableClassId)
+ """);
+
+ using (writer.WriteBlock())
+ {
+ writer.WriteLine("void* activationFactory;");
+
+ // Iterate through all transitvely referenced activation factories from other components and use the first that succeeds
+ foreach (string managedExportsTypeName in info.MergedManagedExportsTypeNames)
+ {
+ writer.WriteLine();
+ writer.WriteLine($$"""
+ activationFactory = global::{{managedExportsTypeName}}.GetActivationFactory(activatableClassId);
+
+ if (activationFactory is not null)
+ {
+ return activationFactory;
+ }
+ """);
+ }
+
+ // No match across the referenced factories, we can't do anything else
+ writer.WriteLine();
+ writer.WriteLine("return null;");
+ }
+ }
+ }
+
+ context.AddSource("ManagedExports.g.cs", writer.ToStringAndClear());
+ }
+
+ ///
+ /// Emits the native exports for authored components.
+ ///
+ /// The instance to use.
+ /// The input info.
+ public static void EmitNativeExports(SourceProductionContext context, AuthoringNativeExportsInfo info)
+ {
+ if (!info.Options.ShouldEmitNativeExports())
+ {
+ return;
+ }
+
+ IndentedTextWriter writer = new(literalLength: 0, formattedCount: 0);
+
+ // Because we're generating code in our own namespace (which nobody else will use), we can use namespace-scoped
+ // using directives, which allows the generated code to be more concise. We can also do the same with type
+ // aliases, so we can emit the native methods with a signature that more closely matches the original.
+ writer.WriteLine($"""
+ //
+ #pragma warning disable
+
+ namespace ABI.{info.AssemblyName.EscapeIdentifierName()};
+
+ using global::System;
+ using global::System.CodeDom.Compiler;
+ using global::System.Diagnostics;
+ using global::System.Diagnostics.CodeAnalysis;
+ using global::System.Runtime.CompilerServices;
+ using global::System.Runtime.InteropServices;
+ using global::WindowsRuntime.InteropServices;
+
+ using HRESULT = int;
+ using unsafe HSTRING = void*;
+ using IActivationFactory = void;
+
+ ///
+ /// Contains the native exports for activating types from the current project in an authoring scenario.
+ ///
+ """);
+
+ // Emit the attributes to mark the code as generated, and to exclude it from code coverage as well. We also use a
+ // file-scoped P/Invoke, so we don't take a dependency on private implementation detail types from 'WinRT.Runtime.dll'.
+ writer.WriteGeneratedAttributes(nameof(AuthoringExportTypesGenerator), useFullyQualifiedTypeNames: false);
+ writer.WriteLine($$"""
+ internal static unsafe class NativeExports
+ {
+ ///
+ /// Retrieves the activation factory from a DLL that contains activatable Windows Runtime classes.
+ ///
+ /// The class identifier that is associated with an activatable runtime class.
+ /// A pointer to the activation factory that corresponds with the class specified by .
+ /// The HRESULT for the operation.
+ ///
+ [UnmanagedCallersOnly(EntryPoint = nameof(DllGetActivationFactory), CallConvs = [typeof(CallConvStdcall)])]
+ public static HRESULT DllGetActivationFactory(HSTRING activatableClassId, IActivationFactory** factory)
+ {
+ const int E_INVALIDARG = unchecked((int)0x80070057);
+ const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)(0x80040111));
+ const int S_OK = 0;
+
+ if (activatableClassId is null || factory is null)
+ {
+ return E_INVALIDARG;
+ }
+
+ ReadOnlySpan managedActivatableClassId = HStringMarshaller.ConvertToManagedUnsafe(activatableClassId);
+
+ try
+ {
+ IActivationFactory* result = ManagedExports.GetActivationFactory(managedActivatableClassId);
+
+ if ((void*)result is null)
+ {
+ *factory = null;
+
+ return CLASS_E_CLASSNOTAVAILABLE;
+ }
+
+ *factory = (void*)result;
+
+ return S_OK;
+ }
+ catch (Exception e)
+ {
+ return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e);
+ }
+ }
+
+ ///
+ /// Determines whether the DLL that implements this function is in use. If not, the caller can unload the DLL from memory.
+ ///
+ /// This method always returns S_FALSE.
+ ///
+ [UnmanagedCallersOnly(EntryPoint = nameof(DllCanUnloadNow), CallConvs = [typeof(CallConvStdcall)])]
+ public static HRESULT DllCanUnloadNow()
+ {
+ const HRESULT S_FALSE = 1;
+
+ return S_FALSE;
+ }
+ }
+
+ ///
+ /// A marshaller for the Windows Runtime HSTRING type.
+ ///
+ file static class HStringMarshaller
+ {
+ ///
+ /// Converts an input HSTRING value to a value.
+ ///
+ /// The input HSTRING value to marshal.
+ /// The resulting value.
+ public static ReadOnlySpan ConvertToManagedUnsafe(HSTRING value)
+ {
+ uint length;
+ char* buffer = WindowsRuntimeImports.WindowsGetStringRawBuffer(value, &length);
+
+ return new(buffer, (int)length);
+ }
+ }
+
+ ///
+ /// Native imports for fundamental COM and Windows Runtime APIs.
+ ///
+ file static class WindowsRuntimeImports
+ {
+ ///
+ [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", EntryPoint = "WindowsGetStringRawBuffer", ExactSpelling = true)]
+ [SupportedOSPlatform("windows6.2")]
+ public static extern char* WindowsGetStringRawBuffer(HSTRING @string, uint* length);
+ }
+ """);
+
+ context.AddSource("NativeExports.g.cs", writer.ToStringAndClear());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs
new file mode 100644
index 000000000..d00837804
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+
+namespace WindowsRuntime.SourceGenerator;
+
+///
+public partial class AuthoringExportTypesGenerator
+{
+ ///
+ /// Helper methods for .
+ ///
+ private static class Helpers
+ {
+ ///
+ /// Tries to get the name of a dependent Windows Runtime component from a given assembly.
+ ///
+ /// The assembly symbol to analyze.
+ /// The instance to use.
+ /// The instance to use.
+ /// The resulting type name, if found.
+ /// Whether a type name was found.
+ public static bool TryGetDependentAssemblyExportsTypeName(
+ IAssemblySymbol assemblySymbol,
+ Compilation compilation,
+ CancellationToken token,
+ [NotNullWhen(true)] out string? name)
+ {
+ // Get the attribute to lookup to find the target type to use
+ INamedTypeSymbol winRTAssemblyExportsTypeAttributeSymbol = compilation.GetTypeByMetadataName("WindowsRuntime.InteropServices.WindowsRuntimeComponentAssemblyExportsTypeAttribute")!;
+
+ // Make sure the assembly does have the attribute on it
+ if (!assemblySymbol.TryGetAttributeWithType(winRTAssemblyExportsTypeAttributeSymbol, out AttributeData? attributeData))
+ {
+ name = null;
+
+ return false;
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ // Sanity check: we should have a valid type in the annotation
+ if (attributeData.ConstructorArguments is not [{ Kind: TypedConstantKind.Type, Value: INamedTypeSymbol assemblyExportsTypeSymbol }])
+ {
+ name = null;
+
+ return false;
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ // Other sanity check: this type should be accessible from this compilation
+ if (!assemblyExportsTypeSymbol.IsAccessibleFromCompilationAssembly(compilation))
+ {
+ name = null;
+
+ return false;
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ name = assemblyExportsTypeSymbol.ToDisplayString();
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.cs b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.cs
new file mode 100644
index 000000000..c81da7aa3
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.CodeAnalysis;
+using WindowsRuntime.SourceGenerator.Models;
+
+namespace WindowsRuntime.SourceGenerator;
+
+///
+/// A generator to emit export types needing for authoring scenarios.
+///
+[Generator]
+public sealed partial class AuthoringExportTypesGenerator : IIncrementalGenerator
+{
+ ///
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // Get the options for the generator from the analyzer options
+ IncrementalValueProvider options = context.AnalyzerConfigOptionsProvider.Select(Execute.GetOptions);
+
+ // Get the generation info specific to managed exports
+ IncrementalValueProvider managedExportsInfo = context.CompilationProvider
+ .Combine(options)
+ .Select(Execute.GetManagedExportsInfo);
+
+ // Get the generation info specific to native exports
+ IncrementalValueProvider nativeExportsInfo = context.CompilationProvider
+ .Combine(options)
+ .Select(Execute.GetNativeExportsInfo);
+
+ // Generate the managed exports type
+ context.RegisterImplementationSourceOutput(managedExportsInfo, Execute.EmitManagedExports);
+
+ // Generate the native exports type
+ context.RegisterImplementationSourceOutput(nativeExportsInfo, Execute.EmitNativeExports);
+ }
+}
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/AnalyzerConfigOptionsExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/AnalyzerConfigOptionsExtensions.cs
index 721ea8c86..f7a6fa202 100644
--- a/src/Authoring/WinRT.SourceGenerator2/Extensions/AnalyzerConfigOptionsExtensions.cs
+++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/AnalyzerConfigOptionsExtensions.cs
@@ -22,6 +22,24 @@ public bool GetPublishAot()
return options.GetBooleanProperty("PublishAot");
}
+ ///
+ /// Gets the value of the "CsWinRTComponent" property.
+ ///
+ /// The value of the "CsWinRTComponent" property.
+ public bool GetCsWinRTComponent()
+ {
+ return options.GetBooleanProperty("CsWinRTComponent");
+ }
+
+ ///
+ /// Gets the value of the "CsWinRTMergeReferencedActivationFactories" property.
+ ///
+ /// The value of the "CsWinRTMergeReferencedActivationFactories" property.
+ public bool GetCsWinRTMergeReferencedActivationFactories()
+ {
+ return options.GetBooleanProperty("CsWinRTMergeReferencedActivationFactories");
+ }
+
///
/// Tries to get the value of a boolean MSBuild property.
///
diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs
index 924c66a9e..117fcd2cd 100644
--- a/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs
+++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs
@@ -13,6 +13,7 @@ namespace WindowsRuntime.SourceGenerator;
///
internal static class ISymbolExtensions
{
+ /// The input instance.
extension(ISymbol symbol)
{
///
@@ -47,5 +48,15 @@ public bool TryGetAttributeWithType(ITypeSymbol typeSymbol, [NotNullWhen(true)]
return false;
}
+
+ ///
+ /// Checks whether a given symbol is accessible from the assembly of a given compilation (including eg. through nested types).
+ ///
+ /// The instance currently in use.
+ /// Whether is accessible from the assembly for .
+ public bool IsAccessibleFromCompilationAssembly(Compilation compilation)
+ {
+ return compilation.IsSymbolAccessibleWithin(symbol, compilation.Assembly);
+ }
}
}
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/IndentedTextWriterExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/IndentedTextWriterExtensions.cs
new file mode 100644
index 000000000..21cfeef59
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/IndentedTextWriterExtensions.cs
@@ -0,0 +1,59 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// Ported from ComputeSharp.
+// See: https://github.com/Sergio0694/ComputeSharp/blob/main/src/ComputeSharp.SourceGeneration/Helpers/IndentedTextWriter.cs.
+
+using System;
+
+namespace WindowsRuntime.SourceGenerator;
+
+///
+/// Extension methods for the type.
+///
+internal static class IndentedTextWriterExtensions
+{
+ ///
+ /// Writes the following attributes into a target writer:
+ ///
+ /// [global::System.CodeDom.Compiler.GeneratedCode("...", "...")]
+ /// [global::System.Diagnostics.DebuggerNonUserCode]
+ /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ ///
+ ///
+ /// The instance to write into.
+ /// The name of the generator.
+ /// Whether to use fully qualified type names or not.
+ /// Whether to also include the attribute for non-user code.
+ public static void WriteGeneratedAttributes(
+ this ref IndentedTextWriter writer,
+ string generatorName,
+ bool useFullyQualifiedTypeNames = true,
+ bool includeNonUserCodeAttributes = true)
+ {
+ // We can use this class to get the assembly, as all files for generators are just included
+ // via shared projects. As such, the assembly will be the same as the generator type itself.
+ Version assemblyVersion = typeof(IndentedTextWriterExtensions).Assembly.GetName().Version!;
+
+ if (useFullyQualifiedTypeNames)
+ {
+ writer.WriteLine($$"""[global::System.CodeDom.Compiler.GeneratedCode("{{generatorName}}", "{{assemblyVersion}}")]""");
+
+ if (includeNonUserCodeAttributes)
+ {
+ writer.WriteLine($$"""[global::System.Diagnostics.DebuggerNonUserCode]""");
+ writer.WriteLine($$"""[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]""");
+ }
+ }
+ else
+ {
+ writer.WriteLine($$"""[GeneratedCode("{{generatorName}}", "{{assemblyVersion}}")]""");
+
+ if (includeNonUserCodeAttributes)
+ {
+ writer.WriteLine($$"""[DebuggerNonUserCode]""");
+ writer.WriteLine($$"""[ExcludeFromCodeCoverage]""");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs
new file mode 100644
index 000000000..f9dbf63ee
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Runtime.CompilerServices;
+using Microsoft.CodeAnalysis.CSharp;
+
+#pragma warning disable IDE0046
+
+namespace WindowsRuntime.SourceGenerator;
+
+///
+/// Extensions for .
+///
+internal static class StringExtensions
+{
+ /// The input value.
+ extension(string value)
+ {
+ ///
+ /// Escapes an identifier name.
+ ///
+ /// The escaped identifier name from the current value.
+ public string EscapeIdentifierName()
+ {
+ // If the current value is empty, we just use an underscore for the identifier name. This
+ // should generally never be the case, as this method is mostly used for assembly names.
+ if (value is null or "")
+ {
+ return "_";
+ }
+
+ string escapedValue;
+
+ // Optimization: we can just return the name as is if it's already a valid identifier. Generally speaking this
+ // should always be the case for assembly names (except if they have one or more parts separated by a '.').
+ if (SyntaxFacts.IsValidIdentifier(value))
+ {
+ escapedValue = value;
+ }
+ else
+ {
+ DefaultInterpolatedStringHandler handler = new(literalLength: value.Length, formattedCount: 0);
+
+ // Add a leading '_' if the first character is not a valid start character for an identifier
+ if (!SyntaxFacts.IsIdentifierStartCharacter(value[0]))
+ {
+ handler.AppendFormatted('_');
+ }
+
+ // Build the escaped name by just replacing all invalid characters with '_'
+ foreach (char c in value)
+ {
+ handler.AppendFormatted(SyntaxFacts.IsIdentifierPartCharacter(c) ? c : '_');
+ }
+
+ escapedValue = handler.ToStringAndClear();
+ }
+
+ // If the resulting identifier is still not valid (eg. a keyword like 'class'),
+ // adjust it so that the final result is actually a valid identifier we can use.
+ if (SyntaxFacts.GetKeywordKind(escapedValue) is not SyntaxKind.None)
+ {
+ return $"_{escapedValue}";
+ }
+
+ return escapedValue;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs
new file mode 100644
index 000000000..64ef7c104
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs
@@ -0,0 +1,533 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// Ported from ComputeSharp.
+// See: https://github.com/Sergio0694/ComputeSharp/blob/main/src/ComputeSharp.SourceGeneration/Helpers/IndentedTextWriter.cs.
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+#pragma warning disable IDE0060, IDE0290
+
+namespace WindowsRuntime.SourceGenerator;
+
+///
+/// A helper type to build sequences of values with pooled buffers.
+///
+internal ref struct IndentedTextWriter
+{
+ ///
+ /// The default indentation (4 spaces).
+ ///
+ private const string DefaultIndentation = " ";
+
+ ///
+ /// The default new line ('\n').
+ ///
+ private const char DefaultNewLine = '\n';
+
+ ///
+ /// The instance that text will be written to.
+ ///
+ private DefaultInterpolatedStringHandler _handler;
+
+ ///
+ /// The current indentation level.
+ ///
+ private int _currentIndentationLevel;
+
+ ///
+ /// The current indentation, as text.
+ ///
+ private string _currentIndentation;
+
+ ///
+ /// The cached array of available indentations, as text.
+ ///
+ private string[] _availableIndentations;
+
+ ///
+ /// Creates a new value with the specified parameters.
+ ///
+ /// The number of constant characters outside of interpolation expressions in the interpolated string.
+ /// The number of interpolation expressions in the interpolated string.
+ public IndentedTextWriter(int literalLength, int formattedCount)
+ {
+ _handler = new DefaultInterpolatedStringHandler(literalLength, formattedCount);
+ _currentIndentationLevel = 0;
+ _currentIndentation = "";
+ _availableIndentations = new string[4];
+ _availableIndentations[0] = "";
+
+ for (int i = 1, n = _availableIndentations.Length; i < n; i++)
+ {
+ _availableIndentations[i] = _availableIndentations[i - 1] + DefaultIndentation;
+ }
+ }
+
+ ///
+ /// Increases the current indentation level.
+ ///
+ public void IncreaseIndent()
+ {
+ _currentIndentationLevel++;
+
+ if (_currentIndentationLevel == _availableIndentations.Length)
+ {
+ Array.Resize(ref _availableIndentations, _availableIndentations.Length * 2);
+ }
+
+ // Set both the current indentation and the current position in the indentations
+ // array to the expected indentation for the incremented level (ie. one level more).
+ _currentIndentation = _availableIndentations[_currentIndentationLevel]
+ ??= _availableIndentations[_currentIndentationLevel - 1] + DefaultIndentation;
+ }
+
+ ///
+ /// Decreases the current indentation level.
+ ///
+ public void DecreaseIndent()
+ {
+ _currentIndentationLevel--;
+ _currentIndentation = _availableIndentations[_currentIndentationLevel];
+ }
+
+ ///
+ /// Writes a block to the underlying buffer.
+ ///
+ /// A value to close the open block with.
+ public Block WriteBlock()
+ {
+ WriteLine("{");
+ IncreaseIndent();
+
+ return new(this);
+ }
+
+ ///
+ /// Writes content to the underlying buffer.
+ ///
+ /// The content to write.
+ /// Whether the input content is multiline.
+ public void Write(string content, bool isMultiline = false)
+ {
+ Write(content.AsSpan(), isMultiline);
+ }
+
+ ///
+ /// Writes content to the underlying buffer.
+ ///
+ /// The content to write.
+ /// Whether the input content is multiline.
+ public void Write(scoped ReadOnlySpan content, bool isMultiline = false)
+ {
+ if (isMultiline)
+ {
+ while (content.Length > 0)
+ {
+ int newLineIndex = content.IndexOf(DefaultNewLine);
+
+ if (newLineIndex < 0)
+ {
+ // There are no new lines left, so the content can be written as a single line
+ WriteRawText(content);
+
+ break;
+ }
+ else
+ {
+ ReadOnlySpan line = content[..newLineIndex];
+
+ // Write the current line (if it's empty, we can skip writing the text entirely).
+ // This ensures that raw multiline string literals with blank lines don't have
+ // extra whitespace at the start of those lines, which would otherwise happen.
+ WriteIf(!line.IsEmpty, line);
+ WriteLine();
+
+ // Move past the new line character (the result could be an empty span)
+ content = content[(newLineIndex + 1)..];
+ }
+ }
+ }
+ else
+ {
+ WriteRawText(content);
+ }
+ }
+
+ ///
+ /// Writes content to the underlying buffer.
+ ///
+ /// The interpolated string handler with content to write.
+ [UnconditionalSuppressMessage("Performance", "CA1822", Justification = "This method implicitly passes 'this' to the input handler.")]
+ public readonly void Write([InterpolatedStringHandlerArgument("")] scoped ref WriteInterpolatedStringHandler handler)
+ {
+ }
+
+ ///
+ /// Writes content to the underlying buffer.
+ ///
+ /// The interpolated string handler with content to write.
+ /// Whether the input content is multiline.
+ [UnconditionalSuppressMessage("Performance", "CA1822", Justification = "This method implicitly passes 'this' to the input handler.")]
+ public readonly void Write(scoped ref DefaultInterpolatedStringHandler handler, bool isMultiline)
+ {
+ Unsafe.AsRef(in this).Write(handler.Text, isMultiline);
+
+ handler.Clear();
+ }
+
+ ///
+ /// Writes content to the underlying buffer depending on an input condition.
+ ///
+ /// The condition to use to decide whether or not to write content.
+ /// The content to write.
+ /// Whether the input content is multiline.
+ public void WriteIf(bool condition, string content, bool isMultiline = false)
+ {
+ if (condition)
+ {
+ Write(content.AsSpan(), isMultiline);
+ }
+ }
+
+ ///
+ /// Writes content to the underlying buffer depending on an input condition.
+ ///
+ /// The condition to use to decide whether or not to write content.
+ /// The content to write.
+ /// Whether the input content is multiline.
+ public void WriteIf(bool condition, scoped ReadOnlySpan content, bool isMultiline = false)
+ {
+ if (condition)
+ {
+ Write(content, isMultiline);
+ }
+ }
+
+ ///
+ /// Writes content to the underlying buffer depending on an input condition.
+ ///
+ /// The condition to use to decide whether or not to write content.
+ /// The interpolated string handler with content to write.
+ [UnconditionalSuppressMessage("Performance", "CA1822", Justification = "This method implicitly passes 'this' to the input handler.")]
+ public readonly void WriteIf(bool condition, [InterpolatedStringHandlerArgument("", nameof(condition))] scoped ref WriteIfInterpolatedStringHandler handler)
+ {
+ }
+
+ ///
+ /// Writes a line to the underlying buffer.
+ ///
+ /// Indicates whether to skip adding the line if there already is one.
+ public void WriteLine(bool skipIfPresent = false)
+ {
+ if (skipIfPresent && _handler.Text is [.., '\n', '\n'])
+ {
+ return;
+ }
+
+ _handler.AppendFormatted(DefaultNewLine);
+ }
+
+ ///
+ /// Writes content to the underlying buffer and appends a trailing new line.
+ ///
+ /// The content to write.
+ /// Whether the input content is multiline.
+ public void WriteLine(string content, bool isMultiline = false)
+ {
+ WriteLine(content.AsSpan(), isMultiline);
+ }
+
+ ///
+ /// Writes content to the underlying buffer and appends a trailing new line.
+ ///
+ /// The content to write.
+ /// Whether the input content is multiline.
+ public void WriteLine(scoped ReadOnlySpan content, bool isMultiline = false)
+ {
+ Write(content, isMultiline);
+ WriteLine();
+ }
+
+ ///
+ /// Writes content to the underlying buffer and appends a trailing new line.
+ ///
+ /// The interpolated string handler with content to write.
+ public readonly void WriteLine([InterpolatedStringHandlerArgument("")] scoped ref WriteInterpolatedStringHandler handler)
+ {
+ Unsafe.AsRef(in this).WriteLine();
+ }
+
+ ///
+ /// Writes content to the underlying buffer and appends a trailing new line.
+ ///
+ /// The interpolated string handler with content to write.
+ /// Whether the input content is multiline.
+ public readonly void WriteLine(scoped ref DefaultInterpolatedStringHandler handler, bool isMultiline)
+ {
+ Unsafe.AsRef(in this).WriteLine(handler.Text, isMultiline);
+
+ handler.Clear();
+ }
+
+ ///
+ /// Writes a line to the underlying buffer depending on an input condition.
+ ///
+ /// The condition to use to decide whether or not to write content.
+ /// Indicates whether to skip adding the line if there already is one.
+ public void WriteLineIf(bool condition, bool skipIfPresent = false)
+ {
+ if (condition)
+ {
+ WriteLine(skipIfPresent);
+ }
+ }
+
+ ///
+ /// Writes content to the underlying buffer and appends a trailing new line depending on an input condition.
+ ///
+ /// The condition to use to decide whether or not to write content.
+ /// The content to write.
+ /// Whether the input content is multiline.
+ public void WriteLineIf(bool condition, string content, bool isMultiline = false)
+ {
+ if (condition)
+ {
+ WriteLine(content.AsSpan(), isMultiline);
+ }
+ }
+
+ ///
+ /// Writes content to the underlying buffer and appends a trailing new line depending on an input condition.
+ ///
+ /// The condition to use to decide whether or not to write content.
+ /// The content to write.
+ /// Whether the input content is multiline.
+ public void WriteLineIf(bool condition, scoped ReadOnlySpan content, bool isMultiline = false)
+ {
+ if (condition)
+ {
+ Write(content, isMultiline);
+ WriteLine();
+ }
+ }
+
+ ///
+ /// Writes content to the underlying buffer and appends a trailing new line depending on an input condition.
+ ///
+ /// The condition to use to decide whether or not to write content.
+ /// The interpolated string handler with content to write.
+ public readonly void WriteLineIf(bool condition, [InterpolatedStringHandlerArgument("", nameof(condition))] scoped ref WriteIfInterpolatedStringHandler handler)
+ {
+ if (condition)
+ {
+ Unsafe.AsRef(in this).WriteLine();
+ }
+ }
+
+ ///
+ public string ToStringAndClear()
+ {
+ string text = _handler.Text.Trim().ToString();
+
+ _handler.Clear();
+
+ return text;
+ }
+
+ ///
+ public void Dispose()
+ {
+ _handler.Clear();
+ }
+
+ ///
+ /// Writes raw text to the underlying buffer, adding leading indentation if needed.
+ ///
+ /// The raw text to write.
+ private void WriteRawText(scoped ReadOnlySpan content)
+ {
+ if (_handler.Text.Length == 0 || _handler.Text[^1] == DefaultNewLine)
+ {
+ _handler.AppendLiteral(_currentIndentation);
+ }
+
+ _handler.AppendFormatted(content);
+ }
+
+ ///
+ /// A delegate representing a callback to write data into an instance.
+ ///
+ /// The type of data to use.
+ /// The input data to use to write into .
+ /// The instance to write into.
+ public delegate void Callback(T value, IndentedTextWriter writer);
+
+ ///
+ /// Represents an indented block that needs to be closed.
+ ///
+ /// The input instance to wrap.
+ public ref struct Block(IndentedTextWriter writer) : IDisposable
+ {
+ ///
+ /// The instance to write to.
+ ///
+ private IndentedTextWriter _writer = writer;
+
+ ///
+ public void Dispose()
+ {
+ IndentedTextWriter writer = _writer;
+
+ _writer = default;
+
+ // We check the indentation as a way of knowing if we have reset the field before.
+ // The field itself can't be 'null', but if we have assigned 'default' to it, then
+ // that field will be 'null' even though it's always set from the constructor.
+ if (writer._currentIndentation is not null)
+ {
+ writer.DecreaseIndent();
+ writer.WriteLine("}");
+ }
+ }
+ }
+
+ ///
+ /// Provides a handler used by the language compiler to append interpolated strings into instances.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [InterpolatedStringHandler]
+ public readonly ref struct WriteInterpolatedStringHandler
+ {
+ /// The associated to which to append.
+ private readonly IndentedTextWriter _writer;
+
+ /// Creates a handler used to append an interpolated string into a .
+ /// The number of constant characters outside of interpolation expressions in the interpolated string.
+ /// The number of interpolation expressions in the interpolated string.
+ /// The associated to which to append.
+ /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.
+ public WriteInterpolatedStringHandler(int literalLength, int formattedCount, IndentedTextWriter writer)
+ {
+ _writer = writer;
+ }
+
+ /// Writes the specified string to the handler.
+ /// The string to write.
+ public void AppendLiteral(string value)
+ {
+ _writer.Write(value);
+ }
+
+ /// Writes the specified value to the handler.
+ /// The value to write.
+ public void AppendFormatted(string? value)
+ {
+ AppendFormatted(value);
+ }
+
+ /// Writes the specified character span to the handler.
+ /// The span to write.
+ public void AppendFormatted(scoped ReadOnlySpan value)
+ {
+ _writer.Write(value);
+ }
+
+ /// Writes the specified value to the handler.
+ /// The value to write.
+ /// The type of the value to write.
+ public void AppendFormatted(T? value)
+ {
+ if (value is not null)
+ {
+ _writer.Write(value.ToString()!);
+ }
+ }
+
+ /// Writes the specified value to the handler.
+ /// The value to write.
+ /// The format string.
+ /// The type of the value to write.
+ [UnconditionalSuppressMessage("Style", "IDE0038", Justification = "Not using pattern matching to avoid boxing.")]
+ public void AppendFormatted(T? value, string? format)
+ {
+ if (value is IFormattable)
+ {
+ _writer.Write(((IFormattable)value).ToString(format, CultureInfo.InvariantCulture));
+ }
+ else if (value is not null)
+ {
+ _writer.Write(value.ToString()!);
+ }
+ }
+ }
+
+ ///
+ /// Provides a handler used by the language compiler to conditionally append interpolated strings into instances.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [InterpolatedStringHandler]
+ public readonly ref struct WriteIfInterpolatedStringHandler
+ {
+ /// The associated to use.
+ private readonly WriteInterpolatedStringHandler _writer;
+
+ /// Creates a handler used to append an interpolated string into a .
+ /// The number of constant characters outside of interpolation expressions in the interpolated string.
+ /// The number of interpolation expressions in the interpolated string.
+ /// The associated to which to append.
+ /// The condition to use to decide whether or not to write content.
+ /// A value indicating whether formatting should proceed.
+ /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.
+ public WriteIfInterpolatedStringHandler(int literalLength, int formattedCount, IndentedTextWriter writer, bool condition, out bool shouldAppend)
+ {
+ if (condition)
+ {
+ _writer = new WriteInterpolatedStringHandler(literalLength, formattedCount, writer);
+
+ shouldAppend = true;
+ }
+ else
+ {
+ _writer = default;
+
+ shouldAppend = false;
+ }
+ }
+
+ ///
+ public void AppendLiteral(string value)
+ {
+ _writer.AppendLiteral(value);
+ }
+
+ ///
+ public void AppendFormatted(string? value)
+ {
+ _writer.AppendFormatted(value);
+ }
+
+ ///
+ public void AppendFormatted(scoped ReadOnlySpan value)
+ {
+ _writer.AppendFormatted(value);
+ }
+
+ ///
+ public void AppendFormatted(T? value)
+ {
+ _writer.AppendFormatted(value);
+ }
+
+ ///
+ public void AppendFormatted(T? value, string? format)
+ {
+ _writer.AppendFormatted(value, format);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringExportTypesOptions.cs b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringExportTypesOptions.cs
new file mode 100644
index 000000000..83a08c8e6
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringExportTypesOptions.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace WindowsRuntime.SourceGenerator.Models;
+
+///
+/// Options for .
+///
+///
+///
+///
+internal record AuthoringExportTypesOptions(
+ bool PublishAot,
+ bool IsComponent,
+ bool MergeReferencedActivationFactories)
+{
+ ///
+ /// Gets whether the managed exports should be emitted.
+ ///
+ /// Whether the managed exports should be emitted.
+ public bool ShouldEmitManagedExports()
+ {
+ return IsComponent || MergeReferencedActivationFactories;
+ }
+
+ ///
+ /// Gets whether the native exports should be emitted.
+ ///
+ /// Whether the native exports should be emitted.
+ public bool ShouldEmitNativeExports()
+ {
+ if (!PublishAot)
+ {
+ return false;
+ }
+
+ // We need these either in normal publishing scenarios where AOT is enabled, or also
+ // if the project is not a component, but we're merging referenced activation factories.
+ return IsComponent || MergeReferencedActivationFactories;
+ }
+}
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringManagedExportsInfo.cs b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringManagedExportsInfo.cs
new file mode 100644
index 000000000..3781bc5fe
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringManagedExportsInfo.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace WindowsRuntime.SourceGenerator.Models;
+
+///
+/// Options for specific to managed exports.
+///
+/// The assembly name for the current assembly.
+/// The names of the merged managed exports types.
+/// The options for the generator.
+internal record AuthoringManagedExportsInfo(
+ string AssemblyName,
+ EquatableArray MergedManagedExportsTypeNames,
+ AuthoringExportTypesOptions Options);
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringNativeExportsInfo.cs b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringNativeExportsInfo.cs
new file mode 100644
index 000000000..38c52a5ef
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringNativeExportsInfo.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace WindowsRuntime.SourceGenerator.Models;
+
+///
+/// Options for specific to native exports.
+///
+/// The assembly name for the current assembly.
+/// The options for the generator.
+internal record AuthoringNativeExportsInfo(string AssemblyName, AuthoringExportTypesOptions Options);
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.Execute.cs b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.Execute.cs
index 73ea894b4..089d82d06 100644
--- a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.Execute.cs
+++ b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.Execute.cs
@@ -11,7 +11,7 @@
namespace WindowsRuntime.SourceGenerator;
///
-public partial class TypeMapAssemblyTargetGenerator : IIncrementalGenerator
+public partial class TypeMapAssemblyTargetGenerator
{
///
/// Generation methods for .
diff --git a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs
index 58d224412..9f695a5df 100644
--- a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs
+++ b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Immutable;
-using System.Linq;
using Microsoft.CodeAnalysis;
namespace WindowsRuntime.SourceGenerator;
diff --git a/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeComponentAssemblyExportsTypeAttribute.cs b/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeComponentAssemblyExportsTypeAttribute.cs
new file mode 100644
index 000000000..9bdc7db4e
--- /dev/null
+++ b/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeComponentAssemblyExportsTypeAttribute.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.ComponentModel;
+
+namespace WindowsRuntime.InteropServices;
+
+///
+/// Indicates which type contains the managed GetActivationFactory method to invoke for authoring scenarios. This
+/// attribute is only meant to be used within an assembly annotated with .
+///
+///
+/// This attribute is emitted by the CsWinRT generator, and it is not meant to be used directly.
+///
+///
+[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
+[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
+ DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
+ UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
+[EditorBrowsable(EditorBrowsableState.Never)]
+public sealed class WindowsRuntimeComponentAssemblyExportsTypeAttribute : Attribute
+{
+ ///
+ /// Creates a new instance with the specified parameters.
+ ///
+ /// The type that contains the managed GetActivationFactory method to invoke for authoring scenarios.
+ public WindowsRuntimeComponentAssemblyExportsTypeAttribute(Type exportsType)
+ {
+ ExportsType = exportsType;
+ }
+
+ ///
+ /// Gets the type that contains the managed GetActivationFactory method to invoke for authoring scenarios.
+ ///
+ public Type ExportsType { get; }
+}
\ No newline at end of file
diff --git a/src/cswinrt.slnx b/src/cswinrt.slnx
index f656521e7..972ff5d1b 100644
--- a/src/cswinrt.slnx
+++ b/src/cswinrt.slnx
@@ -19,6 +19,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+