[runtime] Stop remapping higher versions of Microsoft.Build.Framework/Engine and...
authorAlexander Köplinger <alex.koeplinger@outlook.com>
Thu, 10 Mar 2016 21:17:12 +0000 (22:17 +0100)
committerAlexander Köplinger <alex.koeplinger@outlook.com>
Fri, 11 Mar 2016 14:03:18 +0000 (15:03 +0100)
The previous remapping was added in 725ed3f82400b1466ca17e99b85adcbde54238ca, but it didn't actually
fix the bug in https://bugzilla.xamarin.com/show_bug.cgi?id=32561.

The underlying cause of the bug is that in Mono 4.0 we removed the 2.0/3.5 profiles.
This meant that Microsoft.Build.Utilities.dll and Microsoft.Build.Utilities.v3.5.dll weren't installed
in the GAC anymore, because MSBuild 4.0 uses Microsoft.Build.Utilities.v4.0.dll, so the app failed
to load the 2.0 version of the assembly. The same applies for the .Tasks variants.

We remap earlier versions of the Microsoft.Build.Utilities/Tasks assemblies to the v4.0 version.
I've introduced another item in the AssemblyVersionMap struct which allows remapping to target a different
assembly name, because MSBuild made the questionable decision to put the version number in the assembly name.
While strictly speaking this is not what .NET is doing (it just fails there if .NET 2.0 isn't installed)
on Mono there's no way to install those older profiles anymore, so we remap them to v4.0 to keep apps working.

Newer versions of Microsoft.Build.Framework/Engine (like 12.0, 14.0) however have the same name, so we need to be
more careful here and only do a remapping to the 4.0 version if it's an earlier version (e.g. 2.0 -> 4.0).
This avoids issues with accidentally remapping 14.0 -> 4.0.

I tested the app from the bug report and it now works fine.

mono/metadata/assembly.c
mono/tests/Makefile.am
mono/tests/assembly-load-remap.cs

index 7785abeafa9b643d5d4959a2477969956d0fe4e8..857d377aca3076ff35d6517c83fb9104575f36f7 100644 (file)
 #include <mach-o/dyld.h>
 #endif
 
-/* AssemblyVersionMap: an assembly name and the assembly version set on which it is based */
+/* AssemblyVersionMap: an assembly name, the assembly version set on which it is based, the assembly name it is replaced with and whether only versions lower than the current runtime version should be remapped */
 typedef struct  {
        const char* assembly_name;
        guint8 version_set_index;
+       const char* new_assembly_name;
+       gboolean only_lower_versions;
 } AssemblyVersionMap;
 
 /* the default search path is empty, the first slot is replaced with the computed value */
@@ -89,8 +91,12 @@ static const AssemblyVersionMap framework_assemblies [] = {
        {"I18N.Other", 0},
        {"I18N.Rare", 0},
        {"I18N.West", 0},
-       {"Microsoft.Build.Engine", 2},
-       {"Microsoft.Build.Framework", 2},
+       {"Microsoft.Build.Engine", 2, NULL, TRUE},
+       {"Microsoft.Build.Framework", 2, NULL, TRUE},
+       {"Microsoft.Build.Tasks", 2, "Microsoft.Build.Tasks.v4.0"},
+       {"Microsoft.Build.Tasks.v3.5", 2, "Microsoft.Build.Tasks.v4.0"},
+       {"Microsoft.Build.Utilities", 2, "Microsoft.Build.Utilities.v4.0"},
+       {"Microsoft.Build.Utilities.v3.5", 2, "Microsoft.Build.Utilities.v4.0"},
        {"Microsoft.VisualBasic", 1},
        {"Microsoft.VisualC", 1},
        {"Mono.Cairo", 0},
@@ -1017,6 +1023,9 @@ mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_ana
                                aname->build == vset->build && aname->revision == vset->revision)
                                return aname;
                
+                       if (framework_assemblies[pos].only_lower_versions && compare_versions ((AssemblyVersionSet*)vset, aname) < 0)
+                               return aname;
+
                        if ((aname->major | aname->minor | aname->build | aname->revision) != 0)
                                mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
                                        "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d",
@@ -1030,6 +1039,13 @@ mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_ana
                        dest_aname->minor = vset->minor;
                        dest_aname->build = vset->build;
                        dest_aname->revision = vset->revision;
+                       if (framework_assemblies[pos].new_assembly_name != NULL) {
+                               dest_aname->name = framework_assemblies[pos].new_assembly_name;
+                               mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
+                                                       "The assembly name %s was remapped to %s",
+                                                       aname->name,
+                                                       dest_aname->name);
+                       }
                        return dest_aname;
                } else if (res < 0) {
                        last = pos - 1;
index 7ed3e2813040c578a03440222ff496af4bfdb7e3..4898932259a8431914d536cf941160f85585b9ce 100644 (file)
@@ -1,7 +1,7 @@
 SUBDIRS = assemblyresolve gc-descriptors
 
 check-local: assemblyresolve/test/asm.dll testjit test-generic-sharing test-type-load test-cattr-type-load test-reflection-load-with-context test_platform     \
-                test-console-output test-messages test-env-options test-unhandled-exception-2 test-appdomain-unload test-process-stress test-assembly-load-remap rm-empty-logs
+                test-console-output test-messages test-env-options test-unhandled-exception-2 test-appdomain-unload test-process-stress rm-empty-logs
 check-full: test-sgen check-local
 check-parallel: compile-tests check-full
 
@@ -61,6 +61,7 @@ BASE_TEST_CS_SRC=             \
        bug-2907.cs             \
        array-init.cs           \
        arraylist.cs            \
+       assembly-load-remap.cs  \
        assemblyresolve_event.cs        \
        assemblyresolve_event3.cs       \
        assemblyresolve_event4.cs       \
@@ -1429,13 +1430,6 @@ PROCESS_STRESS_TESTS=    \
 test-process-stress: $(PROCESS_STRESS_TESTS) test-runner.exe
        $(RUNTIME) ./test-runner.exe --testsuite-name $@ --timeout 600 $(PROCESS_STRESS_TESTS)
 
-EXTRA_DIST += assembly-load-remap.cs
-test-assembly-load-remap: assembly-load-remap.exe
-       MONO_LOG_LEVEL=warning MONO_LOG_MASK=asm $(RUNTIME) assembly-load-remap.exe "2.0.0.0" | grep "remapped to v4.0.0.0"
-       MONO_LOG_LEVEL=warning MONO_LOG_MASK=asm $(RUNTIME) assembly-load-remap.exe "4.0.0.0" | grep "remapped to" && exit 1 || exit 0
-       MONO_LOG_LEVEL=warning MONO_LOG_MASK=asm $(RUNTIME) assembly-load-remap.exe "12.0.0.0" | grep "remapped to" && exit 1 || exit 0
-       MONO_LOG_LEVEL=warning MONO_LOG_MASK=asm $(RUNTIME) assembly-load-remap.exe "14.0.0.0" | grep "remapped to" && exit 1 || exit 0
-
 coreclr-gcstress:
        $(MAKE) -C $(mono_build_root)/acceptance-tests coreclr-gcstress
 
index a206cdcd7525560a56d41544eba3956ade1b8f22..f9c93d5da88f65186082c68cd3dc8b432ff2c562 100644 (file)
@@ -6,13 +6,85 @@ public class Tests
 {
        public static void Main (string[] args)
        {
-               if (args.Length != 1)
-                       throw new Exception ("Missing commandline args.");
+               var ver40 = new Version (4, 0, 0, 0);
+               var ver140 = new Version (14, 0, 0, 0);
+               var util20 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Utilities, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+               var util35 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Utilities.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+               var task20 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+               var task35 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+               var engn20 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Engine, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+               var engn35 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Engine, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+               var frwk20 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Framework, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+               var frwk35 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
 
-               string versionLoad = args [0];
-               var asm = Assembly.Load ("Microsoft.Build.Framework, Version=" + versionLoad + ", Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+               // when run as part of the test suite, we need to register the xbuild 14.0 path or v14 assembly lookup will fail 
+               if (!String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("MONO_PATH"))) {
+                       var p = Path.Combine (new DirectoryInfo (Environment.GetEnvironmentVariable ("MONO_PATH")).Parent.FullName, "xbuild_14");
+                       Console.WriteLine("Adding private bin path " + p);
+                       AppDomain.CurrentDomain.AppendPrivatePath (p);
+               }
 
-               if (asm == null)
-                       throw new Exception ("Assembly couldn't be loaded.");
+               var engn140 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Engine, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+               var frwk140 = Assembly.ReflectionOnlyLoad ("Microsoft.Build.Framework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+
+               if (util20 == null)
+                       throw new Exception ("#1 assembly couldn't be loaded.");
+
+               if (util35 == null)
+                       throw new Exception ("#2 assembly couldn't be loaded.");
+
+               if (util20.GetName ().Version != ver40)
+                       throw new Exception ("#3 expected remap to v4.0.0.0, but got " + util20);
+
+               if (util35.GetName ().Version != ver40)
+                       throw new Exception ("#4 expected remap to v4.0.0.0, but got " + util35);
+
+               if (task20 == null)
+                       throw new Exception ("#5 assembly couldn't be loaded.");
+
+               if (task35 == null)
+                       throw new Exception ("#6 assembly couldn't be loaded.");
+
+               if (task20.GetName ().Version != ver40)
+                       throw new Exception ("#7 expected remap to v4.0.0.0, but got " + task20);
+
+               if (task35.GetName ().Version != ver40)
+                       throw new Exception ("#8 expected remap to v4.0.0.0, but got " + task35);
+
+               if (engn20 == null)
+                       throw new Exception ("#9 assembly couldn't be loaded.");
+
+               if (engn35 == null)
+                       throw new Exception ("#10 assembly couldn't be loaded.");
+
+               if (engn140 == null)
+                       throw new Exception ("#11 assembly couldn't be loaded.");
+
+               if (engn20.GetName ().Version != ver40)
+                       throw new Exception ("#12 expected remap to v4.0.0.0, but got " + engn20);
+
+               if (engn35.GetName ().Version != ver40)
+                       throw new Exception ("#13 expected remap to v4.0.0.0, but got " + engn35);
+       
+               if (engn140.GetName ().Version != ver140)
+                       throw new Exception ("#14 expected v14.0.0.0, but got " + engn140);
+
+               if (frwk20 == null)
+                       throw new Exception ("#15 assembly couldn't be loaded.");
+
+               if (frwk35 == null)
+                       throw new Exception ("#16 assembly couldn't be loaded.");
+
+               if (frwk140 == null)
+                       throw new Exception ("#17 assembly couldn't be loaded.");
+
+               if (frwk20.GetName ().Version != ver40)
+                       throw new Exception ("#18 expected remap to v4.0.0.0, but got " + frwk20);
+
+               if (frwk35.GetName ().Version != ver40)
+                       throw new Exception ("#19 expected remap to v4.0.0.0, but got " + frwk35);
+
+               if (frwk140.GetName ().Version != ver140)
+                       throw new Exception ("#20 expected v14.0.0.0, but got " + frwk140);
        }
 }