Skip to content

Commit

Permalink
perf: use custom metadata ref resolver in roslyn workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgerangel-msft committed Apr 5, 2024
1 parent c130a33 commit ef01170
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ await foreach (var file in workspace.GetGeneratedFilesAsync())
Directory.CreateDirectory(Path.GetDirectoryName(filename)!);
await File.WriteAllTextAsync(filename, file.Text);
}


// compile the workspace
//var result = await workspace.CompileProject();
//var errors = result.Diagnostics.Where(n => n.Severity == DiagnosticSeverity.Error);
//Console.WriteLine($"Compilation success?: {result.Success}");
//foreach (var error in errors)
//{
// Console.WriteLine(error);
//}
}

private static void DeleteDirectory(string path, string[] keepFiles)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal class GeneratedCodeWorkspace
public static readonly string GeneratedTestFolder = "GeneratedTests";

private static readonly IReadOnlyList<MetadataReference> AssemblyMetadataReferences;
private static readonly WorkspaceMetadataReferenceResolver _metadataReferenceResolver;

private static readonly CSharpSyntaxRewriter SA1505Rewriter = new SA1505Rewriter();

Expand All @@ -41,15 +42,13 @@ static GeneratedCodeWorkspace()
MetadataReference.CreateFromFile(typeof(Response).Assembly.Location),
MetadataReference.CreateFromFile(typeof(ClientResult).Assembly.Location),
MetadataReference.CreateFromFile(typeof(ArmResource).Assembly.Location),
MetadataReference.CreateFromFile(typeof(NUnit.Framework.NUnitAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Azure.Core.Expressions.DataFactory.DataFactoryElementKind).Assembly.Location),
};

var trustedAssemblies = ((string?)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") ?? "").Split(Path.PathSeparator);
foreach (var tpl in trustedAssemblies)
{
references.Add(MetadataReference.CreateFromFile(tpl));
}

IReadOnlyList<string> trustedAssemblies = ((string?)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") ?? "").Split(Path.PathSeparator);
AssemblyMetadataReferences = references;
_metadataReferenceResolver = new WorkspaceMetadataReferenceResolver(trustedAssemblies);
}

private static readonly string[] SharedFolders = { SharedFolder };
Expand Down Expand Up @@ -248,7 +247,7 @@ public static GeneratedCodeWorkspace CreateExistingCodeProject(string outputDire
project = project
.AddMetadataReferences(AssemblyMetadataReferences)
.WithCompilationOptions(new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Disable));
OutputKind.DynamicallyLinkedLibrary, metadataReferenceResolver: _metadataReferenceResolver, nullableContextOptions: NullableContextOptions.Disable));

return new GeneratedCodeWorkspace(project);
}
Expand All @@ -260,7 +259,7 @@ public static GeneratedCodeWorkspace CreateExistingCodeProject(string outputDire
project = project
.AddMetadataReferences(AssemblyMetadataReferences)
.WithCompilationOptions(new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Disable));
OutputKind.DynamicallyLinkedLibrary, metadataReferenceResolver: _metadataReferenceResolver, nullableContextOptions: NullableContextOptions.Disable));
project = project.AddMetadataReference(MetadataReference.CreateFromFile(dllPath, documentation: XmlDocumentationProvider.CreateFromFile(xmlDocumentationpath)));
return await project.GetCompilationAsync();
}
Expand All @@ -274,7 +273,7 @@ private static Project CreateGeneratedCodeProject()
generatedCodeProject = generatedCodeProject
.AddMetadataReferences(AssemblyMetadataReferences)
.WithCompilationOptions(new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Disable));
OutputKind.DynamicallyLinkedLibrary, metadataReferenceResolver: _metadataReferenceResolver, nullableContextOptions: NullableContextOptions.Disable));
return generatedCodeProject;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;

namespace AutoRest.CSharp.AutoRest.Plugins
{
/// <summary>
/// Resolves metadata references for a workspace.
/// </summary>
internal sealed class WorkspaceMetadataReferenceResolver : MetadataReferenceResolver
{
// The set of unique directory paths to probe for assemblies.
internal readonly HashSet<string> ProbingPaths;

/// <summary>
/// Initializes a new instance of the <see cref="WorkspaceMetadataReferenceResolver"/> class
/// with the specified trusted platform assemblies.
/// </summary>
/// <param name="trustedPlatformAssemblies">The list of paths to the trusted platform assemblies.</param>
internal WorkspaceMetadataReferenceResolver(IReadOnlyList<string> trustedPlatformAssemblies)
{
HashSet<string> probingPaths = new();
foreach (var assembly in trustedPlatformAssemblies)
{
var directory = Path.GetDirectoryName(assembly);
if (directory != null)
{
probingPaths.Add(directory);
}
}

ProbingPaths = probingPaths;
}

internal bool Equals(WorkspaceMetadataReferenceResolver? other)
{
return ReferenceEquals(this, other);
}

public override bool ResolveMissingAssemblies => true;

public override bool Equals(object? other) => Equals(other as WorkspaceMetadataReferenceResolver);

public override int GetHashCode()
{
return RuntimeHelpers.GetHashCode(ProbingPaths);
}

/// <summary>
/// Attempts to resolve a missing assembly reference for the specified definition and reference identity.
/// The resolver will attempt to locate the assembly in the probing paths <see cref="ProbingPaths"/>. If the assembly
/// is found, a <see cref="PortableExecutableReference"/> is created and returned; otherwise, <see langword="null"/> is returned.
/// </summary>
public override PortableExecutableReference? ResolveMissingAssembly(MetadataReference definition, AssemblyIdentity referenceIdentity)
{
// check the probing paths
if (ProbingPaths.Any())
{
foreach (var path in ProbingPaths)
{
var assemblyPath = Path.Combine(path, referenceIdentity.Name + ".dll");
if (File.Exists(assemblyPath))
{
return MetadataReference.CreateFromFile(assemblyPath);
}
}
}

return null;
}

public override ImmutableArray<PortableExecutableReference> ResolveReference(string reference, string? baseFilePath, MetadataReferenceProperties properties)
{
throw new NotImplementedException();
}
}
}

0 comments on commit ef01170

Please sign in to comment.