X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FMicrosoft.Build.Tasks%2FMicrosoft.Build.Tasks%2FAssemblyResolver.cs;h=8b51d76480ddded85b94a5db8b40aab05894f86d;hb=8d44cb69b32bd1776546b6a70b848651f37e6fe9;hp=5c52877d453f9396cb7729ed152ad6f40a5c7f1f;hpb=881b6cf16d326fcefae81e363ea70929111f9dc6;p=mono.git diff --git a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/AssemblyResolver.cs b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/AssemblyResolver.cs index 5c52877d453..8b51d76480d 100644 --- a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/AssemblyResolver.cs +++ b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/AssemblyResolver.cs @@ -32,10 +32,12 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Security; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Mono.PkgConfig; namespace Microsoft.Build.Tasks { internal class AssemblyResolver { @@ -45,6 +47,9 @@ namespace Microsoft.Build.Tasks { Dictionary> gac; TaskLoggingHelper log; StringWriter sw; + List search_log; + + static LibraryPcFileCache cache; public AssemblyResolver () { @@ -54,13 +59,12 @@ namespace Microsoft.Build.Tasks { GatherGacAssemblies (); } - public StringWriter SearchLogger { - get { return sw; } - } - public void ResetSearchLogger () { - sw = new StringWriter (); + if (search_log == null) + search_log = new List (); + else + search_log.Clear (); } string GetGacPath () @@ -114,7 +118,13 @@ namespace Microsoft.Build.Tasks { public ResolvedReference FindInTargetFramework (ITaskItem reference, string framework_dir, bool specific_version) { - AssemblyName key_aname = new AssemblyName (reference.ItemSpec); + if (!Directory.Exists (framework_dir)) + return null; + + AssemblyName key_aname; + if (!TryGetAssemblyNameFromFullName (reference.ItemSpec, out key_aname)) + return null; + TargetFrameworkAssemblies gac_asm; if (!target_framework_cache.TryGetValue (framework_dir, out gac_asm)) { // fill gac_asm @@ -125,41 +135,71 @@ namespace Microsoft.Build.Tasks { if (gac_asm.NameToAssemblyNameCache.TryGetValue (key_aname.Name, out pair)) { if (AssemblyNamesCompatible (key_aname, pair.Key, specific_version)) { // gac and tgt frmwk refs are not copied private - return GetResolvedReference (reference, pair.Value, false, + return GetResolvedReference (reference, pair.Value, pair.Key, false, SearchPath.TargetFrameworkDirectory); } - SearchLogger.WriteLine ("Considered target framework dir {0}, assembly name '{1}' did not " + + LogSearchMessage ("Considered target framework dir {0}, assembly name '{1}' did not " + "match the expected '{2}' (SpecificVersion={3})", framework_dir, pair.Key, key_aname, specific_version); } else { - SearchLogger.WriteLine ("Considered target framework dir {0}, assembly named '{1}' not found.", + LogSearchMessage ("Considered target framework dir {0}, assembly named '{1}' not found.", framework_dir, key_aname.Name); } return null; } - public ResolvedReference FindInDirectory (ITaskItem reference, string directory, string [] file_extensions) + // Look for %(Identity).{dll|exe|..} + // if specific_version==true + // resolve if assembly names match + // else + // resolve the valid assembly + public ResolvedReference FindInDirectory (ITaskItem reference, string directory, string [] file_extensions, bool specific_version) { - if (reference.ItemSpec.IndexOf (',') > 0) { - // Probably an assembly name - AssemblyName key_aname = new AssemblyName (reference.ItemSpec); - foreach (string extn in file_extensions) { - foreach (string file in Directory.GetFiles (directory, "*" + extn)) { - AssemblyName found = AssemblyName.GetAssemblyName (file); - - //FIXME: Extract 'name' and look only for name.dll name.exe ? - if (AssemblyNamesCompatible (key_aname, found, false)) - return GetResolvedReference (reference, file, true, SearchPath.Directory); - - SearchLogger.WriteLine ("Considered {0}, but assembly name wasn't compatible.", file); - } - } + string filename = reference.ItemSpec; + int comma_pos = filename.IndexOf (','); + if (comma_pos >= 0) + filename = filename.Substring (0, comma_pos); + + // Try as a filename + string path = Path.GetFullPath (Path.Combine (directory, filename)); + AssemblyName aname = null; + if (specific_version && !TryGetAssemblyNameFromFullName (reference.ItemSpec, out aname)) + return null; + + ResolvedReference resolved_ref = ResolveReferenceForPath (path, reference, aname, null, SearchPath.Directory, specific_version); + if (resolved_ref != null) + return resolved_ref; + + // try path + Include + {.dll|.exe|..} + foreach (string extn in file_extensions) { + resolved_ref = ResolveReferenceForPath (path + extn, reference, aname, null, SearchPath.Directory, specific_version); + if (resolved_ref != null) + return resolved_ref; + } + + return null; + } + + // tries to resolve reference from the given file path, and compares assembly names + // if @specific_version == true, and logs accordingly + ResolvedReference ResolveReferenceForPath (string filename, ITaskItem reference, AssemblyName aname, + string error_message, SearchPath spath, bool specific_version) + { + AssemblyName found_aname = GetAssemblyNameFromFile (filename); + if (found_aname == null) { + if (error_message != null) + log.LogMessage (MessageImportance.Low, error_message); + return null; + } + + if (!specific_version || AssemblyNamesCompatible (aname, found_aname, specific_version)) { + // Check compatibility only if specific_version == true + return GetResolvedReference (reference, filename, found_aname, true, spath); } else { - // Try as a filename - string path = Path.Combine (directory, reference.ItemSpec); - if (GetAssemblyNameFromFile (path) != null) - return GetResolvedReference (reference, path, true, SearchPath.Directory); + LogSearchMessage ("Considered '{0}', but assembly name '{1}' did not match the " + + "expected '{2}' (SpecificVersion={3})", filename, found_aname, aname, specific_version); + log.LogMessage (MessageImportance.Low, "Assembly names are not compatible."); } return null; @@ -169,9 +209,10 @@ namespace Microsoft.Build.Tasks { { TargetFrameworkAssemblies gac_asm = new TargetFrameworkAssemblies (directory); foreach (string file in Directory.GetFiles (directory, "*.dll")) { - AssemblyName aname = AssemblyName.GetAssemblyName (file); - gac_asm.NameToAssemblyNameCache [aname.Name] = - new KeyValuePair (aname, file); + AssemblyName aname = GetAssemblyNameFromFile (file); + if (aname != null) + gac_asm.NameToAssemblyNameCache [aname.Name] = + new KeyValuePair (aname, file); } return gac_asm; @@ -179,9 +220,12 @@ namespace Microsoft.Build.Tasks { public ResolvedReference ResolveGacReference (ITaskItem reference, bool specific_version) { - AssemblyName name = new AssemblyName (reference.ItemSpec); + AssemblyName name; + if (!TryGetAssemblyNameFromFullName (reference.ItemSpec, out name)) + return null; + if (!gac.ContainsKey (name.Name)) { - SearchLogger.WriteLine ("Considered {0}, but could not find in the GAC.", + LogSearchMessage ("Considered {0}, but could not find in the GAC.", reference.ItemSpec); return null; } @@ -189,11 +233,11 @@ namespace Microsoft.Build.Tasks { if (name.Version != null) { string ret; if (gac [name.Name].TryGetValue (name.Version, out ret)) - return GetResolvedReference (reference, ret, false, SearchPath.Gac); + return GetResolvedReference (reference, ret, name, false, SearchPath.Gac); // not found if (specific_version) { - SearchLogger.WriteLine ("Considered '{0}', but an assembly with the specific version not found.", + LogSearchMessage ("Considered '{0}', but an assembly with the specific version not found.", reference.ItemSpec); return null; } @@ -203,84 +247,137 @@ namespace Microsoft.Build.Tasks { gac [name.Name].Keys.CopyTo (versions, 0); Array.Sort (versions, (IComparer ) null); Version highest = versions [versions.Length - 1]; - return GetResolvedReference (reference, gac [name.Name] [highest], false, SearchPath.Gac); + //FIXME: the aname being used here isn't correct, its version should + // actually match "highest" + return GetResolvedReference (reference, gac [name.Name] [highest], name, false, SearchPath.Gac); } - public ResolvedReference ResolveHintPathReference (ITaskItem reference, bool specific_version) + public ResolvedReference ResolvePkgConfigReference (ITaskItem reference, bool specific_version) { - AssemblyName name = new AssemblyName (reference.ItemSpec); - ResolvedReference resolved = null; + PackageAssemblyInfo pkg = null; + + if (specific_version) { + pkg = PcCache.GetAssemblyLocation (reference.ItemSpec); + } else { + // if not specific version, then just match simple name + string name = reference.ItemSpec; + if (name.IndexOf (',') > 0) + name = name.Substring (0, name.IndexOf (',')); + pkg = PcCache.ResolveAssemblyName (name).FirstOrDefault (); + } + + if (pkg == null) { + LogSearchMessage ("Considered {0}, but could not find in any pkg-config files.", + reference.ItemSpec); + return null; + } + AssemblyName aname; + if (!TryGetAssemblyNameFromFullName (pkg.FullName, out aname)) + return null; + + ResolvedReference rr = GetResolvedReference (reference, pkg.File, aname, + false, SearchPath.PkgConfig); + rr.FoundInSearchPathAsString = String.Format ("{{PkgConfig}} provided by package named {0}", + pkg.ParentPackage.Name); + + return rr; + } + + // HintPath has a valid assembly + // if specific_version==true + // resolve if assembly names match + // else + // resolve the valid assembly + public ResolvedReference ResolveHintPathReference (ITaskItem reference, bool specific_version) + { string hintpath = reference.GetMetadata ("HintPath"); if (String.IsNullOrEmpty (hintpath)) { - SearchLogger.WriteLine ("HintPath attribute not found"); + LogSearchMessage ("HintPath attribute not found"); return null; } if (!File.Exists (hintpath)) { log.LogMessage (MessageImportance.Low, "HintPath {0} does not exist.", hintpath); - SearchLogger.WriteLine ("Considered {0}, but it does not exist.", hintpath); + LogSearchMessage ("Considered {0}, but it does not exist.", hintpath); return null; } - AssemblyName found = GetAssemblyNameFromFile (hintpath); - if (found == null) { - log.LogMessage (MessageImportance.Low, "File at HintPath {0}, is either an invalid assembly or the file does not exist.", hintpath); + AssemblyName aname; + if (!TryGetAssemblyNameFromFullName (reference.ItemSpec, out aname)) return null; - } - if (AssemblyNamesCompatible (name, found, specific_version)) { - resolved = GetResolvedReference (reference, hintpath, true, SearchPath.HintPath); - } else { - SearchLogger.WriteLine ("Considered {0}, but assembly name '{1}' did not match the " + - "expected '{2}' (SpecificVersion={3})", hintpath, found, name, specific_version); - log.LogMessage (MessageImportance.Low, "Assembly names are not compatible."); - } - - return resolved; + return ResolveReferenceForPath (hintpath, reference, aname, + String.Format ("File at HintPath {0}, is either an invalid assembly or the file does not exist.", hintpath), + SearchPath.HintPath, specific_version); } public AssemblyName GetAssemblyNameFromFile (string filename) { AssemblyName aname = null; + filename = Path.GetFullPath (filename); try { aname = AssemblyName.GetAssemblyName (filename); } catch (FileNotFoundException) { + LogSearchMessage ("Considered '{0}' as a file, but the file does not exist", + filename); } catch (BadImageFormatException) { + LogSearchMessage ("Considered '{0}' as a file, but it is an invalid assembly", + filename); } - if (aname != null) - return aname; + return aname; + } - SearchLogger.WriteLine ("Considered '{0}' as a file, but it is either an invalid assembly " + - "or file does not exist.", Path.GetFullPath (filename)); - return null; + bool TryGetAssemblyNameFromFullName (string full_name, out AssemblyName aname) + { + aname = null; + try { + aname = new AssemblyName (full_name); + } catch (FileLoadException) { + LogSearchMessage ("Considered '{0}' as an assembly name, but it is invalid.", full_name); + } + + return aname != null; } - static bool AssemblyNamesCompatible (AssemblyName a, AssemblyName b, bool specificVersion) + internal static bool AssemblyNamesCompatible (AssemblyName a, AssemblyName b, bool specificVersion) { - if (a.Name != b.Name) + return AssemblyNamesCompatible (a, b, specificVersion, true); + } + + // if @specificVersion is true then match full name, else just the simple name + internal static bool AssemblyNamesCompatible (AssemblyName a, AssemblyName b, bool specificVersion, + bool ignoreCase) + { + if (String.Compare (a.Name, b.Name, ignoreCase) != 0) return false; + if (!specificVersion) + // ..and simple names match + return true; + if (a.CultureInfo != null && !a.CultureInfo.Equals (b.CultureInfo)) return false; - if (specificVersion && a.Version != null && a.Version != b.Version) + if (a.Version != null && a.Version != b.Version) return false; byte [] a_bytes = a.GetPublicKeyToken (); byte [] b_bytes = b.GetPublicKeyToken (); - if (specificVersion) { - if (a_bytes == null || a_bytes.Length == 0) - return false; - if (b_bytes == null || b_bytes.Length == 0) - return false; + bool a_is_empty = (a_bytes == null || a_bytes.Length == 0); + bool b_is_empty = (b_bytes == null || b_bytes.Length == 0); - for (int i = 0; i < a_bytes.Length; i++) - if (a_bytes [i] != b_bytes [i]) - return false; - } + if (a_is_empty && b_is_empty) + return true; + + if (a_is_empty || b_is_empty) + return false; + + for (int i = 0; i < a_bytes.Length; i++) + if (a_bytes [i] != b_bytes [i]) + return false; return true; } @@ -294,23 +391,51 @@ namespace Microsoft.Build.Tasks { // FIXME: to get default values of CopyLocal, compare with TargetFrameworkDirectories - // If metadata 'Private' is present then use that or use @default_value + // If metadata 'Private' is present then use that or use @default_copy_local_value // as the value for CopyLocal internal ResolvedReference GetResolvedReference (ITaskItem reference, string filename, - bool default_value, SearchPath search_path) + AssemblyName aname, bool default_copy_local_value, SearchPath search_path) { string pvt = reference.GetMetadata ("Private"); - bool copy_local = default_value; + bool copy_local = default_copy_local_value; if (!String.IsNullOrEmpty (pvt)) //FIXME: log a warning for invalid value Boolean.TryParse (pvt, out copy_local); - return new ResolvedReference (filename, copy_local, search_path); + ITaskItem new_item = new TaskItem (reference); + new_item.ItemSpec = filename; + return new ResolvedReference (new_item, aname, copy_local, search_path, reference.ItemSpec); + } + + public void LogSearchMessage (string msg, params object [] args) + { + search_log.Add (String.Format (msg, args)); + } + + public void LogSearchLoggerMessages (MessageImportance importance) + { + foreach (string msg in search_log) + log.LogMessage (importance, msg); } public TaskLoggingHelper Log { - set { log = value; } + set { + log = value; + PcFileCacheContext.Log = value; + } + } + + static LibraryPcFileCache PcCache { + get { + if (cache == null) { + var context = new PcFileCacheContext (); + cache = new LibraryPcFileCache (context); + cache.Update (); + } + + return cache; + } } } @@ -323,7 +448,33 @@ namespace Microsoft.Build.Tasks { public TargetFrameworkAssemblies (string path) { this.Path = path; - NameToAssemblyNameCache = new Dictionary> (); + NameToAssemblyNameCache = new Dictionary> ( + StringComparer.InvariantCultureIgnoreCase); + } + } + + class PcFileCacheContext : IPcFileCacheContext + { + public static TaskLoggingHelper Log; + + // In the implementation of this method, the host application can extract + // information from the pc file and store it in the PackageInfo object + public void StoreCustomData (PcFile pcfile, LibraryPackageInfo pkg) + { + } + + // Should return false if the provided package does not have required + // custom data + public bool IsCustomDataComplete (string pcfile, LibraryPackageInfo pkg) + { + return true; + } + + // Called to report errors + public void ReportError (string message, Exception ex) + { + Log.LogMessage (MessageImportance.Low, "Error loading pkg-config files: {0} : {1}", + message, ex.ToString ()); } } @@ -334,7 +485,8 @@ namespace Microsoft.Build.Tasks { CandidateAssemblies, HintPath, Directory, - RawFileName + RawFileName, + PkgConfig } }