From 2a240a7afac236357b2428bbc406720bd5b8d344 Mon Sep 17 00:00:00 2001 From: Andy Gerlicher Date: Wed, 26 Apr 2017 13:04:53 -0700 Subject: [PATCH] Use AssemblyName.Clone when possible. AssemblyName.Clone is not available in early versions of .NET Core. Until we can upgrade MSBuild to use .NET Core 2.0, we can't call the Clone method directly. This change will attempt to call .Clone using reflection and if not available fallback to the previous .NET Core behavior. Potential fix for #2015 (when running on .NET Core 2.0). --- src/Shared/AssemblyNameExtension.cs | 8 ++--- src/Shared/AssemblyUtilities.cs | 29 ++++++++++++++++--- src/Tasks/AppConfig/DependentAssembly.cs | 22 ++++---------- .../AssemblyDependency/ReferenceTable.cs | 19 ++---------- 4 files changed, 35 insertions(+), 43 deletions(-) diff --git a/src/Shared/AssemblyNameExtension.cs b/src/Shared/AssemblyNameExtension.cs index 529335c8bea..482bccd1bba 100644 --- a/src/Shared/AssemblyNameExtension.cs +++ b/src/Shared/AssemblyNameExtension.cs @@ -57,7 +57,7 @@ internal enum PartialComparisonFlags : int /// between the two is done lazily on demand. /// [Serializable] - sealed internal class AssemblyNameExtension + internal sealed class AssemblyNameExtension { private AssemblyName asAssemblyName = null; private string asString = null; @@ -592,11 +592,7 @@ internal AssemblyNameExtension Clone() if (asAssemblyName != null) { -#if FEATURE_ASSEMBLYNAME_CLONE - newExtension.asAssemblyName = (AssemblyName)asAssemblyName.Clone(); -#else - newExtension.asAssemblyName = new AssemblyName(asAssemblyName.FullName); -#endif + newExtension.asAssemblyName = asAssemblyName.CloneIfPossible(); } newExtension.asString = asString; diff --git a/src/Shared/AssemblyUtilities.cs b/src/Shared/AssemblyUtilities.cs index a40a6d46268..52ed3e75a3f 100644 --- a/src/Shared/AssemblyUtilities.cs +++ b/src/Shared/AssemblyUtilities.cs @@ -11,6 +11,12 @@ namespace Microsoft.Build.Shared /// internal static class AssemblyUtilities { + private static readonly Lazy _assemblyNameCloneMethod = + new Lazy(() => typeof(AssemblyName).GetMethod("Clone")); + + private static readonly Lazy _assemblylocationProperty = + new Lazy(() => typeof(Assembly).GetProperty("Location", typeof(string))); + public static string GetAssemblyLocation(Assembly assembly) { #if FEATURE_ASSEMBLY_LOCATION @@ -19,14 +25,12 @@ public static string GetAssemblyLocation(Assembly assembly) // Assembly.Location is only available in .netstandard1.5, but MSBuild needs to target 1.3. // use reflection to access the property - var locationProperty = assembly.GetType().GetProperty("Location", typeof(string)); - - if (locationProperty == null) + if (_assemblylocationProperty.Value == null) { throw new NotSupportedException("Type Assembly does not have the Location property"); } - return (string)locationProperty.GetValue(assembly); + return (string)_assemblylocationProperty.Value.GetValue(assembly); #endif } @@ -39,5 +43,22 @@ public static Type GetTypeInfo(this Type t) return t; } #endif + + public static AssemblyName CloneIfPossible(this AssemblyName assemblyNameToClone) + { +#if FEATURE_ASSEMBLYNAME_CLONE + return (AssemblyName) assemblyNameToClone.Clone(); +#else + AssemblyName result = null; + try + { + result = (AssemblyName)_assemblyNameCloneMethod.Value?.Invoke(assemblyNameToClone, null); + } + catch (MemberAccessException) + { } + + return result ?? new AssemblyName(assemblyNameToClone.FullName); +#endif + } } } diff --git a/src/Tasks/AppConfig/DependentAssembly.cs b/src/Tasks/AppConfig/DependentAssembly.cs index 3c70399c50b..c7252c95b3f 100644 --- a/src/Tasks/AppConfig/DependentAssembly.cs +++ b/src/Tasks/AppConfig/DependentAssembly.cs @@ -22,35 +22,23 @@ internal sealed class DependentAssembly private BindingRedirect[] _bindingRedirects = null; /// - /// The partial assemblyname, there should be no version. + /// The partial , there should be no version. /// - private AssemblyName _partialAssemblyName = null; + private AssemblyName _partialAssemblyName; /// - /// The partial assemblyname, there should be no version. + /// The partial , there should be no version. /// internal AssemblyName PartialAssemblyName { set { -#if FEATURE_ASSEMBLYNAME_CLONE - _partialAssemblyName = (AssemblyName)value.Clone(); -#else - _partialAssemblyName = new AssemblyName(value.FullName); -#endif + _partialAssemblyName = value.CloneIfPossible(); _partialAssemblyName.Version = null; } get { - if (_partialAssemblyName == null) - { - return null; - } -#if FEATURE_ASSEMBLYNAME_CLONE - return (AssemblyName)_partialAssemblyName.Clone(); -#else - return new AssemblyName(_partialAssemblyName.FullName); -#endif + return _partialAssemblyName?.CloneIfPossible(); } } diff --git a/src/Tasks/AssemblyDependency/ReferenceTable.cs b/src/Tasks/AssemblyDependency/ReferenceTable.cs index cd4bbc230e3..e6082536856 100644 --- a/src/Tasks/AssemblyDependency/ReferenceTable.cs +++ b/src/Tasks/AssemblyDependency/ReferenceTable.cs @@ -1070,14 +1070,9 @@ IEnumerable preUnificationAssemblyNames { foreach (AssemblyNameExtension preUnificationAssemblyName in preUnificationAssemblyNames) { - string name = preUnificationAssemblyName.Name; // First, unify the assembly name so that we're dealing with the right version. // Not AssemblyNameExtension because we're going to write to it. -#if FEATURE_ASSEMBLYNAME_CLONE - AssemblyNameExtension dependentAssembly = new AssemblyNameExtension((AssemblyName)preUnificationAssemblyName.AssemblyName.Clone()); -#else - AssemblyNameExtension dependentAssembly = new AssemblyNameExtension(new AssemblyName(preUnificationAssemblyName.AssemblyName.FullName)); -#endif + AssemblyNameExtension dependentAssembly = new AssemblyNameExtension(preUnificationAssemblyName.AssemblyName.CloneIfPossible()); Version unifiedVersion; bool isPrerequisite; @@ -1883,11 +1878,7 @@ out AssemblyNameReference[] conflictingReferences byte[] pkt = assemblyName.GetPublicKeyToken(); if (pkt != null && pkt.Length > 0) { -#if FEATURE_ASSEMBLYNAME_CLONE - AssemblyName baseKey = (AssemblyName)assemblyName.AssemblyName.Clone(); -#else - AssemblyName baseKey = new AssemblyName(assemblyName.AssemblyName.FullName); -#endif + AssemblyName baseKey = assemblyName.AssemblyName.CloneIfPossible(); Version version = baseKey.Version; baseKey.Version = null; string key = baseKey.ToString(); @@ -2377,11 +2368,7 @@ out string redistName foreach (DependentAssembly remappedAssembly in _remappedAssemblies) { // First, exclude anything without the simple name match -#if FEATURE_ASSEMBLYNAME_CLONE - AssemblyNameExtension comparisonAssembly = new AssemblyNameExtension((AssemblyName)remappedAssembly.PartialAssemblyName.Clone()); -#else - AssemblyNameExtension comparisonAssembly = new AssemblyNameExtension(new AssemblyName(remappedAssembly.PartialAssemblyName.FullName)); -#endif + AssemblyNameExtension comparisonAssembly = new AssemblyNameExtension(remappedAssembly.PartialAssemblyName.CloneIfPossible()); if (assemblyName.CompareBaseNameTo(comparisonAssembly) == 0) { // Comparison assembly is a partial name. Give it our version.