[mmp] Explicitly resolve assemblies from the GAC / system mono. (#8377)

Cecil has a fall-back mode where it looks in the GAC / system mono for
assemblies when failing to find them elsewhere. This is not the expected
behavior when using Xamarin.Mac in the Full/XM mode, because then we should
only resolve to assemblies shipped with Xamarin.Mac.

Unfortunately doing so will break apps (our own tests break), so instead
change our resolution to be explicit about where we find assemblies, and if we
find assemblies in the GAC / system mono when we're not supposed to, then show
a warning.

Also add a fall-back mechanism, where we use the old logic instead, in case
the new logic is not 100% compatible with the old one.

This showed up when I tried to port mmp to dotnet, because then Cecil stopped
looking in the GAC / system mono for assemblies (Cecil has a special case when
running on Mono to look in Mono's GAC), and tests started failing.
This commit is contained in:
Rolf Bjarne Kvinge 2020-04-14 16:32:42 +02:00 коммит произвёл GitHub
Родитель c150679630
Коммит 511124f4b1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
20 изменённых файлов: 237 добавлений и 14 удалений

Просмотреть файл

@ -654,6 +654,38 @@ namespace Xamarin.MMP.Tests
});
}
[Test]
[TestCase (true, "v4.5.2")]
[TestCase (true, null)]
[TestCase (true, "v4.7.2")]
[TestCase (false, null)]
public void MM0176 (bool xm45, string tfv)
{
RunMMPTest (tmpDir => {
var test = new TI.UnifiedTestConfig (tmpDir) {
References = "<PackageReference Include=\"xunit.runner.utility\" Version=\"2.4.0\" />",
TestCode = "System.Console.WriteLine (typeof (Xunit.AfterTestFinished));",
XM45 = xm45,
};
if (tfv != null)
test.TargetFrameworkVersion = $"<TargetFrameworkVersion>{tfv}</TargetFrameworkVersion>";
string project = TI.GenerateUnifiedExecutableProject (test);
TI.NugetRestore (project);
var rv = new OutputText (TI.BuildProject (project), string.Empty);
Console.WriteLine (rv.BuildOutput);
if (xm45) {
var referenced_version = tfv == null ? "2.0.0.0" : "4.0.0.0";
rv.Messages.AssertWarningPattern (176, $"The assembly 'System.Web, Version={referenced_version}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' was resolved from the system's GAC: /Library/Frameworks/Mono.framework/Versions/.*/lib/mono/gac/System.Web/4.0.0.0__b03f5f7f11d50a3a/System.Web.dll. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.");
rv.Messages.AssertWarningPattern (176, $"The assembly 'System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' was resolved from the system's GAC: /Library/Frameworks/Mono.framework/Versions/.*/lib/mono/gac/System.Drawing/4.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.");
rv.Messages.AssertWarningPattern (176, $"The assembly 'System.Web.ApplicationServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' was resolved from the system's GAC: /Library/Frameworks/Mono.framework/Versions/.*/lib/mono/gac/System.Web.ApplicationServices/4.0.0.0__31bf3856ad364e35/System.Web.ApplicationServices.dll. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.");
// Don't assert number of warnings, because we get a few "MM2006: Native library 'foo.dll' was referenced but could not be found." warnings as well.
} else {
rv.Messages.AssertWarningCount (0);
}
});
}
[Test]
public void BuildingSameSolutionTwice_ShouldNotRunACToolTwice ()
{

Просмотреть файл

@ -103,7 +103,12 @@ namespace Xamarin.Bundler {
catch (Exception e) {
throw new ProductException (9, true, e, Errors.MX0009, fileName);
}
cache.Add (name, assembly);
return CacheAssembly (assembly);
}
public AssemblyDefinition CacheAssembly (AssemblyDefinition assembly)
{
cache [assembly.Name.Name] = assembly;
return assembly;
}

Просмотреть файл

@ -198,6 +198,8 @@ namespace MonoMac.Tuner {
continue;
list.Add (GetFullyQualifiedName (assembly));
Driver.Log (1, "Loaded assembly: {0}", assembly.MainModule.FileName);
}
return list;

Просмотреть файл

@ -134,6 +134,7 @@ namespace Xamarin.Bundler {
public static bool IsUnifiedFullSystemFramework { get { return TargetFramework == TargetFramework.Xamarin_Mac_4_5_System; } }
public static bool IsUnifiedMobile { get { return TargetFramework == TargetFramework.Xamarin_Mac_2_0_Mobile; } }
public static bool LinkProhibitedFrameworks { get; private set; }
public static bool UseLegacyAssemblyResolution { get; private set; }
static string mono_prefix;
static string MonoPrefix {
@ -338,6 +339,7 @@ namespace Xamarin.Bundler {
App.WarnOnTypeRef.AddRange (v.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries));
}
},
{ "legacy-assembly-resolution", "Use a legacy assembly resolution logic when using the Xamarin.Mac Full framework.", v => { UseLegacyAssemblyResolution = true; }, false /* hidden until we know if it's needed */ },
};
var extra_args = Environment.GetEnvironmentVariable ("MMP_ENV_OPTIONS");
@ -625,6 +627,20 @@ namespace Xamarin.Bundler {
references.Add (root_assembly);
BuildTarget.Resolver.CommandLineAssemblies = references;
if (!UseLegacyAssemblyResolution && (IsUnifiedFullSystemFramework || IsUnifiedFullXamMacFramework)) {
// We need to look in the GAC/System mono for both FullSystem and FullXamMac, because that's
// how we've been resolving assemblies in the past (Cecil has a fall-back mode where it looks
// in the GAC, and we never disabled that, meaning that we always looked in the GAC if failing
// to resolve from somewhere else). This makes it explicit that we look in the GAC, and we
// now also warn when using FullXamMac and finding assemblies in the GAC.
BuildTarget.Resolver.GlobalAssemblyCache = Path.Combine (SystemMonoDirectory, "lib", "mono", "gac");
var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
BuildTarget.Resolver.SystemFrameworkDirectories = new [] {
framework_dir,
Path.Combine (framework_dir, "Facades")
};
}
if (string.IsNullOrEmpty (app_name))
app_name = root_wo_ext;
@ -1270,9 +1286,17 @@ namespace Xamarin.Bundler {
static IDictionary<string,List<MethodDefinition>> Link ()
{
var cache = (Dictionary<string, AssemblyDefinition>) BuildTarget.Resolver.ResolverCache;
var resolver = cache != null
? new Mono.Linker.AssemblyResolver (cache)
: new Mono.Linker.AssemblyResolver ();
AssemblyResolver resolver;
if (UseLegacyAssemblyResolution) {
if (cache != null) {
resolver = new Mono.Linker.AssemblyResolver (cache);
} else {
resolver = new Mono.Linker.AssemblyResolver ();
}
} else {
resolver = new MonoMacAssemblyResolver (BuildTarget.Resolver);
}
resolver.AddSearchDirectory (BuildTarget.Resolver.RootDirectory);
resolver.AddSearchDirectory (BuildTarget.Resolver.FrameworkDirectory);
@ -1753,7 +1777,7 @@ namespace Xamarin.Bundler {
resolved_assemblies.Add (fqname);
foreach (AssemblyNameReference reference in assembly.MainModule.AssemblyReferences) {
AssemblyDefinition reference_assembly = AddAssemblyReferenceToResolver (reference.Name);
AssemblyDefinition reference_assembly = AddAssemblyReferenceToResolver (reference);
ProcessAssemblyReferences (reference_assembly);
}
}
@ -1769,10 +1793,10 @@ namespace Xamarin.Bundler {
return assembly;
}
static AssemblyDefinition AddAssemblyReferenceToResolver (string reference)
static AssemblyDefinition AddAssemblyReferenceToResolver (AssemblyNameReference reference)
{
if (AssemblySwapInfo.ReferencedNeedsSwappedOut (reference))
return BuildTarget.Resolver.Load (AssemblySwapInfo.GetSwappedReference (reference));
if (AssemblySwapInfo.ReferencedNeedsSwappedOut (reference.Name))
return BuildTarget.Resolver.Load (AssemblySwapInfo.GetSwappedReference (reference.Name));
return BuildTarget.Resolver.Resolve (reference);
}

Просмотреть файл

@ -23,25 +23,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Mono.Cecil;
using Mono.Linker;
namespace Xamarin.Bundler {
public partial class MonoMacResolver : CoreResolver {
public List <string> CommandLineAssemblies { get; set; }
public List<Exception> Exceptions = new List<Exception> ();
public string GlobalAssemblyCache;
public string[] SystemFrameworkDirectories;
public AssemblyDefinition GetAssembly (string fileName)
{
return Resolve (new AssemblyNameReference (Path.GetFileNameWithoutExtension (fileName), null), new ReaderParameters { AssemblyResolver = this });
}
public AssemblyDefinition Resolve (string fullName)
{
return Resolve (AssemblyNameReference.Parse (fullName), new ReaderParameters { AssemblyResolver = this });
}
public override AssemblyDefinition Resolve (AssemblyNameReference reference, ReaderParameters parameters)
{
var name = reference.Name;
@ -82,7 +80,52 @@ namespace Xamarin.Bundler {
if (assembly != null)
return assembly;
if (!string.IsNullOrEmpty (GlobalAssemblyCache)) {
var gac_folder = new StringBuilder ()
.Append (reference.Version)
.Append ("__");
for (int i = 0; i < reference.PublicKeyToken.Length; i++)
gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));
var gac_path = Path.Combine (GlobalAssemblyCache, reference.Name, gac_folder.ToString (), reference.Name + ".dll");
if (File.Exists (gac_path)) {
if (Driver.IsUnifiedFullXamMacFramework)
ErrorHelper.Warning (176, Errors.MX0176, reference.ToString (), gac_path);
return Load (gac_path);
}
}
if (SystemFrameworkDirectories?.Length > 0) {
var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
var framework_dirs = new [] { framework_dir, Path.Combine (framework_dir, "Facades") };
foreach (var dir in framework_dirs) {
assembly = SearchDirectory (reference.Name, dir);
if (assembly != null) {
if (Driver.IsUnifiedFullXamMacFramework)
ErrorHelper.Warning (176, Errors.MX0176, reference.ToString (), assembly.MainModule.FileName);
return assembly;
}
}
}
return null;
}
}
public class MonoMacAssemblyResolver : AssemblyResolver {
public MonoMacResolver Resolver;
public MonoMacAssemblyResolver (MonoMacResolver resolver)
: base (resolver.cache ?? new Dictionary<string, AssemblyDefinition> ())
{
this.Resolver = resolver;
}
public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
{
return Resolver.Resolve (name, parameters);
}
}
}

6
tools/mtouch/Errors.designer.cs сгенерированный
Просмотреть файл

@ -1073,6 +1073,12 @@ namespace Xamarin.Bundler {
}
}
internal static string MX0176 {
get {
return ResourceManager.GetString("MX0176", resourceCulture);
}
}
internal static string MX1009 {
get {
return ResourceManager.GetString("MX1009", resourceCulture);

Просмотреть файл

@ -1217,6 +1217,13 @@
</comment>
</data>
<data name="MX0176" xml:space="preserve">
<value>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</value>
<comment>
</comment>
</data>
<data name="MX1009" xml:space="preserve">
<value>Could not copy the assembly '{0}' to '{1}': {2}
</value>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>

Просмотреть файл

@ -2882,6 +2882,14 @@
<note>
</note>
</trans-unit>
<trans-unit id="MX0176">
<source>The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</source>
<target state="new">The assembly '{0}' was resolved from the system's GAC: {1}. This could potentially be a problem in the future; to avoid such problems, please make sure to not use assemblies only available in the system's GAC.
</target>
<note>
</note>
</trans-unit>
<trans-unit id="MX1009">
<source>Could not copy the assembly '{0}' to '{1}': {2}
</source>