Merge remote branch 'upstream/master'
[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 - 2010 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 abstract class BaseAssemblyResolver : IAssemblyResolver {
55
56                 static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;
57
58                 readonly Collection<string> directories;
59
60 #if !SILVERLIGHT && !CF
61                 Collection<string> gac_paths;
62 #endif
63
64                 public void AddSearchDirectory (string directory)
65                 {
66                         directories.Add (directory);
67                 }
68
69                 public void RemoveSearchDirectory (string directory)
70                 {
71                         directories.Remove (directory);
72                 }
73
74                 public string [] GetSearchDirectories ()
75                 {
76                         var directories = new string [this.directories.size];
77                         Array.Copy (this.directories.items, directories, directories.Length);
78                         return directories;
79                 }
80
81                 public virtual AssemblyDefinition Resolve (string fullName)
82                 {
83                         return Resolve (fullName, new ReaderParameters ());
84                 }
85
86                 public virtual AssemblyDefinition Resolve (string fullName, ReaderParameters parameters)
87                 {
88                         if (fullName == null)
89                                 throw new ArgumentNullException ("fullName");
90
91                         return Resolve (AssemblyNameReference.Parse (fullName), parameters);
92                 }
93
94                 public event AssemblyResolveEventHandler ResolveFailure;
95
96                 protected BaseAssemblyResolver ()
97                 {
98                         directories = new Collection<string> (2) { ".", "bin" };
99                 }
100
101                 AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
102                 {
103                         if (parameters.AssemblyResolver == null)
104                                 parameters.AssemblyResolver = this;
105
106                         return ModuleDefinition.ReadModule (file, parameters).Assembly;
107                 }
108
109                 public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
110                 {
111                         return Resolve (name, new ReaderParameters ());
112                 }
113
114                 public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
115                 {
116                         if (name == null)
117                                 throw new ArgumentNullException ("name");
118                         if (parameters == null)
119                                 parameters = new ReaderParameters ();
120
121                         var assembly = SearchDirectory (name, directories, parameters);
122                         if (assembly != null)
123                                 return assembly;
124
125 #if !SILVERLIGHT && !CF
126                         var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
127
128                         if (IsZero (name.Version)) {
129                                 assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
130                                 if (assembly != null)
131                                         return assembly;
132                         }
133
134                         if (name.Name == "mscorlib") {
135                                 assembly = GetCorlib (name, parameters);
136                                 if (assembly != null)
137                                         return assembly;
138                         }
139
140                         assembly = GetAssemblyInGac (name, parameters);
141                         if (assembly != null)
142                                 return assembly;
143
144                         assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
145                         if (assembly != null)
146                                 return assembly;
147 #endif
148
149                         if (ResolveFailure != null) {
150                                 assembly = ResolveFailure (this, name);
151                                 if (assembly != null)
152                                         return assembly;
153                         }
154
155                         throw new FileNotFoundException ("Could not resolve: " + name);
156                 }
157
158                 AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
159                 {
160                         var extensions = new [] { ".exe", ".dll" };
161                         foreach (var directory in directories) {
162                                 foreach (var extension in extensions) {
163                                         string file = Path.Combine (directory, name.Name + extension);
164                                         if (File.Exists (file))
165                                                 return GetAssembly (file, parameters);
166                                 }
167                         }
168
169                         return null;
170                 }
171
172                 static bool IsZero (Version version)
173                 {
174                         return version == null || (version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0);
175                 }
176
177 #if !SILVERLIGHT && !CF
178                 AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters)
179                 {
180                         var version = reference.Version;
181                         var corlib = typeof (object).Assembly.GetName ();
182
183                         if (corlib.Version == version || IsZero (version))
184                                 return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters);
185
186                         var path = Directory.GetParent (
187                                 Directory.GetParent (
188                                         typeof (object).Module.FullyQualifiedName).FullName
189                                 ).FullName;
190
191                         if (on_mono) {
192                                 if (version.Major == 1)
193                                         path = Path.Combine (path, "1.0");
194                                 else if (version.Major == 2) {
195                                         if (version.MajorRevision == 5)
196                                                 path = Path.Combine (path, "2.1");
197                                         else
198                                                 path = Path.Combine (path, "2.0");
199                                 } else if (version.Major == 4)
200                                         path = Path.Combine (path, "4.0");
201                                 else
202                                         throw new NotSupportedException ("Version not supported: " + version);
203                         } else {
204                                 switch (version.Major) {
205                                 case 1:
206                                         if (version.MajorRevision == 3300)
207                                                 path = Path.Combine (path, "v1.0.3705");
208                                         else
209                                                 path = Path.Combine (path, "v1.0.5000.0");
210                                         break;
211                                 case 2:
212                                         path = Path.Combine (path, "v2.0.50727");
213                                         break;
214                                 case 4:
215                                         path = Path.Combine (path, "v4.0.30319");
216                                         break;
217                                 default:
218                                         throw new NotSupportedException ("Version not supported: " + version);
219                                 }
220                         }
221
222                         var file = Path.Combine (path, "mscorlib.dll");
223                         if (File.Exists (file))
224                                 return GetAssembly (file, parameters);
225
226                         return null;
227                 }
228
229                 static Collection<string> GetGacPaths ()
230                 {
231                         if (on_mono)
232                                 return GetDefaultMonoGacPaths ();
233
234                         var paths = new Collection<string> (2);
235                         var windir = Environment.GetEnvironmentVariable ("WINDIR");
236                         if (windir == null)
237                                 return paths;
238
239                         paths.Add (Path.Combine (windir, "assembly"));
240                         paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly")));
241                         return paths;
242                 }
243
244                 static Collection<string> GetDefaultMonoGacPaths ()
245                 {
246                         var paths = new Collection<string> (1);
247                         var gac = GetCurrentMonoGac ();
248                         if (gac != null)
249                                 paths.Add (gac);
250
251                         var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX");
252                         if (string.IsNullOrEmpty (gac_paths_env))
253                                 return paths;
254
255                         var prefixes = gac_paths_env.Split (Path.PathSeparator);
256                         foreach (var prefix in prefixes) {
257                                 if (string.IsNullOrEmpty (prefix))
258                                         continue;
259
260                                 var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac");
261                                 if (Directory.Exists (gac_path) && !paths.Contains (gac))
262                                         paths.Add (gac_path);
263                         }
264
265                         return paths;
266                 }
267
268                 static string GetCurrentMonoGac ()
269                 {
270                         return Path.Combine (
271                                 Directory.GetParent (
272                                         Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName,
273                                 "gac");
274                 }
275
276                 AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters)
277                 {
278                         if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
279                                 return null;
280
281                         if (gac_paths == null)
282                                 gac_paths = GetGacPaths ();
283
284                         if (on_mono)
285                                 return GetAssemblyInMonoGac (reference, parameters);
286
287                         return GetAssemblyInNetGac (reference, parameters);
288                 }
289
290                 AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters)
291                 {
292                         for (int i = 0; i < gac_paths.Count; i++) {
293                                 var gac_path = gac_paths [i];
294                                 var file = GetAssemblyFile (reference, string.Empty, gac_path);
295                                 if (File.Exists (file))
296                                         return GetAssembly (file, parameters);
297                         }
298
299                         return null;
300                 }
301
302                 AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters)
303                 {
304                         var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC" };
305                         var prefixes = new [] { string.Empty, "v4.0_" };
306
307                         for (int i = 0; i < 2; i++) {
308                                 for (int j = 0; j < gacs.Length; j++) {
309                                         var gac = Path.Combine (gac_paths [i], gacs [j]);
310                                         var file = GetAssemblyFile (reference, prefixes [i], gac);
311                                         if (Directory.Exists (gac) && File.Exists (file))
312                                                 return GetAssembly (file, parameters);
313                                 }
314                         }
315
316                         return null;
317                 }
318
319                 static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac)
320                 {
321                         var gac_folder = new StringBuilder ();
322                         gac_folder.Append (prefix);
323                         gac_folder.Append (reference.Version);
324                         gac_folder.Append ("__");
325                         for (int i = 0; i < reference.PublicKeyToken.Length; i++)
326                                 gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));
327
328                         return Path.Combine (
329                                 Path.Combine (
330                                         Path.Combine (gac, reference.Name), gac_folder.ToString ()),
331                                 reference.Name + ".dll");
332                 }
333 #endif
334         }
335 }