[MSBuild] Fix assembly resolution issue
authorLluis Sanchez <lluis@xamarin.com>
Fri, 5 Jun 2015 10:00:34 +0000 (12:00 +0200)
committerLluis Sanchez <lluis@xamarin.com>
Fri, 5 Jun 2015 10:00:34 +0000 (12:00 +0200)
The ResolveAssemblyReference task class keeps a reference to an
instance of AssemblyResolver, and AssemblyResolver keeps a cache
of assembly names, indexed by file name. The problem is that
the cache doesn't take into account that files can change between
builds.

For example, if a referenced assembly doesn't exist, the cache
will store that the file doesn't have an assembly name. If after
the build the file is created and a new build operation is started,
the cache will still return that the file doesn't have a name.

This is not a problem when running xbuild from the command line,
since every build starts a new process, but MonoDevelop uses
the MSBuild API to load and build projects, and doesn't start
a new process every time, so the cache is never cleared.

The solution is to keep the last write timestamp in the cache,
so that it can check if the file changed before returning the
cached name. It also needs to do a better handling of non existant
files.

mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/AssemblyResolver.cs

index bf14075f7ff0dd040cf264e7dca6a96a9c4300fd..41e92294d1e247a5226f156334f5bd60c1760740 100644 (file)
@@ -311,16 +311,34 @@ namespace Microsoft.Build.Tasks {
                                                SearchPath.HintPath, specific_version);
                }
 
-               static Dictionary<string, AssemblyName> assemblyNameCache = new Dictionary<string, AssemblyName> ();
+               class CachedAssemblyName
+               {
+                       public DateTime Time;
+                       public AssemblyName Name;
+               }
+
+               static Dictionary<string, CachedAssemblyName> assemblyNameCache = new Dictionary<string, CachedAssemblyName> ();
                public bool TryGetAssemblyNameFromFile (string filename, out AssemblyName aname)
                {
-                       filename = Path.GetFullPath (filename);
-                       if (assemblyNameCache.TryGetValue (filename, out aname))
+                       FileInfo info = new FileInfo (filename);
+                       if (!info.Exists) {
+                               aname = null;
+                               LogSearchMessage ("Considered '{0}' as a file, but the file does not exist",
+                                                 filename);
+                               return false;
+                       }
+                       filename = info.FullName;
+                       CachedAssemblyName cachedName;
+                       if (assemblyNameCache.TryGetValue (filename, out cachedName) && cachedName.Time == info.LastWriteTime) {
+                               aname = cachedName.Name;
                            return aname != null;
+                       }
 
+                       cachedName = new CachedAssemblyName ();
+                       cachedName.Time = info.LastWriteTime;
                        aname = null;
                        try {
-                               aname = AssemblyName.GetAssemblyName (filename);
+                               cachedName.Name = aname = AssemblyName.GetAssemblyName (filename);
                        } catch (FileNotFoundException) {
                                LogSearchMessage ("Considered '{0}' as a file, but the file does not exist",
                                                filename);
@@ -329,7 +347,7 @@ namespace Microsoft.Build.Tasks {
                                                filename);
                        }
 
-                       assemblyNameCache [filename] = aname;
+                       assemblyNameCache [filename] = cachedName;
                        return aname != null;
                }