diff --git a/src/Compilers/Shared/GlobalAssemblyCacheHelpers/ClrGlobalAssemblyCache.cs b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/ClrGlobalAssemblyCache.cs
new file mode 100644
index 0000000000000..49eebbe538431
--- /dev/null
+++ b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/ClrGlobalAssemblyCache.cs
@@ -0,0 +1,251 @@
+// 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.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis
+{
+ using ASM_CACHE = GlobalAssemblyCacheLocation.ASM_CACHE;
+
+ ///
+ /// Provides APIs to enumerate and look up assemblies stored in the Global Assembly Cache.
+ ///
+ internal sealed class ClrGlobalAssemblyCache : GlobalAssemblyCache
+ {
+ #region Interop
+
+ private const int MAX_PATH = 260;
+
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("21b8916c-f28e-11d2-a473-00c04f8ef448")]
+ private interface IAssemblyEnum
+ {
+ [PreserveSig]
+ int GetNextAssembly(out FusionAssemblyIdentity.IApplicationContext ppAppCtx, out FusionAssemblyIdentity.IAssemblyName ppName, uint dwFlags);
+
+ [PreserveSig]
+ int Reset();
+
+ [PreserveSig]
+ int Clone(out IAssemblyEnum ppEnum);
+ }
+
+ [ComImport, Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IAssemblyCache
+ {
+ void UninstallAssembly();
+
+ void QueryAssemblyInfo(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, ref ASSEMBLY_INFO pAsmInfo);
+
+ void CreateAssemblyCacheItem();
+ void CreateAssemblyScavenger();
+ void InstallAssembly();
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private unsafe struct ASSEMBLY_INFO
+ {
+ public uint cbAssemblyInfo;
+ public readonly uint dwAssemblyFlags;
+ public readonly ulong uliAssemblySizeInKB;
+ public char* pszCurrentAssemblyPathBuf;
+ public uint cchBuf;
+ }
+
+ [DllImport("clr", PreserveSig = true)]
+ private static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum, FusionAssemblyIdentity.IApplicationContext pAppCtx, FusionAssemblyIdentity.IAssemblyName pName, ASM_CACHE dwFlags, IntPtr pvReserved);
+
+ [DllImport("clr", PreserveSig = false)]
+ private static extern void CreateAssemblyCache(out IAssemblyCache ppAsmCache, uint dwReserved);
+
+ #endregion
+
+ ///
+ /// Enumerates assemblies in the GAC returning those that match given partial name and
+ /// architecture.
+ ///
+ /// Optional partial name.
+ /// Optional architecture filter.
+ public override IEnumerable GetAssemblyIdentities(AssemblyName partialName, ImmutableArray architectureFilter = default(ImmutableArray))
+ {
+ return GetAssemblyIdentities(FusionAssemblyIdentity.ToAssemblyNameObject(partialName), architectureFilter);
+ }
+
+ ///
+ /// Enumerates assemblies in the GAC returning those that match given partial name and
+ /// architecture.
+ ///
+ /// The optional partial name.
+ /// The optional architecture filter.
+ public override IEnumerable GetAssemblyIdentities(string partialName = null, ImmutableArray architectureFilter = default(ImmutableArray))
+ {
+ FusionAssemblyIdentity.IAssemblyName nameObj;
+ if (partialName != null)
+ {
+ nameObj = FusionAssemblyIdentity.ToAssemblyNameObject(partialName);
+ if (nameObj == null)
+ {
+ return SpecializedCollections.EmptyEnumerable();
+ }
+ }
+ else
+ {
+ nameObj = null;
+ }
+
+ return GetAssemblyIdentities(nameObj, architectureFilter);
+ }
+
+ ///
+ /// Enumerates assemblies in the GAC returning their simple names.
+ ///
+ /// Optional architecture filter.
+ /// Unique simple names of GAC assemblies.
+ public override IEnumerable GetAssemblySimpleNames(ImmutableArray architectureFilter = default(ImmutableArray))
+ {
+ var q = from nameObject in GetAssemblyObjects(partialNameFilter: null, architectureFilter: architectureFilter)
+ select FusionAssemblyIdentity.GetName(nameObject);
+ return q.Distinct();
+ }
+
+ private static IEnumerable GetAssemblyIdentities(
+ FusionAssemblyIdentity.IAssemblyName partialName,
+ ImmutableArray architectureFilter)
+ {
+ return from nameObject in GetAssemblyObjects(partialName, architectureFilter)
+ select FusionAssemblyIdentity.ToAssemblyIdentity(nameObject);
+ }
+
+ private const int S_OK = 0;
+ private const int S_FALSE = 1;
+
+ // Internal for testing.
+ internal static IEnumerable GetAssemblyObjects(
+ FusionAssemblyIdentity.IAssemblyName partialNameFilter,
+ ImmutableArray architectureFilter)
+ {
+ IAssemblyEnum enumerator;
+ FusionAssemblyIdentity.IApplicationContext applicationContext = null;
+
+ int hr = CreateAssemblyEnum(out enumerator, applicationContext, partialNameFilter, ASM_CACHE.GAC, IntPtr.Zero);
+ if (hr == S_FALSE)
+ {
+ // no assembly found
+ yield break;
+ }
+ else if (hr != S_OK)
+ {
+ Exception e = Marshal.GetExceptionForHR(hr);
+ if (e is FileNotFoundException)
+ {
+ // invalid assembly name:
+ yield break;
+ }
+ else if (e != null)
+ {
+ throw e;
+ }
+ else
+ {
+ // for some reason it might happen that CreateAssemblyEnum returns non-zero HR that doesn't correspond to any exception:
+#if SCRIPTING
+ throw new ArgumentException(Microsoft.CodeAnalysis.Scripting.ScriptingResources.InvalidAssemblyName);
+#else
+ throw new ArgumentException(Microsoft.CodeAnalysis.WorkspaceDesktopResources.InvalidAssemblyName);
+#endif
+ }
+ }
+
+ while (true)
+ {
+ FusionAssemblyIdentity.IAssemblyName nameObject;
+
+ hr = enumerator.GetNextAssembly(out applicationContext, out nameObject, 0);
+ if (hr != 0)
+ {
+ if (hr < 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ break;
+ }
+
+ if (!architectureFilter.IsDefault)
+ {
+ var assemblyArchitecture = FusionAssemblyIdentity.GetProcessorArchitecture(nameObject);
+ if (!architectureFilter.Contains(assemblyArchitecture))
+ {
+ continue;
+ }
+ }
+
+ yield return nameObject;
+ }
+ }
+
+ public override AssemblyIdentity ResolvePartialName(
+ string displayName,
+ out string location,
+ ImmutableArray architectureFilter,
+ CultureInfo preferredCulture)
+ {
+ if (displayName == null)
+ {
+ throw new ArgumentNullException(nameof(displayName));
+ }
+
+ location = null;
+ FusionAssemblyIdentity.IAssemblyName nameObject = FusionAssemblyIdentity.ToAssemblyNameObject(displayName);
+ if (nameObject == null)
+ {
+ return null;
+ }
+
+ var candidates = GetAssemblyObjects(nameObject, architectureFilter);
+ string cultureName = (preferredCulture != null && !preferredCulture.IsNeutralCulture) ? preferredCulture.Name : null;
+
+ var bestMatch = FusionAssemblyIdentity.GetBestMatch(candidates, cultureName);
+ if (bestMatch == null)
+ {
+ return null;
+ }
+
+ location = GetAssemblyLocation(bestMatch);
+ return FusionAssemblyIdentity.ToAssemblyIdentity(bestMatch);
+ }
+
+ internal static unsafe string GetAssemblyLocation(FusionAssemblyIdentity.IAssemblyName nameObject)
+ {
+ // NAME | VERSION | CULTURE | PUBLIC_KEY_TOKEN | RETARGET | PROCESSORARCHITECTURE
+ string fullName = FusionAssemblyIdentity.GetDisplayName(nameObject, FusionAssemblyIdentity.ASM_DISPLAYF.FULL);
+
+ fixed (char* p = new char[MAX_PATH])
+ {
+ ASSEMBLY_INFO info = new ASSEMBLY_INFO
+ {
+ cbAssemblyInfo = (uint)Marshal.SizeOf(),
+ pszCurrentAssemblyPathBuf = p,
+ cchBuf = MAX_PATH
+ };
+
+ IAssemblyCache assemblyCacheObject;
+ CreateAssemblyCache(out assemblyCacheObject, 0);
+ assemblyCacheObject.QueryAssemblyInfo(0, fullName, ref info);
+ Debug.Assert(info.pszCurrentAssemblyPathBuf != null);
+ Debug.Assert(info.pszCurrentAssemblyPathBuf[info.cchBuf - 1] == '\0');
+
+ var result = Marshal.PtrToStringUni((IntPtr)info.pszCurrentAssemblyPathBuf, (int)info.cchBuf - 1);
+ Debug.Assert(result.IndexOf('\0') == -1);
+ return result;
+ }
+ }
+ }
+}
diff --git a/src/Compilers/Shared/GlobalAssemblyCacheHelpers/GacFileResolver.cs b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/GacFileResolver.cs
index 1624aedcfa4e1..27010ecac5315 100644
--- a/src/Compilers/Shared/GlobalAssemblyCacheHelpers/GacFileResolver.cs
+++ b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/GacFileResolver.cs
@@ -61,7 +61,7 @@ public GacFileResolver(
public string Resolve(string assemblyName)
{
string path;
- GlobalAssemblyCache.ResolvePartialName(assemblyName, out path, Architectures, this.PreferredCulture);
+ GlobalAssemblyCache.Instance.ResolvePartialName(assemblyName, out path, Architectures, this.PreferredCulture);
return File.Exists(path) ? path : null;
}
diff --git a/src/Compilers/Shared/GlobalAssemblyCacheHelpers/GlobalAssemblyCache.cs b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/GlobalAssemblyCache.cs
index 8654849aaf336..2f9a44adff86e 100644
--- a/src/Compilers/Shared/GlobalAssemblyCacheHelpers/GlobalAssemblyCache.cs
+++ b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/GlobalAssemblyCache.cs
@@ -3,87 +3,44 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
-using System.Diagnostics;
using System.Globalization;
-using System.IO;
-using System.Linq;
using System.Reflection;
-using System.Runtime.InteropServices;
-using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
- using static GlobalAssemblyCacheLocation;
-
///
/// Provides APIs to enumerate and look up assemblies stored in the Global Assembly Cache.
///
- internal static class GlobalAssemblyCache
+ internal abstract class GlobalAssemblyCache
{
+ internal static readonly GlobalAssemblyCache Instance = CreateInstance();
+
+ private static GlobalAssemblyCache CreateInstance()
+ {
+ if (Type.GetType("Mono.Runtime") != null)
+ {
+ return new MonoGlobalAssemblyCache();
+ }
+ else
+ {
+ return new ClrGlobalAssemblyCache();
+ }
+ }
+
///
- /// Represents the current Processor architecture
+ /// Represents the current Processor architecture.
///
public static readonly ImmutableArray CurrentArchitectures = (IntPtr.Size == 4)
? ImmutableArray.Create(ProcessorArchitecture.None, ProcessorArchitecture.MSIL, ProcessorArchitecture.X86)
: ImmutableArray.Create(ProcessorArchitecture.None, ProcessorArchitecture.MSIL, ProcessorArchitecture.Amd64);
- #region Interop
-
- private const int MAX_PATH = 260;
-
- [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("21b8916c-f28e-11d2-a473-00c04f8ef448")]
- private interface IAssemblyEnum
- {
- [PreserveSig]
- int GetNextAssembly(out FusionAssemblyIdentity.IApplicationContext ppAppCtx, out FusionAssemblyIdentity.IAssemblyName ppName, uint dwFlags);
-
- [PreserveSig]
- int Reset();
-
- [PreserveSig]
- int Clone(out IAssemblyEnum ppEnum);
- }
-
- [ComImport, Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- private interface IAssemblyCache
- {
- void UninstallAssembly();
-
- void QueryAssemblyInfo(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, ref ASSEMBLY_INFO pAsmInfo);
-
- void CreateAssemblyCacheItem();
- void CreateAssemblyScavenger();
- void InstallAssembly();
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private unsafe struct ASSEMBLY_INFO
- {
- public uint cbAssemblyInfo;
- public readonly uint dwAssemblyFlags;
- public readonly ulong uliAssemblySizeInKB;
- public char* pszCurrentAssemblyPathBuf;
- public uint cchBuf;
- }
-
- [DllImport("clr", PreserveSig = true)]
- private static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum, FusionAssemblyIdentity.IApplicationContext pAppCtx, FusionAssemblyIdentity.IAssemblyName pName, ASM_CACHE dwFlags, IntPtr pvReserved);
-
- [DllImport("clr", PreserveSig = false)]
- private static extern void CreateAssemblyCache(out IAssemblyCache ppAsmCache, uint dwReserved);
-
- #endregion
-
///
/// Enumerates assemblies in the GAC returning those that match given partial name and
/// architecture.
///
/// Optional partial name.
/// Optional architecture filter.
- public static IEnumerable GetAssemblyIdentities(AssemblyName partialName, ImmutableArray architectureFilter = default(ImmutableArray))
- {
- return GetAssemblyIdentities(FusionAssemblyIdentity.ToAssemblyNameObject(partialName), architectureFilter);
- }
+ public abstract IEnumerable GetAssemblyIdentities(AssemblyName partialName, ImmutableArray architectureFilter = default(ImmutableArray));
///
/// Enumerates assemblies in the GAC returning those that match given partial name and
@@ -91,112 +48,14 @@ private unsafe struct ASSEMBLY_INFO
///
/// The optional partial name.
/// The optional architecture filter.
- public static IEnumerable GetAssemblyIdentities(string partialName = null, ImmutableArray architectureFilter = default(ImmutableArray))
- {
- FusionAssemblyIdentity.IAssemblyName nameObj;
- if (partialName != null)
- {
- nameObj = FusionAssemblyIdentity.ToAssemblyNameObject(partialName);
- if (nameObj == null)
- {
- return SpecializedCollections.EmptyEnumerable();
- }
- }
- else
- {
- nameObj = null;
- }
-
- return GetAssemblyIdentities(nameObj, architectureFilter);
- }
+ public abstract IEnumerable GetAssemblyIdentities(string partialName = null, ImmutableArray architectureFilter = default(ImmutableArray));
///
/// Enumerates assemblies in the GAC returning their simple names.
///
/// Optional architecture filter.
/// Unique simple names of GAC assemblies.
- public static IEnumerable GetAssemblySimpleNames(ImmutableArray architectureFilter = default(ImmutableArray))
- {
- var q = from nameObject in GetAssemblyObjects(partialNameFilter: null, architectureFilter: architectureFilter)
- select FusionAssemblyIdentity.GetName(nameObject);
- return q.Distinct();
- }
-
- private static IEnumerable GetAssemblyIdentities(
- FusionAssemblyIdentity.IAssemblyName partialName,
- ImmutableArray architectureFilter)
- {
- return from nameObject in GetAssemblyObjects(partialName, architectureFilter)
- select FusionAssemblyIdentity.ToAssemblyIdentity(nameObject);
- }
-
- private const int S_OK = 0;
- private const int S_FALSE = 1;
-
- // Internal for testing.
- internal static IEnumerable GetAssemblyObjects(
- FusionAssemblyIdentity.IAssemblyName partialNameFilter,
- ImmutableArray architectureFilter)
- {
- IAssemblyEnum enumerator;
- FusionAssemblyIdentity.IApplicationContext applicationContext = null;
-
- int hr = CreateAssemblyEnum(out enumerator, applicationContext, partialNameFilter, ASM_CACHE.GAC, IntPtr.Zero);
- if (hr == S_FALSE)
- {
- // no assembly found
- yield break;
- }
- else if (hr != S_OK)
- {
- Exception e = Marshal.GetExceptionForHR(hr);
- if (e is FileNotFoundException)
- {
- // invalid assembly name:
- yield break;
- }
- else if (e != null)
- {
- throw e;
- }
- else
- {
- // for some reason it might happen that CreateAssemblyEnum returns non-zero HR that doesn't correspond to any exception:
-#if SCRIPTING
- throw new ArgumentException(Microsoft.CodeAnalysis.Scripting.ScriptingResources.InvalidAssemblyName);
-#else
- throw new ArgumentException(Microsoft.CodeAnalysis.WorkspaceDesktopResources.InvalidAssemblyName);
-#endif
- }
- }
-
- while (true)
- {
- FusionAssemblyIdentity.IAssemblyName nameObject;
-
- hr = enumerator.GetNextAssembly(out applicationContext, out nameObject, 0);
- if (hr != 0)
- {
- if (hr < 0)
- {
- Marshal.ThrowExceptionForHR(hr);
- }
-
- break;
- }
-
- if (!architectureFilter.IsDefault)
- {
- var assemblyArchitecture = FusionAssemblyIdentity.GetProcessorArchitecture(nameObject);
- if (!architectureFilter.Contains(assemblyArchitecture))
- {
- continue;
- }
- }
-
- yield return nameObject;
- }
- }
+ public abstract IEnumerable GetAssemblySimpleNames(ImmutableArray architectureFilter = default(ImmutableArray));
///
/// Looks up specified partial assembly name in the GAC and returns the best matching .
@@ -206,13 +65,13 @@ private static IEnumerable GetAssemblyIdentities(
/// The optional preferred culture information
/// An assembly identity or null, if can't be resolved.
/// is null.
- public static AssemblyIdentity ResolvePartialName(
+ public AssemblyIdentity ResolvePartialName(
string displayName,
ImmutableArray architectureFilter = default(ImmutableArray),
CultureInfo preferredCulture = null)
{
string location;
- return ResolvePartialName(displayName, architectureFilter, preferredCulture, out location, resolveLocation: false);
+ return ResolvePartialName(displayName, out location, architectureFilter, preferredCulture);
}
///
@@ -224,75 +83,10 @@ public static AssemblyIdentity ResolvePartialName(
/// The optional preferred culture information
/// An assembly identity or null, if can't be resolved.
/// is null.
- public static AssemblyIdentity ResolvePartialName(
+ public abstract AssemblyIdentity ResolvePartialName(
string displayName,
out string location,
ImmutableArray architectureFilter = default(ImmutableArray),
- CultureInfo preferredCulture = null)
- {
- return ResolvePartialName(displayName, architectureFilter, preferredCulture, out location, resolveLocation: true);
- }
-
- private static AssemblyIdentity ResolvePartialName(
- string displayName,
- ImmutableArray architectureFilter,
- CultureInfo preferredCulture,
- out string location,
- bool resolveLocation)
- {
- if (displayName == null)
- {
- throw new ArgumentNullException(nameof(displayName));
- }
-
- location = null;
- FusionAssemblyIdentity.IAssemblyName nameObject = FusionAssemblyIdentity.ToAssemblyNameObject(displayName);
- if (nameObject == null)
- {
- return null;
- }
-
- var candidates = GetAssemblyObjects(nameObject, architectureFilter);
- string cultureName = (preferredCulture != null && !preferredCulture.IsNeutralCulture) ? preferredCulture.Name : null;
-
- var bestMatch = FusionAssemblyIdentity.GetBestMatch(candidates, cultureName);
- if (bestMatch == null)
- {
- return null;
- }
-
- if (resolveLocation)
- {
- location = GetAssemblyLocation(bestMatch);
- }
-
- return FusionAssemblyIdentity.ToAssemblyIdentity(bestMatch);
- }
-
- internal static unsafe string GetAssemblyLocation(FusionAssemblyIdentity.IAssemblyName nameObject)
- {
- // NAME | VERSION | CULTURE | PUBLIC_KEY_TOKEN | RETARGET | PROCESSORARCHITECTURE
- string fullName = FusionAssemblyIdentity.GetDisplayName(nameObject, FusionAssemblyIdentity.ASM_DISPLAYF.FULL);
-
- fixed (char* p = new char[MAX_PATH])
- {
- ASSEMBLY_INFO info = new ASSEMBLY_INFO
- {
- cbAssemblyInfo = (uint)Marshal.SizeOf(),
- pszCurrentAssemblyPathBuf = p,
- cchBuf = MAX_PATH
- };
-
- IAssemblyCache assemblyCacheObject;
- CreateAssemblyCache(out assemblyCacheObject, 0);
- assemblyCacheObject.QueryAssemblyInfo(0, fullName, ref info);
- Debug.Assert(info.pszCurrentAssemblyPathBuf != null);
- Debug.Assert(info.pszCurrentAssemblyPathBuf[info.cchBuf - 1] == '\0');
-
- var result = Marshal.PtrToStringUni((IntPtr)info.pszCurrentAssemblyPathBuf, (int)info.cchBuf - 1);
- Debug.Assert(result.IndexOf('\0') == -1);
- return result;
- }
- }
+ CultureInfo preferredCulture = null);
}
}
diff --git a/src/Compilers/Shared/GlobalAssemblyCacheHelpers/MonoGlobalAssemblyCache.cs b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/MonoGlobalAssemblyCache.cs
new file mode 100644
index 0000000000000..33cf016c7ddff
--- /dev/null
+++ b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/MonoGlobalAssemblyCache.cs
@@ -0,0 +1,226 @@
+// 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.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis
+{
+ ///
+ /// Provides APIs to enumerate and look up assemblies stored in the Global Assembly Cache.
+ ///
+ internal sealed class MonoGlobalAssemblyCache : GlobalAssemblyCache
+ {
+ public static readonly ImmutableArray RootLocations;
+
+ static MonoGlobalAssemblyCache()
+ {
+ RootLocations = ImmutableArray.Create(GetMonoCachePath());
+ }
+
+ private static string GetMonoCachePath()
+ {
+ string file = CorLightup.Desktop.GetAssemblyLocation(typeof(Uri).GetTypeInfo().Assembly);
+ return Directory.GetParent(Path.GetDirectoryName(file)).Parent.FullName;
+ }
+
+ private static IEnumerable GetCorlibPaths(Version version)
+ {
+ string corlibPath = CorLightup.Desktop.GetAssemblyLocation(typeof(object).GetTypeInfo().Assembly);
+ var corlibParentDir = Directory.GetParent(corlibPath).Parent;
+
+ var corlibPaths = new List();
+
+ foreach (var corlibDir in corlibParentDir.GetDirectories())
+ {
+ var path = Path.Combine(corlibDir.FullName, "mscorlib.dll");
+ if (!File.Exists(path))
+ {
+ continue;
+ }
+
+ var name = new AssemblyName(path);
+ if (version != null && name.Version != version)
+ {
+ continue;
+ }
+
+ corlibPaths.Add(path);
+ }
+
+ return corlibPaths;
+ }
+
+ private static IEnumerable GetGacAssemblyPaths(string gacPath, string name, Version version, string publicKeyToken)
+ {
+ if (version != null && publicKeyToken != null)
+ {
+ yield return Path.Combine(gacPath, name, version + "__" + publicKeyToken, name + ".dll");
+ yield break;
+ }
+
+ var gacAssemblyRootDir = new DirectoryInfo(Path.Combine(gacPath, name));
+ if (!gacAssemblyRootDir.Exists)
+ {
+ yield break;
+ }
+
+ foreach (var assemblyDir in gacAssemblyRootDir.GetDirectories())
+ {
+ if (version != null && !assemblyDir.Name.StartsWith(version.ToString(), StringComparison.Ordinal))
+ {
+ continue;
+ }
+
+ if (publicKeyToken != null && !assemblyDir.Name.EndsWith(publicKeyToken, StringComparison.Ordinal))
+ {
+ continue;
+ }
+
+ var assemblyPath = Path.Combine(assemblyDir.ToString(), name + ".dll");
+ if (File.Exists(assemblyPath))
+ {
+ yield return assemblyPath;
+ }
+ }
+ }
+
+ private static IEnumerable> GetAssemblyIdentitiesAndPaths(AssemblyName name, ImmutableArray architectureFilter)
+ {
+ if (name == null)
+ {
+ return GetAssemblyIdentitiesAndPaths(null, null, null, architectureFilter);
+ }
+
+ string publicKeyToken = null;
+ if (name.GetPublicKeyToken() != null)
+ {
+ var sb = new StringBuilder();
+ foreach (var b in name.GetPublicKeyToken())
+ {
+ sb.AppendFormat("{0:x2}", b);
+ }
+
+ publicKeyToken = sb.ToString();
+ }
+
+ return GetAssemblyIdentitiesAndPaths(name.Name, name.Version, publicKeyToken, architectureFilter);
+ }
+
+ private static IEnumerable> GetAssemblyIdentitiesAndPaths(string name, Version version, string publicKeyToken, ImmutableArray architectureFilter)
+ {
+ foreach (string gacPath in RootLocations)
+ {
+ var assemblyPaths = (name == "mscorlib") ?
+ GetCorlibPaths(version) :
+ GetGacAssemblyPaths(gacPath, name, version, publicKeyToken);
+
+ foreach (var assemblyPath in assemblyPaths)
+ {
+ if (!File.Exists(assemblyPath))
+ {
+ continue;
+ }
+
+ var gacAssemblyName = new AssemblyName(assemblyPath);
+
+ if (gacAssemblyName.ProcessorArchitecture != ProcessorArchitecture.None &&
+ architectureFilter != default(ImmutableArray) &&
+ architectureFilter.Length > 0 &&
+ !architectureFilter.Contains(gacAssemblyName.ProcessorArchitecture))
+ {
+ continue;
+ }
+
+ var assemblyIdentity = new AssemblyIdentity(
+ gacAssemblyName.Name,
+ gacAssemblyName.Version,
+ gacAssemblyName.CultureName,
+ ImmutableArray.Create(gacAssemblyName.GetPublicKeyToken()));
+
+ yield return new Tuple(assemblyIdentity, assemblyPath);
+ }
+ }
+ }
+
+ public override IEnumerable GetAssemblyIdentities(AssemblyName partialName, ImmutableArray architectureFilter = default(ImmutableArray))
+ {
+ return GetAssemblyIdentitiesAndPaths(partialName, architectureFilter).Select(identityAndPath => identityAndPath.Item1);
+ }
+
+ public override IEnumerable GetAssemblyIdentities(string partialName = null, ImmutableArray architectureFilter = default(ImmutableArray))
+ {
+ AssemblyName name;
+ try
+ {
+ name = (partialName == null) ? null : new AssemblyName(partialName);
+ }
+ catch
+ {
+ return SpecializedCollections.EmptyEnumerable();
+ }
+
+ return GetAssemblyIdentities(name, architectureFilter);
+ }
+
+ public override IEnumerable GetAssemblySimpleNames(ImmutableArray architectureFilter = default(ImmutableArray))
+ {
+ return GetAssemblyIdentitiesAndPaths(name: null, version: null, publicKeyToken: null, architectureFilter: architectureFilter).
+ Select(identityAndPath => identityAndPath.Item1.Name).Distinct();
+ }
+
+ public override AssemblyIdentity ResolvePartialName(
+ string displayName,
+ out string location,
+ ImmutableArray architectureFilter,
+ CultureInfo preferredCulture)
+ {
+ if (displayName == null)
+ {
+ throw new ArgumentNullException(nameof(displayName));
+ }
+
+ string cultureName = (preferredCulture != null && !preferredCulture.IsNeutralCulture) ? preferredCulture.Name : null;
+
+ var assemblyName = new AssemblyName(displayName);
+ AssemblyIdentity assemblyIdentity = null;
+
+ location = null;
+ bool isBestMatch = false;
+
+ foreach (var identityAndPath in GetAssemblyIdentitiesAndPaths(assemblyName, architectureFilter))
+ {
+ var assemblyPath = identityAndPath.Item2;
+
+ if (!File.Exists(assemblyPath))
+ {
+ continue;
+ }
+
+ var gacAssemblyName = new AssemblyName(assemblyPath);
+
+ isBestMatch = cultureName == null || gacAssemblyName.CultureName == cultureName;
+ bool isBetterMatch = location == null || isBestMatch;
+
+ if (isBetterMatch)
+ {
+ location = assemblyPath;
+ assemblyIdentity = identityAndPath.Item1;
+ }
+
+ if (isBestMatch)
+ {
+ break;
+ }
+ }
+
+ return assemblyIdentity;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Interactive/EditorFeatures/Core/Completion/GlobalAssemblyCacheCompletionHelper.cs b/src/Interactive/EditorFeatures/Core/Completion/GlobalAssemblyCacheCompletionHelper.cs
index 4f4afae311edc..f6bc1362a4791 100644
--- a/src/Interactive/EditorFeatures/Core/Completion/GlobalAssemblyCacheCompletionHelper.cs
+++ b/src/Interactive/EditorFeatures/Core/Completion/GlobalAssemblyCacheCompletionHelper.cs
@@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.Completion.FileSystem
internal sealed class GlobalAssemblyCacheCompletionHelper
{
private static readonly Lazy> s_lazyAssemblySimpleNames =
- new Lazy>(() => GlobalAssemblyCache.GetAssemblySimpleNames().ToList());
+ new Lazy>(() => GlobalAssemblyCache.Instance.GetAssemblySimpleNames().ToList());
private readonly CompletionListProvider _completionProvider;
private readonly TextSpan _textChangeSpan;
private readonly CompletionItemRules _itemRules;
@@ -54,7 +54,7 @@ private IEnumerable GetCompletionsWorker(string pathSoFar)
select new CompletionItem(
_completionProvider,
displayName, _textChangeSpan,
- descriptionFactory: c => Task.FromResult(GlobalAssemblyCache.ResolvePartialName(displayName).GetDisplayName().ToSymbolDisplayParts()),
+ descriptionFactory: c => Task.FromResult(GlobalAssemblyCache.Instance.ResolvePartialName(displayName).GetDisplayName().ToSymbolDisplayParts()),
glyph: Glyph.Assembly,
rules: _itemRules);
}
@@ -62,7 +62,7 @@ private IEnumerable GetCompletionsWorker(string pathSoFar)
private IEnumerable GetAssemblyIdentities(string pathSoFar)
{
- return IOUtilities.PerformIO(() => GlobalAssemblyCache.GetAssemblyIdentities(pathSoFar),
+ return IOUtilities.PerformIO(() => GlobalAssemblyCache.Instance.GetAssemblyIdentities(pathSoFar),
SpecializedCollections.EmptyEnumerable());
}
}
diff --git a/src/Scripting/CSharpTest.Desktop/InteractiveSessionTests.cs b/src/Scripting/CSharpTest.Desktop/InteractiveSessionTests.cs
index 780a0b18c0fb2..46f98ed239f58 100644
--- a/src/Scripting/CSharpTest.Desktop/InteractiveSessionTests.cs
+++ b/src/Scripting/CSharpTest.Desktop/InteractiveSessionTests.cs
@@ -233,8 +233,8 @@ public void References2()
private static Lazy IsSystemV2AndV4Available = new Lazy(() =>
{
string path;
- return GlobalAssemblyCache.ResolvePartialName("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", out path) != null &&
- GlobalAssemblyCache.ResolvePartialName("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", out path) != null;
+ return GlobalAssemblyCache.Instance.ResolvePartialName("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", out path) != null &&
+ GlobalAssemblyCache.Instance.ResolvePartialName("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", out path) != null;
});
[Fact]
diff --git a/src/Scripting/Core/Scripting.csproj b/src/Scripting/Core/Scripting.csproj
index 2ea92c0dfe28c..9462438a752ca 100644
--- a/src/Scripting/Core/Scripting.csproj
+++ b/src/Scripting/Core/Scripting.csproj
@@ -48,6 +48,9 @@
Resolvers\RelativePathResolver.cs
+
+ Resolvers\ClrGlobalAssemblyCache.cs
+
Resolvers\FusionAssemblyIdentity.cs
@@ -60,6 +63,9 @@
Resolvers\GlobalAssemblyCache.Location.cs
+
+ Resolvers\MonoGlobalAssemblyCache.cs
+
@@ -144,4 +150,4 @@
-
+
\ No newline at end of file
diff --git a/src/Scripting/CoreTest.Desktop/GlobalAssemblyCacheTests.cs b/src/Scripting/CoreTest.Desktop/GlobalAssemblyCacheTests.cs
index 0bf0e39dc8e42..7eb250b8e1642 100644
--- a/src/Scripting/CoreTest.Desktop/GlobalAssemblyCacheTests.cs
+++ b/src/Scripting/CoreTest.Desktop/GlobalAssemblyCacheTests.cs
@@ -4,42 +4,43 @@
using System.IO;
using System.Linq;
using System.Reflection;
-using Microsoft.CodeAnalysis.Text;
using Xunit;
using System.Collections.Immutable;
-using Microsoft.CodeAnalysis.Scripting.Hosting;
+using Roslyn.Test.Utilities;
namespace Microsoft.CodeAnalysis.UnitTests
{
public class GlobalAssemblyCacheTests
{
- [Fact(Skip = "https://github.com/dotnet/roslyn/issues/6179")]
+ [MonoOnlyFact("https://github.com/dotnet/roslyn/issues/6179")]
public void GetAssemblyIdentities()
{
+ var gac = GlobalAssemblyCache.Instance;
+
AssemblyIdentity[] names;
- names = GlobalAssemblyCache.GetAssemblyIdentities(new AssemblyName("mscorlib")).ToArray();
+ names = gac.GetAssemblyIdentities(new AssemblyName("mscorlib")).ToArray();
Assert.True(names.Length >= 1, "At least 1 mscorlib");
foreach (var name in names)
{
Assert.Equal("mscorlib", name.Name);
}
- names = GlobalAssemblyCache.GetAssemblyIdentities(new AssemblyName("mscorlib"), ImmutableArray.Create(ProcessorArchitecture.MSIL, ProcessorArchitecture.X86)).ToArray();
+ names = gac.GetAssemblyIdentities(new AssemblyName("mscorlib"), ImmutableArray.Create(ProcessorArchitecture.MSIL, ProcessorArchitecture.X86)).ToArray();
Assert.True(names.Length >= 1, "At least one 32bit mscorlib");
foreach (var name in names)
{
Assert.Equal("mscorlib", name.Name);
}
- names = GlobalAssemblyCache.GetAssemblyIdentities("mscorlib").ToArray();
+ names = gac.GetAssemblyIdentities("mscorlib").ToArray();
Assert.True(names.Length >= 1, "At least 1 mscorlib");
foreach (var name in names)
{
Assert.Equal("mscorlib", name.Name);
}
- names = GlobalAssemblyCache.GetAssemblyIdentities("System.Core, Version=4.0.0.0").ToArray();
+ names = gac.GetAssemblyIdentities("System.Core, Version=4.0.0.0").ToArray();
Assert.True(names.Length >= 1, "At least System.Core");
foreach (var name in names)
{
@@ -48,7 +49,7 @@ public void GetAssemblyIdentities()
Assert.True(name.GetDisplayName().Contains("PublicKeyToken=b77a5c561934e089"), "PublicKeyToken matches");
}
- names = GlobalAssemblyCache.GetAssemblyIdentities("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089").ToArray();
+ names = gac.GetAssemblyIdentities("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089").ToArray();
Assert.True(names.Length >= 1, "At least System.Core");
foreach (var name in names)
{
@@ -58,7 +59,7 @@ public void GetAssemblyIdentities()
var n = new AssemblyName("System.Core");
n.Version = new Version(4, 0, 0, 0);
n.SetPublicKeyToken(new byte[] { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 });
- names = GlobalAssemblyCache.GetAssemblyIdentities(n).ToArray();
+ names = gac.GetAssemblyIdentities(n).ToArray();
Assert.True(names.Length >= 1, "At least System.Core");
foreach (var name in names)
@@ -66,30 +67,30 @@ public void GetAssemblyIdentities()
Assert.Equal("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", name.GetDisplayName());
}
- names = GlobalAssemblyCache.GetAssemblyIdentities("x\u0002").ToArray();
+ names = gac.GetAssemblyIdentities("x\u0002").ToArray();
Assert.Equal(0, names.Length);
- names = GlobalAssemblyCache.GetAssemblyIdentities("\0").ToArray();
+ names = gac.GetAssemblyIdentities("\0").ToArray();
Assert.Equal(0, names.Length);
- names = GlobalAssemblyCache.GetAssemblyIdentities("xxxx\0xxxxx").ToArray();
+ names = gac.GetAssemblyIdentities("xxxx\0xxxxx").ToArray();
Assert.Equal(0, names.Length);
// fusion API CreateAssemblyEnum returns S_FALSE for this name
- names = GlobalAssemblyCache.GetAssemblyIdentities("nonexistingassemblyname" + Guid.NewGuid().ToString()).ToArray();
+ names = gac.GetAssemblyIdentities("nonexistingassemblyname" + Guid.NewGuid().ToString()).ToArray();
Assert.Equal(0, names.Length);
}
- [Fact]
+ [ClrOnlyFact(ClrOnlyReason.Fusion)]
public void AssemblyAndGacLocation()
{
- var names = GlobalAssemblyCache.GetAssemblyObjects(partialNameFilter: null, architectureFilter: default(ImmutableArray)).ToArray();
+ var names = ClrGlobalAssemblyCache.GetAssemblyObjects(partialNameFilter: null, architectureFilter: default(ImmutableArray)).ToArray();
Assert.True(names.Length > 100, "There are at least 100 assemblies in the GAC");
var gacLocationsUpper = GlobalAssemblyCacheLocation.RootLocations.Select(location => location.ToUpper());
foreach (var name in names)
{
- string location = GlobalAssemblyCache.GetAssemblyLocation(name);
+ string location = ClrGlobalAssemblyCache.GetAssemblyLocation(name);
Assert.NotNull(location);
Assert.True(gacLocationsUpper.Any(gac => location.StartsWith(gac, StringComparison.OrdinalIgnoreCase)), "Path within some GAC root");
Assert.Equal(Path.GetFullPath(location), location);
diff --git a/src/Test/Utilities/Shared/Assert/ClrOnlyFactAttribute.cs b/src/Test/Utilities/Shared/Assert/ClrOnlyFactAttribute.cs
index e71d31f8b5e50..469af122e8492 100644
--- a/src/Test/Utilities/Shared/Assert/ClrOnlyFactAttribute.cs
+++ b/src/Test/Utilities/Shared/Assert/ClrOnlyFactAttribute.cs
@@ -32,6 +32,8 @@ public enum ClrOnlyReason
// Can't sign.
Signing,
+
+ Fusion,
}
public sealed class ClrOnlyFactAttribute : FactAttribute
@@ -62,9 +64,22 @@ private static string GetSkipReason(ClrOnlyReason reason)
return "Can't sign assemblies in this scenario";
case ClrOnlyReason.DocumentationComment:
return "Documentation comment compiler can't run this test on Mono";
+ case ClrOnlyReason.Fusion:
+ return "Fusion not available on Mono";
default:
return "Test supported only on CLR";
}
}
}
+
+ public sealed class MonoOnlyFactAttribute : FactAttribute
+ {
+ public MonoOnlyFactAttribute(string reason)
+ {
+ if (!MonoHelpers.IsRunningOnMono())
+ {
+ Skip = reason;
+ }
+ }
+ }
}