Merge pull request #121 from LogosBible/processfixes
[mono.git] / mcs / class / Mono.Cecil / Mono.Cecil / BaseAssemblyResolver.cs
1 //
2 // BaseAssemblyResolver.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@gmail.com)
6 //
7 // Copyright (c) 2008 - 2011 Jb Evain
8 //
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
27 //
28
29 using System;
30 using System.Collections.Generic;
31 using System.IO;
32 using System.Text;
33
34 using Mono.Collections.Generic;
35
36 namespace Mono.Cecil {
37
38         public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference);
39
40         public sealed class AssemblyResolveEventArgs : EventArgs {
41
42                 readonly AssemblyNameReference reference;
43
44                 public AssemblyNameReference AssemblyReference {
45                         get { return reference; }
46                 }
47
48                 public AssemblyResolveEventArgs (AssemblyNameReference reference)
49                 {
50                         this.reference = reference;
51                 }
52         }
53
54         public class AssemblyResolutionException : FileNotFoundException {
55
56                 readonly AssemblyNameReference reference;
57
58                 public AssemblyNameReference AssemblyReference {
59                         get { return reference; }
60                 }
61
62                 public AssemblyResolutionException (AssemblyNameReference reference)
63                         : base (string.Format ("Failed to resolve assembly: '{0}'", reference))
64                 {
65                         this.reference = reference;
66                 }
67         }
68
69         public abstract class BaseAssemblyResolver : IAssemblyResolver {
70
71                 static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;
72
73                 readonly Collection<string> directories;
74
75 #if !SILVERLIGHT && !CF
76                 Collection<string> gac_paths;
77 #endif
78
79                 public void AddSearchDirectory (string directory)
80                 {
81                         directories.Add (directory);
82                 }
83
84                 public void RemoveSearchDirectory (string directory)
85                 {
86                         directories.Remove (directory);
87                 }
88
89                 public string [] GetSearchDirectories ()
90                 {
91                         var directories = new string [this.directories.size];
92                         Array.Copy (this.directories.items, directories, directories.Length);
93                         return directories;
94                 }
95
96                 public virtual AssemblyDefinition Resolve (string fullName)
97                 {
98                         return Resolve (fullName, new ReaderParameters ());
99                 }
100
101                 public virtual AssemblyDefinition Resolve (string fullName, ReaderParameters parameters)
102                 {
103                         if (fullName == null)
104                                 throw new ArgumentNullException ("fullName");
105
106                         return Resolve (AssemblyNameReference.Parse (fullName), parameters);
107                 }
108
109                 public event AssemblyResolveEventHandler ResolveFailure;
110
111                 protected BaseAssemblyResolver ()
112                 {
113                         directories = new Collection<string> (2) { ".", "bin" };
114                 }
115
116                 AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
117                 {
118                         if (parameters.AssemblyResolver == null)
119                                 parameters.AssemblyResolver = this;
120
121                         return ModuleDefinition.ReadModule (file, parameters).Assembly;
122                 }
123
124                 public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
125                 {
126                         return Resolve (name, new ReaderParameters ());
127                 }
128
129                 public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
130                 {
131                         if (name == null)
132                                 throw new ArgumentNullException ("name");
133                         if (parameters == null)
134                                 parameters = new ReaderParameters ();
135
136                         var assembly = SearchDirectory (name, directories, parameters);
137                         if (assembly != null)
138                                 return assembly;
139
140 #if !SILVERLIGHT && !CF
141                         var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
142
143                         if (IsZero (name.Version)) {
144                                 assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
145                                 if (assembly != null)
146                                         return assembly;
147                         }
148
149                         if (name.Name == "mscorlib") {
150                                 assembly = GetCorlib (name, parameters);
151                                 if (assembly != null)
152                                         return assembly;
153                         }
154
155                         assembly = GetAssemblyInGac (name, parameters);
156                         if (assembly != null)
157                                 return assembly;
158
159                         assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
160                         if (assembly != null)
161                                 return assembly;
162 #endif
163
164                         if (ResolveFailure != null) {
165                                 assembly = ResolveFailure (this, name);
166                                 if (assembly != null)
167                                         return assembly;
168                         }
169
170                         throw new AssemblyResolutionException (name);
171                 }
172
173                 AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
174                 {
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);
181                                 }
182                         }
183
184                         return null;
185                 }
186
187                 static bool IsZero (Version version)
188                 {
189                         return version == null || (version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0);
190                 }
191
192 #if !SILVERLIGHT && !CF
193                 AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters)
194                 {
195                         var version = reference.Version;
196                         var corlib = typeof (object).Assembly.GetName ();
197
198                         if (corlib.Version == version || IsZero (version))
199                                 return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters);
200
201                         var path = Directory.GetParent (
202                                 Directory.GetParent (
203                                         typeof (object).Module.FullyQualifiedName).FullName
204                                 ).FullName;
205
206                         if (on_mono) {
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");
212                                         else
213                                                 path = Path.Combine (path, "2.0");
214                                 } else if (version.Major == 4)
215                                         path = Path.Combine (path, "4.0");
216                                 else
217                                         throw new NotSupportedException ("Version not supported: " + version);
218                         } else {
219                                 switch (version.Major) {
220                                 case 1:
221                                         if (version.MajorRevision == 3300)
222                                                 path = Path.Combine (path, "v1.0.3705");
223                                         else
224                                                 path = Path.Combine (path, "v1.0.5000.0");
225                                         break;
226                                 case 2:
227                                         path = Path.Combine (path, "v2.0.50727");
228                                         break;
229                                 case 4:
230                                         path = Path.Combine (path, "v4.0.30319");
231                                         break;
232                                 default:
233                                         throw new NotSupportedException ("Version not supported: " + version);
234                                 }
235                         }
236
237                         var file = Path.Combine (path, "mscorlib.dll");
238                         if (File.Exists (file))
239                                 return GetAssembly (file, parameters);
240
241                         return null;
242                 }
243
244                 static Collection<string> GetGacPaths ()
245                 {
246                         if (on_mono)
247                                 return GetDefaultMonoGacPaths ();
248
249                         var paths = new Collection<string> (2);
250                         var windir = Environment.GetEnvironmentVariable ("WINDIR");
251                         if (windir == null)
252                                 return paths;
253
254                         paths.Add (Path.Combine (windir, "assembly"));
255                         paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly")));
256                         return paths;
257                 }
258
259                 static Collection<string> GetDefaultMonoGacPaths ()
260                 {
261                         var paths = new Collection<string> (1);
262                         var gac = GetCurrentMonoGac ();
263                         if (gac != null)
264                                 paths.Add (gac);
265
266                         var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX");
267                         if (string.IsNullOrEmpty (gac_paths_env))
268                                 return paths;
269
270                         var prefixes = gac_paths_env.Split (Path.PathSeparator);
271                         foreach (var prefix in prefixes) {
272                                 if (string.IsNullOrEmpty (prefix))
273                                         continue;
274
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);
278                         }
279
280                         return paths;
281                 }
282
283                 static string GetCurrentMonoGac ()
284                 {
285                         return Path.Combine (
286                                 Directory.GetParent (
287                                         Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName,
288                                 "gac");
289                 }
290
291                 AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters)
292                 {
293                         if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
294                                 return null;
295
296                         if (gac_paths == null)
297                                 gac_paths = GetGacPaths ();
298
299                         if (on_mono)
300                                 return GetAssemblyInMonoGac (reference, parameters);
301
302                         return GetAssemblyInNetGac (reference, parameters);
303                 }
304
305                 AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters)
306                 {
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);
312                         }
313
314                         return null;
315                 }
316
317                 AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters)
318                 {
319                         var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC" };
320                         var prefixes = new [] { string.Empty, "v4.0_" };
321
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);
328                                 }
329                         }
330
331                         return null;
332                 }
333
334                 static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac)
335                 {
336                         var gac_folder = new StringBuilder ()
337                                 .Append (prefix)
338                                 .Append (reference.Version)
339                                 .Append ("__");
340
341                         for (int i = 0; i < reference.PublicKeyToken.Length; i++)
342                                 gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));
343
344                         return Path.Combine (
345                                 Path.Combine (
346                                         Path.Combine (gac, reference.Name), gac_folder.ToString ()),
347                                 reference.Name + ".dll");
348                 }
349 #endif
350         }
351 }