2 // BaseAssemblyResolver.cs
5 // Jb Evain (jbevain@gmail.com)
7 // Copyright (c) 2008 - 2011 Jb Evain
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.
30 using System.Collections.Generic;
34 using Mono.Collections.Generic;
36 namespace Mono.Cecil {
38 public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference);
40 public sealed class AssemblyResolveEventArgs : EventArgs {
42 readonly AssemblyNameReference reference;
44 public AssemblyNameReference AssemblyReference {
45 get { return reference; }
48 public AssemblyResolveEventArgs (AssemblyNameReference reference)
50 this.reference = reference;
54 public class AssemblyResolutionException : FileNotFoundException {
56 readonly AssemblyNameReference reference;
58 public AssemblyNameReference AssemblyReference {
59 get { return reference; }
62 public AssemblyResolutionException (AssemblyNameReference reference)
63 : base (string.Format ("Failed to resolve assembly: '{0}'", reference))
65 this.reference = reference;
69 public abstract class BaseAssemblyResolver : IAssemblyResolver {
71 static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;
73 readonly Collection<string> directories;
75 #if !SILVERLIGHT && !CF
76 Collection<string> gac_paths;
79 public void AddSearchDirectory (string directory)
81 directories.Add (directory);
84 public void RemoveSearchDirectory (string directory)
86 directories.Remove (directory);
89 public string [] GetSearchDirectories ()
91 var directories = new string [this.directories.size];
92 Array.Copy (this.directories.items, directories, directories.Length);
96 public virtual AssemblyDefinition Resolve (string fullName)
98 return Resolve (fullName, new ReaderParameters ());
101 public virtual AssemblyDefinition Resolve (string fullName, ReaderParameters parameters)
103 if (fullName == null)
104 throw new ArgumentNullException ("fullName");
106 return Resolve (AssemblyNameReference.Parse (fullName), parameters);
109 public event AssemblyResolveEventHandler ResolveFailure;
111 protected BaseAssemblyResolver ()
113 directories = new Collection<string> (2) { ".", "bin" };
116 AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
118 if (parameters.AssemblyResolver == null)
119 parameters.AssemblyResolver = this;
121 return ModuleDefinition.ReadModule (file, parameters).Assembly;
124 public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
126 return Resolve (name, new ReaderParameters ());
129 public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
132 throw new ArgumentNullException ("name");
133 if (parameters == null)
134 parameters = new ReaderParameters ();
136 var assembly = SearchDirectory (name, directories, parameters);
137 if (assembly != null)
140 #if !SILVERLIGHT && !CF
141 var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
143 if (IsZero (name.Version)) {
144 assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
145 if (assembly != null)
149 if (name.Name == "mscorlib") {
150 assembly = GetCorlib (name, parameters);
151 if (assembly != null)
155 assembly = GetAssemblyInGac (name, parameters);
156 if (assembly != null)
159 assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
160 if (assembly != null)
164 if (ResolveFailure != null) {
165 assembly = ResolveFailure (this, name);
166 if (assembly != null)
170 throw new AssemblyResolutionException (name);
173 AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
175 var extensions = new [] { ".exe", ".dll" };
176 foreach (var directory in directories) {
177 foreach (var extension in extensions) {
178 string file = Path.Combine (directory, name.Name + extension);
179 if (File.Exists (file))
180 return GetAssembly (file, parameters);
187 static bool IsZero (Version version)
189 return version == null || (version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0);
192 #if !SILVERLIGHT && !CF
193 AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters)
195 var version = reference.Version;
196 var corlib = typeof (object).Assembly.GetName ();
198 if (corlib.Version == version || IsZero (version))
199 return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters);
201 var path = Directory.GetParent (
202 Directory.GetParent (
203 typeof (object).Module.FullyQualifiedName).FullName
207 if (version.Major == 1)
208 path = Path.Combine (path, "1.0");
209 else if (version.Major == 2) {
210 if (version.MajorRevision == 5)
211 path = Path.Combine (path, "2.1");
213 path = Path.Combine (path, "2.0");
214 } else if (version.Major == 4)
215 path = Path.Combine (path, "4.0");
217 throw new NotSupportedException ("Version not supported: " + version);
219 switch (version.Major) {
221 if (version.MajorRevision == 3300)
222 path = Path.Combine (path, "v1.0.3705");
224 path = Path.Combine (path, "v1.0.5000.0");
227 path = Path.Combine (path, "v2.0.50727");
230 path = Path.Combine (path, "v4.0.30319");
233 throw new NotSupportedException ("Version not supported: " + version);
237 var file = Path.Combine (path, "mscorlib.dll");
238 if (File.Exists (file))
239 return GetAssembly (file, parameters);
244 static Collection<string> GetGacPaths ()
247 return GetDefaultMonoGacPaths ();
249 var paths = new Collection<string> (2);
250 var windir = Environment.GetEnvironmentVariable ("WINDIR");
254 paths.Add (Path.Combine (windir, "assembly"));
255 paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly")));
259 static Collection<string> GetDefaultMonoGacPaths ()
261 var paths = new Collection<string> (1);
262 var gac = GetCurrentMonoGac ();
266 var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX");
267 if (string.IsNullOrEmpty (gac_paths_env))
270 var prefixes = gac_paths_env.Split (Path.PathSeparator);
271 foreach (var prefix in prefixes) {
272 if (string.IsNullOrEmpty (prefix))
275 var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac");
276 if (Directory.Exists (gac_path) && !paths.Contains (gac))
277 paths.Add (gac_path);
283 static string GetCurrentMonoGac ()
285 return Path.Combine (
286 Directory.GetParent (
287 Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName,
291 AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters)
293 if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
296 if (gac_paths == null)
297 gac_paths = GetGacPaths ();
300 return GetAssemblyInMonoGac (reference, parameters);
302 return GetAssemblyInNetGac (reference, parameters);
305 AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters)
307 for (int i = 0; i < gac_paths.Count; i++) {
308 var gac_path = gac_paths [i];
309 var file = GetAssemblyFile (reference, string.Empty, gac_path);
310 if (File.Exists (file))
311 return GetAssembly (file, parameters);
317 AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters)
319 var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC" };
320 var prefixes = new [] { string.Empty, "v4.0_" };
322 for (int i = 0; i < 2; i++) {
323 for (int j = 0; j < gacs.Length; j++) {
324 var gac = Path.Combine (gac_paths [i], gacs [j]);
325 var file = GetAssemblyFile (reference, prefixes [i], gac);
326 if (Directory.Exists (gac) && File.Exists (file))
327 return GetAssembly (file, parameters);
334 static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac)
336 var gac_folder = new StringBuilder ()
338 .Append (reference.Version)
341 for (int i = 0; i < reference.PublicKeyToken.Length; i++)
342 gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));
344 return Path.Combine (
346 Path.Combine (gac, reference.Name), gac_folder.ToString ()),
347 reference.Name + ".dll");