5 // Marek Sieradzki (marek.sieradzki@gmail.com)
6 // Ankit Jain (jankit@novell.com)
8 // (C) 2006 Marek Sieradzki
9 // Copyright 2009 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections.Generic;
35 using System.Reflection;
36 using System.Security;
37 using Microsoft.Build.Framework;
38 using Microsoft.Build.Utilities;
40 namespace Microsoft.Build.Tasks {
41 internal class AssemblyResolver {
43 // name -> (version -> assemblypath)
44 Dictionary<string, TargetFrameworkAssemblies> target_framework_cache;
45 Dictionary<string, Dictionary<Version, string>> gac;
46 TaskLoggingHelper log;
48 public AssemblyResolver ()
50 gac = new Dictionary<string, Dictionary<Version, string>> ();
51 target_framework_cache = new Dictionary <string, TargetFrameworkAssemblies> ();
53 GatherGacAssemblies ();
58 // NOTE: code from mcs/tools/gacutil/driver.cs
59 PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
64 MethodInfo get_gac = gac.GetGetMethod (true);
65 return (string) get_gac.Invoke (null, null);
68 void GatherGacAssemblies ()
70 string gac_path = GetGacPath ();
72 throw new InvalidOperationException ("XBuild must be run on Mono runtime");
73 if (!Directory.Exists (gac_path))
74 return; // in case mono isn't "installed".
77 DirectoryInfo version_info, assembly_info;
79 foreach (string assembly_name in Directory.GetDirectories (gac_path)) {
80 assembly_info = new DirectoryInfo (assembly_name);
81 foreach (string version_token in Directory.GetDirectories (assembly_name)) {
82 foreach (string file in Directory.GetFiles (version_token, "*.dll")) {
83 version_info = new DirectoryInfo (version_token);
84 version = new Version (version_info.Name.Split (
85 new char [] {'_'}, StringSplitOptions.RemoveEmptyEntries) [0]);
87 if (!gac.ContainsKey (assembly_info.Name))
88 gac.Add (assembly_info.Name, new Dictionary <Version, string> ());
89 gac [assembly_info.Name].Add (version, file);
95 public string FindInTargetFramework (ITaskItem reference, string framework_dir)
97 AssemblyName key_aname = new AssemblyName (reference.ItemSpec);
98 TargetFrameworkAssemblies gac_asm;
99 if (!target_framework_cache.TryGetValue (framework_dir, out gac_asm)) {
101 gac_asm = target_framework_cache [framework_dir] = PopulateTargetFrameworkAssemblies (framework_dir);
104 KeyValuePair<AssemblyName, string> pair;
105 if (gac_asm.NameToAssemblyNameCache.TryGetValue (key_aname.Name, out pair)) {
106 if (AssemblyNamesCompatible (key_aname, pair.Key, false))
112 public string FindInDirectory (ITaskItem reference, string directory)
114 AssemblyName key_aname = new AssemblyName (reference.ItemSpec);
115 foreach (string file in Directory.GetFiles (directory, "*.dll")) {
116 AssemblyName found = AssemblyName.GetAssemblyName (file);
117 if (AssemblyNamesCompatible (key_aname, found, false))
124 TargetFrameworkAssemblies PopulateTargetFrameworkAssemblies (string directory)
126 TargetFrameworkAssemblies gac_asm = new TargetFrameworkAssemblies (directory);
127 foreach (string file in Directory.GetFiles (directory, "*.dll")) {
128 AssemblyName aname = AssemblyName.GetAssemblyName (file);
129 gac_asm.NameToAssemblyNameCache [aname.Name] =
130 new KeyValuePair<AssemblyName, string> (aname, file);
136 public string ResolveGacReference (ITaskItem reference)
138 // FIXME: deal with SpecificVersion=False
139 AssemblyName name = new AssemblyName (reference.ItemSpec);
140 if (!gac.ContainsKey (name.Name))
143 if (name.Version != null) {
144 if (!gac [name.Name].ContainsKey (name.Version))
147 return gac [name.Name] [name.Version];
150 Version [] versions = new Version [gac [name.Name].Keys.Count];
151 gac [name.Name].Keys.CopyTo (versions, 0);
152 Array.Sort (versions, (IComparer <Version>) null);
153 Version highest = versions [versions.Length - 1];
154 return gac [name.Name] [highest];
157 public string ResolveHintPathReference (ITaskItem reference)
159 AssemblyName name = new AssemblyName (reference.ItemSpec);
160 string resolved = null;
162 string hintpath = reference.GetMetadata ("HintPath");
163 if (String.IsNullOrEmpty (hintpath))
166 bool specificVersion;
167 if (!String.IsNullOrEmpty (reference.GetMetadata ("SpecificVersion")))
168 specificVersion = Boolean.Parse (reference.GetMetadata ("SpecificVersion"));
170 specificVersion = IsStrongNamed (name);
172 if (!File.Exists (hintpath)) {
173 log.LogMessage (MessageImportance.Low, "HintPath {0} does not exist.", hintpath);
178 AssemblyName found = AssemblyName.GetAssemblyName (hintpath);
179 if (AssemblyNamesCompatible (name, found, specificVersion))
182 log.LogMessage (MessageImportance.Low, "Assembly names are not compatible.");
189 static bool AssemblyNamesCompatible (AssemblyName a, AssemblyName b, bool specificVersion)
191 if (a.Name != b.Name)
194 if (a.CultureInfo != null && !a.CultureInfo.Equals (b.CultureInfo))
197 if (specificVersion && a.Version != null && a.Version > b.Version)
200 byte [] a_bytes = a.GetPublicKeyToken ();
201 byte [] b_bytes = b.GetPublicKeyToken ();
203 if (specificVersion) {
204 if (a_bytes == null || a_bytes.Length == 0)
206 if (b_bytes == null || b_bytes.Length == 0)
209 for (int i = 0; i < a_bytes.Length; i++)
210 if (a_bytes [i] != b_bytes [i])
217 static bool IsStrongNamed (AssemblyName name)
219 return (name.Version != null && name.GetPublicKeyToken ().Length != 0);
222 public TaskLoggingHelper Log {
227 class TargetFrameworkAssemblies {
230 // assembly (simple) name -> (AssemblyName, file path)
231 public Dictionary <string, KeyValuePair<AssemblyName, string>> NameToAssemblyNameCache;
233 public TargetFrameworkAssemblies (string path)
236 NameToAssemblyNameCache = new Dictionary<string, KeyValuePair<AssemblyName, string>> ();