5 // Marek Sieradzki (marek.sieradzki@gmail.com)
7 // (C) 2006 Marek Sieradzki
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections.Generic;
33 using System.Reflection;
34 using System.Security;
35 using Microsoft.Build.Framework;
36 using Microsoft.Build.Utilities;
38 namespace Microsoft.Build.Tasks {
39 internal class AssemblyResolver {
41 // name -> (version -> assemblypath)
42 Dictionary <string, Dictionary <Version, string>> gac;
43 TaskLoggingHelper log;
45 public AssemblyResolver ()
47 gac = new Dictionary <string, Dictionary <Version, string>> ();
49 GatherGacAssemblies ();
54 // NOTE: code from mcs/tools/gacutil/driver.cs
55 PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
60 MethodInfo get_gac = gac.GetGetMethod (true);
61 return (string) get_gac.Invoke (null, null);
64 void GatherGacAssemblies ()
66 string gac_path = GetGacPath ();
68 throw new InvalidOperationException ("XBuild must be run on Mono runtime");
69 if (!Directory.Exists (gac_path))
70 return; // in case mono isn't "installed".
73 DirectoryInfo version_info, assembly_info;
75 foreach (string assembly_name in Directory.GetDirectories (gac_path)) {
76 assembly_info = new DirectoryInfo (assembly_name);
77 foreach (string version_token in Directory.GetDirectories (assembly_name)) {
78 foreach (string file in Directory.GetFiles (version_token, "*.dll")) {
79 version_info = new DirectoryInfo (version_token);
80 version = new Version (version_info.Name.Split (
81 new char [] {'_'}, StringSplitOptions.RemoveEmptyEntries) [0]);
83 if (!gac.ContainsKey (assembly_info.Name))
84 gac.Add (assembly_info.Name, new Dictionary <Version, string> ());
85 gac [assembly_info.Name].Add (version, file);
91 public string ResolveAssemblyReference (ITaskItem reference)
93 AssemblyName name = null;
94 string resolved = null;
96 name = new AssemblyName (reference.ItemSpec);
98 if (reference.GetMetadata ("HintPath") != String.Empty) {
100 bool specificVersion;
102 if (reference.GetMetadata ("SpecificVersion") != String.Empty) {
103 specificVersion = Boolean.Parse (reference.GetMetadata ("SpecificVersion"));
105 specificVersion = IsStrongNamed (name);
108 resolved = ResolveHintPathReference (name, reference.GetMetadata ("HintPath"), specificVersion);
111 if (resolved == null)
112 resolved = ResolveGacReference (name, gac);
117 string ResolveGacReference (AssemblyName name, Dictionary <string, Dictionary <Version, string>> dic)
119 // FIXME: deal with SpecificVersion=False
121 if (!dic.ContainsKey (name.Name))
124 if (name.Version != null) {
125 if (!dic [name.Name].ContainsKey (name.Version))
128 return dic [name.Name] [name.Version];
131 Version [] versions = new Version [dic [name.Name].Keys.Count];
132 dic [name.Name].Keys.CopyTo (versions, 0);
133 Array.Sort (versions, (IComparer <Version>) null);
134 Version highest = versions [versions.Length - 1];
135 return dic [name.Name] [highest];
138 string ResolveHintPathReference (AssemblyName name, string hintpath, bool specificVersion)
143 if (!File.Exists (hintpath))
144 log.LogMessage (MessageImportance.Low, "HintPath {0} does not exist.", hintpath);
147 found = AssemblyName.GetAssemblyName (hintpath);
148 if (AssemblyNamesCompatible (name, found, specificVersion))
151 log.LogMessage (MessageImportance.Low, "Assembly names are not compatible.");
158 static bool AssemblyNamesCompatible (AssemblyName a, AssemblyName b, bool specificVersion)
160 if (a.Name != b.Name)
163 if (a.CultureInfo != null && a.CultureInfo != b.CultureInfo)
166 if (specificVersion && a.Version != null && a.Version > b.Version)
169 byte [] a_bytes = a.GetPublicKeyToken ();
170 byte [] b_bytes = b.GetPublicKeyToken ();
172 if (specificVersion) {
173 if (a_bytes == null || a_bytes.Length == 0)
175 if (b_bytes == null || b_bytes.Length == 0)
178 for (int i = 0; i < a_bytes.Length; i++)
179 if (a_bytes [i] != b_bytes [i])
186 static bool IsStrongNamed (AssemblyName name)
188 return (name.Version != null && name.GetPublicKeyToken ().Length != 0);
191 public TaskLoggingHelper Log {