2007-01-29 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                         GatherGacAssemblies ();
52                 }
53
54                 string GetGacPath ()
55                 {
56                         // NOTE: code from mcs/tools/gacutil/driver.cs
57                         PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
58
59                         if (gac == null)
60                                 return null;
61
62                         MethodInfo get_gac = gac.GetGetMethod (true);
63                         return (string) get_gac.Invoke (null, null);
64                 }
65
66                 void GatherGacAssemblies ()
67                 {
68                         string gac_path = GetGacPath ();
69                         if (gac_path == null)
70                                 throw new InvalidOperationException ("XBuild must be run on Mono runtime");
71
72                         Version version;
73                         DirectoryInfo version_info, assembly_info;
74
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]);
82
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);
86                                         }
87                                 }
88                         }
89                 }
90
91                 void GatherHintPathAssemblies (string hintPath)
92                 {
93                         if (hint_paths.ContainsKey (hintPath))
94                                 return;
95
96                         Assembly a;
97                         AssemblyName name;
98
99                         foreach (string assembly_name in Directory.GetDirectories (hintPath)) {
100                                 try {
101                                         a = Assembly.ReflectionOnlyLoadFrom (assembly_name);
102                                         name = new AssemblyName (a.FullName);
103
104                                         if (!hint_path_assemblies.ContainsKey (name.Name))
105                                                 hint_path_assemblies [name.Name] = new Dictionary <Version, string> ();
106                                         hint_path_assemblies [name.Name] [name.Version] = assembly_name;
107                                 } catch {
108                                 }
109                         }
110                 }
111
112                 public string ResolveAssemblyReference (ITaskItem reference)
113                 {
114                         AssemblyName name = null;
115
116                         try {
117                                 name = new AssemblyName (reference.ItemSpec);
118                         } catch {
119                                 return null;
120                         }
121
122                         return ResolveHintPathReference (name, reference.GetMetadata ("HintPath")) ?? ResolveGacReference (name);
123                 }
124
125                 string ResolveGacReference (AssemblyName name)
126                 {
127                         if (!gac.ContainsKey (name.Name))
128                                 return null;
129
130                         if (name.Version != null) {
131                                 if (!gac [name.Name].ContainsKey (name.Version))
132                                         return null;
133                                 else
134                                         return gac [name.Name] [name.Version];
135                         }
136
137                         Version [] versions = new Version [gac [name.Name].Keys.Count];
138                         gac [name.Name].Keys.CopyTo (versions, 0);
139                         Array.Sort (versions, (IComparer <Version>) null);
140                         Version highest = versions [versions.Length - 1];
141                         
142                         return gac [name.Name] [highest];
143                 }
144
145                 string ResolveHintPathReference (AssemblyName name, string hintpath)
146                 {
147                         if (hintpath != String.Empty)
148                                 GatherHintPathAssemblies (hintpath);
149
150                         if (!hint_path_assemblies.ContainsKey (name.Name))
151                                 return null;
152
153                         if (name.Version != null) {
154                                 if (!hint_path_assemblies [name.Name].ContainsKey (name.Version))
155                                         return null;
156                                 else
157                                         return hint_path_assemblies [name.Name] [name.Version];
158                         }
159
160                         Version [] versions = new Version [hint_path_assemblies [name.Name].Keys.Count];
161                         hint_path_assemblies [name.Name].Keys.CopyTo (versions, 0);
162                         Array.Sort (versions, (IComparer <Version>) null);
163                         Version highest = versions [versions.Length - 1];
164                         
165                         return hint_path_assemblies [name.Name] [highest];
166                 }
167         }
168 }
169
170 #endif