提交 c966b31c 编写于 作者: T Tomáš Matoušek 提交者: GitHub

Merge pull request #11956 from tmat/AnalyzerLoader

Improve implementation of analyzer loader
...@@ -12,7 +12,7 @@ public static int Main(string[] args) ...@@ -12,7 +12,7 @@ public static int Main(string[] args)
=> Main(args, SpecializedCollections.EmptyArray<string>()); => Main(args, SpecializedCollections.EmptyArray<string>());
public static int Main(string[] args, string[] extraArgs) public static int Main(string[] args, string[] extraArgs)
=> DesktopBuildClient.Run(args, extraArgs, RequestLanguage.CSharpCompile, Csc.Run, new SimpleAnalyzerAssemblyLoader()); => DesktopBuildClient.Run(args, extraArgs, RequestLanguage.CSharpCompile, Csc.Run, new DesktopAnalyzerAssemblyLoader());
public static int Run(string[] args, string clientDir, string workingDir, string sdkDir, TextWriter textWriter, IAnalyzerAssemblyLoader analyzerLoader) public static int Run(string[] args, string clientDir, string workingDir, string sdkDir, TextWriter textWriter, IAnalyzerAssemblyLoader analyzerLoader)
=> Csc.Run(args, new BuildPaths(clientDir: clientDir, workingDir: workingDir, sdkDir: sdkDir), textWriter, analyzerLoader); => Csc.Run(args, new BuildPaths(clientDir: clientDir, workingDir: workingDir, sdkDir: sdkDir), textWriter, analyzerLoader);
......
...@@ -41,15 +41,12 @@ ...@@ -41,15 +41,12 @@
<Compile Include="..\..\Shared\DesktopBuildClient.cs"> <Compile Include="..\..\Shared\DesktopBuildClient.cs">
<Link>DesktopBuildClient.cs</Link> <Link>DesktopBuildClient.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\Shared\AbstractAnalyzerAssemblyLoader.cs"> <Compile Include="..\..\Shared\DesktopAnalyzerAssemblyLoader.cs">
<Link>AbstractAnalyzerAssemblyLoader.cs</Link> <Link>DesktopAnalyzerAssemblyLoader.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\Shared\ExitingTraceListener.cs"> <Compile Include="..\..\Shared\ExitingTraceListener.cs">
<Link>ExitingTraceListener.cs</Link> <Link>ExitingTraceListener.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\Shared\SimpleAnalyzerAssemblyLoader.cs">
<Link>SimpleAnalyzerAssemblyLoader.cs</Link>
</Compile>
<Compile Include="..\..\Shared\Csc.cs"> <Compile Include="..\..\Shared\Csc.cs">
<Link>Csc.cs</Link> <Link>Csc.cs</Link>
</Compile> </Compile>
...@@ -71,4 +68,4 @@ ...@@ -71,4 +68,4 @@
<ImportGroup Label="Targets"> <ImportGroup Label="Targets">
<Import Project="..\..\..\..\build\Targets\VSL.Imports.targets" /> <Import Project="..\..\..\..\build\Targets\VSL.Imports.targets" />
</ImportGroup> </ImportGroup>
</Project> </Project>
\ No newline at end of file
...@@ -66,7 +66,7 @@ public Exception LoadAnalyzer(string analyzerPath) ...@@ -66,7 +66,7 @@ public Exception LoadAnalyzer(string analyzerPath)
public class AnalyzerFileReferenceTests : TestBase public class AnalyzerFileReferenceTests : TestBase
{ {
private static readonly SimpleAnalyzerAssemblyLoader s_analyzerLoader = new SimpleAnalyzerAssemblyLoader(); private static readonly DesktopAnalyzerAssemblyLoader s_analyzerLoader = new DesktopAnalyzerAssemblyLoader();
public static AnalyzerFileReference CreateAnalyzerFileReference(string fullPath) public static AnalyzerFileReference CreateAnalyzerFileReference(string fullPath)
{ {
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
<Compile Include="PEWriter\PEBuilderTests.cs" /> <Compile Include="PEWriter\PEBuilderTests.cs" />
<Compile Include="PEWriter\UsedNamespaceOrTypeTests.cs" /> <Compile Include="PEWriter\UsedNamespaceOrTypeTests.cs" />
<Compile Include="RealParserTests.cs" /> <Compile Include="RealParserTests.cs" />
<Compile Include="SimpleAnalyzerAssemblyLoaderTests.cs" /> <Compile Include="DesktopAnalyzerAssemblyLoaderTests.cs" />
<Compile Include="SourceFileResolverTest.cs" /> <Compile Include="SourceFileResolverTest.cs" />
<Compile Include="SourceGeneratorTests.cs" /> <Compile Include="SourceGeneratorTests.cs" />
<Compile Include="Text\LargeTextTests.cs" /> <Compile Include="Text\LargeTextTests.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Roslyn.Test.Utilities; using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit; using Xunit;
namespace Microsoft.CodeAnalysis.UnitTests namespace Microsoft.CodeAnalysis.UnitTests
{ {
public sealed class SimpleAnalyzerAssemblyLoaderTests : TestBase public sealed class DesktopAnalyzerAssemblyLoaderTests : TestBase
{ {
[Fact] [Fact]
public void AddDependencyLocationThrowsOnNull() public void AddDependencyLocationThrowsOnNull()
{ {
var loader = new SimpleAnalyzerAssemblyLoader(); var loader = new DesktopAnalyzerAssemblyLoader();
Assert.Throws<ArgumentNullException>("fullPath", () => loader.AddDependencyLocation(null)); Assert.Throws<ArgumentNullException>("fullPath", () => loader.AddDependencyLocation(null));
Assert.Throws<ArgumentException>("fullPath", () => loader.AddDependencyLocation("a"));
} }
[Fact] [Fact]
...@@ -28,7 +25,7 @@ public void ThrowsForMissingFile() ...@@ -28,7 +25,7 @@ public void ThrowsForMissingFile()
{ {
var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".dll"); var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".dll");
var loader = new SimpleAnalyzerAssemblyLoader(); var loader = new DesktopAnalyzerAssemblyLoader();
Assert.ThrowsAny<Exception>(() => loader.LoadFromPath(path)); Assert.ThrowsAny<Exception>(() => loader.LoadFromPath(path));
} }
...@@ -40,7 +37,7 @@ public void BasicLoad() ...@@ -40,7 +37,7 @@ public void BasicLoad()
var alphaDll = directory.CreateFile("Alpha.dll").WriteAllBytes(TestResources.AssemblyLoadTests.Alpha); var alphaDll = directory.CreateFile("Alpha.dll").WriteAllBytes(TestResources.AssemblyLoadTests.Alpha);
var loader = new SimpleAnalyzerAssemblyLoader(); var loader = new DesktopAnalyzerAssemblyLoader();
Assembly alpha = loader.LoadFromPath(alphaDll.Path); Assembly alpha = loader.LoadFromPath(alphaDll.Path);
...@@ -58,7 +55,7 @@ public void AssemblyLoading() ...@@ -58,7 +55,7 @@ public void AssemblyLoading()
var gammaDll = Temp.CreateDirectory().CreateFile("Gamma.dll").WriteAllBytes(TestResources.AssemblyLoadTests.Gamma); var gammaDll = Temp.CreateDirectory().CreateFile("Gamma.dll").WriteAllBytes(TestResources.AssemblyLoadTests.Gamma);
var deltaDll = Temp.CreateDirectory().CreateFile("Delta.dll").WriteAllBytes(TestResources.AssemblyLoadTests.Delta); var deltaDll = Temp.CreateDirectory().CreateFile("Delta.dll").WriteAllBytes(TestResources.AssemblyLoadTests.Delta);
var loader = new SimpleAnalyzerAssemblyLoader(); var loader = new DesktopAnalyzerAssemblyLoader();
loader.AddDependencyLocation(alphaDll.Path); loader.AddDependencyLocation(alphaDll.Path);
loader.AddDependencyLocation(betaDll.Path); loader.AddDependencyLocation(betaDll.Path);
loader.AddDependencyLocation(gammaDll.Path); loader.AddDependencyLocation(gammaDll.Path);
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
<Import_RootNamespace>CommandLine</Import_RootNamespace> <Import_RootNamespace>CommandLine</Import_RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)AssemblyIdentityUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)BuildProtocol.cs" /> <Compile Include="$(MSBuildThisFileDirectory)BuildProtocol.cs" />
<Compile Include="$(MSBuildThisFileDirectory)BuildClient.cs" /> <Compile Include="$(MSBuildThisFileDirectory)BuildClient.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ConsoleUtil.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ConsoleUtil.cs" />
......
...@@ -94,6 +94,9 @@ ...@@ -94,6 +94,9 @@
<Compile Include="CaseInsensitiveComparison.cs" /> <Compile Include="CaseInsensitiveComparison.cs" />
<Compile Include="CodeGen\ILEmitStyle.cs" /> <Compile Include="CodeGen\ILEmitStyle.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisResult.cs" /> <Compile Include="DiagnosticAnalyzer\AnalysisResult.cs" />
<Compile Include="DiagnosticAnalyzer\AnalyzerAssemblyLoader.cs" />
<Compile Include="FileSystem\CompilerPathUtilities.cs" />
<Compile Include="InternalUtilities\AssemblyIdentityUtils.cs" />
<Compile Include="InternalUtilities\EmptyComparer.cs" /> <Compile Include="InternalUtilities\EmptyComparer.cs" />
<Compile Include="InternalUtilities\StringOrdinalComparer.cs" /> <Compile Include="InternalUtilities\StringOrdinalComparer.cs" />
<Compile Include="MetadataReference\AssemblyIdentityMap.cs" /> <Compile Include="MetadataReference\AssemblyIdentityMap.cs" />
...@@ -211,7 +214,6 @@ ...@@ -211,7 +214,6 @@
<Compile Include="Compilation\LoadDirective.cs" /> <Compile Include="Compilation\LoadDirective.cs" />
<Compile Include="Compilation\CommonSyntaxAndDeclarationManager.cs" /> <Compile Include="Compilation\CommonSyntaxAndDeclarationManager.cs" />
<Compile Include="Compilation\SymbolFilter.cs" /> <Compile Include="Compilation\SymbolFilter.cs" />
<Compile Include="CompilerPathUtilities.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisResultBuilder.cs" /> <Compile Include="DiagnosticAnalyzer\AnalysisResultBuilder.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisScope.cs" /> <Compile Include="DiagnosticAnalyzer\AnalysisScope.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisState.AnalyzerStateData.cs" /> <Compile Include="DiagnosticAnalyzer\AnalysisState.AnalyzerStateData.cs" />
......
...@@ -398,15 +398,16 @@ public IEnumerable<AnalyzerReference> ResolveAnalyzerReferences(IAnalyzerAssembl ...@@ -398,15 +398,16 @@ public IEnumerable<AnalyzerReference> ResolveAnalyzerReferences(IAnalyzerAssembl
} }
}; };
var resolvedReferences = ArrayBuilder<AnalyzerFileReference>.GetInstance();
foreach (var reference in AnalyzerReferences) foreach (var reference in AnalyzerReferences)
{ {
var resolvedReference = ResolveAnalyzerReference(reference, analyzerLoader); var resolvedReference = ResolveAnalyzerReference(reference, analyzerLoader);
if (resolvedReference != null) if (resolvedReference != null)
{ {
resolvedReference.AnalyzerLoadFailed += errorHandler; resolvedReferences.Add(resolvedReference);
resolvedReference.AddAnalyzers(analyzerBuilder, language);
resolvedReference.AddGenerators(generatorBuilder, language); // register the reference to the analyzer loader:
resolvedReference.AnalyzerLoadFailed -= errorHandler; analyzerLoader.AddDependencyLocation(resolvedReference.FullPath);
} }
else else
{ {
...@@ -414,6 +415,17 @@ public IEnumerable<AnalyzerReference> ResolveAnalyzerReferences(IAnalyzerAssembl ...@@ -414,6 +415,17 @@ public IEnumerable<AnalyzerReference> ResolveAnalyzerReferences(IAnalyzerAssembl
} }
} }
// All analyzer references are registered now, we can start loading them:
foreach (var resolvedReference in resolvedReferences)
{
resolvedReference.AnalyzerLoadFailed += errorHandler;
resolvedReference.AddAnalyzers(analyzerBuilder, language);
resolvedReference.AddGenerators(generatorBuilder, language);
resolvedReference.AnalyzerLoadFailed -= errorHandler;
}
resolvedReferences.Free();
analyzers = analyzerBuilder.ToImmutable(); analyzers = analyzerBuilder.ToImmutable();
generators = generatorBuilder.ToImmutable(); generators = generatorBuilder.ToImmutable();
} }
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
internal abstract class AnalyzerAssemblyLoader : IAnalyzerAssemblyLoader
{
private readonly object _guard = new object();
// lock _guard to read/write
private readonly Dictionary<string, Assembly> _loadedAssembliesByPath = new Dictionary<string, Assembly>();
private readonly Dictionary<AssemblyIdentity, Assembly> _loadedAssembliesByIdentity = new Dictionary<AssemblyIdentity, Assembly>();
// maps file name to a full path (lock _guard to read/write):
private readonly Dictionary<string, List<string>> _knownAssemblyPathsBySimpleName = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
protected abstract Assembly LoadFromPathImpl(string fullPath);
#region Public API
public void AddDependencyLocation(string fullPath)
{
CompilerPathUtilities.RequireAbsolutePath(fullPath, nameof(fullPath));
string simpleName = PathUtilities.GetFileName(fullPath, includeExtension: false);
lock (_guard)
{
List<string> paths;
if (!_knownAssemblyPathsBySimpleName.TryGetValue(simpleName, out paths))
{
_knownAssemblyPathsBySimpleName.Add(simpleName, new List<string>() { fullPath });
}
else if (!paths.Contains(fullPath))
{
paths.Add(fullPath);
}
}
}
public Assembly LoadFromPath(string fullPath)
{
CompilerPathUtilities.RequireAbsolutePath(fullPath, nameof(fullPath));
return LoadFromPathUnchecked(fullPath);
}
#endregion
private Assembly LoadFromPathUnchecked(string fullPath)
{
Debug.Assert(PathUtilities.IsAbsolute(fullPath));
lock (_guard)
{
Assembly existingAssembly;
if (_loadedAssembliesByPath.TryGetValue(fullPath, out existingAssembly))
{
return existingAssembly;
}
}
Assembly assembly = LoadFromPathImpl(fullPath);
return AddToCache(assembly, fullPath);
}
private Assembly AddToCache(Assembly assembly, string fullPath)
{
Debug.Assert(PathUtilities.IsAbsolute(fullPath));
Debug.Assert(assembly != null);
var identity = AssemblyIdentity.FromAssemblyDefinition(assembly);
lock (_guard)
{
// The same assembly may be loaded from two different full paths (e.g. when loaded from GAC, etc.),
// or another thread might have loaded the assembly after we checked above.
Assembly existingAssembly;
if (_loadedAssembliesByIdentity.TryGetValue(identity, out existingAssembly))
{
return existingAssembly;
}
_loadedAssembliesByIdentity.Add(identity, assembly);
// An assembly file might be replaced by another file with a different identity.
// Last one wins.
_loadedAssembliesByPath[fullPath] = assembly;
return assembly;
}
}
public Assembly Load(string displayName)
{
AssemblyIdentity requestedIdentity;
if (!AssemblyIdentity.TryParseDisplayName(displayName, out requestedIdentity))
{
return null;
}
ImmutableArray<string> candidatePaths;
lock (_guard)
{
Assembly existingAssembly;
// First, check if this loader already loaded the requested assembly:
if (_loadedAssembliesByIdentity.TryGetValue(requestedIdentity, out existingAssembly))
{
return existingAssembly;
}
// Second, check if an assembly file of the same simple name was registered with the loader:
List<string> pathList;
if (!_knownAssemblyPathsBySimpleName.TryGetValue(requestedIdentity.Name, out pathList))
{
return null;
}
Debug.Assert(pathList.Count > 0);
candidatePaths = pathList.ToImmutableArray();
}
// Multiple assemblies of the same simple name but different identities might have been registered.
// Load the one that matches the requested identity (if any).
foreach (var candidatePath in candidatePaths)
{
var candidateIdentity = AssemblyIdentityUtils.TryGetAssemblyIdentity(candidatePath);
if (requestedIdentity.Equals(candidateIdentity))
{
return LoadFromPathUnchecked(candidatePath);
}
}
return null;
}
}
}
...@@ -185,7 +185,7 @@ private IEnumerable<DiagnosticAnalyzer> GetAnalyzersForTypeNames(Assembly analyz ...@@ -185,7 +185,7 @@ private IEnumerable<DiagnosticAnalyzer> GetAnalyzersForTypeNames(Assembly analyz
} }
catch (Exception e) catch (Exception e)
{ {
this.AnalyzerLoadFailed?.Invoke(this, new AnalyzerLoadFailureEventArgs(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer, e.Message, e, typeName)); AnalyzerLoadFailed?.Invoke(this, CreateAnalyzerFailedArgs(e, typeName));
reportedError = true; reportedError = true;
continue; continue;
} }
...@@ -199,7 +199,7 @@ private IEnumerable<DiagnosticAnalyzer> GetAnalyzersForTypeNames(Assembly analyz ...@@ -199,7 +199,7 @@ private IEnumerable<DiagnosticAnalyzer> GetAnalyzersForTypeNames(Assembly analyz
} }
catch (Exception e) catch (Exception e)
{ {
this.AnalyzerLoadFailed?.Invoke(this, new AnalyzerLoadFailureEventArgs(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer, e.Message, e, typeName)); AnalyzerLoadFailed?.Invoke(this, CreateAnalyzerFailedArgs(e, typeName));
reportedError = true; reportedError = true;
continue; continue;
} }
...@@ -213,6 +213,21 @@ private IEnumerable<DiagnosticAnalyzer> GetAnalyzersForTypeNames(Assembly analyz ...@@ -213,6 +213,21 @@ private IEnumerable<DiagnosticAnalyzer> GetAnalyzersForTypeNames(Assembly analyz
return analyzers.ToImmutable(); return analyzers.ToImmutable();
} }
private static AnalyzerLoadFailureEventArgs CreateAnalyzerFailedArgs(Exception e, string typeNameOpt = null)
{
// unwrap:
e = (e as TargetInvocationException) ?? e;
// remove all line breaks from the exception message
string message = e.Message.Replace("\r", "").Replace("\n", "");
var errorCode = (typeNameOpt != null) ?
AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer :
AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToLoadAnalyzer;
return new AnalyzerLoadFailureEventArgs(errorCode, message, e, typeNameOpt);
}
internal void AddGenerators(ImmutableArray<SourceGenerator>.Builder builder, string language) internal void AddGenerators(ImmutableArray<SourceGenerator>.Builder builder, string language)
{ {
_sourceGenerators.AddExtensions(builder, language); _sourceGenerators.AddExtensions(builder, language);
...@@ -403,7 +418,7 @@ internal void AddExtensions(ImmutableDictionary<string, ImmutableArray<TExtensio ...@@ -403,7 +418,7 @@ internal void AddExtensions(ImmutableDictionary<string, ImmutableArray<TExtensio
} }
catch (Exception e) catch (Exception e)
{ {
_reference.AnalyzerLoadFailed?.Invoke(_reference, new AnalyzerLoadFailureEventArgs(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToLoadAnalyzer, e.Message, e)); _reference.AnalyzerLoadFailed?.Invoke(_reference, CreateAnalyzerFailedArgs(e));
return; return;
} }
...@@ -454,7 +469,7 @@ internal void AddExtensions(ImmutableArray<TExtension>.Builder builder, string l ...@@ -454,7 +469,7 @@ internal void AddExtensions(ImmutableArray<TExtension>.Builder builder, string l
} }
catch (Exception e) catch (Exception e)
{ {
_reference.AnalyzerLoadFailed?.Invoke(_reference, new AnalyzerLoadFailureEventArgs(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToLoadAnalyzer, e.Message)); _reference.AnalyzerLoadFailed?.Invoke(_reference, CreateAnalyzerFailedArgs(e));
return; return;
} }
...@@ -501,7 +516,7 @@ private ImmutableArray<TExtension> GetAnalyzersForTypeNames(Assembly analyzerAss ...@@ -501,7 +516,7 @@ private ImmutableArray<TExtension> GetAnalyzersForTypeNames(Assembly analyzerAss
} }
catch (Exception e) catch (Exception e)
{ {
_reference.AnalyzerLoadFailed?.Invoke(_reference, new AnalyzerLoadFailureEventArgs(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer, e.Message, e, typeName)); _reference.AnalyzerLoadFailed?.Invoke(_reference, CreateAnalyzerFailedArgs(e, typeName));
reportedError = true; reportedError = true;
continue; continue;
} }
...@@ -515,7 +530,7 @@ private ImmutableArray<TExtension> GetAnalyzersForTypeNames(Assembly analyzerAss ...@@ -515,7 +530,7 @@ private ImmutableArray<TExtension> GetAnalyzersForTypeNames(Assembly analyzerAss
} }
catch (Exception e) catch (Exception e)
{ {
_reference.AnalyzerLoadFailed?.Invoke(_reference, new AnalyzerLoadFailureEventArgs(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer, e.Message, e, typeName)); _reference.AnalyzerLoadFailed?.Invoke(_reference, CreateAnalyzerFailedArgs(e, typeName));
reportedError = true; reportedError = true;
continue; continue;
} }
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection; using System.Reflection;
namespace Microsoft.CodeAnalysis namespace Microsoft.CodeAnalysis
...@@ -29,11 +30,15 @@ public interface IAnalyzerAssemblyLoader ...@@ -29,11 +30,15 @@ public interface IAnalyzerAssemblyLoader
/// Multiple calls with the same path should return the same /// Multiple calls with the same path should return the same
/// <see cref="Assembly"/> instance. /// <see cref="Assembly"/> instance.
/// </remarks> /// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="fullPath" /> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="fullPath" /> is not a full path.</exception>
Assembly LoadFromPath(string fullPath); Assembly LoadFromPath(string fullPath);
/// <summary> /// <summary>
/// Adds a file to consider when loading an analyzer or its dependencies. /// Adds a file to consider when loading an analyzer or its dependencies.
/// </summary> /// </summary>
/// <exception cref="ArgumentNullException"><paramref name="fullPath" /> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="fullPath" /> is not a full path.</exception>
void AddDependencyLocation(string fullPath); void AddDependencyLocation(string fullPath);
} }
} }
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
using System; using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.IO;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CommandLine namespace Microsoft.CodeAnalysis
{ {
internal static class AssemblyIdentityUtils internal static class AssemblyIdentityUtils
{ {
...@@ -15,7 +15,7 @@ public static AssemblyIdentity TryGetAssemblyIdentity(string filePath) ...@@ -15,7 +15,7 @@ public static AssemblyIdentity TryGetAssemblyIdentity(string filePath)
{ {
try try
{ {
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) using (var stream = PortableShim.FileStream.Create(filePath, PortableShim.FileMode.Open, PortableShim.FileAccess.Read, PortableShim.FileShare.ReadWriteBitwiseOrDelete))
using (var peReader = new PEReader(stream)) using (var peReader = new PEReader(stream))
{ {
var metadataReader = peReader.GetMetadataReader(); var metadataReader = peReader.GetMetadataReader();
......
...@@ -72,12 +72,14 @@ private static bool CheckCore(string baseDirectory, IEnumerable<CommandLineAnaly ...@@ -72,12 +72,14 @@ private static bool CheckCore(string baseDirectory, IEnumerable<CommandLineAnaly
} }
} }
// Second, load all of the assemblies upfront. // Register analyzers and their dependencies upfront,
// so that assembly references can be resolved:
foreach (var resolvedPath in resolvedPaths) foreach (var resolvedPath in resolvedPaths)
{ {
loader.AddDependencyLocation(resolvedPath); loader.AddDependencyLocation(resolvedPath);
} }
// Load all analyzer assemblies:
var loadedAssemblies = new List<Assembly>(); var loadedAssemblies = new List<Assembly>();
foreach (var resolvedPath in resolvedPaths) foreach (var resolvedPath in resolvedPaths)
{ {
......
...@@ -48,8 +48,8 @@ ...@@ -48,8 +48,8 @@
<Reference Include="System.Core" /> <Reference Include="System.Core" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\..\Shared\AbstractAnalyzerAssemblyLoader.cs"> <Compile Include="..\..\Shared\DesktopAnalyzerAssemblyLoader.cs">
<Link>AbstractAnalyzerAssemblyLoader.cs</Link> <Link>DesktopAnalyzerAssemblyLoader.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\Shared\DesktopBuildClient.cs"> <Compile Include="..\..\Shared\DesktopBuildClient.cs">
<Link>DesktopBuildClient.cs</Link> <Link>DesktopBuildClient.cs</Link>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using Roslyn.Utilities;
using static Microsoft.CodeAnalysis.CommandLine.AssemblyIdentityUtils;
namespace Microsoft.CodeAnalysis
{
internal abstract class AbstractAnalyzerAssemblyLoader : IAnalyzerAssemblyLoader
{
private readonly Dictionary<string, Assembly> _pathsToAssemblies = new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, Assembly> _namesToAssemblies = new Dictionary<string, Assembly>();
private readonly List<string> _dependencyPaths = new List<string>();
private readonly object _guard = new object();
private bool _hookedAssemblyResolve;
/// <summary>
/// Implemented by derived types to handle the actual loading of an assembly from
/// a file on disk, and any bookkeeping specific to the derived type.
/// </summary>
protected abstract Assembly LoadCore(string fullPath);
public void AddDependencyLocation(string fullPath)
{
if (fullPath == null)
{
throw new ArgumentNullException(nameof(fullPath));
}
lock (_guard)
{
if (!_dependencyPaths.Contains(fullPath, StringComparer.OrdinalIgnoreCase))
{
_dependencyPaths.Add(fullPath);
}
}
}
public Assembly LoadFromPath(string fullPath)
{
if (fullPath == null)
{
throw new ArgumentNullException(nameof(fullPath));
}
Debug.Assert(PathUtilities.IsAbsolute(fullPath));
lock (_guard)
{
Assembly assembly;
if (_pathsToAssemblies.TryGetValue(fullPath, out assembly))
{
return assembly;
}
assembly = LoadInternal(fullPath);
if (!_hookedAssemblyResolve)
{
_hookedAssemblyResolve = true;
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
return assembly;
}
}
private Assembly LoadInternal(string fullPath)
{
Assembly assembly = LoadCore(fullPath);
string assemblyName = assembly.FullName;
_pathsToAssemblies[fullPath] = assembly;
_namesToAssemblies[assemblyName] = assembly;
return assembly;
}
/// <summary>
/// Handler for <see cref="AppDomain.AssemblyResolve"/>. Delegates to <see cref="AssemblyResolveInternal(ResolveEventArgs)"/>
/// and prevents exceptions from leaking out.
/// </summary>
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
return AssemblyResolveInternal(args);
}
catch
{
return null;
}
}
private Assembly AssemblyResolveInternal(ResolveEventArgs args)
{
string requestedNameWithPolicyApplied = AppDomain.CurrentDomain.ApplyPolicy(args.Name);
lock (_guard)
{
Assembly assembly;
if (_namesToAssemblies.TryGetValue(requestedNameWithPolicyApplied, out assembly))
{
return assembly;
}
AssemblyIdentity requestedAssemblyIdentity;
if (!AssemblyIdentity.TryParseDisplayName(requestedNameWithPolicyApplied, out requestedAssemblyIdentity))
{
return null;
}
foreach (string candidatePath in _dependencyPaths)
{
if (AssemblyAlreadyLoaded(candidatePath) ||
!FileMatchesAssemblyName(candidatePath, requestedAssemblyIdentity.Name))
{
continue;
}
AssemblyIdentity candidateIdentity = TryGetAssemblyIdentity(candidatePath);
if (requestedAssemblyIdentity.Equals(candidateIdentity))
{
return LoadInternal(candidatePath);
}
}
return null;
}
}
private bool AssemblyAlreadyLoaded(string path)
{
return _pathsToAssemblies.ContainsKey(path);
}
private bool FileMatchesAssemblyName(string path, string assemblySimpleName)
{
return Path.GetFileNameWithoutExtension(path).Equals(assemblySimpleName, StringComparison.OrdinalIgnoreCase);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Loader; using System.Runtime.Loader;
using Roslyn.Utilities;
using static Microsoft.CodeAnalysis.CommandLine.AssemblyIdentityUtils;
namespace Microsoft.CodeAnalysis namespace Microsoft.CodeAnalysis
{ {
/// Core CLR compatible wrapper for loading analyzers. internal sealed class CoreClrAnalyzerAssemblyLoader : AnalyzerAssemblyLoader
internal sealed class CoreClrAnalyzerAssemblyLoader : IAnalyzerAssemblyLoader
{ {
private readonly Dictionary<string, Assembly> _pathsToAssemblies = new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase); private readonly AssemblyLoadContext _loadContext;
private readonly Dictionary<string, Assembly> _namesToAssemblies = new Dictionary<string, Assembly>();
private readonly List<string> _dependencyPaths = new List<string>();
private readonly object _guard = new object();
private readonly AssemblyLoadContext _loadContext = AssemblyLoadContext.GetLoadContext(typeof(CoreClrAnalyzerAssemblyLoader).GetTypeInfo().Assembly);
public CoreClrAnalyzerAssemblyLoader() public CoreClrAnalyzerAssemblyLoader()
{ {
_loadContext.Resolving += this.Load; _loadContext = AssemblyLoadContext.GetLoadContext(typeof(CoreClrAnalyzerAssemblyLoader).GetTypeInfo().Assembly);
}
public void AddDependencyLocation(string fullPath)
{
if (fullPath == null)
{
throw new ArgumentNullException(nameof(fullPath));
}
lock (_guard)
{
_dependencyPaths.Add(fullPath);
}
}
public Assembly LoadFromPath(string fullPath)
{
if (fullPath == null)
{
throw new ArgumentNullException(nameof(fullPath));
}
Debug.Assert(PathUtilities.IsAbsolute(fullPath));
lock (_guard)
{
Assembly assembly;
if (_pathsToAssemblies.TryGetValue(fullPath, out assembly))
{
return assembly;
}
return LoadAndCache(fullPath);
}
}
private static readonly string[] s_extensions = new string[] { ".dll", ".exe" };
/// <summary>
/// Searches and loads from the base directory of the current
/// app context
/// </summary>
private Assembly AppContextLoad(AssemblyName assemblyName)
{
var baseDir = AppContext.BaseDirectory;
foreach (var extension in s_extensions)
{
var path = Path.Combine(baseDir, assemblyName.Name + extension);
if (File.Exists(path)) _loadContext.Resolving += (context, name) =>
{
lock (_guard)
{
return LoadAndCache(path);
}
}
}
return null;
}
private Assembly Load(AssemblyLoadContext loadContext, AssemblyName assemblyName)
{
lock (_guard)
{ {
// Try and grab assembly using standard load Debug.Assert(ReferenceEquals(context, _loadContext));
Assembly assembly = AppContextLoad(assemblyName); return Load(name.FullName);
if (assembly != null) };
{
return assembly;
}
string fullName = assemblyName.FullName;
if (_namesToAssemblies.TryGetValue(fullName, out assembly))
{
return assembly;
}
AssemblyIdentity requestedIdentity;
if (!AssemblyIdentity.TryParseDisplayName(fullName, out requestedIdentity))
{
return null;
}
foreach (var candidatePath in _dependencyPaths)
{
if (IsAssemblyAlreadyLoaded(candidatePath) ||
!FileMatchesAssemblyName(candidatePath, requestedIdentity.Name))
{
continue;
}
var candidateIdentity = TryGetAssemblyIdentity(candidatePath);
if (requestedIdentity.Equals(candidateIdentity))
{
return LoadAndCache(candidatePath);
}
}
return null;
}
} }
/// <remarks> protected override Assembly LoadFromPathImpl(string fullPath) => _loadContext.LoadFromAssemblyPath(fullPath);
/// Assumes we have a lock on _guard
/// </remarks>
private Assembly LoadAndCache(string fullPath)
{
var assembly = _loadContext.LoadFromAssemblyPath(fullPath);
var name = assembly.FullName;
_pathsToAssemblies[fullPath] = assembly;
_namesToAssemblies[name] = assembly;
return assembly;
}
private bool IsAssemblyAlreadyLoaded(string path)
{
return _pathsToAssemblies.ContainsKey(path);
}
private bool FileMatchesAssemblyName(string path, string assemblySimpleName)
{
return Path.GetFileNameWithoutExtension(path).Equals(assemblySimpleName, StringComparison.OrdinalIgnoreCase);
}
} }
} }
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection;
using System.Threading;
namespace Microsoft.CodeAnalysis
{
/// <summary>
/// Loads analyzer assemblies from their original locations in the file system.
/// Assemblies will only be loaded from the locations specified when the loader
/// is instantiated.
/// </summary>
/// <remarks>
/// This type is meant to be used in scenarios where it is OK for the analyzer
/// assemblies to be locked on disk for the lifetime of the host; for example,
/// csc.exe and vbc.exe. In scenarios where support for updating or deleting
/// the analyzer on disk is required a different loader should be used.
/// </remarks>
internal class DesktopAnalyzerAssemblyLoader : AnalyzerAssemblyLoader
{
private int _hookedAssemblyResolve;
protected override Assembly LoadFromPathImpl(string fullPath)
{
if (Interlocked.CompareExchange(ref _hookedAssemblyResolve, 0, 1) == 0)
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
return LoadImpl(fullPath);
}
protected virtual Assembly LoadImpl(string fullPath) => Assembly.LoadFrom(fullPath);
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
return Load(AppDomain.CurrentDomain.ApplyPolicy(args.Name));
}
catch
{
return null;
}
}
}
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
namespace Microsoft.CodeAnalysis namespace Microsoft.CodeAnalysis
{ {
internal sealed class ShadowCopyAnalyzerAssemblyLoader : AbstractAnalyzerAssemblyLoader internal sealed class ShadowCopyAnalyzerAssemblyLoader : DesktopAnalyzerAssemblyLoader
{ {
/// <summary> /// <summary>
/// The base directory for shadow copies. Each instance of /// The base directory for shadow copies. Each instance of
...@@ -75,13 +75,7 @@ private void DeleteLeftoverDirectories() ...@@ -75,13 +75,7 @@ private void DeleteLeftoverDirectories()
} }
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", protected override Assembly LoadImpl(string fullPath)
MessageId = "System.Reflection.Assembly.LoadFrom",
Justification = @"We need to call Assembly.LoadFrom in order to load analyzer assemblies.
We can't use Assembly.Load(AssemblyName) because we need to be able to load assemblies outside of the csc/vbc/vbcscompiler/VS binding paths.
We can't use Assembly.Load(byte[]) because VS won't load resource assemblies for those due to an assembly binding optimization.
That leaves Assembly.LoadFrom(string) as the only option that works everywhere.")]
protected override Assembly LoadCore(string fullPath)
{ {
if (_shadowCopyDirectory == null) if (_shadowCopyDirectory == null)
{ {
...@@ -91,7 +85,7 @@ protected override Assembly LoadCore(string fullPath) ...@@ -91,7 +85,7 @@ protected override Assembly LoadCore(string fullPath)
string assemblyDirectory = CreateUniqueDirectoryForAssembly(); string assemblyDirectory = CreateUniqueDirectoryForAssembly();
string shadowCopyPath = CopyFileAndResources(fullPath, assemblyDirectory); string shadowCopyPath = CopyFileAndResources(fullPath, assemblyDirectory);
return Assembly.LoadFrom(shadowCopyPath); return base.LoadImpl(shadowCopyPath);
} }
private string CopyFileAndResources(string fullPath, string assemblyDirectory) private string CopyFileAndResources(string fullPath, string assemblyDirectory)
......
...@@ -21,7 +21,7 @@ public MockCSharpCompiler(string responseFile, string baseDirectory, string[] ar ...@@ -21,7 +21,7 @@ public MockCSharpCompiler(string responseFile, string baseDirectory, string[] ar
} }
public MockCSharpCompiler(string responseFile, string baseDirectory, string[] args, ImmutableArray<DiagnosticAnalyzer> analyzers) public MockCSharpCompiler(string responseFile, string baseDirectory, string[] args, ImmutableArray<DiagnosticAnalyzer> analyzers)
: base(CSharpCommandLineParser.Default, responseFile, args, Path.GetDirectoryName(typeof(CSharpCompiler).Assembly.Location), baseDirectory, RuntimeEnvironment.GetRuntimeDirectory(), Environment.GetEnvironmentVariable("LIB"), new SimpleAnalyzerAssemblyLoader()) : base(CSharpCommandLineParser.Default, responseFile, args, Path.GetDirectoryName(typeof(CSharpCompiler).Assembly.Location), baseDirectory, RuntimeEnvironment.GetRuntimeDirectory(), Environment.GetEnvironmentVariable("LIB"), new DesktopAnalyzerAssemblyLoader())
{ {
_analyzers = analyzers; _analyzers = analyzers;
} }
......
...@@ -10,7 +10,7 @@ Friend Class MockVbi ...@@ -10,7 +10,7 @@ Friend Class MockVbi
Inherits VisualBasicCompiler Inherits VisualBasicCompiler
Public Sub New(responseFile As String, baseDirectory As String, args As String()) Public Sub New(responseFile As String, baseDirectory As String, args As String())
MyBase.New(VisualBasicCommandLineParser.ScriptRunner, responseFile, args, Path.GetDirectoryName(GetType(VisualBasicCompiler).Assembly.Location), baseDirectory, RuntimeEnvironment.GetRuntimeDirectory(), Nothing, New SimpleAnalyzerAssemblyLoader()) MyBase.New(VisualBasicCommandLineParser.ScriptRunner, responseFile, args, Path.GetDirectoryName(GetType(VisualBasicCompiler).Assembly.Location), baseDirectory, RuntimeEnvironment.GetRuntimeDirectory(), Nothing, New DesktopAnalyzerAssemblyLoader())
End Sub End Sub
Protected Overrides Sub CompilerSpecificSqm(sqm As IVsSqmMulti, sqmSession As UInteger) Protected Overrides Sub CompilerSpecificSqm(sqm As IVsSqmMulti, sqmSession As UInteger)
......
...@@ -21,7 +21,7 @@ Friend Class MockVisualBasicCompiler ...@@ -21,7 +21,7 @@ Friend Class MockVisualBasicCompiler
End Sub End Sub
Public Sub New(responseFile As String, baseDirectory As String, args As String(), analyzers As ImmutableArray(Of DiagnosticAnalyzer)) Public Sub New(responseFile As String, baseDirectory As String, args As String(), analyzers As ImmutableArray(Of DiagnosticAnalyzer))
MyBase.New(VisualBasicCommandLineParser.Default, responseFile, args, Path.GetDirectoryName(GetType(VisualBasicCompiler).Assembly.Location), baseDirectory, RuntimeEnvironment.GetRuntimeDirectory(), Environment.GetEnvironmentVariable("LIB"), New SimpleAnalyzerAssemblyLoader()) MyBase.New(VisualBasicCommandLineParser.Default, responseFile, args, Path.GetDirectoryName(GetType(VisualBasicCompiler).Assembly.Location), baseDirectory, RuntimeEnvironment.GetRuntimeDirectory(), Environment.GetEnvironmentVariable("LIB"), New DesktopAnalyzerAssemblyLoader())
_analyzers = analyzers _analyzers = analyzers
End Sub End Sub
......
...@@ -12,7 +12,7 @@ public static int Main(string[] args) ...@@ -12,7 +12,7 @@ public static int Main(string[] args)
=> Main(args, SpecializedCollections.EmptyArray<string>()); => Main(args, SpecializedCollections.EmptyArray<string>());
public static int Main(string[] args, string[] extraArgs) public static int Main(string[] args, string[] extraArgs)
=> DesktopBuildClient.Run(args, extraArgs, RequestLanguage.VisualBasicCompile, Vbc.Run, new SimpleAnalyzerAssemblyLoader()); => DesktopBuildClient.Run(args, extraArgs, RequestLanguage.VisualBasicCompile, Vbc.Run, new DesktopAnalyzerAssemblyLoader());
public static int Run(string[] args, string clientDir, string workingDir, string sdkDir, TextWriter textWriter, IAnalyzerAssemblyLoader analyzerLoader) public static int Run(string[] args, string clientDir, string workingDir, string sdkDir, TextWriter textWriter, IAnalyzerAssemblyLoader analyzerLoader)
=> Vbc.Run(args, new BuildPaths(clientDir: clientDir, workingDir: workingDir, sdkDir: sdkDir), textWriter, analyzerLoader); => Vbc.Run(args, new BuildPaths(clientDir: clientDir, workingDir: workingDir, sdkDir: sdkDir), textWriter, analyzerLoader);
......
...@@ -41,14 +41,11 @@ ...@@ -41,14 +41,11 @@
<Compile Include="..\..\Shared\DesktopBuildClient.cs"> <Compile Include="..\..\Shared\DesktopBuildClient.cs">
<Link>DesktopBuildClient.cs</Link> <Link>DesktopBuildClient.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\Shared\AbstractAnalyzerAssemblyLoader.cs">
<Link>AbstractAnalyzerAssemblyLoader.cs</Link>
</Compile>
<Compile Include="..\..\Shared\ExitingTraceListener.cs"> <Compile Include="..\..\Shared\ExitingTraceListener.cs">
<Link>ExitingTraceListener.cs</Link> <Link>ExitingTraceListener.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\Shared\SimpleAnalyzerAssemblyLoader.cs"> <Compile Include="..\..\Shared\DesktopAnalyzerAssemblyLoader.cs">
<Link>SimpleAnalyzerAssemblyLoader.cs</Link> <Link>DesktopAnalyzerAssemblyLoader.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\Shared\Vbc.cs"> <Compile Include="..\..\Shared\Vbc.cs">
<Link>Vbc.cs</Link> <Link>Vbc.cs</Link>
...@@ -71,4 +68,4 @@ ...@@ -71,4 +68,4 @@
<ImportGroup Label="Targets"> <ImportGroup Label="Targets">
<Import Project="..\..\..\..\build\Targets\VSL.Imports.targets" /> <Import Project="..\..\..\..\build\Targets\VSL.Imports.targets" />
</ImportGroup> </ImportGroup>
</Project> </Project>
\ No newline at end of file
...@@ -91,14 +91,8 @@ ...@@ -91,14 +91,8 @@
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\..\..\Compilers\Core\CommandLine\AssemblyIdentityUtils.cs"> <Compile Include="..\..\..\Compilers\Shared\DesktopAnalyzerAssemblyLoader.cs">
<Link>AssemblyIdentityUtils.cs</Link> <Link>DesktopAnalyzerAssemblyLoader.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Shared\AbstractAnalyzerAssemblyLoader.cs">
<Link>AbstractAnalyzerAssemblyLoader.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Shared\SimpleAnalyzerAssemblyLoader.cs">
<Link>SimpleAnalyzerAssemblyLoader.cs</Link>
</Compile> </Compile>
<Compile Include="AppDomainUtils.cs" /> <Compile Include="AppDomainUtils.cs" />
<Compile Include="CLRHelpers.cs" /> <Compile Include="CLRHelpers.cs" />
......
...@@ -15,7 +15,7 @@ private sealed class AnalyzerAssemblyLoader : IAnalyzerAssemblyLoader ...@@ -15,7 +15,7 @@ private sealed class AnalyzerAssemblyLoader : IAnalyzerAssemblyLoader
public AnalyzerAssemblyLoader() public AnalyzerAssemblyLoader()
{ {
_fallbackLoader = new SimpleAnalyzerAssemblyLoader(); _fallbackLoader = new DesktopAnalyzerAssemblyLoader();
} }
public void AddDependencyLocation(string fullPath) public void AddDependencyLocation(string fullPath)
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.CommandLine;
using Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser.Lists; using Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser.Lists;
using Roslyn.Utilities; using Roslyn.Utilities;
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CommandLine;
using Microsoft.VisualStudio.LanguageServices.Implementation.F1Help; using Microsoft.VisualStudio.LanguageServices.Implementation.F1Help;
using Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser.Lists; using Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser.Lists;
using Microsoft.VisualStudio.LanguageServices.Implementation.Library.VsNavInfo; using Microsoft.VisualStudio.LanguageServices.Implementation.Library.VsNavInfo;
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
using System; using System;
using System.IO; using System.IO;
using Microsoft.CodeAnalysis;
namespace Roslyn.Utilities namespace Roslyn.Utilities
{ {
...@@ -89,19 +88,6 @@ public static string GetRelativePath(string baseDirectory, string fullPath) ...@@ -89,19 +88,6 @@ public static string GetRelativePath(string baseDirectory, string fullPath)
return relativePath; return relativePath;
} }
internal static void RequireAbsolutePath(string path, string argumentName)
{
if (path == null)
{
throw new ArgumentNullException(argumentName);
}
if (!PathUtilities.IsAbsolute(path))
{
throw new ArgumentException(WorkspacesResources.AbsolutePathExpected, argumentName);
}
}
public static bool PathsEqual(string path1, string path2) public static bool PathsEqual(string path1, string path2)
{ {
return string.Compare(path1, path2, StringComparison.OrdinalIgnoreCase) == 0; return string.Compare(path1, path2, StringComparison.OrdinalIgnoreCase) == 0;
......
...@@ -29,7 +29,7 @@ public class FileTextLoader : TextLoader ...@@ -29,7 +29,7 @@ public class FileTextLoader : TextLoader
/// <exception cref="ArgumentException"><paramref name="path"/> is not an absolute path.</exception> /// <exception cref="ArgumentException"><paramref name="path"/> is not an absolute path.</exception>
public FileTextLoader(string path, Encoding defaultEncoding) public FileTextLoader(string path, Encoding defaultEncoding)
{ {
FilePathUtilities.RequireAbsolutePath(path, "path"); CompilerPathUtilities.RequireAbsolutePath(path, "path");
_path = path; _path = path;
_defaultEncoding = defaultEncoding; _defaultEncoding = defaultEncoding;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Composition; using System.Composition;
using System.Reflection;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Host namespace Microsoft.CodeAnalysis.Host
...@@ -11,7 +8,7 @@ namespace Microsoft.CodeAnalysis.Host ...@@ -11,7 +8,7 @@ namespace Microsoft.CodeAnalysis.Host
[ExportWorkspaceService(typeof(IAnalyzerService), ServiceLayer.Default), Shared] [ExportWorkspaceService(typeof(IAnalyzerService), ServiceLayer.Default), Shared]
internal sealed class SimpleAnalyzerAssemblyLoaderService : IAnalyzerService internal sealed class SimpleAnalyzerAssemblyLoaderService : IAnalyzerService
{ {
private readonly SimpleAnalyzerAssemblyLoader _loader = new SimpleAnalyzerAssemblyLoader(); private readonly DesktopAnalyzerAssemblyLoader _loader = new DesktopAnalyzerAssemblyLoader();
public IAnalyzerAssemblyLoader GetLoader() public IAnalyzerAssemblyLoader GetLoader()
{ {
......
...@@ -56,18 +56,12 @@ ...@@ -56,18 +56,12 @@
<Compile Include="..\..\..\Compilers\Core\Portable\FileKey.cs"> <Compile Include="..\..\..\Compilers\Core\Portable\FileKey.cs">
<Link>InternalUtilities\FileKey.cs</Link> <Link>InternalUtilities\FileKey.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\..\Compilers\Shared\AbstractAnalyzerAssemblyLoader.cs"> <Compile Include="..\..\..\Compilers\Shared\DesktopAnalyzerAssemblyLoader.cs">
<Link>InternalUtilities\AbstractAnalyzerAssemblyLoader.cs</Link> <Link>InternalUtilities\DesktopAnalyzerAssemblyLoader.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Core\CommandLine\AssemblyIdentityUtils.cs">
<Link>AssemblyIdentityUtils.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\..\Compilers\Shared\GlobalAssemblyCacheHelpers\GlobalAssemblyCacheLocation.cs"> <Compile Include="..\..\..\Compilers\Shared\GlobalAssemblyCacheHelpers\GlobalAssemblyCacheLocation.cs">
<Link>InternalUtilities\GlobalAssemblyCache.cs</Link> <Link>InternalUtilities\GlobalAssemblyCache.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\..\Compilers\Shared\SimpleAnalyzerAssemblyLoader.cs">
<Link>InternalUtilities\SimpleAnalyzerAssemblyLoader.cs</Link>
</Compile>
<Compile Include="Options\ExportOptionAttribute.cs" /> <Compile Include="Options\ExportOptionAttribute.cs" />
<Compile Include="InternalUtilities\FilePathUtilities.cs" /> <Compile Include="InternalUtilities\FilePathUtilities.cs" />
<Compile Include="Log\EtwLogger.cs" /> <Compile Include="Log\EtwLogger.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis;
namespace Roslyn.Utilities
{
internal static class CompilerPathUtilities
{
internal static void RequireAbsolutePath(string path, string argumentName)
{
if (path == null)
{
throw new ArgumentNullException(argumentName);
}
if (!PathUtilities.IsAbsolute(path))
{
throw new ArgumentException(WorkspacesResources.AbsolutePathExpected, argumentName);
}
}
}
}
...@@ -51,9 +51,15 @@ ...@@ -51,9 +51,15 @@
<Compile Include="..\..\..\Compilers\Core\Portable\CorLightup.cs"> <Compile Include="..\..\..\Compilers\Core\Portable\CorLightup.cs">
<Link>InternalUtilities\CorLightup.cs</Link> <Link>InternalUtilities\CorLightup.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\..\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerAssemblyLoader.cs">
<Link>InternalUtilities\AnalyzerAssemblyLoader.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Core\Portable\FileSystem\RelativePathResolver.cs"> <Compile Include="..\..\..\Compilers\Core\Portable\FileSystem\RelativePathResolver.cs">
<Link>InternalUtilities\RelativePathResolver.cs</Link> <Link>InternalUtilities\RelativePathResolver.cs</Link>
</Compile> </Compile>
<Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\AssemblyIdentityUtils.cs">
<Link>InternalUtilities\AssemblyIdentityUtils.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\BitArithmeticUtilities.cs"> <Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\BitArithmeticUtilities.cs">
<Link>InternalUtilities\BitArithmeticUtilities.cs</Link> <Link>InternalUtilities\BitArithmeticUtilities.cs</Link>
</Compile> </Compile>
...@@ -491,6 +497,7 @@ ...@@ -491,6 +497,7 @@
<Compile Include="Utilities\BKTree.Edge.cs" /> <Compile Include="Utilities\BKTree.Edge.cs" />
<Compile Include="Utilities\BKTree.Node.cs" /> <Compile Include="Utilities\BKTree.Node.cs" />
<Compile Include="Utilities\BKTree.Serialization.cs" /> <Compile Include="Utilities\BKTree.Serialization.cs" />
<Compile Include="Utilities\CompilerUtilities\CompilerPathUtilities.cs" />
<Compile Include="Utilities\ForegroundThreadDataKind.cs" /> <Compile Include="Utilities\ForegroundThreadDataKind.cs" />
<Compile Include="Utilities\IReadOnlyDictionaryExtensions.cs" /> <Compile Include="Utilities\IReadOnlyDictionaryExtensions.cs" />
<Compile Include="Utilities\IReadOnlyListExtensions.cs" /> <Compile Include="Utilities\IReadOnlyListExtensions.cs" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册