diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index d5e45cc903032..92ccd213d591e 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -45,4 +45,5 @@ src/Features/**/PublicAPI.Unshipped.txt @dotnet/roslyn-api-owners
src/EditorFeatures/**/PublicAPI.Unshipped.txt @dotnet/roslyn-api-owners
src/Tools/ExternalAccess/OmniSharp*/ @333fred @dibarbet
+src/Tools/ExternalAccess/RazorCompiler*/ @dotnet/roslyn-compiler
src/Tools/ExternalAccess/CompilerDeveloperSDK/ @333fred
diff --git a/Compilers.slnf b/Compilers.slnf
index c810adbc006d7..e73b1562e7b0b 100644
--- a/Compilers.slnf
+++ b/Compilers.slnf
@@ -67,6 +67,8 @@
"src\\Tools\\BuildBoss\\BuildBoss.csproj",
"src\\Tools\\Replay\\Replay.csproj",
"src\\Tools\\BuildValidator\\BuildValidator.csproj",
+ "src\\Tools\\ExternalAccess\\RazorCompilerTest\\Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.UnitTests.csproj",
+ "src\\Tools\\ExternalAccess\\RazorCompiler\\Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.csproj",
"src\\Tools\\PrepareTests\\PrepareTests.csproj",
"src\\Tools\\Source\\CompilerGeneratorTools\\Source\\BoundTreeGenerator\\CompilersBoundTreeGenerator.csproj",
"src\\Tools\\Source\\CompilerGeneratorTools\\Source\\CSharpErrorFactsGenerator\\CSharpErrorFactsGenerator.csproj",
diff --git a/Roslyn.sln b/Roslyn.sln
index b682dc262a06b..9a79c163e46ca 100644
--- a/Roslyn.sln
+++ b/Roslyn.sln
@@ -504,6 +504,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Test
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestDiscoveryWorker", "src\Tools\TestDiscoveryWorker\TestDiscoveryWorker.csproj", "{8BC50AFF-1EBF-4E9A-AEBB-04F387AA800F}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler", "src\Tools\ExternalAccess\RazorCompiler\Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.csproj", "{E5E0BF73-95F7-4BC3-8443-2336C4FF4297}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.UnitTests", "src\Tools\ExternalAccess\RazorCompilerTest\Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.UnitTests.csproj", "{828FD0DB-9927-42AC-B6C2-D1514965D6C3}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.LanguageServer", "src\Features\LanguageServer\Microsoft.CodeAnalysis.LanguageServer\Microsoft.CodeAnalysis.LanguageServer.csproj", "{2A3C94F7-5B5E-4CDC-B645-672815E61DEB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.LanguageServer.UnitTests", "src\Features\LanguageServer\Microsoft.CodeAnalysis.LanguageServer.UnitTests\Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj", "{9A90AA02-4275-40ED-B1F1-731AF17E675C}"
@@ -1282,6 +1286,14 @@ Global
{8BC50AFF-1EBF-4E9A-AEBB-04F387AA800F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BC50AFF-1EBF-4E9A-AEBB-04F387AA800F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BC50AFF-1EBF-4E9A-AEBB-04F387AA800F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E5E0BF73-95F7-4BC3-8443-2336C4FF4297}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E5E0BF73-95F7-4BC3-8443-2336C4FF4297}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E5E0BF73-95F7-4BC3-8443-2336C4FF4297}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E5E0BF73-95F7-4BC3-8443-2336C4FF4297}.Release|Any CPU.Build.0 = Release|Any CPU
+ {828FD0DB-9927-42AC-B6C2-D1514965D6C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {828FD0DB-9927-42AC-B6C2-D1514965D6C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {828FD0DB-9927-42AC-B6C2-D1514965D6C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {828FD0DB-9927-42AC-B6C2-D1514965D6C3}.Release|Any CPU.Build.0 = Release|Any CPU
{2A3C94F7-5B5E-4CDC-B645-672815E61DEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A3C94F7-5B5E-4CDC-B645-672815E61DEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A3C94F7-5B5E-4CDC-B645-672815E61DEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -1600,6 +1612,8 @@ Global
{58AD1B2C-6FFC-47CB-838A-54D0CA2BF0C8} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A}
{8A29449D-411E-49E4-B99E-E8428076BB21} = {55A62CFA-1155-46F1-ADF3-BEEE51B58AB5}
{8BC50AFF-1EBF-4E9A-AEBB-04F387AA800F} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
+ {E5E0BF73-95F7-4BC3-8443-2336C4FF4297} = {8977A560-45C2-4EC2-A849-97335B382C74}
+ {828FD0DB-9927-42AC-B6C2-D1514965D6C3} = {8977A560-45C2-4EC2-A849-97335B382C74}
{2A3C94F7-5B5E-4CDC-B645-672815E61DEB} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A}
{9A90AA02-4275-40ED-B1F1-731AF17E675C} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A}
{521ADC3E-CC15-414B-9356-D87C5BCF3A24} = {C52D8057-43AF-40E6-A01B-6CDBB7301985}
diff --git a/src/Compilers/CSharp/csc/CscCommandLine.projitems b/src/Compilers/CSharp/csc/CscCommandLine.projitems
index eb07fde346dce..b25a580868321 100644
--- a/src/Compilers/CSharp/csc/CscCommandLine.projitems
+++ b/src/Compilers/CSharp/csc/CscCommandLine.projitems
@@ -36,6 +36,7 @@
+
diff --git a/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs b/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs
index 5c47171fb8a6f..949110ac56a74 100644
--- a/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs
+++ b/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs
@@ -519,6 +519,80 @@ public void AssemblyLoading_DependencyInDifferentDirectory(AnalyzerTestKind kind
});
}
+#if NET472
+ ///
+ /// Verify that MS.CA.EA.RazorCompiler will be loaded from the compiler directory not the
+ /// analyzer directory.
+ ///
+ [Theory]
+ [CombinatorialData]
+ public void AssemblyLoading_RazorCompiler1(AnalyzerTestKind kind)
+ {
+ Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) =>
+ {
+ using var temp = new TempRoot();
+ var tempDir = temp.CreateDirectory();
+
+ var externalAccessRazorPath = typeof(Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.GeneratorExtensions).Assembly.Location;
+ var alternatePath = tempDir.CreateDirectory("a").CreateFile("Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.dll").CopyContentFrom(externalAccessRazorPath).Path;
+
+ loader.AddDependencyLocation(alternatePath);
+ Assembly assembly = loader.LoadFromPath(alternatePath);
+
+ Assert.Equal(externalAccessRazorPath, assembly.Location);
+
+ // Even though EA.RazorCompiler is loaded from the compiler directory the shadow copy loader
+ // still does a defensive copy.
+ var copyCount = loader is ShadowCopyAnalyzerAssemblyLoader
+ ? 1
+ : (int?)null;
+
+ VerifyDependencyAssemblies(
+ loader,
+ copyCount: copyCount,
+ []);
+ });
+ }
+
+ ///
+ /// Verify that MS.CA.EA.RazorCompiler will be loaded from the compiler directory not the
+ /// analyzer directory.
+ ///
+ [Theory]
+ [CombinatorialData]
+ public void AssemblyLoading_RazorCompiler2(AnalyzerTestKind kind)
+ {
+ Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) =>
+ {
+ using var temp = new TempRoot();
+ var tempDir = temp.CreateDirectory();
+
+ var externalAccessRazorPath = typeof(Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.GeneratorExtensions).Assembly.Location;
+ var dir = tempDir.CreateDirectory("a");
+ var alternatePath = dir.CreateFile("Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.dll").CopyContentFrom(externalAccessRazorPath).Path;
+ var deltaFile = dir.CreateFile("Delta.dll").CopyContentFrom(testFixture.Delta1).Path;
+
+ loader.AddDependencyLocation(alternatePath);
+ loader.AddDependencyLocation(deltaFile);
+ Assembly razorAssembly = loader.LoadFromPath(alternatePath);
+ _ = loader.LoadFromPath(deltaFile);
+
+ Assert.Equal(externalAccessRazorPath, razorAssembly.Location);
+
+ // Even though EA.RazorCompiler is loaded from the compiler directory the shadow copy loader
+ // still does a defensive copy.
+ var copyCount = loader is ShadowCopyAnalyzerAssemblyLoader
+ ? 2
+ : (int?)null;
+ VerifyDependencyAssemblies(
+ loader,
+ copyCount: copyCount,
+ deltaFile);
+ });
+ }
+
+#endif
+
///
/// Similar to except want to validate
/// a dependency in the same directory is preferred over one in a different directory.
diff --git a/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj b/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj
index e80335a1d132f..5cd86acd69fc0 100644
--- a/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj
+++ b/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj
@@ -23,6 +23,7 @@
+
diff --git a/src/Compilers/Server/VBCSCompiler/VBCSCompilerCommandLine.projitems b/src/Compilers/Server/VBCSCompiler/VBCSCompilerCommandLine.projitems
index 851b618b90dba..25e47648faab4 100644
--- a/src/Compilers/Server/VBCSCompiler/VBCSCompilerCommandLine.projitems
+++ b/src/Compilers/Server/VBCSCompiler/VBCSCompilerCommandLine.projitems
@@ -56,6 +56,7 @@
+
diff --git a/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj b/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj
index bdfcfbef906ef..d9938a752fd05 100644
--- a/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj
+++ b/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj
@@ -66,6 +66,7 @@
+
diff --git a/src/Compilers/Test/Utilities/CSharp/Microsoft.CodeAnalysis.CSharp.Test.Utilities.csproj b/src/Compilers/Test/Utilities/CSharp/Microsoft.CodeAnalysis.CSharp.Test.Utilities.csproj
index c332e5d4851d8..6e56556faaccf 100644
--- a/src/Compilers/Test/Utilities/CSharp/Microsoft.CodeAnalysis.CSharp.Test.Utilities.csproj
+++ b/src/Compilers/Test/Utilities/CSharp/Microsoft.CodeAnalysis.CSharp.Test.Utilities.csproj
@@ -32,6 +32,7 @@
+
diff --git a/src/Compilers/VisualBasic/vbc/VbcCommandLine.projitems b/src/Compilers/VisualBasic/vbc/VbcCommandLine.projitems
index d1729cd816e1c..a85e09f46fdde 100644
--- a/src/Compilers/VisualBasic/vbc/VbcCommandLine.projitems
+++ b/src/Compilers/VisualBasic/vbc/VbcCommandLine.projitems
@@ -36,6 +36,7 @@
+
diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj
index b628f743b2ed0..cc877c4cc5a8e 100644
--- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj
+++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj
@@ -69,6 +69,9 @@
+
+
+
diff --git a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj
index 20ba57b5f5573..0d3924a25d6f6 100644
--- a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj
+++ b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj
@@ -86,6 +86,11 @@
+
+
+
diff --git a/src/NuGet/Microsoft.Net.Compilers.Toolset/DesktopCompilerArtifacts.targets b/src/NuGet/Microsoft.Net.Compilers.Toolset/DesktopCompilerArtifacts.targets
index aa85224f576fa..7008b7b87c2f6 100644
--- a/src/NuGet/Microsoft.Net.Compilers.Toolset/DesktopCompilerArtifacts.targets
+++ b/src/NuGet/Microsoft.Net.Compilers.Toolset/DesktopCompilerArtifacts.targets
@@ -33,6 +33,7 @@
+
diff --git a/src/NuGet/VS.Tools.Roslyn.Package/VS.Tools.Roslyn.Package.csproj b/src/NuGet/VS.Tools.Roslyn.Package/VS.Tools.Roslyn.Package.csproj
index a2cd0a4560532..5a7283350c3c1 100644
--- a/src/NuGet/VS.Tools.Roslyn.Package/VS.Tools.Roslyn.Package.csproj
+++ b/src/NuGet/VS.Tools.Roslyn.Package/VS.Tools.Roslyn.Package.csproj
@@ -26,6 +26,7 @@
+
diff --git a/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs b/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs
index 95268daf87b17..23717503f643f 100644
--- a/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs
+++ b/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs
@@ -178,7 +178,9 @@ private bool CheckPackages(TextWriter textWriter)
allGood &= VerifyPackageCore(
textWriter,
FindNuGetPackage(Path.Combine(ArtifactsDirectory, "packages", Configuration, "Shipping"), "Microsoft.Net.Compilers.Toolset"),
- excludeFunc: relativeFileName => relativeFileName.StartsWith(@"tasks\netcore\bincore\Microsoft.DiaSymReader.Native", PathComparison),
+ excludeFunc: relativeFileName =>
+ relativeFileName.StartsWith(@"tasks\netcore\bincore\Microsoft.DiaSymReader.Native", PathComparison) ||
+ relativeFileName.StartsWith(@"tasks\netcore\bincore\Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.dll", PathComparison),
(@"tasks\net472", GetProjectOutputDirectory("csc", "net472")),
(@"tasks\net472", GetProjectOutputDirectory("vbc", "net472")),
(@"tasks\net472", GetProjectOutputDirectory("csi", "net472")),
diff --git a/src/Tools/ExternalAccess/RazorCompiler/GeneratorExtensions.cs b/src/Tools/ExternalAccess/RazorCompiler/GeneratorExtensions.cs
new file mode 100644
index 0000000000000..bc7d9f43af7be
--- /dev/null
+++ b/src/Tools/ExternalAccess/RazorCompiler/GeneratorExtensions.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Immutable;
+using System.Threading;
+using Microsoft.CodeAnalysis.PooledObjects;
+
+namespace Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler
+{
+ internal static partial class GeneratorExtensions
+ {
+ public static void RegisterHostOutput(ref this IncrementalGeneratorInitializationContext @this, IncrementalValuesProvider source, Action action)
+ {
+ _ = @this;
+ source.Node.RegisterOutput(new HostOutputNode(source.Node, action));
+ }
+
+ public static ImmutableArray<(string Key, string Value)> GetHostOutputs(this GeneratorRunResult runResult) => runResult.HostOutputs;
+ }
+
+ internal readonly struct HostProductionContext
+ {
+ internal readonly ArrayBuilder<(string, string)> Outputs;
+
+ internal HostProductionContext(ArrayBuilder<(string, string)> outputs)
+ {
+ Outputs = outputs;
+ }
+
+ public void AddOutput(string name, string value) => Outputs.Add((name, value));
+ }
+}
diff --git a/src/Tools/ExternalAccess/RazorCompiler/HostOutputNode.cs b/src/Tools/ExternalAccess/RazorCompiler/HostOutputNode.cs
new file mode 100644
index 0000000000000..3ae1b7dcd296e
--- /dev/null
+++ b/src/Tools/ExternalAccess/RazorCompiler/HostOutputNode.cs
@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Text;
+using System.Threading;
+using Microsoft.CodeAnalysis.PooledObjects;
+using Roslyn.Utilities;
+using TOutput = System.Collections.Immutable.ImmutableArray<(string, string)>;
+
+namespace Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler
+{
+ internal sealed class HostOutputNode : IIncrementalGeneratorOutputNode, IIncrementalGeneratorNode
+ {
+ private readonly IIncrementalGeneratorNode _source;
+
+ private readonly Action _action;
+
+ public HostOutputNode(IIncrementalGeneratorNode source, Action action)
+ {
+ _source = source;
+ _action = action;
+ }
+
+ public IncrementalGeneratorOutputKind Kind => GeneratorDriver.HostKind;
+
+ public NodeStateTable UpdateStateTable(DriverStateTable.Builder graphState, NodeStateTable? previousTable, CancellationToken cancellationToken)
+ {
+ string stepName = "HostOutput";
+ var sourceTable = graphState.GetLatestStateTableForNode(_source);
+ if (sourceTable.IsCached && previousTable is not null)
+ {
+ if (graphState.DriverState.TrackIncrementalSteps)
+ {
+ return previousTable.CreateCachedTableWithUpdatedSteps(sourceTable, stepName, EqualityComparer.Default);
+ }
+ return previousTable;
+ }
+
+ var nodeTable = graphState.CreateTableBuilder(previousTable, stepName, EqualityComparer.Default);
+ foreach (var entry in sourceTable)
+ {
+ var inputs = nodeTable.TrackIncrementalSteps ? ImmutableArray.Create((entry.Step!, entry.OutputIndex)) : default;
+ if (entry.State == EntryState.Removed)
+ {
+ nodeTable.TryRemoveEntries(TimeSpan.Zero, inputs);
+ }
+ else if (entry.State != EntryState.Cached || !nodeTable.TryUseCachedEntries(TimeSpan.Zero, inputs))
+ {
+ ArrayBuilder<(string, string)> output = ArrayBuilder<(string, string)>.GetInstance();
+ HostProductionContext context = new HostProductionContext(output);
+ var stopwatch = SharedStopwatch.StartNew();
+ _action(context, entry.Item, cancellationToken);
+ nodeTable.AddEntry(output.ToImmutableAndFree(), EntryState.Added, stopwatch.Elapsed, inputs, EntryState.Added);
+ }
+ }
+
+ return nodeTable.ToImmutableAndFree();
+ }
+
+ public void AppendOutputs(IncrementalExecutionContext context, CancellationToken cancellationToken)
+ {
+ // get our own state table
+ Debug.Assert(context.TableBuilder is not null);
+ var table = context.TableBuilder!.GetLatestStateTableForNode(this);
+
+ // add each non-removed entry to the context
+ foreach (var (list, state, _, _) in table)
+ {
+ if (state != EntryState.Removed)
+ {
+ context.HostOutputBuilder.AddRange(list);
+ }
+ }
+
+ if (context.GeneratorRunStateBuilder.RecordingExecutedSteps)
+ {
+ context.GeneratorRunStateBuilder.RecordStepsFromOutputNodeUpdate(table);
+ }
+ }
+
+ IIncrementalGeneratorNode IIncrementalGeneratorNode.WithComparer(IEqualityComparer comparer) => throw ExceptionUtilities.Unreachable();
+
+ public IIncrementalGeneratorNode WithTrackingName(string name) => throw ExceptionUtilities.Unreachable();
+
+ void IIncrementalGeneratorNode.RegisterOutput(IIncrementalGeneratorOutputNode output) => throw ExceptionUtilities.Unreachable();
+ }
+}
diff --git a/src/Tools/ExternalAccess/RazorCompiler/InternalAPI.Shipped.txt b/src/Tools/ExternalAccess/RazorCompiler/InternalAPI.Shipped.txt
new file mode 100644
index 0000000000000..7dc5c58110bfa
--- /dev/null
+++ b/src/Tools/ExternalAccess/RazorCompiler/InternalAPI.Shipped.txt
@@ -0,0 +1 @@
+#nullable enable
diff --git a/src/Tools/ExternalAccess/RazorCompiler/InternalAPI.Unshipped.txt b/src/Tools/ExternalAccess/RazorCompiler/InternalAPI.Unshipped.txt
new file mode 100644
index 0000000000000..7c99629a815f4
--- /dev/null
+++ b/src/Tools/ExternalAccess/RazorCompiler/InternalAPI.Unshipped.txt
@@ -0,0 +1,15 @@
+#nullable enable
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.GeneratorExtensions
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostOutputNode
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostOutputNode.AppendOutputs(Microsoft.CodeAnalysis.IncrementalExecutionContext context, System.Threading.CancellationToken cancellationToken) -> void
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostOutputNode.HostOutputNode(Microsoft.CodeAnalysis.IIncrementalGeneratorNode! source, System.Action! action) -> void
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostOutputNode.Kind.get -> Microsoft.CodeAnalysis.IncrementalGeneratorOutputKind
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostOutputNode.UpdateStateTable(Microsoft.CodeAnalysis.DriverStateTable.Builder! graphState, Microsoft.CodeAnalysis.NodeStateTable>? previousTable, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.NodeStateTable>!
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostOutputNode.WithTrackingName(string! name) -> Microsoft.CodeAnalysis.IIncrementalGeneratorNode>!
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostProductionContext
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostProductionContext.AddOutput(string! name, string! value) -> void
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostProductionContext.HostProductionContext() -> void
+Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostProductionContext.HostProductionContext(Microsoft.CodeAnalysis.PooledObjects.ArrayBuilder<(string!, string!)>! outputs) -> void
+readonly Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.HostProductionContext.Outputs -> Microsoft.CodeAnalysis.PooledObjects.ArrayBuilder<(string!, string!)>!
+static Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.GeneratorExtensions.GetHostOutputs(this Microsoft.CodeAnalysis.GeneratorRunResult runResult) -> System.Collections.Immutable.ImmutableArray<(string! Key, string! Value)>
+static Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.GeneratorExtensions.RegisterHostOutput(this ref Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext this, Microsoft.CodeAnalysis.IncrementalValuesProvider source, System.Action! action) -> void
diff --git a/src/Tools/ExternalAccess/RazorCompiler/Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.csproj b/src/Tools/ExternalAccess/RazorCompiler/Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.csproj
new file mode 100644
index 0000000000000..f41f08081f772
--- /dev/null
+++ b/src/Tools/ExternalAccess/RazorCompiler/Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.csproj
@@ -0,0 +1,46 @@
+
+
+
+
+ Library
+ Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler
+ $(NetRoslynSourceBuild);netstandard2.0
+
+
+ false
+
+
+ true
+ Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler
+
+ A supporting package for Razor source generator:
+ https://github.com/dotnet/razor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Tools/ExternalAccess/RazorCompiler/PublicAPI.Shipped.txt b/src/Tools/ExternalAccess/RazorCompiler/PublicAPI.Shipped.txt
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/src/Tools/ExternalAccess/RazorCompiler/PublicAPI.Unshipped.txt b/src/Tools/ExternalAccess/RazorCompiler/PublicAPI.Unshipped.txt
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/src/Tools/ExternalAccess/RazorCompilerTest/HostOutputsTests.cs b/src/Tools/ExternalAccess/RazorCompilerTest/HostOutputsTests.cs
new file mode 100644
index 0000000000000..ac6da1b1b9d6a
--- /dev/null
+++ b/src/Tools/ExternalAccess/RazorCompilerTest/HostOutputsTests.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
+using Microsoft.CodeAnalysis.CSharp.UnitTests;
+using Microsoft.CodeAnalysis.Test.Utilities;
+using Roslyn.Test.Utilities.TestGenerators;
+using Xunit;
+
+namespace Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.UnitTests
+{
+ public class HostOutputsTests : CSharpTestBase
+ {
+ [Fact]
+ public void Added()
+ {
+ var source = """
+ class C { }
+ """;
+ var parseOptions = TestOptions.Regular;
+ var compilation = CreateCompilation(source, options: TestOptions.DebugDllThrowing, parseOptions: parseOptions);
+ compilation.VerifyDiagnostics();
+
+ Assert.Single(compilation.SyntaxTrees);
+
+ var generator = new PipelineCallbackGenerator(ctx =>
+ {
+ var syntaxProvider = ctx.SyntaxProvider.CreateSyntaxProvider((n, _) => n.IsKind(SyntaxKind.ClassDeclaration), (c, _) => c.Node);
+
+ ctx.RegisterHostOutput(syntaxProvider, static (hpc, node, _) =>
+ {
+ hpc.AddOutput("test", node.ToFullString());
+ });
+ });
+
+ GeneratorDriver driver = CSharpGeneratorDriver.Create(new[] { generator.AsSourceGenerator() }, parseOptions: parseOptions);
+ driver = driver.RunGenerators(compilation);
+
+ var result = driver.GetRunResult().Results.Single();
+ Assert.Empty(result.Diagnostics);
+
+ var hostOutputs = result.GetHostOutputs();
+ Assert.Equal(1, hostOutputs.Length);
+ Assert.Equal("test", hostOutputs[0].Key);
+ Assert.Equal(source, hostOutputs[0].Value);
+ }
+ }
+}
diff --git a/src/Tools/ExternalAccess/RazorCompilerTest/Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.UnitTests.csproj b/src/Tools/ExternalAccess/RazorCompilerTest/Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.UnitTests.csproj
new file mode 100644
index 0000000000000..caa37487800e7
--- /dev/null
+++ b/src/Tools/ExternalAccess/RazorCompilerTest/Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.UnitTests.csproj
@@ -0,0 +1,14 @@
+
+
+
+
+ Library
+ Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.UnitTests
+ $(NetRoslyn);net472
+
+
+
+
+
+
+
diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj
index 5183dad27cba1..12a14f196cf15 100644
--- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj
+++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj
@@ -92,6 +92,12 @@
true
BindingRedirect
+
+ Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler
+ BuiltProjectOutputGroup
+ true
+ BindingRedirect
+
Workspaces.Desktop
BuiltProjectOutputGroup;SatelliteDllsProjectOutputGroup
diff --git a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj
index 98ad23cbaab6d..9cab9ff8c290b 100644
--- a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj
+++ b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj
@@ -41,6 +41,9 @@
true
+
+ true
+
diff --git a/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj
index 2d85cd9ac2631..52a04fe8774d6 100644
--- a/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj
+++ b/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj
@@ -16,6 +16,7 @@
+