2007-02-18 Marek Sieradzki <marek.sieradzki@gmail.com>
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / AssemblyResolver.cs
1 //
2 // AssemblyResolver.cs
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 // 
7 // (C) 2006 Marek Sieradzki
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 #if NET_2_0
29
30 using System;
31 using System.Collections.Generic;
32 using System.IO;
33 using System.Reflection;
34 using System.Security;
35 using Microsoft.Build.Framework;
36 using Microsoft.Build.Utilities;
37
38 namespace Microsoft.Build.Tasks {
39         internal class AssemblyResolver {
40
41                 // name -> (version -> assemblypath)
42                 Dictionary <string, Dictionary <Version, string>> gac;
43
44                 Dictionary <string, Dictionary <Version, string>> hint_path_assemblies;
45                 Dictionary <string, object> hint_paths;
46         
47                 public AssemblyResolver ()
48                 {
49                         gac = new Dictionary <string, Dictionary <Version, string>> ();
50                         hint_path_assemblies = new Dictionary <string, Dictionary <Version, string>> ();
51                         hint_paths = new Dictionary <string, object> ();
52                         GatherGacAssemblies ();
53                 }
54
55                 string GetGacPath ()
56                 {
57                         // NOTE: code from mcs/tools/gacutil/driver.cs
58                         PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
59
60                         if (gac == null)
61                                 return null;
62
63                         MethodInfo get_gac = gac.GetGetMethod (true);
64                         return (string) get_gac.Invoke (null, null);
65                 }
66
67                 void GatherGacAssemblies ()
68                 {
69                         string gac_path = GetGacPath ();
70                         if (gac_path == null)
71                                 throw new InvalidOperationException ("XBuild must be run on Mono runtime");
72
73                         Version version;
74                         DirectoryInfo version_info, assembly_info;
75
76                         foreach (string assembly_name in Directory.GetDirectories (gac_path)) {
77                                 assembly_info = new DirectoryInfo (assembly_name);
78                                 foreach (string version_token in Directory.GetDirectories (assembly_name)) {
79                                         foreach (string file in Directory.GetFiles (version_token, "*.dll")) {
80                                                 version_info = new DirectoryInfo (version_token);
81                                                 version = new Version (version_info.Name.Split (
82                                                         new char [] {'_'}, StringSplitOptions.RemoveEmptyEntries) [0]);
83
84                                                 if (!gac.ContainsKey (assembly_info.Name))
85                                                         gac.Add (assembly_info.Name, new Dictionary <Version, string> ());
86                                                 gac [assembly_info.Name].Add (version, file);
87                                         }
88                                 }
89                         }
90                 }
91
92                 void GatherHintPathAssemblies (string hintPath)
93                 {
94                         if (hint_paths.ContainsKey (hintPath))
95                                 return;
96
97                         Assembly a;
98                         AssemblyName name;
99
100                         try {
101                                 foreach (string assembly_name in Directory.GetFiles (Path.GetDirectoryName (hintPath))) {
102                                         try {
103                                                 a = Assembly.ReflectionOnlyLoadFrom (assembly_name);
104                                                 name = new AssemblyName (a.FullName);
105         
106                                                 if (!hint_path_assemblies.ContainsKey (name.Name))
107                                                         hint_path_assemblies [name.Name] = new Dictionary <Version, string> ();
108                                                 hint_path_assemblies [name.Name] [name.Version] = assembly_name;
109                                                 hint_paths [hintPath] = null;
110                                         } catch {
111                                         }
112                                 }
113                         } catch {
114                         }
115                 }
116
117                 public string ResolveAssemblyReference (ITaskItem reference)
118                 {
119                         AssemblyName name = null;
120                         string resolved = null;
121
122                         try {
123                                 name = new AssemblyName (reference.ItemSpec);
124                         } catch {
125                                 return null;
126                         }
127
128                         if (reference.GetMetadata ("HintPath") != String.Empty)
129                                 resolved = ResolveHintPathReference (name, reference.GetMetadata ("HintPath"));
130                         
131                         if (resolved == null)
132                                 resolved = ResolveGacReference (name);
133
134                         return resolved;
135                 }
136
137                 string ResolveGacReference (AssemblyName name)
138                 {
139                         return ResolveGenericReference (name, gac);
140                 }
141
142                 string ResolveHintPathReference (AssemblyName name, string hintpath)
143                 {
144                         if (hintpath != String.Empty)
145                                 GatherHintPathAssemblies (hintpath);
146                         return ResolveGenericReference (name, hint_path_assemblies);
147                 }
148
149                 string ResolveGenericReference (AssemblyName name, Dictionary <string, Dictionary <Version, string>> dic)
150                 {
151                         // FIXME: deal with SpecificVersion=False
152
153                         if (!dic.ContainsKey (name.Name))
154                                 return null;
155
156                         if (name.Version != null) {
157                                 if (!dic [name.Name].ContainsKey (name.Version))
158                                         return null;
159                                 else
160                                         return dic [name.Name] [name.Version];
161                         }
162
163                         Version [] versions = new Version [dic [name.Name].Keys.Count];
164                         dic [name.Name].Keys.CopyTo (versions, 0);
165                         Array.Sort (versions, (IComparer <Version>) null);
166                         Version highest = versions [versions.Length - 1];
167                         return dic [name.Name] [highest];
168                 }
169         }
170 }
171
172 #endif