Add a more functional (i.e. fewer-stubs) implementation of System.Data.Linq.
[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 //   Ankit Jain (jankit@novell.com)
7 // 
8 // (C) 2006 Marek Sieradzki
9 // Copyright 2009 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
30 #if NET_2_0
31
32 using System;
33 using System.Collections.Generic;
34 using System.IO;
35 using System.Reflection;
36 using System.Security;
37 using Microsoft.Build.Framework;
38 using Microsoft.Build.Utilities;
39
40 namespace Microsoft.Build.Tasks {
41         internal class AssemblyResolver {
42
43                 // name -> (version -> assemblypath)
44                 Dictionary<string, TargetFrameworkAssemblies> target_framework_cache;
45                 Dictionary<string, Dictionary<Version, string>> gac;
46                 TaskLoggingHelper log;
47
48                 public AssemblyResolver ()
49                 {
50                         gac = new Dictionary<string, Dictionary<Version, string>> ();
51                         target_framework_cache = new Dictionary <string, TargetFrameworkAssemblies> ();
52
53                         GatherGacAssemblies ();
54                 }
55
56                 string GetGacPath ()
57                 {
58                         // NOTE: code from mcs/tools/gacutil/driver.cs
59                         PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
60
61                         if (gac == null)
62                                 return null;
63
64                         MethodInfo get_gac = gac.GetGetMethod (true);
65                         return (string) get_gac.Invoke (null, null);
66                 }
67
68                 void GatherGacAssemblies ()
69                 {
70                         string gac_path = GetGacPath ();
71                         if (gac_path == null)
72                                 throw new InvalidOperationException ("XBuild must be run on Mono runtime");
73                         if (!Directory.Exists (gac_path))
74                                 return; // in case mono isn't "installed".
75
76                         Version version;
77                         DirectoryInfo version_info, assembly_info;
78
79                         foreach (string assembly_name in Directory.GetDirectories (gac_path)) {
80                                 assembly_info = new DirectoryInfo (assembly_name);
81                                 foreach (string version_token in Directory.GetDirectories (assembly_name)) {
82                                         foreach (string file in Directory.GetFiles (version_token, "*.dll")) {
83                                                 version_info = new DirectoryInfo (version_token);
84                                                 version = new Version (version_info.Name.Split (
85                                                         new char [] {'_'}, StringSplitOptions.RemoveEmptyEntries) [0]);
86
87                                                 if (!gac.ContainsKey (assembly_info.Name))
88                                                         gac.Add (assembly_info.Name, new Dictionary <Version, string> ());
89                                                 gac [assembly_info.Name].Add (version, file);
90                                         }
91                                 }
92                         }
93                 }
94
95                 public string FindInTargetFramework (ITaskItem reference, string framework_dir)
96                 {
97                         AssemblyName key_aname = new AssemblyName (reference.ItemSpec);
98                         TargetFrameworkAssemblies gac_asm;
99                         if (!target_framework_cache.TryGetValue (framework_dir, out gac_asm)) {
100                                 // fill gac_asm
101                                 gac_asm = target_framework_cache [framework_dir] = PopulateTargetFrameworkAssemblies (framework_dir);
102                         }
103
104                         KeyValuePair<AssemblyName, string> pair;
105                         if (gac_asm.NameToAssemblyNameCache.TryGetValue (key_aname.Name, out pair)) {
106                                 if (AssemblyNamesCompatible (key_aname, pair.Key, false))
107                                         return pair.Value;
108                         }
109                         return null;
110                 }
111
112                 public string FindInDirectory (ITaskItem reference, string directory)
113                 {
114                         AssemblyName key_aname = new AssemblyName (reference.ItemSpec);
115                         foreach (string file in Directory.GetFiles (directory, "*.dll")) {
116                                 AssemblyName found = AssemblyName.GetAssemblyName (file);
117                                 if (AssemblyNamesCompatible (key_aname, found, false))
118                                         return file;
119                         }
120
121                         return null;
122                 }
123
124                 TargetFrameworkAssemblies PopulateTargetFrameworkAssemblies (string directory)
125                 {
126                         TargetFrameworkAssemblies gac_asm = new TargetFrameworkAssemblies (directory);
127                         foreach (string file in Directory.GetFiles (directory, "*.dll")) {
128                                 AssemblyName aname = AssemblyName.GetAssemblyName (file);
129                                 gac_asm.NameToAssemblyNameCache [aname.Name] =
130                                         new KeyValuePair<AssemblyName, string> (aname, file);
131                         }
132
133                         return gac_asm;
134                 }
135
136                 public string ResolveGacReference (ITaskItem reference)
137                 {
138                         // FIXME: deal with SpecificVersion=False
139                         AssemblyName name = new AssemblyName (reference.ItemSpec);
140                         if (!gac.ContainsKey (name.Name))
141                                 return null;
142
143                         if (name.Version != null) {
144                                 if (!gac [name.Name].ContainsKey (name.Version))
145                                         return null;
146                                 else
147                                         return gac [name.Name] [name.Version];
148                         }
149
150                         Version [] versions = new Version [gac [name.Name].Keys.Count];
151                         gac [name.Name].Keys.CopyTo (versions, 0);
152                         Array.Sort (versions, (IComparer <Version>) null);
153                         Version highest = versions [versions.Length - 1];
154                         return gac [name.Name] [highest];
155                 }
156
157                 public string ResolveHintPathReference (ITaskItem reference)
158                 {
159                         AssemblyName name = new AssemblyName (reference.ItemSpec);
160                         string resolved = null;
161
162                         string hintpath = reference.GetMetadata ("HintPath");
163                         if (String.IsNullOrEmpty (hintpath))
164                                 return null;
165
166                         bool specificVersion;
167                         if (!String.IsNullOrEmpty (reference.GetMetadata ("SpecificVersion")))
168                                 specificVersion = Boolean.Parse (reference.GetMetadata ("SpecificVersion"));
169                         else
170                                 specificVersion = IsStrongNamed (name);
171
172                         if (!File.Exists (hintpath)) {
173                                 log.LogMessage (MessageImportance.Low, "HintPath {0} does not exist.", hintpath);
174                                 return null;
175                         }
176
177                         try {
178                                 AssemblyName found = AssemblyName.GetAssemblyName (hintpath);
179                                 if (AssemblyNamesCompatible (name, found, specificVersion))
180                                         resolved = hintpath;
181                                 else
182                                         log.LogMessage (MessageImportance.Low, "Assembly names are not compatible.");
183                         } catch {
184                         }
185
186                         return resolved;
187                 }
188
189                 static bool AssemblyNamesCompatible (AssemblyName a, AssemblyName b, bool specificVersion)
190                 {
191                         if (a.Name != b.Name)
192                                 return false;
193
194                         if (a.CultureInfo != null && !a.CultureInfo.Equals (b.CultureInfo))
195                                 return false;
196
197                         if (specificVersion && a.Version != null && a.Version > b.Version)
198                                 return false;
199
200                         byte [] a_bytes = a.GetPublicKeyToken ();
201                         byte [] b_bytes = b.GetPublicKeyToken ();
202
203                         if (specificVersion) {
204                                 if (a_bytes == null || a_bytes.Length == 0)
205                                         return false;
206                                 if (b_bytes == null || b_bytes.Length == 0)
207                                         return false;
208
209                                 for (int i = 0; i < a_bytes.Length; i++)
210                                         if (a_bytes [i] != b_bytes [i])
211                                                 return false;
212                         }
213
214                         return true;
215                 }
216
217                 static bool IsStrongNamed (AssemblyName name)
218                 {
219                         return (name.Version != null && name.GetPublicKeyToken ().Length != 0);
220                 }
221
222                 public TaskLoggingHelper Log {
223                         set { log = value; }
224                 }
225         }
226
227         class TargetFrameworkAssemblies {
228                 public string Path;
229
230                 // assembly (simple) name -> (AssemblyName, file path)
231                 public Dictionary <string, KeyValuePair<AssemblyName, string>> NameToAssemblyNameCache;
232
233                 public TargetFrameworkAssemblies (string path)
234                 {
235                         this.Path = path;
236                         NameToAssemblyNameCache = new Dictionary<string, KeyValuePair<AssemblyName, string>> ();
237                 }
238         }
239 }
240
241 #endif